diff --git a/CMakeLists.txt b/CMakeLists.txt index 5106a0197dbe03a95bd81ca43a8489f7a117821c..fc24d6075c3a69fd8d0e3206d6c6c613ba945bdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,7 +86,6 @@ opt(SOLVER "Enable built-in finite element solvers (required for compounds)" ${D opt(TAUCS "Enable Taucs linear solver" ${DEFAULT}) opt(TCMALLOC "Enable libtcmalloc, a fast malloc implementation but that does not release memory" OFF) opt(TETGEN "Enable Tetgen 3D initial mesh generator" ${DEFAULT}) -opt(TETGEN_OLD "Enable older version of Tetgen" OFF) opt(VORO3D "Enable Voro3D (for hex meshing, experimental)" ${DEFAULT}) opt(WRAP_JAVA "Enable generation of Java wrappers (experimental)" OFF) opt(WRAP_PYTHON "Enable generation of Python wrappers" OFF) @@ -795,11 +794,6 @@ if(HAVE_MESH) include_directories(contrib/Tetgen1.5) set_config_option(HAVE_TETGEN "Tetgen(1.5)") add_definitions(-DTETLIBRARY) - elseif(ENABLE_TETGEN_OLD AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/Tetgen1.4/tetgen.h) - add_subdirectory(contrib/Tetgen1.4) - include_directories(contrib/Tetgen1.4) - set_config_option(HAVE_TETGEN "Tetgen(1.4)") - add_definitions(-DTETLIBRARY) elseif(ENABLE_TETGEN) find_library(TETGEN_LIB tet PATH_SUFFIXES lib) find_path(TETGEN_INC "tetgen.h" PATH_SUFFIXES src include tetgen) @@ -811,7 +805,7 @@ if(HAVE_MESH) endif(ENABLE_TETGEN AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/Tetgen1.5/tetgen.h) if(HAVE_TETGEN) message(STATUS "Note: By including Tetgen you have to comply with Tetgen's " - "special licensing requirements stated in contrib/Tetgen*/LICENSE.") + "special licensing requirements stated in contrib/Tetgen1.5/LICENSE.") endif(HAVE_TETGEN) endif(HAVE_MESH) diff --git a/contrib/Tetgen1.4/CMakeLists.txt b/contrib/Tetgen1.4/CMakeLists.txt deleted file mode 100644 index 6b1004a40a334ed5756cee0e12197c49b1e05ee3..0000000000000000000000000000000000000000 --- a/contrib/Tetgen1.4/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Gmsh - Copyright (C) 1997-2016 C. Geuzaine, J.-F. Remacle -# -# See the LICENSE.txt file for license information. Please report all -# bugs and problems to the public mailing list <gmsh@onelab.info>. - -set(SRC - tetgen.cxx - predicates.cxx -) - -file(GLOB_RECURSE HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) -append_gmsh_src(contrib/Tetgen1.4 "${SRC};${HDR}") diff --git a/contrib/Tetgen1.4/LICENSE b/contrib/Tetgen1.4/LICENSE deleted file mode 100644 index 65e5262522bd3faa6a85ec43002263fc1557455e..0000000000000000000000000000000000000000 --- a/contrib/Tetgen1.4/LICENSE +++ /dev/null @@ -1,66 +0,0 @@ -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/Tetgen1.4/README.txt b/contrib/Tetgen1.4/README.txt deleted file mode 100644 index 125c22c4fa9ef24105952f8cbf5aa24e888af529..0000000000000000000000000000000000000000 --- a/contrib/Tetgen1.4/README.txt +++ /dev/null @@ -1,16 +0,0 @@ -This is TetGen version 1.4.2 (released on April 16, 2007) - -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 -April 16, 2007 diff --git a/contrib/Tetgen1.4/predicates.cxx b/contrib/Tetgen1.4/predicates.cxx deleted file mode 100644 index bc0bd39f9e13f255295e81757db3889767a13196..0000000000000000000000000000000000000000 --- a/contrib/Tetgen1.4/predicates.cxx +++ /dev/null @@ -1,4187 +0,0 @@ -/*****************************************************************************/ -/* */ -/* 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/Tetgen1.4/tetgen.cxx b/contrib/Tetgen1.4/tetgen.cxx deleted file mode 100644 index cd74509640b9c660d4692e5e2ee91432bede203a..0000000000000000000000000000000000000000 --- a/contrib/Tetgen1.4/tetgen.cxx +++ /dev/null @@ -1,35006 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// TetGen // -// // -// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // -// // -// Version 1.4 // -// April 16, 2007 // -// // -// Copyright (C) 2002--2007 // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgen.cxx // -// // -// The TetGen library and program. // -// // -/////////////////////////////////////////////////////////////////////////////// - -#include "tetgen.h" - -/////////////////////////////////////////////////////////////////////////////// -// // -// terminatetetgen() Terminate TetGen with a given exit code. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void terminatetetgen(int x) -{ -#ifdef TETLIBRARY - throw x; -#else - exit(x); -#endif // #ifdef TETLIBRARY -} - -// -// Begin of class 'tetgenio' implementation -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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, 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(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 ((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, 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(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(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(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(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; - } - } - - } 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(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(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(char* filename) -{ - FILE *fp; - tetgenmesh::list *plist; - tetgenio::facet *f; - tetgenio::polygon *p; - char infilename[FILENAMESIZE]; - char buffer[INPUTLINESIZE]; - char *bufferp, *str; - double *coord; - int solid = 0; - int nverts = 0, iverts = 0; - int nfaces = 0; - int line_count = 0, i; - - strncpy(infilename, 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. - plist = new tetgenmesh::list(sizeof(double) * 3, NULL, 1024); - - 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) { - coord = (double *) plist->append(NULL); - for (i = 0; i < 3; i++) { - bufferp = findnextnumber(bufferp); - if (*bufferp == '\0') { - printf("Syntax error reading vertex coords on line %d\n", - line_count); - delete plist; - fclose(fp); - return false; - } - coord[i] = (REAL) strtod(bufferp, &bufferp); - } - } - } - } - } - fclose(fp); - - nverts = plist->len(); - // nverts should be an integer times 3 (every 3 vertices denote a face). - if (nverts == 0 || (nverts % 3 != 0)) { - printf("Error: Wrong number of vertices in file %s.\n", infilename); - delete plist; - return false; - } - numberofpoints = nverts; - pointlist = new REAL[nverts * 3]; - for (i = 0; i < nverts; i++) { - coord = (double *) (* plist)[i]; - iverts = i * 3; - pointlist[iverts] = (REAL) coord[0]; - pointlist[iverts + 1] = (REAL) coord[1]; - pointlist[iverts + 2] = (REAL) coord[2]; - } - - nfaces = (int) (nverts / 3); - numberoffacets = nfaces; - facetlist = new tetgenio::facet[nfaces]; - - // Default use '1' as the array starting index. - firstnumber = 1; - iverts = firstnumber; - for (i = 0; i < nfaces; i++) { - f = &facetlist[i]; - init(f); - // In .stl format, each facet has one polygon, no hole. - f->numberofpolygons = 1; - f->polygonlist = new tetgenio::polygon[1]; - p = &f->polygonlist[0]; - init(p); - // Each polygon has three vertices. - p->numberofvertices = 3; - p->vertexlist = new int[p->numberofvertices]; - p->vertexlist[0] = iverts; - p->vertexlist[1] = iverts + 1; - p->vertexlist[2] = iverts + 2; - iverts += 3; - } - - delete plist; - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// load_medit() Load a surface mesh 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(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_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(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); - 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(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); - } - } - // 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; - } - } - 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(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(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 * 2], - pointlist[i * 2 + 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(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"); - 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"); - } - - fclose(fout); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// save_faces() Save faces to a .face file. // -// // -// 'filename' is a string containing the file name without suffix. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenio::save_faces(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(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(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(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, 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; -} - -// -// End of class 'tetgenio' implementation -// - -static REAL PI = 3.14159265358979323846264338327950288419716939937510582; - -// -// Begin of class 'tetgenbehavior' implementation -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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; - insertaddpoints = 0; - diagnose = 0; - offcenter = 0; - conformdel = 0; - alpha1 = sqrt(2.0); - alpha2 = 1.0; - alpha3 = 0.6; - zeroindex = 0; - facesout = 0; - edgesout = 0; - neighout = 0; - voroout = 0; - meditview = 0; - gidview = 0; - geomview = 0; - optlevel = 3; - optpasses = 3; - order = 1; - nojettison = 0; - nobound = 0; - nonodewritten = 0; - noelewritten = 0; - nofacewritten = 0; - noiterationnum = 0; - nobisect = 0; - noflip = 0; - steiner = -1; - fliprepair = 1; - nomerge = 0; - docheck = 0; - quiet = 0; - verbose = 0; - useshelles = 0; - epsilon = 1.0e-8; - epsilon2 = 1.0e-5; - 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("Version 1.4.2 (April 16, 2007).\n"); - printf("\n"); - printf("Copyright (C) 2002 - 2007\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 (quality == 4) { - alpha2 = (REAL) strtod(workstring, (char **) NULL); - } else if (quality == 5) { - alpha1 = (REAL) strtod(workstring, (char **) NULL); - } - } - } else if (argv[i][j] == 'm') { - metric++; - 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 (metric == 1) { - alpha1 = (REAL) strtod(workstring, (char **) NULL); - } else if (metric == 2) { - alpha2 = (REAL) strtod(workstring, (char **) NULL); - } - } - } else if (argv[i][j] == 'a') { - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - fixedvolume = 1; - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || - (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - maxvolume = (REAL) strtod(workstring, (char **) NULL); - } else { - varvolume = 1; - } - } else if (argv[i][j] == 'A') { - regionattrib++; - } 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; - } else if (argv[i][j] == 'F') { - nofacewritten = 1; - } else if (argv[i][j] == 'I') { - noiterationnum = 1; - } else if (argv[i][j] == 'o') { - if (argv[i][j + 1] == '2') { - j++; - order = 2; - } - } else if (argv[i][j] == 'S') { - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - 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'; - steiner = (int) strtol(workstring, (char **) NULL, 0); - } - } else if (argv[i][j] == 's') { - scount++; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || - (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - if (scount == 1) { - optlevel = (int) strtol(workstring, (char **) NULL, 0); - } else if (scount == 2) { - optpasses = (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] == 'X') { - fliprepair = 0; - } 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], ".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; -} - -// -// End of class 'tetgenbehavior' implementation -// - -// -// Begin of class 'tetgenmesh' implementation -// - -// -// Begin of class 'list', 'memorypool' and 'link' implementation -// - -// Following are predefined compare functions for primitive data types. -// These functions take two pointers of the corresponding date type, -// perform the comparation. Return -1, 0 or 1 indicating the default -// linear order of two operators. - -// Compare two 'integers'. -int tetgenmesh::compare_2_ints(const void* x, const void* y) { - if (* (int *) x < * (int *) y) { - return -1; - } else if (* (int *) x > * (int *) y) { - return 1; - } else { - return 0; - } -} - -// Compare two 'longs'. Note: in 64-bit machine the 'long' type is 64-bit -// (8-byte) where the 'int' only 32-bit (4-byte). -int tetgenmesh::compare_2_longs(const void* x, const void* y) { - if (* (long *) x < * (long *) y) { - return -1; - } else if (* (long *) x > * (long *) y) { - return 1; - } else { - return 0; - } -} - -// Compare two 'unsigned longs'. -int tetgenmesh::compare_2_unsignedlongs(const void* x, const void* y) { - if (* (unsigned long *) x < * (unsigned long *) y) { - return -1; - } else if (* (unsigned long *) x > * (unsigned long *) y) { - return 1; - } else { - return 0; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// set_compfunc() Determine the size of primitive data types and set the // -// corresponding predefined linear order functions. // -// // -// 'str' is a zero-end string indicating a primitive data type, like 'int', // -// 'long' or 'unsigned long'. Every string ending with a '*' is though as a // -// type of pointer and the type 'unsign long' is used for it. // -// // -// When the type of 'str' is determined, the size of this type (in byte) is // -// returned in 'itbytes', and the pointer of corresponding predefined linear // -// order functions is returned in 'pcomp'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::set_compfunc(char* str, int* itbytes, compfunc* pcomp) -{ - // First figure out whether it is a pointer or not. - if (str[strlen(str) - 1] == '*') { - *itbytes = sizeof(unsigned long); - *pcomp = &compare_2_unsignedlongs; - return; - } - // Then determine other types. - if (strcmp(str, "int") == 0) { - *itbytes = sizeof(int); - *pcomp = &compare_2_ints; - } else if (strcmp(str, "long") == 0) { - *itbytes = sizeof(long); - *pcomp = &compare_2_longs; - } else if (strcmp(str, "unsigned long") == 0) { - *itbytes = sizeof(unsigned long); - *pcomp = &compare_2_unsignedlongs; - } else { - // It is an unknown type. - printf("Error in set_compfunc(): unknown type %s.\n", str); - terminatetetgen(1); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// listinit() Initialize a list for storing a data type. // -// // -// Determine the size of each item, set the maximum size allocated at onece, // -// set the expand size in case the list is full, and set the linear order // -// function if it is provided (default is NULL). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::list:: -listinit(int itbytes, compfunc pcomp, int mitems,int exsize) -{ -#ifdef SELF_CHECK - assert(itbytes > 0 && mitems > 0 && exsize > 0); -#endif - itembytes = itbytes; - comp = pcomp; - maxitems = mitems; - expandsize = exsize; - base = (char *) malloc(maxitems * itembytes); - if (base == (char *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - items = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// append() Add a new item at the end of the list. // -// // -// A new space at the end of this list will be allocated for storing the new // -// item. If the memory is not sufficient, reallocation will be performed. If // -// 'appitem' is not NULL, the contents of this pointer will be copied to the // -// new allocated space. Returns the pointer to the new allocated space. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void* tetgenmesh::list::append(void *appitem) -{ - // Do we have enough space? - if (items == maxitems) { - char* newbase = (char *) realloc(base, (maxitems + expandsize) * - itembytes); - if (newbase == (char *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - base = newbase; - maxitems += expandsize; - } - if (appitem != (void *) NULL) { - memcpy(base + items * itembytes, appitem, itembytes); - } - items++; - return (void *) (base + (items - 1) * itembytes); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insert() Insert an item before 'pos' (range from 0 to items - 1). // -// // -// A new space will be inserted at the position 'pos', that is, items lie // -// after pos (including the item at pos) will be moved one space downwords. // -// If 'insitem' is not NULL, its contents will be copied into the new // -// inserted space. Return a pointer to the new inserted space. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void* tetgenmesh::list::insert(int pos, void* insitem) -{ - if (pos >= items) { - return append(insitem); - } - // Do we have enough space. - if (items == maxitems) { - char* newbase = (char *) realloc(base, (maxitems + expandsize) * - itembytes); - if (newbase == (char *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - base = newbase; - maxitems += expandsize; - } - // Do block move. - memmove(base + (pos + 1) * itembytes, // dest - base + pos * itembytes, // src - (items - pos) * itembytes); // size in bytes - // Insert the item. - if (insitem != (void *) NULL) { - memcpy(base + pos * itembytes, insitem, itembytes); - } - items++; - return (void *) (base + pos * itembytes); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// del() Delete an item at 'pos' (range from 0 to items - 1). // -// // -// The space at 'pos' will be overlapped by other item. If 'order' is 1, the // -// remaining items of the list have the same order as usual, i.e., items lie // -// after pos will be moved one space upwords. If 'order' is 0, the last item // -// of the list will be moved up to pos. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::list::del(int pos, int order) -{ - // If 'pos' is the last item of the list, nothing need to do. - if (pos >= 0 && pos < items - 1) { - if (order == 1) { - // Do block move. - memmove(base + pos * itembytes, // dest - base + (pos + 1) * itembytes, // src - (items - pos - 1) * itembytes); - } else { - // Use the last item to overlap the del item. - memcpy(base + pos * itembytes, // item at pos - base + (items - 1) * itembytes, // item at last - itembytes); - } - } - if (items > 0) { - items--; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// hasitem() Search in this list to find if 'checkitem' exists. // -// // -// This routine assumes that a linear order function has been set. It loops // -// through the entire list, compares each item to 'checkitem'. If it exists, // -// return its position (between 0 to items - 1), otherwise, return -1. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::list::hasitem(void* checkitem) -{ - int i; - - for (i = 0; i < items; i++) { - if (comp != (compfunc) NULL) { - if ((* comp)((void *)(base + i * itembytes), checkitem) == 0) { - return i; - } - } - } - return -1; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// sort() Sort the items with respect to a linear order function. // -// // -// Uses QuickSort routines (qsort) of the standard C/C++ library (stdlib.h). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::list::sort() -{ - qsort((void *) base, (size_t) items, (size_t) itembytes, comp); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// memorypool() The constructors of memorypool. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::memorypool::memorypool() -{ - firstblock = nowblock = (void **) NULL; - nextitem = (void *) NULL; - deaditemstack = (void *) NULL; - pathblock = (void **) NULL; - pathitem = (void *) NULL; - itemwordtype = POINTER; - alignbytes = 0; - itembytes = itemwords = 0; - itemsperblock = 0; - items = maxitems = 0l; - unallocateditems = 0; - pathitemsleft = 0; -} - -tetgenmesh::memorypool:: -memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment) -{ - poolinit(bytecount, itemcount, wtype, alignment); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// ~memorypool() Free to the operating system all memory taken by a pool. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::memorypool::~memorypool() -{ - while (firstblock != (void **) NULL) { - nowblock = (void **) *(firstblock); - free(firstblock); - firstblock = nowblock; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// poolinit() Initialize a pool of memory for allocation of items. // -// // -// A `pool' is created whose records have size at least `bytecount'. Items // -// will be allocated in `itemcount'-item blocks. Each item is assumed to be // -// a collection of words, and either pointers or floating-point values are // -// assumed to be the "primary" word type. (The "primary" word type is used // -// to determine alignment of items.) If `alignment' isn't zero, all items // -// will be `alignment'-byte aligned in memory. `alignment' must be either a // -// multiple or a factor of the primary word size; powers of two are safe. // -// `alignment' is normally used to create a few unused bits at the bottom of // -// each item's pointer, in which information may be stored. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::memorypool:: -poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) -{ - int wordsize; - - // Initialize values in the pool. - itemwordtype = wtype; - wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL); - // Find the proper alignment, which must be at least as large as: - // - The parameter `alignment'. - // - The primary word type, to avoid unaligned accesses. - // - sizeof(void *), so the stack of dead items can be maintained - // without unaligned accesses. - if (alignment > wordsize) { - alignbytes = alignment; - } else { - alignbytes = wordsize; - } - if ((int) sizeof(void *) > alignbytes) { - alignbytes = (int) sizeof(void *); - } - itemwords = ((bytecount + alignbytes - 1) / alignbytes) - * (alignbytes / wordsize); - itembytes = itemwords * wordsize; - itemsperblock = itemcount; - - // Allocate a block of items. Space for `itemsperblock' items and one - // pointer (to point to the next block) are allocated, as well as space - // to ensure alignment of the items. - firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) - + alignbytes); - if (firstblock == (void **) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - // Set the next block pointer to NULL. - *(firstblock) = (void *) NULL; - restart(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// restart() Deallocate all items in this pool. // -// // -// The pool is returned to its starting state, except that no memory is // -// freed to the operating system. Rather, the previously allocated blocks // -// are ready to be reused. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::memorypool::restart() -{ - 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; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// linkinit() Initialize a link for storing items. // -// // -// The input parameters are the size of each item, a pointer of a linear // -// order function and the number of items allocating in one memory bulk. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::link::linkinit(int bytecount, compfunc pcomp, int itemcount) -{ -#ifdef SELF_CHECK - assert(bytecount > 0 && itemcount > 0); -#endif - // Remember the real size of each item. - linkitembytes = bytecount; - // Set the linear order function for this link. - comp = pcomp; - - // Call the constructor of 'memorypool' to initialize its variables. - // like: itembytes, itemwords, items, ... Each node has size - // bytecount + 2 * sizeof(void **), and total 'itemcount + 2' (because - // link has additional two nodes 'head' and 'tail'). - poolinit(bytecount + 2 * sizeof(void **), itemcount + 2, POINTER, 0); - - // Initial state of this link. - head = (void **) alloc(); - tail = (void **) alloc(); - *head = (void *) tail; - *(head + 1) = NULL; - *tail = NULL; - *(tail + 1) = (void *) head; - nextlinkitem = *head; - curpos = 1; - linkitems = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// clear() Deallocate all nodes in this link. // -// // -// The link 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::link::clear() -{ - // Reset the pool. - restart(); - - // Initial state of this link. - head = (void **) alloc(); - tail = (void **) alloc(); - *head = (void *) tail; - *(head + 1) = NULL; - *tail = NULL; - *(tail + 1) = (void *) head; - nextlinkitem = *head; - curpos = 1; - linkitems = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// move() Causes 'nextlinkitem' to traverse the specified number of nodes,// -// updates 'curpos' to be the node to which 'nextlinkitem' points. // -// // -// 'numberofnodes' is a number indicating how many nodes need be traversed // -// (not counter the current node) need be traversed. It may be positive(move // -// forward) or negative (move backward). Return TRUE if it is successful. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::link::move(int numberofnodes) -{ - void **nownode; - int i; - - nownode = (void **) nextlinkitem; - if (numberofnodes > 0) { - // Move forward. - i = 0; - while ((i < numberofnodes) && *nownode) { - nownode = (void **) *nownode; - i++; - } - if (*nownode == NULL) return false; - nextlinkitem = (void *) nownode; - curpos += numberofnodes; - } else if (numberofnodes < 0) { - // Move backward. - i = 0; - numberofnodes = -numberofnodes; - while ((i < numberofnodes) && *(nownode + 1)) { - nownode = (void **) *(nownode + 1); - i++; - } - if (*(nownode + 1) == NULL) return false; - nextlinkitem = (void *) nownode; - curpos -= numberofnodes; - } - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// locate() Locates the node at the specified position. // -// // -// The number 'pos' (between 1 and 'linkitems') indicates the location. This // -// routine first decides the shortest path traversing from 'curpos' to 'pos',// -// i.e., from head, tail or 'curpos'. Routine 'move()' is called to really // -// traverse the link. If success, 'nextlinkitem' points to the node, 'curpos'// -// and 'pos' are equal. Otherwise, return FALSE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::link::locate(int pos) -{ - int headdist, taildist, curdist; - int abscurdist, mindist; - - if (pos < 1 || pos > linkitems) return false; - - headdist = pos - 1; - taildist = linkitems - pos; - curdist = pos - curpos; - abscurdist = curdist >= 0 ? curdist : -curdist; - - if (headdist > taildist) { - if (taildist > abscurdist) { - mindist = curdist; - } else { - // taildist <= abs(curdist) - mindist = -taildist; - goend(); - } - } else { - // headdist <= taildist - if (headdist > abscurdist) { - mindist = curdist; - } else { - // headdist <= abs(curdist) - mindist = headdist; - rewind(); - } - } - - return move(mindist); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// add() Add a node at the end of this link. // -// // -// A new node is appended to the end of the link. If 'newitem' is not NULL, // -// its conents will be copied to the data slot of the new node. Returns the // -// pointer to the newest added node. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void* tetgenmesh::link::add(void* newitem) -{ - void **newnode = tail; - if (newitem != (void *) NULL) { - memcpy((void *)(newnode + 2), newitem, linkitembytes); - } - tail = (void **) alloc(); - *tail = NULL; - *newnode = (void*) tail; - *(tail + 1) = (void*) newnode; - linkitems++; - return (void *)(newnode + 2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insert() Inserts a node before the specified position. // -// // -// 'pos' (between 1 and 'linkitems') indicates the inserting position. This // -// routine inserts a new node before the node of 'pos'. If 'newitem' is not // -// NULL, its conents will be copied into the data slot of the new node. If // -// 'pos' is larger than 'linkitems', it is equal as 'add()'. A pointer to // -// the newest inserted item is returned. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void* tetgenmesh::link::insert(int pos, void* insitem) -{ - if (!locate(pos)) { - return add(insitem); - } - - void **nownode = (void **) nextlinkitem; - - // Insert a node before 'nownode'. - void **newnode = (void **) alloc(); - if (insitem != (void *) NULL) { - memcpy((void *)(newnode + 2), insitem, linkitembytes); - } - - *(void **)(*(nownode + 1)) = (void *) newnode; - *newnode = (void *) nownode; - *(newnode + 1) = *(nownode + 1); - *(nownode + 1) = (void *) newnode; - - linkitems++; - - nextlinkitem = (void *) newnode; - return (void *)(newnode + 2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// del() Delete a node. // -// // -// Returns a pointer of the deleted data. If you try to delete a non-existed // -// node (e.g. link is empty or a wrong index is given) return NULL. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void* tetgenmesh::link::deletenode(void** deadnode) -{ - void **nextnode = (void **) *deadnode; - void **prevnode = (void **) *(deadnode + 1); - *prevnode = (void *) nextnode; - *(nextnode + 1) = (void *) prevnode; - - dealloc((void *) deadnode); - linkitems--; - - nextlinkitem = (void *) nextnode; - return (void *)(deadnode + 2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// del() Delete a node at the specified position. // -// // -// 'pos' between 1 and 'linkitems'. Returns a pointer of the deleted data. // -// If you try to delete a non-existed node (e.g. link is empty or a wrong // -// index is given) return NULL. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void* tetgenmesh::link::del(int pos) -{ - if (!locate(pos) || (linkitems == 0)) { - return (void *) NULL; - } - return deletenode((void **) nextlinkitem); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getitem() The link traversal routine. // -// // -// Returns the node to which 'nextlinkitem' points. Returns a 'NULL' if the // -// end of the link is reaching. Both 'nextlinkitem' and 'curpos' will be // -// updated after this operation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void* tetgenmesh::link::getitem() -{ - if (nextlinkitem == (void *) tail) return NULL; - void **nownode = (void **) nextlinkitem; - nextlinkitem = *nownode; - curpos += 1; - return (void *)(nownode + 2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getnitem() Returns the node at a specified position. // -// // -// 'pos' between 1 and 'linkitems'. After this operation, 'nextlinkitem' and // -// 'curpos' will be updated to indicate this node. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void* tetgenmesh::link::getnitem(int pos) -{ - if (!locate(pos)) return NULL; - return (void *)((void **) nextlinkitem + 2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// hasitem() Search in this link to find if 'checkitem' exists. // -// // -// If 'checkitem' exists, return its position (between 1 to 'linkitems'), // -// otherwise, return -1. This routine requires the linear order function has // -// been set. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::link::hasitem(void* checkitem) -{ - void *pathitem; - int count; - - rewind(); - pathitem = getitem(); - count = 0; - while (pathitem) { - count ++; - if (comp) { - if ((* comp)(pathitem, checkitem) == 0) { - return count; - } - } - pathitem = getitem(); - } - return -1; -} - -// -// End of class 'list', 'memorypool' and 'link' implementation -// - -// -// Begin of mesh manipulation primitives -// - -// -// Begin of tables initialization. -// - -// For enumerating three edges of a triangle. - -int tetgenmesh::plus1mod3[3] = {1, 2, 0}; -int tetgenmesh::minus1mod3[3] = {2, 0, 1}; - -// Table 've' takes an edge version as input, returns the next edge version -// in the same edge ring. - -int tetgenmesh::ve[6] = { 2, 5, 4, 1, 0, 3 }; - -// Tables 'vo', 'vd' and 'va' take an edge version, return the positions of -// the origin, destination and apex in the triangle. - -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 }; - -// The following tables are for tetrahedron primitives (operate on trifaces). - -// For 'org()', 'dest()' and 'apex()'. Use 'loc' as the first index and -// 'ver' as the second index. - -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} -}; - -// For oppo() primitives, use 'loc' as the index. - -int tetgenmesh::loc2oppo[4] = { 3, 2, 0, 1 }; - -// For fnext() primitive. Use 'loc' as the first index and 'ver' as the -// second index. Returns a new 'loc' and new 'ver' in an array. (It is -// only valid for edge version equals one of {0, 2, 4}.) - -int tetgenmesh::locver2nextf[4][6][2] = { - { {1, 5}, {-1, -1}, {2, 5}, {-1, -1}, {3, 5}, {-1, -1} }, - { {3, 3}, {-1, -1}, {2, 1}, {-1, -1}, {0, 1}, {-1, -1} }, - { {1, 3}, {-1, -1}, {3, 1}, {-1, -1}, {0, 3}, {-1, -1} }, - { {2, 3}, {-1, -1}, {1, 1}, {-1, -1}, {0, 5}, {-1, -1} } -}; - -// The edge number (from 0 to 5) of a tet is defined as follows: -// 0 - (v0, v1), 1 - (v1, v2), 2 - (v2, v0) -// 3 - (v3, v0), 4 - (v3, v1), 5 - (v3, v2). - -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 -> v1 - {1, 0}, // 3 v0 -> v3 - {1, 2}, // 4 v1 -> v3 - {2, 2} // 5 v2 -> v3 -}; - -// -// End of tables initialization. -// - -// Some macros for convenience - -#define Div2 >> 1 -#define Mod2 & 01 - -// NOTE: These bit operators should only be used in macros below. - -// Get orient(Range from 0 to 2) from face version(Range from 0 to 5). - -#define Orient(V) ((V) Div2) - -// Determine edge ring(0 or 1) from face version(Range from 0 to 5). - -#define EdgeRing(V) ((V) Mod2) - -// -// Begin of primitives for tetrahedra -// - -// Each tetrahedron contains four pointers to its neighboring tetrahedra, -// with face indices. To save memory, both information are kept in a -// single pointer. To make this possible, all tetrahedra are aligned to -// eight-byte boundaries, so that the last three bits of each pointer are -// zeros. A face index (in the range 0 to 3) is compressed into the last -// two bits of each pointer by the function 'encode()'. The function -// 'decode()' decodes a pointer, extracting a face index and a pointer to -// the beginning of a tetrahedron. - -inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { - t.loc = (int) ((unsigned long) (ptr) & (unsigned long) 3l); - t.tet = (tetrahedron *) ((unsigned long) (ptr) & ~(unsigned long) 7l); -} - -inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { - return (tetrahedron) ((unsigned long) t.tet | (unsigned long) t.loc); -} - -// sym() finds the abutting tetrahedron on the same face. - -inline void tetgenmesh::sym(triface& t1, triface& t2) { - tetrahedron ptr = t1.tet[t1.loc]; - decode(ptr, t2); -} - -inline void tetgenmesh::symself(triface& t) { - tetrahedron ptr = t.tet[t.loc]; - decode(ptr, t); -} - -// Bond two tetrahedra together at their faces. - -inline void tetgenmesh::bond(triface& t1, triface& t2) { - t1.tet[t1.loc] = encode(t2); - t2.tet[t2.loc] = encode(t1); -} - -// Dissolve a bond (from one side). Note that the other tetrahedron will -// still think it is connected to this tetrahedron. Usually, however, -// the other tetrahedron is being deleted entirely, or bonded to another -// tetrahedron, so it doesn't matter. - -inline void tetgenmesh::dissolve(triface& t) { - t.tet[t.loc] = (tetrahedron) dummytet; -} - -// These primitives determine or set the origin, destination, apex or -// opposition of a tetrahedron with respect to 'loc' and 'ver'. - -inline tetgenmesh::point tetgenmesh::org(triface& t) { - return (point) t.tet[locver2org[t.loc][t.ver] + 4]; -} - -inline tetgenmesh::point tetgenmesh::dest(triface& t) { - return (point) t.tet[locver2dest[t.loc][t.ver] + 4]; -} - -inline tetgenmesh::point tetgenmesh::apex(triface& t) { - return (point) t.tet[locver2apex[t.loc][t.ver] + 4]; -} - -inline tetgenmesh::point tetgenmesh::oppo(triface& t) { - return (point) t.tet[loc2oppo[t.loc] + 4]; -} - -inline void tetgenmesh::setorg(triface& t, point pointptr) { - t.tet[locver2org[t.loc][t.ver] + 4] = (tetrahedron) pointptr; -} - -inline void tetgenmesh::setdest(triface& t, point pointptr) { - t.tet[locver2dest[t.loc][t.ver] + 4] = (tetrahedron) pointptr; -} - -inline void tetgenmesh::setapex(triface& t, point pointptr) { - t.tet[locver2apex[t.loc][t.ver] + 4] = (tetrahedron) pointptr; -} - -inline void tetgenmesh::setoppo(triface& t, point pointptr) { - t.tet[loc2oppo[t.loc] + 4] = (tetrahedron) pointptr; -} - -// These primitives were drived from Mucke's triangle-edge data structure -// to change face-edge relation in a tetrahedron (esym, enext and enext2) -// or between two tetrahedra (fnext). - -// If e0 = e(i, j), e1 = e(j, i), that is e0 and e1 are the two directions -// of the same undirected edge of a face. e0.sym() = e1 and vice versa. - -inline void tetgenmesh::esym(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = t1.ver + (EdgeRing(t1.ver) ? -1 : 1); -} - -inline void tetgenmesh::esymself(triface& t) { - t.ver += (EdgeRing(t.ver) ? -1 : 1); -} - -// If e0 and e1 are both in the same edge ring of a face, e1 = e0.enext(). - -inline void tetgenmesh::enext(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = ve[t1.ver]; -} - -inline void tetgenmesh::enextself(triface& t) { - t.ver = ve[t.ver]; -} - -// enext2() is equal to e2 = e0.enext().enext() - -inline void tetgenmesh::enext2(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.loc = t1.loc; - t2.ver = ve[ve[t1.ver]]; -} - -inline void tetgenmesh::enext2self(triface& t) { - t.ver = ve[ve[t.ver]]; -} - -// If f0 and f1 are both in the same face ring of a face, f1 = f0.fnext(). -// If f1 exists, return true. Otherwise, return false, i.e., f0 is a -// boundary or hull face. - -inline bool tetgenmesh::fnext(triface& t1, triface& t2) -{ - // Get the next face. - t2.loc = locver2nextf[t1.loc][t1.ver][0]; - // Is the next face in the same tet? - if (t2.loc != -1) { - // It's in the same tet. Get the edge version. - t2.ver = locver2nextf[t1.loc][t1.ver][1]; - t2.tet = t1.tet; - } else { - // The next face is in the neigbhour of 't1'. - sym(t1, t2); - if (t2.tet != dummytet) { - // Find the corresponding edge in t2. - point torg; - int tloc, tver, i; - t2.ver = 0; - torg = org(t1); - for (i = 0; (i < 3) && (org(t2) != torg); i++) { - enextself(t2); - } - // Go to the next face in t2. - tloc = t2.loc; - tver = t2.ver; - t2.loc = locver2nextf[tloc][tver][0]; - t2.ver = locver2nextf[tloc][tver][1]; - } - } - return t2.tet != dummytet; -} - -inline bool tetgenmesh::fnextself(triface& t1) -{ - triface t2; - - // Get the next face. - t2.loc = locver2nextf[t1.loc][t1.ver][0]; - // Is the next face in the same tet? - if (t2.loc != -1) { - // It's in the same tet. Get the edge version. - t2.ver = locver2nextf[t1.loc][t1.ver][1]; - t1.loc = t2.loc; - t1.ver = t2.ver; - } else { - // The next face is in the neigbhour of 't1'. - sym(t1, t2); - if (t2.tet != dummytet) { - // Find the corresponding edge in t2. - point torg; - int i; - t2.ver = 0; - torg = org(t1); - for (i = 0; (i < 3) && (org(t2) != torg); i++) { - enextself(t2); - } - t1.loc = locver2nextf[t2.loc][t2.ver][0]; - t1.ver = locver2nextf[t2.loc][t2.ver][1]; - t1.tet = t2.tet; - } - } - return t2.tet != dummytet; -} - -// enextfnext() and enext2fnext() are combination primitives of enext(), -// enext2() and fnext(). - -inline void tetgenmesh::enextfnext(triface& t1, triface& t2) { - enext(t1, t2); - fnextself(t2); -} - -inline void tetgenmesh::enextfnextself(triface& t) { - enextself(t); - fnextself(t); -} - -inline void tetgenmesh::enext2fnext(triface& t1, triface& t2) { - enext2(t1, t2); - fnextself(t2); -} - -inline void tetgenmesh::enext2fnextself(triface& t) { - enext2self(t); - fnextself(t); -} - -// Primitives to infect or cure a tetrahedron with the virus. The last -// third bit of the pointer is marked for infection. These rely on the -// assumption that all tetrahedron are aligned to eight-byte boundaries. - -inline void tetgenmesh::infect(triface& t) { - t.tet[0] = (tetrahedron) ((unsigned long) t.tet[0] | (unsigned long) 4l); -} - -inline void tetgenmesh::uninfect(triface& t) { - t.tet[0] = (tetrahedron) ((unsigned long) t.tet[0] & ~ (unsigned long) 4l); -} - -// Test a tetrahedron for viral infection. - -inline bool tetgenmesh::infected(triface& t) { - return (((unsigned long) t.tet[0] & (unsigned long) 4l) != 0); -} - -// Check or set a tetrahedron's attributes. - -inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) { - return ((REAL *) (ptr))[elemattribindex + attnum]; -} - -inline void tetgenmesh:: -setelemattribute(tetrahedron* ptr, int attnum, REAL value){ - ((REAL *) (ptr))[elemattribindex + attnum] = value; -} - -// Check or set a tetrahedron's maximum volume bound. - -inline REAL tetgenmesh::volumebound(tetrahedron* ptr) { - return ((REAL *) (ptr))[volumeboundindex]; -} - -inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) { - ((REAL *) (ptr))[volumeboundindex] = value; -} - -// -// End of primitives for tetrahedra -// - -// -// Begin of primitives for subfaces/subsegments -// - -// Each subface contains three pointers to its neighboring subfaces, with -// edge versions. To save memory, both information are kept in a single -// pointer. To make this possible, all subfaces are aligned to eight-byte -// boundaries, so that the last three bits of each pointer are zeros. An -// edge version (in the range 0 to 5) is compressed into the last three -// bits of each pointer by 'sencode()'. 'sdecode()' decodes a pointer, -// extracting an edge version and a pointer to the beginning of a subface. - -inline void tetgenmesh::sdecode(shellface sptr, face& s) { - s.shver = (int) ((unsigned long) (sptr) & (unsigned long) 7l); - s.sh = (shellface *) ((unsigned long) (sptr) & ~ (unsigned long) 7l); -} - -inline tetgenmesh::shellface tetgenmesh::sencode(face& s) { - return (shellface) ((unsigned long) s.sh | (unsigned long) s.shver); -} - -// spivot() finds the other subface (from this subface) that shares the -// same edge. - -inline void tetgenmesh::spivot(face& s1, face& s2) { - shellface sptr = s1.sh[Orient(s1.shver)]; - sdecode(sptr, s2); -} - -inline void tetgenmesh::spivotself(face& s) { - shellface sptr = s.sh[Orient(s.shver)]; - sdecode(sptr, s); -} - -// sbond() bonds two subfaces together, i.e., after bonding, both faces -// are pointing to each other. - -inline void tetgenmesh::sbond(face& s1, face& s2) { - s1.sh[Orient(s1.shver)] = sencode(s2); - s2.sh[Orient(s2.shver)] = sencode(s1); -} - -// sbond1() only bonds s2 to s1, i.e., after bonding, s1 is pointing to s2, -// but s2 is not pointing to s1. - -inline void tetgenmesh::sbond1(face& s1, face& s2) { - s1.sh[Orient(s1.shver)] = sencode(s2); -} - -// Dissolve a subface bond (from one side). Note that the other subface -// will still think it's connected to this subface. - -inline void tetgenmesh::sdissolve(face& s) { - s.sh[Orient(s.shver)] = (shellface) dummysh; -} - -// These primitives determine or set the origin, destination, or apex -// of a subface with respect to the edge version. - -inline tetgenmesh::point tetgenmesh::sorg(face& s) { - return (point) s.sh[3 + vo[s.shver]]; -} - -inline tetgenmesh::point tetgenmesh::sdest(face& s) { - return (point) s.sh[3 + vd[s.shver]]; -} - -inline tetgenmesh::point tetgenmesh::sapex(face& s) { - return (point) s.sh[3 + va[s.shver]]; -} - -inline void tetgenmesh::setsorg(face& s, point pointptr) { - s.sh[3 + vo[s.shver]] = (shellface) pointptr; -} - -inline void tetgenmesh::setsdest(face& s, point pointptr) { - s.sh[3 + vd[s.shver]] = (shellface) pointptr; -} - -inline void tetgenmesh::setsapex(face& s, point pointptr) { - s.sh[3 + va[s.shver]] = (shellface) pointptr; -} - -// These primitives were drived from Mucke[2]'s triangle-edge data structure -// to change face-edge relation in a subface (sesym, senext and senext2). - -inline void tetgenmesh::sesym(face& s1, face& s2) { - s2.sh = s1.sh; - s2.shver = s1.shver + (EdgeRing(s1.shver) ? -1 : 1); -} - -inline void tetgenmesh::sesymself(face& s) { - s.shver += (EdgeRing(s.shver) ? -1 : 1); -} - -inline void tetgenmesh::senext(face& s1, face& s2) { - s2.sh = s1.sh; - s2.shver = ve[s1.shver]; -} - -inline void tetgenmesh::senextself(face& s) { - s.shver = ve[s.shver]; -} - -inline void tetgenmesh::senext2(face& s1, face& s2) { - s2.sh = s1.sh; - s2.shver = ve[ve[s1.shver]]; -} - -inline void tetgenmesh::senext2self(face& s) { - s.shver = ve[ve[s.shver]]; -} - -// If f0 and f1 are both in the same face ring, then f1 = f0.fnext(), - -inline void tetgenmesh::sfnext(face& s1, face& s2) { - getnextsface(&s1, &s2); -} - -inline void tetgenmesh::sfnextself(face& s) { - getnextsface(&s, NULL); -} - -// These primitives read or set a pointer of the badface structure. The -// pointer is stored sh[11]. - -inline tetgenmesh::badface* tetgenmesh::shell2badface(face& s) { - return (badface*) s.sh[11]; -} - -inline void tetgenmesh::setshell2badface(face& s, badface* value) { - s.sh[11] = (shellface) value; -} - -// Check or set a subface's maximum area bound. - -inline REAL tetgenmesh::areabound(face& s) { - return ((REAL *) (s.sh))[areaboundindex]; -} - -inline void tetgenmesh::setareabound(face& s, REAL value) { - ((REAL *) (s.sh))[areaboundindex] = value; -} - -// These two primitives read or set a shell marker. Shell markers are used -// to hold user boundary information. - -inline int tetgenmesh::shellmark(face& s) { - return ((int *) (s.sh))[shmarkindex]; -} - -inline void tetgenmesh::setshellmark(face& s, int value) { - ((int *) (s.sh))[shmarkindex] = value; -} - -// These two primitives set or read the type of the subface or subsegment. - -inline enum tetgenmesh::shestype tetgenmesh::shelltype(face& s) { - return (enum shestype) ((int *) (s.sh))[shmarkindex + 1]; -} - -inline void tetgenmesh::setshelltype(face& s, enum shestype value) { - ((int *) (s.sh))[shmarkindex + 1] = (int) value; -} - -// These two primitives set or read the pbc group of the subface. - -inline int tetgenmesh::shellpbcgroup(face& s) { - return ((int *) (s.sh))[shmarkindex + 2]; -} - -inline void tetgenmesh::setshellpbcgroup(face& s, int value) { - ((int *) (s.sh))[shmarkindex + 2] = value; -} - -// Primitives to infect or cure a subface with the virus. These rely on the -// assumption that all tetrahedra are aligned to eight-byte boundaries. - -inline void tetgenmesh::sinfect(face& s) { - s.sh[6] = (shellface) ((unsigned long) s.sh[6] | (unsigned long) 4l); -} - -inline void tetgenmesh::suninfect(face& s) { - s.sh[6] = (shellface)((unsigned long) s.sh[6] & ~(unsigned long) 4l); -} - -// Test a subface for viral infection. - -inline bool tetgenmesh::sinfected(face& s) { - return (((unsigned long) s.sh[6] & (unsigned long) 4l) != 0); -} - -// -// End of primitives for subfaces/subsegments -// - -// -// Begin of primitives for interacting between tetrahedra and subfaces -// - -// tspivot() finds a subface abutting on this tetrahdera. - -inline void tetgenmesh::tspivot(triface& t, face& s) { - shellface sptr = (shellface) t.tet[8 + t.loc]; - sdecode(sptr, s); -} - -// stpivot() finds a tetrahedron abutting a subface. - -inline void tetgenmesh::stpivot(face& s, triface& t) { - tetrahedron ptr = (tetrahedron) s.sh[6 + EdgeRing(s.shver)]; - decode(ptr, t); -} - -// tsbond() bond a tetrahedron to a subface. - -inline void tetgenmesh::tsbond(triface& t, face& s) { - t.tet[8 + t.loc] = (tetrahedron) sencode(s); - s.sh[6 + EdgeRing(s.shver)] = (shellface) encode(t); -} - -// tsdissolve() dissolve a bond (from the tetrahedron side). - -inline void tetgenmesh::tsdissolve(triface& t) { - t.tet[8 + t.loc] = (tetrahedron) dummysh; -} - -// stdissolve() dissolve a bond (from the subface side). - -inline void tetgenmesh::stdissolve(face& s) { - s.sh[6 + EdgeRing(s.shver)] = (shellface) dummytet; -} - -// -// End of primitives for interacting between tetrahedra and subfaces -// - -// -// Begin of primitives for interacting between subfaces and subsegs -// - -// sspivot() finds a subsegment abutting a subface. - -inline void tetgenmesh::sspivot(face& s, face& edge) { - shellface sptr = (shellface) s.sh[8 + Orient(s.shver)]; - sdecode(sptr, edge); -} - -// ssbond() bond a subface to a subsegment. - -inline void tetgenmesh::ssbond(face& s, face& edge) { - s.sh[8 + Orient(s.shver)] = sencode(edge); - edge.sh[0] = sencode(s); -} - -// ssdisolve() dissolve a bond (from the subface side) - -inline void tetgenmesh::ssdissolve(face& s) { - s.sh[8 + Orient(s.shver)] = (shellface) dummysh; -} - -// -// End of primitives for interacting between subfaces and subsegs -// - -// -// Begin of primitives for interacting between tet and subsegs. -// - -inline void tetgenmesh::tsspivot1(triface& t, face& seg) -{ - shellface sptr = (shellface) t.tet[8 + locver2edge[t.loc][t.ver]]; - sdecode(sptr, seg); -} - -// Only bond/dissolve at tet's side, but not vice versa. - -inline void tetgenmesh::tssbond1(triface& t, face& seg) -{ - t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) sencode(seg); -} - -inline void tetgenmesh::tssdissolve1(triface& t) -{ - t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) dummysh; -} - -// -// End of primitives for interacting between tet and subsegs. -// - -// -// Begin of primitives for points -// - -inline int tetgenmesh::pointmark(point pt) { - return ((int *) (pt))[pointmarkindex]; -} - -inline void tetgenmesh::setpointmark(point pt, int value) { - ((int *) (pt))[pointmarkindex] = value; -} - -// These two primitives set and read the type of the point. - -inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) { - return (enum verttype) ((int *) (pt))[pointmarkindex + 1]; -} - -inline void tetgenmesh::setpointtype(point pt, enum verttype value) { - ((int *) (pt))[pointmarkindex + 1] = (int) value; -} - -// These following primitives set and read a pointer to a tetrahedron -// a subface/subsegment, a point, or a tet of background mesh. - -inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) { - return ((tetrahedron *) (pt))[point2simindex]; -} - -inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) { - ((tetrahedron *) (pt))[point2simindex] = value; -} - -inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) { - return (shellface) ((tetrahedron *) (pt))[point2simindex + 1]; -} - -inline void tetgenmesh::setpoint2sh(point pt, shellface value) { - ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value; -} - -inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { - return (point) ((tetrahedron *) (pt))[point2simindex + 2]; -} - -inline void tetgenmesh::setpoint2ppt(point pt, point value) { - ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; -} - -inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { - return ((tetrahedron *) (pt))[point2simindex + 3]; -} - -inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { - ((tetrahedron *) (pt))[point2simindex + 3] = value; -} - -// These primitives set and read a pointer to its pbc point. - -inline tetgenmesh::point tetgenmesh::point2pbcpt(point pt) { - return (point) ((tetrahedron *) (pt))[point2pbcptindex]; -} - -inline void tetgenmesh::setpoint2pbcpt(point pt, point value) { - ((tetrahedron *) (pt))[point2pbcptindex] = (tetrahedron) value; -} - -// -// End of primitives for points -// - -// -// Begin of advanced primitives -// - -// adjustedgering() adjusts the edge version so that it belongs to the -// indicated edge ring. The 'direction' only can be 0(CCW) or 1(CW). -// If the edge is not in the wanted edge ring, reverse it. - -inline void tetgenmesh::adjustedgering(triface& t, int direction) { - if (EdgeRing(t.ver) != direction) { - esymself(t); - } -} - -inline void tetgenmesh::adjustedgering(face& s, int direction) { - if (EdgeRing(s.shver) != direction) { - sesymself(s); - } -} - -// isdead() returns TRUE if the tetrahedron or subface has been dealloced. - -inline bool tetgenmesh::isdead(triface* t) { - if (t->tet == (tetrahedron *) NULL) return true; - else return t->tet[4] == (tetrahedron) NULL; -} - -inline bool tetgenmesh::isdead(face* s) { - if (s->sh == (shellface *) NULL) return true; - else return s->sh[3] == (shellface) NULL; -} - -// isfacehaspoint() returns TRUE if the 'testpoint' is one of the vertices -// of the tetface 't' subface 's'. - -inline bool tetgenmesh::isfacehaspoint(triface* t, point testpoint) { - return ((org(*t) == testpoint) || (dest(*t) == testpoint) || - (apex(*t) == testpoint)); -} - -inline bool tetgenmesh::isfacehaspoint(face* s, point testpoint) { - return (s->sh[3] == (shellface) testpoint) || - (s->sh[4] == (shellface) testpoint) || - (s->sh[5] == (shellface) testpoint); -} - -// isfacehasedge() returns TRUE if the edge (given by its two endpoints) is -// one of the three edges of the subface 's'. - -inline bool tetgenmesh::isfacehasedge(face* s, point tend1, point tend2) { - return (isfacehaspoint(s, tend1) && isfacehaspoint(s, tend2)); -} - -// issymexist() returns TRUE if the adjoining tetrahedron is not 'duumytet'. - -inline bool tetgenmesh::issymexist(triface* t) { - tetrahedron *ptr = (tetrahedron *) - ((unsigned long)(t->tet[t->loc]) & ~(unsigned long)7l); - return ptr != dummytet; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getnextsface() Finds the next subface in the face ring. // -// // -// For saving space in the data structure of subface, there only exists one // -// face ring around a segment (see programming manual). This routine imple- // -// ments the double face ring as desired in Muecke's data structure. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getnextsface(face* s1, face* s2) -{ - face neighsh, spinsh; - face testseg; - - sspivot(*s1, testseg); - if (testseg.sh != dummysh) { - testseg.shver = 0; - if (sorg(testseg) == sorg(*s1)) { - spivot(*s1, neighsh); - } else { - spinsh = *s1; - do { - neighsh = spinsh; - spivotself(spinsh); - } while (spinsh.sh != s1->sh); - } - } else { - spivot(*s1, neighsh); - } - if (sorg(neighsh) != sorg(*s1)) { - sesymself(neighsh); - } - if (s2 != (face *) NULL) { - *s2 = neighsh; - } else { - *s1 = neighsh; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tsspivot() Finds a subsegment abutting on a tetrahderon's edge. // -// // -// The edge is represented in the primary edge of 'checkedge'. If there is a // -// subsegment bonded at this edge, it is returned in handle 'checkseg', the // -// edge direction of 'checkseg' is conformed to 'checkedge'. If there isn't, // -// set 'checkseg.sh = dummysh' to indicate it is not a subsegment. // -// // -// To find whether an edge of a tetrahedron is a subsegment or not. First we // -// need find a subface around this edge to see if it contains a subsegment. // -// The reason is there is no direct connection between a tetrahedron and its // -// adjoining subsegments. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tsspivot(triface* checkedge, face* checkseg) -{ - triface spintet; - face parentsh; - point tapex; - int hitbdry; - - spintet = *checkedge; - tapex = apex(*checkedge); - hitbdry = 0; - do { - tspivot(spintet, parentsh); - // Does spintet have a (non-fake) subface attached? - if ((parentsh.sh != dummysh) && (sapex(parentsh) != NULL)) { - // Find a subface! Find the edge in it. - findedge(&parentsh, org(*checkedge), dest(*checkedge)); - sspivot(parentsh, *checkseg); - if (checkseg->sh != dummysh) { - // Find a subsegment! Correct its edge direction before return. - if (sorg(*checkseg) != org(*checkedge)) { - sesymself(*checkseg); - } - } - return; - } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(*checkedge, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } - } - } - } while ((apex(spintet) != tapex) && (hitbdry < 2)); - // Not find. - checkseg->sh = dummysh; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// sstpivot() Finds a tetrahedron abutting a subsegment. // -// // -// This is the inverse operation of 'tsspivot()'. One subsegment shared by // -// arbitrary number of tetrahedron, the returned tetrahedron is not unique. // -// The edge direction of the returned tetrahedron is conformed to the given // -// subsegment. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::sstpivot(face* checkseg, triface* retedge) -{ - face parentsh; - - // Get the subface which holds the subsegment. - sdecode(checkseg->sh[0], parentsh); -#ifdef SELF_CHECK - assert(parentsh.sh != dummysh); -#endif - // Get a tetraheron to which the subface attches. - stpivot(parentsh, *retedge); - if (retedge->tet == dummytet) { - sesymself(parentsh); - stpivot(parentsh, *retedge); -#ifdef SELF_CHECK - assert(retedge->tet != dummytet); -#endif - } - // Correct the edge direction before return. - findedge(retedge, sorg(*checkseg), sdest(*checkseg)); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// findorg() Finds a point in the given handle (tetrahedron or subface). // -// // -// If 'dorg' is a one of vertices of the given handle, set the origin of // -// this handle be that point and return TRUE. Otherwise, return FALSE and // -// 'tface' remains unchanged. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::findorg(triface* tface, point dorg) -{ - if (org(*tface) == dorg) { - return true; - } else { - if (dest(*tface) == dorg) { - enextself(*tface); - return true; - } else { - if (apex(*tface) == dorg) { - enext2self(*tface); - return true; - } else { - if (oppo(*tface) == dorg) { - // Keep 'tface' referring to the same tet after fnext(). - adjustedgering(*tface, CCW); - fnextself(*tface); - enext2self(*tface); - return true; - } - } - } - } - return false; -} - -bool tetgenmesh::findorg(face* sface, point dorg) -{ - if (sorg(*sface) == dorg) { - return true; - } else { - if (sdest(*sface) == dorg) { - senextself(*sface); - return true; - } else { - if (sapex(*sface) == dorg) { - senext2self(*sface); - return true; - } - } - } - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// findedge() Find an edge in the given handle (tetrahedron or subface). // -// // -// The edge is given in two points 'eorg' and 'edest'. It is assumed that // -// the edge must exist in the given handle (tetrahedron or subface). This // -// routine sets the right edge version for the input handle. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::findedge(triface* tface, point eorg, point edest) -{ - int i; - - for (i = 0; i < 3; i++) { - if (org(*tface) == eorg) { - if (dest(*tface) == edest) { - // Edge is found, return. - return; - } - } else { - if (org(*tface) == edest) { - if (dest(*tface) == eorg) { - // Edge is found, inverse the direction and return. - esymself(*tface); - return; - } - } - } - enextself(*tface); - } - // It should never be here. - printf("Internalerror in findedge(): Unable to find an edge in tet.\n"); - internalerror(); -} - -void tetgenmesh::findedge(face* sface, point eorg, point edest) -{ - int i; - - for (i = 0; i < 3; i++) { - if (sorg(*sface) == eorg) { - if (sdest(*sface) == edest) { - // Edge is found, return. - return; - } - } else { - if (sorg(*sface) == edest) { - if (sdest(*sface) == eorg) { - // Edge is found, inverse the direction and return. - sesymself(*sface); - return; - } - } - } - senextself(*sface); - } - printf("Internalerror in findedge(): Unable to find an edge in subface.\n"); - internalerror(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// findface() Find the face has the given origin, destination and apex. // -// // -// On input, 'fface' is a handle which may contain the three corners or may // -// not or may be dead. On return, it represents exactly the face with the // -// given origin, destination and apex. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::findface(triface *fface, point forg, point fdest, point fapex) -{ - triface spintet; - enum finddirectionresult collinear; - int hitbdry; - - if (!isdead(fface)) { - // First check the easiest case, that 'fface' is just the right one. - if (org(*fface) == forg && dest(*fface) == fdest && - apex(*fface) == fapex) return; - } else { - // The input handle is dead, use the 'recenttet' if it is alive. - if (!isdead(&recenttet)) *fface = recenttet; - } - - if (!isdead(fface)) { - if (!findorg(fface, forg)) { - // 'forg' is not a corner of 'fface', locate it. - preciselocate(forg, fface, tetrahedrons->items); - } - // It is possible that forg is not found in a non-convex mesh. - if (org(*fface) == forg) { - collinear = finddirection(fface, fdest, tetrahedrons->items); - if (collinear == RIGHTCOLLINEAR) { - // fdest is just the destination. - } else if (collinear == LEFTCOLLINEAR) { - enext2self(*fface); - esymself(*fface); - } else if (collinear == TOPCOLLINEAR) { - fnextself(*fface); - enext2self(*fface); - esymself(*fface); - } - } - // It is possible taht fdest is not found in a non-convex mesh. - if ((org(*fface) == forg) && (dest(*fface) == fdest)) { - // Find the apex of 'fapex'. - spintet = *fface; - hitbdry = 0; - do { - if (apex(spintet) == fapex) { - // We have done. Be careful the edge direction of 'spintet', - // it may reversed because of hitting boundary once. - if (org(spintet) != org(*fface)) { - esymself(spintet); - } - *fface = spintet; - return; - } - if (!fnextself(spintet)) { - hitbdry ++; - if (hitbdry < 2) { - esym(*fface, spintet); - if (!fnextself(spintet)) { - hitbdry ++; - } - } - } - } while (hitbdry < 2 && apex(spintet) != apex(*fface)); - // It is possible that fapex is not found in a non-convex mesh. - } - } - - if (isdead(fface) || (org(*fface) != forg) || (dest(*fface) != fdest) || - (apex(*fface) != fapex)) { - // Too bad, the input handle is useless. We have to find a handle - // for 'fface' contains the 'forg' and 'fdest'. Here a brute force - // search is performed. - if (b->verbose > 1) { - printf("Warning in findface(): Perform a brute-force searching.\n"); - } - enum verttype forgty, fdestty, fapexty; - int share, i; - forgty = pointtype(forg); - fdestty = pointtype(fdest); - fapexty = pointtype(fapex); - setpointtype(forg, DEADVERTEX); - setpointtype(fdest, DEADVERTEX); - setpointtype(fapex, DEADVERTEX); - tetrahedrons->traversalinit(); - fface->tet = tetrahedrontraverse(); - while (fface->tet != (tetrahedron *) NULL) { - share = 0; - for (i = 0; i < 4; i++) { - if (pointtype((point) fface->tet[4 + i]) == DEADVERTEX) share ++; - } - if (share == 3) { - // Found! Set the correct face and desired corners. - if (pointtype((point) fface->tet[4]) != DEADVERTEX) { - fface->loc = 2; - } else if (pointtype((point) fface->tet[5]) != DEADVERTEX) { - fface->loc = 3; - } else if (pointtype((point) fface->tet[6]) != DEADVERTEX) { - fface->loc = 1; - } else { // pointtype((point) fface->tet[7]) != DEADVERTEX - fface->loc = 0; - } - findedge(fface, forg, fdest); - break; - } - fface->tet = tetrahedrontraverse(); - } - setpointtype(forg, forgty); - setpointtype(fdest, fdestty); - setpointtype(fapex, fapexty); - if (fface->tet == (tetrahedron *) NULL) { - // It is impossible to reach here. - printf("Internal error: Fail to find the indicated face.\n"); - internalerror(); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getonextseg() Get the next SEGMENT counterclockwise with the same org. // -// // -// 's' is a subface. This routine reteuns the segment which is counterclock- // -// wise with the origin of s. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getonextseg(face* s, face* lseg) -{ - face checksh, checkseg; - point forg; - - forg = sorg(*s); - checksh = *s; - do { - // Go to the edge at forg's left side. - senext2self(checksh); - // Check if there is a segment attaching this edge. - sspivot(checksh, checkseg); - if (checkseg.sh != dummysh) break; - // No segment! Go to the neighbor of this subface. - spivotself(checksh); -#ifdef SELF_CHECK - // It should always meet a segment before come back. - assert(checksh.sh != s->sh); -#endif - if (sorg(checksh) != forg) { - sesymself(checksh); -#ifdef SELF_CHECK - assert(sorg(checksh) == forg); -#endif - } - } while (true); - if (sorg(checkseg) != forg) sesymself(checkseg); - *lseg = checkseg; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getseghasorg() Get the segment containing the given point. // -// // -// 'dorg' is an endpoint of a segment S. 'sseg' is a subsegment of S. This // -// routine search a subsegment (along sseg) of S containing dorg. On return, // -// 'sseg' contains 'dorg' as its origin. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getseghasorg(face* sseg, point dorg) -{ - face nextseg; - point checkpt; - - nextseg = *sseg; - checkpt = sorg(nextseg); - while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) { - // Search dorg along the original direction of sseg. - senext2self(nextseg); - spivotself(nextseg); - nextseg.shver = 0; - if (sdest(nextseg) != checkpt) sesymself(nextseg); - checkpt = sorg(nextseg); - } - if (checkpt == dorg) { - *sseg = nextseg; - return; - } - nextseg = *sseg; - checkpt = sdest(nextseg); - while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) { - // Search dorg along the destinational direction of sseg. - senextself(nextseg); - spivotself(nextseg); - nextseg.shver = 0; - if (sorg(nextseg) != checkpt) sesymself(nextseg); - checkpt = sdest(nextseg); - } - if (checkpt == dorg) { - sesym(nextseg, *sseg); - return; - } - // Should never be here. - printf("Internalerror in getseghasorg(): Unable to find the subseg.\n"); - internalerror(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsubsegfarorg() Get the origin of the parent segment of a subseg. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::getsubsegfarorg(face* sseg) -{ - face prevseg; - point checkpt; - - checkpt = sorg(*sseg); - senext2(*sseg, prevseg); - spivotself(prevseg); - // Search dorg along the original direction of sseg. - while (prevseg.sh != dummysh) { - prevseg.shver = 0; - if (sdest(prevseg) != checkpt) sesymself(prevseg); - checkpt = sorg(prevseg); - senext2self(prevseg); - spivotself(prevseg); - } - return checkpt; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsubsegfardest() Get the dest. of the parent segment of a subseg. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::getsubsegfardest(face* sseg) -{ - face nextseg; - point checkpt; - - checkpt = sdest(*sseg); - senext(*sseg, nextseg); - spivotself(nextseg); - // Search dorg along the destinational direction of sseg. - while (nextseg.sh != dummysh) { - nextseg.shver = 0; - if (sorg(nextseg) != checkpt) sesymself(nextseg); - checkpt = sdest(nextseg); - senextself(nextseg); - spivotself(nextseg); - } - return checkpt; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// printtet() Print out the details of a tetrahedron on screen. // -// // -// It's also used when the highest level of verbosity (`-VVV') is specified. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::printtet(triface* tface) -{ - triface tmpface, prtface; - point tmppt; - face tmpsh; - int facecount; - - printf("Tetra x%lx with loc(%i) and ver(%i):", - (unsigned long)(tface->tet), tface->loc, tface->ver); - if (infected(*tface)) { - printf(" (infected)"); - } - printf("\n"); - - tmpface = *tface; - facecount = 0; - while(facecount < 4) { - tmpface.loc = facecount; - sym(tmpface, prtface); - if(prtface.tet == dummytet) { - printf(" [%i] Outer space.\n", facecount); - } else { - printf(" [%i] x%lx loc(%i).", facecount, - (unsigned long)(prtface.tet), prtface.loc); - if (infected(prtface)) { - printf(" (infected)"); - } - printf("\n"); - } - facecount ++; - } - - tmppt = org(*tface); - if(tmppt == (point) NULL) { - printf(" Org [%i] NULL\n", locver2org[tface->loc][tface->ver]); - } else { - printf(" Org [%i] x%lx (%.12g,%.12g,%.12g) %d\n", - locver2org[tface->loc][tface->ver], (unsigned long)(tmppt), - tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); - } - tmppt = dest(*tface); - if(tmppt == (point) NULL) { - printf(" Dest[%i] NULL\n", locver2dest[tface->loc][tface->ver]); - } else { - printf(" Dest[%i] x%lx (%.12g,%.12g,%.12g) %d\n", - locver2dest[tface->loc][tface->ver], (unsigned long)(tmppt), - tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); - } - tmppt = apex(*tface); - if(tmppt == (point) NULL) { - printf(" Apex[%i] NULL\n", locver2apex[tface->loc][tface->ver]); - } else { - printf(" Apex[%i] x%lx (%.12g,%.12g,%.12g) %d\n", - locver2apex[tface->loc][tface->ver], (unsigned long)(tmppt), - tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); - } - tmppt = oppo(*tface); - if(tmppt == (point) NULL) { - printf(" Oppo[%i] NULL\n", loc2oppo[tface->loc]); - } else { - printf(" Oppo[%i] x%lx (%.12g,%.12g,%.12g) %d\n", - loc2oppo[tface->loc], (unsigned long)(tmppt), - tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); - } - - if (b->useshelles) { - tmpface = *tface; - facecount = 0; - while(facecount < 6) { - tmpface.loc = facecount; - tspivot(tmpface, tmpsh); - if(tmpsh.sh != dummysh) { - printf(" [%i] x%lx ID(%i) ", facecount, - (unsigned long)(tmpsh.sh), shellmark(tmpsh)); - if (sorg(tmpsh) == (point) NULL) { - printf("(fake)"); - } - printf("\n"); - } - facecount ++; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// printsh() Print out the details of a subface or subsegment on screen. // -// // -// It's also used when the highest level of verbosity (`-VVV') is specified. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::printsh(face* sface) -{ - face prtsh; - triface prttet; - point printpoint; - - if (sapex(*sface) != NULL) { - printf("subface x%lx, ver %d, mark %d:", - (unsigned long)(sface->sh), sface->shver, shellmark(*sface)); - } else { - printf("Subsegment x%lx, ver %d, mark %d:", - (unsigned long)(sface->sh), sface->shver, shellmark(*sface)); - } - if (sinfected(*sface)) { - printf(" (infected)"); - } - if (shell2badface(*sface)) { - printf(" (queued)"); - } - if (sapex(*sface) != NULL) { - if (shelltype(*sface) == SHARP) { - printf(" (sharp)"); - } - } else { - if (shelltype(*sface) == SHARP) { - printf(" (sharp)"); - } - } - if (checkpbcs) { - if (shellpbcgroup(*sface) >= 0) { - printf(" (pbc %d)", shellpbcgroup(*sface)); - } - } - printf("\n"); - - sdecode(sface->sh[0], prtsh); - if (prtsh.sh == dummysh) { - printf(" [0] = No shell\n"); - } else { - printf(" [0] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); - } - sdecode(sface->sh[1], prtsh); - if (prtsh.sh == dummysh) { - printf(" [1] = No shell\n"); - } else { - printf(" [1] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); - } - sdecode(sface->sh[2], prtsh); - if (prtsh.sh == dummysh) { - printf(" [2] = No shell\n"); - } else { - printf(" [2] = x%lx %d\n", (unsigned long)(prtsh.sh), prtsh.shver); - } - - printpoint = sorg(*sface); - if (printpoint == (point) NULL) - printf(" Org [%d] = NULL\n", vo[sface->shver]); - else - printf(" Org [%d] = x%lx (%.12g,%.12g,%.12g) %d\n", - vo[sface->shver], (unsigned long)(printpoint), printpoint[0], - printpoint[1], printpoint[2], pointmark(printpoint)); - printpoint = sdest(*sface); - if (printpoint == (point) NULL) - printf(" Dest[%d] = NULL\n", vd[sface->shver]); - else - printf(" Dest[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", - vd[sface->shver], (unsigned long)(printpoint), printpoint[0], - printpoint[1], printpoint[2], pointmark(printpoint)); - - if (sapex(*sface) != NULL) { - printpoint = sapex(*sface); - if (printpoint == (point) NULL) - printf(" Apex[%d] = NULL\n", va[sface->shver]); - else - printf(" Apex[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", - va[sface->shver], (unsigned long)(printpoint), printpoint[0], - printpoint[1], printpoint[2], pointmark(printpoint)); - - decode(sface->sh[6], prttet); - if (prttet.tet == dummytet) { - printf(" [6] = Outer space\n"); - } else { - printf(" [6] = x%lx %d\n", - (unsigned long)(prttet.tet), prttet.loc); - } - decode(sface->sh[7], prttet); - if (prttet.tet == dummytet) { - printf(" [7] = Outer space\n"); - } else { - printf(" [7] = x%lx %d\n", - (unsigned long)(prttet.tet), prttet.loc); - } - - sdecode(sface->sh[8], prtsh); - if (prtsh.sh == dummysh) { - printf(" [8] = No subsegment\n"); - } else { - printf(" [8] = x%lx %d\n", - (unsigned long)(prtsh.sh), prtsh.shver); - } - sdecode(sface->sh[9], prtsh); - if (prtsh.sh == dummysh) { - printf(" [9] = No subsegment\n"); - } else { - printf(" [9] = x%lx %d\n", - (unsigned long)(prtsh.sh), prtsh.shver); - } - sdecode(sface->sh[10], prtsh); - if (prtsh.sh == dummysh) { - printf(" [10]= No subsegment\n"); - } else { - printf(" [10]= x%lx %d\n", - (unsigned long)(prtsh.sh), prtsh.shver); - } - } -} - -// -// End of advanced primitives -// - -// -// End of mesh manipulation primitives -// - -// -// Begin of mesh items searching routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// makepoint2tetmap() Construct a mapping from points to tetrahedra. // -// // -// Traverses all the tetrahedra, provides each corner of each tetrahedron // -// with a pointer to that tetrahedera. Some pointers will be overwritten by // -// other pointers because each point may be a corner of several tetrahedra, // -// but in the end every point will point to a tetrahedron that contains it. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::makepoint2tetmap() -{ - triface tetloop; - point pointptr; - - if (b->verbose > 0) { - printf(" Constructing mapping from points to tetrahedra.\n"); - } - - // Initialize the point2tet field of each point. - points->traversalinit(); - pointptr = pointtraverse(); - while (pointptr != (point) NULL) { - setpoint2tet(pointptr, (tetrahedron) NULL); - pointptr = pointtraverse(); - } - - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Check all four points of the tetrahedron. - tetloop.loc = 0; - pointptr = org(tetloop); - setpoint2tet(pointptr, encode(tetloop)); - pointptr = dest(tetloop); - setpoint2tet(pointptr, encode(tetloop)); - pointptr = apex(tetloop); - setpoint2tet(pointptr, encode(tetloop)); - pointptr = oppo(tetloop); - setpoint2tet(pointptr, encode(tetloop)); - // Get the next tetrahedron in the list. - tetloop.tet = tetrahedrontraverse(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// makeindex2pointmap() Create a map from index to vertices. // -// // -// 'idx2verlist' returns the created map. Traverse all vertices, a pointer // -// to each vertex is set into the array. The pointer to the first vertex is // -// saved in 'idx2verlist[0]'. Don't forget to minus 'in->firstnumber' when // -// to get the vertex form its index. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::makeindex2pointmap(point*& idx2verlist) -{ - point pointloop; - int idx; - - if (b->verbose > 0) { - printf(" Constructing mapping from indices to points.\n"); - } - - idx2verlist = new point[points->items]; - - points->traversalinit(); - pointloop = pointtraverse(); - idx = 0; - while (pointloop != (point) NULL) { - idx2verlist[idx] = pointloop; - idx++; - pointloop = pointtraverse(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// makesegmentmap() Create a map from vertices (their indices) to // -// segments incident at the same vertices. // -// // -// Two arrays 'idx2seglist' and 'segsperverlist' together return the map. // -// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the // -// number of segments. idx2seglist contains row information and // -// segsperverlist contains all (non-zero) elements. The i-th entry of // -// idx2seglist is the starting position of i-th row's (non-zero) elements in // -// segsperverlist. The number of elements of i-th row is calculated by the // -// (i+1)-th entry minus i-th entry of idx2seglist. // -// // -// NOTE: These two arrays will be created inside this routine, don't forget // -// to free them after using. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::makesegmentmap(int*& idx2seglist, shellface**& segsperverlist) -{ - shellface *shloop; - int i, j, k; - - if (b->verbose > 0) { - printf(" Constructing mapping from points to segments.\n"); - } - - // Create and initialize 'idx2seglist'. - idx2seglist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2seglist[i] = 0; - - // Loop the set of segments once, counter the number of segments sharing - // each vertex. - subsegs->traversalinit(); - shloop = shellfacetraverse(subsegs); - while (shloop != (shellface *) NULL) { - // Increment the number of sharing segments for each endpoint. - for (i = 0; i < 2; i++) { - j = pointmark((point) shloop[3 + i]) - in->firstnumber; - idx2seglist[j]++; - } - shloop = shellfacetraverse(subsegs); - } - - // Calculate the total length of array 'facesperverlist'. - j = idx2seglist[0]; - idx2seglist[0] = 0; // Array starts from 0 element. - for (i = 0; i < points->items; i++) { - k = idx2seglist[i + 1]; - idx2seglist[i + 1] = idx2seglist[i] + j; - j = k; - } - // The total length is in the last unit of idx2seglist. - segsperverlist = new shellface*[idx2seglist[i]]; - // Loop the set of segments again, set the info. of segments per vertex. - subsegs->traversalinit(); - shloop = shellfacetraverse(subsegs); - while (shloop != (shellface *) NULL) { - for (i = 0; i < 2; i++) { - j = pointmark((point) shloop[3 + i]) - in->firstnumber; - segsperverlist[idx2seglist[j]] = shloop; - idx2seglist[j]++; - } - shloop = shellfacetraverse(subsegs); - } - // Contents in 'idx2seglist' are shifted, now shift them back. - for (i = points->items - 1; i >= 0; i--) { - idx2seglist[i + 1] = idx2seglist[i]; - } - idx2seglist[0] = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// makesubfacemap() Create a map from vertices (their indices) to // -// subfaces incident at the same vertices. // -// // -// Two arrays 'idx2facelist' and 'facesperverlist' together return the map. // -// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the // -// number of subfaces. idx2facelist contains row information and // -// facesperverlist contains all (non-zero) elements. The i-th entry of // -// idx2facelist is the starting position of i-th row's(non-zero) elements in // -// facesperverlist. The number of elements of i-th row is calculated by the // -// (i+1)-th entry minus i-th entry of idx2facelist. // -// // -// NOTE: These two arrays will be created inside this routine, don't forget // -// to free them after using. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh:: -makesubfacemap(int*& idx2facelist, shellface**& facesperverlist) -{ - shellface *shloop; - int i, j, k; - - if (b->verbose > 0) { - printf(" Constructing mapping from points to subfaces.\n"); - } - - // Create and initialize 'idx2facelist'. - idx2facelist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2facelist[i] = 0; - - // Loop the set of subfaces once, counter the number of subfaces sharing - // each vertex. - subfaces->traversalinit(); - shloop = shellfacetraverse(subfaces); - while (shloop != (shellface *) NULL) { - // Increment the number of sharing segments for each endpoint. - for (i = 0; i < 3; i++) { - j = pointmark((point) shloop[3 + i]) - in->firstnumber; - idx2facelist[j]++; - } - shloop = shellfacetraverse(subfaces); - } - - // Calculate the total length of array 'facesperverlist'. - j = idx2facelist[0]; - idx2facelist[0] = 0; // Array starts from 0 element. - for (i = 0; i < points->items; i++) { - k = idx2facelist[i + 1]; - idx2facelist[i + 1] = idx2facelist[i] + j; - j = k; - } - // The total length is in the last unit of idx2facelist. - facesperverlist = new shellface*[idx2facelist[i]]; - // Loop the set of segments again, set the info. of segments per vertex. - subfaces->traversalinit(); - shloop = shellfacetraverse(subfaces); - while (shloop != (shellface *) NULL) { - for (i = 0; i < 3; i++) { - j = pointmark((point) shloop[3 + i]) - in->firstnumber; - facesperverlist[idx2facelist[j]] = shloop; - idx2facelist[j]++; - } - shloop = shellfacetraverse(subfaces); - } - // Contents in 'idx2facelist' are shifted, now shift them back. - for (i = points->items - 1; i >= 0; i--) { - idx2facelist[i + 1] = idx2facelist[i]; - } - idx2facelist[0] = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// maketetrahedronmap() Create a map from vertices (their indices) to // -// tetrahedra incident at the same vertices. // -// // -// Two arrays 'idx2tetlist' and 'tetsperverlist' together return the map. // -// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the // -// number of tetrahedra. idx2tetlist contains row information and // -// tetsperverlist contains all (non-zero) elements. The i-th entry of // -// idx2tetlist is the starting position of i-th row's (non-zero) elements in // -// tetsperverlist. The number of elements of i-th row is calculated by the // -// (i+1)-th entry minus i-th entry of idx2tetlist. // -// // -// NOTE: These two arrays will be created inside this routine, don't forget // -// to free them after using. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh:: -maketetrahedronmap(int*& idx2tetlist, tetrahedron**& tetsperverlist) -{ - tetrahedron *tetloop; - int i, j, k; - - if (b->verbose > 0) { - printf(" Constructing mapping from points to tetrahedra.\n"); - } - - // Create and initialize 'idx2tetlist'. - idx2tetlist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) idx2tetlist[i] = 0; - - // Loop the set of tetrahedra once, counter the number of tetrahedra - // sharing each vertex. - tetrahedrons->traversalinit(); - tetloop = tetrahedrontraverse(); - while (tetloop != (tetrahedron *) NULL) { - // Increment the number of sharing tetrahedra for each endpoint. - for (i = 0; i < 4; i++) { - j = pointmark((point) tetloop[4 + i]) - in->firstnumber; - idx2tetlist[j]++; - } - tetloop = tetrahedrontraverse(); - } - - // Calculate the total length of array 'tetsperverlist'. - j = idx2tetlist[0]; - idx2tetlist[0] = 0; // Array starts from 0 element. - for (i = 0; i < points->items; i++) { - k = idx2tetlist[i + 1]; - idx2tetlist[i + 1] = idx2tetlist[i] + j; - j = k; - } - // The total length is in the last unit of idx2tetlist. - tetsperverlist = new tetrahedron*[idx2tetlist[i]]; - // Loop the set of tetrahedra again, set the info. of tet. per vertex. - tetrahedrons->traversalinit(); - tetloop = tetrahedrontraverse(); - while (tetloop != (tetrahedron *) NULL) { - for (i = 0; i < 4; i++) { - j = pointmark((point) tetloop[4 + i]) - in->firstnumber; - tetsperverlist[idx2tetlist[j]] = tetloop; - idx2tetlist[j]++; - } - tetloop = tetrahedrontraverse(); - } - // Contents in 'idx2tetlist' are shifted, now shift them back. - for (i = points->items - 1; i >= 0; i--) { - idx2tetlist[i + 1] = idx2tetlist[i]; - } - idx2tetlist[0] = 0; -} - -// -// End of mesh items searching routines -// - -// -// Begin of linear algebra functions -// - -// dot() returns the dot product: v1 dot v2. - -inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) -{ - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; -} - -// cross() computes the cross product: n = v1 cross v2. - -inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) -{ - n[0] = v1[1] * v2[2] - v2[1] * v1[2]; - n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); - n[2] = v1[0] * v2[1] - v2[0] * v1[1]; -} - -// initm44() initializes a 4x4 matrix. -static void initm44(REAL a00, REAL a01, REAL a02, REAL a03, - REAL a10, REAL a11, REAL a12, REAL a13, - REAL a20, REAL a21, REAL a22, REAL a23, - REAL a30, REAL a31, REAL a32, REAL a33, - REAL M[4][4]) -{ - M[0][0] = a00; M[0][1] = a01; M[0][2] = a02; M[0][3] = a03; - M[1][0] = a10; M[1][1] = a11; M[1][2] = a12; M[1][3] = a13; - M[2][0] = a20; M[2][1] = a21; M[2][2] = a22; M[2][3] = a23; - M[3][0] = a30; M[3][1] = a31; M[3][2] = a32; M[3][3] = a33; -} - -// m4xm4() multiplies 2 4x4 matrics: m1 = m1 * m2. -static void m4xm4(REAL m1[4][4], REAL m2[4][4]) -{ - REAL tmp[4]; - int i, j; - - for (i = 0; i < 4; i++) { // i-th row - for (j = 0; j < 4; j++) { // j-th col - tmp[j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j] - + m1[i][2] * m2[2][j] + m1[i][3] * m2[3][j]; - } - for (j = 0; j < 4; j++) - m1[i][j] = tmp[j]; - } -} - -// m4xv4() multiplies a 4x4 matrix and 4x1 vector: v2 = m * v1 -static void m4xv4(REAL v2[4], REAL m[4][4], REAL v1[4]) -{ - v2[0] = m[0][0]*v1[0] + m[0][1]*v1[1] + m[0][2]*v1[2] + m[0][3]*v1[3]; - v2[1] = m[1][0]*v1[0] + m[1][1]*v1[1] + m[1][2]*v1[2] + m[1][3]*v1[3]; - v2[2] = m[2][0]*v1[0] + m[2][1]*v1[1] + m[2][2]*v1[2] + m[2][3]*v1[3]; - v2[3] = m[3][0]*v1[0] + m[3][1]*v1[1] + m[3][2]*v1[2] + m[3][3]*v1[3]; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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]; -} - -// -// End of linear algebra functions -// - -// -// Begin of geometric tests -// - -// All the following routines require the input objects are not degenerate. -// i.e., a triangle must has three non-collinear corners; an edge must -// has two identical endpoints. Degenerate cases should have to detect -// first and then handled as special cases. - -/////////////////////////////////////////////////////////////////////////////// -// // -// edge_vert_col_inter() Test whether an edge (ab) and a collinear vertex // -// (p) are intersecting or not. // -// // -// Possible cases are p is coincident to a (p = a), or to b (p = b), or p is // -// inside ab (a < p < b), or outside ab (p < a or p > b). These cases can be // -// quickly determined by comparing the corresponding coords of a, b, and p // -// (which are not all equal). // -// // -// The return value indicates one of the three cases: DISJOINT, SHAREVERTEX // -// (p = a or p = b), and INTERSECT (a < p < b). // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::edge_vert_col_inter(REAL* A, REAL* B, - REAL* P) -{ - int i = 0; - do { - if (A[i] < B[i]) { - if (P[i] < A[i]) { - return DISJOINT; - } else if (P[i] > A[i]) { - if (P[i] < B[i]) { - return INTERSECT; - } else if (P[i] > B[i]) { - return DISJOINT; - } else { - // assert(P[i] == B[i]); - return SHAREVERTEX; - } - } else { - // assert(P[i] == A[i]); - return SHAREVERTEX; - } - } else if (A[i] > B[i]) { - if (P[i] < B[i]) { - return DISJOINT; - } else if (P[i] > B[i]) { - if (P[i] < A[i]) { - return INTERSECT; - } else if (P[i] > A[i]) { - return DISJOINT; - } else { - // assert(P[i] == A[i]); - return SHAREVERTEX; - } - } else { - // assert(P[i] == B[i]); - return SHAREVERTEX; - } - } - // i-th coordinates are equal, try i+1-th; - i++; - } while (i < 3); - // Should never be here. - return DISJOINT; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// edge_edge_cop_inter() Test whether two coplanar edges (ab, and pq) are // -// intersecting or not. // -// // -// Possible cases are ab and pq are disjointed, or proper intersecting (int- // -// ersect at a point other than their endpoints), or both collinear and int- // -// ersecting, or sharing at a common endpoint, or are coincident. // -// // -// A reference point R is required, which is exactly not coplanar with these // -// two edges. Since the caller knows these two edges are coplanar, it must // -// be able to provide (or calculate) such a point. // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh:: edge_edge_cop_inter(REAL* A, REAL* B, - REAL* P, REAL* Q, REAL* R) -{ - REAL s1, s2, s3, s4; - -#ifdef SELF_CHECK - assert(R != NULL); -#endif - s1 = orient3d(A, B, R, P); - s2 = orient3d(A, B, R, Q); - if (s1 * s2 > 0.0) { - // Both p and q are at the same side of ab. - return DISJOINT; - } - s3 = orient3d(P, Q, R, A); - s4 = orient3d(P, Q, R, B); - if (s3 * s4 > 0.0) { - // Both a and b are at the same side of pq. - return DISJOINT; - } - - // Possible degenerate cases are: - // (1) Only one of p and q is collinear with ab; - // (2) Both p and q are collinear with ab; - // (3) Only one of a and b is collinear with pq. - enum interresult abp, abq; - enum interresult pqa, pqb; - - if (s1 == 0.0) { - // p is collinear with ab. - abp = edge_vert_col_inter(A, B, P); - if (abp == INTERSECT) { - // p is inside ab. - return INTERSECT; - } - if (s2 == 0.0) { - // q is collinear with ab. Case (2). - abq = edge_vert_col_inter(A, B, Q); - if (abq == INTERSECT) { - // q is inside ab. - return INTERSECT; - } - if (abp == SHAREVERTEX && abq == SHAREVERTEX) { - // ab and pq are identical. - return SHAREEDGE; - } - pqa = edge_vert_col_inter(P, Q, A); - if (pqa == INTERSECT) { - // a is inside pq. - return INTERSECT; - } - pqb = edge_vert_col_inter(P, Q, B); - if (pqb == INTERSECT) { - // b is inside pq. - return INTERSECT; - } - if (abp == SHAREVERTEX || abq == SHAREVERTEX) { - // either p or q is coincident with a or b. -#ifdef SELF_CHECK - // ONLY one case is possible, otherwise, shoule be SHAREEDGE. - assert(abp ^ abq); -#endif - return SHAREVERTEX; - } - // The last case. They are disjointed. -#ifdef SELF_CHECK - assert((abp == DISJOINT) && (abp == abq && abq == pqa && pqa == pqb)); -#endif - return DISJOINT; - } else { - // p is collinear with ab. Case (1). -#ifdef SELF_CHECK - assert(abp == SHAREVERTEX || abp == DISJOINT); -#endif - return abp; - } - } - // p is NOT collinear with ab. - if (s2 == 0.0) { - // q is collinear with ab. Case (1). - abq = edge_vert_col_inter(A, B, Q); -#ifdef SELF_CHECK - assert(abq == SHAREVERTEX || abq == DISJOINT || abq == INTERSECT); -#endif - return abq; - } - - // We have found p and q are not collinear with ab. However, it is still - // possible that a or b is collinear with pq (ONLY one of a and b). - if (s3 == 0.0) { - // a is collinear with pq. Case (3). -#ifdef SELF_CHECK - assert(s4 != 0.0); -#endif - pqa = edge_vert_col_inter(P, Q, A); -#ifdef SELF_CHECK - // This case should have been detected in above. - assert(pqa != SHAREVERTEX); - assert(pqa == INTERSECT || pqa == DISJOINT); -#endif - return pqa; - } - if (s4 == 0.0) { - // b is collinear with pq. Case (3). -#ifdef SELF_CHECK - assert(s3 != 0.0); -#endif - pqb = edge_vert_col_inter(P, Q, B); -#ifdef SELF_CHECK - // This case should have been detected in above. - assert(pqb != SHAREVERTEX); - assert(pqb == INTERSECT || pqb == DISJOINT); -#endif - return pqb; - } - - // ab and pq are intersecting properly. - return INTERSECT; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// Notations // -// // -// Let ABC be the plane passes through a, b, and c; ABC+ be the halfspace // -// including the set of all points x, such that orient3d(a, b, c, x) > 0; // -// ABC- be the other halfspace, such that for each point x in ABC-, // -// orient3d(a, b, c, x) < 0. For the set of x which are on ABC, orient3d(a, // -// b, c, x) = 0. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_vert_copl_inter() Test whether a triangle (abc) and a coplanar // -// point (p) are intersecting or not. // -// // -// Possible cases are p is inside abc, or on an edge of, or coincident with // -// a vertex of, or outside abc. // -// // -// A reference point R is required. R is exactly not coplanar with abc and p.// -// Since the caller knows they are coplanar, it must be able to provide (or // -// calculate) such a point. // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_vert_cop_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* R) -{ - REAL s1, s2, s3; - int sign; - -#ifdef SELF_CHECK - assert(R != (REAL *) NULL); -#endif - // Adjust the orientation of a, b, c and r, so that we can assume that - // r is strictly in ABC- (i.e., r is above ABC wrt. right-hand rule). - s1 = orient3d(A, B, C, R); -#ifdef SELF_CHECK - assert(s1 != 0.0); -#endif - sign = s1 < 0.0 ? 1 : -1; - - // Test starts from here. - s1 = orient3d(A, B, R, P) * sign; - if (s1 < 0.0) { - // p is in ABR-. - return DISJOINT; - } - s2 = orient3d(B, C, R, P) * sign; - if (s2 < 0.0) { - // p is in BCR-. - return DISJOINT; - } - s3 = orient3d(C, A, R, P) * sign; - if (s3 < 0.0) { - // p is in CAR-. - return DISJOINT; - } - if (s1 == 0.0) { - // p is on ABR. - if (s2 == 0.0) { - // p is on BCR. -#ifdef SELF_CHECK - assert(s3 > 0.0); -#endif - // p is coincident with b. - return SHAREVERTEX; - } - if (s3 == 0.0) { - // p is on CAR. - // p is coincident with a. - return SHAREVERTEX; - } - // p is on edge ab. - return INTERSECT; - } - // p is in ABR+. - if (s2 == 0.0) { - // p is on BCR. - if (s3 == 0.0) { - // p is on CAR. - // p is coincident with c. - return SHAREVERTEX; - } - // p is on edge bc. - return INTERSECT; - } - if (s3 == 0.0) { - // p is on CAR. - // p is on edge ca. - return INTERSECT; - } - - // p is strictly inside abc. - return INTERSECT; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_edge_cop_inter() Test whether a triangle (abc) and a coplanar edge // -// (pq) are intersecting or not. // -// // -// A reference point R is required. R is exactly not coplanar with abc and // -// pq. Since the caller knows they are coplanar, it must be able to provide // -// (or calculate) such a point. // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_edge_cop_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q, REAL* R) -{ - enum interresult abpq, bcpq, capq; - enum interresult abcp, abcq; - - // Test if pq is intersecting one of edges of abc. - abpq = edge_edge_cop_inter(A, B, P, Q, R); - if (abpq == INTERSECT || abpq == SHAREEDGE) { - return abpq; - } - bcpq = edge_edge_cop_inter(B, C, P, Q, R); - if (bcpq == INTERSECT || bcpq == SHAREEDGE) { - return bcpq; - } - capq = edge_edge_cop_inter(C, A, P, Q, R); - if (capq == INTERSECT || capq == SHAREEDGE) { - return capq; - } - - // Test if p and q is inside abc. - abcp = tri_vert_cop_inter(A, B, C, P, R); - if (abcp == INTERSECT) { - return INTERSECT; - } - abcq = tri_vert_cop_inter(A, B, C, Q, R); - if (abcq == INTERSECT) { - return INTERSECT; - } - - // Combine the test results of edge intersectings and triangle insides - // to detect whether abc and pq are sharing vertex or disjointed. - if (abpq == SHAREVERTEX) { - // p or q is coincident with a or b. -#ifdef SELF_CHECK - assert(abcp ^ abcq); -#endif - return SHAREVERTEX; - } - if (bcpq == SHAREVERTEX) { - // p or q is coincident with b or c. -#ifdef SELF_CHECK - assert(abcp ^ abcq); -#endif - return SHAREVERTEX; - } - if (capq == SHAREVERTEX) { - // p or q is coincident with c or a. -#ifdef SELF_CHECK - assert(abcp ^ abcq); -#endif - return SHAREVERTEX; - } - - // They are disjointed. - return DISJOINT; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_edge_inter_tail() Test whether a triangle (abc) and an edge (pq) // -// are intersecting or not. // -// // -// s1 and s2 are results of pre-performed orientation tests. s1 = orient3d( // -// a, b, c, p); s2 = orient3d(a, b, c, q). To separate this routine from // -// tri_edge_inter() can save two orientation tests in tri_tri_inter(). // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q, REAL s1, REAL s2) -{ - REAL s3, s4, s5; - int sign; - - if (s1 * s2 > 0.0) { - // p, q are at the same halfspace of ABC, no intersection. - return DISJOINT; - } - - if (s1 * s2 < 0.0) { - // p, q are both not on ABC (and not sharing vertices, edges of abc). - // Adjust the orientation of a, b, c and p, so that we can assume that - // p is strictly in ABC-, and q is strictly in ABC+. - sign = s1 < 0.0 ? 1 : -1; - s3 = orient3d(A, B, P, Q) * sign; - if (s3 < 0.0) { - // q is at ABP-. - return DISJOINT; - } - s4 = orient3d(B, C, P, Q) * sign; - if (s4 < 0.0) { - // q is at BCP-. - return DISJOINT; - } - s5 = orient3d(C, A, P, Q) * sign; - if (s5 < 0.0) { - // q is at CAP-. - return DISJOINT; - } - if (s3 == 0.0) { - // q is on ABP. - if (s4 == 0.0) { - // q is on BCP (and q must in CAP+). -#ifdef SELF_CHECK - assert(s5 > 0.0); -#endif - // pq intersects abc at vertex b. - return SHAREVERTEX; - } - if (s5 == 0.0) { - // q is on CAP (and q must in BCP+). - // pq intersects abc at vertex a. - return SHAREVERTEX; - } - // q in both BCP+ and CAP+. - // pq crosses ab properly. - return INTERSECT; - } - // q is in ABP+; - if (s4 == 0.0) { - // q is on BCP. - if (s5 == 0.0) { - // q is on CAP. - // pq intersects abc at vertex c. - return SHAREVERTEX; - } - // pq crosses bc properly. - return INTERSECT; - } - // q is in BCP+; - if (s5 == 0.0) { - // q is on CAP. - // pq crosses ca properly. - return INTERSECT; - } - // q is in CAP+; - // pq crosses abc properly. - return INTERSECT; - } - - if (s1 != 0.0 || s2 != 0.0) { - // Either p or q is coplanar with abc. ONLY one of them is possible. - if (s1 == 0.0) { - // p is coplanar with abc, q can be used as reference point. -#ifdef SELF_CHECK - assert(s2 != 0.0); -#endif - return tri_vert_cop_inter(A, B, C, P, Q); - } else { - // q is coplanar with abc, p can be used as reference point. -#ifdef SELF_CHECK - assert(s2 == 0.0); -#endif - return tri_vert_cop_inter(A, B, C, Q, P); - } - } - - // pq is coplanar with abc. Calculate a point which is exactly not - // coplanar with a, b, and c. - REAL R[3], N[3]; - REAL ax, ay, az, bx, by, bz; - - ax = A[0] - B[0]; - ay = A[1] - B[1]; - az = A[2] - B[2]; - bx = A[0] - C[0]; - by = A[1] - C[1]; - bz = A[2] - C[2]; - N[0] = ay * bz - by * az; - N[1] = az * bx - bz * ax; - N[2] = ax * by - bx * ay; - // The normal should not be a zero vector (otherwise, abc are collinear). -#ifdef SELF_CHECK - assert((fabs(N[0]) + fabs(N[1]) + fabs(N[2])) > 0.0); -#endif - // The reference point R is lifted from A to the normal direction with - // a distance d = average edge length of the triangle abc. - R[0] = N[0] + A[0]; - R[1] = N[1] + A[1]; - R[2] = N[2] + A[2]; - // Becareful the case: if the non-zero component(s) in N is smaller than - // the machine epsilon (i.e., 2^(-16) for double), R will exactly equal - // to A due to the round-off error. Do check if it is. - if (R[0] == A[0] && R[1] == A[1] && R[2] == A[2]) { - int i, j; - for (i = 0; i < 3; i++) { -#ifdef SELF_CHECK - assert (R[i] == A[i]); -#endif - j = 2; - do { - if (N[i] > 0.0) { - N[i] += (j * macheps); - } else { - N[i] -= (j * macheps); - } - R[i] = N[i] + A[i]; - j *= 2; - } while (R[i] == A[i]); - } - } - - return tri_edge_cop_inter(A, B, C, P, Q, R); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_edge_inter() Test whether a triangle (abc) and an edge (pq) are // -// intersecting or not. // -// // -// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_edge_inter(REAL* A, REAL* B, - REAL* C, REAL* P, REAL* Q) -{ - REAL s1, s2; - - // Test the locations of p and q with respect to ABC. - s1 = orient3d(A, B, C, P); - s2 = orient3d(A, B, C, Q); - - return tri_edge_inter_tail(A, B, C, P, Q, s1, s2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_tri_inter() Test whether two triangle (abc) and (opq) are // -// intersecting or not. // -// // -// The return value indicates one of the five cases: DISJOINT, SHAREVERTEX, // -// SHAREEDGE, SHAREFACE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult tetgenmesh::tri_tri_inter(REAL* A, REAL* B, - REAL* C, REAL* O, REAL* P, REAL* Q) -{ - REAL s_o, s_p, s_q; - REAL s_a, s_b, s_c; - - s_o = orient3d(A, B, C, O); - s_p = orient3d(A, B, C, P); - s_q = orient3d(A, B, C, Q); - if ((s_o * s_p > 0.0) && (s_o * s_q > 0.0)) { - // o, p, q are all in the same halfspace of ABC. - return DISJOINT; - } - - s_a = orient3d(O, P, Q, A); - s_b = orient3d(O, P, Q, B); - s_c = orient3d(O, P, Q, C); - if ((s_a * s_b > 0.0) && (s_a * s_c > 0.0)) { - // a, b, c are all in the same halfspace of OPQ. - return DISJOINT; - } - - enum interresult abcop, abcpq, abcqo; - int shareedge = 0; - - abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p); - if (abcop == INTERSECT) { - return INTERSECT; - } else if (abcop == SHAREEDGE) { - shareedge++; - } - abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q); - if (abcpq == INTERSECT) { - return INTERSECT; - } else if (abcpq == SHAREEDGE) { - shareedge++; - } - abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o); - if (abcqo == INTERSECT) { - return INTERSECT; - } else if (abcqo == SHAREEDGE) { - shareedge++; - } - if (shareedge == 3) { - // opq are coincident with abc. - return SHAREFACE; - } -#ifdef SELF_CHECK - // It is only possible either no share edge or one. - assert(shareedge == 0 || shareedge == 1); -#endif - - // Continue to detect whether opq and abc are intersecting or not. - enum interresult opqab, opqbc, opqca; - - opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b); - if (opqab == INTERSECT) { - return INTERSECT; - } - opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c); - if (opqbc == INTERSECT) { - return INTERSECT; - } - opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a); - if (opqca == INTERSECT) { - return INTERSECT; - } - - // At this point, two triangles are not intersecting and not coincident. - // They may be share an edge, or share a vertex, or disjoint. - if (abcop == SHAREEDGE) { -#ifdef SELF_CHECK - assert(abcpq == SHAREVERTEX && abcqo == SHAREVERTEX); -#endif - // op is coincident with an edge of abc. - return SHAREEDGE; - } - if (abcpq == SHAREEDGE) { -#ifdef SELF_CHECK - assert(abcop == SHAREVERTEX && abcqo == SHAREVERTEX); -#endif - // pq is coincident with an edge of abc. - return SHAREEDGE; - } - if (abcqo == SHAREEDGE) { -#ifdef SELF_CHECK - assert(abcop == SHAREVERTEX && abcpq == SHAREVERTEX); -#endif - // qo is coincident with an edge of abc. - return SHAREEDGE; - } - - // They may share a vertex or disjoint. - if (abcop == SHAREVERTEX) { - // o or p is coincident with a vertex of abc. - if (abcpq == SHAREVERTEX) { - // p is the coincident vertex. -#ifdef SELF_CHECK - assert(abcqo != SHAREVERTEX); -#endif - } else { - // o is the coincident vertex. -#ifdef SELF_CHECK - assert(abcqo == SHAREVERTEX); -#endif - } - return SHAREVERTEX; - } - if (abcpq == SHAREVERTEX) { - // q is the coincident vertex. -#ifdef SELF_CHECK - assert(abcqo == SHAREVERTEX); -#endif - return SHAREVERTEX; - } - - // They are disjoint. - return DISJOINT; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insphere_sos() Insphere test with symbolic perturbation. // -// // -// The input points a, b, c, and d should be non-coplanar. They must be ord- // -// ered so that they have a positive orientation (as defined by orient3d()), // -// or the sign of the result will be reversed. // -// // -// Return a positive value if the point e lies inside the circumsphere of a, // -// b, c, and d; a negative value if it lies outside. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::insphere_sos(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, - int ia, int ib, int ic, int id, int ie) -{ - REAL det; - - det = insphere(pa, pb, pc, pd, pe); - if (det != 0.0) { - return det; - } - - // det = 0.0, use symbolic perturbation. - REAL *p[5], *tmpp; - REAL sign, det_c, det_d; - int idx[5], perm, tmp; - int n, i, j; - - p[0] = pa; idx[0] = ia; - p[1] = pb; idx[1] = ib; - p[2] = pc; idx[2] = ic; - p[3] = pd; idx[3] = id; - p[4] = pe; idx[4] = ie; - - // Bubble sort the points by the increasing order of the indices. - n = 5; - perm = 0; // The number of total swaps. - for (i = 0; i < n - 1; i++) { - for (j = 0; j < n - 1 - i; j++) { - if (idx[j + 1] < idx[j]) { // compare the two neighbors. - tmp = idx[j]; // swap idx[j] and idx[j + 1] - idx[j] = idx[j + 1]; - idx[j + 1] = tmp; - tmpp = p[j]; // swap p[j] and p[j + 1] - p[j] = p[j + 1]; - p[j + 1] = tmpp; - perm++; - } - } - } - - sign = (perm % 2 == 0) ? 1.0 : -1.0; - det_c = orient3d(p[1], p[2], p[3], p[4]); // orient3d(b, c, d, e) - if (det_c != 0.0) { - return sign * det_c; - } - det_d = orient3d(p[0], p[2], p[3], p[4]); // orient3d(a, c, d, e) - return -sign * det_d; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// iscollinear() Check if three points are approximately collinear. // -// // -// 'eps' is a relative error tolerance. The collinearity is determined by // -// the value q = cos(theta), where theta is the angle between two vectors // -// A->B and A->C. They're collinear if 1.0 - q <= epspp. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::iscollinear(REAL* A, REAL* B, REAL* C, REAL eps) -{ - REAL abx, aby, abz; - REAL acx, acy, acz; - REAL Lv, Lw, dd; - REAL d, q; - - // Limit of two closed points. - q = longest * eps; - q *= q; - - abx = A[0] - B[0]; - aby = A[1] - B[1]; - abz = A[2] - B[2]; - acx = A[0] - C[0]; - acy = A[1] - C[1]; - acz = A[2] - C[2]; - Lv = abx * abx + aby * aby + abz * abz; - // Is AB (nearly) indentical? - if (Lv < q) return true; - Lw = acx * acx + acy * acy + acz * acz; - // Is AC (nearly) indentical? - if (Lw < q) return true; - dd = abx * acx + aby * acy + abz * acz; - - d = (dd * dd) / (Lv * Lw); - if (d > 1.0) d = 1.0; // Rounding. - q = 1.0 - sqrt(d); // Notice 0 < q < 1.0. - - return q <= eps; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// iscoplanar() Check if four points are approximately coplanar. // -// // -// 'vol6' is six times of the signed volume of the tetrahedron formed by the // -// four points. 'eps' is the relative error tolerance. The coplanarity is // -// determined by the value: q = fabs(vol6) / L^3, where L is the average // -// edge length of the tet. They're coplanar if q <= eps. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh:: -iscoplanar(REAL* k, REAL* l, REAL* m, REAL* n, REAL vol6, REAL eps) -{ - REAL L, q; - REAL x, y, z; - - if (vol6 == 0.0) return true; - - x = k[0] - l[0]; - y = k[1] - l[1]; - z = k[2] - l[2]; - L = sqrt(x * x + y * y + z * z); - x = l[0] - m[0]; - y = l[1] - m[1]; - z = l[2] - m[2]; - L += sqrt(x * x + y * y + z * z); - x = m[0] - k[0]; - y = m[1] - k[1]; - z = m[2] - k[2]; - L += sqrt(x * x + y * y + z * z); - x = k[0] - n[0]; - y = k[1] - n[1]; - z = k[2] - n[2]; - L += sqrt(x * x + y * y + z * z); - x = l[0] - n[0]; - y = l[1] - n[1]; - z = l[2] - n[2]; - L += sqrt(x * x + y * y + z * z); - x = m[0] - n[0]; - y = m[1] - n[1]; - z = m[2] - n[2]; - L += sqrt(x * x + y * y + z * z); -#ifdef SELF_CHECK - assert(L > 0.0); -#endif - L /= 6.0; - q = fabs(vol6) / (L * L * L); - - return q <= eps; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// iscospheric() Check if five points are approximately coplanar. // -// // -// 'vol24' is the 24 times of the signed volume of the 4-dimensional simplex // -// formed by the five points. 'eps' is the relative tolerance. The cosphere // -// case is determined by the value: q = fabs(vol24) / L^4, where L is the // -// average edge length of the simplex. They're cosphere if q <= eps. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh:: -iscospheric(REAL* k, REAL* l, REAL* m, REAL* n, REAL* o, REAL vol24, REAL eps) -{ - REAL L, q; - - // A 4D simplex has 10 edges. - L = distance(k, l); - L += distance(l, m); - L += distance(m, k); - L += distance(k, n); - L += distance(l, n); - L += distance(m, n); - L += distance(k, o); - L += distance(l, o); - L += distance(m, o); - L += distance(n, o); -#ifdef SELF_CHECK - assert(L > 0.0); -#endif - L /= 10.0; - q = fabs(vol24) / (L * L * L * L); - - return q < eps; -} - -// -// End of geometric tests -// - -// -// Begin of Geometric quantities calculators -// - -// distance() computs the Euclidean distance between two points. -inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) -{ - return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) + - (p2[1] - p1[1]) * (p2[1] - p1[1]) + - (p2[2] - p1[2]) * (p2[2] - p1[2])); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// shortdistance() Returns the shortest distance from point p to a line // -// defined by two points e1 and e2. // -// // -// First compute the projection length l_p of the vector v1 = p - e1 along // -// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the // -// shortest distance. // -// // -// This routine allows that p is collinear with the line. In this case, the // -// return value is zero. The two points e1 and e2 should not be identical. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) -{ - REAL v1[3], v2[3]; - REAL len, l_p; - - v1[0] = e2[0] - e1[0]; - v1[1] = e2[1] - e1[1]; - v1[2] = e2[2] - e1[2]; - v2[0] = p[0] - e1[0]; - v2[1] = p[1] - e1[1]; - v2[2] = p[2] - e1[2]; - - len = sqrt(dot(v1, v1)); -#ifdef SELF_CHECK - assert(len != 0.0); -#endif - v1[0] /= len; - v1[1] /= len; - v1[2] /= len; - l_p = dot(v1, v2); - - return sqrt(dot(v2, v2) - l_p * l_p); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// shortdistance() Returns the shortest distance from point p to a face. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3) -{ - REAL prj[3]; - - projpt2face(p, e1, e2, e3, prj); - return distance(p, prj); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // -// o->p1 and o->p2. // -// // -// 'n' is the normal of the plane containing face (o, p1, p2). The interior // -// angle is the total angle rotating from o->p1 around n to o->p2. Exchange // -// the position of p1 and p2 will get the complement angle of the other one. // -// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1). Set // -// 'n' be NULL if you only want the interior angle between 0 - PI. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) -{ - REAL v1[3], v2[3], np[3]; - REAL theta, costheta, lenlen; - REAL ori, len1, len2; - - // Get the interior angle (0 - PI) between o->p1, and o->p2. - v1[0] = p1[0] - o[0]; - v1[1] = p1[1] - o[1]; - v1[2] = p1[2] - o[2]; - v2[0] = p2[0] - o[0]; - v2[1] = p2[1] - o[1]; - v2[2] = p2[2] - o[2]; - len1 = sqrt(dot(v1, v1)); - len2 = sqrt(dot(v2, v2)); - lenlen = len1 * len2; -#ifdef SELF_CHECK - assert(lenlen != 0.0); -#endif - costheta = dot(v1, v2) / lenlen; - if (costheta > 1.0) { - costheta = 1.0; // Roundoff. - } else if (costheta < -1.0) { - costheta = -1.0; // Roundoff. - } - theta = acos(costheta); - if (n != NULL) { - // Get a point above the face (o, p1, p2); - np[0] = o[0] + n[0]; - np[1] = o[1] + n[1]; - np[2] = o[2] + n[2]; - // Adjust theta (0 - 2 * PI). - ori = orient3d(p1, o, np, p2); - if (ori > 0.0) { - theta = 2 * PI - theta; - } - } - - return theta; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// projpt2edge() Return the projection point from a point to an edge. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) -{ - REAL v1[3], v2[3]; - REAL len, l_p; - - v1[0] = e2[0] - e1[0]; - v1[1] = e2[1] - e1[1]; - v1[2] = e2[2] - e1[2]; - v2[0] = p[0] - e1[0]; - v2[1] = p[1] - e1[1]; - v2[2] = p[2] - e1[2]; - - len = sqrt(dot(v1, v1)); -#ifdef SELF_CHECK - assert(len != 0.0); -#endif - v1[0] /= len; - v1[1] /= len; - v1[2] /= len; - l_p = dot(v1, v2); - - prj[0] = e1[0] + l_p * v1[0]; - prj[1] = e1[1] + l_p * v1[1]; - prj[2] = e1[2] + l_p * v1[2]; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// projpt2face() Return the projection point from a point to a face. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) -{ - REAL fnormal[3], v1[3]; - REAL len, dist; - - // Get the unit face normal. - facenormal(f1, f2, f3, fnormal, &len); -#ifdef SELF_CHECK - assert(len > 0.0); -#endif - fnormal[0] /= len; - fnormal[1] /= len; - fnormal[2] /= len; - // Get the vector v1 = |p - f1|. - v1[0] = p[0] - f1[0]; - v1[1] = p[1] - f1[1]; - v1[2] = p[2] - f1[2]; - // Get the project distance. - dist = dot(fnormal, v1); - - // Get the project point. - prj[0] = p[0] - dist * fnormal[0]; - prj[1] = p[1] - dist * fnormal[1]; - prj[2] = p[2] - dist * fnormal[2]; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// facenormal() Calculate the normal of a face given by three points. // -// // -// In general, the face normal can be calculate by the cross product of any // -// pair of the three edge vectors. However, if the three points are nearly // -// collinear, the rounding error may harm the result. To choose a good pair // -// of vectors is helpful to reduce the error. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen) -{ - REAL v1[3], v2[3]; - - v1[0] = pb[0] - pa[0]; - v1[1] = pb[1] - pa[1]; - v1[2] = pb[2] - pa[2]; - v2[0] = pc[0] - pa[0]; - v2[1] = pc[1] - pa[1]; - v2[2] = pc[2] - pa[2]; - - cross(v1, v2, n); - if (nlen != (REAL *) NULL) { - *nlen = sqrt(dot(n, n)); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// edgeorthonormal() Return the unit normal of an edge in a given plane. // -// // -// The edge is from e1 to e2, the plane is defined by given an additional // -// point op, which is non-collinear with the edge. In addition, the side of // -// the edge in which op lies defines the positive position of the normal. // -// // -// Let v1 be the unit vector from e1 to e2, v2 be the unit edge vector from // -// e1 to op, fn be the unit face normal calculated by fn = v1 x v2. Then the // -// unit edge normal of e1e2 pointing to op is n = fn x v1. Note, we should // -// not change the position of fn and v1, otherwise, we get the edge normal // -// pointing to the other side of op. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n) -{ - REAL v1[3], v2[3], fn[3]; - REAL len; - - // Get the edge vector v1. - v1[0] = e2[0] - e1[0]; - v1[1] = e2[1] - e1[1]; - v1[2] = e2[2] - e1[2]; - // Get the edge vector v2. - v2[0] = op[0] - e1[0]; - v2[1] = op[1] - e1[1]; - v2[2] = op[2] - e1[2]; - // Get the face normal fn = v1 x v2. - cross(v1, v2, fn); - // Get the edge normal n pointing to op. n = fn x v1. - cross(fn, v1, n); - // Normalize the vector. - len = sqrt(dot(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// facedihedral() Return the dihedral angle (in radian) between two // -// adjoining faces. // -// // -// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are // -// apexes of these two faces. Return the angle (between 0 to 2*pi) between // -// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2). // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2) -{ - REAL n1[3], n2[3]; - REAL n1len, n2len; - REAL costheta, ori; - REAL theta; - - facenormal(pa, pb, pc1, n1, &n1len); - facenormal(pa, pb, pc2, n2, &n2len); - costheta = dot(n1, n2) / (n1len * n2len); - // Be careful rounding error! - if (costheta > 1.0) { - costheta = 1.0; - } else if (costheta < -1.0) { - costheta = -1.0; - } - theta = acos(costheta); - ori = orient3d(pa, pb, pc1, pc2); - if (ori > 0.0) { - theta = 2 * PI - theta; - } - - return theta; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetalldihedral() Get all (six) dihedral angles of a tet. // -// // -// The tet is given by its four corners a, b, c, and d. If 'cosdd' is not // -// NULL, it returns the cosines of the 6 dihedral angles, the corresponding // -// edges are: ab, bc, ca, ad, bd, and cd. If 'cosmaxd' (or 'cosmind') is not // -// NULL, it returns the cosine of the maximal (or minimal) dihedral angle. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, - REAL* cosdd, REAL* cosmaxd, REAL* cosmind) -{ - REAL N[4][3], cosd, len; - int f1, f2, i, j; - - // Get four normals of faces of the tet. - tetallnormal(pa, pb, pc, pd, N, NULL); - // Normalize the normals. - for (i = 0; i < 4; i++) { - len = sqrt(dot(N[i], N[i])); - if (len != 0.0) { - for (j = 0; j < 3; j++) N[i][j] /= len; - } - } - - for (i = 0; i < 6; i++) { - switch (i) { - case 0: f1 = 2; f2 = 3; break; // edge ab. - case 1: f1 = 0; f2 = 3; break; // edge bc. - case 2: f1 = 1; f2 = 3; break; // edge ca. - case 3: f1 = 1; f2 = 2; break; // edge ad. - case 4: f1 = 2; f2 = 0; break; // edge bd. - case 5: f1 = 0; f2 = 1; break; // edge cd. - } - cosd = -dot(N[f1], N[f2]); - if (cosdd) cosdd[i] = cosd; - if (i == 0) { - if (cosmaxd) *cosmaxd = cosd; - if (cosmind) *cosmind = cosd; - } else { - if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd; - if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetallnormal() Get the in-noramls of the four faces of a given tet. // -// // -// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd, // -// N[1] acd, N[2] bad, N[3] abc. These normals are unnormalized. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, - REAL N[4][3], REAL* volume) -{ - REAL A[4][4], rhs[4], D; - int indx[4]; - int i, j; - - // get the entries of A[3][3]. - for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i]; // d->a vec - for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i]; // d->b vec - for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i]; // d->c vec - // Compute the inverse of matrix A, to get 3 normals of the 4 faces. - lu_decmp(A, 3, indx, &D, 0); // Decompose the matrix just once. - if (volume != NULL) { - // Get the volume of the tet. - *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0; - } - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetaspectratio() Calculate the aspect ratio of the tetrahedron. // -// // -// The aspect ratio of a tet is R/h, where R is the circumradius and h is // -// the shortest height of the tet. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) -{ - REAL vda[3], vdb[3], vdc[3]; - REAL N[4][3], A[4][4], rhs[4], D; - REAL H[4], volume, radius2, minheightinv; - int indx[4]; - int i, j; - - // Set the matrix A = [vda, vdb, vdc]^T. - for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; - for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i]; - for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i]; - // Lu-decompose the matrix A. - lu_decmp(A, 3, indx, &D, 0); - // Get the volume of abcd. - volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; - // Check if it is zero. - if (volume == 0.0) return 1.0e+200; // A degenerate tet. - // if (volume < 0.0) volume = -volume; - // Check the radiu-edge ratio of the tet. - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - // Get the circumcenter. - // for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i]; - // Get the square of the circumradius. - radius2 = dot(rhs, rhs); - - // Compute the 4 face normals (N[0], ..., N[3]). - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; - // Normalized the normals. - for (i = 0; i < 4; i++) { - // H[i] is the inverse of the height of its corresponding face. - H[i] = sqrt(dot(N[i], N[i])); - // if (H[i] > 0.0) { - // for (j = 0; j < 3; j++) N[i][j] /= H[i]; - // } - } - // Get the radius of the inscribed sphere. - // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]); - // Get the biggest H[i] (corresponding to the smallest height). - minheightinv = H[0]; - for (i = 1; i < 3; i++) { - if (H[i] > minheightinv) minheightinv = H[i]; - } - - return sqrt(radius2) * minheightinv; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// circumsphere() Calculate the smallest circumsphere (center and radius) // -// of the given three or four points. // -// // -// The circumsphere of four points (a tetrahedron) is unique if they are not // -// degenerate. If 'pd = NULL', the smallest circumsphere of three points is // -// the diametral sphere of the triangle if they are not degenerate. // -// // -// Return TRUE if the input points are not degenerate and the circumcenter // -// and circumradius are returned in 'cent' and 'radius' respectively if they // -// are not NULLs. Otherwise, return FALSE indicated the points are degenrate.// -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh:: -circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* cent, REAL* radius) -{ - REAL A[4][4], rhs[4], D; - int indx[4]; - - // Compute the coefficient matrix A (3x3). - A[0][0] = pb[0] - pa[0]; - A[0][1] = pb[1] - pa[1]; - A[0][2] = pb[2] - pa[2]; - A[1][0] = pc[0] - pa[0]; - A[1][1] = pc[1] - pa[1]; - A[1][2] = pc[2] - pa[2]; - if (pd != NULL) { - A[2][0] = pd[0] - pa[0]; - A[2][1] = pd[1] - pa[1]; - A[2][2] = pd[2] - pa[2]; - } else { - cross(A[0], A[1], A[2]); - } - - // Compute the right hand side vector b (3x1). - rhs[0] = 0.5 * dot(A[0], A[0]); - rhs[1] = 0.5 * dot(A[1], A[1]); - if (pd != NULL) { - rhs[2] = 0.5 * dot(A[2], A[2]); - } else { - rhs[2] = 0.0; - } - - // Solve the 3 by 3 equations use LU decomposition with partial pivoting - // and backward and forward substitute.. - if (!lu_decmp(A, 3, indx, &D, 0)) { - if (radius != (REAL *) NULL) *radius = 0.0; - return false; - } - lu_solve(A, 3, indx, rhs, 0); - if (cent != (REAL *) NULL) { - cent[0] = pa[0] + rhs[0]; - cent[1] = pa[1] + rhs[1]; - cent[2] = pa[2] + rhs[2]; - } - if (radius != (REAL *) NULL) { - *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); - } - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// inscribedsphere() Compute the radius and center of the biggest // -// inscribed sphere of a given tetrahedron. // -// // -// The tetrahedron is given by its four points, it must not be degenerate. // -// The center and radius are returned in 'cent' and 'radius' respectively if // -// they are not NULLs. // -// // -// Geometrical fact. For any simplex in d dimension, // -// r/h1 + r/h2 + ... r/hn = 1 (n <= d + 1); // -// where r is the radius of inscribed ball, and h is the height of each side // -// of the simplex. The value of 'r/h' is just the barycenter coordinates of // -// each vertex of the simplex. Therefore, we can compute the radius and // -// center of the smallest inscribed ball as following equations: // -// r = 1.0 / (1/h1 + 1/h2 + ... + 1/hn); (1) // -// C = r/h1 * P1 + r/h2 * P2 + ... + r/hn * Pn; (2) // -// where C is the vector of center, P1, P2, .. Pn are vectors of vertices. // -// Here (2) contains n linear equations with n variables. (h, P) must be a // -// pair, h is the height from P to its opposite face. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::inscribedsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, - REAL* cent, REAL* radius) -{ - REAL N[4][3], H[4]; // Normals (colume vectors) and heights of each face. - REAL rd; - int i; - - // Get the all normals of the tet. - tetallnormal(pa, pb, pc, pd, N, NULL); - for (i = 0; i < 4; i++) { - // H[i] is the inverse of height of its corresponding face. - H[i] = sqrt(dot(N[i], N[i])); - } - // Compute the radius use eq. (1). - rd = 1.0 / (H[0] + H[1] + H[2] + H[3]); - if (radius != (REAL*) NULL) *radius = rd; - if (cent != (REAL*) NULL) { - // Compute the center use eq. (2). - cent[0] = rd * (H[0] * pa[0] + H[1] * pb[0] + H[2] * pc[0] + H[3] * pd[0]); - cent[1] = rd * (H[0] * pa[1] + H[1] * pb[1] + H[2] * pc[1] + H[3] * pd[1]); - cent[2] = rd * (H[0] * pa[2] + H[1] * pb[2] + H[2] * pc[2] + H[3] * pd[2]); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// rotatepoint() Create a point by rotating an existing point. // -// // -// Create a 3D point by rotating point 'p' with an angle 'rotangle' (in arc // -// degree) around a rotating axis given by a vector from point 'p1' to 'p2'. // -// The rotation is according with right-hand rule, i.e., use your right-hand // -// to grab the axis with your thumber pointing to its positive direction, // -// your fingers indicate the rotating direction. // -// // -// The rotating steps are the following: // -// 1. Translate vector 'p1->p2' to origin, M1; // -// 2. Rotate vector around the Y-axis until it lies in the YZ plane, M2; // -// 3. Rotate vector around the X-axis until it lies on the Z axis, M3; // -// 4. Perform the rotation of 'p' around the z-axis, M4; // -// 5. Undo Step 3, M5; // -// 6. Undo Step 2, M6; // -// 7. Undo Step 1, M7; // -// Use matrix multiplication to combine the above sequences, we get: // -// p0' = T * p0, where T = M7 * M6 * M5 * M4 * M3 * M2 * M1 // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2) -{ - REAL T[4][4], pp0[4], p0t[4], p2t[4]; - REAL roty, rotx, alphaR, projlen; - REAL dx, dy, dz; - - initm44(1, 0, 0, -p1[0], - 0, 1, 0, -p1[1], - 0, 0, 1, -p1[2], - 0, 0, 0, 1, T); - pp0[0] = p[0]; pp0[1] = p[1]; pp0[2] = p[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 1 - pp0[0] = p2[0]; pp0[1] = p2[1]; pp0[2] = p2[2]; pp0[3] = 1.0; - m4xv4(p2t, T, pp0); // Step 1 - - // Get the rotation angle around y-axis; - dx = p2t[0]; - dz = p2t[2]; - projlen = sqrt(dx * dx + dz * dz); - if (projlen <= (b->epsilon * 1e-2) * longest) { - roty = 0; - } else { - roty = acos(dz / projlen); - if (dx < 0) { - roty = -roty; - } - } - - initm44(cos(-roty), 0, sin(-roty), 0, - 0, 1, 0, 0, - -sin(-roty), 0, cos(-roty), 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 2 - pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0; - m4xv4(p2t, T, pp0); // Step 2 - - // Get the rotation angle around x-axis - dy = p2t[1]; - dz = p2t[2]; - projlen = sqrt(dy * dy + dz * dz); - if (projlen <= (b->epsilon * 1e-2) * longest) { - rotx = 0; - } else { - rotx = acos(dz / projlen); - if (dy < 0) { - rotx = -rotx; - } - } - - initm44(1, 0, 0, 0, - 0, cos(rotx), -sin(rotx), 0, - 0, sin(rotx), cos(rotx), 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 3 - // pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0; - // m4xv4(p2t, T, pp0); // Step 3 - - alphaR = rotangle; - initm44(cos(alphaR), -sin(alphaR), 0, 0, - sin(alphaR), cos(alphaR), 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 4 - - initm44(1, 0, 0, 0, - 0, cos(-rotx), -sin(-rotx), 0, - 0, sin(-rotx), cos(-rotx), 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 5 - - initm44(cos(roty), 0, sin(roty), 0, - 0, 1, 0, 0, - -sin(roty), 0, cos(roty), 0, - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 6 - - initm44(1, 0, 0, p1[0], - 0, 1, 0, p1[1], - 0, 0, 1, p1[2], - 0, 0, 0, 1, T); - pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0; - m4xv4(p0t, T, pp0); // Step 7 - - p[0] = p0t[0]; - p[1] = p0t[1]; - p[2] = p0t[2]; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// spherelineint() 3D line sphere (or circle) intersection. // -// // -// The line is given by two points p1, and p2, the sphere is centered at c // -// with radius r. This function returns a pointer array p which first index // -// indicates the number of intersection point, followed by coordinate pairs. // -// // -// The following code are adapted from: http://astronomy.swin.edu.au/pbourke // -// /geometry/sphereline. Paul Bourke pbourke@swin.edu.au // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::spherelineint(REAL* p1, REAL* p2, REAL* C, REAL R, REAL p[7]) -{ - REAL x1, y1, z1; // P1 coordinates (point of line) - REAL x2, y2, z2; // P2 coordinates (point of line) - REAL x3, y3, z3, r; // P3 coordinates and radius (sphere) - REAL a, b, c, mu, i ; - - x1 = p1[0]; y1 = p1[1]; z1 = p1[2]; - x2 = p2[0]; y2 = p2[1]; z2 = p2[2]; - x3 = C[0]; y3 = C[1]; z3 = C[2]; - r = R; - - a = (x2 - x1) * (x2 - x1) - + (y2 - y1) * (y2 - y1) - + (z2 - z1) * (z2 - z1); - b = 2 * ( (x2 - x1) * (x1 - x3) - + (y2 - y1) * (y1 - y3) - + (z2 - z1) * (z1 - z3) ) ; - c = (x3 * x3) + (y3 * y3) + (z3 * z3) - + (x1 * x1) + (y1 * y1) + (z1 * z1) - - 2 * (x3 * x1 + y3 * y1 + z3 * z1) - (r * r) ; - i = b * b - 4 * a * c ; - - if (i < 0.0) { - // no intersection - p[0] = 0.0; - } else if (i == 0.0) { - // one intersection - p[0] = 1.0; - mu = -b / (2 * a) ; - p[1] = x1 + mu * (x2 - x1); - p[2] = y1 + mu * (y2 - y1); - p[3] = z1 + mu * (z2 - z1); - } else { - // two intersections - p[0] = 2.0; - // first intersection - mu = (-b + sqrt((b * b) - 4 * a * c)) / (2 * a); - p[1] = x1 + mu * (x2 - x1); - p[2] = y1 + mu * (y2 - y1); - p[3] = z1 + mu * (z2 - z1); - // second intersection - mu = (-b - sqrt((b * b) - 4 * a * c)) / (2 * a); - p[4] = x1 + mu * (x2 - x1); - p[5] = y1 + mu * (y2 - y1); - p[6] = z1 + mu * (z2 - z1); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// linelineint() Calculate the shortest line between two lines in 3D. // -// // -// Two 3D lines generally don't intersect at a point, they may be parallel ( // -// no intersections), or coincident (infinite intersections) but most often // -// only their projections onto a plane intersect. If they don't exactly int- // -// ersect at a point they can be connected by a line segment, the shortest // -// segment is unique and is often considered to be their intersection in 3D. // -// // -// The following code are adapted from: http://astronomy.swin.edu.au/pbourke // -// /geometry/lineline3d. Paul Bourke pbourke@swin.edu.au // -// // -// Calculate the line segment PaPb that is the shortest route between two // -// lines P1P2 and P3P4. This function returns a pointer array p which first // -// index indicates there exists solution or not, 0 means no solution, 1 meas // -// has solution followed by two coordinate pairs. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::linelineint(REAL *p1,REAL *p2, REAL *p3, REAL *p4, REAL p[7]) -{ - REAL p13[3], p43[3], p21[3]; - REAL d1343, d4321, d1321, d4343, d2121; - REAL numer, denom; - REAL mua, mub; - - p13[0] = p1[0] - p3[0]; - p13[1] = p1[1] - p3[1]; - p13[2] = p1[2] - p3[2]; - p43[0] = p4[0] - p3[0]; - p43[1] = p4[1] - p3[1]; - p43[2] = p4[2] - p3[2]; - if (p43[0] == 0.0 && p43[1] == 0.0 && p43[2] == 0.0) { - p[0] = 0.0; - return; - } - - p21[0] = p2[0] - p1[0]; - p21[1] = p2[1] - p1[1]; - p21[2] = p2[2] - p1[2]; - if (p21[0] == 0.0 && p21[1] == 0.0 && p21[2] == 0.0) { - p[0] = 0.0; - return; - } - - d1343 = p13[0] * p43[0] + p13[1] * p43[1] + p13[2] * p43[2]; - d4321 = p43[0] * p21[0] + p43[1] * p21[1] + p43[2] * p21[2]; - d1321 = p13[0] * p21[0] + p13[1] * p21[1] + p13[2] * p21[2]; - d4343 = p43[0] * p43[0] + p43[1] * p43[1] + p43[2] * p43[2]; - d2121 = p21[0] * p21[0] + p21[1] * p21[1] + p21[2] * p21[2]; - - denom = d2121 * d4343 - d4321 * d4321; - if (denom == 0.0) { - p[0] = 0.0; - return; - } - numer = d1343 * d4321 - d1321 * d4343; - mua = numer / denom; - mub = (d1343 + d4321 * mua) / d4343; - - p[0] = 1.0; - p[1] = p1[0] + mua * p21[0]; - p[2] = p1[1] + mua * p21[1]; - p[3] = p1[2] + mua * p21[2]; - p[4] = p3[0] + mub * p43[0]; - p[5] = p3[1] + mub * p43[1]; - p[6] = p3[2] + mub * p43[2]; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// planelineint() Calculate the intersection of a line and a plane. // -// // -// The equation of a plane (points P are on the plane with normal N and P3 // -// on the plane) can be written as: N dot (P - P3) = 0. The equation of the // -// line (points P on the line passing through P1 and P2) can be written as: // -// P = P1 + u (P2 - P1). The intersection of these two occurs when: // -// N dot (P1 + u (P2 - P1)) = N dot P3. // -// Solving for u gives: // -// N dot (P3 - P1) // -// u = ------------------. // -// N dot (P2 - P1) // -// If the denominator is 0 then N (the normal to the plane) is perpendicular // -// to the line. Thus the line is either parallel to the plane and there are // -// no solutions or the line is on the plane in which case there are an infi- // -// nite number of solutions. // -// // -// The plane is given by three points pa, pb, and pc, e1 and e2 defines the // -// line. If u is non-zero, The intersection point (if exists) returns in ip. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, - REAL* ip, REAL* u) -{ - REAL n[3], det, det1; - - // Calculate N. - facenormal(pa, pb, pc, n, NULL); - // Calculate N dot (e2 - e1). - det = n[0] * (e2[0] - e1[0]) + n[1] * (e2[1] - e1[1]) - + n[2] * (e2[2] - e1[2]); - if (det != 0.0) { - // Calculate N dot (pa - e1) - det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1]) - + n[2] * (pa[2] - e1[2]); - *u = det1 / det; - ip[0] = e1[0] + *u * (e2[0] - e1[0]); - ip[1] = e1[1] + *u * (e2[1] - e1[1]); - ip[2] = e1[2] + *u * (e2[2] - e1[2]); - } else { - *u = 0.0; - } -} - -// -// End of Geometric quantities calculators -// - -// -// Begin of memory management routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// dummyinit() Initialize the tetrahedron that fills "outer space" and // -// the omnipresent subface. // -// // -// The tetrahedron that fills "outer space" called 'dummytet', is pointed to // -// by every tetrahedron and subface on a boundary (be it outer or inner) of // -// the tetrahedralization. Also, 'dummytet' points to one of the tetrahedron // -// on the convex hull(until the holes and concavities are carved), making it // -// possible to find a starting tetrahedron for point location. // -// // -// The omnipresent subface,'dummysh', is pointed to by every tetrahedron or // -// subface that doesn't have a full complement of real subface to point to. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::dummyinit(int tetwords, int shwords) -{ - unsigned long alignptr; - - // Set up 'dummytet', the 'tetrahedron' that occupies "outer space". - dummytetbase = (tetrahedron *) new char[tetwords * sizeof(tetrahedron) - + tetrahedrons->alignbytes]; - // Align 'dummytet' on a 'tetrahedrons->alignbytes'-byte boundary. - alignptr = (unsigned long) dummytetbase; - dummytet = (tetrahedron *) - (alignptr + (unsigned long) tetrahedrons->alignbytes - - (alignptr % (unsigned long) tetrahedrons->alignbytes)); - // Initialize the four adjoining tetrahedra to be "outer space". These - // will eventually be changed by various bonding operations, but their - // values don't really matter, as long as they can legally be - // dereferenced. - dummytet[0] = (tetrahedron) dummytet; - dummytet[1] = (tetrahedron) dummytet; - dummytet[2] = (tetrahedron) dummytet; - dummytet[3] = (tetrahedron) dummytet; - // Four null vertex points. - dummytet[4] = (tetrahedron) NULL; - dummytet[5] = (tetrahedron) NULL; - dummytet[6] = (tetrahedron) NULL; - dummytet[7] = (tetrahedron) NULL; - - if (b->useshelles) { - // Set up 'dummysh', the omnipresent "subface" pointed to by any - // tetrahedron side or subface end that isn't attached to a real - // subface. - dummyshbase = (shellface *) new char[shwords * sizeof(shellface) - + subfaces->alignbytes]; - // Align 'dummysh' on a 'subfaces->alignbytes'-byte boundary. - alignptr = (unsigned long) dummyshbase; - dummysh = (shellface *) - (alignptr + (unsigned long) subfaces->alignbytes - - (alignptr % (unsigned long) subfaces->alignbytes)); - // Initialize the three adjoining subfaces to be the omnipresent - // subface. These will eventually be changed by various bonding - // operations, but their values don't really matter, as long as they - // can legally be dereferenced. - dummysh[0] = (shellface) dummysh; - dummysh[1] = (shellface) dummysh; - dummysh[2] = (shellface) dummysh; - // Three null vertex points. - dummysh[3] = (shellface) NULL; - dummysh[4] = (shellface) NULL; - dummysh[5] = (shellface) NULL; - // Initialize the two adjoining tetrahedra to be "outer space". - dummysh[6] = (shellface) dummytet; - dummysh[7] = (shellface) dummytet; - // Initialize the three adjoining subsegments to be "out boundary". - dummysh[8] = (shellface) dummysh; - dummysh[9] = (shellface) dummysh; - dummysh[10] = (shellface) dummysh; - // Initialize the pointer to badface structure. - dummysh[11] = (shellface) NULL; - // Initialize the four adjoining subfaces of 'dummytet' to be the - // omnipresent subface. - dummytet[8 ] = (tetrahedron) dummysh; - dummytet[9 ] = (tetrahedron) dummysh; - dummytet[10] = (tetrahedron) dummysh; - dummytet[11] = (tetrahedron) dummysh; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// initializepools() Calculate the sizes of the point, tetrahedron, and // -// subface. Initialize their memory pools. // -// // -// This routine also computes the indices 'pointmarkindex', 'point2simindex',// -// and 'point2pbcptindex' used to find values within each point; computes // -// indices 'highorderindex', 'elemattribindex', and 'volumeboundindex' used // -// to find values within each tetrahedron. // -// // -// There are two types of boundary elements, which are subfaces and subsegs, // -// they are stored in seperate pools. However, the data structures of them // -// are the same. A subsegment can be regarded as a degenerate subface, i.e.,// -// one of its three corners is not used. We set the apex of it be 'NULL' to // -// distinguish it's a subsegment. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::initializepools() -{ - enum wordtype wtype; - int pointsize, elesize, shsize; - - // Default checkpbc = 0; - if ((b->plc || b->refine) && (in->pbcgrouplist != NULL)) { - checkpbcs = 1; - } - // Default varconstraint = 0; - if (in->segmentconstraintlist || in->facetconstraintlist) { - varconstraint = 1; - } - - // The index within each point at which its metric tensor is found. It is - // saved directly after the list of point attributes. - pointmtrindex = 3 + in->numberofpointattributes; - // Decide the size (1, 3, or 6) of the metric tensor. - if (b->metric) { - // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file). - if (bgm != (tetgenmesh *) NULL) { - // A background mesh is allocated. It may not exist though. - sizeoftensor = (bgm->in != (tetgenio *) NULL) ? - bgm->in->numberofpointmtrs : in->numberofpointmtrs; - } else { - // No given background mesh - Itself is a background mesh. - sizeoftensor = in->numberofpointmtrs; - } - // Make sure sizeoftensor is at least 1. - sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1; - } else { - // For '-q' option. Make sure to have space for saving a scalar value. - sizeoftensor = b->quality ? 1 : 0; - } - // The index within each point at which an element pointer is found, where - // the index is measured in pointers. Ensure the index is aligned to a - // sizeof(tetrahedron)-byte address. - point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL) - + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); - if (b->plc || b->refine) { - // Increase the point size by three pointers, which are: - // - a pointer to a tet, read by point2tet(); - // - a pointer to a subface/subsegment , read by point2sh(); - // - a pointer to a parent point, read by point2ppt()). - if (b->metric) { - // Increase one pointer to a tet of the background mesh. - pointsize = (point2simindex + 4) * sizeof(tetrahedron); - } else { - pointsize = (point2simindex + 3) * sizeof(tetrahedron); - } - // The index within each point at which a pbc point is found. - point2pbcptindex = (pointsize + sizeof(tetrahedron) - 1) - / sizeof(tetrahedron); - if (checkpbcs) { - // Increase the size by one pointer to a corresponding pbc point, - // read by point2pbcpt(). - pointsize = (point2pbcptindex + 1) * sizeof(tetrahedron); - } - } else { - pointsize = point2simindex * sizeof(tetrahedron); - } - // The index within each point at which the boundary marker is found, - // Ensure the point marker is aligned to a sizeof(int)-byte address. - pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int); - // Now point size is the ints (inidcated by pointmarkindex) plus: - // - an integer for boundary marker; - // - an integer for vertex type; - pointsize = (pointmarkindex + 2) * sizeof(int); - // Decide the wordtype used in vertex pool. - wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? FLOATINGPOINT : POINTER; - // Initialize the pool of vertices. - points = new memorypool(pointsize, VERPERBLOCK, wtype, 0); - - // The number of bytes occupied by a tetrahedron. There are four pointers - // to other tetrahedra, four pointers to corners, and possibly four - // pointers to subfaces. - elesize = (8 + b->useshelles * 6) * sizeof(tetrahedron); - // If Voronoi diagram is wanted, make sure we have additional space. - if (b->voroout && (b->useshelles == 0)) { - elesize = (8 + 4) * 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. - if (b->varvolume) { - elesize = (volumeboundindex + 1) * sizeof(REAL); - } else if (in->numberoftetrahedronattributes + b->regionattrib > 0) { - elesize = volumeboundindex * sizeof(REAL); - } - // If element neighbor graph is requested (-n switch), an additional - // integer is allocated for each element. - elemmarkerindex = (elesize + sizeof(int) - 1) / sizeof(int); - if (b->neighout || b->voroout) { - elesize = (elemmarkerindex + 1) * sizeof(int); - } - // If -o2 switch is used, an additional pointer pointed to the list of - // higher order nodes is allocated for each element. - highorderindex = (elesize + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); - if (b->order == 2) { - elesize = (highorderindex + 1) * sizeof(tetrahedron); - } - // Having determined the memory size of an element, initialize the pool. - tetrahedrons = new memorypool(elesize, ELEPERBLOCK, POINTER, 8); - - if (b->useshelles) { - // The number of bytes occupied by a subface. The list of pointers - // stored in a subface are: three to other subfaces, three to corners, - // three to subsegments, two to tetrahedra, and one to a badface. - shsize = 12 * sizeof(shellface); - // The index within each subface at which the maximum area bound is - // found, where the index is measured in REALs. - areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL); - // If -q switch is in use, increase the number of bytes occupied by - // a subface for saving maximum area bound. - if (b->quality && varconstraint) { - 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. - subfaces = new memorypool(shsize, SUBPERBLOCK, POINTER, 8); - // Initialize the pool of subsegments. The subsegment's record is same - // with subface. - subsegs = new memorypool(shsize, SUBPERBLOCK, POINTER, 8); - // Initialize the "outer space" tetrahedron and omnipresent subface. - dummyinit(tetrahedrons->itemwords, subfaces->itemwords); - } else { - // Initialize the "outer space" tetrahedron. - dummyinit(tetrahedrons->itemwords, 0); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedrondealloc() Deallocate space for a tet., marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) -{ - // Set tetrahedron's vertices to NULL. This makes it possible to detect - // dead tetrahedra when traversing the list of all tetrahedra. - dyingtetrahedron[4] = (tetrahedron) NULL; - dyingtetrahedron[5] = (tetrahedron) NULL; - dyingtetrahedron[6] = (tetrahedron) NULL; - dyingtetrahedron[7] = (tetrahedron) NULL; - tetrahedrons->dealloc((void *) dyingtetrahedron); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() -{ - tetrahedron *newtetrahedron; - - do { - newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); - if (newtetrahedron == (tetrahedron *) NULL) { - return (tetrahedron *) NULL; - } - } while (newtetrahedron[7] == (tetrahedron) NULL); // Skip dead ones. - return newtetrahedron; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// shellfacedealloc() Deallocate space for a shellface, marking it dead. // -// Used both for dealloc a subface and subsegment. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh) -{ - // Set shellface's vertices to NULL. This makes it possible to detect dead - // shellfaces when traversing the list of all shellfaces. - dyingsh[3] = (shellface) NULL; - dyingsh[4] = (shellface) NULL; - dyingsh[5] = (shellface) NULL; - pool->dealloc((void *) dyingsh); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used // -// for both subfaces and subsegments pool traverse. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) -{ - shellface *newshellface; - - do { - newshellface = (shellface *) pool->traverse(); - if (newshellface == (shellface *) NULL) { - return (shellface *) NULL; - } - } while (newshellface[3] == (shellface) NULL); // Skip dead ones. - return newshellface; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// badfacedealloc() Deallocate space for a badface, marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::badfacedealloc(memorypool *pool, badface *dying) -{ - // Set badface's forg to NULL. This makes it possible to detect dead - // ones when traversing the list of all items. - dying->forg = (point) NULL; - pool->dealloc((void *) dying); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// badfacetraverse() Traverse the pools, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool) -{ - badface *newsh; - - do { - newsh = (badface *) pool->traverse(); - if (newsh == (badface *) NULL) { - return (badface *) NULL; - } - } while (newsh->forg == (point) NULL); // Skip dead ones. - return newsh; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// pointdealloc() Deallocate space for a point, marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::pointdealloc(point dyingpoint) -{ - // Mark the point as dead. This makes it possible to detect dead points - // when traversing the list of all points. - setpointtype(dyingpoint, DEADVERTEX); - points->dealloc((void *) dyingpoint); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// pointtraverse() Traverse the points, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::pointtraverse() -{ - point newpoint; - - do { - newpoint = (point) points->traverse(); - if (newpoint == (point) NULL) { - return (point) NULL; - } - } while (pointtype(newpoint) == DEADVERTEX); // Skip dead ones. - return newpoint; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// maketetrahedron() Create a new tetrahedron. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::maketetrahedron(triface *newtet) -{ - newtet->tet = (tetrahedron *) tetrahedrons->alloc(); - // Initialize the four adjoining tetrahedra to be "outer space". - newtet->tet[0] = (tetrahedron) dummytet; - newtet->tet[1] = (tetrahedron) dummytet; - newtet->tet[2] = (tetrahedron) dummytet; - newtet->tet[3] = (tetrahedron) dummytet; - // Four NULL vertices. - newtet->tet[4] = (tetrahedron) NULL; - newtet->tet[5] = (tetrahedron) NULL; - newtet->tet[6] = (tetrahedron) NULL; - newtet->tet[7] = (tetrahedron) NULL; - // Initialize the four adjoining subfaces to be the omnipresent subface. - if (b->useshelles) { - newtet->tet[8 ] = (tetrahedron) dummysh; - newtet->tet[9 ] = (tetrahedron) dummysh; - newtet->tet[10] = (tetrahedron) dummysh; - newtet->tet[11] = (tetrahedron) dummysh; - newtet->tet[12] = (tetrahedron) dummysh; - newtet->tet[13] = (tetrahedron) dummysh; - } - for (int i = 0; i < in->numberoftetrahedronattributes; i++) { - setelemattribute(newtet->tet, i, 0.0); - } - if (b->varvolume) { - setvolumebound(newtet->tet, -1.0); - } - // Initialize the location and version to be Zero. - newtet->loc = 0; - newtet->ver = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// makeshellface() Create a new shellface with version zero. Used for // -// both subfaces and seusegments. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::makeshellface(memorypool *pool, face *newface) -{ - newface->sh = (shellface *) pool->alloc(); - //Initialize the three adjoining subfaces to be the omnipresent subface. - newface->sh[0] = (shellface) dummysh; - newface->sh[1] = (shellface) dummysh; - newface->sh[2] = (shellface) dummysh; - // Three NULL vertices. - newface->sh[3] = (shellface) NULL; - newface->sh[4] = (shellface) NULL; - newface->sh[5] = (shellface) NULL; - // Initialize the two adjoining tetrahedra to be "outer space". - newface->sh[6] = (shellface) dummytet; - newface->sh[7] = (shellface) dummytet; - // Initialize the three adjoining subsegments to be the omnipresent - // subsegments. - newface->sh [8] = (shellface) dummysh; - newface->sh [9] = (shellface) dummysh; - newface->sh[10] = (shellface) dummysh; - // Initialize the pointer to badface structure. - newface->sh[11] = (shellface) NULL; - if (b->quality && varconstraint) { - // Initialize the maximum area bound. - setareabound(*newface, 0.0); - } - // Set the boundary marker to zero. - setshellmark(*newface, 0); - // Set the type. - setshelltype(*newface, NSHARP); - if (checkpbcs) { - // Set the pbcgroup be ivalid. - setshellpbcgroup(*newface, -1); - } - // Initialize the version to be Zero. - newface->shver = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// makepoint() Create a new point. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::makepoint(point* pnewpoint) -{ - int ptmark, i; - - *pnewpoint = (point) points->alloc(); - // Initialize three coordinates. - (*pnewpoint)[0] = 0.0; - (*pnewpoint)[1] = 0.0; - (*pnewpoint)[2] = 0.0; - // Initialize the list of user-defined attributes. - for (i = 0; i < in->numberofpointattributes; i++) { - (*pnewpoint)[3 + i] = 0.0; - } - // Initialize the metric tensor. - for (i = 0; i < sizeoftensor; i++) { - (*pnewpoint)[pointmtrindex + i] = 0.0; - } - if (b->plc || b->refine) { - // Initialize the point-to-simplex filed. - setpoint2tet(*pnewpoint, NULL); - setpoint2sh(*pnewpoint, NULL); - setpoint2ppt(*pnewpoint, NULL); - if (b->metric) { - setpoint2bgmtet(*pnewpoint, NULL); - } - if (checkpbcs) { - // Initialize the other pointer to its pbc point. - setpoint2pbcpt(*pnewpoint, NULL); - } - } - // Initialize the point marker (starting from in->firstnumber). - ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1); - setpointmark(*pnewpoint, ptmark); - // Initialize the point type. - setpointtype(*pnewpoint, UNUSEDVERTEX); -} - -// -// End of memory management routines -// - -// -// Begin of point location routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// randomnation() Generate a random number between 0 and 'choices' - 1. // -// // -/////////////////////////////////////////////////////////////////////////////// - -unsigned long tetgenmesh::randomnation(unsigned int choices) -{ - unsigned long newrandom; - - if (choices >= 714025l) { - newrandom = (randomseed * 1366l + 150889l) % 714025l; - randomseed = (newrandom * 1366l + 150889l) % 714025l; - newrandom = newrandom * (choices / 714025l) + randomseed; - if (newrandom >= choices) { - return newrandom - choices; - } else { - return newrandom; - } - } else { - randomseed = (randomseed * 1366l + 150889l) % 714025l; - return randomseed % choices; - } - // Old function. - // randomseed = (randomseed * 1366l + 150889l) % 714025l; - // return randomseed / (714025l / choices + 1); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// distance2() Returns the square "distance" of a tetrahedron to point p. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::distance2(tetrahedron* tetptr, point p) -{ - point p1, p2, p3, p4; - REAL dx, dy, dz; - - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; - p3 = (point) tetptr[6]; - p4 = (point) tetptr[7]; - - dx = p[0] - 0.25 * (p1[0] + p2[0] + p3[0] + p4[0]); - dy = p[1] - 0.25 * (p1[1] + p2[1] + p3[1] + p4[1]); - dz = p[2] - 0.25 * (p1[2] + p2[2] + p3[2] + p4[2]); - - return dx * dx + dy * dy + dz * dz; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// preciselocate() 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 // -// a vertex of 'searchtet' to the query point 'searchpt', and simply walk // -// towards 'searchpt' by traversing all faces intersected by L. // -// // -// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The // -// returned value indicates one of the following cases: // -// - Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' // -// is a handle whose origin is the existing vertex. // -// - Returns ONEDGE if the point lies on a mesh edge. 'searchtet' is a // -// handle whose primary edge is the edge on which the point lies. // -// - Returns ONFACE if the point lies strictly within a face. 'searchtet' // -// is a handle whose primary face is the face on which the point lies. // -// - Returns INTETRAHEDRON if the point lies strictly in a tetrahededron. // -// 'searchtet' is a handle on the tetrahedron that contains the point. // -// - Returns OUTSIDE if the point lies outside the mesh. 'searchtet' is a // -// handle whose location is the face the point is to 'above' of. // -// // -// WARNING: This routine is designed for convex triangulations, and will not // -// generally work after the holes and concavities have been carved. // -// // -// If 'maxtetnumber' > 0, stop the searching process if the number of passed // -// tets is larger than it and return OUTSIDE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh::preciselocate(point searchpt, - triface* searchtet, long maxtetnumber) -{ - triface backtracetet; - triface walkthroface; - point forg, fdest, fapex, toppo; - REAL ori1, ori2, ori3, ori4; - long tetnumber; - int side; - - if (isdead(searchtet)) searchtet->tet = dummytet; - if (searchtet->tet == dummytet) { - searchtet->loc = 0; - symself(*searchtet); - } - // 'searchtet' should be a valid tetrahedron now. -#ifdef SELF_CHECK - // assert(!isdead(searchtet) && (searchtet->tet != dummytet)); -#endif - if (isdead(searchtet)) { - printf("Warning: Point location failed.\n"); - return OUTSIDE; - } - - searchtet->ver = 0; // Keep in CCW edge ring. - // Find a face of 'searchtet' such that the 'searchpt' lies strictly - // above it. Such face should always exist. - for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) { - forg = org(*searchtet); - fdest = dest(*searchtet); - fapex = apex(*searchtet); - ori1 = orient3d(forg, fdest, fapex, searchpt); - if (ori1 < 0.0) break; - } -#ifdef SELF_CHECK - assert(searchtet->loc < 4); -#endif - - // Define 'tetnumber' for exit the loop when it's running endless. - tetnumber = 0l; - while ((maxtetnumber > 0l) && (tetnumber <= maxtetnumber)) { - // Check if we are reaching the boundary of the triangulation. - if (searchtet->tet == dummytet) { - *searchtet = backtracetet; - return OUTSIDE; - } - // Initialize the face for returning the walk-through face. - walkthroface.tet = (tetrahedron *) NULL; - // Adjust the edge ring, so that 'ori1 < 0.0' holds. - searchtet->ver = 0; - // 'toppo' remains unchange for the following orientation tests. - toppo = oppo(*searchtet); - // Check the three sides of 'searchtet' to find the face through which - // we can walk next. - for (side = 0; side < 3; side++) { - forg = org(*searchtet); - fdest = dest(*searchtet); - ori2 = orient3d(forg, fdest, toppo, searchpt); - if (ori2 == 0.0) { - // They are coplanar, check if 'searchpt' lies inside, or on an edge, - // or coindice with a vertex of face (forg, fdest, toppo). - fapex = apex(*searchtet); - ori3 = orient3d(fdest, fapex, toppo, searchpt); - if (ori3 < 0.0) { - // Outside the face (fdest, fapex, toppo), walk through it. - enextself(*searchtet); - fnext(*searchtet, walkthroface); - break; - } - ori4 = orient3d(fapex, forg, toppo, searchpt); - if (ori4 < 0.0) { - // Outside the face (fapex, forg, toppo), walk through it. - enext2self(*searchtet); - fnext(*searchtet, walkthroface); - break; - } - // Remember, ori1 < 0.0, which means 'searchpt' will not on edge - // (forg, fdest) or on vertex forg or fdest. -#ifdef SELF_CHECK - assert(ori1 < 0.0); -#endif - // The rest possible cases are: - // (1) 'searchpt' lies on edge (fdest, toppo); - // (2) 'searchpt' lies on edge (toppo, forg); - // (3) 'searchpt' coincident with toppo; - // (4) 'searchpt' lies inside face (forg, fdest, toppo). - fnextself(*searchtet); - if (ori3 == 0.0) { - if (ori4 == 0.0) { - // Case (4). - enext2self(*searchtet); - return ONVERTEX; - } else { - // Case (1). - enextself(*searchtet); - return ONEDGE; - } - } - if (ori4 == 0.0) { - // Case (2). - enext2self(*searchtet); - return ONEDGE; - } - // Case (4). - return ONFACE; - } else if (ori2 < 0.0) { - // Outside the face (forg, fdest, toppo), walk through it. - fnext(*searchtet, walkthroface); - break; - } - // Go to check next side. - enextself(*searchtet); - } - if (side >= 3) { - // Found! Inside tetrahedron. - return INTETRAHEDRON; - } - // We walk through the face 'walkthroface' and continue the searching. -#ifdef SELF_CHECK - assert(walkthroface.tet != (tetrahedron *) NULL); -#endif - // Store the face handle in 'backtracetet' before we take the real walk. - // So we are able to restore the handle to 'searchtet' if we are - // reaching the outer boundary. - backtracetet = walkthroface; - sym(walkthroface, *searchtet); - tetnumber++; - } - - // Should never be here. - // printf("Internal error in preciselocate(): Point location failed.\n"); - // internalerror(); - return OUTSIDE; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// locate() Find a simplex containing a given point. // -// // -// 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 barycenter is closest to the point we are searcing for. Having chosen // -// the starting tetrahedron, the simple Walk-through algorithm is used to do // -// the real walking. // -// // -// The return value indicates the location of the 'searchpt' (INTETRAHEDRON, // -// or ONFACE, ...). 'searchtet' is adjusted to a tetrahedron corresponding // -// to that value. See the introduction part of preciselocate() for detail. // -// // -// WARNING: This routine is designed for convex triangulations, and will not // -// generally work after the holes and concavities have been carved. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, - triface *searchtet) -{ - tetrahedron *firsttet, *tetptr; - void **sampleblock; - long sampleblocks, samplesperblock, samplenum; - long tetblocks, i, j; - unsigned long alignptr; - REAL searchdist, dist; - - // 'searchtet' should be a valid tetrahedron. - if (isdead(searchtet)) { - searchtet->tet = dummytet; - } - if (searchtet->tet == dummytet) { - // This is an 'Outer Space' handle, get a hull tetrahedron. - searchtet->loc = 0; - symself(*searchtet); - } -#ifdef SELF_CHECK - // assert(!isdead(searchtet)); -#endif - if (isdead(searchtet)) { - printf("Warning: Point location failed.\n"); - return OUTSIDE; - } - - // Get the distance from the suggested starting tet to the point we seek. - searchdist = distance2(searchtet->tet, searchpt); - - // If a recently encountered tetrahedron has been recorded and has not - // been deallocated, test it as a good starting point. - if (!isdead(&recenttet) && (recenttet.tet != searchtet->tet)) { - dist = distance2(recenttet.tet, searchpt); - if (dist < searchdist) { - *searchtet = recenttet; - searchdist = dist; - } - } - - // Select "good" candidate using k random samples, taking the closest one. - // The number of random samples taken is proportional to the fourth root - // of the number of tetrahedra in the mesh. The next bit of code assumes - // that the number of tetrahedra increases monotonically. - while (SAMPLEFACTOR * samples * samples * samples * samples < - tetrahedrons->items) { - samples++; - } - // Find how much blocks in current tet pool. - tetblocks = (tetrahedrons->maxitems + ELEPERBLOCK - 1) / ELEPERBLOCK; - // Find the average samles per block. Each block at least have 1 sample. - samplesperblock = 1 + (samples / tetblocks); - sampleblocks = samples / samplesperblock; - sampleblock = tetrahedrons->firstblock; - for (i = 0; i < sampleblocks; i++) { - alignptr = (unsigned long) (sampleblock + 1); - firsttet = (tetrahedron *) - (alignptr + (unsigned long) tetrahedrons->alignbytes - - (alignptr % (unsigned long) tetrahedrons->alignbytes)); - for (j = 0; j < samplesperblock; j++) { - if (i == tetblocks - 1) { - // This is the last block. - samplenum = randomnation((int) - (tetrahedrons->maxitems - (i * ELEPERBLOCK))); - } else { - samplenum = randomnation(ELEPERBLOCK); - } - tetptr = (tetrahedron *) - (firsttet + (samplenum * tetrahedrons->itemwords)); - if (tetptr[4] != (tetrahedron) NULL) { - dist = distance2(tetptr, searchpt); - if (dist < searchdist) { - searchtet->tet = tetptr; - searchdist = dist; - } - } - } - sampleblock = (void **) *sampleblock; - } - - // Call simple walk-through to locate the point. - return preciselocate(searchpt, searchtet, tetrahedrons->items); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// adjustlocate() Adjust the precise location of a vertex. // -// // -// 'precise' is the value returned from preciselocate(). It indicates the // -// exact location of the point 'searchpt' with respect to the tetrahedron // -// 'searchtet'. 'epspp' is a given relative tolerance. // -// // -// This routine re-evaluates the orientations of searchpt with respect to // -// the four sides of searchtet. Detects the coplanarities by additinal tests // -// which are based on the given tolerance. If 'precise' is ONFACE or ONEDGE, // -// we can save one or two orientation tests. // -// // -// The return value indicates the location of the 'searchpt' (INTETRAHEDRON, // -// or ONFACE, ...). 'searchtet' is adjusted to a tetrahedron corresponding // -// to that value. See the introduction part of preciselocate() for detail. // -// // -// WARNING: This routine detect degenerate case using relative tolerance. // -// It is better used after locate() or preciselocate(). For general inputs, // -// it may not able to tell the correct location. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh::adjustlocate(point searchpt, - triface* searchtet, enum locateresult precise, REAL epspp) -{ - point torg, tdest, tapex, toppo; - REAL s1, s2, s3, s4; - - // For the given 'searchtet', the orientations tests are: - // s1: (tdest, torg, tapex, searchpt); - // s2: (torg, tdest, toppo, searchpt); - // s3: (tdest, tapex, toppo, searchpt); - // s4: (tapex, torg, toppo, searchpt); - adjustedgering(*searchtet, CCW); - torg = org(*searchtet); - tdest = dest(*searchtet); - tapex = apex(*searchtet); - toppo = oppo(*searchtet); - - switch (precise) { - case ONVERTEX: - // This case we don't need do any further test. - return ONVERTEX; - case ONEDGE: - // (torg, tdest); - s1 = 0.0; - s2 = 0.0; - break; - case ONFACE: - // (tdest, torg, tapex); - s1 = 0.0; - s2 = orient3d(torg, tdest, toppo, searchpt); - break; - default: // INTETRAHEDRON or OUTSIDE - s1 = orient3d(tdest, torg, tapex, searchpt); - s2 = orient3d(torg, tdest, toppo, searchpt); - } - - if (s1 != 0.0) { - if (iscoplanar(tdest, torg, tapex, searchpt, s1, epspp)) { - s1 = 0.0; - } - } - if (s1 < 0.0) { - return OUTSIDE; - } - - if (s2 != 0.0) { - if (iscoplanar(torg, tdest, toppo, searchpt, s2, epspp)) { - s2 = 0.0; - } - } - if (s2 < 0.0) { - fnextself(*searchtet); - return OUTSIDE; - } - - s3 = orient3d(tdest, tapex, toppo, searchpt); - if (s3 != 0.0) { - if (iscoplanar(tdest, tapex, toppo, searchpt, s3, epspp)) { - s3 = 0.0; - } - } - if (s3 < 0.0) { - enextfnextself(*searchtet); - return OUTSIDE; - } - - s4 = orient3d(tapex, torg, toppo, searchpt); - if (s4 != 0.0) { - if (iscoplanar(tapex, torg, toppo, searchpt, s4, epspp)) { - s4 = 0.0; - } - } - if (s4 < 0.0) { - enext2fnextself(*searchtet); - return OUTSIDE; - } - - // Determine degenerate cases. - if (s1 == 0.0) { - if (s2 == 0.0) { - if (s3 == 0.0) { - // On tdest. - enextself(*searchtet); - return ONVERTEX; - } - if (s4 == 0.0) { - // On torg. - return ONVERTEX; - } - // On edge (torg, tdest). - return ONEDGE; - } - if (s3 == 0.0) { - if (s4 == 0.0) { - // On tapex. - enext2self(*searchtet); - return ONVERTEX; - } - // On edge (tdest, tapex). - enextself(*searchtet); - return ONEDGE; - } - if (s4 == 0.0) { - // On edge (tapex, torg). - enext2self(*searchtet); - return ONEDGE; - } - // On face (torg, tdest, tapex). - return ONFACE; - } - if (s2 == 0.0) { - fnextself(*searchtet); - if (s3 == 0.0) { - if (s4 == 0.0) { - // On toppo. - enext2self(*searchtet); - return ONVERTEX; - } - // On edge (tdest, toppo). - enextself(*searchtet); - return ONEDGE; - } - if (s4 == 0.0) { - // On edge (toppo, torg). - enext2self(*searchtet); - return ONEDGE; - } - // On face (torg, tdest, toppo). - return ONFACE; - } - if (s3 == 0.0) { - enextfnextself(*searchtet); - if (s4 == 0.0) { - // On edge (tapex, toppo). - enextself(*searchtet); - return ONEDGE; - } - // On face (tdest, tapex, toppo). - return ONFACE; - } - if (s4 == 0.0) { - enext2fnextself(*searchtet); - // On face (tapex, torg, toppo). - return ONFACE; - } - - // Inside tetrahedron. - return INTETRAHEDRON; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// hullwalk() Find a tetrahedron on the hull to continue search. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh::hullwalk(point searchpt, - triface *hulltet) -{ - list* travtetlist; - triface travtet, neightet; - point pa, pb, pc; - enum locateresult loc; - REAL ori; - int i; - - travtetlist = new list(sizeof(triface), NULL, 256); - travtet = *hulltet; - infect(travtet); - travtetlist->append(&travtet); - - loc = OUTSIDE; - for (i = 0; i < travtetlist->len(); i++) { - travtet = * (triface *)(* travtetlist)[i]; - // Choose the CCW-edgering in face. - travtet.ver = 0; - // Look for a side where pt lies below it. - for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) { - pa = org(travtet); - pb = dest(travtet); - pc = apex(travtet); - ori = orient3d(pa, pb, pc, searchpt); - if (ori > 0.0) break; - } - // Is pt above all (or coplanar with some of) the four sides? - if (travtet.loc == 4) { - hulltet->tet = travtet.tet; - loc = adjustlocate(searchpt, hulltet, INTETRAHEDRON, b->epsilon); - assert(loc != OUTSIDE); - } else { // ori > 0.0 - // pt is below (behind) this side. We want to walk through it. - sym(travtet, neightet); - if (neightet.tet == dummytet) { - // This is a hull side. Is p approximately on this side. - loc = adjustlocate(searchpt, &travtet, OUTSIDE, b->epsilon); - } - if (loc == OUTSIDE) { - // Let's collect all the neighbors for next searching. - for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) { - sym(travtet, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Neighbor exists and not visited. - infect(neightet); - travtetlist->append(&neightet); - } - } // for (travtet.loc = 0; - } // if (loc == OUTSIDE) - } // if (travtet.loc == 4) - if (loc != OUTSIDE) break; - } // for (i = 0; i < travtetlist->len(); i++) - - // Uninfect traversed tets. - for (i = 0; i < travtetlist->len(); i++) { - travtet = * (triface *)(* travtetlist)[i]; - uninfect(travtet); - } - - delete travtetlist; - return loc; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// locatesub() Find a point in the surface mesh of a facet. // -// // -// Searching begins from the input 'searchsh', it should be a handle on the // -// convex hull of the facet triangulation. // -// // -// If 'stopatseg' is nonzero, the search will stop if it tries to walk // -// through a subsegment, and will return OUTSIDE. // -// // -// On completion, 'searchsh' is a subface that contains 'searchpt'. // -// - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh' // -// is a handle whose origin is the existing vertex. // -// - Returns ONEDGE if the point lies on a mesh edge. 'searchsh' is a // -// handle whose primary edge is the edge on which the point lies. // -// - Returns ONFACE if the point lies strictly within a subface. // -// 'searchsh' is a handle on which the point lies. // -// - Returns OUTSIDE if the point lies outside the triangulation. // -// // -// WARNING: This routine is designed for convex triangulations, and will not // -// not generally work after the holes and concavities have been carved. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh::locatesub(point searchpt, - face* searchsh, int stopatseg, REAL epspp) -{ - face backtracksh, spinsh, checkedge; - point forg, fdest, fapex; - REAL orgori, destori; - REAL ori, sign; - int moveleft, i; - - if (searchsh->sh == dummysh) { - searchsh->shver = 0; - spivotself(*searchsh); -#ifdef SELF_CHECK - assert(searchsh->sh != dummysh); -#endif - } - // Find the sign to simulate that abovepoint is 'above' the facet. - adjustedgering(*searchsh, CCW); - forg = sorg(*searchsh); - fdest = sdest(*searchsh); - fapex = sapex(*searchsh); - ori = orient3d(forg, fdest, fapex, abovepoint); - sign = ori > 0.0 ? -1 : 1; - - // Orient 'searchsh' so that 'searchpt' is below it (i.e., searchpt has - // CCW orientation with respect to searchsh in plane). Such edge - // should always exist. Save it as (forg, fdest). - for (i = 0; i < 3; i++) { - forg = sorg(*searchsh); - fdest = sdest(*searchsh); - ori = orient3d(forg, fdest, abovepoint, searchpt) * sign; - if (ori > 0.0) break; - senextself(*searchsh); - } -#ifdef SELF_CHECK - assert(i < 3); -#endif - - while (1) { - fapex = sapex(*searchsh); - // Check whether the apex is the point we seek. - if (fapex[0] == searchpt[0] && fapex[1] == searchpt[1] && - fapex[2] == searchpt[2]) { - senext2self(*searchsh); - return ONVERTEX; - } - // Does the point lie on the other side of the line defined by the - // triangle edge opposite the triangle's destination? - destori = orient3d(forg, fapex, abovepoint, searchpt) * sign; - if (epspp > 0.0) { - if (iscoplanar(forg, fapex, abovepoint, searchpt, destori, epspp)) { - destori = 0.0; - } - } - // Does the point lie on the other side of the line defined by the - // triangle edge opposite the triangle's origin? - orgori = orient3d(fapex, fdest, abovepoint, searchpt) * sign; - if (epspp > 0.0) { - if (iscoplanar(fapex, fdest, abovepoint, searchpt, orgori, epspp)) { - orgori = 0.0; - } - } - if (destori > 0.0) { - moveleft = 1; - } else { - if (orgori > 0.0) { - moveleft = 0; - } else { - // The point must be on the boundary of or inside this triangle. - if (destori == 0.0) { - senext2self(*searchsh); - return ONEDGE; - } - if (orgori == 0.0) { - senextself(*searchsh); - return ONEDGE; - } - return ONFACE; - } - } - // Move to another triangle. Leave a trace `backtracksh' in case - // walking off a boundary of the triangulation. - if (moveleft) { - senext2(*searchsh, backtracksh); - fdest = fapex; - } else { - senext(*searchsh, backtracksh); - forg = fapex; - } - // Check if we meet a segment. - sspivot(backtracksh, checkedge); - if (checkedge.sh != dummysh) { - if (stopatseg) { - // The flag indicates we should not cross a segment. Stop. - *searchsh = backtracksh; - return OUTSIDE; - } - // Try to walk through a segment. We need to find a coplanar subface - // sharing this segment to get into. - spinsh = backtracksh; - do { - spivotself(spinsh); - if (spinsh.sh == backtracksh.sh) { - // Turn back, no coplanar subface is found. - break; - } - // Are they belong to the same facet. - if (shellmark(spinsh) == shellmark(backtracksh)) { - // Find a coplanar subface. Walk into it. - *searchsh = spinsh; - break; - } - // Are they (nearly) coplanar? - ori = orient3d(forg, fdest, sapex(backtracksh), sapex(spinsh)); - if (iscoplanar(forg, fdest, sapex(backtracksh), sapex(spinsh), ori, - b->epsilon)) { - // Find a coplanar subface. Walk into it. - *searchsh = spinsh; - break; - } - } while (spinsh.sh != backtracksh.sh); - } else { - spivot(backtracksh, *searchsh); - } - // Check for walking right out of the triangulation. - if ((searchsh->sh == dummysh) || (searchsh->sh == backtracksh.sh)) { - // Go back to the last triangle. - *searchsh = backtracksh; - return OUTSIDE; - } - // To keep the same orientation wrt abovepoint. - if (sorg(*searchsh) != forg) sesymself(*searchsh); -#ifdef SELF_CHECK - assert((sorg(*searchsh) == forg) && (sdest(*searchsh) == fdest)); -#endif - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// adjustlocatesub() Adjust the precise location of a vertex. // -// // -// 'precise' is the precise location (returned from locatesub()) of 'searcht'// -// with respect to 'searchsh'. 'epspp' is the given relative tolerance. // -// // -// This routine re-evaluates the orientations of 'searchpt' with respect to // -// the three edges of 'searchsh'. Detects the collinearities by additinal // -// tests based on the given tolerance. If 'precise' is ONEDGE, one can save // -// one orientation test for the current edge of 'searchsh'. // -// // -// On completion, 'searchsh' is a subface contains 'searchpt'. The returned // -// value indicates one of the following cases: // -// - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh' // -// is a handle whose origin is the existing vertex. // -// - Returns ONEDGE if the point lies on a mesh edge. 'searchsh' is a // -// handle whose primary edge is the edge on which the point lies. // -// - Returns ONFACE if the point lies strictly within a subface. // -// 'searchsh' is a handle on which the point lies. // -// - Returns OUTSIDE if the point lies outside 'searchsh'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh:: -adjustlocatesub(point searchpt, face* searchsh, enum locateresult precise, - REAL epspp) -{ - point pa, pb, pc; - bool s1, s2, s3; - - pa = sorg(*searchsh); - pb = sdest(*searchsh); - pc = sapex(*searchsh); - - if (precise == ONEDGE) { - s1 = true; - } else { - s1 = iscollinear(pa, pb, searchpt, epspp); - } - s2 = iscollinear(pb, pc, searchpt, epspp); - s3 = iscollinear(pc, pa, searchpt, epspp); - if (s1) { - if (s2) { - // on vertex pb. -#ifdef SELF_CHECK - assert(!s3); -#endif - senextself(*searchsh); - return ONVERTEX; - } else if (s3) { - // on vertex pa. - return ONVERTEX; - } else { - // on edge pa->pb. - return ONEDGE; - } - } else if (s2) { - if (s3) { - // on vertex pc. - senext2self(*searchsh); - return ONVERTEX; - } else { - // on edge pb->pc. - senextself(*searchsh); - return ONEDGE; - } - } else if (s3) { - // on edge pc->pa. - senext2self(*searchsh); - return ONEDGE; - } else { - return precise; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// locateseg() Find a point in subsegments. // -// // -// Searching begins from the input 'searchseg', it should be a subsegment of // -// the whole segment. // -// // -// On completion, 'searchseg' is a subsegment that contains 'searchpt'. // -// - Returns ONVERTEX if the point lies on an existing vertex. 'searchseg' // -// is a handle whose origin is the existing vertex. // -// - Returns ONEDGE if the point lies inside 'searchseg'. // -// - Returns OUTSIDE if the point lies outside the segment. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh:: -locateseg(point searchpt, face* searchseg) -{ - face backtraceseg; - point pa, pb; - REAL dx, dy, dz; - int moveleft; - int i; - - moveleft = 0; - while (1) { - searchseg->shver = 0; - pa = sorg(*searchseg); - pb = sdest(*searchseg); - // Find the biggest difference in x, y, and z coordinates of a and b. - dx = fabs(pb[0] - pa[0]); - dy = fabs(pb[1] - pa[1]); - dz = fabs(pb[2] - pa[2]); - if (dx > dy) { - if (dx > dz) { - i = 0; - } else { - i = 2; - } - } else { - if (dy > dz) { - i = 1; - } else { - i = 2; - } - } - if (pa[i] < pb[i]) { - if (searchpt[i] < pa[i]) { - moveleft = 1; - } else if (searchpt[i] > pa[i]) { - if (searchpt[i] < pb[i]) { - return ONEDGE; - } else if (searchpt[i] > pb[i]) { - moveleft = 0; - } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pb[i]); -#endif - sesymself(*searchseg); - return ONVERTEX; - } - } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pa[i]); -#endif - return ONVERTEX; - } - } else if (pa[i] > pb[i]) { - if (searchpt[i] < pb[i]) { - moveleft = 0; - } else if (searchpt[i] > pb[i]) { - if (searchpt[i] < pa[i]) { - return ONEDGE; - } else if (searchpt[i] > pa[i]) { - moveleft = 1; - } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pa[i]); -#endif - return ONVERTEX; - } - } else { -#ifdef SELF_CHECK - assert(searchpt[i] == pb[i]); -#endif - sesymself(*searchseg); - return ONVERTEX; - } - } - backtraceseg = *searchseg; - if (moveleft) { - senext2self(*searchseg); - } else { - senextself(*searchseg); - } - spivotself(*searchseg); - if (searchseg->sh == dummysh) { - *searchseg = backtraceseg; - break; - } - } - - return OUTSIDE; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// adjustlocateseg() Adjust the precise location of a vertex on segment. // -// // -// 'searchpt' is either inside or ouside the segment 'searchseg'. It will be // -// adjusted to on vertex if it is very close to an endpoint of 'searchseg'. // -// 'epspp' is the given relative tolerance. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh:: -adjustlocateseg(point searchpt, face* searchseg, enum locateresult precise, - REAL epspp) -{ - point pa, pb; - REAL L, d, r; - - pa = sorg(*searchseg); - pb = sdest(*searchseg); - L = distance(pa, pb); - - // Is searchpt approximate to pa? - d = distance(pa, searchpt); - r = d / L; - if (r <= epspp) { - return ONVERTEX; - } - // Is searchpt approximate to pb? - d = distance(pb, searchpt); - r = d / L; - if (r <= epspp) { - sesymself(*searchseg); - return ONVERTEX; - } - - return precise; -} - -// -// End of point location routines -// - -// -// Begin of mesh transformation routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// Flip operations // -// // -// If abc is a hull face, it is unflipable, and is locally Delaunay. In the // -// following, we assume abc is an interior face, and the other tetrahedron // -// adjoining at abc is bace. // -// // -// If the convex hull CH of the set {a, b, c, d, e} only has four vertices, // -// i.e., one vertex lies inside CH, then abc is unflipable, and is locally // -// Delaunay. If CH is the vertex set itself, we have the following cases to // -// determine whether abc is flipable or not. // -// // -// If no four points of {a, b, c, d, e} are coplanar, a 2-to-3 flip can be // -// applied to abc if the edge de crosses the triangle abc; a 3-to-2 flip can // -// be applied to abc if ab crosses cde, and abde exists, otherwise, face abc // -// is unflipable, i.e., the tetrahedron abde is not present. // -// // -// If four points of {a, b, c, d, e} are coplanar (two faces are coplanar). // -// Assume faces abd and abe are coplanar (it is impossible be abc). If a, b, // -// d, e form a non-convex quadrilateral, then abc is unflipable, furthermore,// -// it is locally Delaunay. Assume they are convex quadrilateral, if abd and // -// abe are hull faces, a 2-to-2 flip can be applied to abc; if abd and abe // -// are interior faces, assume two tetrahedra adjoining abd and abe at the // -// opposite sides are abdg and abef, respectively. If g = f, a 4-to-4 flip // -// can be applied to abc, otherwise, abc is unflipable. // -// // -// There are other cases which can cause abc unflipable. If abc is a subface,// -// a 2-to-3 flip is forbidden; if ab is a subsegment, flips 3-to-2, 2-to-2, // -// and 4-to-4 are forbidden. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// categorizeface() Determine the flip type of a given face. // -// // -// On input, 'horiz' represents the face abc we want to flip (imagine it is // -// parallel to the horizon). Let the tet above it be abcd. // -// // -// This routine determines the suitable type of flip operation for 'horiz'. // -// - Returns T23 if a 2-to-3 flip is applicable. 'horiz' is same as input. // -// - Returns T32 if a 3-to-2 flip is applicable. 'horiz' returns the edge // -// of abc which is the flipable. // -// - Returns T22 if a 2-to-2 or 4-to-4 flip is applicable. 'horiz' returns // -// the edge of abc which is flipable. // -// - Returns N32 indicates it is unflipable due to the absence of a tet. // -// 'horize' returns the unflipable edge. // -// - Returns N40 indicates it is unflipable and is locally Delaunay. // -// - Returns FORBIDDENFACE indicates abc is a subface. // -// - Returns FORBIDDENEDGE indicates the flipable edge of abc is a segment.// -// 'horize' returns the flipable edge. // -// // -// Given a face abc, with two adjoining tetrahedra abcd and bace. If abc is // -// flipable, i.e., T23, T32, T22 or T44, its flip type can be determined by // -// doing five orientation tests: two tests for determining that d, e lie on // -// the different sides of abc, three tests for determining if the edge de // -// intersects the face abc. However, if we use the neighbor information of // -// the mesh data structure, we can reduce the five orientation tests to at // -// most three tests, that is, the two tests for determining whether d and e // -// lie on the different sides of abc can be saved. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::fliptype tetgenmesh::categorizeface(triface& horiz) -{ - triface symhoriz, casing; - face checksh, checkseg; - face cassh1, cassh2; - point pa, pb, pc, pd, pe, pf, pg; - point abdoppo, bcdoppo, cadoppo; - REAL ori1, ori2, ori3; - int adjtet; - - sym(horiz, symhoriz); - if (symhoriz.tet == dummytet) { - // A hull face is unflipable and locally Delaunay. - return N40; - } - - adjustedgering(horiz, CCW); - findedge(&symhoriz, dest(horiz), org(horiz)); - pa = org(horiz); - pb = dest(horiz); - pc = apex(horiz); - pd = oppo(horiz); - pe = oppo(symhoriz); - - // Find the number of adjacent tetrahedra of abc, which have d, e, and one - // of corners of abc as their corners. This number can be 0, 1 and 2. - abdoppo = bcdoppo = cadoppo = (point) NULL; - adjtet = 0; - fnext(horiz, casing); // at edge 'ab'. - symself(casing); - if (casing.tet != dummytet) { - abdoppo = oppo(casing); - if (abdoppo == pe) adjtet++; - } - enextfnext(horiz, casing); // at edge 'bc'. - symself(casing); - if (casing.tet != dummytet) { - bcdoppo = oppo(casing); - if (bcdoppo == pe) adjtet++; - } - enext2fnext(horiz, casing); // at edge 'ca'. - symself(casing); - if (casing.tet != dummytet) { - cadoppo = oppo(casing); - if (cadoppo == pe) adjtet++; - } - - if (adjtet == 0) { - // No adjacent tetrahedron. Types T23, T22 and T44 are possible. - ori1 = orient3d(pa, pb, pd, pe); - if (checksubfaces && ori1 != 0.0) { - // Check if abd and abe are both boundary faces? - fnext(horiz, casing); - tspivot(casing, cassh1); - fnext(symhoriz, casing); - tspivot(casing, cassh2); - if ((cassh1.sh != dummysh) && (cassh2.sh != dummysh)) { - // abd and abe are both boundary faces. Check if ab is a segment. - findedge(&cassh1, pa, pb); - sspivot(cassh1, checkseg); - if (checkseg.sh == dummysh) { - // ab is not a segment - abd and abe belong to the same facet. - // The four points are forced to be coplanar. - ori1 = 0.0; - } else { - // ab is a segment - abd and abe belong to two different facets. - // In principle, a, b, c and d can form a tetrahedron (since - // ori1 != 0.0). However, we should avoid to create a very - // flat one which may form a sequence of extremely badly-shaped - // or even wrong orientational tets. Test with a larger epsilon. - if (iscoplanar(pa, pb, pd, pe, ori1, b->epsilon * 1e+2)) ori1 = 0.0; - } - } else { - // abd and abe are not both boundary faces. Check if abd and bae - // are approximately coplanar with respect to the epsilon. - if (iscoplanar(pa, pb, pd, pe, ori1, b->epsilon)) ori1 = 0.0; - } - } - if (ori1 < 0.0) { - // e lies above abd, unflipable, tet abde is not present. -#ifdef SELF_CHECK - if (!nonconvex) { - // abd and abe should not be hull faces, check it. - fnext(horiz, casing); - symself(casing); - assert(casing.tet != dummytet); - fnext(symhoriz, casing); - symself(casing); - assert(casing.tet != dummytet); - } -#endif - if (checksubfaces) { - // The nonconvexbility may be casued by existing an subsegment. - tsspivot(&horiz, &checkseg); - if (checkseg.sh != dummysh) { - return FORBIDDENEDGE; - } - } - return N32; - } - ori2 = orient3d(pb, pc, pd, pe); - if (checksubfaces && ori2 != 0.0) { - // Check if bcd and cbe are both boundary faces. - enextfnext(horiz, casing); - tspivot(casing, cassh1); - enext2fnext(symhoriz, casing); - tspivot(casing, cassh2); - if (cassh1.sh != dummysh && cassh2.sh != dummysh) { - // bcd and cbe are both boundary faces. Check if bc is a segment. - findedge(&cassh1, pb, pc); - sspivot(cassh1, checkseg); - if (checkseg.sh == dummysh) { - // bc is not a segment - bcd and cbe belong to the same facet. - // The four points are forced to be coplanar. - ori2 = 0.0; - } else { - if (iscoplanar(pb, pc, pd, pe, ori2, b->epsilon * 1e+2)) ori2 = 0.0; - } - } else { - // bcd and cbe are not both boundary faces. Check if bcd and cbe - // are approximately coplanar with respect to the epsilon. - if (iscoplanar(pb, pc, pd, pe, ori2, b->epsilon)) ori2 = 0.0; - } - } - if (ori2 < 0.0) { - // e lies above bcd, unflipable, tet bcde is not present. -#ifdef SELF_CHECK - if (!nonconvex) { - // bcd and cbe should not be hull faces, check it. - enextfnext(horiz, casing); - symself(casing); - assert(casing.tet != dummytet); - enext2fnext(symhoriz, casing); - symself(casing); - assert(casing.tet != dummytet); - } -#endif - enextself(horiz); - if (checksubfaces) { - // The nonconvexbility may be casued by existing an subsegment. - tsspivot(&horiz, &checkseg); - if (checkseg.sh != dummysh) { - return FORBIDDENEDGE; - } - } - return N32; - } - ori3 = orient3d(pc, pa, pd, pe); - if (checksubfaces && ori3 != 0.0) { - // Check if cad and ace are both boundary faces. - enext2fnext(horiz, casing); - tspivot(casing, cassh1); - enextfnext(symhoriz, casing); - tspivot(casing, cassh2); - if (cassh1.sh != dummysh && cassh2.sh != dummysh) { - // cad and ace are both boundary faces. Check if ca is a segment. - findedge(&cassh1, pc, pa); - sspivot(cassh1, checkseg); - if (checkseg.sh == dummysh) { - // ca is not a segment - cad and ace belong to the same facet. - // The four points are forced to be coplanar. - ori3 = 0.0; - } else { - // ca is a segment - cad and ace belong to two different facets. - // In principle, c, a, d and e can form a tetrahedron (since - // ori3 != 0.0). Use a larger eps to test if they're coplanar. - if (iscoplanar(pc, pa, pd, pe, ori3, b->epsilon * 1e+2)) ori3 = 0.0; - } - } else { - // cad and ace are not both boundary faces. Check if cad and ace - // are approximately coplanar with respect to the epsilon. - if (iscoplanar(pc, pa, pd, pe, ori3, b->epsilon)) ori3 = 0.0; - } - } - if (ori3 < 0.0) { - // e lies above cad, unflipable, tet cade is not present. -#ifdef SELF_CHECK - if (!nonconvex) { - // cad and ace should not be hull faces, check it. - enext2fnext(horiz, casing); - symself(casing); - assert(casing.tet != dummytet); - enextfnext(symhoriz, casing); - symself(casing); - assert(casing.tet != dummytet); - } -#endif - enext2self(horiz); - if (checksubfaces) { - // The nonconvexbility may be casued by existing an subsegment. - tsspivot(&horiz, &checkseg); - if (checkseg.sh != dummysh) { - return FORBIDDENEDGE; - } - } - return N32; - } - if (ori1 == 0.0) { - // e is coplanar with abd. - if (ori2 * ori3 == 0.0) { - // only one zero is possible. - // assert(!(ori2 == 0.0 && ori3 == 0.0)); - // Three points (d, e, and a or b) are collinear, abc is unflipable - // and locally Delaunay. - return N40; - } - } else if (ori2 == 0.0) { - // e is coplanar with bcd. - if (ori1 * ori3 == 0.0) { - // only one zero is possible. - // assert(!(ori1 == 0.0 && ori3 == 0.0)); - // Three points (d, e, and b or c) are collinear, abc is unflipable - // and locally Delaunay. - return N40; - } - // Adjust 'horiz' and 'symhoriz' be the edge bc. - enextself(horiz); - enext2self(symhoriz); - } else if (ori3 == 0.0) { - // e is coplanar with cad. - if (ori1 * ori2 == 0.0) { - // only one zero is possible. - // assert(!(ori1 == 0.0 && ori2 == 0.0)); - // Three points (d, e, and c or a) are collinear, abc is unflipable - // and locally Delaunay. - return N40; - } - // Adjust 'horiz' and 'symhoriz' be the edge ca. - enext2self(horiz); - enextself(symhoriz); - } else { - // e lies below all three faces, flipable. - if (checksubfaces) { - tspivot(horiz, checksh); - if (checksh.sh != dummysh) { - // To flip a subface is forbidden. - return FORBIDDENFACE; - } - } - return T23; - } - // Four points are coplanar, T22 or T44 is possible. - if (checksubfaces) { - tsspivot(&horiz, &checkseg); - if (checkseg.sh != dummysh) { - // To flip a subsegment is forbidden. - return FORBIDDENEDGE; - } - tspivot(horiz, checksh); - if (checksh.sh != dummysh) { - // To flip a subface is forbidden. - return FORBIDDENFACE; - } - } - // Assume the four coplanar points are a, b, d, e, abd and abe are two - // coplanar faces. If both abd and abe are hull faces, flipable(T22). - // If they are interior faces, get the opposite tetrahedra abdf and - // abeg, if f = g, flipable (T44). Otherwise, unflipable. - pf = pg = (point) NULL; - fnext(horiz, casing); - symself(casing); - if (casing.tet != dummytet) { - pf = oppo(casing); - } - fnext(symhoriz, casing); - symself(casing); - if (casing.tet != dummytet) { - pg = oppo(casing); - } - if (pf == pg) { - // Either T22 (pf == pg == NULL) or T44 (pf and pg) is possible. - if (checksubfaces) { - // Retreat the corner points a, b, and c. - pa = org(horiz); - pb = dest(horiz); - pc = apex(horiz); - // Be careful not to create an inverted tetrahedron. Check the case. - ori1 = orient3d(pc, pd, pe, pa); - if (ori1 <= 0) return N40; - ori1 = orient3d(pd, pc, pe, pb); - if (ori1 <= 0) return N40; - if (pf != (point) NULL) { - ori1 = orient3d(pd, pf, pe, pa); - if (ori1 <= 0) return N40; - ori1 = orient3d(pf, pd, pe, pb); - if (ori1 <= 0) return N40; - } - } - if (pf == (point) NULL) { - // abd and abe are hull faces, flipable. - return T22; - } else { - // abd and abe are interior faces, flipable. -#ifdef SELF_CHECK - assert(pf != (point) NULL); -#endif - return T44; - } - } else { - // ab has more than four faces around it, unflipable. - return N32; - } - } else if (adjtet == 1) { - // One of its three edges is locally non-convex. Type T32 is possible. - // Adjust current configuration so that edge ab is non-convex. - if (bcdoppo == pe) { - // Edge bc is non-convex. Adjust 'horiz' and 'symhoriz' be edge bc. - enextself(horiz); - enext2self(symhoriz); - pa = org(horiz); - pb = dest(horiz); - pc = apex(horiz); - } else if (cadoppo == pe) { - // Edge ca is non-convex. Adjust 'horiz' and 'symhoriz' be edge ca. - enext2self(horiz); - enextself(symhoriz); - pa = org(horiz); - pb = dest(horiz); - pc = apex(horiz); - } else { - // Edge ab is non-convex. -#ifdef SELF_CHECK - assert(abdoppo == pe); -#endif - } // Now ab is the non-convex edge. - // In order to be flipable, ab should cross face cde. Check it. - ori1 = orient3d(pc, pd, pe, pa); - if (checksubfaces && ori1 != 0.0) { - // Check if cad and ace are both boundary faces. - enext2fnext(horiz, casing); - tspivot(casing, cassh1); - enextfnext(symhoriz, casing); - tspivot(casing, cassh2); - if (cassh1.sh != dummysh && cassh2.sh != dummysh) { - // cad and ace are both boundary faces. Check if ca is a segment. - findedge(&cassh1, pc, pa); - sspivot(cassh1, checkseg); - if (checkseg.sh == dummysh) { - // ca is not a segment. cad and ace belong to the same facet. - // The four points are forced to be coplanar. - ori1 = 0.0; - } else { - // ca is a segment. cad and ace belong to different facets. - // In principle, c, d, e, and a can form a tetrahedron (since - // ori1 != 0.0). However, we should avoid to create a very - // flat tet. Use a larger epsilon to test if they're coplanar. - if (iscoplanar(pc, pd, pe, pa, ori1, b->epsilon * 1e+2)) ori1 = 0.0; - } - } else { - // Check if c, d, e, and a are approximately coplanar. - if (iscoplanar(pc, pd, pe, pa, ori1, b->epsilon)) ori1 = 0.0; - } - } - if (ori1 <= 0.0) { - // a lies above or is coplanar cde, abc is locally Delaunay. - return N40; - } - ori2 = orient3d(pd, pc, pe, pb); - if (checksubfaces && ori2 != 0.0) { - // Check if bcd and cbe are both boundary faces. - enextfnext(horiz, casing); - tspivot(casing, cassh1); - enext2fnext(symhoriz, casing); - tspivot(casing, cassh2); - if (cassh1.sh != dummysh && cassh2.sh != dummysh) { - // bcd and cbe are both boundary faces. Check if bc is a segment. - findedge(&cassh1, pb, pc); - sspivot(cassh1, checkseg); - if (checkseg.sh == dummysh) { - // bc is not a segment. bcd and cbe belong to the same facet. - // The four points are forced to be coplanar. - ori2 = 0.0; - } else { - // bc is a segment. bcd and cbe belong to different facets. - // In principle, d, c, e, and b can form a tetrahedron (since - // ori2 != 0.0). However, we should avoid to create a very - // flat tet. Use a larger epsilon to test if they're coplanar. - if (iscoplanar(pd, pc, pe, pb, ori2, b->epsilon * 1e+2)) ori2 = 0.0; - } - } else { - // Check if d, c, e, and b are approximately coplanar. - if (iscoplanar(pd, pc, pe, pb, ori2, b->epsilon)) ori2 = 0.0; - } - } - if (ori2 <= 0.0) { - // b lies above dce, unflipable, and abc is locally Delaunay. - return N40; - } - // Edge ab crosses face cde properly. - if (checksubfaces) { - // If abc is subface, then ab must be a subsegment (because abde is - // a tetrahedron and ab crosses cde properly). - tsspivot(&horiz, &checkseg); - if (checkseg.sh != dummysh) { - // To flip a subsegment is forbidden. - return FORBIDDENEDGE; - } - // Both abd and bae should not be subfaces (because they're not - // coplanar and ab is not a subsegment). However, they may be - // subfaces and belong to a facet (created during facet recovery), - // that is, abde is an invalid tetrahedron. Find this case out. - fnext(horiz, casing); - tspivot(casing, cassh1); - fnext(symhoriz, casing); - tspivot(casing, cassh2); - if (cassh1.sh != dummysh || cassh2.sh != dummysh) { - if (!b->quiet) { - // Unfortunately, they're subfaces. Corrections need be done here. - printf("Warning: A tetrahedron spans two subfaces of a facet.\n"); - } - // Temporarily, let it be there. - return N32; - } - } - return T32; - } else { - // The convex hull of {a, b, c, d, e} has only four vertices, abc is - // unflipable, furthermore, it is locally Delaunay. - return N40; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueueflipface(), enqueueflipedge() Queue a face (or an edge). // -// // -// The face (or edge) may be non-locally Delaunay. It is queued for process- // -// ing in flip() (or flipsub()). The vertices of the face (edge) are stored // -// seperatly to ensure the face (or edge) is still the same one when we save // -// it since other flips will cause this face (or edge) be changed or dead. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::enqueueflipface(triface& checkface, queue* flipqueue) -{ - badface *queface; - triface symface; - - sym(checkface, symface); - if (symface.tet != dummytet) { - queface = (badface *) flipqueue->push((void *) NULL); - queface->tt = checkface; - queface->foppo = oppo(symface); - } -} - -void tetgenmesh::enqueueflipedge(face& checkedge, queue* flipqueue) -{ - badface *queface; - - queface = (badface *) flipqueue->push((void *) NULL); - queface->ss = checkedge; - queface->forg = sorg(checkedge); - queface->fdest = sdest(checkedge); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// flip23() Perform a 2-to-3 flip. // -// // -// On input, 'flipface' represents the face will be flipped. Let it is abc, // -// the two tetrahedra sharing abc are abcd, bace. abc is not a subface. // -// // -// A 2-to-3 flip is to change two tetrahedra abcd, bace to three tetrahedra // -// edab, edbc, and edca. As a result, face abc has been removed and three // -// new faces eda, edb and edc have been created. // -// // -// On completion, 'flipface' returns edab. If 'flipqueue' is not NULL, all // -// possibly non-Delaunay faces are added into it. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::flip23(triface* flipface, queue* flipqueue) -{ - triface abcd, bace; // Old configuration. - triface oldabd, oldbcd, oldcad; - triface abdcasing, bcdcasing, cadcasing; - triface oldbae, oldcbe, oldace; - triface baecasing, cbecasing, acecasing; - triface worktet; - face abdsh, bcdsh, cadsh; // The six subfaces on the CH. - face baesh, cbesh, acesh; - face abseg, bcseg, caseg; // The nine segs on the CH. - face adseg, bdseg, cdseg; - face aeseg, beseg, ceseg; - triface edab, edbc, edca; // New configuration. - point pa, pb, pc, pd, pe; - REAL attrib, volume; - int i; - - abcd = *flipface; - adjustedgering(abcd, CCW); // abcd represents edge ab. - pa = org(abcd); - pb = dest(abcd); - pc = apex(abcd); - pd = oppo(abcd); - // sym(abcd, bace); - // findedge(&bace, dest(abcd), org(abcd)); // bace represents edge ba. - sym(abcd, bace); - bace.ver = 0; // CCW. - for (i = 0; (i < 3) && (org(bace) != pb); i++) { - enextself(bace); - } - pe = oppo(bace); - - if (b->verbose > 2) { - printf(" Do T23 on face (%d, %d, %d) %d, %d.\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); - } - flip23s++; - - // Storing the old configuration outside the convex hull. - fnext(abcd, oldabd); - enextfnext(abcd, oldbcd); - enext2fnext(abcd, oldcad); - fnext(bace, oldbae); - enext2fnext(bace, oldcbe); - enextfnext(bace, oldace); - sym(oldabd, abdcasing); - sym(oldbcd, bcdcasing); - sym(oldcad, cadcasing); - sym(oldbae, baecasing); - sym(oldcbe, cbecasing); - sym(oldace, acecasing); - if (checksubfaces) { - tspivot(oldabd, abdsh); - tspivot(oldbcd, bcdsh); - tspivot(oldcad, cadsh); - tspivot(oldbae, baesh); - tspivot(oldcbe, cbesh); - tspivot(oldace, acesh); - } else if (checksubsegs) { - tsspivot1(abcd, abseg); - enext(abcd, worktet); - tsspivot1(worktet, bcseg); - enext2(abcd, worktet); - tsspivot1(worktet, caseg); - enext2(oldabd, worktet); - tsspivot1(worktet, adseg); - enext2(oldbcd, worktet); - tsspivot1(worktet, bdseg); - enext2(oldcad, worktet); - tsspivot1(worktet, cdseg); - enext(oldbae, worktet); - tsspivot1(worktet, aeseg); - enext(oldcbe, worktet); - tsspivot1(worktet, beseg); - enext(oldace, worktet); - tsspivot1(worktet, ceseg); - } - - // Creating the new configuration inside the convex hull. - edab.tet = abcd.tet; // Update abcd to be edab. - setorg (edab, pe); - setdest(edab, pd); - setapex(edab, pa); - setoppo(edab, pb); - edbc.tet = bace.tet; // Update bace to be edbc. - setorg (edbc, pe); - setdest(edbc, pd); - setapex(edbc, pb); - setoppo(edbc, pc); - maketetrahedron(&edca); // Create edca. - setorg (edca, pe); - setdest(edca, pd); - setapex(edca, pc); - setoppo(edca, pa); - // Set the element attributes of the new tetrahedron 'edca'. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abcd.tet, i); - setelemattribute(edca.tet, i, attrib); - } - // Set the volume constraint of the new tetrahedron 'edca' if the -ra - // switches are not used together. In -ra case, the various volume - // constraints can be spreaded very far. - if (b->varvolume && !b->refine) { - volume = volumebound(abcd.tet); - setvolumebound(edca.tet, volume); - } - - // Clear old bonds in edab(was abcd) and edbc(was bace). - for (i = 0; i < 4; i ++) { - edab.tet[i] = (tetrahedron) dummytet; - } - for (i = 0; i < 4; i ++) { - edbc.tet[i] = (tetrahedron) dummytet; - } - // Bond the faces inside the convex hull. - edab.loc = 0; - edca.loc = 1; - bond(edab, edca); - edab.loc = 1; - edbc.loc = 0; - bond(edab, edbc); - edbc.loc = 1; - edca.loc = 0; - bond(edbc, edca); - // Bond the faces on the convex hull. - edab.loc = 2; - bond(edab, abdcasing); - edab.loc = 3; - bond(edab, baecasing); - edbc.loc = 2; - bond(edbc, bcdcasing); - edbc.loc = 3; - bond(edbc, cbecasing); - edca.loc = 2; - bond(edca, cadcasing); - edca.loc = 3; - bond(edca, acecasing); - // There may exist subfaces that need to be bonded to new configuarton. - if (checksubfaces) { - // Clear old flags in edab(was abcd) and edbc(was bace). - for (i = 0; i < 4; i ++) { - edab.loc = i; - tsdissolve(edab); - edbc.loc = i; - tsdissolve(edbc); - } - if (abdsh.sh != dummysh) { - edab.loc = 2; - tsbond(edab, abdsh); - } - if (baesh.sh != dummysh) { - edab.loc = 3; - tsbond(edab, baesh); - } - if (bcdsh.sh != dummysh) { - edbc.loc = 2; - tsbond(edbc, bcdsh); - } - if (cbesh.sh != dummysh) { - edbc.loc = 3; - tsbond(edbc, cbesh); - } - if (cadsh.sh != dummysh) { - edca.loc = 2; - tsbond(edca, cadsh); - } - if (acesh.sh != dummysh) { - edca.loc = 3; - tsbond(edca, acesh); - } - } else if (checksubsegs) { - for (i = 0; i < 6; i++) { - edab.tet[8 + i] = (tetrahedron) dummysh; - } - for (i = 0; i < 6; i++) { - edbc.tet[8 + i] = (tetrahedron) dummysh; - } - edab.loc = edab.ver = 0; - edbc.loc = edab.ver = 0; - edca.loc = edab.ver = 0; - // Operate in tet edab (5 edges). - enext(edab, worktet); - tssbond1(worktet, adseg); - enext2(edab, worktet); - tssbond1(worktet, aeseg); - fnext(edab, worktet); - enextself(worktet); - tssbond1(worktet, bdseg); - enextself(worktet); - tssbond1(worktet, beseg); - enextfnext(edab, worktet); - enextself(worktet); - tssbond1(worktet, abseg); - // Operate in tet edbc (5 edges) - enext(edbc, worktet); - tssbond1(worktet, bdseg); - enext2(edbc, worktet); - tssbond1(worktet, beseg); - fnext(edbc, worktet); - enextself(worktet); - tssbond1(worktet, cdseg); - enextself(worktet); - tssbond1(worktet, ceseg); - enextfnext(edbc, worktet); - enextself(worktet); - tssbond1(worktet, bcseg); - // Operate in tet edca (5 edges) - enext(edca, worktet); - tssbond1(worktet, cdseg); - enext2(edca, worktet); - tssbond1(worktet, ceseg); - fnext(edca, worktet); - enextself(worktet); - tssbond1(worktet, adseg); - enextself(worktet); - tssbond1(worktet, aeseg); - enextfnext(edca, worktet); - enextself(worktet); - tssbond1(worktet, caseg); - } - - edab.loc = 0; - edbc.loc = 0; - edca.loc = 0; - if (b->verbose > 3) { - printf(" Updating edab "); - printtet(&edab); - printf(" Updating edbc "); - printtet(&edbc); - printf(" Creating edca "); - printtet(&edca); - } - - if (flipqueue != (queue *) NULL) { - enextfnext(edab, abdcasing); - enqueueflipface(abdcasing, flipqueue); - enext2fnext(edab, baecasing); - enqueueflipface(baecasing, flipqueue); - enextfnext(edbc, bcdcasing); - enqueueflipface(bcdcasing, flipqueue); - enext2fnext(edbc, cbecasing); - enqueueflipface(cbecasing, flipqueue); - enextfnext(edca, cadcasing); - enqueueflipface(cadcasing, flipqueue); - enext2fnext(edca, acecasing); - enqueueflipface(acecasing, flipqueue); - } - - // Save a live handle in 'recenttet'. - recenttet = edbc; - // Set the return handle be edab. - *flipface = edab; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// flip32() Perform a 3-to-2 flip. // -// // -// On input, 'flipface' represents the face will be flipped. Let it is eda, // -// where edge ed is locally non-convex. Three tetrahedra sharing ed are edab,// -// edbc, and edca. ed is not a subsegment. // -// // -// A 3-to-2 flip is to change the three tetrahedra edab, edbc, and edca into // -// another two tetrahedra abcd and bace. As a result, the edge ed has been // -// removed and the face abc has been created. // -// // -// On completion, 'flipface' returns abcd. If 'flipqueue' is not NULL, all // -// possibly non-Delaunay faces are added into it. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::flip32(triface* flipface, queue* flipqueue) -{ - triface edab, edbc, edca; // Old configuration. - triface oldabd, oldbcd, oldcad; - triface abdcasing, bcdcasing, cadcasing; - triface oldbae, oldcbe, oldace; - triface baecasing, cbecasing, acecasing; - triface worktet; - face abdsh, bcdsh, cadsh; - face baesh, cbesh, acesh; - face abseg, bcseg, caseg; // The nine segs on the CH. - face adseg, bdseg, cdseg; - face aeseg, beseg, ceseg; - triface abcd, bace; // New configuration. - point pa, pb, pc, pd, pe; - int i; - - edab = *flipface; - adjustedgering(edab, CCW); - pa = apex(edab); - pb = oppo(edab); - pd = dest(edab); - pe = org(edab); - fnext(edab, edbc); - symself(edbc); - edbc.ver = 0; - for (i = 0; (i < 3) && (org(edbc) != pe); i++) { - enextself(edbc); - } - pc = oppo(edbc); - fnext(edbc, edca); - symself(edca); - edca.ver = 0; - for (i = 0; (i < 3) && (org(edca) != pe); i++) { - enextself(edca); - } - - if (b->verbose > 2) { - printf(" Do T32 on edge (%d, %d) %d, %d, %d.\n", pointmark(pe), - pointmark(pd), pointmark(pa), pointmark(pb), pointmark(pc)); - } - flip32s++; - - // Storing the old configuration outside the convex hull. - enextfnext(edab, oldabd); - enext2fnext(edab, oldbae); - enextfnext(edbc, oldbcd); - enext2fnext(edbc, oldcbe); - enextfnext(edca, oldcad); - enext2fnext(edca, oldace); - sym(oldabd, abdcasing); - sym(oldbcd, bcdcasing); - sym(oldcad, cadcasing); - sym(oldbae, baecasing); - sym(oldcbe, cbecasing); - sym(oldace, acecasing); - if (checksubfaces) { - tspivot(oldabd, abdsh); - tspivot(oldbcd, bcdsh); - tspivot(oldcad, cadsh); - tspivot(oldbae, baesh); - tspivot(oldcbe, cbesh); - tspivot(oldace, acesh); - } else if (checksubsegs) { - enext(edab, worktet); - tsspivot1(worktet, adseg); - enext2(edab, worktet); - tsspivot1(worktet, aeseg); - enext(edbc, worktet); - tsspivot1(worktet, bdseg); - enext2(edbc, worktet); - tsspivot1(worktet, beseg); - enext(edca, worktet); - tsspivot1(worktet, cdseg); - enext2(edca, worktet); - tsspivot1(worktet, ceseg); - enextfnext(edab, worktet); - enextself(worktet); - tsspivot1(worktet, abseg); - enextfnext(edbc, worktet); - enextself(worktet); - tsspivot1(worktet, bcseg); - enextfnext(edca, worktet); - enextself(worktet); - tsspivot1(worktet, caseg); - } - - // Creating the new configuration inside the convex hull. - abcd.tet = edab.tet; // Update edab to be abcd. - setorg (abcd, pa); - setdest(abcd, pb); - setapex(abcd, pc); - setoppo(abcd, pd); - bace.tet = edbc.tet; // Update edbc to be bace. - setorg (bace, pb); - setdest(bace, pa); - setapex(bace, pc); - setoppo(bace, pe); - // Dealloc a redundant tetrahedron (edca). - tetrahedrondealloc(edca.tet); - - // Clear the old bonds in abcd (was edab) and bace (was edbc). - for (i = 0; i < 4; i ++) { - abcd.tet[i] = (tetrahedron) dummytet; - } - for (i = 0; i < 4; i ++) { - bace.tet[i] = (tetrahedron) dummytet; - } - // Bond the inside face of the convex hull. - abcd.loc = 0; - bace.loc = 0; - bond(abcd, bace); - // Bond the outside faces of the convex hull. - abcd.loc = 1; - bond(abcd, abdcasing); - abcd.loc = 2; - bond(abcd, bcdcasing); - abcd.loc = 3; - bond(abcd, cadcasing); - bace.loc = 1; - bond(bace, baecasing); - bace.loc = 3; - bond(bace, cbecasing); - bace.loc = 2; - bond(bace, acecasing); - if (checksubfaces) { - // Clear old bonds in abcd(was edab) and bace(was edbc). - for (i = 0; i < 4; i ++) { - abcd.tet[8 + i] = (tetrahedron) dummysh; - } - for (i = 0; i < 4; i ++) { - bace.tet[8 + i] = (tetrahedron) dummysh; - } - if (abdsh.sh != dummysh) { - abcd.loc = 1; - tsbond(abcd, abdsh); - } - if (bcdsh.sh != dummysh) { - abcd.loc = 2; - tsbond(abcd, bcdsh); - } - if (cadsh.sh != dummysh) { - abcd.loc = 3; - tsbond(abcd, cadsh); - } - if (baesh.sh != dummysh) { - bace.loc = 1; - tsbond(bace, baesh); - } - if (cbesh.sh != dummysh) { - bace.loc = 3; - tsbond(bace, cbesh); - } - if (acesh.sh != dummysh) { - bace.loc = 2; - tsbond(bace, acesh); - } - } else if (checksubsegs) { - for (i = 0; i < 6; i++) { - abcd.tet[8 + i] = (tetrahedron) dummysh; - } - for (i = 0; i < 6; i++) { - bace.tet[8 + i] = (tetrahedron) dummysh; - } - abcd.loc = abcd.ver = 0; - bace.loc = bace.ver = 0; - tssbond1(abcd, abseg); // 1 - enext(abcd, worktet); - tssbond1(worktet, bcseg); // 2 - enext2(abcd, worktet); - tssbond1(worktet, caseg); // 3 - fnext(abcd, worktet); - enext2self(worktet); - tssbond1(worktet, adseg); // 4 - enextfnext(abcd, worktet); - enext2self(worktet); - tssbond1(worktet, bdseg); // 5 - enext2fnext(abcd, worktet); - enext2self(worktet); - tssbond1(worktet, cdseg); // 6 - tssbond1(bace, abseg); - enext2(bace, worktet); - tssbond1(worktet, bcseg); - enext(bace, worktet); - tssbond1(worktet, caseg); - fnext(bace, worktet); - enextself(worktet); - tssbond1(worktet, aeseg); // 7 - enext2fnext(bace, worktet); - enextself(worktet); - tssbond1(worktet, beseg); // 8 - enextfnext(bace, worktet); - enextself(worktet); - tssbond1(worktet, ceseg); // 9 - } - - abcd.loc = 0; - bace.loc = 0; - if (b->verbose > 3) { - printf(" Updating abcd "); - printtet(&abcd); - printf(" Updating bace "); - printtet(&bace); - printf(" Deleting edca "); - // printtet(&edca); - } - - if (flipqueue != (queue *) NULL) { - fnext(abcd, abdcasing); - enqueueflipface(abdcasing, flipqueue); - fnext(bace, baecasing); - enqueueflipface(baecasing, flipqueue); - enextfnext(abcd, bcdcasing); - enqueueflipface(bcdcasing, flipqueue); - enextfnext(bace, cbecasing); - enqueueflipface(cbecasing, flipqueue); - enext2fnext(abcd, cadcasing); - enqueueflipface(cadcasing, flipqueue); - enext2fnext(bace, acecasing); - enqueueflipface(acecasing, flipqueue); - } - - // Save a live handle in 'recenttet'. - recenttet = abcd; - // Set the return handle be abcd. - *flipface = abcd; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// flip22() Perform a 2-to-2 (or 4-to-4) flip. // -// // -// On input, 'flipface' represents the face will be flipped. Let it is abe, // -// ab is the flipable edge, the two tetrahedra sharing abe are abce and bade,// -// hence a, b, c and d are coplanar. If abc, bad are interior faces, the two // -// tetrahedra opposite to e are bacf and abdf. ab is not a subsegment. // -// // -// A 2-to-2 flip is to change two tetrahedra abce and bade into another two // -// tetrahedra dcae and cdbe. If bacf and abdf exist, they're changed to cdaf // -// and dcbf, thus a 4-to-4 flip. As a result, two or four tetrahedra have // -// rotated counterclockwise (using right-hand rule with thumb points to e): // -// abce->dcae, bade->cdbe, and bacf->cdaf, abdf->dcbf. // -// // -// If abc and bad are subfaces, a 2-to-2 flip is performed simultaneously by // -// calling routine flip22sub(), hence abc->dca, bad->cdb. The edge rings of // -// the flipped subfaces dca and cdb have the same orientation as abc and bad.// -// Hence, they have the same orientation as other subfaces of the facet with // -// respect to the lift point of this facet. // -// // -// On completion, 'flipface' holds edge dc of tetrahedron dcae. 'flipqueue' // -// contains all possibly non-Delaunay faces if it is not NULL. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::flip22(triface* flipface, queue* flipqueue) -{ - triface abce, bade; - triface oldbce, oldcae, oldade, olddbe; - triface bcecasing, caecasing, adecasing, dbecasing; - face bcesh, caesh, adesh, dbesh; - triface bacf, abdf; - triface oldacf, oldcbf, oldbdf, olddaf; - triface acfcasing, cbfcasing, bdfcasing, dafcasing; - triface worktet; - face acfsh, cbfsh, bdfsh, dafsh; - face abc, bad; - face adseg, dbseg, bcseg, caseg; // Coplanar segs. - face aeseg, deseg, beseg, ceseg; // Above segs. - face afseg, dfseg, bfseg, cfseg; // Below segs. - point pa, pb, pc, pd, pe, pf; - int mirrorflag, i; - - adjustedgering(*flipface, CCW); // 'flipface' is bae. - fnext(*flipface, abce); - esymself(abce); - adjustedgering(*flipface, CW); // 'flipface' is abe. - fnext(*flipface, bade); -#ifdef SELF_CHECK - assert(bade.tet != dummytet); -#endif - esymself(bade); - pa = org(abce); - pb = dest(abce); - pc = apex(abce); - pd = apex(bade); - pe = oppo(bade); -#ifdef SELF_CHECK - assert(oppo(abce) == pe); -#endif - sym(abce, bacf); - mirrorflag = bacf.tet != dummytet; - if (mirrorflag) { - // findedge(&bacf, pb, pa); - bacf.ver = 0; - for (i = 0; (i < 3) && (org(bacf) != pb); i++) { - enextself(bacf); - } - sym(bade, abdf); -#ifdef SELF_CHECK - assert(abdf.tet != dummytet); -#endif - // findedge(&abdf, pa, pb); - abdf.ver = 0; - for (i = 0; (i < 3) && (org(abdf) != pa); i++) { - enextself(abdf); - } - pf = oppo(bacf); -#ifdef SELF_CHECK - assert(oppo(abdf) == pf); -#endif - } - - if (b->verbose > 2) { - printf(" Do %s on edge (%d, %d).\n", mirrorflag ? "T44" : "T22", - pointmark(pa), pointmark(pb)); - } - mirrorflag ? flip44s++ : flip22s++; - - // Save the old configuration at the convex hull. - enextfnext(abce, oldbce); - enext2fnext(abce, oldcae); - enextfnext(bade, oldade); - enext2fnext(bade, olddbe); - sym(oldbce, bcecasing); - sym(oldcae, caecasing); - sym(oldade, adecasing); - sym(olddbe, dbecasing); - if (checksubfaces) { - tspivot(oldbce, bcesh); - tspivot(oldcae, caesh); - tspivot(oldade, adesh); - tspivot(olddbe, dbesh); - tspivot(abce, abc); - tspivot(bade, bad); - } else if (checksubsegs) { - // Coplanar segs: a->d->b->c. - enext(bade, worktet); - tsspivot1(worktet, adseg); - enext2(bade, worktet); - tsspivot1(worktet, dbseg); - enext(abce, worktet); - tsspivot1(worktet, bcseg); - enext2(abce, worktet); - tsspivot1(worktet, caseg); - // Above segs: a->e, d->e, b->e, c->e. - fnext(bade, worktet); - enextself(worktet); - tsspivot1(worktet, aeseg); - enextfnext(bade, worktet); - enextself(worktet); - tsspivot1(worktet, deseg); - enext2fnext(bade, worktet); - enextself(worktet); - tsspivot1(worktet, beseg); - enextfnext(abce, worktet); - enextself(worktet); - tsspivot1(worktet, ceseg); - } - if (mirrorflag) { - enextfnext(bacf, oldacf); - enext2fnext(bacf, oldcbf); - enextfnext(abdf, oldbdf); - enext2fnext(abdf, olddaf); - sym(oldacf, acfcasing); - sym(oldcbf, cbfcasing); - sym(oldbdf, bdfcasing); - sym(olddaf, dafcasing); - if (checksubfaces) { - tspivot(oldacf, acfsh); - tspivot(oldcbf, cbfsh); - tspivot(oldbdf, bdfsh); - tspivot(olddaf, dafsh); - } else if (checksubsegs) { - // Below segs: a->f, d->f, b->f, c->f. - fnext(abdf, worktet); - enext2self(worktet); - tsspivot1(worktet, afseg); - enext2fnext(abdf, worktet); - enext2self(worktet); - tsspivot1(worktet, dfseg); - enextfnext(abdf, worktet); - enext2self(worktet); - tsspivot1(worktet, bfseg); - enextfnext(bacf, worktet); - enextself(worktet); - tsspivot1(worktet, cfseg); - } - } - - // Rotate abce, bade one-quarter turn counterclockwise. - bond(oldbce, caecasing); - bond(oldcae, adecasing); - bond(oldade, dbecasing); - bond(olddbe, bcecasing); - if (checksubfaces) { - // Check for subfaces and rebond them to the rotated tets. - if (caesh.sh == dummysh) { - tsdissolve(oldbce); - } else { - tsbond(oldbce, caesh); - } - if (adesh.sh == dummysh) { - tsdissolve(oldcae); - } else { - tsbond(oldcae, adesh); - } - if (dbesh.sh == dummysh) { - tsdissolve(oldade); - } else { - tsbond(oldade, dbesh); - } - if (bcesh.sh == dummysh) { - tsdissolve(olddbe); - } else { - tsbond(olddbe, bcesh); - } - } else if (checksubsegs) { - // 5 edges in abce are changed. - enext(abce, worktet); // fit b->c into c->a. - if (caseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, caseg); - } - enext2(abce, worktet); // fit c->a into a->d. - if (adseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, adseg); - } - fnext(abce, worktet); // fit b->e into c->e. - enextself(worktet); - if (ceseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, ceseg); - } - enextfnext(abce, worktet); // fit c->e into a->e. - enextself(worktet); - if (aeseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, aeseg); - } - enext2fnext(abce, worktet); // fit a->e into d->e. - enextself(worktet); - if (deseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, deseg); - } - // 5 edges in bade are changed. - enext(bade, worktet); // fit a->d into d->b. - if (dbseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, dbseg); - } - enext2(bade, worktet); // fit d->b into b->c. - if (bcseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, bcseg); - } - fnext(bade, worktet); // fit a->e into d->e. - enextself(worktet); - if (deseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, deseg); - } - enextfnext(bade, worktet); // fit d->e into b->e. - enextself(worktet); - if (beseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, beseg); - } - enext2fnext(bade, worktet); // fit b->e into c->e. - enextself(worktet); - if (ceseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, ceseg); - } - } - if (mirrorflag) { - // Rotate bacf, abdf one-quarter turn counterclockwise. - bond(oldcbf, acfcasing); - bond(oldacf, dafcasing); - bond(olddaf, bdfcasing); - bond(oldbdf, cbfcasing); - if (checksubfaces) { - // Check for subfaces and rebond them to the rotated tets. - if (acfsh.sh == dummysh) { - tsdissolve(oldcbf); - } else { - tsbond(oldcbf, acfsh); - } - if (dafsh.sh == dummysh) { - tsdissolve(oldacf); - } else { - tsbond(oldacf, dafsh); - } - if (bdfsh.sh == dummysh) { - tsdissolve(olddaf); - } else { - tsbond(olddaf, bdfsh); - } - if (cbfsh.sh == dummysh) { - tsdissolve(oldbdf); - } else { - tsbond(oldbdf, cbfsh); - } - } else if (checksubsegs) { - // 5 edges in bacf are changed. - enext2(bacf, worktet); // fit b->c into c->a. - if (caseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, caseg); - } - enext(bacf, worktet); // fit c->a into a->d. - if (adseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, adseg); - } - fnext(bacf, worktet); // fit b->f into c->f. - enext2self(worktet); - if (cfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, cfseg); - } - enext2fnext(bacf, worktet); // fit c->f into a->f. - enext2self(worktet); - if (afseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, afseg); - } - enextfnext(bacf, worktet); // fit a->f into d->f. - enext2self(worktet); - if (dfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, dfseg); - } - // 5 edges in abdf are changed. - enext2(abdf, worktet); // fit a->d into d->b. - if (dbseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, dbseg); - } - enext(abdf, worktet); // fit d->b into b->c. - if (bcseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, bcseg); - } - fnext(abdf, worktet); // fit a->f into d->f. - enext2self(worktet); - if (dfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, dfseg); - } - enext2fnext(abdf, worktet); // fit d->f into b->f. - enext2self(worktet); - if (bfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, bfseg); - } - enextfnext(abdf, worktet); // fit b->f into c->f. - enext2self(worktet); - if (cfseg.sh == dummysh) { - tssdissolve1(worktet); - } else { - tssbond1(worktet, cfseg); - } - } - } - - // New vertex assignments for the rotated tetrahedra. - setorg(abce, pd); // Update abce to dcae - setdest(abce, pc); - setapex(abce, pa); - setorg(bade, pc); // Update bade to cdbe - setdest(bade, pd); - setapex(bade, pb); - if (mirrorflag) { - setorg(bacf, pc); // Update bacf to cdaf - setdest(bacf, pd); - setapex(bacf, pa); - setorg(abdf, pd); // Update abdf to dcbf - setdest(abdf, pc); - setapex(abdf, pb); - } - - // Are there subfaces need to be flipped? - if (checksubfaces && abc.sh != dummysh) { -#ifdef SELF_CHECK - assert(bad.sh != dummysh); -#endif - // Adjust the edge be ab, so the rotation of subfaces is according with - // the rotation of tetrahedra. - findedge(&abc, pa, pb); - // Flip an edge of two subfaces, ignore non-Delaunay edges. - flip22sub(&abc, NULL); - } - - if (b->verbose > 3) { - printf(" Updating abce "); - printtet(&abce); - printf(" Updating bade "); - printtet(&bade); - if (mirrorflag) { - printf(" Updating bacf "); - printtet(&bacf); - printf(" Updating abdf "); - printtet(&abdf); - } - } - - if (flipqueue != (queue *) NULL) { - enextfnext(abce, bcecasing); - enqueueflipface(bcecasing, flipqueue); - enext2fnext(abce, caecasing); - enqueueflipface(caecasing, flipqueue); - enextfnext(bade, adecasing); - enqueueflipface(adecasing, flipqueue); - enext2fnext(bade, dbecasing); - enqueueflipface(dbecasing, flipqueue); - if (mirrorflag) { - enextfnext(bacf, acfcasing); - enqueueflipface(acfcasing, flipqueue); - enext2fnext(bacf, cbfcasing); - enqueueflipface(cbfcasing, flipqueue); - enextfnext(abdf, bdfcasing); - enqueueflipface(bdfcasing, flipqueue); - enext2fnext(abdf, dafcasing); - enqueueflipface(dafcasing, flipqueue); - } - // The two new faces dcae (abce), cdbe (bade) may still not be locally - // Delaunay, and may need be flipped (flip23). On the other hand, in - // conforming Delaunay algorithm, two new subfaces dca (abc), and cdb - // (bad) may be non-conforming Delaunay, they need be queued if they - // are locally Delaunay but non-conforming Delaunay. - enqueueflipface(abce, flipqueue); - enqueueflipface(bade, flipqueue); - } - - // Save a live handle in 'recenttet'. - recenttet = abce; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// flip22sub() Perform a 2-to-2 flip on a subface edge. // -// // -// The flip edge is given by subface 'flipedge'. Let it is abc, where ab is // -// the flipping edge. The other subface is bad, where a, b, c, d form a // -// convex quadrilateral. ab is not a subsegment. // -// // -// A 2-to-2 subface flip is to change two subfaces abc and bad to another // -// two subfaces dca and cdb. Hence, edge ab has been removed and dc becomes // -// an edge. If a point e is above abc, this flip is equal to rotate abc and // -// bad counterclockwise using right-hand rule with thumb points to e. It is // -// important to know that the edge rings of the flipped subfaces dca and cdb // -// are keeping the same orientation as their original subfaces. So they have // -// the same orientation with respect to the lift point of this facet. // -// // -// During rotating, the face rings of the four edges bc, ca, ad, and de need // -// be re-connected. If the edge is not a subsegment, then its face ring has // -// only two faces, a sbond() will bond them together. If it is a subsegment, // -// one should use sbond1() twice to bond two different handles to the rotat- // -// ing subface, one is predecssor (-casin), another is successor (-casout). // -// // -// If 'flipqueue' is not NULL, it returns four edges bc, ca, ad, de, which // -// may be non-Delaunay. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue) -{ - face abc, bad; - face oldbc, oldca, oldad, olddb; - face bccasin, bccasout, cacasin, cacasout; - face adcasin, adcasout, dbcasin, dbcasout; - face bc, ca, ad, db; - face spinsh; - point pa, pb, pc, pd; - - abc = *flipedge; - spivot(abc, bad); - if (sorg(bad) != sdest(abc)) { - sesymself(bad); - } - pa = sorg(abc); - pb = sdest(abc); - pc = sapex(abc); - pd = sapex(bad); - - if (b->verbose > 2) { - printf(" Flip sub edge (%d, %d).\n", pointmark(pa), pointmark(pb)); - } - - // Save the old configuration outside the quadrilateral. - senext(abc, oldbc); - senext2(abc, oldca); - senext(bad, oldad); - senext2(bad, olddb); - // Get the outside connection. Becareful if there is a subsegment on the - // quadrilateral, two casings (casin and casout) are needed to save for - // keeping the face link. - spivot(oldbc, bccasout); - sspivot(oldbc, bc); - if (bc.sh != dummysh) { - // 'bc' is a subsegment. - if (bccasout.sh != dummysh) { - if (oldbc.sh != bccasout.sh) { - // 'oldbc' is not self-bonded. - spinsh = bccasout; - do { - bccasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldbc.sh); - } else { - bccasout.sh = dummysh; - } - } - ssdissolve(oldbc); - } - spivot(oldca, cacasout); - sspivot(oldca, ca); - if (ca.sh != dummysh) { - // 'ca' is a subsegment. - if (cacasout.sh != dummysh) { - if (oldca.sh != cacasout.sh) { - // 'oldca' is not self-bonded. - spinsh = cacasout; - do { - cacasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldca.sh); - } else { - cacasout.sh = dummysh; - } - } - ssdissolve(oldca); - } - spivot(oldad, adcasout); - sspivot(oldad, ad); - if (ad.sh != dummysh) { - // 'ad' is a subsegment. - if (adcasout.sh != dummysh) { - if (oldad.sh != adcasout.sh) { - // 'adcasout' is not self-bonded. - spinsh = adcasout; - do { - adcasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldad.sh); - } else { - adcasout.sh = dummysh; - } - } - ssdissolve(oldad); - } - spivot(olddb, dbcasout); - sspivot(olddb, db); - if (db.sh != dummysh) { - // 'db' is a subsegment. - if (dbcasout.sh != dummysh) { - if (olddb.sh != dbcasout.sh) { - // 'dbcasout' is not self-bonded. - spinsh = dbcasout; - do { - dbcasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != olddb.sh); - } else { - dbcasout.sh = dummysh; - } - } - ssdissolve(olddb); - } - - // Rotate abc and bad one-quarter turn counterclockwise. - if (ca.sh != dummysh) { - if (cacasout.sh != dummysh) { - sbond1(cacasin, oldbc); - sbond1(oldbc, cacasout); - } else { - // Bond 'oldbc' to itself. - sbond(oldbc, oldbc); - // Make sure that dummysh always correctly bonded. - dummysh[0] = sencode(oldbc); - } - ssbond(oldbc, ca); - } else { - sbond(oldbc, cacasout); - } - if (ad.sh != dummysh) { - if (adcasout.sh != dummysh) { - sbond1(adcasin, oldca); - sbond1(oldca, adcasout); - } else { - // Bond 'oldca' to itself. - sbond(oldca, oldca); - // Make sure that dummysh always correctly bonded. - dummysh[0] = sencode(oldca); - } - ssbond(oldca, ad); - } else { - sbond(oldca, adcasout); - } - if (db.sh != dummysh) { - if (dbcasout.sh != dummysh) { - sbond1(dbcasin, oldad); - sbond1(oldad, dbcasout); - } else { - // Bond 'oldad' to itself. - sbond(oldad, oldad); - // Make sure that dummysh always correctly bonded. - dummysh[0] = sencode(oldad); - } - ssbond(oldad, db); - } else { - sbond(oldad, dbcasout); - } - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - sbond1(bccasin, olddb); - sbond1(olddb, bccasout); - } else { - // Bond 'olddb' to itself. - sbond(olddb, olddb); - // Make sure that dummysh always correctly bonded. - dummysh[0] = sencode(olddb); - } - ssbond(olddb, bc); - } else { - sbond(olddb, bccasout); - } - - // New vertex assignments for the rotated subfaces. - setsorg(abc, pd); // Update abc to dca. - setsdest(abc, pc); - setsapex(abc, pa); - setsorg(bad, pc); // Update bad to cdb. - setsdest(bad, pd); - setsapex(bad, pb); - - if (flipqueue != (queue *) NULL) { - enqueueflipedge(bccasout, flipqueue); - enqueueflipedge(cacasout, flipqueue); - enqueueflipedge(adcasout, flipqueue); - enqueueflipedge(dbcasout, flipqueue); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// flip() Flips non-locally Delaunay faces in flipqueue until it is empty.// -// // -// Assumpation: Current tetrahedralization is non-Delaunay after inserting // -// a point or performing a flip operation, all possibly non-Delaunay faces // -// are in 'flipqueue'. // -// // -// If 'plastflip' is not NULL, it is used to return a stack of recently // -// flipped faces. This stack will be used to reverse the flips done in this // -// routine later for removing a newly inserted point because it encroaches // -// any subfaces or subsegments. // -// // -// The return value is the total number of flips done during this invocation.// -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::flip(queue* flipqueue, badface **plastflip) -{ - badface *qface, *newflip; - triface flipface, symface; - point pa, pb, pc, pd, pe; - enum fliptype fc; - REAL sign, bakepsilon; - long flipcount, maxfaces; - int epscount, fcount; - int ia, ib, ic, id, ie; - - if (b->verbose > 1) { - printf(" Do flipface queue: %ld faces.\n", flipqueue->len()); - } - - flipcount = flip23s + flip32s + flip22s + flip44s; - if (checksubfaces) { - maxfaces = (4l * tetrahedrons->items + hullsize) / 2l; - fcount = 0; - } - - if (plastflip != (badface **) NULL) { - // Initialize the stack of the flip sequence. - flipstackers->restart(); - *plastflip = (badface *) NULL; - } - - // Loop until the queue is empty. - while (!flipqueue->empty()) { - qface = (badface *) flipqueue->pop(); - flipface = qface->tt; - if (isdead(&flipface)) continue; - sym(flipface, symface); - // Only do check when the adjacent tet exists and it's not a "fake" tet. - if ((symface.tet != dummytet) && (oppo(symface) == qface->foppo)) { - // For positive orientation that insphere() test requires. - adjustedgering(flipface, CW); - pa = org(flipface); - pb = dest(flipface); - pc = apex(flipface); - pd = oppo(flipface); - pe = oppo(symface); - if (symbolic) { - ia = pointmark(pa); - ib = pointmark(pb); - ic = pointmark(pc); - id = pointmark(pd); - ie = pointmark(pe); - sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie); - assert(sign != 0.0); - } else { - sign = insphere(pa, pb, pc, pd, pe); - } - } else { - sign = -1.0; // A hull face is locally Delaunay. - } - if (sign > 0.0) { - // 'flipface' is non-locally Delaunay, try to flip it. - if (checksubfaces) { - fcount++; - bakepsilon = b->epsilon; - epscount = 0; - while (epscount < 32) { - fc = categorizeface(flipface); - if (fc == N40) { - b->epsilon *= 1e-1; - epscount++; - continue; - } - break; - } - b->epsilon = bakepsilon; - if (epscount >= 32) { - if (b->verbose > 0) { - printf("Warning: Can't flip a degenerate tetrahedron.\n"); - } - fc = N40; - } - } else { - fc = categorizeface(flipface); -#ifdef SELF_CHECK - assert(fc != N40); -#endif - } - switch (fc) { - // The following face types are flipable. - case T44: - case T22: - flip22(&flipface, flipqueue); - break; - case T23: - flip23(&flipface, flipqueue); - break; - case T32: - flip32(&flipface, flipqueue); - break; - // The following face types are unflipable. - case N32: - break; - case FORBIDDENFACE: - break; - case FORBIDDENEDGE: - break; - // This case is only possible when the domain is nonconvex. - case N40: - // assert(nonconvex); - break; - } - if (plastflip != (badface **) NULL) { - if ((fc == T44) || (fc == T22) || (fc == T23) || (fc == T32)) { - // Push the flipped face into stack. - newflip = (badface *) flipstackers->alloc(); - newflip->tt = flipface; - newflip->key = (REAL) fc; - newflip->forg = org(flipface); - newflip->fdest = dest(flipface); - newflip->fapex = apex(flipface); - newflip->previtem = *plastflip; - *plastflip = newflip; - } - } - } - } - - flipcount = flip23s + flip32s + flip22s + flip44s - flipcount; - if (b->verbose > 1) { - printf(" %ld flips.\n", flipcount); - } - - return flipcount; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// lawson() Flip locally non-Delaunay faces by Lawson's algorithm. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::lawson(list *misseglist, queue* flipqueue) -{ - badface *qface, *misseg; - triface flipface, symface; - triface starttet, spintet; - face checksh, checkseg; - point pa, pb, pc, pd, pe; - point swappt; - REAL sign, ori; - long flipcount; - int ia, ib, ic, id, ie; - int hitbdry, i; - - if (b->verbose > 1) { - printf(" Do flipface queue: %ld faces.\n", flipqueue->len()); - } - flipcount = flip23s + flip32s + flip22s + flip44s; - - // Go through the stack of possible flips and decide whether to do them. - // Note that during the loop new possible flips will be pushed onto - // this stack, while they popped in this loop. - while (!flipqueue->empty()) { - qface = (badface *) flipqueue->pop(); - flipface = qface->tt; - // Check if tet has already been flipped out of existence. - if (!isdead(&flipface)) { - sym(flipface, symface); - // Check if this tet is the same as the one which was stacked. - if ((symface.tet != dummytet) && (oppo(symface) == qface->foppo)) { - flipface.ver = 0; // Select the CCW ring. - pa = org(flipface); - pb = dest(flipface); - pc = apex(flipface); - pd = oppo(flipface); - pe = oppo(symface); - if (symbolic) { - ia = pointmark(pa); - ib = pointmark(pb); - ic = pointmark(pc); - id = pointmark(pd); - ie = pointmark(pe); - sign = insphere_sos(pb, pa, pc, pd, pe, ib, ia, ic, id, ie); - } else { - sign = insphere(pb, pa, pc, pd, pe); - } - if (sign > 0.0) { - for (i = 0; i < 3; i++) { - ori = orient3d(pa, pb, pd, pe); - if (ori > 0.0) { - // Goto and check the next edge. - swappt = pa; - pa = pb; - pb = pc; - pc = swappt; - enextself(flipface); - } else { - break; // either (ori < 0.0) or (ori == 0.0) - } - } // for (i = 0; ....) - if (ori > 0.0) { - // All three edges are convex, a 2-3 flip is possible. - if (checksubfaces) { - tspivot(flipface, checksh); - if (checksh.sh != dummysh) { - // A subface is not flipable. - continue; - } - } - flip23(&flipface, flipqueue); - } else if (ori < 0.0) { - // The edge (a, b) is non-convex, check for a 3-2 flip. - fnext(flipface, symface); - symself(symface); - if (oppo(symface) == pe) { - // Only three tets adjoining this edge. - if (checksubfaces) { - tsspivot(&flipface, &checkseg); - if (checkseg.sh != dummysh) { - // A subsegment is not flipable. - continue; - } - } else if (checksubsegs) { - tsspivot1(flipface, checkseg); - if (checkseg.sh != dummysh) { - if (b->verbose > 2) { - printf(" Queuing missing segment (%d, %d).\n", - pointmark(org(flipface)), pointmark(dest(flipface))); - } - misseg = (badface *) misseglist->append(NULL); - misseg->ss = checkseg; - misseg->forg = sorg(checkseg); - misseg->fdest = sdest(checkseg); - // Detach all tets having this seg. - starttet = flipface; - adjustedgering(starttet, CCW); - fnextself(starttet); - spintet = starttet; - hitbdry = 0; - do { - tssdissolve1(spintet); - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(starttet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } - } - } - } while ((apex(spintet) != apex(starttet)) && (hitbdry < 2)); - } - } // if (checksubfaces) - flip32(&flipface, flipqueue); - } - } else { - // Four points (a, b, d, e) are coplanar. - fnext(flipface, symface); - if (fnextself(symface)) { - // Check for a 4-4 flip. - fnextself(symface); - if (apex(symface) == pe) { - if (checksubfaces) { - tsspivot(&flipface, &checkseg); - if (checkseg.sh != dummysh) { - // A subsegment is not flippable. - continue; - } - } else if (checksubsegs) { - tsspivot1(flipface, checkseg); - if (checkseg.sh != dummysh) { - if (b->verbose > 2) { - printf(" Queuing missing segment (%d, %d).\n", - pointmark(org(flipface)), pointmark(dest(flipface))); - } - misseg = (badface *) misseglist->append(NULL); - misseg->ss = checkseg; - misseg->forg = sorg(checkseg); - misseg->fdest = sdest(checkseg); - // Detach all tets having this seg. - starttet = flipface; - adjustedgering(starttet, CCW); - fnextself(starttet); - spintet = starttet; - hitbdry = 0; - do { - tssdissolve1(spintet); - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(starttet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } - } - } - } while ((apex(spintet) != apex(starttet)) && - (hitbdry < 2)); - } - } // if (checksubfaces) - flip22(&flipface, flipqueue); - } - } else { - // Check for a 2-2 flip. - esym(flipface, symface); - fnextself(symface); - symself(symface); - if (symface.tet == dummytet) { - if (checksubfaces) { - tsspivot(&flipface, &checkseg); - if (checkseg.sh != dummysh) { - // A subsegment is not flipable. - continue; - } - } else if (checksubsegs) { - tsspivot1(flipface, checkseg); - if (checkseg.sh != dummysh) { - if (b->verbose > 2) { - printf(" Queuing missing segment (%d, %d).\n", - pointmark(org(flipface)), pointmark(dest(flipface))); - } - misseg = (badface *) misseglist->append(NULL); - misseg->ss = checkseg; - misseg->forg = sorg(checkseg); - misseg->fdest = sdest(checkseg); - // Detach all tets having this seg. - starttet = flipface; - adjustedgering(starttet, CCW); - fnextself(starttet); - spintet = starttet; - hitbdry = 0; - do { - tssdissolve1(spintet); - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(starttet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } - } - } - } while ((apex(spintet) != apex(starttet)) && - (hitbdry < 2)); - } - } // if (checksubfaces) - flip22(&flipface, flipqueue); - } - } - } // if (ori > 0.0) - } // if (sign > 0.0) - } - } // !isdead(&qface->tt) - } // while (!flipqueue->empty()) - - flipcount = flip23s + flip32s + flip22s + flip44s - flipcount; - if (b->verbose > 1) { - printf(" %ld flips.\n", flipcount); - } - return flipcount; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// undoflip() Undo the most recent flip sequence induced by flip(). // -// // -// 'lastflip' is the stack of recently flipped faces. Walks through the list // -// of flips, in the reverse of the order in which they were done, and undoes // -// them. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::undoflip(badface *lastflip) -{ - enum fliptype fc; - - while (lastflip != (badface *) NULL) { - // Get the right flipped face. - findface(&lastflip->tt, lastflip->forg, lastflip->fdest, lastflip->fapex); - fc = (enum fliptype) (int) lastflip->key; - switch (fc) { - case T23: - // The reverse operation of T23 is T32. - flip32(&lastflip->tt, NULL); - break; - case T32: - // The reverse operation of T32 is T23. - flip23(&lastflip->tt, NULL); - break; - case T22: - case T44: - // The reverse operation of T22 or T44 is again T22 or T44. - flip22(&lastflip->tt, NULL); - break; - default: // To omit compile warnings. - break; - } - // Go on and process the next transformation. - lastflip = lastflip->previtem; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// flipsub() Flip non-Delaunay edges in a queue of (coplanar) subfaces. // -// // -// Assumpation: Current triangulation T contains non-Delaunay edges (after // -// inserting a point or performing a flip). Non-Delaunay edges are queued in // -// 'facequeue'. Returns the total number of flips done during this call. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::flipsub(queue* flipqueue) -{ - badface *qedge; - face flipedge, symedge; - face checkseg; - point pa, pb, pc, pd; - REAL vab[3], vac[3], vad[3]; - REAL dot1, dot2, lac, lad; - REAL sign, ori; - int edgeflips; - int i; - - if (b->verbose > 1) { - printf(" Start do edge queue: %ld edges.\n", flipqueue->len()); - } - - edgeflips = 0; - - while (!flipqueue->empty()) { - qedge = (badface *) flipqueue->pop(); - flipedge = qedge->ss; - if (flipedge.sh == dummysh) continue; - if ((sorg(flipedge) != qedge->forg) || - (sdest(flipedge) != qedge->fdest)) continue; - sspivot(flipedge, checkseg); - if (checkseg.sh != dummysh) continue; // Can't flip a subsegment. - spivot(flipedge, symedge); - if (symedge.sh == dummysh) continue; // Can't flip a hull edge. - pa = sorg(flipedge); - pb = sdest(flipedge); - pc = sapex(flipedge); - pd = sapex(symedge); - // Choose the triangle abc or abd as the base depending on the angle1 - // (Vac, Vab) and angle2 (Vad, Vab). - for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i]; - for (i = 0; i < 3; i++) vac[i] = pc[i] - pa[i]; - for (i = 0; i < 3; i++) vad[i] = pd[i] - pa[i]; - dot1 = dot(vac, vab); - dot2 = dot(vad, vab); - dot1 *= dot1; - dot2 *= dot2; - lac = dot(vac, vac); - lad = dot(vad, vad); - if (lad * dot1 <= lac * dot2) { - // angle1 is closer to 90 than angle2, choose abc (flipedge). - abovepoint = facetabovepointarray[shellmark(flipedge)]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&flipedge); - } - sign = insphere(pa, pb, pc, abovepoint, pd); - ori = orient3d(pa, pb, pc, abovepoint); - } else { - // angle2 is closer to 90 than angle1, choose abd (symedge). - abovepoint = facetabovepointarray[shellmark(symedge)]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&symedge); - } - sign = insphere(pa, pb, pd, abovepoint, pc); - ori = orient3d(pa, pb, pd, abovepoint); - } - // Correct the sign. - sign = ori > 0.0 ? sign : -sign; - if (sign > 0.0) { - // Flip the non-Delaunay edge. - flip22sub(&flipedge, flipqueue); - edgeflips++; - } - } - - if (b->verbose > 1) { - printf(" Total %d flips.\n", edgeflips); - } - - return edgeflips; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removetetbypeeloff() Remove a boundary tet by peeling it off. // -// // -// 'striptet' (abcd) is on boundary and can be removed by stripping it off. // -// Let abc and bad are the external boundary faces. // -// // -// To strip 'abcd' from the mesh is to detach its two interal faces (dca and // -// cdb) from their adjoining tets together with a 2-to-2 flip to transform // -// two subfaces (abc and bad) into another two (dca and cdb). // -// // -// In mesh optimization. It is possible that ab is a segment and abcd is a // -// sliver on the hull. Strip abcd will also delete the segment ab. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::removetetbypeeloff(triface *striptet) -{ - triface abcd, badc; - triface dcacasing, cdbcasing; - face abc, bad; - face abseg; - REAL ang; - - abcd = *striptet; - adjustedgering(abcd, CCW); - // Get the casing tets at the internal sides. - enextfnext(abcd, cdbcasing); - enext2fnext(abcd, dcacasing); - symself(cdbcasing); - symself(dcacasing); - // Do the neighboring tets exist? During optimization. It is possible - // that the neighboring tets are already dead. - if ((cdbcasing.tet == dummytet) || (dcacasing.tet == dummytet)) { - // Do not strip this tet. - return false; - } - - // Are there subfaces? - if (checksubfaces) { - // Get the external subfaces abc, bad. - fnext(abcd, badc); - esymself(badc); - tspivot(abcd, abc); - tspivot(badc, bad); - if (abc.sh != dummysh) { - assert(bad.sh != dummysh); - findedge(&abc, org(abcd), dest(abcd)); - findedge(&bad, org(badc), dest(badc)); - // Is ab a segment? - sspivot(abc, abseg); - if (abseg.sh != dummysh) { - // Does a segment allow to be removed? - if ((b->optlevel > 3) && (b->nobisect == 0)) { - // Only remove this segment if the dihedal angle at ab is between - // [b->maxdihedral-9, 180] (deg). This avoids mistakely fliping - // ab when it has actually no big dihedral angle while cd has. - ang = facedihedral(org(abcd), dest(abcd), apex(abcd), oppo(abcd)); - ang = ang * 180.0 / PI; - if ((ang + 9.0) > b->maxdihedral) { - if (b->verbose > 1) { - printf(" Remove a segment during peeling.\n"); - } - face prevseg, nextseg; - // It is only shared by abc and bad (abcd is a tet). - ssdissolve(abc); - ssdissolve(bad); - abseg.shver = 0; - senext(abseg, nextseg); - spivotself(nextseg); - if (nextseg.sh != dummysh) { - ssdissolve(nextseg); - } - senext2(abseg, prevseg); - spivotself(prevseg); - if (prevseg.sh != dummysh) { - ssdissolve(prevseg); - } - shellfacedealloc(subsegs, abseg.sh); - optcount[1]++; - } else { - return false; - } - } else { - return false; - } - } - // Do a 2-to-2 flip on abc and bad, transform abc->dca, bad->cdb. - flip22sub(&abc, NULL); - // The two internal faces become boundary faces. - tsbond(cdbcasing, bad); - tsbond(dcacasing, abc); - } - } - - // Detach abcd from the two internal faces. - dissolve(cdbcasing); - dissolve(dcacasing); - // Delete abcd. - tetrahedrondealloc(abcd.tet); - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedgebyflip22() Remove an edge by a 2-to-2 (or 4-to-4) flip. // -// // -// 'abtetlist' contains n tets (n is 2 or 4) sharing edge ab, abtetlist[0] // -// and abtetlist[1] are tets abec and abde, respectively (NOTE, both are in // -// CW edge ring), where a, b, c, and d are coplanar. If n = 4, abtetlist[2] // -// and abtetlist[3] are tets abfd and abcf, respectively. This routine uses // -// flip22() to replace edge ab with cd, the surrounding tets are rotated. // -// // -// If 'key' != NULL. The old tets are replaced by the new tets only if the // -// local mesh quality is improved. Current 'key' = cos(\theta), where \theta // -// is the maximum dihedral angle in the old tets. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::removeedgebyflip22(REAL *key, int n, triface *abtetlist, - queue *flipque) -{ - point pa, pb, pc, pd, pe, pf; - REAL cosmaxd, d1, d2, d3; - bool doflip; - - doflip = true; - adjustedgering(abtetlist[0], CW); - pa = org(abtetlist[0]); - pb = dest(abtetlist[0]); - pe = apex(abtetlist[0]); - pc = oppo(abtetlist[0]); - pd = apex(abtetlist[1]); - if (n == 4) { - pf = apex(abtetlist[2]); - } - if (key && (*key > -1.0)) { - tetalldihedral(pc, pd, pe, pa, NULL, &d1, NULL); - tetalldihedral(pd, pc, pe, pb, NULL, &d2, NULL); - cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle. - if (n == 4) { - tetalldihedral(pd, pc, pf, pa, NULL, &d1, NULL); - tetalldihedral(pc, pd, pf, pb, NULL, &d2, NULL); - d3 = d1 < d2 ? d1 : d2; // Choose the bigger angle. - cosmaxd = cosmaxd < d3 ? cosmaxd : d3; // Choose the bigger angle. - } - doflip = (*key < cosmaxd); // Can local quality be improved? - } - - if (doflip) { - flip22(&abtetlist[0], NULL); - // Return the improved quality value. - if (key) *key = cosmaxd; - } - - return doflip; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removefacebyflip23() Remove a face by a 2-to-3 flip. // -// // -// 'abctetlist' contains 2 tets sharing abc, which are [0]abcd and [1]bace. // -// This routine forms three new tets that abc is not a face anymore. Save // -// them in 'newtetlist': [0]edab, [1]edbc, and [2]edca. Note that the new // -// tets may not valid if one of them get inverted. return false if so. // -// // -// If 'key' != NULL. The old tets are replaced by the new tets only if the // -// local mesh quality is improved. Current 'key' = cos(\theta), where \theta // -// is the maximum dihedral angle in the old tets. // -// // -// If the face is flipped, 'newtetlist' returns the three new tets. The two // -// tets in 'abctetlist' are NOT deleted. The caller has the right to either // -// delete them or reverse the operation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::removefacebyflip23(REAL *key, triface *abctetlist, - triface *newtetlist, queue *flipque) -{ - triface edab, edbc, edca; // new configuration. - triface newfront, oldfront, adjfront; - face checksh; - point pa, pb, pc, pd, pe; - REAL ori, cosmaxd, d1, d2, d3; - REAL attrib, volume; - bool doflip; - int i; - - adjustedgering(abctetlist[0], CCW); - pa = org(abctetlist[0]); - pb = dest(abctetlist[0]); - pc = apex(abctetlist[0]); - pd = oppo(abctetlist[0]); - pe = oppo(abctetlist[1]); - - // Check if the flip creates valid new tets. - ori = orient3d(pe, pd, pa, pb); - if (ori < 0.0) { - ori = orient3d(pe, pd, pb, pc); - if (ori < 0.0) { - ori = orient3d(pe, pd, pc, pa); - } - } - doflip = (ori < 0.0); // Can abc be flipped away? - if (doflip && (key != (REAL *) NULL)) { - if (*key > -1.0) { - // Test if the new tets reduce the maximal dihedral angle. - tetalldihedral(pe, pd, pa, pb, NULL, &d1, NULL); - tetalldihedral(pe, pd, pb, pc, NULL, &d2, NULL); - tetalldihedral(pe, pd, pc, pa, NULL, &d3, NULL); - cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle. - cosmaxd = cosmaxd < d3 ? cosmaxd : d3; // Choose the bigger angle. - doflip = (*key < cosmaxd); // Can local quality be improved? - } - } - - if (doflip) { - // A valid (2-to-3) flip is found. - flip23s++; - // Create the new tets. - maketetrahedron(&edab); - setorg(edab, pe); - setdest(edab, pd); - setapex(edab, pa); - setoppo(edab, pb); - maketetrahedron(&edbc); - setorg(edbc, pe); - setdest(edbc, pd); - setapex(edbc, pb); - setoppo(edbc, pc); - maketetrahedron(&edca); - setorg(edca, pe); - setdest(edca, pd); - setapex(edca, pc); - setoppo(edca, pa); - // Transfer the element attributes. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abctetlist[0].tet, i); - setelemattribute(edab.tet, i, attrib); - setelemattribute(edbc.tet, i, attrib); - setelemattribute(edca.tet, i, attrib); - } - // Transfer the volume constraints. - if (b->varvolume && !b->refine) { - volume = volumebound(abctetlist[0].tet); - setvolumebound(edab.tet, volume); - setvolumebound(edbc.tet, volume); - setvolumebound(edca.tet, volume); - } - // Return two new tets. - newtetlist[0] = edab; - newtetlist[1] = edbc; - newtetlist[2] = edca; - // Glue the three new tets. - for (i = 0; i < 3; i++) { - fnext(newtetlist[i], newfront); - bond(newfront, newtetlist[(i + 1) % 3]); - } - // Substitute the three new tets into the old cavity. - for (i = 0; i < 3; i++) { - fnext(abctetlist[0], oldfront); - sym(oldfront, adjfront); // may be outside. - enextfnext(newtetlist[i], newfront); - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - enqueueflipface(newfront, flipque); - } - enextself(abctetlist[0]); - } - findedge(&(abctetlist[1]), pb, pa); - for (i = 0; i < 3; i++) { - fnext(abctetlist[1], oldfront); - sym(oldfront, adjfront); // may be outside. - enext2fnext(newtetlist[i], newfront); - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - enqueueflipface(newfront, flipque); - } - enext2self(abctetlist[1]); - } - // Do not delete the old tets. - // for (i = 0; i < 2; i++) { - // tetrahedrondealloc(abctetlist[i].tet); - // } - // Return the improved quality value. - if (key != (REAL *) NULL) *key = cosmaxd; - return true; - } - - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedgebyflip32() Remove an edge by a 3-to-2 flip. // -// // -// 'abtetlist' contains 3 tets sharing ab. Imaging that ab is perpendicular // -// to the screen, where a lies in front of and b lies behind it. The 3 tets // -// of the list are: [0]abce, [1]abdc, and [2]abed, respectively. // -// // -// This routine forms two new tets that ab is not an edge of them. Save them // -// in 'newtetlist', [0]dcea, [1]cdeb. Note that the new tets may not valid // -// if one of them get inverted. return false if so. // -// // -// If 'key' != NULL. The old tets are replaced by the new tets only if the // -// local mesh quality is improved. Current 'key' = cos(\theta), where \theta // -// is the maximum dihedral angle in the old tets. // -// // -// If the edge is flipped, 'newtetlist' returns the two new tets. The three // -// tets in 'abtetlist' are NOT deleted. The caller has the right to either // -// delete them or reverse the operation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::removeedgebyflip32(REAL *key, triface *abtetlist, - triface *newtetlist, queue *flipque) -{ - triface dcea, cdeb; // new configuration. - triface newfront, oldfront, adjfront; - face checksh; - point pa, pb, pc, pd, pe; - REAL ori, cosmaxd, d1, d2; - REAL attrib, volume; - bool doflip; - int i; - - pa = org(abtetlist[0]); - pb = dest(abtetlist[0]); - pc = apex(abtetlist[0]); - pd = apex(abtetlist[1]); - pe = apex(abtetlist[2]); - - ori = orient3d(pd, pc, pe, pa); - if (ori < 0.0) { - ori = orient3d(pc, pd, pe, pb); - } - doflip = (ori < 0.0); // Can ab be flipped away? - - // Does the caller ensure a valid configuration? - if (doflip && (key != (REAL *) NULL)) { - if (*key > -1.0) { - // Test if the new tets reduce the maximal dihedral angle. - tetalldihedral(pd, pc, pe, pa, NULL, &d1, NULL); - tetalldihedral(pc, pd, pe, pb, NULL, &d2, NULL); - cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle. - doflip = (*key < cosmaxd); // Can local quality be improved? - // Return the key - *key = cosmaxd; - } - } - - if (doflip) { - // Create the new tets. - maketetrahedron(&dcea); - setorg(dcea, pd); - setdest(dcea, pc); - setapex(dcea, pe); - setoppo(dcea, pa); - maketetrahedron(&cdeb); - setorg(cdeb, pc); - setdest(cdeb, pd); - setapex(cdeb, pe); - setoppo(cdeb, pb); - // Transfer the element attributes. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abtetlist[0].tet, i); - setelemattribute(dcea.tet, i, attrib); - setelemattribute(cdeb.tet, i, attrib); - } - // Transfer the volume constraints. - if (b->varvolume && !b->refine) { - volume = volumebound(abtetlist[0].tet); - setvolumebound(dcea.tet, volume); - setvolumebound(cdeb.tet, volume); - } - // Return two new tets. - newtetlist[0] = dcea; - newtetlist[1] = cdeb; - // Glue the two new tets. - bond(dcea, cdeb); - // Substitute the two new tets into the old three-tets cavity. - for (i = 0; i < 3; i++) { - fnext(dcea, newfront); // face dca, cea, eda. - esym(abtetlist[(i + 1) % 3], oldfront); - enextfnextself(oldfront); - // Get the adjacent tet at the face (may be a dummytet). - sym(oldfront, adjfront); - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - enqueueflipface(newfront, flipque); - } - enext2self(dcea); - } - for (i = 0; i < 3; i++) { - fnext(cdeb, newfront); // face cdb, deb, ecb. - esym(abtetlist[(i + 1) % 3], oldfront); - enext2fnextself(oldfront); - // Get the adjacent tet at the face (may be a dummytet). - sym(oldfront, adjfront); - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - enqueueflipface(newfront, flipque); - } - enextself(cdeb); - } - // Do not delete the old tets. - // for (i = 0; i < 3; i++) { - // tetrahedrondealloc(abtetlist[i].tet); - // } - return true; - } // if (doflip) - - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedgebytranNM() Remove an edge by transforming n-to-m tets. // -// // -// This routine attempts to remove a given edge (ab) by transforming the set // -// T of tets surrounding ab into another set T' of tets. T and T' have the // -// same outer faces and ab is not an edge of T' anymore. Let |T|=n, and |T'| // -// =m, it is actually a n-to-m flip for n > 3. The relation between n and m // -// depends on the method, ours is found below. // -// // -// 'abtetlist' contains n tets sharing ab. Imaging that ab is perpendicular // -// to the screen, where a lies in front of and b lies behind it. Let the // -// projections of the n apexes onto screen in clockwise order are: p_0, ... // -// p_n-1, respectively. The tets in the list are: [0]abp_0p_n-1,[1]abp_1p_0, // -// ..., [n-1]abp_n-1p_n-2, respectively. // -// // -// The principle of the approach is: Recursively reduce the link of ab by // -// using flip23 until only three faces remain, hence a flip32 can be applied // -// to remove ab. For a given face a.b.p_0, check a flip23 can be applied on // -// it, i.e, edge p_1.p_n-1 crosses it. NOTE*** We do the flip even p_1.p_n-1 // -// intersects with a.b (they are coplanar). If so, a degenerate tet (a.b.p_1.// -// p_n-1) is temporarily created, but it will be eventually removed by the // -// final flip32. This relaxation splits a flip44 into flip23 + flip32. *NOTE // -// Now suppose a.b.p_0 gets flipped, p_0 is not on the link of ab anymore. // -// The link is then reduced (by 1). 2 of the 3 new tets, p_n-1.p_1.p_0.a and // -// p_1.p_n-1.p_0.b, will be part of the new configuration. The left new tet,// -// a.b.p_1.p_n-1, goes into the new link of ab. A recurrence can be applied. // -// // -// If 'e1' and 'e2' are not NULLs, they specify an wanted edge to appear in // -// the new tet configuration. In such case, only do flip23 if edge e1<->e2 // -// can be recovered. It is used in removeedgebycombNM(). // -// // -// If ab gets removed. 'newtetlist' contains m new tets. By using the above // -// approach, the pairs (n, m) can be easily enumerated. For example, (3, 2),// -// (4, 4), (5, 6), (6, 8), (7, 10), (8, 12), (9, 14), (10, 16), and so on. // -// It is easy to deduce, that m = (n - 2) * 2, when n >= 3. The n tets in // -// 'abtetlist' are NOT deleted in this routine. The caller has the right to // -// either delete them or reverse this operation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::removeedgebytranNM(REAL *key, int n, triface *abtetlist, - triface *newtetlist, point e1, point e2, queue *flipque) -{ - triface tmpabtetlist[9]; // Temporary max 9 tets configuration. - triface newfront, oldfront, adjfront; - face checksh; - point pa, pb, p[10]; - REAL ori, cosmaxd, d1, d2; - REAL tmpkey; - REAL attrib, volume; - bool doflip, copflag, success; - int i, j, k; - - // Maximum 10 tets. - assert(n <= 10); - // Two points a and b are fixed. - pa = org(abtetlist[0]); - pb = dest(abtetlist[0]); - // The points p_0, p_1, ..., p_n-1 are permuted in each new configuration. - // These permutations can be easily done in the following loop. - // Loop through all the possible new tets configurations. Stop on finding - // a valid new tet configuration which also immproves the quality value. - for (i = 0; i < n; i++) { - // Get other n points for the current configuration. - for (j = 0; j < n; j++) { - p[j] = apex(abtetlist[(i + j) % n]); - } - // Is there a wanted edge? - if ((e1 != (point) NULL) && (e2 != (point) NULL)) { - // Yes. Skip this face if p[1]<->p[n-1] is not the edge. - if (!(((p[1] == e1) && (p[n - 1] == e2)) || - ((p[1] == e2) && (p[n - 1] == e1)))) continue; - } - // Test if face a.b.p_0 can be flipped (by flip23), ie, to check if the - // edge p_n-1.p_1 crosses face a.b.p_0 properly. - // Note. It is possible that face a.b.p_0 has type flip44, ie, a,b,p_1, - // and p_n-1 are coplanar. A trick is to split the flip44 into two - // steps: frist a flip23, then a flip32. The first step creates a - // degenerate tet (vol=0) which will be removed by the second flip. - ori = orient3d(pa, pb, p[1], p[n - 1]); - copflag = (ori == 0.0); // Are they coplanar? - if (ori >= 0.0) { - // Accept the coplanar case which supports flip44. - ori = orient3d(pb, p[0], p[1], p[n - 1]); - if (ori > 0.0) { - ori = orient3d(p[0], pa, p[1], p[n - 1]); - } - } - // Is face abc flipable? - if (ori > 0.0) { - // A valid (2-to-3) flip (or 4-to-4 flip) is found. - copflag ? flip44s++ : flip23s++; - doflip = true; - if (key != (REAL *) NULL) { - if (*key > -1.0) { - // Test if the new tets reduce the maximal dihedral angle. Only 2 - // tets, p_n-1.p_1.p_0.a and p_1.p_n-1.p_0.b, need to be tested - // The left one a.b.p_n-1.p_1 goes into the new link of ab. - tetalldihedral(p[n - 1], p[1], p[0], pa, NULL, &d1, NULL); - tetalldihedral(p[1], p[n - 1], p[0], pb, NULL, &d2, NULL); - cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle. - doflip = *key < cosmaxd; // Can the local quality be improved? - } - } - if (doflip) { - tmpkey = key != NULL ? *key : -1.0; - // Create the two new tets. - maketetrahedron(&(newtetlist[0])); - setorg(newtetlist[0], p[n - 1]); - setdest(newtetlist[0], p[1]); - setapex(newtetlist[0], p[0]); - setoppo(newtetlist[0], pa); - maketetrahedron(&(newtetlist[1])); - setorg(newtetlist[1], p[1]); - setdest(newtetlist[1], p[n - 1]); - setapex(newtetlist[1], p[0]); - setoppo(newtetlist[1], pb); - // Create the n - 1 temporary new tets (the new Star(ab)). - maketetrahedron(&(tmpabtetlist[0])); - setorg(tmpabtetlist[0], pa); - setdest(tmpabtetlist[0], pb); - setapex(tmpabtetlist[0], p[n - 1]); - setoppo(tmpabtetlist[0], p[1]); - for (j = 1; j < n - 1; j++) { - maketetrahedron(&(tmpabtetlist[j])); - setorg(tmpabtetlist[j], pa); - setdest(tmpabtetlist[j], pb); - setapex(tmpabtetlist[j], p[j]); - setoppo(tmpabtetlist[j], p[j + 1]); - } - // Transfer the element attributes. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(abtetlist[0].tet, j); - setelemattribute(newtetlist[0].tet, j, attrib); - setelemattribute(newtetlist[1].tet, j, attrib); - for (k = 0; k < n - 1; k++) { - setelemattribute(tmpabtetlist[k].tet, j, attrib); - } - } - // Transfer the volume constraints. - if (b->varvolume && !b->refine) { - volume = volumebound(abtetlist[0].tet); - setvolumebound(newtetlist[0].tet, volume); - setvolumebound(newtetlist[1].tet, volume); - for (k = 0; k < n - 1; k++) { - setvolumebound(tmpabtetlist[k].tet, volume); - } - } - // Glue the new tets at their internal faces: 2 + (n - 1). - bond(newtetlist[0], newtetlist[1]); // p_n-1.p_1.p_0. - fnext(newtetlist[0], newfront); - enext2fnext(tmpabtetlist[0], adjfront); - bond(newfront, adjfront); // p_n-1.p_1.a. - fnext(newtetlist[1], newfront); - enextfnext(tmpabtetlist[0], adjfront); - bond(newfront, adjfront); // p_n-1.p_1.b. - // Glue n - 1 internal faces around ab. - for (j = 0; j < n - 1; j++) { - fnext(tmpabtetlist[j], newfront); - bond(newfront, tmpabtetlist[(j + 1) % (n - 1)]); // a.b.p_j+1 - } - // Substitute the old tets with the new tets by connecting the new - // tets to the adjacent tets in the mesh. There are n * 2 (outer) - // faces of the new tets need to be operated. - // Note, after the substitution, the old tets still have pointers to - // their adjacent tets in the mesh. These pointers can be re-used - // to inverse the substitution. - for (j = 0; j < n; j++) { - // Get an old tet: [0]a.b.p_0.p_n-1 or [j]a.b.p_j.p_j-1, (j > 0). - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enextfnextself(oldfront); - // Get an adjacent tet at face: [0]a.p_0.p_n-1 or [j]a.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - // Get the corresponding face from the new tets. - if (j == 0) { - enext2fnext(newtetlist[0], newfront); // a.p_0.n_n-1 - } else if (j == 1) { - enextfnext(newtetlist[0], newfront); // a.p_1.p_0 - } else { // j >= 2. - enext2fnext(tmpabtetlist[j - 1], newfront); // a.p_j.p_j-1 - } - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - // Only queue the faces of the two new tets. - if (j < 2) enqueueflipface(newfront, flipque); - } - } - for (j = 0; j < n; j++) { - // Get an old tet: [0]a.b.p_0.p_n-1 or [j]a.b.p_j.p_j-1, (j > 0). - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enext2fnextself(oldfront); - // Get an adjacent tet at face: [0]b.p_0.p_n-1 or [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - // Get the corresponding face from the new tets. - if (j == 0) { - enextfnext(newtetlist[1], newfront); // b.p_0.n_n-1 - } else if (j == 1) { - enext2fnext(newtetlist[1], newfront); // b.p_1.p_0 - } else { // j >= 2. - enextfnext(tmpabtetlist[j - 1], newfront); // b.p_j.p_j-1 - } - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - if (flipque != (queue *) NULL) { - // Only queue the faces of the two new tets. - if (j < 2) enqueueflipface(newfront, flipque); - } - } - // Adjust the faces in the temporary new tets at ab for recursively - // processing on the n-1 tets.(See the description at beginning) - for (j = 0; j < n - 1; j++) { - fnextself(tmpabtetlist[j]); - } - if (n > 4) { - success = removeedgebytranNM(&tmpkey, n-1, tmpabtetlist, - &(newtetlist[2]), NULL, NULL, flipque); - } else { // assert(n == 4); - success = removeedgebyflip32(&tmpkey, tmpabtetlist, - &(newtetlist[2]), flipque); - } - // No matter it was success or not, delete the temporary tets. - for (j = 0; j < n - 1; j++) { - tetrahedrondealloc(tmpabtetlist[j].tet); - } - if (success) { - // The new configuration is good. - // Do not delete the old tets. - // for (j = 0; j < n; j++) { - // tetrahedrondealloc(abtetlist[j].tet); - // } - // Save the minimal improved quality value. - if (key != (REAL *) NULL) { - *key = (tmpkey < cosmaxd ? tmpkey : cosmaxd); - } - return true; - } else { - // The new configuration is bad, substitue back the old tets. - for (j = 0; j < n; j++) { - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enextfnextself(oldfront); // [0]a.p_0.p_n-1, [j]a.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - for (j = 0; j < n; j++) { - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - // Delete the new tets. - tetrahedrondealloc(newtetlist[0].tet); - tetrahedrondealloc(newtetlist[1].tet); - // If tmpkey has been modified, then the failure was not due to - // unflipable configuration, but the non-improvement. - if (key && (tmpkey < *key)) { - *key = tmpkey; - return false; - } - } // if (success) - } // if (doflip) - } // if (ori > 0.0) - } // for (i = 0; i < n; i++) - - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedgebycombNM() Remove an edge by combining two flipNMs. // -// // -// Given a set T of tets surrounding edge ab. The premise is that ab can not // -// be removed by a flipNM. This routine attempts to remove ab by two flipNMs,// -// i.e., first find and flip an edge af (or bf) by flipNM, then flip ab by // -// flipNM. If it succeeds, two sets T(ab) and T(af) of tets are replaced by // -// a new set T' and both ab and af are not edges in T' anymore. // -// // -// 'abtetlist' contains n tets sharing ab. Imaging that ab is perpendicular // -// to the screen, such that a lies in front of and b lies behind it. Let the // -// projections of the n apexes on the screen in clockwise order are: p_0,...,// -// p_n-1, respectively. So the list of tets are: [0]abp_0p_n-1, [1]abp_1p_0, // -// ..., [n-1]abp_n-1p_n-2, respectively. // -// // -// The principle of the approach is: for a face a.b.p_0, check if edge b.p_0 // -// is of type N32 (or N44). If it is, then try to do a flipNM on it. If the // -// flip is successful, then try to do another flipNM on a.b. If one of the // -// two flipNMs fails, restore the old tets as they have never been flipped. // -// Then try the next face a.b.p_1. The process can be looped for all faces // -// having ab. Stop if ab is removed or all faces have been visited. Note in // -// the above description only b.p_0 is considered, a.p_0 is done by swapping // -// the position of a and b. // -// // -// Similar operations have been described in [Joe,1995]. My approach checks // -// more cases for finding flips than Joe's. For instance, the cases (1)-(7) // -// of Joe only consider abf for finding a flip (T23/T32). My approach looks // -// all faces at ab for finding flips. Moreover, the flipNM can flip an edge // -// whose star may have more than 3 tets while Joe's only works on 3-tet case.// -// // -// If ab is removed, 'newtetlist' contains the new tets. Two sets 'abtetlist'// -// (n tets) and 'bftetlist' (n1 tets) have been replaced. The number of new // -// tets can be calculated by follows: the 1st flip transforms n1 tets into // -// (n1 - 2) * 2 new tets, however,one of the new tets goes into the new link // -// of ab, i.e., the reduced tet number in Star(ab) is n - 1; the 2nd flip // -// transforms n - 1 tets into (n - 3) * 2 new tets. Hence the number of new // -// tets are: m = ((n1 - 2) * 2 - 1) + (n - 3) * 2. The old tets are NOT del-// -// eted. The caller has the right to delete them or reverse the operation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist, - int *n1, triface *bftetlist, triface *newtetlist, queue *flipque) -{ - triface tmpabtetlist[11]; - triface newfront, oldfront, adjfront; - face checksh; - point pa, pb, p[10]; - REAL ori, tmpkey, tmpkey2; - REAL attrib, volume; - bool doflip, success; - int twice, count; - int i, j, k, m; - - // Maximal 10 tets in Star(ab). - assert(n <= 10); - - // Do the following procedure twice, one for flipping edge b.p_0 and the - // other for p_0.a which is symmetric to the first. - twice = 0; - do { - // Two points a and b are fixed. - pa = org(abtetlist[0]); - pb = dest(abtetlist[0]); - // The points p_0, ..., p_n-1 are permuted in the following loop. - for (i = 0; i < n; i++) { - // Get the n points for the current configuration. - for (j = 0; j < n; j++) { - p[j] = apex(abtetlist[(i + j) % n]); - } - // Check if b.p_0 is of type N32 or N44. - ori = orient3d(pb, p[0], p[1], p[n - 1]); - if ((ori > 0) && (key != (REAL *) NULL)) { - // b.p_0 is not N32. However, it is possible that the tet b.p_0.p_1. - // p_n-1 has worse quality value than the key. In such case, also - // try to flip b.p_0. - tetalldihedral(pb, p[0], p[n - 1], p[1], NULL, &tmpkey, NULL); - if (tmpkey < *key) ori = 0.0; - } - if (ori <= 0.0) { - // b.p_0 is either N32 or N44. Try the 1st flipNM. - bftetlist[0] = abtetlist[i]; - enextself(bftetlist[0]);// go to edge b.p_0. - adjustedgering(bftetlist[0], CW); // edge p_0.b. - assert(apex(bftetlist[0]) == pa); - // Form Star(b.p_0). - doflip = true; - *n1 = 0; - do { - // Is the list full? - if (*n1 == 10) break; - if (checksubfaces) { - // Stop if a subface appears. - tspivot(bftetlist[*n1], checksh); - if (checksh.sh != dummysh) { - doflip = false; break; - } - } - // Get the next tet at p_0.b. - fnext(bftetlist[*n1], bftetlist[(*n1) + 1]); - (*n1)++; - } while (apex(bftetlist[*n1]) != pa); - // 2 <= n1 <= 10. - if (doflip) { - success = false; - tmpkey = -1.0; // = acos(pi). - if (key != (REAL *) NULL) tmpkey = *key; - m = 0; - if (*n1 == 3) { - // Three tets case. Try flip32. - success = removeedgebyflip32(&tmpkey,bftetlist,newtetlist,flipque); - m = 2; - } else if ((*n1 > 3) && (*n1 < 7)) { - // Four or more tets case. Try flipNM. - success = removeedgebytranNM(&tmpkey, *n1, bftetlist, newtetlist, - p[1], p[n - 1], flipque); - // If success, the number of new tets. - m = ((*n1) - 2) * 2; - } else { - if (b->verbose > 1) { - printf(" !! Unhandled case: n1 = %d.\n", *n1); - } - } - if (success) { - // b.p_0 is flipped. The link of ab is reduced (by 1), i.e., p_0 - // is not on the link of ab. Two old tets a.b.p_0.p_n-1 and - // a.b.p_1.p_0 have been removed from the Star(ab) and one new - // tet t = a.b.p_1.p_n-1 belongs to Star(ab). - // Find t in the 'newtetlist' and remove it from the list. - setpointmark(pa, -pointmark(pa) - 1); - setpointmark(pb, -pointmark(pb) - 1); - assert(m > 0); - for (j = 0; j < m; j++) { - tmpabtetlist[0] = newtetlist[j]; - // Does it has ab? - count = 0; - for (k = 0; k < 4; k++) { - if (pointmark((point)(tmpabtetlist[0].tet[4+k])) < 0) count++; - } - if (count == 2) { - // It is. Adjust t to be the edge ab. - for (tmpabtetlist[0].loc = 0; tmpabtetlist[0].loc < 4; - tmpabtetlist[0].loc++) { - if ((oppo(tmpabtetlist[0]) != pa) && - (oppo(tmpabtetlist[0]) != pb)) break; - } - // The face of t must contain ab. - assert(tmpabtetlist[0].loc < 4); - findedge(&(tmpabtetlist[0]), pa, pb); - break; - } - } - assert(j < m); // The tet must exist. - // Remove t from list. Fill t's position by the last tet. - newtetlist[j] = newtetlist[m - 1]; - setpointmark(pa, -(pointmark(pa) + 1)); - setpointmark(pb, -(pointmark(pb) + 1)); - // Create the temporary Star(ab) for the next flipNM. - adjustedgering(tmpabtetlist[0], CCW); - if (org(tmpabtetlist[0]) != pa) { - fnextself(tmpabtetlist[0]); - esymself(tmpabtetlist[0]); - } -#ifdef SELF_CHECK - // Make sure current edge is a->b. - assert(org(tmpabtetlist[0]) == pa); - assert(dest(tmpabtetlist[0]) == pb); - assert(apex(tmpabtetlist[0]) == p[n - 1]); - assert(oppo(tmpabtetlist[0]) == p[1]); -#endif // SELF_CHECK - // There are n - 2 left temporary tets. - for (j = 1; j < n - 1; j++) { - maketetrahedron(&(tmpabtetlist[j])); - setorg(tmpabtetlist[j], pa); - setdest(tmpabtetlist[j], pb); - setapex(tmpabtetlist[j], p[j]); - setoppo(tmpabtetlist[j], p[j + 1]); - } - // Transfer the element attributes. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(abtetlist[0].tet, j); - for (k = 0; k < n - 1; k++) { - setelemattribute(tmpabtetlist[k].tet, j, attrib); - } - } - // Transfer the volume constraints. - if (b->varvolume && !b->refine) { - volume = volumebound(abtetlist[0].tet); - for (k = 0; k < n - 1; k++) { - setvolumebound(tmpabtetlist[k].tet, volume); - } - } - // Glue n - 1 internal faces of Star(ab). - for (j = 0; j < n - 1; j++) { - fnext(tmpabtetlist[j], newfront); - bond(newfront, tmpabtetlist[(j + 1) % (n - 1)]); // a.b.p_j+1 - } - // Substitute the old tets with the new tets by connecting the - // new tets to the adjacent tets in the mesh. There are (n-2) - // * 2 (outer) faces of the new tets need to be operated. - // Note that the old tets still have the pointers to their - // adjacent tets in the mesh. These pointers can be re-used - // to inverse the substitution. - for (j = 2; j < n; j++) { - // Get an old tet: [j]a.b.p_j.p_j-1, (j > 1). - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enextfnextself(oldfront); - // Get an adjacent tet at face: [j]a.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - // Get the corresponding face from the new tets. - // j >= 2. - enext2fnext(tmpabtetlist[j - 1], newfront); // a.p_j.p_j-1 - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - } - for (j = 2; j < n; j++) { - // Get an old tet: [j]a.b.p_j.p_j-1, (j > 2). - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enext2fnextself(oldfront); - // Get an adjacent tet at face: [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - // Get the corresponding face from the new tets. - // j >= 2. - enextfnext(tmpabtetlist[j - 1], newfront); // b.p_j.p_j-1 - bond(newfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(newfront, checksh); - } - } - } - // Adjust the faces in the temporary new tets at ab for - // recursively processing on the n-1 tets. - for (j = 0; j < n - 1; j++) { - fnextself(tmpabtetlist[j]); - } - tmpkey2 = -1; - if (key) tmpkey2 = *key; - if ((n - 1) == 3) { - success = removeedgebyflip32(&tmpkey2, tmpabtetlist, - &(newtetlist[m - 1]), flipque); - } else { // assert((n - 1) >= 4); - success = removeedgebytranNM(&tmpkey2, n - 1, tmpabtetlist, - &(newtetlist[m - 1]), NULL, NULL, flipque); - } - // No matter it was success or not, delete the temporary tets. - for (j = 0; j < n - 1; j++) { - tetrahedrondealloc(tmpabtetlist[j].tet); - } - if (success) { - // The new configuration is good. - // Do not delete the old tets. - // for (j = 0; j < n; j++) { - // tetrahedrondealloc(abtetlist[j].tet); - // } - // Return the bigger dihedral in the two sets of new tets. - if (key != (REAL *) NULL) { - *key = tmpkey2 < tmpkey ? tmpkey2 : tmpkey; - } - return true; - } else { - // The new configuration is bad, substitue back the old tets. - for (j = 0; j < n; j++) { - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enextfnextself(oldfront); // [0]a.p_0.p_n-1, [j]a.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy. - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - for (j = 0; j < n; j++) { - oldfront = abtetlist[(i + j) % n]; - esymself(oldfront); - enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - // Substitute back the old tets of the first flip. - for (j = 0; j < *n1; j++) { - oldfront = bftetlist[j]; - esymself(oldfront); - enextfnextself(oldfront); - sym(oldfront, adjfront); // adjfront may be dummy. - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - for (j = 0; j < *n1; j++) { - oldfront = bftetlist[j]; - esymself(oldfront); - enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1. - sym(oldfront, adjfront); // adjfront may be dummy - bond(oldfront, adjfront); - if (checksubfaces) { - tspivot(oldfront, checksh); - if (checksh.sh != dummysh) { - tsbond(oldfront, checksh); - } - } - } - // Delete the new tets of the first flip. Note that one new - // tet has already been removed from the list. - for (j = 0; j < m - 1; j++) { - tetrahedrondealloc(newtetlist[j].tet); - } - } // if (success) - } // if (success) - } // if (doflip) - } // if (ori <= 0.0) - } // for (i = 0; i < n; i++) - // Inverse a and b and the tets configuration. - for (i = 0; i < n; i++) newtetlist[i] = abtetlist[i]; - for (i = 0; i < n; i++) { - oldfront = newtetlist[n - i - 1]; - esymself(oldfront); - fnextself(oldfront); - abtetlist[i] = oldfront; - } - twice++; - } while (twice < 2); - - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// splittetrahedron() Insert a point into a tetrahedron, split it into // -// four tetrahedra. // -// // -// The tetrahedron is given by 'splittet'. Let it is abcd. The inserting // -// point 'newpoint' v should lie strictly inside abcd. // -// // -// Splitting a tetrahedron is to shrink abcd to abcv, and create three new // -// tetrahedra badv, cbdv, and acdv. // -// // -// On completion, 'splittet' returns abcv. If 'flipqueue' is not NULL, it // -// contains all possibly non-locally Delaunay faces. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::splittetrahedron(point newpoint, triface* splittet, - queue* flipqueue) -{ - triface oldabd, oldbcd, oldcad; // Old configuration. - triface abdcasing, bcdcasing, cadcasing; - face abdsh, bcdsh, cadsh; - triface abcv, badv, cbdv, acdv; // New configuration. - triface worktet; - face abseg, bcseg, caseg; - face adseg, bdseg, cdseg; - point pa, pb, pc, pd; - REAL attrib, volume; - int i; - - abcv = *splittet; - abcv.ver = 0; - // Set the changed vertices and new tetrahedron. - pa = org(abcv); - pb = dest(abcv); - pc = apex(abcv); - pd = oppo(abcv); - - if (b->verbose > 1) { - printf(" Inserting point %d in tetrahedron (%d, %d, %d, %d).\n", - pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc), - pointmark(pd)); - } - - fnext(abcv, oldabd); - enextfnext(abcv, oldbcd); - enext2fnext(abcv, oldcad); - sym(oldabd, abdcasing); - sym(oldbcd, bcdcasing); - sym(oldcad, cadcasing); - maketetrahedron(&badv); - maketetrahedron(&cbdv); - maketetrahedron(&acdv); - - // Set 'badv' vertices. - setorg (badv, pb); - setdest(badv, pa); - setapex(badv, pd); - setoppo(badv, newpoint); - // Set 'cbdv' vertices. - setorg (cbdv, pc); - setdest(cbdv, pb); - setapex(cbdv, pd); - setoppo(cbdv, newpoint); - // Set 'acdv' vertices. - setorg (acdv, pa); - setdest(acdv, pc); - setapex(acdv, pd); - setoppo(acdv, newpoint); - // Set 'abcv' vertices - setoppo(abcv, newpoint); - - // Set the element attributes of the new tetrahedra. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abcv.tet, i); - setelemattribute(badv.tet, i, attrib); - setelemattribute(cbdv.tet, i, attrib); - setelemattribute(acdv.tet, i, attrib); - } - // Set the volume constraint of the new tetrahedra. - if (b->varvolume) { - volume = volumebound(abcv.tet); - setvolumebound(badv.tet, volume); - setvolumebound(cbdv.tet, volume); - setvolumebound(acdv.tet, volume); - } - - // Bond the new triangles to the surrounding tetrahedron. - bond(badv, abdcasing); - bond(cbdv, bcdcasing); - bond(acdv, cadcasing); - // There may exist subfaces need to be bonded to the new tetrahedra. - if (checksubfaces) { - tspivot(oldabd, abdsh); - if (abdsh.sh != dummysh) { - tsdissolve(oldabd); - tsbond(badv, abdsh); - } - tspivot(oldbcd, bcdsh); - if (bcdsh.sh != dummysh) { - tsdissolve(oldbcd); - tsbond(cbdv, bcdsh); - } - tspivot(oldcad, cadsh); - if (cadsh.sh != dummysh) { - tsdissolve(oldcad); - tsbond(acdv, cadsh); - } - } else if (checksubsegs) { - tsspivot1(abcv, abseg); - if (abseg.sh != dummysh) { - tssbond1(badv, abseg); - } - enext(abcv, worktet); - tsspivot1(worktet, bcseg); - if (bcseg.sh != dummysh) { - tssbond1(cbdv, bcseg); - } - enext2(abcv, worktet); - tsspivot1(worktet, caseg); - if (caseg.sh != dummysh) { - tssbond1(acdv, caseg); - } - fnext(abcv, worktet); - enext2self(worktet); - tsspivot1(worktet, adseg); - if (adseg.sh != dummysh) { - tssdissolve1(worktet); - enext(badv, worktet); - tssbond1(worktet, adseg); - enext2(acdv, worktet); - tssbond1(worktet, adseg); - } - enextfnext(abcv, worktet); - enext2self(worktet); - tsspivot1(worktet, bdseg); - if (bdseg.sh != dummysh) { - tssdissolve1(worktet); - enext(cbdv, worktet); - tssbond1(worktet, bdseg); - enext2(badv, worktet); - tssbond1(worktet, bdseg); - } - enext2fnext(abcv, worktet); - enext2self(worktet); - tsspivot1(worktet, cdseg); - if (cdseg.sh != dummysh) { - tssdissolve1(worktet); - enext(acdv, worktet); - tssbond1(worktet, cdseg); - enext2(cbdv, worktet); - tssbond1(worktet, cdseg); - } - } - badv.loc = 3; - cbdv.loc = 2; - bond(badv, cbdv); - cbdv.loc = 3; - acdv.loc = 2; - bond(cbdv, acdv); - acdv.loc = 3; - badv.loc = 2; - bond(acdv, badv); - badv.loc = 1; - bond(badv, oldabd); - cbdv.loc = 1; - bond(cbdv, oldbcd); - acdv.loc = 1; - bond(acdv, oldcad); - - badv.loc = 0; - cbdv.loc = 0; - acdv.loc = 0; - if (b->verbose > 3) { - printf(" Updating abcv "); - printtet(&abcv); - printf(" Creating badv "); - printtet(&badv); - printf(" Creating cbdv "); - printtet(&cbdv); - printf(" Creating acdv "); - printtet(&acdv); - } - - if (flipqueue != (queue *) NULL) { - enqueueflipface(abcv, flipqueue); - enqueueflipface(badv, flipqueue); - enqueueflipface(cbdv, flipqueue); - enqueueflipface(acdv, flipqueue); - } - - // Save a handle for quick point location. - recenttet = abcv; - // Set the return handle be abcv. - *splittet = abcv; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unsplittetrahedron() Reverse the operation of inserting a point into a // -// tetrahedron, so as to remove the newly inserted // -// point from the mesh. // -// // -// Assume the origional tetrahedron is abcd, it was split by v into four // -// tetrahedra abcv, badv, cbdv, and acdv. 'splittet' represents face abc of // -// abcv (i.e., its opposite is v). // -// // -// Point v is removed by expanding abcv to abcd, deleting three tetrahedra // -// badv, cbdv and acdv. On return, point v is not deleted in this routine. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unsplittetrahedron(triface* splittet) -{ - triface abcv, badv, cbdv, acdv; - triface oldabv, oldbcv, oldcav; - triface badcasing, cbdcasing, acdcasing; - face badsh, cbdsh, acdsh; - - abcv = *splittet; - adjustedgering(abcv, CCW); // for sure. - fnext(abcv, oldabv); - fnext(oldabv, badv); - esymself(badv); - enextfnext(abcv, oldbcv); - fnext(oldbcv, cbdv); - esymself(cbdv); - enext2fnext(abcv, oldcav); - fnext(oldcav, acdv); - esymself(acdv); - - if (b->verbose > 1) { - printf(" Removing point %d in tetrahedron (%d, %d, %d, %d).\n", - pointmark(oppo(abcv)), pointmark(org(abcv)), pointmark(dest(abcv)), - pointmark(apex(abcv)), pointmark(apex(badv))); - } - - sym(badv, badcasing); - tspivot(badv, badsh); - sym(cbdv, cbdcasing); - tspivot(cbdv, cbdsh); - sym(acdv, acdcasing); - tspivot(acdv, acdsh); - - // Expanding abcv to abcd. - setoppo(abcv, apex(badv)); - bond(oldabv, badcasing); - if (badsh.sh != dummysh) { - tsbond(oldabv, badsh); - } - bond(oldbcv, cbdcasing); - if (cbdsh.sh != dummysh) { - tsbond(oldbcv, cbdsh); - } - bond(oldcav, acdcasing); - if (acdsh.sh != dummysh) { - tsbond(oldcav, acdsh); - } - - // Delete the three split-out tetrahedra. - tetrahedrondealloc(badv.tet); - tetrahedrondealloc(cbdv.tet); - tetrahedrondealloc(acdv.tet); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// splittetface() Insert a point on a face of a mesh. // -// // -// 'splittet' is the splitting face. Let it is abcd, where abc is the face // -// will be split. If abc is not a hull face, abce is the tetrahedron at the // -// opposite of d. // -// // -// To split face abc by a point v is to shrink the tetrahedra abcd to abvd, // -// create two new tetrahedra bcvd, cavd. If abc is not a hull face, shrink // -// the tetrahedra bace to bave, create two new tetrahedra cbve, acve. // -// // -// If abc is a subface, it is split into three subfaces simultaneously by // -// calling routine splitsubface(), hence, abv, bcv, cav. The edge rings of // -// the split subfaces have the same orientation as abc's. // -// // -// On completion, 'splittet' returns abvd. If 'flipqueue' is not NULL, it // -// contains all possibly non-locally Delaunay faces. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::splittetface(point newpoint, triface* splittet, - queue* flipqueue) -{ - triface abcd, bace; // Old configuration. - triface oldbcd, oldcad, oldace, oldcbe; - triface bcdcasing, cadcasing, acecasing, cbecasing; - face abcsh, bcdsh, cadsh, acesh, cbesh; - triface abvd, bcvd, cavd, bave, cbve, acve; // New configuration. - triface worktet; - face bcseg, caseg; - face adseg, bdseg, cdseg; - face aeseg, beseg, ceseg; - point pa, pb, pc, pd, pe; - REAL attrib, volume; - bool mirrorflag; - int i; - - abcd = *splittet; - // abcd.ver = 0; // Adjust to be CCW edge ring. - adjustedgering(abcd, CCW); - pa = org(abcd); - pb = dest(abcd); - pc = apex(abcd); - pd = oppo(abcd); - pe = (point) NULL; // avoid a compile warning. - // Is there a second tetrahderon? - mirrorflag = issymexist(&abcd); - if (mirrorflag) { - // This is an interior face. - sym(abcd, bace); - findedge(&bace, dest(abcd), org(abcd)); - pe = oppo(bace); - } - if (checksubfaces) { - // Is there a subface need to be split together? - tspivot(abcd, abcsh); - if (abcsh.sh != dummysh) { - // Exists! Keep the edge ab of both handles be the same. - findedge(&abcsh, org(abcd), dest(abcd)); - } - } - - if (b->verbose > 1) { - printf(" Inserting point %d on face (%d, %d, %d).\n", pointmark(newpoint), - pointmark(pa), pointmark(pb), pointmark(pc)); - } - - // Save the old configuration at faces bcd and cad. - enextfnext(abcd, oldbcd); - enext2fnext(abcd, oldcad); - sym(oldbcd, bcdcasing); - sym(oldcad, cadcasing); - // Create two new tetrahedra. - maketetrahedron(&bcvd); - maketetrahedron(&cavd); - if (mirrorflag) { - // Save the old configuration at faces bce and cae. - enextfnext(bace, oldace); - enext2fnext(bace, oldcbe); - sym(oldace, acecasing); - sym(oldcbe, cbecasing); - // Create two new tetrahedra. - maketetrahedron(&acve); - maketetrahedron(&cbve); - } else { - // Splitting a boundary face increases the number of boundary faces. - hullsize += 2; - } - - // Set vertices to the changed tetrahedron and new tetrahedra. - abvd = abcd; // Update 'abcd' to 'abvd'. - setapex(abvd, newpoint); - setorg (bcvd, pb); // Set 'bcvd'. - setdest(bcvd, pc); - setapex(bcvd, newpoint); - setoppo(bcvd, pd); - setorg (cavd, pc); // Set 'cavd'. - setdest(cavd, pa); - setapex(cavd, newpoint); - setoppo(cavd, pd); - // Set the element attributes of the new tetrahedra. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(abvd.tet, i); - setelemattribute(bcvd.tet, i, attrib); - setelemattribute(cavd.tet, i, attrib); - } - if (b->varvolume) { - // Set the area constraint of the new tetrahedra. - volume = volumebound(abvd.tet); - setvolumebound(bcvd.tet, volume); - setvolumebound(cavd.tet, volume); - } - if (mirrorflag) { - bave = bace; // Update 'bace' to 'bave'. - setapex(bave, newpoint); - setorg (acve, pa); // Set 'acve'. - setdest(acve, pc); - setapex(acve, newpoint); - setoppo(acve, pe); - setorg (cbve, pc); // Set 'cbve'. - setdest(cbve, pb); - setapex(cbve, newpoint); - setoppo(cbve, pe); - // Set the element attributes of the new tetrahedra. - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(bave.tet, i); - setelemattribute(acve.tet, i, attrib); - setelemattribute(cbve.tet, i, attrib); - } - if (b->varvolume) { - // Set the area constraint of the new tetrahedra. - volume = volumebound(bave.tet); - setvolumebound(acve.tet, volume); - setvolumebound(cbve.tet, volume); - } - } - - // Bond the new tetrahedra to the surrounding tetrahedra. - bcvd.loc = 1; - bond(bcvd, bcdcasing); - cavd.loc = 1; - bond(cavd, cadcasing); - bcvd.loc = 3; - bond(bcvd, oldbcd); - cavd.loc = 2; - bond(cavd, oldcad); - bcvd.loc = 2; - cavd.loc = 3; - bond(bcvd, cavd); - if (mirrorflag) { - acve.loc = 1; - bond(acve, acecasing); - cbve.loc = 1; - bond(cbve, cbecasing); - acve.loc = 3; - bond(acve, oldace); - cbve.loc = 2; - bond(cbve, oldcbe); - acve.loc = 2; - cbve.loc = 3; - bond(acve, cbve); - // Bond two new coplanar facets. - bcvd.loc = 0; - cbve.loc = 0; - bond(bcvd, cbve); - cavd.loc = 0; - acve.loc = 0; - bond(cavd, acve); - } - - // There may exist subface needed to be bonded to the new tetrahedra. - if (checksubfaces) { - tspivot(oldbcd, bcdsh); - if (bcdsh.sh != dummysh) { - tsdissolve(oldbcd); - bcvd.loc = 1; - tsbond(bcvd, bcdsh); - } - tspivot(oldcad, cadsh); - if (cadsh.sh != dummysh) { - tsdissolve(oldcad); - cavd.loc = 1; - tsbond(cavd, cadsh); - } - if (mirrorflag) { - tspivot(oldace, acesh); - if (acesh.sh != dummysh) { - tsdissolve(oldace); - acve.loc = 1; - tsbond(acve, acesh); - } - tspivot(oldcbe, cbesh); - if (cbesh.sh != dummysh) { - tsdissolve(oldcbe); - cbve.loc = 1; - tsbond(cbve, cbesh); - } - } - // Is there a subface needs to be split together? - if (abcsh.sh != dummysh) { - // Split this subface 'abc' into three i.e, abv, bcv, cav. - splitsubface(newpoint, &abcsh, (queue *) NULL); - } - } else if (checksubsegs) { - // abvd.loc = abvd.ver = 0; - bcvd.loc = bcvd.ver = 0; - cavd.loc = cavd.ver = 0; - if (mirrorflag) { - // bave.loc = bave.ver = 0; - cbve.loc = cbve.ver = 0; - acve.loc = acve.ver = 0; - } - enext(abvd, worktet); - tsspivot1(worktet, bcseg); - if (bcseg.sh != dummysh) { - tssdissolve1(worktet); - tssbond1(bcvd, bcseg); - if (mirrorflag) { - enext2(bave, worktet); - tssdissolve1(worktet); - tssbond1(cbve, bcseg); - } - } - enext2(abvd, worktet); - tsspivot1(worktet, caseg); - if (caseg.sh != dummysh) { - tssdissolve1(worktet); - tssbond1(cavd, caseg); - if (mirrorflag) { - enext(bave, worktet); - tssdissolve1(worktet); - tssbond1(acve, caseg); - } - } - fnext(abvd, worktet); - enext2self(worktet); - tsspivot1(worktet, adseg); - if (adseg.sh != dummysh) { - fnext(cavd, worktet); - enextself(worktet); - tssbond1(worktet, adseg); - } - fnext(abvd, worktet); - enextself(worktet); - tsspivot1(worktet, bdseg); - if (bdseg.sh != dummysh) { - fnext(bcvd, worktet); - enext2self(worktet); - tssbond1(worktet, bdseg); - } - enextfnext(abvd, worktet); - enextself(worktet); - tsspivot1(worktet, cdseg); - if (cdseg.sh != dummysh) { - tssdissolve1(worktet); - fnext(bcvd, worktet); - enextself(worktet); - tssbond1(worktet, cdseg); - fnext(cavd, worktet); - enext2self(worktet); - tssbond1(worktet, cdseg); - } - if (mirrorflag) { - fnext(bave, worktet); - enextself(worktet); - tsspivot1(worktet, aeseg); - if (aeseg.sh != dummysh) { - fnext(acve, worktet); - enext2self(worktet); - tssbond1(worktet, aeseg); - } - fnext(bave, worktet); - enext2self(worktet); - tsspivot1(worktet, beseg); - if (beseg.sh != dummysh) { - fnext(cbve, worktet); - enextself(worktet); - tssbond1(worktet, beseg); - } - enextfnext(bave, worktet); - enextself(worktet); - tsspivot1(worktet, ceseg); - if (ceseg.sh != dummysh) { - tssdissolve1(worktet); - fnext(cbve, worktet); - enext2self(worktet); - tssbond1(worktet, ceseg); - fnext(acve, worktet); - enextself(worktet); - tssbond1(worktet, ceseg); - } - } - } - - // Save a handle for quick point location. - recenttet = abvd; - // Set the return handle be abvd. - *splittet = abvd; - - bcvd.loc = 0; - cavd.loc = 0; - if (mirrorflag) { - cbve.loc = 0; - acve.loc = 0; - } - if (b->verbose > 3) { - printf(" Updating abvd "); - printtet(&abvd); - printf(" Creating bcvd "); - printtet(&bcvd); - printf(" Creating cavd "); - printtet(&cavd); - if (mirrorflag) { - printf(" Updating bave "); - printtet(&bave); - printf(" Creating cbve "); - printtet(&cbve); - printf(" Creating acve "); - printtet(&acve); - } - } - - if (flipqueue != (queue *) NULL) { - fnextself(abvd); - enqueueflipface(abvd, flipqueue); - fnextself(bcvd); - enqueueflipface(bcvd, flipqueue); - fnextself(cavd); - enqueueflipface(cavd, flipqueue); - if (mirrorflag) { - fnextself(bave); - enqueueflipface(bave, flipqueue); - fnextself(cbve); - enqueueflipface(cbve, flipqueue); - fnextself(acve); - enqueueflipface(acve, flipqueue); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unsplittetface() Reverse the operation of inserting a point on a face, // -// so as to remove the newly inserted point. // -// // -// Assume the original face is abc, the tetrahedron containing abc is abcd. // -// If abc is not a hull face, bace is the tetrahedron at the opposite of d. // -// After face abc was split by a point v, tetrahedron abcd had been split // -// into three tetrahedra, abvd, bcvd, cavd, and bace (if it exists) had been // -// split into bave, cbve, acve. 'splittet' represents abvd (its apex is v). // -// // -// Point v is removed by expanding abvd to abcd, deleting two tetrahedra // -// bcvd, cavd. Expanding bave(if it exists) to bace, deleting two tetrahedra // -// cbve, acve. If abv is a subface, routine unsplitsubface() will be called // -// to reverse the operation of splitting a subface. On completion, point v // -// is not deleted in this routine. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unsplittetface(triface* splittet) -{ - triface abvd, bcvd, cavd, bave, cbve, acve; - triface oldbvd, oldvad, oldvbe, oldave; - triface bcdcasing, cadcasing, cbecasing, acecasing; - face bcdsh, cadsh, cbesh, acesh; - face abvsh; - bool mirrorflag; - - abvd = *splittet; - adjustedgering(abvd, CCW); // for sure. - enextfnext(abvd, oldbvd); - fnext(oldbvd, bcvd); - esymself(bcvd); - enextself(bcvd); - enext2fnext(abvd, oldvad); - fnext(oldvad, cavd); - esymself(cavd); - enext2self(cavd); - // Is there a second tetrahedron? - sym(abvd, bave); - mirrorflag = bave.tet != dummytet; - if (mirrorflag) { - findedge(&bave, dest(abvd), org(abvd)); - enextfnext(bave, oldave); - fnext(oldave, acve); - esymself(acve); - enextself(acve); - enext2fnext(bave, oldvbe); - fnext(oldvbe, cbve); - esymself(cbve); - enext2self(cbve); - } else { - // Unsplit a hull face decrease the number of boundary faces. - hullsize -= 2; - } - // Is there a subface at abv. - tspivot(abvd, abvsh); - if (abvsh.sh != dummysh) { - // Exists! Keep the edge ab of both handles be the same. - findedge(&abvsh, org(abvd), dest(abvd)); - } - - if (b->verbose > 1) { - printf(" Removing point %d on face (%d, %d, %d).\n", - pointmark(apex(abvd)), pointmark(org(abvd)), pointmark(dest(abvd)), - pointmark(dest(bcvd))); - } - - fnextself(bcvd); // bcvd has changed to bcdv. - sym(bcvd, bcdcasing); - tspivot(bcvd, bcdsh); - fnextself(cavd); // cavd has changed to cadv. - sym(cavd, cadcasing); - tspivot(cavd, cadsh); - if (mirrorflag) { - fnextself(acve); // acve has changed to acev. - sym(acve, acecasing); - tspivot(acve, acesh); - fnextself(cbve); // cbve has changed to cbev. - sym(cbve, cbecasing); - tspivot(cbve, cbesh); - } - - // Expand abvd to abcd. - setapex(abvd, dest(bcvd)); - bond(oldbvd, bcdcasing); - if (bcdsh.sh != dummysh) { - tsbond(oldbvd, bcdsh); - } - bond(oldvad, cadcasing); - if (cadsh.sh != dummysh) { - tsbond(oldvad, cadsh); - } - if (mirrorflag) { - // Expanding bave to bace. - setapex(bave, dest(acve)); - bond(oldave, acecasing); - if (acesh.sh != dummysh) { - tsbond(oldave, acesh); - } - bond(oldvbe, cbecasing); - if (cbesh.sh != dummysh) { - tsbond(oldvbe, cbesh); - } - } - - // Unsplit a subface if there exists. - if (abvsh.sh != dummysh) { - unsplitsubface(&abvsh); - } - - // Delete the split-out tetrahedra. - tetrahedrondealloc(bcvd.tet); - tetrahedrondealloc(cavd.tet); - if (mirrorflag) { - tetrahedrondealloc(acve.tet); - tetrahedrondealloc(cbve.tet); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// splitsubface() Insert a point on a subface, split it into three. // -// // -// The subface is 'splitface'. Let it is abc. The inserting point 'newpoint'// -// v should lie inside abc. If the neighbor tetrahedra of abc exist, i.e., // -// abcd and bace, they should have been split by routine splittetface() // -// before calling this routine, so the connection between the new tetrahedra // -// and new subfaces can be correctly set. // -// // -// To split subface abc by point v is to shrink abc to abv, create two new // -// subfaces bcv and cav. Set the connection between updated and new created // -// subfaces. If there is a subsegment at edge bc or ca, connection of new // -// subface (bcv or cav) to its casing subfaces is a face link, 'casingin' is // -// the predecessor and 'casingout' is the successor. It is important to keep // -// the orientations of the edge rings of the updated and created subfaces be // -// the same as abc's. So they have the same orientation as other subfaces of // -// this facet with respect to the lift point of this facet. // -// // -// On completion, 'splitface' returns abv. If 'flipqueue' is not NULL, it // -// returns all possibly non-Delaunay edges. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::splitsubface(point newpoint, face* splitface, - queue* flipqueue) -{ - triface abvd, bcvd, cavd, bave, cbve, acve; - face abc, oldbc, oldca, bc, ca, spinsh; - face bccasin, bccasout, cacasin, cacasout; - face abv, bcv, cav; - point pa, pb, pc; - - abc = *splitface; - // The newly created subfaces will have the same edge ring as abc. - adjustedgering(abc, CCW); - pa = sorg(abc); - pb = sdest(abc); - pc = sapex(abc); - - if (b->verbose > 1) { - printf(" Inserting point %d on subface (%d, %d, %d).\n", - pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc)); - } - - // Save the old configuration at edge bc and ca. Subsegments may appear - // at both sides, save the face links and dissolve them. - senext(abc, oldbc); - senext2(abc, oldca); - spivot(oldbc, bccasout); - sspivot(oldbc, bc); - if (bc.sh != dummysh) { - if (oldbc.sh != bccasout.sh) { - // 'oldbc' is not self-bonded. - spinsh = bccasout; - do { - bccasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldbc.sh); - } else { - bccasout.sh = dummysh; - } - ssdissolve(oldbc); - } - spivot(oldca, cacasout); - sspivot(oldca, ca); - if (ca.sh != dummysh) { - if (oldca.sh != cacasout.sh) { - // 'oldca' is not self-bonded. - spinsh = cacasout; - do { - cacasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldca.sh); - } else { - cacasout.sh = dummysh; - } - ssdissolve(oldca); - } - // Create two new subfaces. - makeshellface(subfaces, &bcv); - makeshellface(subfaces, &cav); - - // Set the vertices of changed and new subfaces. - abv = abc; // Update 'abc' to 'abv'. - setsapex(abv, newpoint); - setsorg(bcv, pb); // Set 'bcv'. - setsdest(bcv, pc); - setsapex(bcv, newpoint); - setsorg(cav, pc); // Set 'cav'. - setsdest(cav, pa); - setsapex(cav, newpoint); - if (b->quality && varconstraint) { - // Copy yhr area bound into the new subfaces. - setareabound(bcv, areabound(abv)); - setareabound(cav, areabound(abv)); - } - // Copy the boundary mark into the new subfaces. - setshellmark(bcv, shellmark(abv)); - setshellmark(cav, shellmark(abv)); - // Copy the subface type into the new subfaces. - setshelltype(bcv, shelltype(abv)); - setshelltype(cav, shelltype(abv)); - if (checkpbcs) { - // Copy the pbcgroup into the new subfaces. - setshellpbcgroup(bcv, shellpbcgroup(abv)); - setshellpbcgroup(cav, shellpbcgroup(abv)); - } - // Bond the new subfaces to the surrounding subfaces. - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - sbond1(bccasin, bcv); - sbond1(bcv, bccasout); - } else { - // Bond 'bcv' to itsself. - sbond(bcv, bcv); - } - ssbond(bcv, bc); - } else { - sbond(bcv, bccasout); - } - if (ca.sh != dummysh) { - if (cacasout.sh != dummysh) { - sbond1(cacasin, cav); - sbond1(cav, cacasout); - } else { - // Bond 'cav' to itself. - sbond(cav, cav); - } - ssbond(cav, ca); - } else { - sbond(cav, cacasout); - } - senext2self(bcv); - sbond(bcv, oldbc); - senextself(cav); - sbond(cav, oldca); - senext2self(bcv); - senextself(cav); - sbond(bcv, cav); - - // Bond the new subfaces to the new tetrahedra if they exist. - stpivot(abv, abvd); - if (abvd.tet != dummytet) { - // Get two new tetrahedra and their syms. - findedge(&abvd, sorg(abv), sdest(abv)); - enextfnext(abvd, bcvd); -#ifdef SELF_CHECK - assert(bcvd.tet != dummytet); -#endif - fnextself(bcvd); - enext2fnext(abvd, cavd); -#ifdef SELF_CHECK - assert(cavd.tet != dummytet); -#endif - fnextself(cavd); - // Bond two new subfaces to the two new tetrahedra. - tsbond(bcvd, bcv); - tsbond(cavd, cav); - } - // Set the connection at the other sides if the tetrahedra exist. - sesymself(abv); // bav - stpivot(abv, bave); - if (bave.tet != dummytet) { - sesymself(bcv); // cbv - sesymself(cav); // acv - // Get two new tetrahedra and their syms. - findedge(&bave, sorg(abv), sdest(abv)); - enextfnext(bave, acve); -#ifdef SELF_CHECK - assert(acve.tet != dummytet); -#endif - fnextself(acve); - enext2fnext(bave, cbve); -#ifdef SELF_CHECK - assert(cbve.tet != dummytet); -#endif - fnextself(cbve); - // Bond two new subfaces to the two new tetrahedra. - tsbond(acve, cav); - tsbond(cbve, bcv); - } - - bcv.shver = 0; - cav.shver = 0; - if (b->verbose > 3) { - printf(" Updating abv "); - printsh(&abv); - printf(" Creating bcv "); - printsh(&bcv); - printf(" Creating cav "); - printsh(&cav); - } - - if (flipqueue != (queue *) NULL) { - enqueueflipedge(abv, flipqueue); - enqueueflipedge(bcv, flipqueue); - enqueueflipedge(cav, flipqueue); - } - - // Set the return handle be abv. - *splitface = abv; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unsplitsubface() Reverse the operation of inserting a point on a // -// subface, so as to remove the newly inserted point. // -// // -// Assume the original subface is abc, it was split by a point v into three // -// subfaces abv, bcv and cav. 'splitsh' represents abv. // -// // -// To remove point v is to expand abv to abc, delete bcv and cav. If edge bc // -// or ca is a subsegment, the connection at a subsegment is a subface link, // -// '-casin' and '-casout' are used to save the predecessor and successor of // -// bcv or cav. On completion, point v is not deleted in this routine. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unsplitsubface(face* splitsh) -{ - face abv, bcv, cav; - face oldbv, oldva, bc, ca, spinsh; - face bccasin, bccasout, cacasin, cacasout; - - abv = *splitsh; - senext(abv, oldbv); - spivot(oldbv, bcv); - if (sorg(bcv) != sdest(oldbv)) { - sesymself(bcv); - } - senextself(bcv); - senext2(abv, oldva); - spivot(oldva, cav); - if (sorg(cav) != sdest(oldva)) { - sesymself(cav); - } - senext2self(cav); - - if (b->verbose > 1) { - printf(" Removing point %d on subface (%d, %d, %d).\n", - pointmark(sapex(abv)), pointmark(sorg(abv)), pointmark(sdest(abv)), - pointmark(sdest(bcv))); - } - - spivot(bcv, bccasout); - sspivot(bcv, bc); - if (bc.sh != dummysh) { - if (bcv.sh != bccasout.sh) { - // 'bcv' is not self-bonded. - spinsh = bccasout; - do { - bccasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != bcv.sh); - } else { - bccasout.sh = dummysh; - } - } - spivot(cav, cacasout); - sspivot(cav, ca); - if (ca.sh != dummysh) { - if (cav.sh != cacasout.sh) { - // 'cav' is not self-bonded. - spinsh = cacasout; - do { - cacasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != cav.sh); - } else { - cacasout.sh = dummysh; - } - } - - // Expand abv to abc. - setsapex(abv, sdest(bcv)); - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - sbond1(bccasin, oldbv); - sbond1(oldbv, bccasout); - } else { - // Bond 'oldbv' to itself. - sbond(oldbv, oldbv); - } - ssbond(oldbv, bc); - } else { - sbond(oldbv, bccasout); - } - if (ca.sh != dummysh) { - if (cacasout.sh != dummysh) { - sbond1(cacasin, oldva); - sbond1(oldva, cacasout); - } else { - // Bond 'oldva' to itself. - sbond(oldva, oldva); - } - ssbond(oldva, ca); - } else { - sbond(oldva, cacasout); - } - - // Delete two split-out subfaces. - shellfacedealloc(subfaces, bcv.sh); - shellfacedealloc(subfaces, cav.sh); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// splittetedge() Insert a point on an edge of the mesh. // -// // -// The edge is given by 'splittet'. Assume its four corners are a, b, n1 and // -// n2, where ab is the edge will be split. Around ab may exist any number of // -// tetrahedra. For convenience, they're ordered in a sequence following the // -// right-hand rule with your thumb points from a to b. Let the vertex set of // -// these tetrahedra be {a, b, n1, n2, ..., n(i)}. NOTE the tetrahedra around // -// ab may not connect to each other (can only happen when ab is a subsegment,// -// hence some faces abn(i) are subfaces). If ab is a subsegment, abn1 must // -// be a subface. // -// // -// To split edge ab by a point v is to split all tetrahedra containing ab by // -// v. More specifically, for each such tetrahedron, an1n2b, it is shrunk to // -// an1n2v, and a new tetrahedra bn2n1v is created. If ab is a subsegment, or // -// some faces of the splitting tetrahedra are subfaces, they must be split // -// either by calling routine 'splitsubedge()'. // -// // -// On completion, 'splittet' returns avn1n2. If 'flipqueue' is not NULL, it // -// returns all faces which may become non-Delaunay after this operation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::splittetedge(point newpoint, triface* splittet, - queue* flipqueue) -{ - triface *bots, *newtops; - triface oldtop, topcasing; - triface spintet, tmpbond0, tmpbond1; - face abseg, splitsh, topsh, spinsh; - triface worktet; - face n1n2seg, n2vseg, n1vseg; - point pa, pb, n1, n2; - REAL attrib, volume; - int wrapcount, hitbdry; - int i, j; - - if (checksubfaces) { - // Is there a subsegment need to be split together? - tsspivot(splittet, &abseg); - if (abseg.sh != dummysh) { - abseg.shver = 0; - // Orient the edge direction of 'splittet' be abseg. - if (org(*splittet) != sorg(abseg)) { - esymself(*splittet); - } - } - } - spintet = *splittet; - pa = org(spintet); - pb = dest(spintet); - - if (b->verbose > 1) { - printf(" Inserting point %d on edge (%d, %d).\n", - pointmark(newpoint), pointmark(pa), pointmark(pb)); - } - - // Collect the tetrahedra containing the splitting edge (ab). - n1 = apex(spintet); - hitbdry = 0; - wrapcount = 1; - if (checksubfaces && abseg.sh != dummysh) { - // It may happen that some tetrahedra containing ab (a subsegment) are - // completely disconnected with others. If it happens, use the face - // link of ab to cross the boundary. - while (true) { - if (!fnextself(spintet)) { - // Meet a boundary, walk through it. - hitbdry ++; - tspivot(spintet, spinsh); -#ifdef SELF_CHECK - assert(spinsh.sh != dummysh); -#endif - findedge(&spinsh, pa, pb); - sfnextself(spinsh); - stpivot(spinsh, spintet); -#ifdef SELF_CHECK - assert(spintet.tet != dummytet); -#endif - findedge(&spintet, pa, pb); - // Remember this position (hull face) in 'splittet'. - *splittet = spintet; - // Split two hull faces increase the hull size; - hullsize += 2; - } - if (apex(spintet) == n1) break; - wrapcount ++; - } - if (hitbdry > 0) { - wrapcount -= hitbdry; - } - } else { - // All the tetrahedra containing ab are connected together. If there - // are subfaces, 'splitsh' keeps one of them. - splitsh.sh = dummysh; - while (hitbdry < 2) { - if (checksubfaces && splitsh.sh == dummysh) { - tspivot(spintet, splitsh); - } - if (fnextself(spintet)) { - if (apex(spintet) == n1) break; - wrapcount++; - } else { - hitbdry ++; - if (hitbdry < 2) { - esym(*splittet, spintet); - } - } - } - if (hitbdry > 0) { - // ab is on the hull. - wrapcount -= 1; - // 'spintet' now is a hull face, inverse its edge direction. - esym(spintet, *splittet); - // Split two hull faces increases the number of hull faces. - hullsize += 2; - } - } - - // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra. - bots = new triface[wrapcount]; - newtops = new triface[wrapcount]; - // Spin around ab, gather tetrahedra and set up new tetrahedra. - spintet = *splittet; - for (i = 0; i < wrapcount; i++) { - // Get 'bots[i] = an1n2b'. - enext2fnext(spintet, bots[i]); - esymself(bots[i]); - // Create 'newtops[i]'. - maketetrahedron(&(newtops[i])); - // Go to the next. - fnextself(spintet); - if (checksubfaces && abseg.sh != dummysh) { - if (!issymexist(&spintet)) { - // We meet a hull face, walk through it. - tspivot(spintet, spinsh); -#ifdef SELF_CHECK - assert(spinsh.sh != dummysh); -#endif - findedge(&spinsh, pa, pb); - sfnextself(spinsh); - stpivot(spinsh, spintet); -#ifdef SELF_CHECK - assert(spintet.tet != dummytet); -#endif - findedge(&spintet, pa, pb); - } - } - } - - // Set the vertices of updated and new tetrahedra. - for (i = 0; i < wrapcount; i++) { - // Update 'bots[i] = an1n2v'. - setoppo(bots[i], newpoint); - // Set 'newtops[i] = bn2n1v'. - n1 = dest(bots[i]); - n2 = apex(bots[i]); - // Set 'newtops[i]'. - setorg(newtops[i], pb); - setdest(newtops[i], n2); - setapex(newtops[i], n1); - setoppo(newtops[i], newpoint); - // Set the element attributes of a new tetrahedron. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(bots[i].tet, j); - setelemattribute(newtops[i].tet, j, attrib); - } - if (b->varvolume) { - // Set the area constraint of a new tetrahedron. - volume = volumebound(bots[i].tet); - setvolumebound(newtops[i].tet, volume); - } -#ifdef SELF_CHECK - // Make sure no inversed tetrahedron has been created. - // volume = orient3d(pa, n1, n2, newpoint); - // if (volume >= 0.0) { - // printf("Internal error in splittetedge(): volume = %.12g.\n", volume); - // } - // volume = orient3d(pb, n2, n1, newpoint); - // if (volume >= 0.0) { - // printf("Internal error in splittetedge(): volume = %.12g.\n", volume); - // } -#endif - } - - // Bond newtops to topcasings and bots. - for (i = 0; i < wrapcount; i++) { - // Get 'oldtop = n1n2va' from 'bots[i]'. - enextfnext(bots[i], oldtop); - sym(oldtop, topcasing); - bond(newtops[i], topcasing); - if (checksubfaces) { - tspivot(oldtop, topsh); - if (topsh.sh != dummysh) { - tsdissolve(oldtop); - tsbond(newtops[i], topsh); - } - } - enextfnext(newtops[i], tmpbond0); - bond(oldtop, tmpbond0); - } - // Bond between newtops. - fnext(newtops[0], tmpbond0); - enext2fnext(bots[0], spintet); - for (i = 1; i < wrapcount; i ++) { - if (issymexist(&spintet)) { - enext2fnext(newtops[i], tmpbond1); - bond(tmpbond0, tmpbond1); - } - fnext(newtops[i], tmpbond0); - enext2fnext(bots[i], spintet); - } - // Bond the last to the first if no boundary. - if (issymexist(&spintet)) { - enext2fnext(newtops[0], tmpbond1); - bond(tmpbond0, tmpbond1); - } - if (checksubsegs) { - for (i = 0; i < wrapcount; i++) { - enextfnext(bots[i], worktet); // edge n1->n2. - tsspivot1(worktet, n1n2seg); - if (n1n2seg.sh != dummysh) { - enext(newtops[i], tmpbond0); - tssbond1(tmpbond0, n1n2seg); - } - enextself(worktet); // edge n2->v ==> n2->b - tsspivot1(worktet, n2vseg); - if (n2vseg.sh != dummysh) { - tssdissolve1(worktet); - tssbond1(newtops[i], n2vseg); - } - enextself(worktet); // edge v->n1 ==> b->n1 - tsspivot1(worktet, n1vseg); - if (n1vseg.sh != dummysh) { - tssdissolve1(worktet); - enext2(newtops[i], tmpbond0); - tssbond1(tmpbond0, n1vseg); - } - } - } - - // Is there exist subfaces and subsegment need to be split? - if (checksubfaces) { - if (abseg.sh != dummysh) { - // A subsegment needs be split. - spivot(abseg, splitsh); -#ifdef SELF_CHECK - assert(splitsh.sh != dummysh); -#endif - } - if (splitsh.sh != dummysh) { - // Split subfaces (and subsegment). - findedge(&splitsh, pa, pb); - splitsubedge(newpoint, &splitsh, (queue *) NULL); - } - } - - if (b->verbose > 3) { - for (i = 0; i < wrapcount; i++) { - printf(" Updating bots[%i] ", i); - printtet(&(bots[i])); - printf(" Creating newtops[%i] ", i); - printtet(&(newtops[i])); - } - } - - if (flipqueue != (queue *) NULL) { - for (i = 0; i < wrapcount; i++) { - enqueueflipface(bots[i], flipqueue); - enqueueflipface(newtops[i], flipqueue); - } - } - - // Set the return handle be avn1n2. It is got by transforming from - // 'bots[0]' (which is an1n2v). - fnext(bots[0], spintet); // spintet is an1vn2. - esymself(spintet); // spintet is n1avn2. - enextself(spintet); // spintet is avn1n2. - *splittet = spintet; - - delete [] bots; - delete [] newtops; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unsplittetedge() Reverse the operation of splitting an edge, so as to // -// remove the newly inserted point. // -// // -// Assume the original edge is ab, the tetrahedron containing ab is abn1n2. // -// After ab was split by a point v, every tetrahedron containing ab (e.g., // -// abn1n2) has been split into two (e.g., an1n2v and bn2n1v). 'splittet' // -// represents avn1n2 (i.e., its destination is v). // -// // -// To remove point v is to expand each split tetrahedron containing ab (e.g.,// -// (avn1n2 to abn1n2), then delete the redundant one(e.g., vbn1n2). If there // -// exists any subface around ab, routine unsplitsubedge() will be called to // -// reverse the operation of splitting a edge (or a subsegment) of subfaces. // -// On completion, point v is not deleted in this routine. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unsplittetedge(triface* splittet) -{ - triface *bots, *newtops; - triface oldtop, topcasing; - triface spintet; - face avseg, splitsh, topsh, spinsh; - point pa, pv, n1; - int wrapcount, hitbdry; - int i; - - spintet = *splittet; - pa = org(spintet); - pv = dest(spintet); - if (checksubfaces) { - // Is there a subsegment need to be unsplit together? - tsspivot(splittet, &avseg); - if (avseg.sh != dummysh) { - // The subsegment's direction should conform to 'splittet'. - if (sorg(avseg) != pa) { - sesymself(avseg); - } - } - } - - n1 = apex(spintet); - hitbdry = 0; - wrapcount = 1; - if (checksubfaces && avseg.sh != dummysh) { - // It may happen that some tetrahedra containing ab (a subsegment) are - // completely disconnected with others. If it happens, use the face - // link of ab to cross the boundary. - while (true) { - if (!fnextself(spintet)) { - // Meet a boundary, walk through it. - hitbdry ++; - tspivot(spintet, spinsh); -#ifdef SELF_CHECK - assert(spinsh.sh != dummysh); -#endif - findedge(&spinsh, pa, pv); - sfnextself(spinsh); - stpivot(spinsh, spintet); -#ifdef SELF_CHECK - assert(spintet.tet != dummytet); -#endif - findedge(&spintet, pa, pv); - // Remember this position (hull face) in 'splittet'. - *splittet = spintet; - // Split two hull faces increase the hull size; - hullsize += 2; - } - if (apex(spintet) == n1) break; - wrapcount ++; - } - if (hitbdry > 0) { - wrapcount -= hitbdry; - } - } else { - // All the tetrahedra containing ab are connected together. If there - // are subfaces, 'splitsh' keeps one of them. - splitsh.sh = dummysh; - while (hitbdry < 2) { - if (checksubfaces && splitsh.sh == dummysh) { - tspivot(spintet, splitsh); - } - if (fnextself(spintet)) { - if (apex(spintet) == n1) break; - wrapcount++; - } else { - hitbdry ++; - if (hitbdry < 2) { - esym(*splittet, spintet); - } - } - } - if (hitbdry > 0) { - // ab is on the hull. - wrapcount -= 1; - // 'spintet' now is a hull face, inverse its edge direction. - esym(spintet, *splittet); - // Split two hull faces increases the number of hull faces. - hullsize += 2; - } - } - - // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra. - bots = new triface[wrapcount]; - newtops = new triface[wrapcount]; - // Spin around av, gather tetrahedra and set up new tetrahedra. - spintet = *splittet; - for (i = 0; i < wrapcount; i++) { - // Get 'bots[i] = an1n2v'. - enext2fnext(spintet, bots[i]); - esymself(bots[i]); - // Get 'oldtop = n1n2va'. - enextfnext(bots[i], oldtop); - // Get 'newtops[i] = 'bn1n2v' - fnext(oldtop, newtops[i]); // newtop = n1n2bv - esymself(newtops[i]); // newtop = n2n1bv - enext2self(newtops[i]); // newtop = bn2n1v - // Go to the next. - fnextself(spintet); - if (checksubfaces && avseg.sh != dummysh) { - if (!issymexist(&spintet)) { - // We meet a hull face, walk through it. - tspivot(spintet, spinsh); -#ifdef SELF_CHECK - assert(spinsh.sh != dummysh); -#endif - findedge(&spinsh, pa, pv); - sfnextself(spinsh); - stpivot(spinsh, spintet); -#ifdef SELF_CHECK - assert(spintet.tet != dummytet); -#endif - findedge(&spintet, pa, pv); - } - } - } - - if (b->verbose > 1) { - printf(" Removing point %d from edge (%d, %d).\n", - pointmark(oppo(bots[0])), pointmark(org(bots[0])), - pointmark(org(newtops[0]))); - } - - for (i = 0; i < wrapcount; i++) { - // Expand an1n2v to an1n2b. - setoppo(bots[i], org(newtops[i])); - // Get 'oldtop = n1n2va' from 'bot[i]'. - enextfnext(bots[i], oldtop); - // Get 'topcasing' from 'newtop[i]' - sym(newtops[i], topcasing); - // Bond them. - bond(oldtop, topcasing); - if (checksubfaces) { - tspivot(newtops[i], topsh); - if (topsh.sh != dummysh) { - tsbond(oldtop, topsh); - } - } - // Delete the tetrahedron above an1n2v. - tetrahedrondealloc(newtops[i].tet); - } - - // If there exists any subface, unsplit them. - if (checksubfaces) { - if (avseg.sh != dummysh) { - spivot(avseg, splitsh); -#ifdef SELF_CHECK - assert(splitsh.sh != dummysh); -#endif - } - if (splitsh.sh != dummysh) { - findedge(&splitsh, pa, pv); - unsplitsubedge(&splitsh); - } - } - - delete [] bots; - delete [] newtops; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// splitsubedge() Insert a point on an edge of the surface mesh. // -// // -// The splitting edge is given by 'splitsh'. Assume its three corners are a, // -// b, c, where ab is the edge will be split. ab may be a subsegment. // -// // -// To split edge ab is to split all subfaces conatining ab. If ab is not a // -// subsegment, there are only two subfaces need be split, otherwise, there // -// may have any number of subfaces need be split. Each splitting subface abc // -// is shrunk to avc, a new subface vbc is created. It is important to keep // -// the orientations of edge rings of avc and vbc be the same as abc's. If ab // -// is a subsegment, it is shrunk to av and a new subsegment vb is created. // -// // -// If there are tetrahedra adjoining to the splitting subfaces, they should // -// be split before calling this routine, so the connection between the new // -// tetrahedra and the new subfaces can be correctly set. // -// // -// On completion, 'splitsh' returns avc. If 'flipqueue' is not NULL, it // -// returns all edges which may be non-Delaunay. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue) -{ - triface abcd, bace, vbcd, bvce; - face startabc, spinabc, spinsh; - face oldbc, bccasin, bccasout; - face ab, bc; - face avc, vbc, vbc1; - face av, vb; - point pa, pb; - - startabc = *splitsh; - // Is there a subsegment? - sspivot(startabc, ab); - if (ab.sh != dummysh) { - ab.shver = 0; - if (sorg(startabc) != sorg(ab)) { - sesymself(startabc); - } - } - pa = sorg(startabc); - pb = sdest(startabc); - - if (b->verbose > 1) { - printf(" Inserting point %d on subedge (%d, %d) %s.\n", - pointmark(newpoint), pointmark(pa), pointmark(pb), - (ab.sh != dummysh ? "(seg)" : " ")); - } - - // Spin arround ab, split every subface containing ab. - spinabc = startabc; - do { - // Adjust spinabc be edge ab. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - // Save old configuration at edge bc, if bc has a subsegment, save the - // face link of it and dissolve it from bc. - senext(spinabc, oldbc); - spivot(oldbc, bccasout); - sspivot(oldbc, bc); - if (bc.sh != dummysh) { - if (spinabc.sh != bccasout.sh) { - // 'spinabc' is not self-bonded. - spinsh = bccasout; - do { - bccasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != oldbc.sh); - } else { - bccasout.sh = dummysh; - } - ssdissolve(oldbc); - } - // Create a new subface. - makeshellface(subfaces, &vbc); - // Split abc. - avc = spinabc; // Update 'abc' to 'avc'. - setsdest(avc, newpoint); - // Make 'vbc' be in the same edge ring as 'avc'. - vbc.shver = avc.shver; - setsorg(vbc, newpoint); // Set 'vbc'. - setsdest(vbc, pb); - setsapex(vbc, sapex(avc)); - if (b->quality && varconstraint) { - // Copy the area bound into the new subface. - setareabound(vbc, areabound(avc)); - } - // Copy the shell marker and shell type into the new subface. - setshellmark(vbc, shellmark(avc)); - setshelltype(vbc, shelltype(avc)); - if (checkpbcs) { - // Copy the pbcgroup into the new subface. - setshellpbcgroup(vbc, shellpbcgroup(avc)); - } - // Set the connection between updated and new subfaces. - senext2self(vbc); - sbond(vbc, oldbc); - // Set the connection between new subface and casings. - senext2self(vbc); - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - // Insert 'vbc' into face link. - sbond1(bccasin, vbc); - sbond1(vbc, bccasout); - } else { - // Bond 'vbc' to itself. - sbond(vbc, vbc); - } - ssbond(vbc, bc); - } else { - sbond(vbc, bccasout); - } - // Go to next subface at edge ab. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - } while (spinabc.sh != startabc.sh); - - // Get the new subface vbc above the updated subface avc (= startabc). - senext(startabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } -#ifdef SELF_CHECK - assert(sorg(vbc) == sdest(oldbc) && sdest(vbc) == sorg(oldbc)); -#endif - senextself(vbc); - // Set the face link for the new created subfaces around edge vb. - spinabc = startabc; - do { - // Go to the next subface at edge av. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - // Get the new subface vbc1 above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc1); - if (sorg(vbc1) == newpoint) { - sesymself(vbc1); - } -#ifdef SELF_CHECK - assert(sorg(vbc1) == sdest(oldbc) && sdest(vbc1) == sorg(oldbc)); -#endif - senextself(vbc1); - // Set the connection: vbc->vbc1. - sbond1(vbc, vbc1); - // For the next connection. - vbc = vbc1; - } while (spinabc.sh != startabc.sh); - - // Split ab if it is a subsegment. - if (ab.sh != dummysh) { - // Update subsegment ab to av. - av = ab; - setsdest(av, newpoint); - // Create a new subsegment vb. - makeshellface(subsegs, &vb); - setsorg(vb, newpoint); - setsdest(vb, pb); - // vb gets the same mark and segment type as av. - setshellmark(vb, shellmark(av)); - setshelltype(vb, shelltype(av)); - if (b->quality && varconstraint) { - // Copy the area bound into the new subsegment. - setareabound(vb, areabound(av)); - } - // Save the old connection at ab (re-use the handles oldbc, bccasout). - senext(av, oldbc); - spivot(oldbc, bccasout); - // Bond av and vb (bonded at their "fake" edges). - senext2(vb, bccasin); - sbond(bccasin, oldbc); - if (bccasout.sh != dummysh) { - // There is a subsegment connecting with ab at b. It will connect - // to vb at b after splitting. - bccasout.shver = 0; - if (sorg(bccasout) != pb) sesymself(bccasout); -#ifdef SELF_CHECK - assert(sorg(bccasout) == pb); -#endif - senext2self(bccasout); - senext(vb, bccasin); - sbond(bccasin, bccasout); - } - // Bond all new subfaces (vbc) to vb. - spinabc = startabc; - do { - // Adjust spinabc be edge av. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - // Get new subface vbc above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } - senextself(vbc); - // Bond the new subface and the new subsegment. - ssbond(vbc, vb); - // Go to the next. - spivotself(spinabc); -#ifdef SELF_CHECK - assert(spinabc.sh != dummysh); -#endif - } while (spinabc.sh != startabc.sh); - } - - // Bond the new subfaces to new tetrahedra if they exist. New tetrahedra - // should have been created before calling this routine. - spinabc = startabc; - do { - // Adjust spinabc be edge av. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - // Get new subface vbc above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } - senextself(vbc); - // Get the adjacent tetrahedra at 'spinabc'. - stpivot(spinabc, abcd); - if (abcd.tet != dummytet) { - findedge(&abcd, sorg(spinabc), sdest(spinabc)); - enextfnext(abcd, vbcd); - fnextself(vbcd); -#ifdef SELF_CHECK - assert(vbcd.tet != dummytet); -#endif - tsbond(vbcd, vbc); - sym(vbcd, bvce); - sesymself(vbc); - tsbond(bvce, vbc); - } else { - // One side is empty, check the other side. - sesymself(spinabc); - stpivot(spinabc, bace); - if (bace.tet != dummytet) { - findedge(&bace, sorg(spinabc), sdest(spinabc)); - enext2fnext(bace, bvce); - fnextself(bvce); -#ifdef SELF_CHECK - assert(bvce.tet != dummytet); -#endif - sesymself(vbc); - tsbond(bvce, vbc); - } - } - // Go to the next. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - } while (spinabc.sh != startabc.sh); - - if (b->verbose > 3) { - spinabc = startabc; - do { - // Adjust spinabc be edge av. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - printf(" Updating abc:\n"); - printsh(&spinabc); - // Get new subface vbc above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } - senextself(vbc); - printf(" Creating vbc:\n"); - printsh(&vbc); - // Go to the next. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - } while (spinabc.sh != startabc.sh); - } - - if (flipqueue != (queue *) NULL) { - spinabc = startabc; - do { - // Adjust spinabc be edge av. - if (sorg(spinabc) != pa) { - sesymself(spinabc); - } - senext2(spinabc, oldbc); // Re-use oldbc. - enqueueflipedge(oldbc, flipqueue); - // Get new subface vbc above the updated subface avc (= spinabc). - senext(spinabc, oldbc); - spivot(oldbc, vbc); - if (sorg(vbc) == newpoint) { - sesymself(vbc); - } - senextself(vbc); - senext(vbc, oldbc); // Re-use oldbc. - enqueueflipedge(oldbc, flipqueue); - // Go to the next. - spivotself(spinabc); - if (spinabc.sh == dummysh) { - break; // 'ab' is a hull edge. - } - } while (spinabc.sh != startabc.sh); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unsplitsubedge() Reverse the operation of splitting an edge of subface,// -// so as to remove a point from the edge. // -// // -// Assume the original edge is ab, the subface containing it is abc. It was // -// split by a point v into avc, and vbc. 'splitsh' represents avc, further- // -// more, if av is a subsegment, av should be the zero version of the split // -// subsegment (i.e., av.shver = 0), so we are sure that the destination (v) // -// of both avc and av is the deleting point. // -// // -// To remove point v is to expand avc to abc, delete vbc, do the same for // -// other subfaces containing av and vb. If av and vb are subsegments, expand // -// av to ab, delete vb. On completion, point v is not deleted. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unsplitsubedge(face* splitsh) -{ - face startavc, spinavc, spinbcv; - face oldvc, bccasin, bccasout, spinsh; - face av, vb, bc; - point pa, pv, pb; - - startavc = *splitsh; - sspivot(startavc, av); - if (av.sh != dummysh) { - // Orient the direction of subsegment to conform the subface. - if (sorg(av) != sorg(startavc)) { - sesymself(av); - } -#ifdef SELF_CHECK - assert(av.shver == 0); -#endif - } - senext(startavc, oldvc); - spivot(oldvc, vb); // vb is subface vbc - if (sorg(vb) != sdest(oldvc)) { - sesymself(vb); - } - senextself(vb); - pa = sorg(startavc); - pv = sdest(startavc); - pb = sdest(vb); - - if (b->verbose > 1) { - printf(" Removing point %d from subedge (%d, %d).\n", - pointmark(pv), pointmark(pa), pointmark(pb)); - } - - // Spin arround av, unsplit every subface containing av. - spinavc = startavc; - do { - // Adjust spinavc be edge av. - if (sorg(spinavc) != pa) { - sesymself(spinavc); - } - // Save old configuration at edge bc, if bc has a subsegment, save the - // face link of it. - senext(spinavc, oldvc); - spivot(oldvc, spinbcv); - if (sorg(spinbcv) != sdest(oldvc)) { - sesymself(spinbcv); - } - senext2self(spinbcv); - spivot(spinbcv, bccasout); - sspivot(spinbcv, bc); - if (bc.sh != dummysh) { - if (spinbcv.sh != bccasout.sh) { - // 'spinbcv' is not self-bonded. - spinsh = bccasout; - do { - bccasin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != spinbcv.sh); - } else { - bccasout.sh = dummysh; - } - } - // Expand avc to abc. - setsdest(spinavc, pb); - if (bc.sh != dummysh) { - if (bccasout.sh != dummysh) { - sbond1(bccasin, oldvc); - sbond1(oldvc, bccasout); - } else { - // Bond 'oldbc' to itself. - sbond(oldvc, oldvc); - } - ssbond(oldvc, bc); - } else { - sbond(oldvc, bccasout); - } - // Delete bcv. - shellfacedealloc(subfaces, spinbcv.sh); - // Go to next subface at edge av. - spivotself(spinavc); - if (spinavc.sh == dummysh) { - break; // 'av' is a hull edge. - } - } while (spinavc.sh != startavc.sh); - - // Is there a subsegment need to be unsplit? - if (av.sh != dummysh) { - senext(av, oldvc); // Re-use oldvc. - spivot(oldvc, vb); - vb.shver = 0; -#ifdef SELF_CHECK - assert(sdest(av) == sorg(vb)); -#endif - senext(vb, spinbcv); // Re-use spinbcv. - spivot(spinbcv, bccasout); - // Expand av to ab. - setsdest(av, pb); - sbond(oldvc, bccasout); - // Delete vb. - shellfacedealloc(subsegs, vb.sh); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertsite() Insert a point into the mesh. // -// // -// The 'newpoint' is located. If 'searchtet->tet' is not NULL, the search // -// for the containing tetrahedron begins from 'searchtet', otherwise, a full // -// point location procedure is called. If 'newpoint' is found inside a // -// tetrahedron, the tetrahedron is split into four (by splittetrahedron()); // -// if 'newpoint' lies on a face, the face is split into three, thereby // -// splitting the two adjacent tetrahedra into six (by splittetface()); if // -// 'newpoint' lies on an edge, the edge is split into two, thereby, every // -// tetrahedron containing this edge is split into two. If 'newpoint' lies on // -// an existing vertex, no action is taken, and the value DUPLICATEPOINT is // -// returned and 'searchtet' is set to a handle whose origin is the vertex. // -// // -// If 'flipqueue' is not NULL, after 'newpoint' is inserted, it returns all // -// faces which may become non-Delaunay due to the newly inserted point. Flip // -// operations can be performed as necessary on them to maintain the Delaunay // -// property. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::insertsiteresult tetgenmesh::insertsite(point newpoint, - triface* searchtet, bool approx, queue* flipqueue) -{ - enum locateresult intersect, exactloc; - point checkpt; - REAL epspp, checklen; - int count; - - if (b->verbose > 1) { - printf(" Insert point to mesh: (%.12g, %.12g, %.12g) %d.\n", - newpoint[0], newpoint[1], newpoint[2], pointmark(newpoint)); - } - - if (searchtet->tet == (tetrahedron *) NULL) { - // Search for a tetrahedron containing 'newpoint'. - searchtet->tet = dummytet; - exactloc = locate(newpoint, searchtet); - } else { - // Start searching from the tetrahedron provided by the caller. - exactloc = preciselocate(newpoint, searchtet, tetrahedrons->items); - } - intersect = exactloc; - if (approx && (exactloc != ONVERTEX)) { - // Adjust the exact location to an approx. location wrt. epsilon. - epspp = b->epsilon; - count = 0; - while (count < 16) { - intersect = adjustlocate(newpoint, searchtet, exactloc, epspp); - if (intersect == ONVERTEX) { - checkpt = org(*searchtet); - checklen = distance(checkpt, newpoint); - if (checklen / longest > b->epsilon) { - epspp *= 1e-2; - count++; - continue; - } - } - break; - } - } - // Keep current search state for next searching. - recenttet = *searchtet; - - // Insert the point using the right routine - switch (intersect) { - case ONVERTEX: - // There's already a vertex there. Return in 'searchtet' a tetrahedron - // whose origin is the existing vertex. - if (b->verbose > 1) { - printf(" Not insert for duplicating point.\n"); - } - return DUPLICATEPOINT; - - case OUTSIDE: - if (b->verbose > 1) { - printf(" Not insert for locating outside the mesh.\n"); - } - return OUTSIDEPOINT; - - case ONEDGE: - // 'newpoint' falls on an edge. - splittetedge(newpoint, searchtet, flipqueue); - return SUCCESSONEDGE; - - case ONFACE: - // 'newpoint' falls on a face. - splittetface(newpoint, searchtet, flipqueue); - return SUCCESSONFACE; - - case INTETRAHEDRON: - // 'newpoint' falls inside a tetrahedron. - splittetrahedron(newpoint, searchtet, flipqueue); - return SUCCESSINTET; - - default: - // Impossible case. - return OUTSIDEPOINT; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// undosite() Undo the most recently point insertion. // -// // -// 'insresult' indicates in where the newpoint has been inserted, i.e., in a // -// tetrahedron, on a face, or on an edge. A correspoding routine will be // -// called to undo the point insertion. 'splittet' is a handle represent one // -// of the resulting tetrahedra, but it may be changed after transformation, // -// even may be dead. Four points 'torg', ... 'toppo' are the corners which // -// 'splittet' should have. On finish, 'newpoint' is not removed. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::undosite(enum insertsiteresult insresult, triface* splittet, - point torg, point tdest, point tapex, point toppo) -{ - // Set the four corners of 'splittet' exactly be 'torg', ... 'toppo'. - findface(splittet, torg, tdest, tapex); - if (oppo(*splittet) != toppo) { - symself(*splittet); -#ifdef SELF_CHECK - assert(oppo(*splittet) == toppo); -#endif - // The sym() operation may inverse the edge, correct it if so. - findedge(splittet, torg, tdest); - } - - // Unsplit the tetrahedron according to 'insresult'. - switch (insresult) { - case SUCCESSINTET: - // 'splittet' should be the face with 'newpoint' as its opposite. - unsplittetrahedron(splittet); - break; - case SUCCESSONFACE: - // 'splittet' should be the one of three splitted face with 'newpoint' - // as its apex. - unsplittetface(splittet); - break; - case SUCCESSONEDGE: - // 'splittet' should be the tet with destination is 'newpoint'. - unsplittetedge(splittet); - break; - default: // To omit compile warnings. - break; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// closeopenface() Close "open" faces recursively. // -// // -// This is the support routine of inserthullsite(). A point p which lies out-// -// side of CH(T). p is inserted to T by forming a tet t from p and a visible // -// CH face f. The three sides of f which have p as a vertex is called "open" // -// face. Each open face will be closed by either creating a tet on top of it // -// or become a new CH face. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::closeopenface(triface* openface, queue* flipque) -{ - triface newtet, oldhull; - triface newopenface, closeface; - point inspoint, pa, pb, pc; - REAL attrib, volume; - int i; - - // Get the new point p. - inspoint = apex(*openface); - // Find the old CH face f_o (f and f_o share the same edge). - esym(*openface, oldhull); - while (fnextself(oldhull)) ; - if (apex(oldhull) != inspoint) { - // Is f_o visible by p? - pa = org(oldhull); - pb = dest(oldhull); - pc = apex(oldhull); - if (orient3d(pa, pb, pc, inspoint) < 0.0) { - // Yes. Create a new tet t above f_o. - maketetrahedron(&newtet); - setorg(newtet, pa); - setdest(newtet, pb); - setapex(newtet, pc); - setoppo(newtet, inspoint); - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(oldhull.tet, i); - setelemattribute(newtet.tet, i, attrib); - } - if (b->varvolume) { - volume = volumebound(oldhull.tet); - setvolumebound(newtet.tet, volume); - } - // Connect t to T. - bond(newtet, oldhull); - // Close f. - fnext(newtet, newopenface); - bond(newopenface, *openface); - // f_o becomes an interior face. - enqueueflipface(oldhull, flipque); - // Hull face number decreases. - hullsize--; - // Two faces of t become open face. - enextself(newtet); - for (i = 0; i < 2; i++) { - fnext(newtet, newopenface); - sym(newopenface, closeface); - if (closeface.tet == dummytet) { - closeopenface(&newopenface, flipque); - } - enextself(newtet); - } - } else { - // Inivisible. f becomes a new CH face. - hullsize++; - // Let 'dummytet' holds f for the next point location. - dummytet[0] = encode(*openface); - } - } else { - // f_o is co-incident with f --> f is closed by f_o. - bond(*openface, oldhull); - // f is an interior face. - enqueueflipface(*openface, flipque); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// inserthullsite() Insert a point which lies outside the convex hull. // -// // -// The 'inspoint' p lies outside the tetrahedralization T. The 'horiz' f is // -// on the convex hull of T, CH(T), which is visible by p (Imagine f is para- // -// llel to the horizon). To insert p into T we have to enlarge the CH(T) and // -// update T so that p is on the new CH(T). // -// // -// To enlarge the CH(T). We need to find the set F of faces which are on CH // -// (T) and visible by p (F can be formed by a depth-first search from f). p // -// is then inserted into T by mounting new tets formed by p and these faces. // -// Faces of F become interior faces and may non-locally Delaunay. They are // -// queued in 'flipqueue' for flip tests. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::inserthullsite(point inspoint, triface* horiz, queue* flipque) -{ - triface firstnewtet; - triface openface, closeface; - REAL attrib, volume; - int i; - - // Let f face to p. - adjustedgering(*horiz, CW); - // Create the first tet t (from f and p). - maketetrahedron(&firstnewtet); - setorg (firstnewtet, org(*horiz)); - setdest(firstnewtet, dest(*horiz)); - setapex(firstnewtet, apex(*horiz)); - setoppo(firstnewtet, inspoint); - for (i = 0; i < in->numberoftetrahedronattributes; i++) { - attrib = elemattribute(horiz->tet, i); - setelemattribute(firstnewtet.tet, i, attrib); - } - if (b->varvolume) { - volume = volumebound(horiz->tet); - setvolumebound(firstnewtet.tet, volume); - } - // Connect t to T. - bond(firstnewtet, *horiz); - // f is not on CH(T) anymore. - enqueueflipface(*horiz, flipque); - // Hull face number decreases. - hullsize--; - - // Call the faces of t which have p as a vertex "open" face. - for (i = 0; i < 3; i++) { - // Get an open face f_i of t. - fnext(firstnewtet, openface); - // Close f_i if it is still open. - sym(openface, closeface); - if (closeface.tet == dummytet) { - closeopenface(&openface, flipque); - } - // Go to the next open face of t. - enextself(firstnewtet); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// Terminology: BC(p) and CBC(p), B(p) and C(p). // -// // -// Given an arbitrary point p, the Bowyer-Watson cavity BC(p) is formed by // -// tets whose circumspheres containing p. The outer faces of BC(p) form a // -// polyhedron B(p). // -// // -// If p is on a facet F, the constrained Bowyer-Watson cavity CBC(p) on F is // -// formed by subfaces of F whose circumspheres containing p. The outer edges // -// of CBC(p) form a polygon C(p). B(p) is separated into two parts by C(p), // -// denoted as B_1(p) and B_2(p), one of them may be empty (F is on the hull).// -// // -// If p is on a segment S which is shared by n facets. There exist n C(p)s, // -// each one is a non-closed polygon (without S). B(p) is split into n parts, // -// each of them is denoted as B_i(p), some B_i(p) may be empty. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// formbowatcavitysub() Form CBC(p) and C(p) on a facet F. // -// // -// Parameters: bp = p, bpseg = S, sublist = CBC(p), subceillist = C(p). // -// // -// CBC(p) contains at least one subface on input; S may be NULL which means // -// that p is inside a facet. On output, all subfaces of CBC(p) are infected, // -// and the edge rings are oriented to the same halfspace. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::formbowatcavitysub(point bp, face* bpseg, list* sublist, - list* subceillist) -{ - triface adjtet; - face startsh, neighsh; - face checkseg; - point pa, pb, pc, pd; - REAL sign; - int i, j; - - // Form CBC(p) and C(p) by a broadth-first searching. - for (i = 0; i < sublist->len(); i++) { - startsh = * (face *)(* sublist)[i]; // startsh = f. - // Look for three neighbors of f. - for (j = 0; j < 3; j++) { - sspivot(startsh, checkseg); - if (checkseg.sh == dummysh) { - // Get its neighbor n. - spivot(startsh, neighsh); - // Is n already in CBC(p)? - if (!sinfected(neighsh)) { - stpivot(neighsh, adjtet); - if (adjtet.tet == dummytet) { - sesymself(neighsh); - stpivot(neighsh, adjtet); - } - // For positive orientation that insphere() test requires. - adjustedgering(adjtet, CW); - pa = org(adjtet); - pb = dest(adjtet); - pc = apex(adjtet); - pd = oppo(adjtet); - sign = insphere(pa, pb, pc, pd, bp); - if (sign >= 0.0) { - // Orient edge ring of n according to that of f. - if (sorg(neighsh) != sdest(startsh)) sesymself(neighsh); - // Collect it into CBC(p). - sinfect(neighsh); - sublist->append(&neighsh); - } else { - subceillist->append(&startsh); // Found an edge of C(p). - } - } - } else { - // Do not cross a segment. - if (bpseg != (face *) NULL) { - if (checkseg.sh != bpseg->sh) { - subceillist->append(&startsh); // Found an edge of C(p). - } - } else { - subceillist->append(&startsh); // Found an edge of C(p). - } - } - senextself(startsh); - } - } - - if (b->verbose > 2) { - printf(" Collect CBC(%d): %d subfaces, %d edges.\n", pointmark(bp), - sublist->len(), subceillist->len()); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// formbowatcavityquad() Form BC_i(p) and B_i(p) in a quadrant. // -// // -// Parameters: bp = p, tetlist = BC_i(p), ceillist = B_i(p). // -// // -// BC_i(p) contains at least one tet on input. On finish, all tets collected // -// in BC_i(p) are infected. B_i(p) may not closed when p is on segment or in // -// facet. C(p) must be formed before this routine. Check the infect flag of // -// a subface to identify the unclosed side of B_i(p). These sides will be // -// closed by new subfaces of C(p)s. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::formbowatcavityquad(point bp, list* tetlist, list* ceillist) -{ - triface starttet, neightet; - face checksh; - point pa, pb, pc, pd; - REAL sign; - int i; - - // Form BC_i(p) and B_i(p) by a broadth-first searching. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { - // Try to collect the neighbor of the face (f). - tspivot(starttet, checksh); - if (checksh.sh == dummysh) { - // Get its neighbor n. - sym(starttet, neightet); - // Is n already in BC_i(p)? - if (!infected(neightet)) { - // For positive orientation that insphere() test requires. - adjustedgering(neightet, CW); - pa = org(neightet); - pb = dest(neightet); - pc = apex(neightet); - pd = oppo(neightet); - sign = insphere(pa, pb, pc, pd, bp); - if (sign >= 0.0) { - // Collect it into BC_i(p). - infect(neightet); - tetlist->append(&neightet); - } else { - ceillist->append(&starttet); // Found a face of B_i(p). - } - } - } else { - // Do not cross a boundary face. - if (!sinfected(checksh)) { - ceillist->append(&starttet); // Found a face of B_i(p). - } - } - } - } - - if (b->verbose > 2) { - printf(" Collect BC_i(%d): %d tets, %d faces.\n", pointmark(bp), - tetlist->len(), ceillist->len()); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// formbowatcavitysegquad() Form BC_i(p) and B_i(p) in a segment quadrant.// -// // -// Parameters: bp = p, tetlist = BC_i(p), ceillist = B_i(p). // -// // -// BC_i(p) contains at least one tet on input. On finish, all tets collected // -// in BC_i(p) are infected. B_i(p) is not closed. C(p) must be formed before // -// this routine. Check the infect flag of a subface to identify the unclosed // -// sides of B_i(p). These sides will be closed by new subfaces of C(p)s. // -// // -// During the repair of encroaching subsegments, there may exist locally non-// -// Delaunay faces. These faces are collected in BC_i(p) either. B_i(p) has // -// to be formed later than BC_i(p). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::formbowatcavitysegquad(point bp, list* tetlist,list* ceillist) -{ - triface starttet, neightet, cavtet; - face checksh; - point pa, pb, pc, pd, pe; - REAL sign; - int i; - - // Form BC_i(p) by a broadth-first searching. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { - // Try to collect the neighbor of the face f. - tspivot(starttet, checksh); - if (checksh.sh == dummysh) { - // Get its neighbor n. - sym(starttet, neightet); - // Is n already in BC_i(p)? - if (!infected(neightet)) { - // For positive orientation that insphere() test requires. - adjustedgering(neightet, CW); - pa = org(neightet); - pb = dest(neightet); - pc = apex(neightet); - pd = oppo(neightet); - sign = insphere(pa, pb, pc, pd, bp); - if (sign >= 0.0) { - // Collect it into BC_i(p). - infect(neightet); - tetlist->append(&neightet); - } else { - // Check if the face is locally non-Delaunay. - pe = oppo(starttet); - sign = insphere(pa, pb, pc, pd, pe); - if (sign >= 0.0) { - // Collect it into BC_i(p). - infect(neightet); - tetlist->append(&neightet); - } - } - } - } - } - } - - // Generate B_i(p). - for (i = 0; i < tetlist->len(); i++) { - cavtet = * (triface *)(* tetlist)[i]; - for (cavtet.loc = 0; cavtet.loc < 4; cavtet.loc++) { - tspivot(cavtet, checksh); - if (checksh.sh == dummysh) { - sym(cavtet, neightet); - if (!infected(neightet)) { - ceillist->append(&cavtet); // Found a face of B(p). - } - } else { - // Do not cross a boundary face. - if (!sinfected(checksh)) { - ceillist->append(&cavtet); // Found a face of B(p). - } - } - } - } - - if (b->verbose > 2) { - printf(" Collect BC_i(%d): %d tets, %d faces.\n", pointmark(bp), - tetlist->len(), ceillist->len()); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// formbowatcavity() Form BC(p), B(p), CBC(p)s, and C(p)s. // -// // -// If 'bpseg'(S) != NULL, p is on segment S, else, p is on facet containing // -// 'bpsh' (F). 'n' returns the number of quadrants in BC(p). 'nmax' is the // -// maximum pre-allocated array length for the lists. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::formbowatcavity(point bp, face* bpseg, face* bpsh, int* n, - int* nmax, list** sublists, list** subceillists, list** tetlists, - list** ceillists) -{ - list *sublist; - triface adjtet; - face startsh, spinsh; - point pa, pb; - int i, j; - - *n = 0; - if (bpseg != (face *) NULL) { - // p is on segment S. - bpseg->shver = 0; - pa = sorg(*bpseg); - pb = sdest(*bpseg); - // Count the number of facets sharing at S. - spivot(*bpseg, startsh); - spinsh = startsh; - do { - (*n)++; // spinshlist->append(&spinsh); - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - // *n is the number of quadrants around S. - if (*n > *nmax) { - // Reallocate arrays. Should not happen very often. - delete [] tetlists; - delete [] ceillists; - delete [] sublists; - delete [] subceillists; - tetlists = new list*[*n]; - ceillists = new list*[*n]; - sublists = new list*[*n]; - subceillists = new list*[*n]; - *nmax = *n; - } - // Form CBC(p)s and C(p)s. - spinsh = startsh; - for (i = 0; i < *n; i++) { - sublists[i] = new list(sizeof(face), NULL, 256); - subceillists[i] = new list(sizeof(face), NULL, 256); - // Set a subface f to start search. - startsh = spinsh; - // Let f face to the quadrant of interest (used in forming BC(p)). - findedge(&startsh, pa, pb); - sinfect(startsh); - sublists[i]->append(&startsh); - formbowatcavitysub(bp, bpseg, sublists[i], subceillists[i]); - // Go to the next facet. - spivotself(spinsh); - } - } else if (sublists != (list **) NULL) { - // p is on a facet. - *n = 2; - // Form CBC(p) and C(p). - sublists[0] = new list(sizeof(face), NULL, 256); - subceillists[0] = new list(sizeof(face), NULL, 256); - sinfect(*bpsh); - sublists[0]->append(bpsh); - formbowatcavitysub(bp, NULL, sublists[0], subceillists[0]); - } else { - // p is inside a tet. - *n = 1; - } - - // Form BC_i(p) and B_i(p). - for (i = 0; i < *n; i++) { - tetlists[i] = new list(sizeof(triface), NULL, 256); - ceillists[i] = new list(sizeof(triface), NULL, 256); - if (sublists != (list **) NULL) { - // There are C(p)s. - sublist = ((bpseg == (face *) NULL) ? sublists[0] : sublists[i]); - // Add all adjacent tets of C_i(p) into BC_i(p). - for (j = 0; j < sublist->len(); j++) { - startsh = * (face *)(* sublist)[j]; - // Adjust the side facing to the right quadrant for C(p). - if ((bpseg == (face *) NULL) && (i == 1)) sesymself(startsh); - stpivot(startsh, adjtet); - if (adjtet.tet != dummytet) { - if (!infected(adjtet)) { - infect(adjtet); - tetlists[i]->append(&adjtet); - } - } - } - if (bpseg != (face *) NULL) { - // The quadrant is bounded by another facet. - sublist = ((i < *n - 1) ? sublists[i + 1] : sublists[0]); - for (j = 0; j < sublist->len(); j++) { - startsh = * (face *)(* sublist)[j]; - // Adjust the side facing to the right quadrant for C(p). - sesymself(startsh); - stpivot(startsh, adjtet); - if (adjtet.tet != dummytet) { - if (!infected(adjtet)) { - infect(adjtet); - tetlists[i]->append(&adjtet); - } - } - } - } - } - // It is possible that BC_i(p) is empty. - if (tetlists[i]->len() == 0) continue; - // Collect the rest of tets of BC_i(p) and form B_i(p). - // if (b->conformdel) { - // formbowatcavitysegquad(bp, tetlists[i], ceillists[i]); - // } else { - formbowatcavityquad(bp, tetlists[i], ceillists[i]); - // } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// releasebowatcavity() Undo and free the memory allocated in routine // -// formbowatcavity(). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::releasebowatcavity(face* bpseg, int n, list** sublists, - list** subceillist, list** tetlists, list** ceillists) -{ - triface oldtet; - face oldsh; - int i, j; - - if (sublists != (list **) NULL) { - // Release CBC(p)s. - for (i = 0; i < n; i++) { - // Uninfect subfaces of CBC(p). - for (j = 0; j < sublists[i]->len(); j++) { - oldsh = * (face *)(* (sublists[i]))[j]; -#ifdef SELF_CHECK - assert(sinfected(oldsh)); -#endif - suninfect(oldsh); - } - delete sublists[i]; - delete subceillist[i]; - sublists[i] = (list *) NULL; - subceillist[i] = (list *) NULL; - if (bpseg == (face *) NULL) break; - } - } - // Release BC(p). - for (i = 0; i < n; i++) { - // Uninfect tets of BC_i(p). - for (j = 0; j < tetlists[i]->len(); j++) { - oldtet = * (triface *)(* (tetlists[i]))[j]; -#ifdef SELF_CHECK - assert(infected(oldtet)); -#endif - uninfect(oldtet); - } - delete tetlists[i]; - delete ceillists[i]; - tetlists[i] = (list *) NULL; - ceillists[i] = (list *) NULL; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// validatebowatcavityquad() Valid B_i(p). // -// // -// B_i(p) is valid if all faces of B_i(p) are visible by p, else B_i(p) is // -// invalid. Each tet of BC_i(p) which has such a face is marked (uninfect). // -// They will be removed in updatebowatcavityquad(). // -// // -// Return TRUE if B(p) is valid, else, return FALSE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::validatebowatcavityquad(point bp,list* ceillist,REAL maxcosd) -{ - triface ceiltet; - point pa, pb, pc; - REAL ori, cosd; - int remcount, i; - - // Check the validate of B(p), cut tets having invisible faces. - remcount = 0; - for (i = 0; i < ceillist->len(); i++) { - ceiltet = * (triface *)(* ceillist)[i]; - if (infected(ceiltet)) { - adjustedgering(ceiltet, CCW); - pa = org(ceiltet); - pb = dest(ceiltet); - pc = apex(ceiltet); - ori = orient3d(pa, pb, pc, bp); - if (ori >= 0.0) { - // Found an invisible face. - uninfect(ceiltet); - remcount++; - continue; - } - // If a non-trival 'maxcosd' is given. - if (maxcosd > -1.0) { - // Get the maximal dihedral angle of tet abcp. - tetalldihedral(pa, pb, pc, bp, NULL, &cosd, NULL); - // Do not form the tet if the maximal dihedral angle is not reduced. - if (cosd < maxcosd) { - uninfect(ceiltet); - remcount++; - } - } - } - } - return remcount == 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// updatebowatcavityquad() Update BC_i(p) and reform B_i(p). // -// // -// B_i(p) is invalid and some tets in BC_i(p) have been marked to be removed // -// in validatebowatcavityquad(). This routine actually remove the cut tets // -// of BC_i(p) and re-form the B_i(p). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::updatebowatcavityquad(list* tetlist, list* ceillist) -{ - triface cavtet, neightet; - face checksh; - int remcount, i; - - remcount = 0; - for (i = 0; i < tetlist->len(); i++) { - cavtet = * (triface *)(* tetlist)[i]; - if (!infected(cavtet)) { - tetlist->del(i, 1); - remcount++; - i--; - } - } - - // Are there tets have been cut in BC_i(p)? - if (remcount > 0) { - // Re-form B_i(p). - ceillist->clear(); - for (i = 0; i < tetlist->len(); i++) { - cavtet = * (triface *)(* tetlist)[i]; - for (cavtet.loc = 0; cavtet.loc < 4; cavtet.loc++) { - tspivot(cavtet, checksh); - if (checksh.sh == dummysh) { - sym(cavtet, neightet); - if (!infected(neightet)) { - ceillist->append(&cavtet); // Found a face of B_i(p). - } - } else { - // Do not cross a boundary face. - if (!sinfected(checksh)) { - ceillist->append(&cavtet); // Found a face of B_i(p). - } - } - } - } - if (b->verbose > 2) { - printf(" Update BC_i(p): %d tets, %d faces.\n", tetlist->len(), - ceillist->len()); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// updatebowatcavitysub() Check and update CBC(p) and C(p). // -// // -// A CBC(p) is valid if all its subfaces are inside or on the hull of BC(p). // -// A subface s of CBC(p) is invalid if it is in one of the two cases: // -// (1) s is completely outside BC(p); // -// (2) s has two adjacent tets but only one of them is in BC(p); // -// s is removed from CBC(p) if it is invalid. If there is an adjacent tet of // -// s which is in BC(p), it gets removed from BC(p) too. If CBC(p) is updated,// -// C(p) is re-formed. // -// // -// A C(p) is valid if all its edges are on the hull of BC(p). An edge e of // -// C(p) may be inside BC(p) if e is a segment and belongs to only one facet. // -// To correct C(p), a tet of BC(p) which shields e gets removed. // -// // -// If BC(p) is formed with locally non-Delaunay check (b->conformdel > 0). // -// A boundary-consistent check is needed for non-segment edges of C(p). Let // -// e be such an edge, the subface f contains e and outside C(p) may belong // -// to B(p) due to the non-coplanarity of the facet definition. The tet of // -// BC(p) containing f gets removed to avoid creating a degenerate new tet. // -// // -// 'cutcount' accumulates the total number of cuttets(not only by this call).// -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::updatebowatcavitysub(list* sublist, list* subceillist, - int* cutcount) -{ - triface adjtet, rotface; - face checksh, neighsh; - face checkseg; - point pa, pb, pc; - REAL ori1, ori2; - int remcount; - int i, j; - - remcount = 0; - // Check the validity of CBC(p). - for (i = 0; i < sublist->len(); i++) { - checksh = * (face *)(* sublist)[i]; - // Check two adjacent tets of s. - for (j = 0; j < 2; j++) { - stpivot(checksh, adjtet); - if (adjtet.tet != dummytet) { - if (!infected(adjtet)) { - // Could be either case (1) or (2). - suninfect(checksh); // s survives. - // If the sym. adjtet exists, it should remove from BC(p) too. - sesymself(checksh); - stpivot(checksh, adjtet); - if (adjtet.tet != dummytet) { - if (infected(adjtet)) { - // Found an adj. tet in BC(p), remove it. - uninfect(adjtet); - (*cutcount)++; - } - } - // Remove s from C(p). - sublist->del(i, 1); - i--; - remcount++; - break; - } - } - sesymself(checksh); - } - } - if (remcount > 0) { - if (b->verbose > 2) { - printf(" Removed %d subfaces from CBC(p).\n", remcount); - } - // Re-generate C(p). - subceillist->clear(); - for (i = 0; i < sublist->len(); i++) { - checksh = * (face *)(* sublist)[i]; - for (j = 0; j < 3; j++) { - spivot(checksh, neighsh); - if (!sinfected(neighsh)) { - subceillist->append(&checksh); - } - senextself(checksh); - } - } - if (b->verbose > 2) { - printf(" Update CBC(p): %d subs, %d edges.\n", sublist->len(), - subceillist->len()); - } - } - - // Check the validity of C(p). - for (i = 0; i < subceillist->len(); i++) { - checksh = * (face *)(* subceillist)[i]; - sspivot(checksh, checkseg); - if (checkseg.sh != dummysh) { - // A segment. Check if it is inside BC(p). - stpivot(checksh, adjtet); - if (adjtet.tet == dummytet) { - sesym(checksh, neighsh); - stpivot(neighsh, adjtet); - } - findedge(&adjtet, sorg(checkseg), sdest(checkseg)); - adjustedgering(adjtet, CCW); - fnext(adjtet, rotface); // It's the same tet. - // Rotate rotface (f), stop on either of the following cases: - // (a) meet a subface, or - // (b) enter an uninfected tet, or - // (c) rewind back to adjtet. - do { - if (!infected(rotface)) break; // case (b) - tspivot(rotface, neighsh); - if (neighsh.sh != dummysh) break; // case (a) - // Go to the next tet of the facing ring. - fnextself(rotface); - } while (apex(rotface) != apex(adjtet)); - // Is it case (c)? - if (apex(rotface) == apex(adjtet)) { - // The segment is enclosed by BC(p), invalid cavity. - pa = org(adjtet); - pb = dest(adjtet); - pc = apex(adjtet); - // Find the shield tet and cut it. Notice that the shield tet may - // not be unique when there are four coplanar points, ie., - // ori1 * ori2 == 0.0. In such case, choose either of them. - fnext(adjtet, rotface); - do { - fnextself(rotface); - assert(infected(rotface)); - ori1 = orient3d(pa, pb, pc, apex(rotface)); - ori2 = orient3d(pa, pb, pc, oppo(rotface)); - } while (ori1 * ori2 > 0.0); - // Cut this tet from BC(p). - uninfect(rotface); - (*cutcount)++; - } - } else { - /*// An edge. Check if boundary-consistency should be enforced. - if (b->conformdel > 0) { - // Get the adj-sub n at e, it must be outside C(p). - spivot(checksh, neighsh); - assert(!sinfected(neighsh)); - // Check if n is on B(p). - for (j = 0; j < 2; j++) { - stpivot(neighsh, adjtet); - if (adjtet.tet != dummytet) { - if (infected(adjtet)) { - uninfect(adjtet); - (*cutcount)++; - } - } - sesymself(neighsh); - } - } */ - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// trimbowatcavity() Validate B(p), CBC(p)s and C(p)s, update BC(p). // -// // -// A B(p) is valid if all its faces are visible by p. If a face f of B(p) is // -// found invisible by p, the tet of BC(p) containing f gets removed and B(p) // -// is refromed. The new B(p) may still contain invisible faces by p. Iterat- // -// ively do the above procedure until B(p) is satisfied. // -// // -// A CBC(p) is valid if each subface of CBC(p) is either on the hull of BC(p)// -// or completely inside BC(p). If a subface s of CBC(p) is not valid, it is // -// removed from CBC(p) and C(p) is reformed. If there exists a tet t of BC(p)// -// containg s, t is removed from BC(p). The process for validating BC(p) and // -// B(p) is re-excuted. // -// // -// A C(p) is valid if each edge of C(p) is on the hull of BC(p). If an edge // -// e of C(p) is invalid (e should be a subsegment which only belong to one // -// facet), a tet of BC(p) which contains e and has two other faces shielding // -// e is removed. The process for validating BC(p) and B(p) is re-excuted. // -// // -// If either BC(p) or CBC(p) becomes empty. No valid BC(p) is found, return // -// FALSE. else, return TRUE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::trimbowatcavity(point bp, face* bpseg, int n, list** sublists, - list** subceillists, list** tetlists, list** ceillists, REAL maxcosd) -{ - bool valflag; - int oldnum, cutnum, cutcount; - int i; - - cutnum = 0; // Count the total number of cut-off tets of BC(p). - valflag = true; - - do { - // Validate BC(p), B(p). - for (i = 0; i < n && valflag; i++) { - oldnum = tetlists[i]->len(); - // Iteratively validate BC_i(p) and B_i(p). - while (!validatebowatcavityquad(bp, ceillists[i], maxcosd)) { - // Update BC_i(p) and B_i(p). - updatebowatcavityquad(tetlists[i], ceillists[i]); - valflag = tetlists[i]->len() > 0; - } - cutnum += (oldnum - tetlists[i]->len()); - } - if (valflag && (sublists != (list **) NULL)) { - // Validate CBC(p), C(p). - cutcount = 0; - for (i = 0; i < n; i++) { - updatebowatcavitysub(sublists[i], subceillists[i], &cutcount); - // Only do once if p is on a facet. - if (bpseg == (face *) NULL) break; - } - // Are there cut tets? - if (cutcount > 0) { - // Squeeze all cut tets in BC(p), keep valflag once it gets FLASE. - for (i = 0; i < n; i++) { - if (tetlists[i]->len() > 0) { - updatebowatcavityquad(tetlists[i], ceillists[i]); - if (valflag) { - valflag = tetlists[i]->len() > 0; - } - } - } - cutnum += cutcount; - // Go back to valid the updated BC(p). - continue; - } - } - break; // Leave the while-loop. - } while (true); - - // Check if any CBC(p) becomes non-empty. - if (valflag && (sublists != (list **) NULL)) { - for (i = 0; i < n && valflag; i++) { - valflag = (sublists[i]->len() > 0); - if (bpseg == (face *) NULL) break; - } - } - - if (valflag && (cutnum > 0)) { - // Accumulate counters. - if (bpseg != (face *) NULL) { - updsegcount++; - } else if (sublists != (list **) NULL) { - updsubcount++; - } else { - updvolcount++; - } - } - - if (!valflag) { - // Accumulate counters. - if (bpseg != (face *) NULL) { - failsegcount++; - } else if (sublists != (list **) NULL) { - failsubcount++; - } else { - failvolcount++; - } - } - - return valflag; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// bowatinsertsite() Insert a point using the Bowyer-Watson method. // -// // -// Parameters: 'bp' = p, 'splitseg' = S, 'n' = the number of quadrants, // -// 'sublists', an array of CBC_i(p)s, 'subceillists', an array of C_i(p)s, // -// 'tetlists', an array of BC_i(p)s, 'ceillists', an array of B_i(p)s. // -// // -// If p is inside the mesh domain, then S = NULL, n = 1, CBC(p) and C(p) are // -// NULLs. 'tetlists[0]' = BC(p), 'ceillists[0]' = B(p). // -// If p is on a facet F, then S = NULL, n = 2, and 'subceillists[0]' = C(p), // -// 'subceillists[1]' is not needed (set it to NULL). B_1(p) and B_2(p) are // -// in 'ceillists[0]' and 'ceillists[1]'. // -// If p is on a segment S, then F(S) is a list of subfaces around S, and n = // -// len(F(S)), there are n C_i(p)s and B_i(p)s supplied in 'subceillists[i]'// -// and 'ceillists[i]'. // -// // -// If 'verlist' != NULL, it returns a list of vertices which connect to p. // -// This vertices are used for interpolating size of p. // -// // -// If 'flipque' != NULL, it returns a list of internal faces of new tets in // -// BC(p), faces on C(p)s are excluded. These faces may be locally non- // -// Delaunay and will be flipped if they are flippable. Such non-Delaunay // -// faces may exist when p is inserted to split an encroaching segment. // -// // -// 'chkencseg', 'chkencsub', and 'chkbadtet' are flags that indicate whether // -// or not there should be checks for the creation of encroached subsegments, // -// subfaces, or bad quality tets. If 'chkencseg' = TRUE, the encroached sub- // -// segments are added to the list of subsegments to be split. // -// // -// On return, 'ceillists' returns Star(p). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::bowatinsertsite(point bp,face* splitseg,int n,list** sublists, - list** subceillists, list** tetlists, list** ceillists, list* verlist, - queue* flipque, bool chkencseg, bool chkencsub, bool chkbadtet) -{ - list *ceillist, *subceillist; - triface oldtet, newtet, newface, rotface, neightet; - face oldsh, newsh, newedge, checksh; - face spinsh, casingin, casingout; - face *apsegshs, *pbsegshs; - face apseg, pbseg, checkseg; - point pa, pb, pc; - REAL attrib, volume; - int idx, i, j, k; - - if (b->verbose > 1) { - printf(" Insert point %d (%.12g, %.12g, %.12g)", pointmark(bp), bp[0], - bp[1], bp[2]); - } - if (splitseg != (face *) NULL) { - if (b->verbose > 1) { - printf(" on segment.\n"); - } - bowatsegcount++; - } else { - if (subceillists != (list **) NULL) { - if (b->verbose > 1) { - printf(" on facet.\n"); - } - bowatsubcount++; - } else { - if (b->verbose > 1) { - printf(" in volume.\n"); - } - bowatvolcount++; - } - } - - // Create new tets to fill B(p). - for (k = 0; k < n; k++) { - // Create new tets from each B_i(p). - ceillist = ceillists[k]; - for (i = 0; i < ceillist->len(); i++) { - oldtet = * (triface *)(* ceillist)[i]; - adjustedgering(oldtet, CCW); - pa = org(oldtet); - pb = dest(oldtet); - pc = apex(oldtet); - maketetrahedron(&newtet); - setorg(newtet, pa); - setdest(newtet, pb); - setapex(newtet, pc); - setoppo(newtet, bp); - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(oldtet.tet, j); - setelemattribute(newtet.tet, j, attrib); - } - if (b->varvolume) { - volume = volumebound(oldtet.tet); - if (volume > 0.0) { - if (!b->fixedvolume && b->refine) { - // '-r -a' switches and a .vol file case. Enlarge the maximum - // volume constraint for the new tets. Hence the new points - // only spread near the original constrained tet. - volume *= 1.2; - } - } - setvolumebound(newtet.tet, volume); - } - sym(oldtet, neightet); - tspivot(oldtet, checksh); - if (neightet.tet != dummytet) { - bond(newtet, neightet); - } - if (checksh.sh != dummysh) { - tsbond(newtet, checksh); - } - if (verlist != (list *) NULL) { - // Collect vertices connecting to p. - idx = pointmark(pa); - if (idx >= 0) { - setpointmark(pa, -idx - 1); - verlist->append(&pa); - } - idx = pointmark(pb); - if (idx >= 0) { - setpointmark(pb, -idx - 1); - verlist->append(&pb); - } - idx = pointmark(pc); - if (idx >= 0) { - setpointmark(pc, -idx - 1); - verlist->append(&pc); - } - } - // Replace the tet by the newtet for checking the quality. - * (triface *)(* ceillist)[i] = newtet; - } - } - if (verlist != (list *) NULL) { - // Uninfect collected vertices. - for (i = 0; i < verlist->len(); i++) { - pa = * (point *)(* verlist)[i]; - idx = pointmark(pa); - setpointmark(pa, -(idx + 1)); - } - } - - // Connect new tets of B(p). Not all faces of new tets can be connected, - // e.g., if there are empty B_i(p)s. - for (k = 0; k < n; k++) { - ceillist = ceillists[k]; - for (i = 0; i < ceillist->len(); i++) { - newtet = * (triface *)(* ceillist)[i]; - newtet.ver = 0; - for (j = 0; j < 3; j++) { - fnext(newtet, newface); - sym(newface, neightet); - if (neightet.tet == dummytet) { - // Find the neighbor face by rotating the faces at edge ab. - esym(newtet, rotface); - pa = org(rotface); - pb = dest(rotface); - while (fnextself(rotface)); - // Do we meet a boundary face? - tspivot(rotface, checksh); - if (checksh.sh != dummysh) { - // Walk through the boundary and continue to rotate faces. - do { - findedge(&checksh, pa, pb); - sfnextself(checksh); - assert((sorg(checksh) == pa) && (sdest(checksh) == pb)); - stpivot(checksh, rotface); - if (infected(rotface)) { - // Meet an old tet of B_i(p). This side is on the hull and - // will be connected to a new subface created in C(p). - break; - } - findedge(&rotface, pa, pb); - while (fnextself(rotface)); - tspivot(rotface, checksh); - } while (checksh.sh != dummysh); - } - // The rotface has edge ab, but it may not have newpt. - if (apex(rotface) == apex(newface)) { - // Bond the two tets together. - bond(newface, rotface); - // Queue (uniquely) this face if 'flipque' is given. - if (flipque != (queue *) NULL) { - enqueueflipface(newface, flipque); - } - } - } - enextself(newtet); - } - } - } - - if (subceillists != (list **) NULL) { - // There are C(p)s. - if (splitseg != (face *) NULL) { - // S (ab) is split by p. - splitseg->shver = 0; - pa = sorg(*splitseg); - pb = sdest(*splitseg); - // Allcate two arrays for saving the subface rings of the two new - // segments a->p and p->b. - apsegshs = new face[n]; - pbsegshs = new face[n]; - } - - // For each C_k(p), do the following: - // (1) Create new subfaces to fill C_k(p), insert them into B(p); - // (2) Connect new subfaces to each other; - for (k = 0; k < n; k++) { - subceillist = subceillists[k]; - - // Check if 'hullsize' should be updated. - oldsh = * (face *)(* subceillist)[0]; - stpivot(oldsh, neightet); - if (neightet.tet != dummytet) { - sesymself(oldsh); - stpivot(oldsh, neightet); - } - if (neightet.tet == dummytet) { - // The hull size changes. - hullsize += (subceillist->len() - sublists[k]->len()); - } - - // (1) Create new subfaces to fill C_k(p), insert them into B(p). - for (i = 0; i < subceillist->len(); i++) { - oldsh = * (face *)(* subceillist)[i]; - makeshellface(subfaces, &newsh); - setsorg(newsh, sorg(oldsh)); - setsdest(newsh, sdest(oldsh)); - setsapex(newsh, bp); - if (b->quality && varconstraint) { - setareabound(newsh, areabound(oldsh)); - } - setshellmark(newsh, shellmark(oldsh)); - setshelltype(newsh, shelltype(oldsh)); - if (checkpbcs) { - setshellpbcgroup(newsh, shellpbcgroup(oldsh)); - } - // Replace oldsh by newsh at the edge. - spivot(oldsh, casingout); - sspivot(oldsh, checkseg); - if (checkseg.sh != dummysh) { - // A segment. Insert s into the face ring, ie, s_in -> s -> s_out. - if (oldsh.sh != casingout.sh) { - // s is not bonded to itself. - spinsh = casingout; - do { - casingin = spinsh; - spivotself(spinsh); - } while (sapex(spinsh) != sapex(oldsh)); - assert(casingin.sh != oldsh.sh); - // Bond s_in -> s -> s_out (and dissolve s_in -> s_old -> s_out). - sbond1(casingin, newsh); - sbond1(newsh, casingout); - } else { - // Bond newsh -> newsh. - sbond(newsh, newsh); - } - // Bond the segment. - ssbond(newsh, checkseg); - } else { - // Bond s <-> s_out (and dissolve s_out -> s_old). - sbond(newsh, casingout); - } - - // Insert newsh into B(p). Use the coonections of oldsh. - stpivot(oldsh, neightet); - if (neightet.tet == dummytet) { - sesymself(oldsh); - sesymself(newsh); // Keep the same orientation as oldsh. - stpivot(oldsh, neightet); - } - assert(infected(neightet)); - // Set on the rotating edge. - findedge(&neightet, sorg(oldsh), sdest(oldsh)); - // Choose the rotating direction (to the inside of B(p)). - adjustedgering(neightet, CCW); - rotface = neightet; - // Rotate face. Stop at a non-infected tet t (not in B(p)) or a - // hull face f (on B(p)). Get the neighbor n of t or f. n is - // a new tet that has just been created to fill B(p). - do { - fnextself(rotface); - sym(rotface, neightet); - if (neightet.tet == dummytet) { - tspivot(rotface, checksh); - assert(checksh.sh != dummysh); - stpivot(checksh, newtet); - break; - } else if (!infected(neightet)) { - sym(neightet, newtet); - break; - } - } while (true); - assert(newtet.tet != rotface.tet); - // Set the rotating edge of n. - findedge(&newtet, sorg(oldsh), sdest(oldsh)); - // Choose the rotating direction (to the inside of B(p)). - adjustedgering(newtet, CCW); - fnext(newtet, newface); - assert(apex(newface) == bp); - // newsh has already been oriented toward n. - tsbond(newface, newsh); - sym(newface, neightet); // 'neightet' maybe outside. - sesymself(newsh); - tsbond(neightet, newsh); // Bond them anyway. - - // Replace oldsh by newsh in list. - * (face *)(* subceillist)[i] = newsh; - } - - // (2) Connect new subfaces to each other. - for (i = 0; i < subceillist->len(); i++) { - // Get a face cdp. - newsh = * (face *)(* subceillist)[i]; - // Get a new tet containing cdp. - stpivot(newsh, newtet); - if (newtet.tet == dummytet) { - sesymself(newsh); - stpivot(newsh, newtet); - } - for (j = 0; j < 2; j++) { - if (j == 0) { - senext(newsh, newedge); // edge dp. - } else { - senext2(newsh, newedge); // edge pc. - sesymself(newedge); // edge cp. - } - if (splitseg != (face *) NULL) { - // Don not operate on newedge if it is ap or pb. - if (sorg(newedge) == pa) { - apsegshs[k] = newedge; - continue; - } else if (sorg(newedge) == pb) { - pbsegshs[k] = newedge; - continue; - } - } - // There should no segment inside the cavity. Check it. - sspivot(newedge, checkseg); - assert(checkseg.sh == dummysh); - spivot(newedge, casingout); - if (casingout.sh == dummysh) { - rotface = newtet; - findedge(&rotface, sorg(newedge), sdest(newedge)); - // Rotate newtet until meeting a new subface which contains - // newedge. It must exist since newedge is not a seg. - adjustedgering(rotface, CCW); - do { - fnextself(rotface); - tspivot(rotface, checksh); - if (checksh.sh != dummysh) break; - } while (true); - findedge(&checksh, sorg(newedge), sdest(newedge)); - sbond(newedge, checksh); - } - } - } - // Only do once if p is on a facet. - if (splitseg == (face *) NULL) break; - } // for (k = 0; k < n; k++) - - if (splitseg != (face *) NULL) { - // Update a->b to be a->p. - apseg = *splitseg; - setsdest(apseg, bp); - // Create a new subsegment p->b. - makeshellface(subsegs, &pbseg); - setsorg(pbseg, bp); - setsdest(pbseg, pb); - // p->b gets the same mark and segment type as a->p. - setshellmark(pbseg, shellmark(apseg)); - setshelltype(pbseg, shelltype(apseg)); - if (b->quality && varconstraint) { - // Copy the area bound into the new subsegment. - setareabound(pbseg, areabound(apseg)); - } - senext(apseg, checkseg); - // Get the old connection at b of a->b. - spivot(checkseg, casingout); - // Bond a->p and p->b together. - senext2(pbseg, casingin); - sbond(casingin, checkseg); - if (casingout.sh != dummysh) { - // There is a subsegment connect at b of p->b. - casingout.shver = 0; -#ifdef SELF_CHECK - assert(sorg(casingout) == pb); -#endif - senext2self(casingout); - senext(pbseg, casingin); - sbond(casingin, casingout); - } - - // Bond all new subfaces to a->p and p->b. - for (i = 0; i < n; i++) { - spinsh = apsegshs[i]; - findedge(&spinsh, pa, bp); - ssbond(spinsh, apseg); - spinsh = pbsegshs[i]; - findedge(&spinsh, bp, pb); - ssbond(spinsh, pbseg); - } - // Bond all subfaces share at a->p together. - for (i = 0; i < n; i++) { - spinsh = apsegshs[i]; - if (i < (n - 1)) { - casingout = apsegshs[i + 1]; - } else { - casingout = apsegshs[0]; - } - sbond1(spinsh, casingout); - } - // Bond all subfaces share at p->b together. - for (i = 0; i < n; i++) { - spinsh = pbsegshs[i]; - if (i < (n - 1)) { - casingout = pbsegshs[i + 1]; - } else { - casingout = pbsegshs[0]; - } - sbond1(spinsh, casingout); - } - delete [] apsegshs; - delete [] pbsegshs; - - // Check for newly encroached subsegments if the flag is set. - if (chkencseg) { - // Check if a->p and p->b are encroached by other vertices. - checkseg4encroach(&apseg, NULL, NULL, true); - checkseg4encroach(&pbseg, NULL, NULL, true); - // Check if the adjacent segments are encroached by p. - tallencsegs(bp, n, ceillists); - } - } // if (splitseg != (face *) NULL) - - // Delete subfaces of old CBC_i(p)s. - for (k = 0; k < n; k++) { - for (i = 0; i < sublists[k]->len(); i++) { - oldsh = * (face *)(* (sublists[k]))[i]; - shellfacedealloc(subfaces, oldsh.sh); - } - // Clear the list so that the subs will not get unmarked later in - // routine releasebowatcavity() which only frees the memory. - sublists[k]->clear(); - // Only do once if p is on a facet. - if (splitseg == (face *) NULL) break; - } - - // Check for newly encroached subfaces if the flag is set. - if (chkencsub) { - // Check if new subfaces of C_i(p) are encroached by other vertices. - for (k = 0; k < n; k++) { - subceillist = subceillists[k]; - for (i = 0; i < subceillist->len(); i++) { - newsh = * (face *)(* subceillist)[i]; - checksub4encroach(&newsh, NULL, true); - } - // Only do once if p is on a facet. - if (splitseg == (face *) NULL) break; - } - // Check if the adjacent subfaces are encroached by p. - tallencsubs(bp, n, ceillists); - } - } // if (subceillists != (list **) NULL) - - // Delete tets of old BC_i(p)s. - for (k = 0; k < n; k++) { - for (i = 0; i < tetlists[k]->len(); i++) { - oldtet = * (triface *)(* (tetlists[k]))[i]; - tetrahedrondealloc(oldtet.tet); - } - // Clear the list so that the tets will not get unmarked later in - // routine releasebowatcavity() which only frees the memory. - tetlists[k]->clear(); - } - - // check for bad quality tets if the flags is set. - if (chkbadtet) { - for (k = 0; k < n; k++) { - ceillist = ceillists[k]; - for (i = 0; i < ceillist->len(); i++) { - newtet = * (triface *)(* ceillist)[i]; - checktet4badqual(&newtet, true); - } - } - } - - if (flipque != (queue *) NULL) { - // Newly created internal faces of BC(p) (excluding faces on C(p)s) are - // in 'flipque'. Some of these faces may be locally non-Delaunay due, - // to the existence of non-constrained tets. check and fix them. - repairflipcount += flip(flipque, NULL); - } -} - -// -// End of mesh transformation routines -// - -// -// Begin Delaunay tetrahedralization routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// formstarpolyhedron() Get the star ployhedron of a point 'pt'. // -// // -// The polyhedron P is formed by faces of tets having 'pt' as a vertex. If // -// 'complete' is TRUE, P is the complete star of 'pt'. Otherwise, P is boun- // -// ded by subfaces, i.e. P is only part of the star of 'pt'. // -// // -// 'tetlist' T returns the tets, it has one of such tets on input. Moreover, // -// if t is in T, then oppo(t) = p. Topologically, T is the star of p; and // -// the faces of T is the link of p. 'verlist' V returns the vertices of T. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::formstarpolyhedron(point pt, list* tetlist, list* verlist, - bool complete) -{ - triface starttet, neightet; - face checksh; - point ver[3]; - int idx, i, j; - - // Get a tet t containing p. - starttet = * (triface *)(* tetlist)[0]; - // Let oppo(t) = p. - for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { - if (oppo(starttet) == pt) break; - } - assert(starttet.loc < 4); - // Add t into T. - * (triface *)(* tetlist)[0] = starttet; - infect(starttet); - if (verlist != (list *) NULL) { - // Add three verts of t into V. - ver[0] = org(starttet); - ver[1] = dest(starttet); - ver[2] = apex(starttet); - for (i = 0; i < 3; i++) { - // Mark the vert by inversing the index of the vert. - idx = pointmark(ver[i]); - setpointmark(ver[i], -idx - 1); // -1 to distinguish the zero. - verlist->append(&(ver[i])); - } - } - - // Find other tets by a broadth-first search. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - starttet.ver = 0; - for (j = 0; j < 3; j++) { - fnext(starttet, neightet); - tspivot(neightet, checksh); - // Should we cross a subface. - if ((checksh.sh == dummysh) || complete) { - // Get the neighbor n. - symself(neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Let oppo(n) = p. - for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - if (oppo(neightet) == pt) break; - } - assert(neightet.loc < 4); - // Add n into T. - infect(neightet); - tetlist->append(&neightet); - if (verlist != (list *) NULL) { - // Add the apex vertex in n into V. - ver[0] = org(starttet); - ver[1] = dest(starttet); - findedge(&neightet, ver[0], ver[1]); - ver[2] = apex(neightet); - idx = pointmark(ver[2]); - if (idx >= 0) { - setpointmark(ver[2], -idx - 1); - verlist->append(&(ver[2])); - } - } - } - } - enextself(starttet); - } - } - - // Uninfect tets. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - uninfect(starttet); - } - if (verlist != (list *) NULL) { - // Uninfect vertices. - for (i = 0; i < verlist->len(); i++) { - ver[0] = * (point *)(* verlist)[i]; - idx = pointmark(ver[0]); - setpointmark(ver[0], -(idx + 1)); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unifypoint() Unify two distinct points if they're very close. // -// // -// This function is used for dealing with inputs from CAD tools. Two points // -// p and q are unified if: dist(p, q) / longest < eps. Where dist() is the // -// Euclidean distance between p and q, longest is the maximum edge size of // -// the input point set, eps is the tolerrence specified by user, default is // -// 1e-6, it can be adjusted by '-T' switch. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::unifypoint(point testpt, triface *starttet, enum locateresult - loc, REAL eps) -{ - triface symtet, spintet; - point checkpt, tapex; - REAL tol; - bool merged; - int hitbdry; - int i; - - merged = false; - tol = longest * eps; - if ((loc == OUTSIDE) || (loc == INTETRAHEDRON) || (loc == ONFACE)) { - // Check p is close to the four corners of the tet. - for (i = 0; i < 4; i++) { - checkpt = (point) starttet->tet[4 + i]; - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - break; - } - } - if (!merged && (loc == ONFACE)) { - // Check the opposite point of the neighbor tet if it exists. - sym(*starttet, symtet); - if (symtet.tet != dummytet) { - checkpt = oppo(symtet); - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - } - } - } - } else if (loc == ONEDGE) { - // Check two endpoints of the edge. - checkpt = org(*starttet); - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - } - if (!merged) { - checkpt = dest(*starttet); - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - } - } - if (!merged) { - // Check apexes of the faces having the edge. - spintet = *starttet; - tapex = apex(*starttet); - hitbdry = 0; - do { - checkpt = apex(spintet); - if (distance(testpt, checkpt) < tol) { - merged = true; // Found a merge point p'. - break; - } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(*starttet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } - } - } - } while ((apex(spintet) != tapex) && (hitbdry < 2)); - } - } - if (merged) { - if (b->object != tetgenbehavior::STL) { - if (!b->quiet) { - printf("Warning: Point %d is unified to point %d.\n", - pointmark(testpt), pointmark(checkpt)); - } - // Count the number of duplicated points. - dupverts++; - } - // Remember it is a duplicated point. - setpointtype(testpt, DUPLICATEDVERTEX); - // Set a pointer to the point it duplicates. - setpoint2ppt(testpt, checkpt); - } - return merged; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// incrflipdelaunay() Construct a delaunay tetrahedrization from a set of // -// 3D points by the incremental flip algorithm. // -// // -// The incremental flip algorithm (by Edelsbrunner and Shah) can be describ- // -// ed as follows: // -// // -// S be a set of points in 3D, Let 4 <= i <= n and assume that the // -// Delaunay tetrahedralization of the first i-1 points in S is already // -// constructed; call it D(i-1). Add the i-th point p_i (belong to S) to // -// D(i-1), and restore Delaunayhood by flipping; this result in D(i). // -// Repeat this procedure until i = n. // -// // -// This strategy always leads to the Delaunay triangulation of a point set. // -// The return value is the number of convex hull faces of D. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::incrflipdelaunay(triface* oldtet, point* insertarray, - long arraysize, bool jump, bool merge, REAL eps, queue* flipque) -{ - triface newtet, searchtet; - point swappt, lastpt; - enum locateresult loc; - REAL det, n[3]; - REAL attrib, volume; - int i, j; - clock_t loc_start, loc_end; - - if (b->verbose > 0) { - printf(" Creating initial tetrahedralization.\n"); - } - - // The initial tetrahedralization T only has one tet formed by 4 affinely - // linear independent vertices of the point set V = 'insertarray'. The - // first point a = insertarray[0]. - - // Get the second point b, that is not identical or very close to a. - for (i = 1; i < arraysize; i++) { - det = distance(insertarray[0], insertarray[i]); - if (det > (longest * eps)) break; - } - if (i == arraysize) { - printf("\nAll points seem to be identical.\n"); - return; - } else { - // Swap to move b from index i to index 1. - swappt = insertarray[i]; - insertarray[i] = insertarray[1]; - insertarray[1] = swappt; - } - // Get the third point c, that is not collinear with a and b. - for (i++; i < arraysize; i++) { - if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps)) - break; - } - if (i == arraysize) { - printf("\nAll points seem to be collinear.\n"); - return; - } else { - // Swap to move c from index i to index 2. - swappt = insertarray[i]; - insertarray[i] = insertarray[2]; - insertarray[2] = swappt; - } - // Get the fourth point d, that is not coplanar with a, b, and c. - for (i++; i < arraysize; i++) { - det = orient3d(insertarray[0], insertarray[1], insertarray[2], - insertarray[i]); - if (det == 0.0) continue; - if (!iscoplanar(insertarray[0], insertarray[1], insertarray[2], - insertarray[i], det, eps)) break; - } - if (i == arraysize) { - // It's a 2D problem. - in->mesh_dim = 2; - // All points are coplanar. - if (b->plc) { - // Create an abovepoint. Maybe a surface triangulation can be formed. - facenormal(insertarray[0], insertarray[1], insertarray[2], n, &det); - if (det != 0.0) for (j = 0; j < 3; j++) n[j] /= det; - // Take the average edge length of the bounding box. - det = (0.5*(xmax - xmin) + 0.5*(ymax - ymin) + 0.5*(zmax - zmin)) / 3.0; - // Temporarily create a point. It will be removed by jettison(); - makepoint(&lastpt); - for (j = 0; j < 3; j++) lastpt[j] = insertarray[0][j] + det * n[j]; - abovepoint = lastpt; - det = orient3d(insertarray[0], insertarray[1], insertarray[2], lastpt); - // The index of the next inserting point is 3. - i = 3; - } else { - printf("\nAll points seem to be coplanar.\n"); - return; - } - } else { - // Swap to move d from index i to index 3. - swappt = insertarray[i]; - insertarray[i] = insertarray[3]; - insertarray[3] = swappt; - lastpt = insertarray[3]; - // The index of the next inserting point is 4. - i = 4; - } - - // Create the initial tet. - maketetrahedron(&newtet); - if (det > 0.0) { - // For keeping the positive orientation. - swappt = insertarray[0]; - insertarray[0] = insertarray[1]; - insertarray[1] = swappt; - } - if (b->verbose > 2) { - printf(" Create the first tet (%d, %d, %d, %d).\n", - pointmark(insertarray[0]), pointmark(insertarray[1]), - pointmark(insertarray[2]), pointmark(lastpt)); - } - setorg(newtet, insertarray[0]); - setdest(newtet, insertarray[1]); - setapex(newtet, insertarray[2]); - setoppo(newtet, lastpt); - if (oldtet != (triface *) NULL) { - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(oldtet->tet, j); - setelemattribute(newtet.tet, j, attrib); - } - if (b->varvolume) { - volume = volumebound(oldtet->tet); - setvolumebound(newtet.tet, volume); - } - } - // Set vertex type be FREEVOLVERTEX if it has no type yet. - if (pointtype(insertarray[0]) == UNUSEDVERTEX) { - setpointtype(insertarray[0], FREEVOLVERTEX); - } - if (pointtype(insertarray[1]) == UNUSEDVERTEX) { - setpointtype(insertarray[1], FREEVOLVERTEX); - } - if (pointtype(insertarray[2]) == UNUSEDVERTEX) { - setpointtype(insertarray[2], FREEVOLVERTEX); - } - if (pointtype(lastpt) == UNUSEDVERTEX) { - setpointtype(lastpt, FREEVOLVERTEX); - } - // Bond to 'dummytet' for point location. - dummytet[0] = encode(newtet); - if (b->verbose > 3) { - printf(" Creating tetra "); - printtet(&newtet); - } - // At init, all faces of this tet are hull faces. - hullsize = 4; - - if (b->verbose > 0) { - printf(" Incrementally inserting points.\n"); - } - - flip23s = flip32s = flip22s = flip44s = 0; - searchtet.tet = (tetrahedron *) NULL; - - // Insert the rest of points, one by one. - for (; i < arraysize; i++) { - // Locate p_i in T. -#ifdef SELF_CHECK - loc_start = clock(); -#endif - if (jump) { - loc = locate(insertarray[i], &searchtet); - } else { - loc = preciselocate(insertarray[i], &searchtet, tetrahedrons->items); - } -#ifdef SELF_CHECK - loc_end = clock(); - tloctime += ((REAL) (loc_end - loc_start)) / CLOCKS_PER_SEC; -#endif - // Keep current search state for next searching. - recenttet = searchtet; - if (loc == ONVERTEX) { - if (b->object != tetgenbehavior::STL) { - if (!b->quiet) { - printf("Warning: Point %d is identical with point %d.\n", - pointmark(insertarray[i]), pointmark(org(searchtet))); - } - } - // Count the number of duplicated points. - dupverts++; - // Remember it is a duplicated point. - setpointtype(insertarray[i], DUPLICATEDVERTEX); - if (b->plc || b->refine) { - // Set a pointer to the point it duplicates. - setpoint2ppt(insertarray[i], org(searchtet)); - } - continue; // p_i is not inserted. - } - if (merge) { - // Unify p_i if it is too close to a point of T. - if (unifypoint(insertarray[i], &searchtet, loc, eps)) { - continue; // p_i is not inserted. - } - } - // Insert p_i in T. - if (loc != OUTSIDE) { - if (b->verbose > 1) { - printf(" Insert point %d in tetrahedralization.\n", - pointmark(insertarray[i])); - } - if (loc == INTETRAHEDRON) { - splittetrahedron(insertarray[i], &searchtet, flipque); - } else if (loc == ONFACE) { - splittetface(insertarray[i], &searchtet, flipque); - } else if (loc == ONEDGE) { - splittetedge(insertarray[i], &searchtet, flipque); - } - } else { - if (b->verbose > 1) { - printf(" Insert point %d on convex hull.\n", - pointmark(insertarray[i])); - } - inserthullsite(insertarray[i], &searchtet, flipque); - } - if (pointtype(insertarray[i]) == UNUSEDVERTEX) { - // p_i becomes a (volume) vertex of T. - setpointtype(insertarray[i], FREEVOLVERTEX); - } -#ifdef SELF_CHECK - loc_start = clock(); -#endif - if (!b->noflip) { - // Recover Delaunayness of T by flipping. - flip(flipque, NULL); - } else { - lawson(NULL, flipque); - // T remains regular. - // flipque->clear(); - } -#ifdef SELF_CHECK - loc_end = clock(); - tfliptime += ((REAL) (loc_end - loc_start)) / CLOCKS_PER_SEC; -#endif - } - - if (b->verbose > 0) { - printf(" %ld Flips (T23 %ld, T32 %ld, T22 %ld, T44 %ld)\n", - flip23s+flip32s+flip22s+flip44s, flip23s, flip32s, flip22s, flip44s); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunizevertices() Form a Delaunay tetrahedralization. // -// // -// Given a point set V (saved in 'points'). The Delaunay tetrahedralization // -// D of V is created by incrementally inserting vertices. Returns the number // -// of triangular faces bounding the convex hull of D. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::delaunizevertices() -{ - queue *flipque; - point *insertarray; - long arraysize; - int i, j; - - if (!b->quiet) { - if (!b->noflip) { - printf("Constructing Delaunay tetrahedralization.\n"); - } else { - printf("Constructing regular tetrahedralization.\n"); - } - } - - flipque = new queue(sizeof(badface)); - // Prepare the array of points for inserting. - arraysize = points->items; - insertarray = new point[arraysize]; - points->traversalinit(); - - // Randomize the point order. - // randomseed = b->srandseed; - for (i = 0; i < arraysize; i++) { - j = (int) randomnation(i + 1); // 0 <= j <= i; - insertarray[i] = insertarray[j]; - insertarray[j] = pointtraverse(); - } - - // Use lawson flip. - b->noflip = 1; - - // Form the DT by incremental flip Delaunay algorithm. - incrflipdelaunay(NULL, insertarray, arraysize, true, b->plc, b->epsilon, - flipque); - - b->noflip = 0; - - delete [] insertarray; - delete flipque; - return hullsize; -} - -// -// End Delaunay tetrahedralization routines -// - -// -// Begin of surface triangulation routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// formstarpolygon() Form the star polygon of a point in facet. // -// // -// The polygon P is formed by all coplanar subfaces having 'pt' as a vertex. // -// P is bounded by segments, e.g, if no segments, P is the full star of pt. // -// // -// 'trilist' T returns the subfaces, it has one of such subfaces on input. // -// In addition, if f is in T, then sapex(f) = p. 'vertlist' V are verts of P.// -// Topologically, T is the star of p; V and the edges of T are the link of p.// -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::formstarpolygon(point pt, list* trilist, list* vertlist) -{ - face steinsh, lnextsh, rnextsh; - face checkseg; - point pa, pb, pc, pd; - int i; - - // Get a subface f containing p. - steinsh = * (face *)(* trilist)[0]; - steinsh.shver = 0; // CCW - // Let sapex(f) be p. - for (i = 0; i < 3; i++) { - if (sapex(steinsh) == pt) break; - senextself(steinsh); - } - assert(i < 3); - // Add the edge f into list. - * (face *)(* trilist)[0] = steinsh; - pa = sorg(steinsh); - pb = sdest(steinsh); - if (vertlist != (list *) NULL) { - // Add two verts a, b into V, - vertlist->append(&pa); - vertlist->append(&pb); - } - - // Rotate edge pa to the left (CW) until meet pb or a segment. - lnextsh = steinsh; - pc = pa; - do { - senext2self(lnextsh); - assert(sorg(lnextsh) == pt); - sspivot(lnextsh, checkseg); - if (checkseg.sh != dummysh) break; // Do not cross a segment. - // Get neighbor subface n (must exist). - spivotself(lnextsh); - if (lnextsh.sh == dummysh) break; // It's a hull edge. - // Go to the edge ca opposite to p. - if (sdest(lnextsh) != pt) sesymself(lnextsh); - assert(sdest(lnextsh) == pt); - senext2self(lnextsh); - // Add n (at edge ca) to T. - trilist->append(&lnextsh); - // Add edge ca to E. - pc = sorg(lnextsh); - if (pc == pb) break; // Rotate back. - if (vertlist != (list *) NULL) { - // Add vert c into V. - vertlist->append(&pc); - } - } while (true); - - if (pc != pb) { - // Rotate edge bp to the right (CCW) until meet a segment. - rnextsh = steinsh; - do { - senextself(rnextsh); - assert(sdest(rnextsh) == pt); - sspivot(rnextsh, checkseg); - if (checkseg.sh != dummysh) break; // Do not cross a segment. - // Get neighbor subface n (must exist). - spivotself(rnextsh); - if (rnextsh.sh == dummysh) break; // It's a hull edge. - // Go to the edge bd opposite to p. - if (sorg(rnextsh) != pt) sesymself(rnextsh); - assert(sorg(rnextsh) == pt); - senextself(rnextsh); - // Add n (at edge bd) to T. - trilist->append(&rnextsh); - // Add edge bd to E. - pd = sdest(rnextsh); - if (pd == pa) break; // Rotate back. - if (vertlist != (list *) NULL) { - // Add vert d into V. - vertlist->append(&pd); - } - } while (true); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// About the 'abovepoint' // -// // -// The 'abovepoint' of a facet is a point which is exactly non-coplanar with // -// the plane containing that facet. With such an point, the 3D predicates: // -// orient3d(), and insphere() can be used to substitute the corresponding 2D // -// siblings, e.g. orient2d(), and incircle(). Its location is not critical, // -// but floating-point accuracy is improved if it is nicely placed over the // -// facet, not too close or too far away. // -// // -// We take the convention that the abovepoint of a facet always lies above // -// the facet. By this convention, given three points a, b, and c in a facet, // -// we say c has the counterclockwise order with ab is corresponding to say // -// that c is below the plane abp, where p is the lift point. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// getfacetabovepoint() Get a point above a plane pass through a facet. // -// // -// The calculcated point is saved in 'facetabovepointarray'. The 'abovepoint'// -// is set on return. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getfacetabovepoint(face* facetsh) -{ - list *verlist, *trilist, *tetlist; - triface adjtet; - face symsh; - point p1, p2, p3, pa; - enum locateresult loc; - REAL smallcos, cosa; - REAL largevol, volume; - REAL v1[3], v2[3], len; - int smallidx, largeidx; - int shmark; - int i, j; - - abovecount++; - // Initialize working lists. - verlist = new list(sizeof(point *), NULL); - trilist = new list(sizeof(face), NULL); - tetlist = new list(sizeof(triface), NULL); - - // Get three pivotal points p1, p2, and p3 in the facet as a base triangle - // which is non-trivil and has good base angle (close to 90 degree). - - // p1 is chosen as the one which has the smallest index in pa, pb, pc. - p1 = sorg(*facetsh); - pa = sdest(*facetsh); - if (pointmark(pa) < pointmark(p1)) p1 = pa; - pa = sapex(*facetsh); - if (pointmark(pa) < pointmark(p1)) p1 = pa; - // Form the star polygon of p1. - trilist->append(facetsh); - formstarpolygon(p1, trilist, verlist); - - // Get the second pivotal point p2. - p2 = * (point *)(* verlist)[0]; - // Get vector v1 = p1->p2. - for (i = 0; i < 3; i++) v1[i] = p2[i] - p1[i]; - len = sqrt(dot(v1, v1)); - assert(len > 0.0); // p2 != p1. - for (i = 0; i < 3; i++) v1[i] /= len; - - // Get the third pivotal point p3. p3 is chosen as the one in 'verlist' - // which forms an angle with v1 closer to 90 degree than others do. - smallcos = 1.0; // The cosine value of 0 degree. - smallidx = 1; // Default value. - for (i = 1; i < verlist->len(); i++) { - p3 = * (point *)(* verlist)[i]; - for (j = 0; j < 3; j++) v2[j] = p3[j] - p1[j]; - len = sqrt(dot(v2, v2)); - if (len > 0.0) { // v2 is not too small. - cosa = fabs(dot(v1, v2)) / len; - if (cosa < smallcos) { - smallidx = i; - smallcos = cosa; - } - } - } - assert(smallcos < 1.0); // p1->p3 != p1->p2. - p3 = * (point *)(* verlist)[smallidx]; - verlist->clear(); - - if (tetrahedrons->items > 0l) { - // Get a tet having p1 as a vertex. - stpivot(*facetsh, adjtet); - if (adjtet.tet == dummytet) { - sesym(*facetsh, symsh); - stpivot(symsh, adjtet); - } - if (adjtet.tet == dummytet) { - decode(point2tet(p1), adjtet); - if (isdead(&adjtet)) { - adjtet.tet = dummytet; - } else { - if (!findorg(&adjtet, p1)) { - adjtet.tet = dummytet; - } - } - } - if (adjtet.tet == dummytet) { - loc = locate(p1, &adjtet); - if (loc == ONVERTEX) { - setpoint2tet(p1, encode(adjtet)); - } else { - adjtet.tet = dummytet; - } - } - if (adjtet.tet != dummytet) { - // Get the star polyhedron of p1. - tetlist->append(&adjtet); - formstarpolyhedron(p1, tetlist, verlist, false); - } - } - - // Get the abovepoint in 'verlist'. It is the one form the largest valid - // volumw with the base triangle over other points in 'verlist. - largevol = 0.0; - largeidx = 0; - for (i = 0; i < verlist->len(); i++) { - pa = * (point *)(* verlist)[i]; - volume = orient3d(p1, p2, p3, pa); - if (!iscoplanar(p1, p2, p3, pa, volume, b->epsilon * 1e+2)) { - if (fabs(volume) > largevol) { - largevol = fabs(volume); - largeidx = i; - } - } - } - - // Do we have the abovepoint? - if (largevol > 0.0) { - abovepoint = * (point *)(* verlist)[largeidx]; - if (b->verbose > 1) { - printf(" Chosen abovepoint %d for facet %d.\n", pointmark(abovepoint), - shellmark(*facetsh)); - } - } else { - // Calculate an abovepoint for this facet. - facenormal(p1, p2, p3, v1, &len); - if (len != 0.0) for (i = 0; i < 3; i++) v1[i] /= len; - // Take the average edge length of the bounding box. - len = (0.5*(xmax - xmin) + 0.5*(ymax - ymin) + 0.5*(zmax - zmin)) / 3.0; - // Temporarily create a point. It will be removed by jettison(); - makepoint(&abovepoint); - setpointtype(abovepoint, UNUSEDVERTEX); - unuverts++; - for (i = 0; i < 3; i++) abovepoint[i] = p1[i] + len * v1[i]; - if (b->verbose > 1) { - printf(" Calculated abovepoint %d for facet %d.\n", - pointmark(abovepoint), shellmark(*facetsh)); - } - } - // Save the abovepoint in 'facetabovepointarray'. - shmark = shellmark(*facetsh); - facetabovepointarray[shmark] = abovepoint; - - delete trilist; - delete tetlist; - delete verlist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// collectcavsubs() Collect non-locally Delaunay subfaces wrt a point. // -// // -// 'cavsublist' returns the list of subfaces. On input, it conatins at least // -// one subface. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::collectcavsubs(point newpoint, list* cavsublist) -{ - face startsub, neighsub; - face checkseg; - point pa, pb, pc; - REAL sign, ori; - int i, j; - - // First infect subfaces in 'cavsublist'. - for (i = 0; i < cavsublist->len(); i++) { - startsub = * (face *)(* cavsublist)[i]; - sinfect(startsub); - } - // Find the other subfaces by a broadth-first searching. - for (i = 0; i < cavsublist->len(); i++) { - startsub = * (face *)(* cavsublist)[i]; - for (j = 0; j < 3; j++) { - sspivot(startsub, checkseg); - // Is there a segment? - if (checkseg.sh == dummysh) { - // No segment. Get the neighbor. - spivot(startsub, neighsub); - if (!sinfected(neighsub)) { - pa = sorg(neighsub); - pb = sdest(neighsub); - pc = sapex(neighsub); - sign = insphere(pa, pb, pc, abovepoint, newpoint); - ori = orient3d(pa, pb, pc, abovepoint); - if (sign != 0.0) { - // Correct the sign. - sign = ori > 0.0 ? sign : -sign; - } - if (sign > 0.0) { - // neighsub is encroached by newpoint. - sinfect(neighsub); - cavsublist->append(&neighsub); - } - } - } - senextself(startsub); - } - } - // Having found all subfaces, uninfect them before return. - for (i = 0; i < cavsublist->len(); i++) { - startsub = * (face *)(* cavsublist)[i]; - suninfect(startsub); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// collectvisiblesubs() Collect convex hull edges which are visible from // -// the inserting point. Construct new subfaces from // -// these edges and the point. // -// // -// Let T be the current Delaunay triangulation (of vertices of a facet F). // -// 'shmark', the index of F in 'in->facetlist' (starts from 1); 'inspoint' // -// lies outside of T; 'horiz' is a hull edge of T which is visible by it. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::collectvisiblesubs(int shmark, point inspoint, face* horiz, - queue* flipqueue) -{ - face newsh, hullsh; - face rightsh, leftsh, spinedge; - point horg, hdest; - bool aboveflag; - REAL ori, sign; - - // Get the sign of abovepoint (so we can assume it is above the plane). - adjustedgering(*horiz, CCW); - horg = sorg(*horiz); - hdest = sdest(*horiz); - ori = orient3d(horg, hdest, sapex(*horiz), abovepoint); - sign = ori > 0.0 ? -1 : 1; - - // Create a new subface above 'horiz'. - makeshellface(subfaces, &newsh); - setsorg(newsh, hdest); - setsdest(newsh, horg); - setsapex(newsh, inspoint); - setshellmark(newsh, shmark); - if (b->quality && varconstraint) { - setareabound(newsh, areabound(*horiz)); - } - if (checkpbcs) { - setshellpbcgroup(newsh, shellpbcgroup(*horiz)); - } - // Make the connection. - sbond(newsh, *horiz); - // 'horiz' becomes interior edge. - enqueueflipedge(*horiz, flipqueue); - - // Finish the hull edges at the right side of the newsh. - hullsh = *horiz; - while (1) { - senext(newsh, rightsh); - // Get the right hull edge of 'horiz' by spinning inside edges around - // 'horg' until reaching the 'dummysh'. - spinedge = hullsh; - do { - hullsh = spinedge; - senext2self(hullsh); - spivot(hullsh, spinedge); - if (spinedge.sh == dummysh) break; - if (sorg(spinedge) != horg) sesymself(spinedge); - assert(sorg(spinedge) == horg); - } while (true); - horg = sorg(hullsh); - // Test whether 'inspoint' is visible by 'hullsh'. - ori = orient3d(horg, sdest(hullsh), abovepoint, inspoint); - ori *= sign; - aboveflag = ori < 0.0; - if (aboveflag) { - // It's visible. - makeshellface(subfaces, &newsh); - setsorg(newsh, sdest(hullsh)); - setsdest(newsh, horg); - setsapex(newsh, inspoint); - setshellmark(newsh, shmark); - if (b->quality && varconstraint) { - setareabound(newsh, areabound(hullsh)); - } - if (checkpbcs) { - setshellpbcgroup(newsh, shellpbcgroup(hullsh)); - } - // Make the connection. - sbond(newsh, hullsh); - senext2(newsh, leftsh); - sbond(leftsh, rightsh); - // 'hullsh' becomes interior edge. - enqueueflipedge(hullsh, flipqueue); - } else { - // 'rightsh' is a new hull edge. - dummysh[0] = sencode(rightsh); - break; - } - } - - // Finish the hull edges at the left side of the newsh. - hullsh = *horiz; - spivot(*horiz, newsh); - while (1) { - senext2(newsh, leftsh); - // Get the left hull edge of 'horiz' by spinning edges around 'hdest'. - spinedge = hullsh; - do { - hullsh = spinedge; - senextself(hullsh); - spivot(hullsh, spinedge); - if (spinedge.sh == dummysh) break; - if (sdest(spinedge) != hdest) sesymself(spinedge); - assert(sdest(spinedge) == hdest); - } while (true); - // Update 'hdest'. - hdest = sdest(hullsh); - // Test whether 'inspoint' is visible from 'hullsh'. - ori = orient3d(sorg(hullsh), hdest, abovepoint, inspoint); - ori *= sign; - aboveflag = ori < 0.0; - if (aboveflag) { - // It's a visible hull edge. - makeshellface(subfaces, &newsh); - setsorg(newsh, hdest); - setsdest(newsh, sorg(hullsh)); - setsapex(newsh, inspoint); - setshellmark(newsh, shmark); - if (b->quality && varconstraint) { - setareabound(newsh, areabound(hullsh)); - } - if (checkpbcs) { - setshellpbcgroup(newsh, shellpbcgroup(hullsh)); - } - // Make the connection. - sbond(newsh, hullsh); - senext(newsh, rightsh); - sbond(rightsh, leftsh); - // 'horiz' becomes interior edge. - enqueueflipedge(hullsh, flipqueue); - } else { - // 'leftsh' is a new hull edge. - dummysh[0] = sencode(leftsh); - break; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// incrflipdelaunaysub() Create a DT from a 3D coplanar point set using // -// the incremental flip algorithm. // -// // -// Let T be the current Delaunay triangulation (of vertices of a facet F). // -// 'shmark', the index of F in 'in->facetlist' (starts from 1). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::incrflipdelaunaysub(int shmark, REAL eps, list* ptlist, - int holes, REAL* holelist, queue* flipque) -{ - face newsh, startsh; - point *insertarray; - point swappt; - pbcdata *pd; - enum locateresult loc; - REAL det, area; - bool aboveflag; - int arraysize; - int epscount; - int fmarker; - int idx, i, j, k; - - // Get the point array (saved in 'ptlist'). - insertarray = (point *) ptlist->base; - arraysize = ptlist->len(); - if (arraysize < 3) return; - - // Do calculation of 'abovepoint' if number of points > 3. - aboveflag = (arraysize > 3); - - // The initial triangulation T only has one triangle formed by 3 not - // cillinear points of the set V = 'insertarray'. The first point: - // a = insertarray[0]. - - epscount = 0; - while (true) { - for (i = 1; i < arraysize; i++) { - det = distance(insertarray[0], insertarray[i]); - if (det > (longest * eps)) break; - } - if (i < arraysize) { - // Swap to move b from index i to index 1. - swappt = insertarray[i]; - insertarray[i] = insertarray[1]; - insertarray[1] = swappt; - } - // Get the third point c, that is not collinear with a and b. - for (i++; i < arraysize; i++) { - if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps)) - break; - } - if (i < arraysize) { - // Swap to move c from index i to index 2. - swappt = insertarray[i]; - insertarray[i] = insertarray[2]; - insertarray[2] = swappt; - i = 3; // The next inserting point. - } else { - // The set of vertices is not good (or nearly degenerate). However, - // a trivial triangulation can be formed (using 3 vertices). It may - // be corrected (or deleted) by mergefacet(). - if ((eps == 0.0) || (epscount > 16)) { - printf("Error: Invalid PLC.\n"); - printf(" Facet (%d, %d, %d", pointmark(insertarray[0]), - pointmark(insertarray[1]), pointmark(insertarray[2])); - if (ptlist->len() > 3) { - printf(", ..."); - } - printf(") (%d) is not a valid polygon.\n", shmark); - terminatetetgen(1); - } - // Decrease the eps, and continue to try. - eps *= 1e-2; - epscount++; - continue; - } - break; - } // while (true); - - // Create the initial triangle. - makeshellface(subfaces, &newsh); - setsorg(newsh, insertarray[0]); - setsdest(newsh, insertarray[1]); - setsapex(newsh, insertarray[2]); - // Remeber the facet it belongs to. - setshellmark(newsh, shmark); - // Set vertex type be FREESUBVERTEX if it has no type yet. - if (pointtype(insertarray[0]) == FREEVOLVERTEX) { - setpointtype(insertarray[0], FREESUBVERTEX); - } - if (pointtype(insertarray[1]) == FREEVOLVERTEX) { - setpointtype(insertarray[1], FREESUBVERTEX); - } - if (pointtype(insertarray[2]) == FREEVOLVERTEX) { - setpointtype(insertarray[2], FREESUBVERTEX); - } - // Let 'dummysh' point to it (for point location). - dummysh[0] = sencode(newsh); - - // Are there area constraints? - if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) { - idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. - for (k = 0; k < in->numberoffacetconstraints; k++) { - fmarker = (int) in->facetconstraintlist[k * 2]; - if (fmarker == idx) { - area = in->facetconstraintlist[k * 2 + 1]; - setareabound(newsh, area); - break; - } - } - } - - // Are there pbc conditions? - if (checkpbcs) { - idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. - for (k = 0; k < in->numberofpbcgroups; k++) { - pd = &subpbcgrouptable[k]; - for (j = 0; j < 2; j++) { - if (pd->fmark[j] == idx) { - setshellpbcgroup(newsh, k); - pd->ss[j] = newsh; - } - } - } - } - - if (aboveflag) { - // Compute the 'abovepoint' for orient3d(). - abovepoint = facetabovepointarray[shmark]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&newsh); - } - } - - if (holes > 0) { - // Project hole points onto the plane containing the facet. - REAL prj[3]; - for (k = 0; k < holes; k++) { - projpt2face(&(holelist[k * 3]), insertarray[0], insertarray[1], - insertarray[2], prj); - for (j = 0; j < 3; j++) holelist[k * 3 + j] = prj[j]; - } - } - - // Incrementally insert the rest of points into T. - for (; i < arraysize; i++) { - // Insert p_i. - startsh.sh = dummysh; - loc = locatesub(insertarray[i], &startsh, 0, 0.0); - if (loc == ONFACE) { - splitsubface(insertarray[i], &startsh, flipque); - } else if (loc == ONEDGE) { - splitsubedge(insertarray[i], &startsh, flipque); - } else if (loc == OUTSIDE) { - collectvisiblesubs(shmark, insertarray[i], &startsh, flipque); - } else if (loc == ONVERTEX) { - // !should not happen! - } - // Set p_i's type FREESUBVERTEX if it has no type yet. - if (pointtype(insertarray[i]) == FREEVOLVERTEX) { - setpointtype(insertarray[i], FREESUBVERTEX); - } - flipsub(flipque); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// finddirectionsub() Find the first subface in a facet on the path from // -// one point to another. // -// // -// Finds the subface in the facet that intersects a line segment drawn from // -// the origin of `searchsh' to the point `tend', and returns the result in // -// `searchsh'. The origin of `searchsh' does not change, even though the // -// subface returned may differ from the one passed in. // -// // -// The return value notes whether the destination or apex of the found face // -// is collinear with the two points in question. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::finddirectionresult tetgenmesh::finddirectionsub( - face* searchsh, point tend) -{ - face checksh; - point startpoint, leftpoint, rightpoint; - REAL leftccw, rightccw; - REAL ori, sign; - int leftflag, rightflag; - - startpoint = sorg(*searchsh); - // Find the sign to simulate that abovepoint is 'above' the facet. - adjustedgering(*searchsh, CCW); - // Make sure 'startpoint' is the origin. - if (sorg(*searchsh) != startpoint) senextself(*searchsh); - rightpoint = sdest(*searchsh); - leftpoint = sapex(*searchsh); - ori = orient3d(startpoint, rightpoint, leftpoint, abovepoint); - sign = ori > 0.0 ? -1 : 1; - - // Is `tend' to the left? - ori = orient3d(tend, startpoint, abovepoint, leftpoint); - leftccw = ori * sign; - leftflag = leftccw > 0.0; - // Is `tend' to the right? - ori = orient3d(startpoint, tend, abovepoint, rightpoint); - rightccw = ori * sign; - rightflag = rightccw > 0.0; - if (leftflag && rightflag) { - // `searchsh' faces directly away from `tend'. We could go left or - // right. Ask whether it's a triangle or a boundary on the left. - senext2(*searchsh, checksh); - spivotself(checksh); - if (checksh.sh == dummysh) { - leftflag = 0; - } else { - rightflag = 0; - } - } - while (leftflag) { - // Turn left until satisfied. - senext2self(*searchsh); - spivotself(*searchsh); - if (searchsh->sh == dummysh) { - printf("Internal error in finddirectionsub(): Unable to find a\n"); - printf(" subface leading from %d to %d.\n", pointmark(startpoint), - pointmark(tend)); - internalerror(); - } - if (sorg(*searchsh) != startpoint) sesymself(*searchsh); - assert(sorg(*searchsh) == startpoint); - leftpoint = sapex(*searchsh); - rightccw = leftccw; - ori = orient3d(tend, startpoint, abovepoint, leftpoint); - leftccw = ori * sign; - leftflag = leftccw > 0.0; - } - while (rightflag) { - // Turn right until satisfied. - spivotself(*searchsh); - if (searchsh->sh == dummysh) { - printf("Internal error in finddirectionsub(): Unable to find a\n"); - printf(" subface leading from %d to %d.\n", pointmark(startpoint), - pointmark(tend)); - internalerror(); - } - if (sdest(*searchsh) != startpoint) sesymself(*searchsh); - assert(sdest(*searchsh) == startpoint); - senextself(*searchsh); - rightpoint = sdest(*searchsh); - leftccw = rightccw; - ori = orient3d(startpoint, tend, abovepoint, rightpoint); - rightccw = ori * sign; - rightflag = rightccw > 0.0; - } - if (leftccw == 0.0) { - return LEFTCOLLINEAR; - } else if (rightccw == 0.0) { - return RIGHTCOLLINEAR; - } else { - return ACROSSEDGE; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertsubseg() Create a subsegment and insert it between two subfaces. // -// // -// The new subsegment ab is inserted at the edge of subface 'tri'. If ab is // -// not a hull edge, it is inserted between two subfaces. If 'tri' is a hull // -// face, the initial face ring of ab will be set only one face which is self-// -// bonded. The final face ring will be constructed in 'unifysegments()'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::insertsubseg(face* tri) -{ - face oppotri; - face newsubseg; - point pa, pb; - REAL len; - int e1, e2; - int i; - - // Check if there's already a subsegment here. - sspivot(*tri, newsubseg); - if (newsubseg.sh == dummysh) { - // Make new subsegment and initialize its vertices. - makeshellface(subsegs, &newsubseg); - pa = sorg(*tri); - pb = sdest(*tri); - setsorg(newsubseg, pa); - setsdest(newsubseg, pb); - // Are there length constraints? - if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { - for (i = 0; i < in->numberofsegmentconstraints; i++) { - e1 = (int) in->segmentconstraintlist[i * 3]; - e2 = (int) in->segmentconstraintlist[i * 3 + 1]; - if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) || - ((pointmark(pa) == e2) && (pointmark(pb) == e1))) { - len = in->segmentconstraintlist[i * 3 + 2]; - setareabound(newsubseg, len); - break; - } - } - } - // Bond new subsegment to the two subfaces it is sandwiched between. - ssbond(*tri, newsubseg); - spivot(*tri, oppotri); - // 'oppotri' might be "out space". - if (oppotri.sh != dummysh) { - ssbond(oppotri, newsubseg); - } /* else { - // Outside! Bond '*tri' to itself. - sbond(*tri, *tri); - } */ - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutsegmentsub() Scout the first triangle on the path from one point // -// to another, and check for completion (reaching the // -// second point), a collinear point,or the intersection // -// of two segments. // -// // -// Returns true if the entire segment is successfully inserted, and false if // -// the job must be finished by constrainededge(). // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::scoutsegmentsub(face* searchsh, point tend) -{ - face newsubseg; - face crosssub, crosssubseg; - point leftpoint, rightpoint; - enum finddirectionresult collinear; - - collinear = finddirectionsub(searchsh, tend); - rightpoint = sdest(*searchsh); - leftpoint = sapex(*searchsh); - if (rightpoint == tend || leftpoint == tend) { - // The segment is already an edge. - if (leftpoint == tend) { - senext2self(*searchsh); - } - // Insert a subsegment. - insertsubseg(searchsh); - return true; - } else if (collinear == LEFTCOLLINEAR) { - // We've collided with a vertex between the segment's endpoints. - // Make the collinear vertex be the triangle's origin. - senextself(*searchsh); // lprevself(*searchtri); - // Insert a subsegment. - insertsubseg(searchsh); - // Insert the remainder of the segment. - return scoutsegmentsub(searchsh, tend); - } else if (collinear == RIGHTCOLLINEAR) { - // We've collided with a vertex between the segment's endpoints. - // Insert a subsegment. - insertsubseg(searchsh); - // Make the collinear vertex be the triangle's origin. - senextself(*searchsh); // lnextself(*searchtri); - // Insert the remainder of the segment. - return scoutsegmentsub(searchsh, tend); - } else { - senext(*searchsh, crosssub); // lnext(*searchtri, crosstri); - // Check for a crossing segment. - sspivot(crosssub, crosssubseg); -#ifdef SELF_CHECK - assert(crosssubseg.sh == dummysh); -#endif - return false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// flipedgerecursive() Flip an edge. // -// // -// This is a support routine for inserting segments into a CDT. // -// // -// Let 'flipedge' be ab, and two triangles abc, abd share at it. ab may not // -// flipable if the four vertices a, b, c, and d are non-convex. If it is the // -// case, recursively flip ad or bd. Return when ab is flipped. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::flipedgerecursive(face* flipedge, queue* flipqueue) -{ - face fixupsh; - point pa, pb, pc, pd; - REAL oria, orib; - bool doflip; - - pa = sorg(*flipedge); - pb = sdest(*flipedge); - pc = sapex(*flipedge); - do { - spivot(*flipedge, fixupsh); - pd = sapex(fixupsh); - oria = orient3d(pc, pd, abovepoint, pa); - orib = orient3d(pc, pd, abovepoint, pb); - doflip = (oria * orib < 0.0); - if (doflip) { - // Flip the edge (a, b) away. - flip22sub(flipedge, flipqueue); - // Fix flipedge on edge e (c, d). - findedge(flipedge, pc, pd); - } else { - // ab is unflipable. Get the next edge (bd, or da) to flip. - if (sorg(fixupsh) != pb) sesymself(fixupsh); - assert(sdest(fixupsh) == pa); - if (fabs(oria) > fabs(orib)) { - // acd has larger area. Choose da. - senextself(fixupsh); - } else { - // bcd has larger area. Choose bd. - senext2self(fixupsh); - } - // Flip the edge. - flipedgerecursive(&fixupsh, flipqueue); - } - } while (!doflip); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainededge() Force a segment into a CDT. // -// // -// The segment s is recovered by flipping away the edges it intersects, and // -// triangulating the polygons that form on each side of it. // -// // -// Generates a single subsegment connecting `tstart' to `tend'. The triangle // -// `startsh' has `tstart' as its origin. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::constrainededge(face* startsh, point tend, queue* flipqueue) -{ - point tstart, tright, tleft; - REAL rori, lori; - bool collision; - - tstart = sorg(*startsh); - do { - // Loop edges oppo to tstart until find one crosses the segment. - do { - tright = sdest(*startsh); - tleft = sapex(*startsh); - // Is edge (tright, tleft) corss the segment. - rori = orient3d(tstart, tright, abovepoint, tend); - collision = (rori == 0.0); - if (collision) break; // tright is on the segment. - lori = orient3d(tstart, tleft, abovepoint, tend); - collision = (lori == 0.0); - if (collision) { // tleft is on the segment. - senext2self(*startsh); - break; - } - if (rori * lori < 0.0) break; // Find the crossing edge. - // Both points are at one side of the segment. - finddirectionsub(startsh, tend); - } while (true); - if (collision) break; - // Get the neighbor face at edge e (tright, tleft). - senextself(*startsh); - // Flip the crossing edge. - flipedgerecursive(startsh, flipqueue); - // After flip, sorg(*startsh) == tstart. - assert(sorg(*startsh) == tstart); - } while (sdest(*startsh) != tend); - - // Insert a subsegment to make the segment permanent. - insertsubseg(startsh); - // If there was a collision with an interceding vertex, install another - // segment connecting that vertex with endpoint2. - if (collision) { - // Insert the remainder of the segment. - if (!scoutsegmentsub(startsh, tend)) { - constrainededge(startsh, tend, flipqueue); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// recoversegment() Recover a segment in the surface triangulation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::recoversegment(point tstart, point tend, queue* flipqueue) -{ - face searchsh; - - if (b->verbose > 2) { - printf(" Insert seg (%d, %d).\n", pointmark(tstart), pointmark(tend)); - } - - // Find a triangle whose origin is the segment's first endpoint. - searchsh.sh = dummysh; - // Search for the segment's first endpoint by point location. - if (locatesub(tstart, &searchsh, 0, 0.0) != ONVERTEX) { - // Possibly caused by a degenerate subface. Do a brute-force search. - list *newshlist; - int i, j; - newshlist = new list(sizeof(face), NULL, 256); - // Get new subfaces, do not remove protected segments. - retrievenewsubs(newshlist, false); - // Search for a sub contain tstart. - for (i = 0; i < newshlist->len(); i++) { - searchsh = * (face *)(* newshlist)[i]; - for (j = 0; j < 3; j++) { - if (sorg(searchsh) == tstart) break; - senextself(searchsh); - } - if (j < 3) break; - } - delete newshlist; - if (sorg(searchsh) != tstart) { - printf("Internal error in recoversegment(): Vertex location failed.\n"); - internalerror(); - } - } - // Scout the segment and insert it if it is found. - if (scoutsegmentsub(&searchsh, tend)) { - // The segment was easily inserted. - return; - } - // Insert the segment into the triangulation by flips. - constrainededge(&searchsh, tend, flipqueue); - // Some edges may need flipping. - flipsub(flipqueue); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// infecthullsub() Virally infect all of the triangles of the convex hull // -// that are not protected by subsegments. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::infecthullsub(memorypool* viri) -{ - face hulltri, nexttri, starttri; - face hullsubseg; - shellface **deadshellface; - - // Find a triangle handle on the hull. - hulltri.sh = dummysh; - hulltri.shver = 0; - spivotself(hulltri); - adjustedgering(hulltri, CCW); - // Remember where we started so we know when to stop. - starttri = hulltri; - // Go once counterclockwise around the convex hull. - do { - // Ignore triangles that are already infected. - if (!sinfected(hulltri)) { - // Is the triangle protected by a subsegment? - sspivot(hulltri, hullsubseg); - if (hullsubseg.sh == dummysh) { - // The triangle is not protected; infect it. - if (!sinfected(hulltri)) { - sinfect(hulltri); - deadshellface = (shellface **) viri->alloc(); - *deadshellface = hulltri.sh; - } - } - } - // To find the next hull edge, go clockwise around the next vertex. - senextself(hulltri); // lnextself(hulltri); - spivot(hulltri, nexttri); // oprev(hulltri, nexttri); - if (nexttri.sh == hulltri.sh) { - nexttri.sh = dummysh; // 'hulltri' is self-bonded. - } else { - adjustedgering(nexttri, CCW); - senextself(nexttri); - } - while (nexttri.sh != dummysh) { - hulltri = nexttri; - spivot(hulltri, nexttri); // oprev(hulltri, nexttri); - if (nexttri.sh == hulltri.sh) { - nexttri.sh = dummysh; // 'hulltri' is self-bonded. - } else { - adjustedgering(nexttri, CCW); - senextself(nexttri); - } - } - } while (hulltri != starttri); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// plaguesub() Spread the virus from all infected triangles to any // -// neighbors not protected by subsegments. Delete all // -// infected triangles. // -// // -// This is the procedure that actually creates holes and concavities. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::plaguesub(memorypool* viri) -{ - face testtri, neighbor, ghostsh; - face neighborsubseg; - shellface **virusloop; - shellface **deadshellface; - int i; - - // Loop through all the infected triangles, spreading the virus to - // their neighbors, then to their neighbors' neighbors. - viri->traversalinit(); - virusloop = (shellface **) viri->traverse(); - while (virusloop != (shellface **) NULL) { - testtri.sh = *virusloop; - // Check each of the triangle's three neighbors. - for (i = 0; i < 3; i++) { - // Find the neighbor. - spivot(testtri, neighbor); - // Check for a subsegment between the triangle and its neighbor. - sspivot(testtri, neighborsubseg); - // Check if the neighbor is nonexistent or already infected. - if ((neighbor.sh == dummysh) || sinfected(neighbor)) { - if (neighborsubseg.sh != dummysh) { - // There is a subsegment separating the triangle from its - // neighbor, but both triangles are dying, so the subsegment - // dies too. - shellfacedealloc(subsegs, neighborsubseg.sh); - if (neighbor.sh != dummysh) { - // Make sure the subsegment doesn't get deallocated again - // later when the infected neighbor is visited. - ssdissolve(neighbor); - } - } - } else { // The neighbor exists and is not infected. - if (neighborsubseg.sh == dummysh) { - // There is no subsegment protecting the neighbor, so the - // neighbor becomes infected. - sinfect(neighbor); - // Ensure that the neighbor's neighbors will be infected. - deadshellface = (shellface **) viri->alloc(); - *deadshellface = neighbor.sh; - } else { // The neighbor is protected by a subsegment. - // Remove this triangle from the subsegment. - ssbond(neighbor, neighborsubseg); - } - } - senextself(testtri); - } - virusloop = (shellface **) viri->traverse(); - } - - ghostsh.sh = dummysh; // A handle of outer space. - viri->traversalinit(); - virusloop = (shellface **) viri->traverse(); - while (virusloop != (shellface **) NULL) { - testtri.sh = *virusloop; - // Record changes in the number of boundary edges, and disconnect - // dead triangles from their neighbors. - for (i = 0; i < 3; i++) { - spivot(testtri, neighbor); - if (neighbor.sh != dummysh) { - // Disconnect the triangle from its neighbor. - // sdissolve(neighbor); - sbond(neighbor, ghostsh); - } - senextself(testtri); - } - // Return the dead triangle to the pool of triangles. - shellfacedealloc(subfaces, testtri.sh); - virusloop = (shellface **) viri->traverse(); - } - // Empty the virus pool. - viri->restart(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// carveholessub() Find the holes and infect them. Find the area // -// constraints and infect them. Infect the convex hull. // -// Spread the infection and kill triangles. Spread the // -// area constraints. // -// // -// This routine mainly calls other routines to carry out all these functions.// -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::carveholessub(int holes, REAL* holelist, memorypool *viri) -{ - face searchtri, triangleloop; - shellface **holetri; - enum locateresult intersect; - int i; - - // Mark as infected any unprotected triangles on the boundary. - // This is one way by which concavities are created. - infecthullsub(viri); - - if (holes > 0) { - // Infect each triangle in which a hole lies. - for (i = 0; i < 3 * holes; i += 3) { - // Ignore holes that aren't within the bounds of the mesh. - if ((holelist[i] >= xmin) && (holelist[i] <= xmax) - && (holelist[i + 1] >= ymin) && (holelist[i + 1] <= ymax) - && (holelist[i + 2] >= zmin) && (holelist[i + 2] <= zmax)) { - // Start searching from some triangle on the outer boundary. - searchtri.sh = dummysh; - // Find a triangle that contains the hole. - intersect = locatesub(&holelist[i], &searchtri, 0, 0.0); - if ((intersect != OUTSIDE) && (!sinfected(searchtri))) { - // Infect the triangle. This is done by marking the triangle - // as infected and including the triangle in the virus pool. - sinfect(searchtri); - holetri = (shellface **) viri->alloc(); - *holetri = searchtri.sh; - } - } - } - } - - if (viri->items > 0) { - // Carve the holes and concavities. - plaguesub(viri); - } - // The virus pool should be empty now. -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// triangulate() Triangulate a PSLG into a CDT. // -// // -// A Planar Straight Line Graph (PSLG) P is actually a 2D polygonal region, // -// possibly contains holes, segments and vertices in its interior. P is tri- // -// angulated into a set of _subfaces_ forming a CDT of P. // -// // -// The vertices and segments of P are found in 'ptlist' and 'conlist', resp- // -// ectively. 'holelist' contains a list of hole points. 'shmark' will be set // -// to all subfaces of P. // -// // -// The CDT is created directly in the pools 'subfaces' and 'subsegs'. It can // -// be retrived by a broadth-first searching starting from 'dummysh[0]'(debug // -// function 'outsurfmesh()' does it). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::triangulate(int shmark, REAL eps, list* ptlist, list* conlist, - int holes, REAL* holelist, memorypool* viri, queue* flipqueue) -{ - face newsh; - point *cons; - int i; - - if (b->verbose > 1) { - printf(" %d vertices, %d segments", ptlist->len(), conlist->len()); - if (holes > 0) { - printf(", %d holes", holes); - } - printf(", shmark: %d.\n", shmark); - } - - // Create the DT of V by the 2D incremental flip algorithm. - incrflipdelaunaysub(shmark, eps, ptlist, holes, holelist, flipqueue); - // Recover boundary edges. - if (ptlist->len() > 3) { - // Insert segments into the DT. - for (i = 0; i < conlist->len(); i++) { - cons = (point *)(* conlist)[i]; - recoversegment(cons[0], cons[1], flipqueue); - } - // Carve holes and concavities. - carveholessub(holes, holelist, viri); - } else if (ptlist->len() == 3) { - // Insert 3 segments directly. - newsh.sh = dummysh; - newsh.shver = 0; - spivotself(newsh); - for (i = 0; i < 3; i++) { - insertsubseg(&newsh); - senextself(newsh); - } - } else if (ptlist->len() == 2) { - // This facet is actually a segment. It is not support by the mesh data - // strcuture. Hence the segment will not be maintained in the mesh. - // However, during segment recovery, the segment can be processed. - cons = (point *)(* conlist)[0]; - makeshellface(subsegs, &newsh); - setsorg(newsh, cons[0]); - setsdest(newsh, cons[1]); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// retrievenewsubs() Retrieve newly created subfaces. // -// // -// The new subfaces created by triangulate() can be found by a broadth-first // -// searching starting from 'dummysh[0]'. // -// // -// 'newshlist' (empty on input) returns the retrieved subfaces. Each edge on // -// the hull is bound to 'dummysh' and protected by a segment. If 'removeseg' // -// is TRUE, the segment is removed. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::retrievenewsubs(list* newshlist, bool removeseg) -{ - face startsh, neighsh; - face deadseg; - int i, j; - - // The first new subface is found at dummysh[0]. - startsh.sh = dummysh; - startsh.shver = 0; - spivotself(startsh); - assert(startsh.sh != dummysh); - sinfect(startsh); - newshlist->append(&startsh); - - // Find the rest of new subfaces by a broadth-first searching. - for (i = 0; i < newshlist->len(); i++) { - // Get a new subface s. - startsh = * (face *)(* newshlist)[i]; - for (j = 0; j < 3; j++) { - spivot(startsh, neighsh); - if (neighsh.sh != dummysh) { - if (!sinfected(neighsh)) { - // Discovered a new subface. - sinfect(neighsh); - newshlist->append(&neighsh); - } - } else { - // Found a boundary edge. - if (removeseg) { - // This side of s may be protected by a segment. - sspivot(startsh, deadseg); - if (deadseg.sh != dummysh) { - // Detach it from s. - ssdissolve(startsh); - // Delete the segment. - shellfacedealloc(subsegs, deadseg.sh); - } - } - } - senextself(startsh); - } - } - for (i = 0; i < newshlist->len(); i++) { - startsh = * (face *)(* newshlist)[i]; - suninfect(startsh); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unifysegments() Unify identical segments and build facet connections. // -// // -// After creating the surface mesh. Each facet has its own segments. There // -// are duplicated segments between adjacent facets. This routine has three // -// purposes: // -// (1) identify the set of segments which have the same endpoints and // -// unify them into one segment, remove redundant ones; // -// (2) create the face rings of the unified segments, hence setup the // -// connections between facets; and // -// (3) set a unique marker (1-based) for each segment. // -// On finish, each segment is unique and the face ring around it (right-hand // -// rule) is constructed. The connections between facets-facets are setup. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unifysegments() -{ - list *sfacelist; - shellface **facesperverlist; - face subsegloop, testseg; - face sface, sface1, sface2; - point torg, tdest; - REAL da1, da2; - int *idx2facelist; - int segmarker; - int idx, k, m; - - if (b->verbose > 0) { - printf(" Unifying segments.\n"); - } - - // Compute a mapping from indices of vertices to subfaces. - makesubfacemap(idx2facelist, facesperverlist); - // Initialize 'sfacelist' for constructing the face link of each segment. - sfacelist = new list(sizeof(face), NULL); - - segmarker = 1; - subsegs->traversalinit(); - subsegloop.sh = shellfacetraverse(subsegs); - while (subsegloop.sh != (shellface *) NULL) { - subsegloop.shver = 0; // For sure. - torg = sorg(subsegloop); - tdest = sdest(subsegloop); - idx = pointmark(torg) - in->firstnumber; - // Loop through the set of subfaces containing 'torg'. Get all the - // subfaces containing the edge (torg, tdest). Save and order them - // in 'sfacelist', the ordering is defined by the right-hand rule - // with thumb points from torg to tdest. - for (k = idx2facelist[idx]; k < idx2facelist[idx + 1]; k++) { - sface.sh = facesperverlist[k]; - sface.shver = 0; - // sface may be died due to the removing of duplicated subfaces. - if (!isdead(&sface) && isfacehasedge(&sface, torg, tdest)) { - // 'sface' contains this segment. - findedge(&sface, torg, tdest); - // Save it in 'sfacelist'. - if (sfacelist->len() < 2) { - sfacelist->append(&sface); - } else { - for (m = 0; m < sfacelist->len() - 1; m++) { - sface1 = * (face *)(* sfacelist)[m]; - sface2 = * (face *)(* sfacelist)[m + 1]; - da1 = facedihedral(torg, tdest, sapex(sface1), sapex(sface)); - da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2)); - if (da1 < da2) { - break; // Insert it after m. - } - } - sfacelist->insert(m + 1, &sface); - } - } - } - if (b->verbose > 1) { - printf(" Identifying %d segments of (%d %d).\n", sfacelist->len(), - pointmark(torg), pointmark(tdest)); - } - // Set the connection between this segment and faces containing it, - // at the same time, remove redundant segments. - for (k = 0; k < sfacelist->len(); k++) { - sface = *(face *)(* sfacelist)[k]; - sspivot(sface, testseg); - // If 'testseg' is not 'subsegloop', it is a redundant segment that - // needs be removed. BE CAREFUL it may already be removed. Do not - // remove it twice, i.e., do test 'isdead()' together. - if ((testseg.sh != subsegloop.sh) && !isdead(&testseg)) { - shellfacedealloc(subsegs, testseg.sh); - } - // 'ssbond' bonds the subface and the segment together, and dissloves - // the old bond as well. - ssbond(sface, subsegloop); - } - // Set connection between these faces. - sface = *(face *)(* sfacelist)[0]; - for (k = 1; k <= sfacelist->len(); k++) { - if (k < sfacelist->len()) { - sface1 = *(face *)(* sfacelist)[k]; - } else { - sface1 = *(face *)(* sfacelist)[0]; // Form a face loop. - } - /* - // Check if these two subfaces are the same. It is possible when user - // defines one facet (or polygon) two or more times. If they are, - // they should not be bonded together, instead of that, one of them - // should be delete from the surface mesh. - if ((sfacelist->len() > 1) && sapex(sface) == sapex(sface1)) { - // They are duplicated faces. - if (b->verbose > 0) { - printf(" A duplicated subface (%d, %d, %d) is removed.\n", - pointmark(torg), pointmark(tdest), pointmark(sapex(sface))); - } - if (k == sfacelist->len()) { - // 'sface' is the last face, however, it is same as the first one. - // In order to form the ring, we have to let the second last - // face bond to the first one 'sface1'. - shellfacedealloc(subfaces, sface.sh); - assert(sfacelist->len() >= 2); - assert(k == sfacelist->len()); - sface = *(face *)(* sfacelist)[k - 2]; - } else { - // 'sface1' is in the middle and may be the last one. - shellfacedealloc(subfaces, sface1.sh); - // Skip this face and go to the next one. - continue; - } - } - */ - if (b->verbose > 2) { - printf(" Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n", - pointmark(torg), pointmark(tdest), pointmark(sapex(sface)), - pointmark(torg), pointmark(tdest), pointmark(sapex(sface1))); - } - sbond1(sface, sface1); - sface = sface1; - } - // Set the unique segment marker into the unified segment. - setshellmark(subsegloop, segmarker); - // Increase the marker. - segmarker++; - // Clear the working list. - sfacelist->clear(); - subsegloop.sh = shellfacetraverse(subsegs); - } - - delete [] idx2facelist; - delete [] facesperverlist; - delete sfacelist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// mergefacets() Merge adjacent facets to be one facet if they are // -// coplanar and have the same boundary marker. // -// // -// Segments between two merged facets will be removed from the mesh. If all // -// segments around a vertex have been removed, change its vertex type to be // -// FREESUBVERTEX. Edge flips will be performed to ensure the Delaunayness of // -// the triangulation of merged facets. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::mergefacets(queue* flipqueue) -{ - face parentsh, neighsh, neineighsh; - face segloop; - point eorg, edest; - REAL ori; - bool mergeflag, pbcflag; - int* segspernodelist; - int fidx1, fidx2; - int i, j; - - if (b->verbose > 0) { - printf(" Merging coplanar facets.\n"); - } - // Create and initialize 'segspernodelist'. - segspernodelist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) segspernodelist[i] = 0; - - // Loop the segments, counter the number of segments sharing each vertex. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - // Increment the number of sharing segments for each endpoint. - for (i = 0; i < 2; i++) { - j = pointmark((point) segloop.sh[3 + i]); - segspernodelist[j]++; - } - segloop.sh = shellfacetraverse(subsegs); - } - - // Loop the segments, find out dead segments. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - eorg = sorg(segloop); - edest = sdest(segloop); - spivot(segloop, parentsh); - spivot(parentsh, neighsh); - spivot(neighsh, neineighsh); - if (parentsh.sh != neighsh.sh && parentsh.sh == neineighsh.sh) { - // Exactly two subfaces at this segment. - fidx1 = shellmark(parentsh) - 1; - fidx2 = shellmark(neighsh) - 1; - pbcflag = false; - if (checkpbcs) { - pbcflag = (shellpbcgroup(parentsh) >= 0) - || (shellpbcgroup(neighsh) >= 0); - } - // Possibly merge them if they are not in the same facet. - if ((fidx1 != fidx2) && !pbcflag) { - // Test if they are coplanar. - ori = orient3d(eorg, edest, sapex(parentsh), sapex(neighsh)); - if (ori != 0.0) { - if (iscoplanar(eorg, edest, sapex(parentsh), sapex(neighsh), ori, - b->epsilon)) { - ori = 0.0; // They are assumed as coplanar. - } - } - if (ori == 0.0) { - mergeflag = (in->facetmarkerlist == (int *) NULL || - in->facetmarkerlist[fidx1] == in->facetmarkerlist[fidx2]); - if (mergeflag) { - // This segment becomes dead. - if (b->verbose > 1) { - printf(" Removing segment (%d, %d).\n", pointmark(eorg), - pointmark(edest)); - } - ssdissolve(parentsh); - ssdissolve(neighsh); - shellfacedealloc(subsegs, segloop.sh); - j = pointmark(eorg); - segspernodelist[j]--; - if (segspernodelist[j] == 0) { - setpointtype(eorg, FREESUBVERTEX); - } - j = pointmark(edest); - segspernodelist[j]--; - if (segspernodelist[j] == 0) { - setpointtype(edest, FREESUBVERTEX); - } - // Add 'parentsh' to queue checking for flip. - enqueueflipedge(parentsh, flipqueue); - } - } - } - } - segloop.sh = shellfacetraverse(subsegs); - } - - if (!flipqueue->empty()) { - // Restore the Delaunay property in the facet triangulation. - flipsub(flipqueue); - } - - delete [] segspernodelist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// meshsurface() Create the surface mesh of a PLC. // -// // -// Let X be the PLC, the surface mesh S of X consists of triangulated facets.// -// S is created mainly in the following steps: // -// // -// (1) Form the CDT of each facet of X separately (by routine triangulate()).// -// After it is done, the subfaces of each facet are connected to each other, // -// however there is no connection between facets yet. Notice each facet has // -// its own segments, some of them are duplicated. // -// // -// (2) Remove the redundant segments created in step (1) (by routine unify- // -// segment()). The subface ring of each segment is created, the connection // -// between facets are established as well. // -// // -// The return value indicates the number of segments of X. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::meshsurface() -{ - list *ptlist, *conlist; - queue *flipqueue; - tetgenio::facet *f; - tetgenio::polygon *p; - memorypool *viri; - point *idx2verlist; - point tstart, tend, *cons; - int *worklist; - int end1, end2; - int shmark, i, j; - - if (!b->quiet) { - printf("Creating surface mesh.\n"); - } - - // Compute a mapping from indices to points. - makeindex2pointmap(idx2verlist); - // Compute a mapping from points to tets for computing abovepoints. - makepoint2tetmap(); - // Initialize 'facetabovepointarray'. - facetabovepointarray = new point[in->numberoffacets + 1]; - for (i = 0; i < in->numberoffacets + 1; i++) { - facetabovepointarray[i] = (point) NULL; - } - if (checkpbcs) { - // Initialize the global array 'subpbcgrouptable'. - createsubpbcgrouptable(); - } - - // Initialize working lists. - viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0); - flipqueue = new queue(sizeof(badface)); - ptlist = new list(sizeof(point *), NULL, 256); - conlist = new list(sizeof(point *) * 2, NULL, 256); - worklist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) worklist[i] = 0; - - // Loop the facet list, triangulate each facet. On finish, all subfaces - // are in 'subfaces', all segments are in 'subsegs'. Notice: there're - // redundant segments. Remember: All facet indices count from 1. - for (shmark = 1; shmark <= in->numberoffacets; shmark++) { - // Get a facet F. - f = &in->facetlist[shmark - 1]; - - // Process the duplicated points first, they are marked with type - // DUPLICATEDVERTEX by incrflipdelaunay(). Let p and q are dup. - // and the index of p is larger than q's, p is substituted by q. - // In a STL mesh, duplicated points are implicitly included. - if ((b->object == tetgenbehavior::STL) || dupverts) { - // Loop all polygons of this facet. - for (i = 0; i < f->numberofpolygons; i++) { - p = &(f->polygonlist[i]); - // Loop other vertices of this polygon. - for (j = 0; j < p->numberofvertices; j++) { - end1 = p->vertexlist[j]; - tstart = idx2verlist[end1 - in->firstnumber]; - if (pointtype(tstart) == DUPLICATEDVERTEX) { - // Reset the index of vertex-j. - tend = point2ppt(tstart); - end2 = pointmark(tend); - p->vertexlist[j] = end2; - } - } - } - } - - // Loop polygons of F, get the set V of vertices and S of segments. - for (i = 0; i < f->numberofpolygons; i++) { - // Get a polygon. - p = &(f->polygonlist[i]); - // Get the first vertex. - end1 = p->vertexlist[0]; - if ((end1 < in->firstnumber) || - (end1 >= in->firstnumber + in->numberofpoints)) { - if (!b->quiet) { - printf("Warning: Invalid the 1st vertex %d of polygon", end1); - printf(" %d in facet %d.\n", i + 1, shmark); - } - continue; // Skip this polygon. - } - tstart = idx2verlist[end1 - in->firstnumber]; - // Add tstart to V if it haven't been added yet. - if (worklist[end1] == 0) { - ptlist->append(&tstart); - worklist[end1] = 1; - } - // 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 - in->firstnumber]; - // Add tstart to V if it haven't been added yet. - if (worklist[end2] == 0) { - ptlist->append(&tend); - worklist[end2] = 1; - } - // Save the segment in S (conlist). - cons = (point *) conlist->append(NULL); - cons[0] = tstart; - cons[1] = tend; - // Set the start for next continuous segment. - end1 = end2; - tstart = tend; - } else { - // Two identical vertices represent an isolated vertex of F. - 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->len(); i++) { - tstart = * (point *)(* ptlist)[i]; - end1 = pointmark(tstart); - assert(worklist[end1] == 1); - worklist[end1] = 0; - } - - // Create a CDT of F. - triangulate(shmark, b->epsilon * 1e+2, ptlist, conlist, f->numberofholes, - f->holelist, viri, flipqueue); - // Clear working lists. - ptlist->clear(); - conlist->clear(); - viri->restart(); - } - - // Unify segments in 'subsegs', remove redundant segments. Face links - // of segments are also built. - unifysegments(); - // Remember the number of input segments (for output). - insegments = subsegs->items; - - if (checkpbcs) { - // Create the global array 'segpbcgrouptable'. - createsegpbcgrouptable(); - } - - if (b->object == tetgenbehavior::STL) { - // Remove redundant vertices (for .stl input mesh). - jettisonnodes(); - } - - if (!b->nomerge && !b->nobisect && !checkpbcs) { - // No '-M' switch - merge adjacent facets if they are coplanar. - mergefacets(flipqueue); - } - - delete [] idx2verlist; - delete [] worklist; - delete ptlist; - delete conlist; - delete flipqueue; - delete viri; - - return subsegs->items; -} - -// -// End of surface triangulation routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// interecursive() Recursively do intersection test on a set of triangles.// -// // -// Recursively split the set 'subfacearray' of subfaces into two sets using // -// a cut plane parallel to x-, or, y-, or z-axies. The split criteria are // -// follows. Assume the cut plane is H, and H+ denotes the left halfspace of // -// H, and H- denotes the right halfspace of H; and s be a subface: // -// // -// (1) If all points of s lie at H+, put it into left array; // -// (2) If all points of s lie at H-, put it into right array; // -// (3) If some points of s lie at H+ and some of lie at H-, or some // -// points lie on H, put it into both arraies. // -// // -// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis // -// if axis == '2'. If current cut plane is parallel to the x-axis, the next // -// one will be parallel to y-axis, and the next one after the next is z-axis,// -// and then alternately return back to x-axis. // -// // -// Stop splitting when the number of triangles of the input array is not // -// decreased anymore. Do tests on the current set. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh:: -interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin, - REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax, - int* internum) -{ - shellface **leftarray, **rightarray; - face sface1, sface2; - point p1, p2, p3; - point p4, p5, p6; - enum interresult intersect; - REAL split; - bool toleft, toright; - int leftsize, rightsize; - int i, j; - - if (b->verbose > 1) { - printf(" Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n", - arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax, - axis == 0 ? "x" : (axis == 1 ? "y" : "z")); - } - - leftarray = new shellface*[arraysize]; - if (leftarray == NULL) { - printf("Error in interecursive(): Insufficient memory.\n"); - terminatetetgen(1); - } - rightarray = new shellface*[arraysize]; - if (rightarray == NULL) { - printf("Error in interecursive(): Insufficient memory.\n"); - terminatetetgen(1); - } - leftsize = rightsize = 0; - - if (axis == 0) { - // Split along x-axis. - split = 0.5 * (bxmin + bxmax); - } else if (axis == 1) { - // Split along y-axis. - split = 0.5 * (bymin + bymax); - } else { - // Split along z-axis. - split = 0.5 * (bzmin + bzmax); - } - - for (i = 0; i < arraysize; i++) { - sface1.sh = subfacearray[i]; - p1 = (point) sface1.sh[3]; - p2 = (point) sface1.sh[4]; - p3 = (point) sface1.sh[5]; - toleft = toright = false; - if (p1[axis] < split) { - toleft = true; - if (p2[axis] >= split || p3[axis] >= split) { - toright = true; - } - } else if (p1[axis] > split) { - toright = true; - if (p2[axis] <= split || p3[axis] <= split) { - toleft = true; - } - } else { - // p1[axis] == split; - toleft = true; - toright = true; - } - // At least one is true; -#ifdef SELF_CHECK - assert(!(toleft == false && toright == false)); -#endif - if (toleft) { - leftarray[leftsize] = sface1.sh; - leftsize++; - } - if (toright) { - rightarray[rightsize] = sface1.sh; - rightsize++; - } - } - - if (leftsize < arraysize && rightsize < arraysize) { - // Continue to partition the input set. Now 'subfacearray' has been - // split into two sets, it's memory can be freed. 'leftarray' and - // 'rightarray' will be freed in the next recursive (after they're - // partitioned again or performing tests). - delete [] subfacearray; - // Continue to split these two sets. - if (axis == 0) { - interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax, - bzmin, bzmax, internum); - interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax, - bzmin, bzmax, internum); - } else if (axis == 1) { - interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split, - bzmin, bzmax, internum); - interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax, - bzmin, bzmax, internum); - } else { - interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax, - bzmin, split, internum); - interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax, - split, bzmax, internum); - } - } else { - if (b->verbose > 1) { - printf(" Checking intersecting faces.\n"); - } - // Perform a brute-force compare on the set. - for (i = 0; i < arraysize; i++) { - sface1.sh = subfacearray[i]; - p1 = (point) sface1.sh[3]; - p2 = (point) sface1.sh[4]; - p3 = (point) sface1.sh[5]; - for (j = i + 1; j < arraysize; j++) { - sface2.sh = subfacearray[j]; - p4 = (point) sface2.sh[3]; - p5 = (point) sface2.sh[4]; - p6 = (point) sface2.sh[5]; - intersect = tri_tri_inter(p1, p2, p3, p4, p5, p6); - if (intersect == INTERSECT || intersect == SHAREFACE) { - if (!b->quiet) { - if (intersect == INTERSECT) { - printf(" Facet #%d intersects facet #%d at triangles:\n", - shellmark(sface1), shellmark(sface2)); - printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", - pointmark(p1), pointmark(p2), pointmark(p3), - pointmark(p4), pointmark(p5), pointmark(p6)); - } else { - printf(" Facet #%d duplicates facet #%d at triangle:\n", - shellmark(sface1), shellmark(sface2)); - printf(" (%4d, %4d, %4d)\n", pointmark(p1), pointmark(p2), - pointmark(p3)); - } - } - // Increase the number of intersecting pairs. - (*internum)++; - // Infect these two faces (although they may already be infected). - sinfect(sface1); - sinfect(sface2); - } - } - } - // Don't forget to free all three arrays. No further partition. - delete [] leftarray; - delete [] rightarray; - delete [] subfacearray; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// detectinterfaces() Detect intersecting triangles. // -// // -// Given a set of triangles, find the pairs of intersecting triangles from // -// them. Here the set of triangles is in 'subfaces' which is a surface mesh // -// of a PLC (.poly or .smesh). // -// // -// To detect whether two triangles are intersecting is done by the routine // -// 'tri_tri_inter()'. The algorithm for the test is very simple and stable. // -// It is based on geometric orientation test which uses exact arithmetics. // -// // -// Use divide-and-conquer algorithm for reducing the number of intersection // -// tests. Start from the bounding box of the input point set, recursively // -// partition the box into smaller boxes, until the number of triangles in a // -// box is not decreased anymore. Then perform triangle-triangle tests on the // -// remaining set of triangles. The memory allocated in the input set is // -// freed immediately after it has been partitioned into two arrays. So it // -// can be re-used for the consequent partitions. // -// // -// On return, the pool 'subfaces' will be cleared, and only the intersecting // -// triangles remain for output (to a .face file). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::detectinterfaces() -{ - shellface **subfacearray; - face shloop; - int internum; - int i; - - if (!b->quiet) { - printf("Detecting intersecting facets.\n"); - } - - // Construct a map from indices to subfaces; - subfacearray = new shellface*[subfaces->items]; - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - i = 0; - while (shloop.sh != (shellface *) NULL) { - subfacearray[i] = shloop.sh; - shloop.sh = shellfacetraverse(subfaces); - i++; - } - - internum = 0; - // Recursively split the set of triangles into two sets using a cut plane - // parallel to x-, or, y-, or z-axies. Stop splitting when the number - // of subfaces is not decreasing anymore. Do tests on the current set. - interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax, - zmin, zmax, &internum); - - if (!b->quiet) { - if (internum > 0) { - printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum); - } else { - printf("\nNo faces are intersecting.\n\n"); - } - } - - if (internum > 0) { - // Traverse all subfaces, deallocate those have not been infected (they - // are not intersecting faces). Uninfect those have been infected. - // After this loop, only intersecting faces remain. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - if (sinfected(shloop)) { - suninfect(shloop); - } else { - shellfacedealloc(subfaces, shloop.sh); - } - shloop.sh = shellfacetraverse(subfaces); - } - } else { - // Deallocate all subfaces. - subfaces->restart(); - } -} - -// -// Begin of periodic boundary condition routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// createsubpbcgrouptable() Create the 'subpbcgrouptable'. // -// // -// Allocate the memory for 'subpbcgrouptable'. Each entry i (a pbcdata) of // -// the table represents a pbcgroup. Most of the fields of a group-i are set // -// in this routine. 'fmark[0]', 'fmark[1]', and 'transmat[0]' are directly // -// copied from the corresponding data of 'in->numberofpbcgroups'. 'transmat // -// [1]' is calculated as the inverse matrix of 'transmat[0]'. 'ss[0]' and // -// 'ss[1]' are initilized be 'dummysh'. They are set in 'trangulatefacet()' // -// (when -p is in use) or 'reconstructmesh()' (when -r is in use). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::createsubpbcgrouptable() -{ - tetgenio::pbcgroup *pg; - pbcdata *pd; - REAL A[4][4], rhs[4], D; - int indx[4]; - int i, j, k; - - subpbcgrouptable = new pbcdata[in->numberofpbcgroups]; - for (i = 0; i < in->numberofpbcgroups; i++) { - pg = &(in->pbcgrouplist[i]); - pd = &(subpbcgrouptable[i]); - // Copy data from pg to pd. - pd->fmark[0] = pg->fmark1; - pd->fmark[1] = pg->fmark2; - // Initialize array 'pd->ss'. - pd->ss[0].sh = dummysh; - pd->ss[1].sh = dummysh; - // Copy the transform matrix from pg to pd->transmat[0]. - for (j = 0; j < 4; j++) { - for (k = 0; k < 4; k++) { - pd->transmat[0][j][k] = pg->transmat[j][k]; - // Prepare for inverting the matrix. - A[j][k] = pg->transmat[j][k]; - } - } - // Calculate the inverse matrix (pd->transmat[1]) of pd->transmat[0]. - lu_decmp(A, 4, indx, &D, 0); - for (j = 0; j < 4; j++) { - for (k = 0; k < 4; k++) rhs[k] = 0.0; - rhs[j] = 1.0; - lu_solve(A, 4, indx, rhs, 0); - for (k = 0; k < 4; k++) pd->transmat[1][k][j] = rhs[k]; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsubpbcgroup() Get the pbcgroup of a subface. // -// // -// 'pbcsub' has pbc defined. Its pbcgroup is returned in 'pd'. In addition, // -// 'f1' (0 or 1) indicates the position of 'pbcsub' in 'pd'; 'f2' (= 1 - f1) // -// is the position where the symmetric subface of 'pbcsub' is found. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getsubpbcgroup(face* pbcsub, pbcdata** pd, int *f1, int *f2) -{ - int groupid, fmark, idx; - - groupid = shellpbcgroup(*pbcsub); - *pd = &subpbcgrouptable[groupid]; - - // Get the facet index (1 - based). - idx = shellmark(*pbcsub); - // Get the facet marker from array (0 - based). - fmark = in->facetmarkerlist[idx - 1]; - if ((*pd)->fmark[0] == fmark) { - *f1 = 0; - } else { -#ifdef SELF_CHECK - assert((*pd)->fmark[1] == fmark); -#endif - *f1 = 1; - } - *f2 = 1 - (*f1); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsubpbcsympoint() Compute the symmetric point for a subface point. // -// // -// 'newpoint' lies on 'splitsub'. This routine calculates a 'sympoint' which // -// locates on 'symsplitsub' and symmtric to 'newpoint'. Return the location // -// of sympoint wrt. symsplitsub. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh:: getsubpbcsympoint(point newpoint, - face* splitsub, point sympoint, face* symsplitsub) -{ - pbcdata *pd; - face subloop; - point pa, pb, pc; - enum locateresult symloc; - REAL ori; - int f1, f2, i; - - // Get the pbcgroup of 'splitsub'. - getsubpbcgroup(splitsub, &pd, &f1, &f2); - - // Transform newpoint from f1 -> f2. - for (i = 0; i < 3; i++) { - sympoint[i] = pd->transmat[f1][i][0] * newpoint[0] - + pd->transmat[f1][i][1] * newpoint[1] - + pd->transmat[f1][i][2] * newpoint[2] - + pd->transmat[f1][i][3] * 1.0; - } - // Locate sympoint in f2. - symloc = OUTSIDE; - *symsplitsub = pd->ss[f2]; - // Is the stored subface valid? Hole removal may delete the subface. - if ((symsplitsub->sh != dummysh) && !isdead(symsplitsub)) { - // 'symsplitsub' should lie on the symmetric facet. Check it. - i = shellmark(*symsplitsub); - if (in->facetmarkerlist[i - 1] == pd->fmark[f2]) { - // 'symsplitsub' has the symmetric boundary marker. - pa = sorg(*symsplitsub); - pb = sdest(*symsplitsub); - pc = sapex(*symsplitsub); - // Test if they are (nearly) coplanar. Some facets may have the - // same boundary marker but not coplanar with this point. - ori = orient3d(pa, pb, pc, sympoint); - if (iscoplanar(pa, pb, pc, sympoint, ori, b->epsilon * 1e+2)) { - // Locate sympoint in facet. Don't stop at subsegment. - abovepoint = facetabovepointarray[shellmark(*symsplitsub)]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(symsplitsub); - } - symloc = locatesub(sympoint, symsplitsub, 0, b->epsilon * 1e+2); - } - } - } - if (symloc == OUTSIDE) { - // Do a brute-force searching for the symmetric subface. - REAL epspp = b->epsilon * 1e+2; - int lcount = 0; - do { - // Locate sympoint in the pool of subfaces (with fmark pd->fmark[f2]). - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - i = shellmark(subloop); - if (in->facetmarkerlist[i - 1] == pd->fmark[f2]) { - // Found a facet have the symmetric boundary marker. - pa = sorg(subloop); - pb = sdest(subloop); - pc = sapex(subloop); - // Test if they are (nearly) coplanar. Some facets may have the - // same boundary marker but not coplanar with this point. - ori = orient3d(pa, pb, pc, sympoint); - if (iscoplanar(pa, pb, pc, sympoint, ori, epspp)) { - // Test if sympoint is (nearly) inside this facet. - // Get the abovepoint of the facet. - abovepoint = facetabovepointarray[shellmark(subloop)]; - // Do we need to calculate the abovepoint? - if (abovepoint == (point) NULL) { - getfacetabovepoint(&subloop); - } - // subloop is on the facet, search sympoint. - symloc = locatesub(sympoint, &subloop, 0, epspp); - if (symloc != OUTSIDE) break; - } - } - subloop.sh = shellfacetraverse(subfaces); - } - lcount++; - epspp *= 10.0; - } while ((symloc == OUTSIDE) && (lcount < 3)); -#ifdef SELF_CHECK - // sympoint should be inside the facet. - assert(symloc != OUTSIDE); -#endif - // Set the returning subface. - *symsplitsub = subloop; - // Update the stored subface for next searching. - pd->ss[f2] = *symsplitsub; - } - - return adjustlocatesub(sympoint, symsplitsub, symloc, b->epsilon); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// createsegpbcgrouptable() Create the 'segpbcgrouptable'. // -// // -// Each segment may belong to more than one pbcgroups. For example, segment // -// ab may need to be symmteric to both segments cd, and ef, then ab and cd, // -// cd and ef, ef and ab form three pbcgroups. // -// // -// 'segpbcgrouptable' is implemented as a list of pbcdatas. Each item i is // -// a pbcgroup. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::createsegpbcgrouptable() -{ - shellface** segsperverlist; - pbcdata *pd, *ppd, pd1, pd2; - face segloop, symseg; - face startsh, spinsh, symsh; - point pa, pb, syma, symb; - enum locateresult symloc; - REAL testpt[3], sympt[3]; - bool inflag; - int *idx2seglist; - int segid1, segid2; - int f1, f2; - int i, j, k, l; - - // Allocate memory for 'subpbcgrouptable'. - segpbcgrouptable = new list(sizeof(pbcdata), NULL, 256); - - if (b->refine) { - // Create a point-to-seg map for quickly finding PBC seg pairs. - makesegmentmap(idx2seglist, segsperverlist); - } - - // Loop through the segment list. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - // Loop the subface ring of segloop ab. - pa = sorg(segloop); - pb = sdest(segloop); - segid1 = shellmark(segloop); - spivot(segloop, startsh); - spinsh = startsh; - do { - // Adjust spinsh be edge ab. - if (sorg(spinsh) != pa) { - sesymself(spinsh); - } - // Does spinsh belong to a pbcgroup? - if (shellpbcgroup(spinsh) != -1) { - // Yes! There exists a segment cd. ab and cd form a pbcgroup. - if (b->refine) { - getsubpbcgroup(&spinsh, &pd, &f1, &f2); - // Transform pa from f1 -> f2. - for (i = 0; i < 3; i++) { - sympt[i] = pd->transmat[f1][i][0] * pa[0] - + pd->transmat[f1][i][1] * pa[1] - + pd->transmat[f1][i][2] * pa[2] - + pd->transmat[f1][i][3] * 1.0; - } - syma = point2pbcpt(pa); - // Is 'sympt == syma'? - if (distance(sympt, syma) > (longest * b->epsilon)) { - // No. Search the symmetric vertex of pa. - symloc = getsubpbcsympoint(pa, &spinsh, sympt, &symsh); - syma = sorg(symsh); - if (symloc != ONVERTEX) { - // Do a brute force search. Not done yet. - assert(0); - } - } - // Transform pb from f1 -> f2. - for (i = 0; i < 3; i++) { - sympt[i] = pd->transmat[f1][i][0] * pb[0] - + pd->transmat[f1][i][1] * pb[1] - + pd->transmat[f1][i][2] * pb[2] - + pd->transmat[f1][i][3] * 1.0; - } - // Search sym subface from the point-to-subface map. - symseg.shver = 0; - j = pointmark(syma) - in->firstnumber; - for (i = idx2seglist[j]; i < idx2seglist[j + 1]; i++) { - symseg.sh = segsperverlist[i]; - if (sorg(symseg) == syma) symb = sdest(symseg); - else symb = sorg(symseg); - if (distance(sympt, symb) <= (longest * b->epsilon)) break; - } - assert(i < idx2seglist[j + 1]); - } else { - // 'testpt' is the midpoint of ab used to find cd. - for (i = 0; i < 3; i++) testpt[i] = 0.5 * (pa[i] + pb[i]); - symloc = getsubpbcsympoint(testpt, &spinsh, sympt, &symsh); -#ifdef SELF_CHECK - assert(symloc == ONEDGE); -#endif - sspivot(symsh, symseg); - } -#ifdef SELF_CHECK - assert(symseg.sh != dummysh); -#endif - // Check whether this group has already been created in list. - segid2 = shellmark(symseg); - inflag = false; - for (i = 0; i < segpbcgrouptable->len() && !inflag; i++) { - pd = (pbcdata *)(* segpbcgrouptable)[i]; - if (pd->segid[0] == segid1) { - if (pd->segid[1] == segid2) inflag = true; - } else if (pd->segid[0] == segid2) { - if (pd->segid[1] == segid1) inflag = true; - } - } - if (!inflag) { - // Create a segment pbcgroup in list for ab and cd. - pd = (pbcdata *) segpbcgrouptable->append(NULL); - // Save the markers of ab and cd. - pd->segid[0] = segid1; - pd->segid[1] = segid2; - // Save the handles of ab and cd. - pd->ss[0] = segloop; - pd->ss[1] = symseg; - // Find the map from ab to cd. - getsubpbcgroup(&spinsh, &ppd, &f1, &f2); - pd->fmark[0] = ppd->fmark[f1]; - pd->fmark[1] = ppd->fmark[f2]; - // Set the map from ab to cd. - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - pd->transmat[0][i][j] = ppd->transmat[f1][i][j]; - } - } - // Set the map from cd to ab. - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - pd->transmat[1][i][j] = ppd->transmat[f2][i][j]; - } - } - } - } - // Go to the next subface in the ring of ab. - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - segloop.sh = shellfacetraverse(subsegs); - } - - if (b->refine) { - delete [] segsperverlist; - delete [] idx2seglist; - } - - // Create the indirect segment pbcgroups. - // Bug-fixed (08 Sept. 2006). The total size of 'segpbcgrouptable' may get - // increased. Do not use pointers for 'pd1' and 'pd2'. The addresses may - // be invaild after realloc(). - for (i = 0; i < segpbcgrouptable->len(); i++) { - pd1 = * (pbcdata *)(* segpbcgrouptable)[i]; - for (f1 = 0; f1 < 2; f1++) { - // Search for a group (except i) contains pd1.segid[f1]. - for (j = 0; j < segpbcgrouptable->len(); j++) { - if (j == i) continue; - pd2 = * (pbcdata *)(* segpbcgrouptable)[j]; - f2 = -1; - if (pd1.segid[f1] == pd2.segid[0]) { - f2 = 0; - } else if (pd1.segid[f1] == pd2.segid[1]) { - f2 = 1; - } - if (f2 != -1) { -#ifdef SELF_CHECK - assert(pd1.segid[f1] == pd2.segid[f2]); -#endif - segid1 = pd1.segid[1 - f1]; - segid2 = pd2.segid[1 - f2]; - // Search for the existence of segment pbcgroup (segid1, segid2). - inflag = false; - for (k = 0; k < segpbcgrouptable->len() && !inflag; k++) { - pd = (pbcdata *)(* segpbcgrouptable)[k]; - if (pd->segid[0] == segid1) { - if (pd->segid[1] == segid2) inflag = true; - } else if (pd->segid[0] == segid2) { - if (pd->segid[1] == segid1) inflag = true; - } - } - if (!inflag) { - pd = (pbcdata *) segpbcgrouptable->append(NULL); - pd->segid[0] = pd1.segid[1 - f1]; - pd->segid[1] = pd2.segid[1 - f2]; - pd->ss[0] = pd1.ss[1 - f1]; - pd->ss[1] = pd2.ss[1 - f2]; - // Invalid the fmark[0] == fmark[1]. - pd->fmark[0] = pd->fmark[1] = 0; - // Translate matrix pd->transmat[0] = m2 * m1, where m1 = - // pd1.transmat[1 - f1], m2 = pd2.transmat[f2]. - for (k = 0; k < 4; k++) { - for (l = 0; l < 4; l++) { - pd->transmat[0][k][l] = pd2.transmat[f2][k][l]; - } - } - m4xm4(pd->transmat[0], pd1.transmat[1 - f1]); - // Translate matrix pd->transmat[1] = m4 * m3, where m3 = - // pd2.transmat[1 - f2], m4 = pd1.transmat[f1]. - for (k = 0; k < 4; k++) { - for (l = 0; l < 4; l++) { - pd->transmat[1][k][l] = pd1.transmat[f1][k][l]; - } - } - m4xm4(pd->transmat[1], pd2.transmat[1 - f2]); - } - } - } - } - } - - // Form a map from segment index to pbcgroup list of this segment. - idx2segpglist = new int[subsegs->items + 1]; - for (i = 0; i < subsegs->items + 1; i++) idx2segpglist[i] = 0; - // Loop through 'segpbcgrouptable', counter the number of pbcgroups of - // each segment. - for (i = 0; i < segpbcgrouptable->len(); i++) { - pd = (pbcdata *)(* segpbcgrouptable)[i]; - for (j = 0; j < 2; j++) { - k = pd->segid[j] - 1; - idx2segpglist[k]++; - } - } - // Calculate the total length of array 'segpglist'. - j = idx2segpglist[0]; - idx2segpglist[0] = 0; // Array starts from 0 element. - for (i = 0; i < subsegs->items; i++) { - k = idx2segpglist[i + 1]; - idx2segpglist[i + 1] = idx2segpglist[i] + j; - j = k; - } - // The total length is in the last unit of idx2segpglist. - segpglist = new int[idx2segpglist[i]]; - // Loop the set of pbcgroups again, set the data into segpglist. - for (i = 0; i < segpbcgrouptable->len(); i++) { - pd = (pbcdata *)(* segpbcgrouptable)[i]; - for (j = 0; j < 2; j++) { - k = pd->segid[j] - 1; - segpglist[idx2segpglist[k]] = i; - idx2segpglist[k]++; - } - } - // Contents in 'idx2segpglist' are shifted, now shift them back. - for (i = subsegs->items - 1; i >= 0; i--) { - idx2segpglist[i + 1] = idx2segpglist[i]; - } - idx2segpglist[0] = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsegpbcsympoint() Compute the symmetric point for a segment point. // -// // -// 'newpoint' lies on 'splitseg'. This routine calculates a 'sympoint' which // -// locates on 'symsplitseg' and symmtric to 'newpoint'. Return the location // -// of sympoint wrt. symsplitseg. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::locateresult tetgenmesh:: -getsegpbcsympoint(point newpoint, face* splitseg, point sympoint, - face* symsplitseg, int groupid) -{ - pbcdata *pd; - enum locateresult symloc; - int segid, f1, f2, i; - - pd = (pbcdata *)(* segpbcgrouptable)[groupid]; - segid = shellmark(*splitseg); - if (pd->segid[0] == segid) { - f1 = 0; - } else { -#ifdef SELF_CHECK - assert(pd->segid[1] == segid); -#endif - f1 = 1; - } - f2 = 1 - f1; - - // Transform newpoint from f1 -> f2. - for (i = 0; i < 3; i++) { - sympoint[i] = pd->transmat[f1][i][0] * newpoint[0] - + pd->transmat[f1][i][1] * newpoint[1] - + pd->transmat[f1][i][2] * newpoint[2] - + pd->transmat[f1][i][3] * 1.0; - } - // Locate sympoint in f2. - *symsplitseg = pd->ss[f2]; -#ifdef SELF_CHECK - assert(symsplitseg->sh != dummysh); -#endif - // Locate sympoint in facet. Stop at subsegment. - symloc = locateseg(sympoint, symsplitseg); - symloc = adjustlocateseg(sympoint, symsplitseg, symloc, b->epsilon * 1e+2); - return symloc; -} - -// -// End of periodic boundary condition routines -// - -// -// Begin of vertex perturbation routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// randgenerator() Generate a random REAL number between (0, |range|). // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::randgenerator(REAL range) -{ - REAL worknumber, result; - int expo; - - if (range == 0.0) return 0.0; - - expo = 0; - worknumber = fabs(range); - // Normalize worknumber (i.e., 1.xxxExx) - if (worknumber > 10.0) { - while (worknumber > 10.0) { - worknumber /= 10.0; - expo++; - } - } else if (worknumber < 1.0) { - while (worknumber < 1.0) { - worknumber *= 10.0; - expo--; - } - } -#ifdef SELF_CHECK - assert(worknumber >= 1.0 && worknumber <= 10.0); -#endif - - // Enlarge worknumber 1000 times. - worknumber *= 1e+3; - expo -= 3; - // Generate a randome number between (0, worknumber). - result = (double) randomnation((int) worknumber); - - // Scale result back into the original size. - if (expo > 0) { - while (expo != 0) { - result *= 10.0; - expo--; - } - } else if (expo < 0) { - while (expo != 0) { - result /= 10.0; - expo++; - } - } -#ifdef SELF_CHECK - assert((result >= 0.0) && (result <= fabs(range))); -#endif - - return result; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checksub4cocir() Test a subface to find co-circular pair of subfaces. // -// // -// 'eps' is a relative tolerance for testing approximately cospherical case. // -// Set it to zero if only exact test is desired. // -// // -// An edge(not a segment) of 'testsub' is locally degenerate if the opposite // -// vertex of the adjacent subface is cocircular with the vertices of testsub.// -// If 'once' is TRUE, operate on the edge only if the pointer 'testsub->sh' // -// is smaller than its neighbor (for each edge is considered only once). // -// // -// Return TRUE if find an edge of testsub is locally degenerate. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::checksub4cocir(face* testsub, REAL eps, bool once, - bool enqflag) -{ - badface *cocirsub; - face subloop, neighsub; - face checkseg; - point pa, pb, pc, pd; - REAL sign; - int i; - - subloop = *testsub; - subloop.shver = 0; // Keep the CCW orientation. - // Get the abovepoint of the facet. - abovepoint = facetabovepointarray[shellmark(subloop)]; - // Do we need to calculate the abovepoint? - if (abovepoint == (point) NULL) { - getfacetabovepoint(&subloop); - } - // Check the three edges of subloop. - for (i = 0; i < 3; i++) { - sspivot(subloop, checkseg); - if (checkseg.sh == dummysh) { - // It is not a segment, get the adjacent subface. - spivot(subloop, neighsub); - // assert(neighsub.sh != dummysh); - if (!once || (once && (neighsub.sh > subloop.sh))) { - pa = sorg(subloop); - pb = sdest(subloop); - pc = sapex(subloop); - pd = sapex(neighsub); - sign = insphere(pa, pb, pc, abovepoint, pd); - if ((sign != 0.0) && (eps > 0.0)) { - if (iscospheric(pa, pb, pc, abovepoint, pd, sign, eps)) sign = 0.0; - } - if (sign == 0.0) { - // It's locally degenerate! - if (enqflag && badsubfaces != (memorypool *) NULL) { - // Save it. - cocirsub = (badface *) badsubfaces->alloc(); - cocirsub->ss = subloop; - cocirsub->forg = pa; - cocirsub->fdest = pb; - cocirsub->fapex = pc; - cocirsub->foppo = pd; - setshell2badface(cocirsub->ss, cocirsub); - } - if (b->verbose > 1) { - printf(" Found set (%d, %d, %d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - } - return true; - } - } - } - senextself(subloop); - } - - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tallcocirsubs() Find all co-circular subfaces and save them in list. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tallcocirsubs(REAL eps, bool enqflag) -{ - face subloop; - - // Loop over all subfaces. - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - checksub4cocir(&subloop, eps, true, enqflag); - subloop.sh = shellfacetraverse(subfaces); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tallencsegsfsubs() Check for encroached segs from a list of subfaces. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::tallencsegsfsubs(point testpt, list* cavsublist) -{ - face startsub, checkseg; - long oldencnum; - int i, j; - - // Remember the current number of encroached segments. - oldencnum = badsubsegs->items; - - // Check segments in the list of subfaces. - for (i = 0; i < cavsublist->len(); i++) { - startsub = * (face *)(* cavsublist)[i]; - // Test all three edges of startsub. - for (j = 0; j < 3; j++) { - sspivot(startsub, checkseg); - if (checkseg.sh != dummysh) { - if (!shell2badface(checkseg)) { - checkseg4encroach(&checkseg, testpt, NULL, true); - } - } - senextself(startsub); - } - } - - return (badsubsegs->items > oldencnum); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// collectflipedges() Collect edges of split subfaces for flip checking. // -// // -// 'inspoint' is a newly inserted segment point (inserted by insertsite()). // -// 'splitseg' is one of the two split subsegments. Some subfaces may be non- // -// Delaunay since they're still not bonded to CDT. This routine collect all // -// such possible subfaces in 'flipqueue'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh:: -collectflipedges(point inspoint, face* splitseg, queue* flipqueue) -{ - face startsh, spinsh, checksh; - face nextseg; - point pa, pb; - - // Let the dest of splitseg be inspoint. - splitseg->shver = 0; - if (sdest(*splitseg) != inspoint) { - sesymself(*splitseg); - } -#ifdef SELF_CHECK - assert(sdest(*splitseg) == inspoint); -#endif - pa = sorg(*splitseg); - spivot(*splitseg, startsh); - spinsh = startsh; - do { - findedge(&spinsh, pa, inspoint); - senext2(spinsh, checksh); - enqueueflipedge(checksh, flipqueue); - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - - // Get the next subsegment. - senext(*splitseg, nextseg); - spivotself(nextseg); -#ifdef SELF_CHECK - assert(nextseg.sh != (shellface *) NULL); -#endif - - // Let the org of nextseg be inspoint. - nextseg.shver = 0; - if (sorg(nextseg) != inspoint) { - sesymself(nextseg); - } -#ifdef SELF_CHECK - assert(sorg(nextseg) == inspoint); -#endif - pb = sdest(nextseg); - spivot(nextseg, startsh); - spinsh = startsh; - do { - findedge(&spinsh, inspoint, pb); - senext(spinsh, checksh); - enqueueflipedge(checksh, flipqueue); - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// perturbrepairencsegs() Repair all encroached segments. // -// // -// All encroached segments are stored in 'badsubsegs'. Each segment will be // -// split by adding a perturbed point near its circumcenter. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::perturbrepairencsegs(queue* flipqueue) -{ - badface *encloop; - tetrahedron encodedtet; - triface splittet; - face splitsub, symsplitsub; - face splitseg, symsplitseg; - point newpoint, sympoint; - point pa, pb, pc; - enum insertsiteresult success; - enum locateresult loc, symloc; - REAL cent[3], d1, ps, rs; - int i, j; - - // Note that steinerleft == -1 if an unlimited number of Steiner points - // is allowed. Loop until 'badsubsegs' is empty. - badsubsegs->traversalinit(); - encloop = badfacetraverse(badsubsegs); - while ((encloop != (badface *) NULL) && (steinerleft != 0)) { - splitseg = encloop->ss; -#ifdef SELF_CHECK - assert(shell2badface(splitseg) == encloop); -#endif - setshell2badface(splitseg, NULL); - pa = sorg(splitseg); - pb = sdest(splitseg); - if ((pa == encloop->forg) && (pb == encloop->fdest)) { - if (b->verbose > 1) { - printf(" Get seg (%d, %d).\n", pointmark(pa), pointmark(pb)); - } - // Create the newpoint. - makepoint(&newpoint); - // Get the circumcenter and radius of ab. - for (i = 0; i < 3; i++) cent[i] = 0.5 * (pa[i] + pb[i]); - d1 = 0.5 * distance(pa, pb); - // Add a random perturbation to newpoint along the vector ab. - ps = randgenerator(d1 * 1.0e-3); - rs = ps / d1; - // Set newpoint (be at the perturbed circumcenter of ab). - for (i = 0; i < 3; i++) newpoint[i] = cent[i] + rs * (cent[i] - pa[i]); - setpointtype(newpoint, FREESEGVERTEX); - // Set splitseg into the newpoint. - setpoint2sh(newpoint, sencode(splitseg)); - - // Is there periodic boundary condition? - if (checkpbcs) { - // Insert points on other segments of incident pbcgroups. - i = shellmark(splitseg) - 1; - for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) { - makepoint(&sympoint); - symloc = getsegpbcsympoint(newpoint, &splitseg, sympoint, - &symsplitseg, segpglist[j]); -#ifdef SELF_CHECK - assert(symloc != OUTSIDE); -#endif - // Note: the symsplitseg and splitseg may be identical, in case - // when the the splitseg is the axis of the rotational sym. - if ((symloc == ONEDGE) && (symsplitseg.sh != splitseg.sh)) { - setpointtype(sympoint, FREESEGVERTEX); - setpoint2sh(sympoint, sencode(symsplitseg)); - // Insert sympoint into DT. - pc = sorg(symsplitseg); - splittet.tet = dummytet; - // Find a good start point to search. - encodedtet = point2tet(pc); - if (encodedtet != (tetrahedron) NULL) { - decode(encodedtet, splittet); - if (isdead(&splittet)) { - splittet.tet = dummytet; - } - } - // Locate sympoint in DT. Do exact location. - success = insertsite(sympoint, &splittet, false, flipqueue); -#ifdef SELF_CHECK - assert(success != DUPLICATEPOINT); -#endif - if (success == OUTSIDEPOINT) { - inserthullsite(sympoint, &splittet, flipqueue); - } - if (steinerleft > 0) steinerleft--; - // Let sympoint remember splittet. - setpoint2tet(sympoint, encode(splittet)); - // Do flip in DT. - flip(flipqueue, NULL); - // Insert sympoint into F. - symloc = locateseg(sympoint, &symsplitseg); - if (symloc == ONEDGE) { - symsplitseg.shver = 0; - spivot(symsplitseg, symsplitsub); - // sympoint should on the edge of symsplitsub. - splitsubedge(sympoint, &symsplitsub, flipqueue); - } else { - // insertsite() has done the whole job. -#ifdef SELF_CHECK - assert(symloc == ONVERTEX); - assert(checksubfaces); -#endif - // Some edges may need to be flipped. - collectflipedges(sympoint, &symsplitseg, flipqueue); - } - // Do flip in facet. - flipsub(flipqueue); - } else { // if (symloc == ONVERTEX) { - // The symmtric point already exists. It is possible when two - // pbc group are idebtical. Omit sympoint. - pointdealloc(sympoint); - } - } - } - - // Insert newpoint into DT. - splittet.tet = dummytet; - // Find a good start point to search. - encodedtet = point2tet(pa); - if (encodedtet != (tetrahedron) NULL) { - decode(encodedtet, splittet); - if (isdead(&splittet)) { - splittet.tet = dummytet; - } - } - if (splittet.tet == dummytet) { // Try pb. - encodedtet = point2tet(pb); - if (encodedtet != (tetrahedron) NULL) { - decode(encodedtet, splittet); - if (isdead(&splittet)) { - splittet.tet = dummytet; - } - } - } - // Locate the newpoint in DT. Do exact location. - success = insertsite(newpoint, &splittet, false, flipqueue); -#ifdef SELF_CHECK - assert(success != DUPLICATEPOINT); -#endif - if (success == OUTSIDEPOINT) { - // A convex hull edge is mssing, and the inserting point lies - // (slightly) outside the convex hull due to the significant - // digits lost in the calculation. Enlarge the convex hull. - inserthullsite(newpoint, &splittet, flipqueue); - } - if (steinerleft > 0) steinerleft--; - // Let newpoint remember splittet. - setpoint2tet(newpoint, encode(splittet)); - // Do flip in DT. - flip(flipqueue, NULL); - // Insert newpoint into F. - loc = locateseg(newpoint, &splitseg); - if (loc == ONEDGE) { - splitseg.shver = 0; - spivot(splitseg, splitsub); - // newpoint should on the edge of splitsub. - splitsubedge(newpoint, &splitsub, flipqueue); - } else { - // insertsite() has done the whole job. -#ifdef SELF_CHECK - assert(loc == ONVERTEX); - assert(checksubfaces); -#endif - // Some edges may need to be flipped. - collectflipedges(newpoint, &splitseg, flipqueue); - } - // Do flip in facet. - flipsub(flipqueue); - } - // Remove this entry from list. - badfacedealloc(badsubsegs, encloop); - // Get the next encroached segments. - encloop = badfacetraverse(badsubsegs); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// perturbrepairencsubs() Repair all encroached subfaces. // -// // -// All encroached subfaces are stored in 'badsubfaces'. Each subface will be // -// split by adding a perturbed point near its circumcenter. However, if the // -// point encroaches some segments, it will not be inserted. Instead, the // -// encroached segments are split. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::perturbrepairencsubs(list* cavsublist, queue* flipqueue) -{ - badface *encloop, *encsubseg; - tetrahedron encodedtet; - triface splittet; - face splitsub, symsplitsub; - face checkseg, symsplitseg; - point newpoint, sympoint; - point pa, pb, pc, pd; - enum insertsiteresult success; - enum locateresult loc, symloc; - REAL cent[3], d1, ps, rs; - bool reject; - int i; - - // Note that steinerleft == -1 if an unlimited number of Steiner points - // is allowed. Loop until the list 'badsubfaces' is empty. - while ((badsubfaces->items > 0) && (steinerleft != 0)) { - badsubfaces->traversalinit(); - encloop = badfacetraverse(badsubfaces); - while ((encloop != (badface *) NULL) && (steinerleft != 0)) { - splitsub = encloop->ss; -#ifdef SELF_CHECK - assert(shell2badface(splitsub) == encloop); -#endif - setshell2badface(splitsub, NULL); - pa = sorg(splitsub); - pb = sdest(splitsub); - pc = sapex(splitsub); - // The subface may be not the same one when it was determined to be - // encroached. If its adjacent encroached subface was split, the - // consequent flips may change it into another subface. - if ((pa == encloop->forg) && (pb == encloop->fdest) && - (pc == encloop->fapex)) { - if (b->verbose > 1) { - printf(" Get subface (%d, %d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(pc)); - } - // Create the newpoint. - makepoint(&newpoint); - // Get the circumcenter of abc. - circumsphere(pa, pb, pc, NULL, cent, &d1); -#ifdef SELF_CHECK - assert(d1 > 0.0); -#endif - // Add a random perturbation to newpoint along the vector a->cent. - // This way, the perturbed point still lies in the plane of abc. - ps = randgenerator(d1 * 1.0e-3); - rs = ps / d1; - // Set newpoint (be at the perturbed circumcenter of abc). - for (i = 0; i < 3; i++) newpoint[i] = cent[i] + rs * (cent[i] - pa[i]); - // Get the abovepoint of the facet. - abovepoint = facetabovepointarray[shellmark(splitsub)]; - // Do we need to calculate the abovepoint? - if (abovepoint == (point) NULL) { - getfacetabovepoint(&splitsub); - } - loc = locatesub(newpoint, &splitsub, 1, 0.0); -#ifdef SELF_CHECK - assert(loc != ONVERTEX); -#endif - if (loc != OUTSIDE) { - // Add 'splitsub' into 'cavsublist'. - cavsublist->append(&splitsub); - // Collect all subfaces that encroached by newpoint. - collectcavsubs(newpoint, cavsublist); - // Find if there are encroached segments. - reject = tallencsegsfsubs(newpoint, cavsublist); - // Clear cavsublist for the next use. - cavsublist->clear(); - } else { - // newpoint lies outside. splitsub contains the boundary segment. - sspivot(splitsub, checkseg); -#ifdef SELF_CHECK - assert(checkseg.sh != dummysh); -#endif - // Add this segment into list for splitting. - if (b->verbose > 2) { - printf(" Queuing boundary segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - encsubseg = (badface *) badsubsegs->alloc(); - encsubseg->ss = checkseg; - encsubseg->forg = sorg(checkseg); - encsubseg->fdest = sdest(checkseg); - encsubseg->foppo = (point) NULL; - setshell2badface(encsubseg->ss, encsubseg); - // Reject newpoint. - reject = true; - } - - if (!reject) { - // newpoint is going to be inserted. - - // Is there periodic boundary condition? - if (checkpbcs) { - if (shellpbcgroup(splitsub) >= 0) { - // Insert a point on another facet of the pbcgroup. - makepoint(&sympoint); - // Note: 'abovepoint' will be changed. - symloc = getsubpbcsympoint(newpoint, &splitsub, sympoint, - &symsplitsub); -#ifdef SELF_CHECK - assert(symloc != ONVERTEX); -#endif - setpoint2pbcpt(newpoint, sympoint); - setpoint2pbcpt(sympoint, newpoint); - setpointtype(sympoint, FREESUBVERTEX); - // setpoint2sh(sympoint, sencode(symsplitsub)); - // Insert sympoint into DT. - pd = sorg(symsplitsub); - splittet.tet = dummytet; - // Find a good start point to search. - encodedtet = point2tet(pd); - if (encodedtet != (tetrahedron) NULL) { - decode(encodedtet, splittet); - if (isdead(&splittet)) { - splittet.tet = dummytet; - } - } - // Locate sympoint in DT. Do exact location. - success = insertsite(sympoint, &splittet, false, flipqueue); -#ifdef SELF_CHECK - assert(success != DUPLICATEPOINT); -#endif - if (success == OUTSIDEPOINT) { - inserthullsite(sympoint, &splittet, flipqueue); - } - if (steinerleft > 0) steinerleft--; - // Let sympoint remember splittet. - setpoint2tet(sympoint, encode(splittet)); - // Do flip in DT. - flip(flipqueue, NULL); - // Insert sympoint into F. - // getabovepoint(&symsplitsub); - // symloc = locatesub(sympoint, &symsplitsub, 1, 0.0); - if (symloc == ONFACE) { - splitsubface(sympoint, &symsplitsub, flipqueue); - } else if (symloc == ONEDGE) { - splitsubedge(sympoint, &symsplitsub, flipqueue); - } else { - // 'insertsite()' has done the whole job. -#ifdef SELF_CHECK - assert(symloc == ONVERTEX); - assert(checksubfaces); -#endif - // Split subfaces have been flipped. - flipqueue->clear(); - } - // Do flip in facet. - flipsub(flipqueue); - } - } - - // Insert newpoint into DT. - splittet.tet = dummytet; - // Find a good start point to search. - encodedtet = point2tet(pa); - if (encodedtet != (tetrahedron) NULL) { - decode(encodedtet, splittet); - if (isdead(&splittet)) { - splittet.tet = dummytet; - } - } - if (splittet.tet == dummytet) { // Try pb. - encodedtet = point2tet(pb); - if (encodedtet != (tetrahedron) NULL) { - decode(encodedtet, splittet); - if (isdead(&splittet)) { - splittet.tet = dummytet; - } - } - } - // Locate the newpoint in DT. Do exact location. - success = insertsite(newpoint, &splittet, false, flipqueue); -#ifdef SELF_CHECK - assert(success != DUPLICATEPOINT); -#endif - if (success == OUTSIDEPOINT) { - inserthullsite(newpoint, &splittet, flipqueue); - } - if (steinerleft > 0) steinerleft--; - // Let newpoint remember splittet. - setpoint2tet(newpoint, encode(splittet)); - // Do flip in DT. - flip(flipqueue, NULL); - // Insert newpoint into F. - // if (checkpbcs) { - // 'abovepoint' has been changed. - // getabovepoint(&splitsub); - // loc = locatesub(newpoint, &splitsub, 1, 0.0); - // } - if (loc == ONFACE) { - // Insert the newpoint in facet. - splitsubface(newpoint, &splitsub, flipqueue); - } else if (loc == ONEDGE) { - // Insert the newpoint in facet. - splitsubedge(newpoint, &splitsub, flipqueue); - } else { - // 'insertsite()' has done the whole job. -#ifdef SELF_CHECK - assert(loc == ONVERTEX); - assert(checksubfaces); -#endif - // Split subfaces have been flipped. - flipqueue->clear(); - } - // Set the type of the newpoint. - setpointtype(newpoint, FREESUBVERTEX); - // Set splitsub into the newpoint. - // setpoint2sh(newpoint, sencode(splitsub)); - // Do flip in the facet. - flipsub(flipqueue); - - // Remove this entry from list. - badfacedealloc(badsubfaces, encloop); - } else { - // newpoint is rejected. Remove it from points. - pointdealloc(newpoint); - // Repair all encroached segments. - perturbrepairencsegs(flipqueue); - // Do not remove 'encloop'. Later it will be tested again. - setshell2badface(encloop->ss, encloop); - } - } else { - // This subface has been changed. Remove this entry from list. - badfacedealloc(badsubfaces, encloop); - // It may be co-circular with its neighbors. - // checksub4cocir(&splitsub, eps, false, true); - } - // Get the next encroached subfaces. - encloop = badfacetraverse(badsubfaces); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// incrperturbvertices() Remove the local degeneracies in DT. // -// // -// A local degeneracy of a DT D is a set of 5 or more vertices which share a // -// common sphere S and no other vertex of D in S. D is not unique if it has // -// local degeneracies. This routine removes the local degeneracies from D by // -// inserting break points (as described in reference [2]). // -// // -// 'eps' is a user-provided error tolerance. It is used to detect whether or // -// not five points are approximate cospherical (evaluated in iscospheric()). // -// Set it to 0.0 to disable it, i.e., only test pure degenerate point set. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::incrperturbvertices(REAL eps) -{ - queue *flipqueue; - list *cavsublist; - long vertcount; - - if (!b->quiet) { - printf("Perturbing vertices.\n"); - } - - vertcount = points->items; - // Create a map from points to tets for fastening search. - // makepoint2tetmap(); // This has been done in meshsurface(). - - // Initialize working queues, lists. - flipqueue = new queue(sizeof(badface)); - cavsublist = new list(sizeof(face), NULL, 256); - // Initialize the pool of encroached subfaces and subsegments. - badsubsegs = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); - badsubfaces = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); - // Find all pairs of co-circular subfaces. - tallcocirsubs(eps, true); - if (b->verbose && badsubfaces->items > 0) { - printf(" Removing degenerate subfaces.\n"); - } - perturbrepairencsubs(cavsublist, flipqueue); - - if (b->verbose > 0) { - printf(" %ld break points.\n", points->items - vertcount); - } - - delete cavsublist; - delete flipqueue; - delete badsubfaces; - delete badsubsegs; - badsubsegs = (memorypool *) NULL; - badsubfaces = (memorypool *) NULL; -} - -// -// End of vertex perturbation routines -// - -// -// Begin of segment recovery routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// markacutevertices() Mark acute vertices. // -// // -// A vertex v is called acute if there are two segments sharing at v forming // -// an acute angle (i.e. smaller than 90 degree). // -// // -// This routine finds all acute vertices in the PLC and marks them as point- // -// type ACUTEVERTEX. The other vertices of segments which are non-acute will // -// be marked as NACUTEVERTEX. Vertices which are not endpoints of segments // -// (such as DUPLICATEDVERTEX, UNUSEDVERTEX, etc) are not infected. // -// // -// NOTE: This routine should be called before Steiner points are introduced. // -// That is, no point has type like FREESEGVERTEX, etc. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::markacutevertices(REAL acuteangle) -{ - shellface **segsperverlist; - face segloop, nextseg; - point pointloop, edest, eapex; - REAL cosbound, anglearc; - REAL v1[3], v2[3], L, D; - bool isacute; - int *idx2seglist; - int acutecount; - int idx, i, j, k; - - if (b->verbose > 0) { - printf(" Marking acute vertices.\n"); - } - - anglearc = acuteangle * PI / 180.0; - cosbound = cos(anglearc); - acutecount = 0; - // Constructing a map from vertex to segments. - makesegmentmap(idx2seglist, segsperverlist); - - // Loop over the set of vertices. - points->traversalinit(); - pointloop = pointtraverse(); - while (pointloop != (point) NULL) { - idx = pointmark(pointloop) - in->firstnumber; - // Only do test if p is an endpoint of some segments. - if (idx2seglist[idx + 1] > idx2seglist[idx]) { - // Init p to be non-acute. - setpointtype(pointloop, NACUTEVERTEX); - isacute = false; - // Loop through all segments sharing at p. - for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isacute; i++) { - segloop.sh = segsperverlist[i]; - // segloop.shver = 0; - if (sorg(segloop) != pointloop) sesymself(segloop); - edest = sdest(segloop); - for (j = i + 1; j < idx2seglist[idx + 1] && !isacute; j++) { - nextseg.sh = segsperverlist[j]; - // nextseg.shver = 0; - if (sorg(nextseg) != pointloop) sesymself(nextseg); - eapex = sdest(nextseg); - // Check the angle formed by segs (p, edest) and (p, eapex). - for (k = 0; k < 3; k++) { - v1[k] = edest[k] - pointloop[k]; - v2[k] = eapex[k] - pointloop[k]; - } - L = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]); - for (k = 0; k < 3; k++) v1[k] /= L; - L = sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]); - for (k = 0; k < 3; k++) v2[k] /= L; - D = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - // Is D acute? - isacute = (D >= cosbound); - } - } - if (isacute) { - // Mark p to be acute. - setpointtype(pointloop, ACUTEVERTEX); - acutecount++; - } - } - pointloop = pointtraverse(); - } - - delete [] idx2seglist; - delete [] segsperverlist; - - if ((b->verbose > 0) && (acutecount > 0)) { - printf(" %d acute vertices.\n", acutecount); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// finddirection() Find the first tetrahedron on the path from one point // -// to another. // -// // -// Find the tetrahedron that intersects a line segment L (from the origin of // -// 'searchtet' to the point 'tend'), and returns the result in 'searchtet'. // -// The origin of 'searchtet' does not change, even though the tetrahedron // -// returned may differ from the one passed in. This routine is used to find // -// the direction to move in to get from one point to another. // -// // -// The return value notes the location of the line segment L with respect to // -// 'searchtet': // -// - Returns RIGHTCOLLINEAR indicates L is collinear with the line segment // -// from the origin to the destination of 'searchtet'. // -// - Returns LEFTCOLLINEAR indicates L is collinear with the line segment // -// from the origin to the apex of 'searchtet'. // -// - Returns TOPCOLLINEAR indicates L is collinear with the line segment // -// from the origin to the opposite of 'searchtet'. // -// - Returns ACROSSEDGE indicates L intersects with the line segment from // -// the destination to the apex of 'searchtet'. // -// - Returns ACROSSFACE indicates L intersects with the face opposite to // -// the origin of 'searchtet'. // -// - Returns BELOWHULL indicates L crosses outside the mesh domain. This // -// can only happen when the domain is non-convex. // -// // -// NOTE: This routine only works correctly when the mesh is exactly Delaunay.// -// // -// If 'maxtetnumber' > 0, stop the searching process if the number of passed // -// tets is larger than it. Return BELOWHULL. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::finddirectionresult tetgenmesh:: -finddirection(triface *searchtet, point tend, long maxtetnumber) -{ - triface neightet; - point tstart, tdest, tapex, toppo; - REAL ori1, ori2, ori3; - long tetnumber; - - tstart = org(*searchtet); -#ifdef SELF_CHECK - assert(tstart != tend); -#endif - adjustedgering(*searchtet, CCW); - if (tstart != org(*searchtet)) { - enextself(*searchtet); // For keeping the same origin. - } - tdest = dest(*searchtet); - if (tdest == tend) { - return RIGHTCOLLINEAR; - } - tapex = apex(*searchtet); - if (tapex == tend) { - return LEFTCOLLINEAR; - } - - ori1 = orient3d(tstart, tdest, tapex, tend); - if (ori1 > 0.0) { - // 'tend' is below the face, get the neighbor of this side. - sym(*searchtet, neightet); - if (neightet.tet != dummytet) { - findorg(&neightet, tstart); - adjustedgering(neightet, CCW); - if (org(neightet) != tstart) { - enextself(neightet); // keep the same origin. - } - // Set the changed configuratiuon. - *searchtet = neightet; - ori1 = -1.0; - tdest = dest(*searchtet); - tapex = apex(*searchtet); - } else { - // A hull face. Only possible for a nonconvex mesh. -#ifdef SELF_CHECK - assert(nonconvex); -#endif - return BELOWHULL; - } - } - - // Repeatedly change the 'searchtet', remain 'tstart' be its origin, until - // find a tetrahedron contains 'tend' or is crossed by the line segment - // from 'tstart' to 'tend'. - tetnumber = 0l; - while ((maxtetnumber > 0) && (tetnumber <= maxtetnumber)) { - tetnumber++; - toppo = oppo(*searchtet); - if (toppo == tend) { - return TOPCOLLINEAR; - } - ori2 = orient3d(tstart, toppo, tdest, tend); - if (ori2 > 0.0) { - // 'tend' is below the face, get the neighbor at this side. - fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - findorg(&neightet, tstart); - adjustedgering(neightet, CCW); - if (org(neightet) != tstart) { - enextself(neightet); // keep the same origin. - } - // Set the changed configuration. - *searchtet = neightet; - ori1 = -1.0; - tdest = dest(*searchtet); - tapex = apex(*searchtet); - // Continue the search from the changed 'searchtet'. - continue; - } else { - // A hull face. Only possible for a nonconvex mesh. -#ifdef SELF_CHECK - assert(nonconvex); -#endif - return BELOWHULL; - } - } - ori3 = orient3d(tapex, toppo, tstart, tend); - if (ori3 > 0.0) { - // 'tend' is below the face, get the neighbor at this side. - enext2fnext(*searchtet, neightet); - symself(neightet); - if (neightet.tet != dummytet) { - findorg(&neightet, tstart); - adjustedgering(neightet, CCW); - if (org(neightet) != tstart) { - enextself(neightet); // keep the same origin. - } - // Set the changed configuration. - *searchtet = neightet; - ori1 = -1.0; - tdest = dest(*searchtet); - tapex = apex(*searchtet); - // Continue the search from the changed 'searchtet'. - continue; - } else { - // A hull face. Only possible for a nonconvex mesh. -#ifdef SELF_CHECK - assert(nonconvex); -#endif - return BELOWHULL; - } - } - // Now 'ori1', 'ori2' and 'ori3' are possible be 0.0 or all < 0.0; - if (ori1 < 0.0) { - // Possible cases are: ACROSSFACE, ACROSSEDGE, TOPCOLLINEAR. - if (ori2 < 0.0) { - if (ori3 < 0.0) { - return ACROSSFACE; - } else { // ori3 == 0.0; - // Cross edge (apex, oppo) - enext2fnextself(*searchtet); - esymself(*searchtet); // org(*searchtet) == tstart; - return ACROSSEDGE; - } - } else { // ori2 == 0.0; - if (ori3 < 0.0) { - // Cross edge (dest, oppo) - fnextself(*searchtet); - esymself(*searchtet); - enextself(*searchtet); // org(*searchtet) == tstart; - return ACROSSEDGE; - } else { // ori3 == 0.0; - // Collinear with edge (org, oppo) - return TOPCOLLINEAR; - } - } - } else { // ori1 == 0.0; - // Possible cases are: RIGHTCOLLINEAR, LEFTCOLLINEAR, ACROSSEDGE. - if (ori2 < 0.0) { - if (ori3 < 0.0) { - // Cross edge (tdest, tapex) - return ACROSSEDGE; - } else { // ori3 == 0.0 - // Collinear with edge (torg, tapex) - return LEFTCOLLINEAR; - } - } else { // ori2 == 0.0; -#ifdef SELF_CHECK - assert(ori3 != 0.0); -#endif - // Collinear with edge (torg, tdest) - return RIGHTCOLLINEAR; - } - } - } - // Loop breakout. It may happen when the mesh is non-Delaunay. - return BELOWHULL; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsearchtet() Find a tetrahedron whose origin is either 'p1' or 'p2'. // -// // -// On return, the origin of 'searchtet' is either 'p1' or 'p2', and 'tend' // -// returns the other point. 'searchtet' serves as the starting tetrahedron // -// for searching of the line segment from 'p1' to 'p2' or vice versa. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getsearchtet(point p1, point p2, triface* searchtet, - point* tend) -{ - tetrahedron encodedtet1, encodedtet2; - - // Is there a valid handle provided by the user? - if ((searchtet->tet != (tetrahedron *) NULL) && !isdead(searchtet)) { - // Find which endpoint the handle holds. - if (findorg(searchtet, p1)) { - *tend = p2; - return; - } else { - if (findorg(searchtet, p2)) { - *tend = p1; - return; - } - } - } - // If not, search the tet handle stored in 'p1' or 'p2'. - *tend = (point) NULL; - encodedtet1 = point2tet(p1); - encodedtet2 = point2tet(p2); - if (encodedtet1 != (tetrahedron) NULL) { - decode(encodedtet1, *searchtet); - // Be careful, here 'searchtet' may be dead. - if (findorg(searchtet, p1)) { - *tend = p2; - } - } else if (encodedtet2 != (tetrahedron) NULL) { - decode(encodedtet2, *searchtet); - // Be careful, here 'searchtet' may be dead. - if (findorg(searchtet, p2)) { - *tend = p1; - } - } - // If still not, perform a full point location. The starting tet is - // chosen as follows: Use the handle stored in 'p1' or 'p2' if it is - // alive; otherwise, start from a tet on the convex hull. - if (*tend == (point) NULL) { - if (encodedtet1 != (tetrahedron) NULL) { - decode(encodedtet1, *searchtet); - // Be careful, here 'searchtet' may be dead. - } - if (isdead(searchtet)) { - if (encodedtet2 != (tetrahedron) NULL) { - decode(encodedtet2, *searchtet); - // Be careful, here 'searchtet' may be dead. - } - if (isdead(searchtet)) { - searchtet->tet = dummytet; - searchtet->loc = 0; - symself(*searchtet); - } -#ifdef SELF_CHECK - assert(!isdead(searchtet)); -#endif - } - if (locate(p1, searchtet) != ONVERTEX) { - printf("Internal error in getsearchtet(): Failed to locate point\n"); - internalerror(); - } - // Remember this handle in 'p1' to enhance the search speed. - setpoint2tet(p1, encode(*searchtet)); - *tend = p2; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// isedgeencroached() Check whether or not a subsegment is encroached. // -// // -// A segment with endpoints 'p1' and 'p2' is encroached by the point 'testpt'// -// if it lies in the diametral sphere of this segment. The degenerate case // -// that 'testpt' lies on the sphere is treated as encroached if 'degflag' is // -// set to be TRUE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::isedgeencroached(point p1, point p2, point testpt, - bool degflag) -{ - REAL dotproduct; - - // Check if the segment is facing an angle larger than 90 degree? - dotproduct = (p1[0] - testpt[0]) * (p2[0] - testpt[0]) - + (p1[1] - testpt[1]) * (p2[1] - testpt[1]) - + (p1[2] - testpt[2]) * (p2[2] - testpt[2]); - if (dotproduct < 0) { - return true; - } else if (dotproduct == 0 && degflag) { - return true; - } else { - return false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutrefpoint() Search the reference point of a missing segment. // -// // -// A segment S is missing in current Delaunay tetrahedralization DT and will // -// be split by inserting a point V in it. The two end points of S are the // -// origin of 'searchtet' and 'tend'. And we know that S is crossing the face // -// of 'searchtet' opposite to its origin (may be intersecting with the edge // -// from the destination to the apex of the 'searchtet'). The search of P is // -// completed by walking through all faces of DT across by S. // -// // -// Warning: This routine is correct when the tetrahedralization is Delaunay // -// and convex. Otherwise, the search loop may not terminate. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::scoutrefpoint(triface* searchtet, point tend) -{ - triface checkface; - point tstart, testpt, refpoint; - REAL cent[3], radius, largest; - REAL ahead; - bool ncollinear; - int sides; - - if (b->verbose > 2) { - printf(" Scout the reference point of segment (%d, %d).\n", - pointmark(org(*searchtet)), pointmark(tend)); - } - - tstart = org(*searchtet); - refpoint = (point) NULL; - largest = 0; // avoid compile warning. - - // Check the three vertices of the crossing face. - testpt = apex(*searchtet); - if (isedgeencroached(tstart, tend, testpt, true)) { - ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); -#ifdef SELF_CHECK - assert(ncollinear); -#endif - refpoint = testpt; - largest = radius; - } - testpt = dest(*searchtet); - if (isedgeencroached(tstart, tend, testpt, true)) { - ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); -#ifdef SELF_CHECK - assert(ncollinear); -#endif - if (refpoint == (point) NULL) { - refpoint = testpt; - largest = radius; - } else { - if (radius > largest) { - refpoint = testpt; - largest = radius; - } - } - } - testpt = oppo(*searchtet); - if (isedgeencroached(tstart, tend, testpt, true)) { - ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); -#ifdef SELF_CHECK - assert(ncollinear); -#endif - if (refpoint == (point) NULL) { - refpoint = testpt; - largest = radius; - } else { - if (radius > largest) { - refpoint = testpt; - largest = radius; - } - } - } - // Check the opposite vertex of the neighboring tet in case the segment - // crosses the edge (leftpoint, rightpoint) of the crossing face. - sym(*searchtet, checkface); - if (checkface.tet != dummytet) { - testpt = oppo(checkface); - if (isedgeencroached(tstart, tend, testpt, true)) { - ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); -#ifdef SELF_CHECK - assert(ncollinear); -#endif - if (refpoint == (point) NULL) { - refpoint = testpt; - largest = radius; - } else { - if (radius > largest) { - refpoint = testpt; - largest = radius; - } - } - } - } - - // Walk through all crossing faces. - enextfnext(*searchtet, checkface); - sym(checkface, *searchtet); - while (true) { - // Check if we are reaching the boundary of the triangulation. -#ifdef SELF_CHECK - assert(searchtet->tet != dummytet); -#endif - // Search for an adjoining tetrahedron we can walk through. - searchtet->ver = 0; - // 'testpt' is the shared vertex for the following orientation tests. - testpt = oppo(*searchtet); - if (testpt == tend) { - // The searching is finished. - break; - } else { - // 'testpt' may encroach the segment. - if ((testpt != tstart) && (testpt != refpoint)) { - if (isedgeencroached(tstart, tend, testpt, true)) { - ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius); - if (!ncollinear) { - // 'testpt' is collinear with the segment. It may happen when a - // set of collinear and continuous segments is defined by two - // extreme endpoints. In this case, we should choose 'testpt' - // as the splitting point immediately. No new point should be - // created. - refpoint = testpt; - break; - } - if (refpoint == (point) NULL) { - refpoint = testpt; - largest = radius; - } else { - if (radius > largest) { - refpoint = testpt; - largest = radius; - } - } - } - } - } - // Check three side-faces of 'searchtet' to find the one through - // which we can walk next. - for (sides = 0; sides < 3; sides++) { - fnext(*searchtet, checkface); - ahead = orient3d(org(checkface), dest(checkface), testpt, tend); - if (ahead < 0.0) { - // We can walk through this face and continue the searching. - sym(checkface, *searchtet); - break; - } - enextself(*searchtet); - } -#ifdef SELF_CHECK - assert (sides < 3); -#endif - } - -#ifdef SELF_CHECK - assert(refpoint != (point) NULL); -#endif - return refpoint; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsegmentorigin() Return the origin of the (unsplit) segment. // -// // -// After a segment (or a subsegment) is split. Two resulting subsegments are // -// connecting each other through the pointers saved in their data fields. // -// With these pointers, the whole (unsplit) segment can be found. 'splitseg' // -// may be a split subsegment. Returns the origin of the unsplit segment. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::getsegmentorigin(face* splitseg) -{ - face workseg; - point farorg; - - farorg = sorg(*splitseg); - if ((pointtype(farorg) != ACUTEVERTEX) && - (pointtype(farorg) != NACUTEVERTEX)) { - workseg = *splitseg; - do { - senext2self(workseg); - spivotself(workseg); - if (workseg.sh != dummysh) { - workseg.shver = 0; // It's a subsegment. - if (sdest(workseg) != farorg) { - sesymself(workseg); -#ifdef SELF_CHECK - assert(sdest(workseg) == farorg); -#endif - } - farorg = sorg(workseg); - if ((pointtype(farorg) == ACUTEVERTEX) || - (pointtype(farorg) == NACUTEVERTEX)) break; - } - } while (workseg.sh != dummysh); - } -#ifdef SELF_CHECK - assert((pointtype(farorg) == ACUTEVERTEX) || - (pointtype(farorg) == NACUTEVERTEX)); -#endif - return farorg; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsplitpoint() Get a point for splitting a segment. // -// // -// 'splitseg' is the segment will be split. 'refpoint' is a reference point // -// for splitting this segment. Moreover, it should not collinear with the // -// splitting segment. (The collinear case will be detected by iscollinear() // -// before entering this routine.) The calculation of the splitting point is // -// governed by three rules introduced in my paper. // -// // -// After the position is calculated, a new point is created at this location.// -// The new point has one of the two pointtypes: FREESEGVERTEX indicating it // -// is an inserting vertex on segment, and NACUTEVERTEX indicating it is an // -// endpoint of a segment which original has type-3 now becomes type-2. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::point tetgenmesh::getsplitpoint(face* splitseg, point refpoint) -{ - point splitpoint; - point farorg, fardest; - point ei, ej, ek, c; - REAL v[3], r, split; - REAL d1, d2, ps, rs; - bool acuteorg, acutedest; - int stype, rule; - int i; - - // First determine the type of the segment (type-1, type-2, or type-3). - farorg = getsegmentorigin(splitseg); - acuteorg = (pointtype(farorg) == ACUTEVERTEX); - sesymself(*splitseg); - fardest = getsegmentorigin(splitseg); - acutedest = (pointtype(fardest) == ACUTEVERTEX); - sesymself(*splitseg); - - ek = (point) NULL; // avoid a compilation warning. - - if (acuteorg) { - if (acutedest) { - stype = 3; - } else { - stype = 2; - ek = farorg; - } - } else { - if (acutedest) { - stype = 2; - // Adjust splitseg, so that its origin is acute. - sesymself(*splitseg); - ek = fardest; - } else { - stype = 1; - } - } - ei = sorg(*splitseg); - ej = sdest(*splitseg); - - if (b->verbose > 1) { - printf(" Splitting segment (%d, %d) type-%d with refpoint %d.\n", - pointmark(ei), pointmark(ej), stype, pointmark(refpoint)); - } - - if (stype == 1 || stype == 3) { - // Use rule-1. - REAL eij, eip, ejp; - eij = distance(ei, ej); - eip = distance(ei, refpoint); - ejp = distance(ej, refpoint); - if ((eip < ejp) && (eip < 0.5 * eij)) { - c = ei; - r = eip; - } else if ((eip > ejp) && (ejp < 0.5 * eij)) { - c = ej; - ej = ei; - r = ejp; - } else { - c = ei; - r = 0.5 * eij; - } - split = r / eij; - for (i = 0; i < 3; i++) { - v[i] = c[i] + split * (ej[i] - c[i]); - } - rule = 1; - } else { - // Use rule-2 or rule-3. - REAL eki, ekj, ekp, evj, evp, eiv; - c = ek; - eki = distance(ek, ei); // eki may equal zero. - ekj = distance(ek, ej); - ekp = distance(ek, refpoint); - // Calculate v (the going to split position between ei, ej). - r = ekp; - // Check the validity of the position. - if (!(eki < r && r < ekj)) { - printf("Error: Invalid PLC.\n"); - printf(" Hint: Use -d switch to check it.\n"); - terminatetetgen(1); - } - split = r / ekj; - for (i = 0; i < 3; i++) { - v[i] = c[i] + split * (ej[i] - c[i]); - } - rule = 2; - evj = ekj - r; // distance(v, ej); - evp = distance(v, refpoint); - if (evj < evp) { - // v is rejected, use rule-3. - eiv = distance(ei, v); - if (evp <= 0.5 * eiv) { - r = eki + eiv - evp; - } else { - r = eki + 0.5 * eiv; - } -#ifdef SELF_CHECK - assert(eki < r && r < ekj); -#endif - split = r / ekj; - for (i = 0; i < 3; i++) { - v[i] = c[i] + split * (ej[i] - c[i]); - } - if (b->verbose > 1) { - printf(" Using rule-3.\n"); - } - rule = 3; - } - } - - // Accumulate the corresponding counters. - if (rule == 1) r1count++; - else if (rule == 2) r2count++; - else if (rule == 3) r3count++; - - if (b->verbose > 1) { - if (stype == 2) { - printf(" Split = %.12g.\n", distance(ei, v) / distance(ei, ej)); - } else { - printf(" Split = %.12g.\n", distance(c, v) / distance(c, ej)); - } - } - - // Create the newpoint. - makepoint(&splitpoint); - // Add a random perturbation on splitpoint. - d1 = distance(c, v); - d2 = distance(refpoint, v); - if (stype == 1 || stype == 3) { - ps = randgenerator(d1 * 1.0e-3); - } else { - // For type-2 segment, add a smaller perturbation. - // ps = randgenerator(d1 * 1.0e-5); - // REAL d2 = distance(refpoint, v); - ps = randgenerator(d2 * 1.0e-5); - } - rs = ps / d1; - // Perturb splitpoint away from c. - for (i = 0; i < 3; i++) { - splitpoint[i] = c[i] + (1.0 + rs) * (v[i] - c[i]); - } - // for (i = 0; i < in->numberofpointattributes; i++) { - // splitpoint[i + 3] = c[i + 3] + (split + rs) * (ej[i + 3] - c[i + 3]); - // } - if (stype == 3) { - // Change a type-3 segment into two type-2 segments. - setpointtype(splitpoint, NACUTEVERTEX); - } else { - // Set it's type be FREESEGVERTEX. - setpointtype(splitpoint, FREESEGVERTEX); - } - setpoint2sh(splitpoint, sencode(*splitseg)); - - return splitpoint; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertsegment() Insert segment into DT. Queue it if it does not exist. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::insertsegment(face *insseg, list *misseglist) -{ - badface *misseg; - triface searchtet, spintet; - point tend, checkpoint; - point p1, p2; - enum finddirectionresult collinear; - int hitbdry; - - // Search segment ab in DT. - p1 = (point) insseg->sh[3]; - p2 = (point) insseg->sh[4]; - getsearchtet(p1, p2, &searchtet, &tend); - collinear = finddirection(&searchtet, tend, tetrahedrons->items); - if (collinear == LEFTCOLLINEAR) { - checkpoint = apex(searchtet); - enext2self(searchtet); - esymself(searchtet); - } else if (collinear == RIGHTCOLLINEAR) { - checkpoint = dest(searchtet); - } else if (collinear == TOPCOLLINEAR) { - checkpoint = oppo(searchtet); - fnextself(searchtet); - enext2self(searchtet); - esymself(searchtet); - } else { - // assert(collinear == ACROSSFACE || collinear == ACROSSEDGE); - checkpoint = (point) NULL; - } - if (checkpoint == tend) { - // Segment exist. Bond it to all tets containing it. - hitbdry = 0; - adjustedgering(searchtet, CCW); - fnextself(searchtet); - spintet = searchtet; - do { - tssbond1(spintet, *insseg); - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(searchtet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } - } - } - } while ((apex(spintet) != apex(searchtet)) && (hitbdry < 2)); - return true; - } else { - // Segment is missing. - if (misseglist != (list *) NULL) { - if (b->verbose > 2) { - printf(" Queuing missing segment (%d, %d).\n", pointmark(p1), - pointmark(p2)); - } - misseg = (badface *) misseglist->append(NULL); - misseg->ss = *insseg; - misseg->forg = p1; - misseg->fdest = p2; - misseg->foppo = (point) NULL; // Not used. - // setshell2badface(misseg->ss, misseg); - } - return false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tallmissegs() Find and queue all missing segments in DT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tallmissegs(list *misseglist) -{ - face segloop; - - if (b->verbose) { - printf(" Queuing missing segments.\n"); - } - - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - insertsegment(&segloop, misseglist); - segloop.sh = shellfacetraverse(subsegs); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunizesegments() Split segments repeatedly until they appear in a // -// Delaunay tetrahedralization. // -// // -// Given a PLC X, which has a set V of vertices and a set of segments. Start // -// from a Delaunay tetrahedralization D of V, this routine recovers segments // -// of X in D by incrementally inserting points on missing segments, updating // -// D with the newly inserted points into D', which remains to be a Delaunay // -// tetrahedralization and respects the segments of X. Hence, each segment of // -// X appears as a union of edges in D'. // -// // -// This routine dynamically maintains two meshes, one is DT, another is the // -// surface mesh F of X. DT and F have exactly the same vertices. They are // -// updated simultaneously with the newly inserted points. // -// // -// Missing segments are found by looping the set S of segments, checking the // -// existence of each segment in DT. Once a segment is found missing in DT, // -// it is split into two subsegments by inserting a point into both DT and F, // -// and S is updated accordingly. However, the inserted point may cause some // -// other existing segments be non-Delaunay, hence are missing from the DT. // -// In order to force all segments to appear in DT, we have to loop S again // -// after some segments are split. (A little ugly method) Use a handle to // -// remember the last segment be split in one loop, hence all segments after // -// it are existing and need not be checked. // -// // -// In priciple, a segment on the convex hull should exist in DT. However, if // -// there are four coplanar points on the convex hull, and the DT only can // -// contain one diagonal edge which is unfortunately not the segment, then it // -// is missing. During the recovery of the segment, it is possible that the // -// calculated inserting point for recovering this convex hull segment is not // -// exact enough and lies (slightly) outside the DT. In order to insert the // -// point, we enlarge the convex hull of the DT, so it can contain the point // -// and remains convex. 'inserthullsite()' is called for this case. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::delaunizesegments() -{ - list *misseglist; - queue *flipqueue; - badface *misloop; - tetrahedron encodedtet; - triface searchtet, splittet; - face splitsh, symsplitsub; - face segloop, symsplitseg; - point refpoint, splitpoint, sympoint; - point tend, checkpoint; - point p1, p2, pa; - enum finddirectionresult collinear; - enum insertsiteresult success; - enum locateresult symloc; - bool coll; - long vertcount; - int i, j; - - if (!b->quiet) { - printf("Delaunizing segments.\n"); - } - - // Construct a map from points to tets for speeding point location. - makepoint2tetmap(); - // Initialize a flipqueue. - flipqueue = new queue(sizeof(badface)); - // Initialize the pool of missing segments. - misseglist = new list(sizeof(badface), NULL, SUBPERBLOCK); - // Looking for missing segments. - tallmissegs(misseglist); - // The DT contains segments now. - checksubsegs = 1; - // Remember the current number of points. - vertcount = points->items; - // Initialize the counters. - r1count = r2count = r3count = 0l; - - // Loop until 'misseglist' is empty. - while (misseglist->items > 0) { - // Randomly pick a missing segment to recover. - i = randomnation(misseglist->items); - misloop = (badface *)(* misseglist)[i]; - segloop = misloop->ss; - // Fill the "hole" in the list by filling the last one. - *misloop = *(badface *)(* misseglist)[misseglist->items - 1]; - misseglist->items--; - // Now recover the segment. - p1 = (point) segloop.sh[3]; - p2 = (point) segloop.sh[4]; - if (b->verbose > 1) { - printf(" Recover segment (%d, %d).\n", pointmark(p1), pointmark(p2)); - } - getsearchtet(p1, p2, &searchtet, &tend); - collinear = finddirection(&searchtet, tend, tetrahedrons->items); - if (collinear == LEFTCOLLINEAR) { - checkpoint = apex(searchtet); - } else if (collinear == RIGHTCOLLINEAR) { - checkpoint = dest(searchtet); - } else if (collinear == TOPCOLLINEAR) { - checkpoint = oppo(searchtet); - } else { -#ifdef SELF_CHECK - assert(collinear == ACROSSFACE || collinear == ACROSSEDGE); -#endif - checkpoint = (point) NULL; - } - if (checkpoint != tend) { - // ab is missing. - splitpoint = (point) NULL; - if (checkpoint != (point) NULL) { - // An existing point c is found on the segment. It can happen when - // ab is defined by a long segment with c inside it. Use c to - // split ab. No new point is created. - splitpoint = checkpoint; - if (pointtype(checkpoint) == FREEVOLVERTEX) { - // c is not a segment vertex yet. It becomes NACUTEVERTEX. - setpointtype(splitpoint, NACUTEVERTEX); - } else if (pointtype(checkpoint) == ACUTEVERTEX) { - // c is an acute vertex. The definition of PLC is wrong. - } else if (pointtype(checkpoint) == NACUTEVERTEX) { - // c is an nonacute vertex. The definition of PLC is wrong. - } else { - // assert(0); - } - } else { - // Find a reference point p of ab. - refpoint = scoutrefpoint(&searchtet, tend); - if (pointtype(refpoint) == FREEVOLVERTEX) { - // p is an input point, check if it is nearly collinear with ab. - coll = iscollinear(p1, p2, refpoint, b->epsilon); - if (coll) { - // a, b, and p are collinear. We insert p into ab. p becomes - // a segment vertex with type NACUTEVERTEX. - splitpoint = refpoint; - setpointtype(splitpoint, NACUTEVERTEX); - } - } - if (splitpoint == (point) NULL) { - // Calculate a split point v using rule 1, or 2, or 3. - splitpoint = getsplitpoint(&segloop, refpoint); - - // Is there periodic boundary conditions? - if (checkpbcs) { - // Yes! Insert points on other segments of incident pbcgroups. - i = shellmark(segloop) - 1; - for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) { - makepoint(&sympoint); - symloc = getsegpbcsympoint(splitpoint, &segloop, sympoint, - &symsplitseg, segpglist[j]); -#ifdef SELF_CHECK - assert(symloc != OUTSIDE); -#endif - if ((symloc == ONEDGE) && (symsplitseg.sh != segloop.sh)) { -#ifdef SELF_CHECK - assert(symsplitseg.sh != dummysh); -#endif - setpointtype(sympoint, FREESEGVERTEX); - setpoint2sh(sympoint, sencode(symsplitseg)); - // Insert sympoint into DT. - pa = sorg(symsplitseg); - splittet.tet = dummytet; - // Find a good start point to search. - encodedtet = point2tet(pa); - if (encodedtet != (tetrahedron) NULL) { - decode(encodedtet, splittet); - if (isdead(&splittet)) { - splittet.tet = dummytet; - } - } - // Locate sympoint in DT. Do exact location. - success = insertsite(sympoint, &splittet, false, flipqueue); -#ifdef SELF_CHECK - assert(success != DUPLICATEPOINT); -#endif - if (success == OUTSIDEPOINT) { - inserthullsite(sympoint, &splittet, flipqueue); - } - if (steinerleft > 0) steinerleft--; - // Let sympoint remember splittet. - setpoint2tet(sympoint, encode(splittet)); - // Do flip in DT. - lawson(misseglist, flipqueue); - // Insert sympoint into F. - symsplitseg.shver = 0; - spivot(symsplitseg, symsplitsub); - // sympoint should on the edge of symsplitsub. - splitsubedge(sympoint, &symsplitsub, flipqueue); - // Do flip in facet. - flipsub(flipqueue); - // Insert the two subsegments. - symsplitseg.shver = 0; - insertsegment(&symsplitseg, misseglist); - senextself(symsplitseg); - spivotself(symsplitseg); - symsplitseg.shver = 0; - insertsegment(&symsplitseg, misseglist); - } else { // if (symloc == ONVERTEX) { - // The sympoint already exists. It is possible when two - // pbc groups are exactly the same. Omit this point. - pointdealloc(sympoint); - } - } - } - - // Insert 'splitpoint' into DT. - if (isdead(&searchtet)) searchtet.tet = dummytet; - success = insertsite(splitpoint, &searchtet, false, flipqueue); - if (success == OUTSIDEPOINT) { - // A convex hull edge is missing, and the inserting point lies - // (slightly) outside the convex hull due to the significant - // digits lost in the calculation. Enlarge the convex hull. - inserthullsite(splitpoint, &searchtet, flipqueue); - } - if (steinerleft > 0) steinerleft--; - // Remember a handle in 'splitpoint' to enhance the speed of - // consequent point location. - setpoint2tet(splitpoint, encode(searchtet)); - // Maintain Delaunayness in DT. - lawson(misseglist, flipqueue); - } - } - // Insert 'splitpoint' into F. - spivot(segloop, splitsh); - splitsubedge(splitpoint, &splitsh, flipqueue); - flipsub(flipqueue); - // Insert the two subsegments. - segloop.shver = 0; - insertsegment(&segloop, misseglist); - senextself(segloop); - spivotself(segloop); - segloop.shver = 0; - insertsegment(&segloop, misseglist); - } - } - - // Detach all segments from tets. - tetrahedrons->traversalinit(); - searchtet.tet = tetrahedrontraverse(); - while (searchtet.tet != (tetrahedron *) NULL) { - for (i = 0; i < 6; i++) { - searchtet.tet[8 + i] = (tetrahedron) dummysh; - } - searchtet.tet = tetrahedrontraverse(); - } - // No segments now. - checksubsegs = 0; - - if (b->verbose > 0) { - printf(" %ld protect points.\n", points->items - vertcount); - printf(" R1: %ld, R2: %ld, R3: %ld.\n", r1count, r2count, r3count); - } - - delete flipqueue; - delete misseglist; -} - -// -// End of segments recovery routines -// - -// -// Begin of facet recovery routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertsubface() Fix a subface in place. // -// // -// Search a subface s in current tetrahedralization T. If s is found a face // -// face of T, it is inserted into T. Return FALSE if s is not found in T. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::insertsubface(face* insertsh, triface* searchtet) -{ - triface spintet, symtet; - face testsh, testseg; - face spinsh, casin, casout; - point tapex, checkpoint; - enum finddirectionresult collinear; - int hitbdry; - - // Search an edge of s. - getsearchtet(sorg(*insertsh), sdest(*insertsh), searchtet, &checkpoint); - collinear = finddirection(searchtet, checkpoint, tetrahedrons->items); - if (collinear == LEFTCOLLINEAR) { - enext2self(*searchtet); - esymself(*searchtet); - } else if (collinear == TOPCOLLINEAR) { - fnextself(*searchtet); - enext2self(*searchtet); - esymself(*searchtet); - } - if (dest(*searchtet) != checkpoint) { - // The edge doesn't exist => s is missing. - return false; - } - - // Search s by spinning faces around the edge. - tapex = sapex(*insertsh); - spintet = *searchtet; - hitbdry = 0; - do { - if (apex(spintet) == tapex) { - // Found s in T. Check if s has already been inserted. - tspivot(spintet, testsh); - if (testsh.sh == dummysh) { - adjustedgering(spintet, CCW); - findedge(insertsh, org(spintet), dest(spintet)); - tsbond(spintet, *insertsh); - sym(spintet, symtet); // 'symtet' maybe outside, use it anyway. - sesymself(*insertsh); - tsbond(symtet, *insertsh); - } else { - // Found a duplicated subface (due to the redundant input). - if (!b->quiet) { - printf("Warning: Two subfaces are found duplicated at "); - printf("(%d, %d, %d)\n", pointmark(sorg(testsh)), - pointmark(sdest(testsh)), pointmark(sapex(testsh))); - printf(" Subface of facet #%d is deleted.\n", shellmark(*insertsh)); - // printf(" Hint: -d switch can find all duplicated facets.\n"); - } - shellfacedealloc(subfaces, insertsh->sh); - } - return true; - } - if (!fnextself(spintet)) { - hitbdry ++; - if (hitbdry < 2) { - esym(*searchtet, spintet); - if (!fnextself(spintet)) { - hitbdry ++; - } - } - } - } while (hitbdry < 2 && apex(spintet) != apex(*searchtet)); - - // s is missing. - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tritritest() Test if two triangles are intersecting in their interior. // -// // -// One triangle t1 is the face of 'checktet', the other t2 is given by three // -// corners 'p1', 'p2' and 'p3'. This routine calls tri_tri_inter() to detect // -// whether t1 and t2 exactly intersect in their interior. Cases like share a // -// vertex, share an edge, or coincidence are considered not intersect. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::tritritest(triface* checktet, point p1, point p2, point p3) -{ - point forg, fdest, fapex; - enum interresult intersect; - - forg = org(*checktet); - fdest = dest(*checktet); - fapex = apex(*checktet); - -#ifdef SELF_CHECK - REAL ax, ay, az, bx, by, bz; - REAL n[3]; - // face (torg, tdest, tapex) should not be degenerate. However p1, p2, - // and p3 may be collinear. Check it. - ax = forg[0] - fdest[0]; - ay = forg[1] - fdest[1]; - az = forg[2] - fdest[2]; - bx = forg[0] - fapex[0]; - by = forg[1] - fapex[1]; - bz = forg[2] - fapex[2]; - n[0] = ay * bz - by * az; - n[1] = az * bx - bz * ax; - n[2] = ax * by - bx * ay; - assert(fabs(n[0]) + fabs(n[1]) + fabs(n[2]) > 0.0); - // The components of n should not smaller than the machine epsilon. - - ax = p1[0] - p2[0]; - ay = p1[1] - p2[1]; - az = p1[2] - p2[2]; - bx = p1[0] - p3[0]; - by = p1[1] - p3[1]; - bz = p1[2] - p3[2]; - n[0] = ay * bz - by * az; - n[1] = az * bx - bz * ax; - n[2] = ax * by - bx * ay; - assert(fabs(n[0]) + fabs(n[1]) + fabs(n[2]) > 0.0); - // The components of n should not smaller than the machine epsilon. -#endif - - intersect = tri_tri_inter(forg, fdest, fapex, p1, p2, p3); - return intersect == INTERSECT; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// initializecavity() Initialize the cavity. // -// // -// A cavity C is bounded by a list of faces, called fronts. Each front f is // -// hold by a tet t adjacent to C, t is not in C (uninfected). If f is a hull // -// face, t does't exist, a fake tet t' is created to hold f. t' has the same // -// vertices as f but no opposite. t' will be removed automatically after C // -// is filled with new tets (by carvecavity()). // -// // -// The faces of C are given in two lists. 'floorlist' is a set of subfaces, // -// each subface has been oriented to face to the inside of C. 'ceillist' is // -// a set of tetrahedral faces. 'frontlist' returns the initialized fronts. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::initializecavity(list* floorlist, list* ceillist, - list* frontlist) -{ - triface neightet, casingtet; - triface faketet; - face worksh; - int i; - - // Initialize subfaces of C. - for (i = 0; i < floorlist->len(); i++) { - // Get a subface s. - worksh = * (face *)(* floorlist)[i]; -#ifdef SELF_CHECK - // Current side of s should be empty. - stpivot(worksh, neightet); - assert(neightet.tet == dummytet); -#endif - // Get the adjacent tet t. - sesymself(worksh); - stpivot(worksh, casingtet); - // Does t exist? - if (casingtet.tet == dummytet) { - // Create a fake tet t' to hold f temporarily. - maketetrahedron(&faketet); - setorg(faketet, sorg(worksh)); - setdest(faketet, sdest(worksh)); - setapex(faketet, sapex(worksh)); - setoppo(faketet, (point) NULL); // Indicates it is 'fake'. - tsbond(faketet, worksh); - frontlist->append(&faketet); - } else { - frontlist->append(&casingtet); - } - } - // Initialize tet faces of C. - for (i = 0; i < ceillist->len(); i++) { - // Get a tet face c. - neightet = * (triface *) (* ceillist)[i]; -#ifdef SELF_CHECK - // The tet of c must be inside C (going to be deleted). - assert(infected(neightet)); -#endif - // Get the adjacent tet t. - sym(neightet, casingtet); - // Does t exist? - if (casingtet.tet == dummytet) { - // No. Create a fake tet t' to hold f temporarily. - maketetrahedron(&faketet); - // Be sure that the vertices of t' are CCW oriented. - adjustedgering(neightet, CW); // CW edge ring. - setorg(faketet, org(neightet)); - setdest(faketet, dest(neightet)); - setapex(faketet, apex(neightet)); - setoppo(faketet, (point) NULL); // Indicates it is 'fake'. - // Bond t' to a subface if it exists. - tspivot(neightet, worksh); - if (worksh.sh != dummysh) { - sesymself(worksh); - tsbond(faketet, worksh); - } - // Bond c <--> t'. So we're able to find t' and remove it. - bond(faketet, neightet); - // c may become uninfected due to the bond(). - infect(neightet); - frontlist->append(&faketet); - } else { - frontlist->append(&casingtet); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// retrievenewtets() Retrieve the newly created tets. // -// // -// On input, 'newtetlist' contains at least one alive new tet. From this tet,// -// other new tets can be found by a broadth-first searching. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::retrievenewtets(list* newtetlist) -{ - triface searchtet, casingtet; - int i; - - // There may be dead tets due to flip32(). Delete them first. - for (i = 0; i < newtetlist->len(); i++) { - searchtet = * (triface *)(* newtetlist)[i]; - if (isdead(&searchtet)) { - newtetlist->del(i, 0); i--; - continue; - } - infect(searchtet); - } - // Find all new tets. - for (i = 0; i < newtetlist->len(); i++) { - searchtet = * (triface *)(* newtetlist)[i]; - for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { - sym(searchtet, casingtet); - if ((casingtet.tet != dummytet) && !infected(casingtet)) { - infect(casingtet); - newtetlist->append(&casingtet); - } - } - } - // Uninfect new tets. - for (i = 0; i < newtetlist->len(); i++) { - searchtet = * (triface *)(* newtetlist)[i]; - uninfect(searchtet); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunizecavvertices() Form a DT of the vertices of a cavity. // -// // -// 'floorptlist' and 'ceilptlist' are the vertices of the cavity. // -// // -// The tets of the DT are created directly in the pool 'tetrahedrons', i.e., // -// no auxiliary data structure and memory are required. The trick is at the // -// time they're created, there are no connections between them to the other // -// tets in the pool. You can imagine they form an ioslated island. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::delaunizecavvertices(triface* oldtet, list* floorptlist, - list* ceilptlist, list* newtetlist, queue* flipque) -{ - point *insertarray; - triface bakhulltet, newtet; - long bakhullsize; - long arraysize; - int bakchksub; - int i, j; - - // Prepare the array of points for inserting. - arraysize = floorptlist->len(); - if (ceilptlist != (list *) NULL) { - arraysize += ceilptlist->len(); - } - insertarray = new point[arraysize]; - for (i = 0; i < floorptlist->len(); i++) { - insertarray[i] = * (point *)(* floorptlist)[i]; - } - if (ceilptlist != (list *) NULL) { - for (j = 0; j < ceilptlist->len(); j++) { - insertarray[i + j] = * (point *)(* ceilptlist)[j]; - } - } - - // The incrflipdelaunay() is re-used. Backup global variables. - decode(dummytet[0], bakhulltet); - bakhullsize = hullsize; - bakchksub = checksubfaces; - checksubfaces = 0; - b->verbose--; - - // Form the DT by incremental flip Delaunay algorithm. Do not jump for - // point location, do not merge points. - incrflipdelaunay(oldtet, insertarray, arraysize, false, false, 0.0, flipque); - - // Get a tet in D. - decode(dummytet[0], newtet); - newtetlist->append(&newtet); - // Get all tets of D. - retrievenewtets(newtetlist); - - // Restore global variables. - dummytet[0] = encode(bakhulltet); - hullsize = bakhullsize; - checksubfaces = bakchksub; - b->verbose++; - - delete [] insertarray; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertauxsubface() Fix an auxilary subface in place. // -// // -// An auxilary subface s is fixed in D as it is a real subface, but s has no // -// vertices and neighbors. It has two uses: (1) it protects an identfied // -// front f in D; (2) it serves the link to bond a tet in C and f later. The // -// first neighbor of s (s->sh[0]) stores a pointer to f. // -// // -// 'front' is a front f of C. idfront' t is a tet in D where f is identified // -// be a face of it. s will be fixed between t and its neighbor. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::insertauxsubface(triface* front, triface* idfront) -{ - triface neightet; - face auxsh; - - // Create the aux subface s. - makeshellface(subfaces, &auxsh); - // Bond s <--> t. - tsbond(*idfront, auxsh); - // Does t's neighbor n exist? - sym(*idfront, neightet); - if (neightet.tet != dummytet) { - // Bond s <--> n. - sesymself(auxsh); - tsbond(neightet, auxsh); - } - // Let s remember f. - auxsh.sh[0] = (shellface) encode(*front); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutfront() Scout a face in D. // -// // -// Search a 'front' f in D. If f is found, return TRUE and the face of D is // -// returned in 'idfront'. Otherwise, return FALSE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::scoutfront(triface* front, triface* idfront, list* newtetlist) -{ - triface spintet; - point pa, pb, pc; - enum locateresult loc; - enum finddirectionresult col; - int hitbdry; - int i; - - // Let the front we're searching is abc. - pa = org(*front); - pb = dest(*front); - // Get a tet in D for searching. - *idfront = recenttet; - // Make sure the tet is valid (it may be killed by flips). - if (isdead(idfront)) { - // The tet is dead. Search a live tet in D. !!! - for (i = 0; i < newtetlist->len(); i++) { - recenttet = * (triface *)(* newtetlist)[i]; - if (!isdead(&recenttet)) break; - } - assert(i < newtetlist->len()); - } - - // Search a tet having vertex a. - loc = preciselocate(pa, idfront, (long) newtetlist->len()); - assert(loc == ONVERTEX); - recenttet = *idfront; - // Search a tet having edge ab. - col = finddirection(idfront, pb, (long) newtetlist->len()); - if (col == RIGHTCOLLINEAR) { - // b is just the destination. - } else if (col == LEFTCOLLINEAR) { - enext2self(*idfront); - esymself(*idfront); - } else if (col == TOPCOLLINEAR) { - fnextself(*idfront); - enext2self(*idfront); - esymself(*idfront); - } - - if (dest(*idfront) == pb) { - // Search a tet having face abc - pc = apex(*front); - spintet = *idfront; - hitbdry = 0; - do { - if (apex(spintet) == pc) { - // Found abc. Insert an auxilary subface s at idfront. - // insertauxsubface(front, &spintet); - *idfront = spintet; - return true; - } - if (!fnextself(spintet)) { - hitbdry ++; - if (hitbdry < 2) { - esym(*idfront, spintet); - if (!fnextself(spintet)) { - hitbdry ++; - } - } - } - if (apex(spintet) == apex(*idfront)) break; - } while (hitbdry < 2); - } - - // f is missing in D. - if (b->verbose > 2) { - printf(" Front (%d, %d, %d) is missing.\n", pointmark(pa), - pointmark(pb), pointmark(apex(*front))); - } - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// gluefronts() Glue two fronts together. // -// // -// This is a support routine for identifyfront(). Two fronts f and f1 are // -// found indentical. This is caused by the non-coplanarity of vertices of a // -// facet. Hence f and f1 are a subface and a tet. They are not fronts of the // -// cavity anymore. This routine glues f and f1 together. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::gluefronts(triface* front, triface* front1) -{ - face consh; - - // Glue f and f1 together. There're four cases: - // (1) both f and f1 are not fake; - // (2) f is not fake, f1 is fake; - // (3) f is fake and f1 is not fake; - // (4) both f and f1 are fake. - // Case (4) should be not possible. - - // Is there a concrete subface c at f. - tspivot(*front, consh); - if (consh.sh != dummysh) { - sesymself(consh); - tsbond(*front1, consh); // Bond: f1 <--> c. - sesymself(consh); - } - // Does f hold by a fake tet. - if (oppo(*front) == (point) NULL) { - // f is fake. Case (3) or (4). - assert(oppo(*front1) != (point) NULL); // Eliminate (4). - // Case (3). - if (consh.sh != dummysh) { - stdissolve(consh); // Dissolve: c -x-> f. - } - // Dealloc f. - tetrahedrondealloc(front->tet); - // f1 becomes a hull. let 'dummytet' bond to it. - dummytet[0] = encode(*front1); - } else { - // Case (1) or (2). - bond(*front, *front1); // Bond f1 <--> f. - } - // Is f a fake tet? - if (!isdead(front)) { - // No. Check for case (2). - tspivot(*front1, consh); - // Is f1 fake? - if (oppo(*front1) == (point) NULL) { - // Case (2) or (4) - assert(oppo(*front) != (point) NULL); // Eliminate (4). - // Case (2). - if (consh.sh != dummysh) { - stdissolve(consh); // Dissolve: c -x-> f1. - sesymself(consh); // Bond: f <--> c. - tsbond(*front, consh); - } - // Dissolve: f -x->f1. - dissolve(*front); - // Dealloc f1. - tetrahedrondealloc(front1->tet); - // f becomes a hull. let 'dummytet' bond to it. - dummytet[0] = encode(*front); - } else { - // Case (1). - if (consh.sh != dummysh) { - sesymself(consh); - tsbond(*front, consh); // Bond: f <--> c. - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// identifyfronts() Identify cavity faces in D. // -// // -// 'frontlist' are fronts of C need indentfying. This routine searches each // -// front f in D. Once f is found, an auxilary subface s is inserted in D at // -// the face. If f is not found in D, remove it from frontlist and save it in // -// 'misfrontlist'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::identifyfronts(list* frontlist, list* misfrontlist, - list* newtetlist) -{ - triface front, front1, tfront; - triface idfront, neightet; - face auxsh; - int len, i, j; - - misfrontlist->clear(); - // Set a new tet in D for searching. - recenttet = * (triface *)(* newtetlist)[0]; - - // Identify all fronts in D. - for (i = 0; i < frontlist->len(); i++) { - // Get a front f. - front = * (triface *)( *frontlist)[i]; - if (scoutfront(&front, &idfront, newtetlist)) { - // Found f. Insert an aux subface s. - assert((idfront.tet != dummytet) && !isdead(&idfront)); - // Does s already exist? - tspivot(idfront, auxsh); - if (auxsh.sh != dummysh) { - // There're two identical fronts, f (front) and f1 (s.sh[0])! - decode((tetrahedron) auxsh.sh[0], front1); - assert((front1.tet != dummytet) && !infected(front1)); - // Detach s in D. - tsdissolve(idfront); - sym(idfront, neightet); - if (neightet.tet != dummytet) { - tsdissolve(neightet); - } - // s has fulfilled its duty. Can be deleted. - shellfacedealloc(subfaces, auxsh.sh); - // Remove f from frontlist. - frontlist->del(i, 1); i--; - // Remove f1 from frontlist. - len = frontlist->len(); - for (j = 0; j < frontlist->len(); j++) { - tfront = * (triface *)(* frontlist)[j]; - if ((tfront.tet == front1.tet) && (tfront.loc == front1.loc)) { - // Found f1 in list. Check f1 != f. - assert((tfront.tet != front.tet) || (tfront.loc != front.loc)); - frontlist->del(j, 1); i--; - break; - } - } - assert((frontlist->len() + 1) == len); - // Glue f and f1 together. - gluefronts(&front, &front1); - } else { - // Insert an aux subface to protect f in D. - insertauxsubface(&front, &idfront); - } - } else { - // f is missing. - frontlist->del(i, 1); i--; - // Are there two identical fronts, f (front) and f1 (front1)? - for (j = 0; j < misfrontlist->len(); j++) { - front1 = * (triface *)(* misfrontlist)[j]; - if (isfacehaspoint(&front1, org(front)) && - isfacehaspoint(&front1, dest(front)) && - isfacehaspoint(&front1, apex(front))) break; - } - if (j < misfrontlist->len()) { - // Found an identical front f1. Remove f1 from the list. - misfrontlist->del(j, 1); - // Glue f and f1 together. - gluefronts(&front, &front1); - } else { - // Add f into misfrontlist. - misfrontlist->append(&front); - } - } - } - return misfrontlist->len() == 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// detachauxsubfaces() Detach auxilary subfaces in D. // -// // -// This is a reverse routine of identifyfronts(). Some fronts are missing in // -// D. C can not be easily tetrahedralized. It needs remediation (expansion, // -// or constrained flips, or adding a Steiner point). This routine detaches // -// the auxilary subfaces have been inserted in D and delete them. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::detachauxsubfaces(list* newtetlist) -{ - triface newtet, neightet; - face auxsh; - int i; - - for (i = 0; i < newtetlist->len(); i++) { - // Get a new tet t. - newtet = * (triface *)(* newtetlist)[i]; - // t may e dead due to flips. - if (isdead(&newtet)) continue; - assert(!infected(newtet)); - // Check the four faces of t. - for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { - tspivot(newtet, auxsh); - if (auxsh.sh != dummysh) { - // An auxilary subface s. - assert(sorg(auxsh) == (point) NULL); - tsdissolve(newtet); // t -x-> s. - sym(newtet, neightet); - if (neightet.tet != dummytet) { - assert(!isdead(&neightet)); - tsdissolve(neightet); // n -x-> s. - } - // Delete s. - shellfacedealloc(subfaces, auxsh.sh); - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// expandcavity() Expand the cavity by adding new fronts. // -// // -// This is the support routine for delaunizecavity(). Some fronts of C are // -// missing in D since they're not strongly Delaunay. Such fronts are removed // -// and the faces of the tets abutting to them are added. C is then expanded. // -// Some removed faces may be subfaces, they're queued to recover later. D is // -// expanded simultaneously with the new vertices of the new fronts. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::expandcavity(list* frontlist, list* misfrontlist, - list* newtetlist, list* crosstetlist, queue* missingshqueue, queue* flipque) -{ - triface misfront, newfront, casingtet, crosstet; - triface searchtet, faketet, bakhulltet; - face checksh; - point pd; - enum insertsiteresult success; - long bakhullsize; - int bakchksub; - int i, j, k; - - if (b->verbose > 1) { - printf(" Expand cavity (%d missing fronts).\n", misfrontlist->len()); - } - // Increase the number of expanded times. - expcavcount++; - // The incrflipdelaunay() is re-used. Backup global variables. - decode(dummytet[0], bakhulltet); - bakhullsize = hullsize; - bakchksub = checksubfaces; - checksubfaces = 0; - b->verbose--; - - // Choose a tet in D for searching. - recenttet = * (triface *)(* newtetlist)[0]; - assert((recenttet.tet != dummytet) && !isdead(&recenttet)); - - // Loop through 'misfrontlist'. - for (i = 0; i < misfrontlist->len(); i++) { - // Get a missing front f. - misfront = * (triface *)(* misfrontlist)[i]; - // C will be expanded at f. - if (b->verbose > 1) { - printf(" Get misfront (%d, %d, %d).\n", pointmark(org(misfront)), - pointmark(dest(misfront)), pointmark(apex(misfront))); - } - // Is f has a subface s? - tspivot(misfront, checksh); - if (checksh.sh != dummysh) { - // A subface s is found. Check whether f is expandable at s. - sym(misfront, crosstet); - if (!infected(crosstet)) { - // f is not expandable. In principle is should not happen. However, - // it can happen when PBC is in use. - assert(checkpbcs); - // Skip expanding f. It will be processed later. - continue; - } - // Temporarily remove s. Queue and recover it later. - if (b->verbose > 1) { - printf(" Queuing subface (%d, %d, %d).\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - // Detach s from tets at its both sides. - tsdissolve(misfront); - tsdissolve(crosstet); - // Detach tets at from s. - stdissolve(checksh); - sesymself(checksh); - stdissolve(checksh); - // Mark and queue it. - sinfect(checksh); - missingshqueue->push(&checksh); - } - // f may already be processed (become a cross tet of C). - if (infected(misfront)) continue; - // Get the point p = oppo(t), t is the tet holds f. - pd = oppo(misfront); -#ifdef SELF_CHECK - // t must not be fake. - assert(pd != (point) NULL); -#endif - // Insert p in D. p may not be inserted if it is one of the two cases: - // (1) p is already a vertex of D; - // (2) p lies outside the CH of D; - searchtet = recenttet; - // Make sure the tet is valid (it may be killed by flips). - if (isdead(&searchtet)) { - // The tet is dead. Get a live tet in D. !!! - for (j = 0; j < newtetlist->len(); j++) { - recenttet = * (triface *)(* newtetlist)[j]; - if (!isdead(&recenttet)) break; - } - assert(j < newtetlist->len()); - searchtet = recenttet; - } - success = insertsite(pd, &searchtet, false, flipque); - if (success == OUTSIDEPOINT) { - // case (2). Insert p onto CH of D. - inserthullsite(pd, &searchtet, flipque); - } - if (success != DUPLICATEPOINT) { - // p is inserted. Recover Delaunness of D by flips. - flip(flipque, NULL); - } - // Expand C by adding new fronts. The three faces of t which have p as a - // vertex become new fronts. However, if a new front is coincident with - // an old front of C, it is not added and the old front is removed. - adjustedgering(misfront, CCW); - for (j = 0; j < 3; j++) { - // Notice: Below I mis-used the names. 'newfront' is not exactly a new - // front, instead the 'casingtet' should be called new front. - // Get a new front f_n. - fnext(misfront, newfront); - // Get the neighbor tet n at f_n. - sym(newfront, casingtet); - // Is n a cross tet? - if (!infected(casingtet)) { - // f_n becomes a new front of C. - // Does n exist? - if (casingtet.tet == dummytet) { - // Create a fake tet n' to hold f_n temporarily. - maketetrahedron(&faketet); - // Be sure that the vertices of fake tet are CCW oriented. - adjustedgering(newfront, CW); // CW edge ring. - setorg(faketet, org(newfront)); - setdest(faketet, dest(newfront)); - setapex(faketet, apex(newfront)); - setoppo(faketet, (point) NULL); // Indicates it is 'fake'. - // Bond n' to a subface if it exists. - tspivot(newfront, checksh); - if (checksh.sh != dummysh) { - sesymself(checksh); - tsbond(faketet, checksh); - } - // Bond f_n <--> n'. So we're able to find n' and remove it. - bond(faketet, newfront); - frontlist->append(&faketet); - } else { - // Add n to frontlist. - frontlist->append(&casingtet); - } - } else { - // f_n is coincident with an existing front f' of C. f' is no longer - // a front, remove it from frontlist. Use the inverse order to - // search f' (most likely, a newly added front may be f'). - for (k = frontlist->len() - 1; k >= 0; k--) { - searchtet = * (triface *)(* frontlist)[k]; - if ((newfront.tet == searchtet.tet) && - (newfront.loc == searchtet.loc)) { - frontlist->del(k, 0); - break; - } - } - // Is f_n a subface? - tspivot(newfront, checksh); - if (checksh.sh != dummysh) { - // Temporarily remove checksh. Make it missing. recover it later. - if (b->verbose > 2) { - printf(" Queuing subface (%d, %d, %d).\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - tsdissolve(newfront); - tsdissolve(casingtet); - // Detach tets at the both sides of checksh. - stdissolve(checksh); - sesymself(checksh); - stdissolve(checksh); - sinfect(checksh); - missingshqueue->push(&checksh); - } - } - enextself(misfront); - } - // C has been expanded at f. t becomes a cross tet. - if (!infected(misfront)) { - // t will be deleted, queue it. - infect(misfront); - crosstetlist->append(&misfront); - } - } - - // Loop through misfrontlist, remove infected misfronts. - for (i = 0; i < misfrontlist->len(); i++) { - misfront = * (triface *)(* misfrontlist)[i]; - if (infected(misfront)) { - // Remove f, keep original list order. - misfrontlist->del(i, 1); - i--; - } - } - - // Are we done? - if (misfrontlist->len() > 0) { - // No. There are unexpandable fronts. - // expandcavity_sos(misfrontlist); - assert(0); // Not done yet. - } - - // D has been updated (by added new tets or dead tets) (due to flips). - retrievenewtets(newtetlist); - - // Restore global variables. - dummytet[0] = encode(bakhulltet); - hullsize = bakhullsize; - checksubfaces = bakchksub; - b->verbose++; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// carvecavity() Remove redundant (outside) tetrahedra from D. // -// // -// The fronts of C have been identified in D. Hence C can be tetrahedralized // -// by removing the tets outside C. The CDT is then updated by filling C with // -// the remaining tets (inside C) of D. // -// // -// Each front is protected by an auxilary subface s in D. s has a pointer to // -// f (s.sh[0]). f can be used to classified the in- and out- tets of C (the // -// CW orientation of f faces to the inside of C). The classified out-tets of // -// C are marked (infected) for removing. // -// // -// Notice that the out-tets may not only the tets on the CH of C, but also // -// tets completely inside D, eg., there is a "hole" in D. Such tets must be // -// marked during classification. The hole tets are poped up and removed too. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::carvecavity(list* newtetlist, list* outtetlist, - queue* flipque) -{ - triface newtet, neightet, front, outtet; - face auxsh, consh; - point pointptr; - REAL ori; - int i; - - // Clear work list. - outtetlist->clear(); - - // Classify in- and out- tets in D. Mark and queue classified out-tets. - for (i = 0; i < newtetlist->len(); i++) { - // Get a new tet t. - newtet = * (triface *)(* newtetlist)[i]; - assert(!isdead(&newtet)); - // Look for aux subfaces attached at t. - for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { - tspivot(newtet, auxsh); - if (auxsh.sh != dummysh) { - // Has this side a neighbor n? - sym(newtet, neightet); - if (neightet.tet != dummytet) { - // Classify t and n (one is "in" and another is "out"). - // Get the front f. - decode((tetrahedron) auxsh.sh[0], front); - // Let f face to the inside of C. - adjustedgering(front, CW); - ori = orient3d(org(front), dest(front), apex(front), oppo(newtet)); - assert(ori != 0.0); - if (ori < 0.0) { - // t is in-tet. n is out-tet. - outtet = neightet; - } else { - // n is in-tet. t is out-tet. - outtet = newtet; - } - // Add the out-tet into list. - if (!infected(outtet)) { - infect(outtet); - outtetlist->append(&outtet); - } - } - } - } - } - - // Find and mark all out-tets. - for (i = 0; i < outtetlist->len(); i++) { - outtet = * (triface *)(* outtetlist)[i]; - for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) { - sym(outtet, neightet); - // Does the neighbor exist and unmarked? - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Is it protected by an aux subface? - tspivot(outtet, auxsh); - if (auxsh.sh == dummysh) { - // It's an out-tet. - infect(neightet); - outtetlist->append(&neightet); - } - } - } - } - - // Remove the out- (and hole) tets. - for (i = 0; i < outtetlist->len(); i++) { - // Get an out-tet t. - outtet = * (triface *)(* outtetlist)[i]; - // Detach t from the in-tets. - for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) { - // Is there an aux subface s? - tspivot(outtet, auxsh); - if (auxsh.sh != dummysh) { - // Get the neighbor n. - sym(outtet, neightet); - assert(!infected(neightet)); // t must be in-tet. - // Detach n -x-> t. - dissolve(neightet); - } - } - // Dealloc the tet. - tetrahedrondealloc(outtet.tet); - } - - // Connect the in-tets of C to fronts. Remove aux subfaces and fake tets. - for (i = 0; i < newtetlist->len(); i++) { - // Get a new tet t. - newtet = * (triface *)(* newtetlist)[i]; - // t may be an out-tet and has got deleted. - if (isdead(&newtet)) continue; - // t is an in-tet. Look for aux subfaces attached at t. - for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { - // Is there an aux subface s? - tspivot(newtet, auxsh); - if (auxsh.sh != dummysh) { - // Get the front f. - decode((tetrahedron) auxsh.sh[0], front); - assert((front.tet != dummytet) && !infected(front)); - // s has fulfilled its duty. Can be deleted. - tsdissolve(newtet); // dissolve: t -x-> s. - // Delete s. - shellfacedealloc(subfaces, auxsh.sh); - // Connect the newtet t and front f. - // Is there a concrete subface c at f. - tspivot(front, consh); - if (consh.sh != dummysh) { - sesymself(consh); - // Bond: t <--> c. - tsbond(newtet, consh); - } - // Does f hold by a fake tet. - if (oppo(front) == (point) NULL) { - // f is fake. - if (consh.sh != dummysh) { - sesymself(consh); - // Dissolve: c -x-> f. - stdissolve(consh); - } - // Dealloc f. - tetrahedrondealloc(front.tet); - // f becomes a hull. let 'dummytet' bond to it. - dummytet[0] = encode(newtet); - } else { - // Bond t <--> f. - bond(newtet, front); - } - // t may be non-locally Delaunay and flipable. - if (flipque != (queue *) NULL) { - enqueueflipface(newtet, flipque); - } - } - } - // Let the corners of t2 point to it for fast searching. - pointptr = org(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = dest(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = apex(newtet); - setpoint2tet(pointptr, encode(newtet)); - pointptr = oppo(newtet); - setpoint2tet(pointptr, encode(newtet)); - } - // The cavity has been re-tetrahedralized. -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunizecavity() Tetrahedralize a cavity by Delaunay tetrahedra. // -// // -// The cavity C is bounded by a set of triangles in 'floorlist' (a list of // -// coplanar subfaces) and 'ceillist' (a list of tetrahedral faces lie above // -// the subfaces). 'floorptlist' and 'ceilptlist' are the vertices of C. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::delaunizecavity(list* floorlist, list* ceillist, - list* ceilptlist, list* floorptlist, list* frontlist, list* misfrontlist, - list* newtetlist, list* crosstetlist, queue* missingshqueue, queue* flipque) -{ - int vertnum; - - vertnum = floorptlist->len(); - vertnum += (ceilptlist != (list *) NULL ? ceilptlist->len() : 0); - if (b->verbose > 1) { - printf(" Delaunizing cavity (%d floors, %d ceilings, %d vertices).\n", - floorlist->len(), ceillist->len(), vertnum); - } - // Save the size of the largest cavity. - if ((floorlist->len() + ceillist->len()) > maxcavfaces) { - maxcavfaces = floorlist->len() + ceillist->len(); - } - if (vertnum > maxcavverts) { - maxcavverts = vertnum; - } - - // Clear these lists. - frontlist->clear(); - misfrontlist->clear(); - newtetlist->clear(); - - // Initialize the cavity C. - initializecavity(floorlist, ceillist, frontlist); - // Form the D of the vertices of C. - delaunizecavvertices(NULL, floorptlist, ceilptlist, newtetlist, flipque); - // Identify faces of C in D. - while (!identifyfronts(frontlist, misfrontlist, newtetlist)) { - // Remove protecting subfaces, keep new tets. - detachauxsubfaces(newtetlist); - // Expand C and updateing D. - expandcavity(frontlist, misfrontlist, newtetlist, crosstetlist, - missingshqueue, flipque); - } - // All fronts have identified in D. Get the shape of C by removing out - // tets of C. 'misfrontlist' is reused for removing out tets. - carvecavity(newtetlist, misfrontlist, NULL); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// formmissingregion() Form the missing region. // -// // -// 'missingsh' is a missing subface. Start from it we can form the missing // -// region R (a set of connected missing subfaces). Because all missing sub- // -// faces have been marked (infected) before. R can be formed by checking the // -// neighbors of 'missingsh', and the neighbors of the neighbors, and so on. // -// Stop checking further at either a segment or an unmarked subface. // -// // -// 'missingshlist' returns R. The edge ring of subfaces of R are oriented in // -// the same direction. 'equatptlist' returns the vertices of R, each vertex // -// is marked with '1' (in 'worklist'). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::formmissingregion(face* missingsh, list* missingshlist, - list* equatptlist, int* worklist) -{ - face neighsh, worksh, workseg; - point workpt[3]; - int idx, i, j; - - // Add 'missingsh' into 'missingshlist'. - missingshlist->append(missingsh); - // Save and mark its three vertices. - workpt[0] = sorg(*missingsh); - workpt[1] = sdest(*missingsh); - workpt[2] = sapex(*missingsh); - for (i = 0; i < 3; i++) { - idx = pointmark(workpt[i]) - in->firstnumber; - worklist[idx] = 1; - equatptlist->append(&workpt[i]); - } - // Temporarily uninfect it (avoid to save it again). - suninfect(*missingsh); - - // Find the other missing subfaces. - for (i = 0; i < missingshlist->len(); i++) { - // Get a missing subface. - worksh = * (face *)(* missingshlist)[i]; - // Check three neighbors of this face. - for (j = 0; j < 3; j++) { - sspivot(worksh, workseg); - if (workseg.sh == dummysh) { - spivot(worksh, neighsh); - if (sinfected(neighsh)) { - // Find a missing subface, adjust the face orientation. - if (sorg(neighsh) != sdest(worksh)) { - sesymself(neighsh); - } - if (b->verbose > 2) { - printf(" Add missing subface (%d, %d, %d).\n", - pointmark(sorg(neighsh)), pointmark(sdest(neighsh)), - pointmark(sapex(neighsh))); - } - missingshlist->append(&neighsh); - // Save and mark its apex. - workpt[0] = sapex(neighsh); - idx = pointmark(workpt[0]) - in->firstnumber; - // Has workpt[0] been added? - if (worklist[idx] == 0) { - worklist[idx] = 1; - equatptlist->append(&workpt[0]); - } - // Temporarily uninfect it (avoid to save it again). - suninfect(neighsh); - } - } - senextself(worksh); - } - } - - // R has been formed. Infect missing subfaces again. - for (i = 0; i < missingshlist->len(); i++) { - worksh = * (face *)(* missingshlist)[i]; - sinfect(worksh); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// rearrangesubfaces() Rearrange the set of subfaces of a missing region // -// so that they conform to the faces of DT. // -// // -// The missing region formed by subfaces of 'missingshlist' contains a set // -// of degenerate vertices, hence the set of subfaces don't match the set of // -// faces in DT. Instead of forcing them to present in DT, we re-arrange the // -// connection of them so that the new subfaces conform to the faces of DT. // -// 'boundedgelist' is a set of boundary edges of the region, these edges(may // -// be subsegments) must exist in DT. // -// // -// On completion, we have created and inserted a set of new subfaces which // -// conform to faces of DT. The set of old subfaces in 'missingshlist' are // -// deleted. The region vertices in 'equatptlist' are unmarked. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::rearrangesubfaces(list* missingshlist, list* boundedgelist, - list* equatptlist, int* worklist) -{ - link *boundedgelink; - link *newshlink; - triface starttet, spintet, neightet, worktet; - face shloop, newsh, neighsh, spinsh, worksh; - face workseg, casingin, casingout; - point torg, tdest, workpt; - point spt1, spt2, spt3; - enum finddirectionresult collinear; - enum shestype shtype; - REAL area; - bool matchflag, finishflag; - int shmark, pbcgp, idx, hitbdry; - int i, j; - - // Initialize the boundary edge link. - boundedgelink = new link(sizeof(face), NULL, 256); - // Initialize the new subface link. - newshlink = new link(sizeof(face), NULL, 256); - // Remember the type (skinny or not) of replaced subfaces. They should - // all have the same type since there is no segment inside the region. - worksh = * (face *)(* missingshlist)[0]; - shtype = shelltype(worksh); - // The following loop is only for checking purpose. - for (i = 1; i < missingshlist->len(); i++) { - worksh = * (face *)(* missingshlist)[i]; - assert(shelltype(worksh) == shtype); - } - // To avoid compilation warnings. - shmark = pbcgp = 0; - area = 0.0; - - // Create an initial boundary link. - for (i = 0; i < boundedgelist->len(); i++) { - shloop = * (face *)(* boundedgelist)[i]; - if (i == 0) { - // 'shmark' will be set to all new created subfaces. - shmark = shellmark(shloop); - if (b->quality && varconstraint) { - // area will be copied to all new created subfaces. - area = areabound(shloop); - } - if (checkpbcs) { - // pbcgp will be copied to all new created subfaces. - pbcgp = shellpbcgroup(shloop); - } - // Get the abovepoint of this facet. - abovepoint = facetabovepointarray[shellmark(shloop)]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&shloop); - } - } - sspivot(shloop, workseg); - if (workseg.sh == dummysh) { - // This edge is an interior edge. - spivot(shloop, neighsh); - boundedgelink->add(&neighsh); - } else { - // This side has a segment, the edge exists. - boundedgelink->add(&shloop); - } - } - - // Each edge ab of boundedgelink will be finished by finding a vertex c - // which is a vertex of the missing region, such that: - // (1) abc is inside the missing region, i.e., abc intersects at least - // one of missing subfaces (saved in missingshlist); - // (2) abc is not intersect with any previously created new subfaces - // in the missing region (saved in newshlink). - // After abc is created, it will be inserted into both the surface mesh - // and the DT. The boundedgelink will be updated, ab is removed, bc and - // ca will be added if they are open. - - while (boundedgelink->len() > 0) { - // Remove an edge (ab) from the link. - shloop = * (face *) boundedgelink->del(1); - // 'workseg' indicates it is a segment or not. - sspivot(shloop, workseg); - torg = sorg(shloop); // torg = a; - tdest = sdest(shloop); // tdest = b; - // Find a tetrahedron containing ab. - getsearchtet(torg, tdest, &starttet, &workpt); - collinear = finddirection(&starttet, workpt, tetrahedrons->items); - if (collinear == LEFTCOLLINEAR) { - enext2self(starttet); - esymself(starttet); - } else if (collinear == TOPCOLLINEAR) { - fnextself(starttet); - enext2self(starttet); - esymself(starttet); - } - assert(dest(starttet) == workpt); - // Checking faces around ab until a valid face is found. - matchflag = false; - spintet = starttet; - hitbdry = 0; - do { - workpt = apex(spintet); - idx = pointmark(workpt) - in->firstnumber; - if (worklist[idx] == 1) { - // (trog, tdest, workpt) is on the facet. Check if it satisfies (1). - finishflag = false; - for (i = 0; i < missingshlist->len(); i++) { - worksh = * (face *)(* missingshlist)[i]; - spt1 = sorg(worksh); - spt2 = sdest(worksh); - spt3 = sapex(worksh); - // Does bc intersect the face? - if (tri_edge_cop_inter(spt1, spt2, spt3, tdest, workpt, abovepoint) - == INTERSECT) { - finishflag = true; break; - } - // Does ca intersect the face? - if (tri_edge_cop_inter(spt1, spt2, spt3, workpt, torg, abovepoint) - == INTERSECT) { - finishflag = true; break; - } - // Does c inside the face? - if (tri_vert_cop_inter(spt1, spt2, spt3, workpt, abovepoint) - == INTERSECT) { - finishflag = true; break; - } - } - if (finishflag) { - // Satisfying (1). Check if it satisfies (2). - matchflag = true; - for (i = 0; i < newshlink->len() && matchflag; i++) { - worksh = * (face *) newshlink->getnitem(i + 1); - spt1 = sorg(worksh); - spt2 = sdest(worksh); - spt3 = sapex(worksh); - // Does bc intersect the face? - if (tri_edge_cop_inter(spt1, spt2, spt3, tdest, workpt, abovepoint) - == INTERSECT) { - matchflag = false; break; - } - // Does ca intersect the face? - if (tri_edge_cop_inter(spt1, spt2, spt3, workpt, torg, abovepoint) - == INTERSECT) { - matchflag = false; break; - } - // Does c inside the face? - if (tri_vert_cop_inter(spt1, spt2, spt3, workpt, abovepoint) - == INTERSECT) { - matchflag = false; break; - } - } - } - if (matchflag == true) { - // Satisfying both (1) and (2). Find abc. - break; - } - } - if (!fnextself(spintet)) { - hitbdry ++; - if (hitbdry < 2) { - esym(starttet, spintet); - if (!fnextself(spintet)) { - hitbdry ++; - } - } - } - } while (hitbdry < 2 && apex(spintet) != apex(starttet)); -#ifdef SELF_CHECK //GMSH - assert(matchflag == true); -#endif //GMSH - tspivot(spintet, neighsh); - if (neighsh.sh != dummysh) { - printf("Error: Invalid PLC.\n"); - printf(" Facet #%d and facet #%d overlap each other.\n", - shellmark(neighsh), shellmark(shloop)); - printf(" It might be caused by a facet is defined more than once.\n"); - printf(" Hint: Use -d switch to find all overlapping facets.\n"); - terminatetetgen(1); - //GMSH exit(1); - } - // The side of 'spintet' is at which a new subface will be attached. - adjustedgering(spintet, CCW); - // Create the new subface. - makeshellface(subfaces, &newsh); - setsorg(newsh, org(spintet)); - setsdest(newsh, dest(spintet)); - setsapex(newsh, apex(spintet)); - if (b->quality && varconstraint) { - setareabound(newsh, area); - } - if (checkpbcs) { - setshellpbcgroup(newsh, pbcgp); - } - setshellmark(newsh, shmark); - setshelltype(newsh, shtype); // It may be a skinny subface. - // Add newsh into newshlink for intersecting checking. - newshlink->add(&newsh); - // Insert it into the current mesh. - tsbond(spintet, newsh); - sym(spintet, neightet); - if (neightet.tet != dummytet) { - sesym(newsh, neighsh); - tsbond(neightet, neighsh); - } - // Insert it into the surface mesh. - sspivot(shloop, workseg); - if (workseg.sh == dummysh) { - sbond(shloop, newsh); - } else { - // There is a subsegment, 'shloop' is the subface which is going to - // die. Insert the 'newsh' at the place of 'shloop' into its face - // link, so as to dettach 'shloop'. The original connection is: - // -> casingin -> shloop -> casingout ->, it will be changed with: - // -> casingin -> newsh -> casingout ->. Pay attention to the - // case when this subsegment is dangling in the mesh, i.e., 'shloop' - // is bonded to itself. - spivot(shloop, casingout); - if (shloop.sh != casingout.sh) { - // 'shloop' is not bonded to itself. - spinsh = casingout; - do { - casingin = spinsh; - spivotself(spinsh); - } while (sapex(spinsh) != sapex(shloop)); - assert(casingin.sh != shloop.sh); - // Bond casingin -> newsh -> casingout. - sbond1(casingin, newsh); - sbond1(newsh, casingout); - } else { - // Bond newsh -> newsh. - sbond(newsh, newsh); - } - // Bond the segment. - ssbond(newsh, workseg); - } - // Check other two sides of this new subface. If a side is not bonded - // to any edge in the link, it will be added to the link. - for (i = 0; i < 2; i++) { - if (i == 0) { - senext(newsh, worksh); - } else { - senext2(newsh, worksh); - } - torg = sorg(worksh); - tdest = sdest(worksh); - finishflag = false; - for (j = 0; j < boundedgelink->len() && !finishflag; j++) { - neighsh = * (face *) boundedgelink->getnitem(j + 1); - if ((sorg(neighsh) == torg && sdest(neighsh) == tdest) || - (sorg(neighsh) == tdest && sdest(neighsh) == torg)) { - // Find a boundary edge. Bond them and exit the loop. - sspivot(neighsh, workseg); - if (workseg.sh == dummysh) { - sbond(neighsh, worksh); - } else { - // There is a subsegment, 'neighsh' is the subface which is - // going to die. Do the same as above for 'worksh'. - spivot(neighsh, casingout); - if (neighsh.sh != casingout.sh) { - // 'neighsh' is not bonded to itself. - spinsh = casingout; - do { - casingin = spinsh; - spivotself(spinsh); - } while (sapex(spinsh) != sapex(neighsh)); - assert(casingin.sh != neighsh.sh); - // Bond casingin -> worksh -> casingout. - sbond1(casingin, worksh); - sbond1(worksh, casingout); - } else { - // Bond worksh -> worksh. - sbond(worksh, worksh); - } - // Bond the segment. - ssbond(worksh, workseg); - } - // Remove this boundary edge from the link. - boundedgelink->del(j + 1); - finishflag = true; - } - } - if (!finishflag) { - // It's a new boundary edge, add it to link. - boundedgelink->add(&worksh); - } - } - } - - // Deallocate the set of old missing subfaces. - for (i = 0; i < missingshlist->len(); i++) { - worksh = * (face *)(* missingshlist)[i]; - shellfacedealloc(subfaces, worksh.sh); - } - // Unmark region vertices in 'worklist'. - for (i = 0; i < equatptlist->len(); i++) { - workpt = * (point *)(* equatptlist)[i]; - idx = pointmark(workpt) - in->firstnumber; - worklist[idx] = 0; - } - - delete boundedgelink; - delete newshlink; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutcrossingedge() Search an edge crossing the missing region. // -// // -// 'missingshlist' forms the missing region R. This routine searches for an // -// edge crossing R. It first forms a 'boundedgelist' consisting of the // -// boundary edges of R. Such edges are existing in CDT. A crossing edge is // -// found by rotating faces around one of the boundary edges. It is possible // -// there is no edge crosses R (e.g. R has a degenerate point set). // -// // -// If find a croosing edge, return TRUE, 'crossedgelist' contains this edge. // -// Otherwise, return FALSE. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::scoutcrossingedge(list* missingshlist, list* boundedgelist, - list* crossedgelist, int* worklist) -{ - triface starttet, spintet, worktet; - face startsh, neighsh, worksh, workseg; - point torg, tdest, tapex; - point workpt[3], pa, pb, pc; - enum finddirectionresult collinear; - REAL ori1, ori2; - bool crossflag; - int hitbdry; - int i, j, k; - - // Form the 'boundedgelist'. Loop through 'missingshlist', check each - // edge of these subfaces. If an edge is a segment or the neighbor - // subface is uninfected, add it to 'boundedgelist'. - for (i = 0; i < missingshlist->len(); i++) { - worksh = * (face *)(* missingshlist)[i]; - for (j = 0; j < 3; j++) { - sspivot(worksh, workseg); - if (workseg.sh == dummysh) { - spivot(worksh, neighsh); - if (!sinfected(neighsh)) { - boundedgelist->append(&worksh); - } - } else { - boundedgelist->append(&worksh); - } - senextself(worksh); - } - } - - crossflag = false; - // Find a crossing edge. It is possible there is no such edge. We need to - // loop through all edges of 'boundedgelist' for sure we don't miss any. - for (i = 0; i < boundedgelist->len() && !crossflag; i++) { - startsh = * (face *)(* boundedgelist)[i]; - // 'startsh' (abc) holds an existing edge of the DT, find it. - torg = sorg(startsh); - tdest = sdest(startsh); - tapex = sapex(startsh); - getsearchtet(torg, tdest, &starttet, &workpt[0]); - collinear = finddirection(&starttet, workpt[0], tetrahedrons->items); - if (collinear == LEFTCOLLINEAR) { - enext2self(starttet); - esymself(starttet); - } else if (collinear == TOPCOLLINEAR) { - fnextself(starttet); - enext2self(starttet); - esymself(starttet); - } -#ifdef SELF_CHECK - assert(dest(starttet) == workpt[0]); -#endif - // Now starttet holds edge ab. Find is edge de crossing R. - spintet = starttet; - hitbdry = 0; - do { - if (fnextself(spintet)) { - // splittet = abde. Check if de crosses abc. - workpt[1] = apex(spintet); // workpt[1] = d. - workpt[2] = oppo(spintet); // workpt[2] = e. - j = pointmark(workpt[1]) - in->firstnumber; - k = pointmark(workpt[2]) - in->firstnumber; - if (worklist[j] == 1) { - ori1 = 0.0; // d is a vertex of the missing region. - } else { - // Get the orientation of d wrt. abc. - ori1 = orient3d(torg, tdest, tapex, workpt[1]); - } - if (worklist[k] == 1) { - ori2 = 0.0; // e is a vertex of the missing region. - } else { - // Get the orientation of e wrt. abc. - ori2 = orient3d(torg, tdest, tapex, workpt[2]); - } - // Only do check if d and e locate on different sides of abc. - if (ori1 * ori2 < 0.0) { - // Check if de crosses any subface of R. - for (j = 0; j < missingshlist->len(); j++) { - worksh = * (face *)(* missingshlist)[j]; - pa = sorg(worksh); - pb = sdest(worksh); - pc = sapex(worksh); - crossflag = (tri_tri_inter(pa, pb, pc, workpt[0], workpt[1], - workpt[2]) == INTERSECT); - if (crossflag) { - // Find a crossing edge. We're done. - worktet = spintet; - adjustedgering(worktet, CCW); - enextfnextself(worktet); - enextself(worktet); - // Add this edge (worktet) into 'crossedgelist'. - crossedgelist->append(&worktet); - break; - } - } - if (crossflag) break; - } - if (apex(spintet) == apex(starttet)) break; - } else { - hitbdry++; - // It is only possible to hit boundary once. - if (hitbdry < 2) { - esym(starttet, spintet); - } - } - } while (hitbdry < 2); - } - - return crossflag; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// formcavity() Form the cavity for recovering the missing region. // -// // -// The cavity C is bounded by faces of current CDT. All tetrahedra inside C // -// will be removed, intead a set of constrained Delaunay tetrahedra will be // -// filled in and the missing region are recovered. // -// // -// 'missingshlist' contains a set of subfaces forming the missing region R. // -// C is formed by first finding all the tetrahedra in CDT that intersect the // -// relative interior of R; then deleting them from the CDT, this will form C // -// inside the CDT. At the beginning, 'crossedgelist' contains an edge which // -// is crossing R. All tets containing this edge must cross R. Start from it, // -// other crossing edges can be found incrementally. The discovered crossing // -// tets are saved in 'crosstetlist'. // -// // -// Notice that not all tets in 'crosstetlist' are crossing R. The discovered // -// tets are connected each other. However, there may be other tets crossing // -// R but disjoint with the found tets. Due to this fact we need to check the // -// 'missingshlist' once more. Only recover those subfaces which are crossed // -// by the set of discovered tets, i.e., R may be shrinked to conform the set // -// of discovered tets. The extra subfaces of R will be recovered later. // -// // -// Notice that some previous recovered subfaces may completely included in C.// -// This can happen when R is very big and these subfaces lie above R and so // -// close to it. Such subfaces have to be queued (and sinfected()) to recover // -// them later. Otherwise, we lost the connection to these subfaces forever. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::formcavity(list* missingshlist, list* crossedgelist, - list* equatptlist, list* crossshlist, list* crosstetlist, - list* belowfacelist, list* abovefacelist, list* horizptlist, - list* belowptlist, list* aboveptlist, queue* missingshqueue, int* worklist) -{ - triface starttet, spintet, neightet, worktet; - face startsh, neighsh, worksh, workseg; - point torg, tdest, tapex, workpt[3]; - REAL checksign, orgori, destori; - bool crossflag, inlistflag; - bool belowflag, aboveflag; - int idx, share; - int i, j, k; - - // Get a face at horizon. - startsh = * (face *)(* missingshlist)[0]; - torg = sorg(startsh); - tdest = sdest(startsh); - tapex = sapex(startsh); - - // Collect the set of crossing tetrahedra by rotating crossing edges. - for (i = 0; i < crossedgelist->len(); i++) { - // Get a tet abcd, ab is a crossing edge. - starttet = * (triface *)(* crossedgelist)[i]; - adjustedgering(starttet, CCW); - if (b->verbose > 2) { - printf(" Collect tets containing edge (%d, %d).\n", - pointmark(org(starttet)), pointmark(dest(starttet))); - } - orgori = orient3d(torg, tdest, tapex, org(starttet)); - destori = orient3d(torg, tdest, tapex, dest(starttet)); -#ifdef SELF_CHECK - assert(orgori * destori < 0.0); -#endif - spintet = starttet; - do { - // The face rotation should not meet boundary. - fnextself(spintet); - // Check the validity of the PLC. - tspivot(spintet, worksh); - if (worksh.sh != dummysh) { - printf("Error: Invalid PLC.\n"); - printf(" Two subfaces (%d, %d, %d) and (%d, %d, %d)\n", - pointmark(torg), pointmark(tdest), pointmark(tapex), - pointmark(sorg(worksh)), pointmark(sdest(worksh)), - pointmark(sapex(worksh))); - printf(" are found intersecting each other.\n"); - printf(" Hint: Use -d switch to find all intersecting facets.\n"); - terminatetetgen(1); - } - if (!infected(spintet)) { - if (b->verbose > 2) { - printf(" Add crossing tet (%d, %d, %d, %d).\n", - pointmark(org(spintet)), pointmark(dest(spintet)), - pointmark(apex(spintet)), pointmark(oppo(spintet))); - } - infect(spintet); - crosstetlist->append(&spintet); - } - // Check whether other two edges of 'spintet' is a crossing edge. - // It can be quickly checked from the apex of 'spintet', if it is - // not on the facet, then there exists a crossing edge. - workpt[0] = apex(spintet); - idx = pointmark(workpt[0]) - in->firstnumber; - if (worklist[idx] != 1) { - // Either edge (dest, apex) or edge (apex, org) crosses. - checksign = orient3d(torg, tdest, tapex, workpt[0]); -#ifdef SELF_CHECK - assert(checksign != 0.0); -#endif - if (checksign * orgori < 0.0) { - enext2(spintet, worktet); // edge (apex, org). - workpt[1] = org(spintet); - } else { -#ifdef SELF_CHECK - assert(checksign * destori < 0.0); -#endif - enext(spintet, worktet); // edge (dest, apex). - workpt[1] = dest(spintet); - } - // 'worktet' represents the crossing edge. Add it into list only - // it doesn't exist in 'crossedgelist'. - inlistflag = false; - for (j = 0; j < crossedgelist->len() && !inlistflag; j++) { - neightet = * (triface *)(* crossedgelist)[j]; - if (org(neightet) == workpt[0]) { - if (dest(neightet) == workpt[1]) inlistflag = true; - } else if (org(neightet) == workpt[1]) { - if (dest(neightet) == workpt[0]) inlistflag = true; - } - } - if (!inlistflag) { - crossedgelist->append(&worktet); - } - } - } while (apex(spintet) != apex(starttet)); - } - - // Identifying the boundary faces and vertices of C. Sort them into - // 'abovefacelist', 'aboveptlist, 'belowfacelist', and 'belowptlist', - // respectively. "above" and "below" are wrt.(torg, tdest, tapex). - for (i = 0; i < crosstetlist->len(); i++) { - // Get a tet abcd, ab is the crossing edge. - starttet = * (triface *)(* crosstetlist)[i]; -#ifdef SELF_CHECK - assert(infected(starttet)); -#endif - adjustedgering(starttet, CCW); - // abc and abd are sharing the crossing edge, the two neighbors must - // be crossing tetrahedra too. They can't be boundaries of C. - for (j = 0; j < 2; j++) { - if (j == 0) { - enextfnext(starttet, worktet); // Check bcd. - } else { - enext2fnext(starttet, worktet); // Check acd. - } - sym(worktet, neightet); - // If the neighbor doesn't exist or exists but doesn't be infected, - // it's a boundary face of C, save it. - if ((neightet.tet == dummytet) || !infected(neightet)) { - workpt[0] = org(worktet); - workpt[1] = dest(worktet); - workpt[2] = apex(worktet); - belowflag = aboveflag = false; - share = 0; - for (k = 0; k < 3; k++) { - idx = pointmark(workpt[k]) - in->firstnumber; - if (worklist[idx] == 0) { - // It's not a vertices of facet, find which side it lies. - checksign = orient3d(torg, tdest, tapex, workpt[k]); -#ifdef SELF_CHECK - assert(checksign != 0.0); -#endif - if (checksign > 0.0) { - // It lies "below" the facet wrt. 'startsh'. - worklist[idx] = 2; - belowptlist->append(&workpt[k]); - } else if (checksign < 0.0) { - // It lies "above" the facet wrt. 'startsh'. - worklist[idx] = 3; - aboveptlist->append(&workpt[k]); - } - } - if (worklist[idx] == 2) { - // This face lies "below" the facet wrt. 'startsh'. - belowflag = true; - } else if (worklist[idx] == 3) { - // This face lies "above" the facet wrt. 'startsh'. - aboveflag = true; - } else { -#ifdef SELF_CHECK - // In degenerate case, this face may just be the equator. - assert(worklist[idx] == 1); -#endif - share++; - } - } -#ifdef SELF_CHECK - // The degenerate case has been ruled out. - assert(share < 3); - // Only one flag is possible for a cavity face. - assert(belowflag ^ aboveflag); -#endif - if (belowflag) { - belowfacelist->append(&worktet); - } else if (aboveflag) { - abovefacelist->append(&worktet); - } - } - } - } - - // Shrink R if not all its subfaces are crossing by the discovered tets. - // 'crossshlist' and 'horizptlist' represent the set of subfaces and - // vertices of the shrinked missing region, respectively. - for (i = 0; i < missingshlist->len(); i++) { - worksh = * (face *)(* missingshlist)[i]; -#ifdef SELF_CHECK - assert(sinfected(worksh)); -#endif - workpt[0] = sorg(worksh); - workpt[1] = sdest(worksh); - workpt[2] = sapex(worksh); - crossflag = false; - for (j = 0; j < crosstetlist->len() && !crossflag; j++) { - // Get a tet abcd, ab is a crossing edge. - starttet = * (triface *)(* crosstetlist)[j]; - adjustedgering(starttet, CCW); - // Only need to check two sides of worktet. - for (k = 0; k < 2 && !crossflag; k++) { - if (k == 0) { - worktet = starttet; // Check abc. - } else { - fnext(starttet, worktet); // Check abd. - } - crossflag = tritritest(&worktet, workpt[0], workpt[1], workpt[2]); - } - } - if (crossflag) { - // 'worksh' is crossed by 'worktet', uninfect it. - suninfect(worksh); - crossshlist->append(&worksh); - // Add its corners into 'horizptlist'. - for (k = 0; k < 3; k++) { - idx = pointmark(workpt[k]) - in->firstnumber; - if (worklist[idx] != 4) { - worklist[idx] = 4; - horizptlist->append(&workpt[k]); - } - } - } - } - - // Check 'crossingtetlist'. Queue subfaces inside them. - for (i = 0; i < crosstetlist->len(); i++) { - starttet = * (triface *)(* crosstetlist)[i]; - for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) { - sym(starttet, neightet); - // If the neighbor exist and is infected, check it. - if ((neightet.tet != dummytet) && infected(neightet)) { - tspivot(starttet, worksh); - if (worksh.sh != dummysh) { - // Temporarily remove worksh. Make it missing. recover it later. - if (b->verbose > 2) { - printf(" Queuing subface (%d, %d, %d).\n", - pointmark(sorg(worksh)), pointmark(sdest(worksh)), - pointmark(sapex(worksh))); - } - tsdissolve(neightet); - tsdissolve(starttet); - // Detach tets at the both sides of this subface. - stdissolve(worksh); - sesymself(worksh); - stdissolve(worksh); - sinfect(worksh); - missingshqueue->push(&worksh); - } - } - } - } - - // Clear flags set in 'worklist'. - for (i = 0; i < equatptlist->len(); i++) { - workpt[0] = * (point *)(* equatptlist)[i]; - idx = pointmark(workpt[0]) - in->firstnumber; -#ifdef SELF_CHECK - assert((worklist[idx] == 1) || (worklist[idx] == 4)); -#endif - worklist[idx] = 0; - } - for (i = 0; i < belowptlist->len(); i++) { - workpt[0] = * (point *)(* belowptlist)[i]; - idx = pointmark(workpt[0]) - in->firstnumber; -#ifdef SELF_CHECK - assert(worklist[idx] == 2); -#endif - worklist[idx] = 0; - } - for (i = 0; i < aboveptlist->len(); i++) { - workpt[0] = * (point *)(* aboveptlist)[i]; - idx = pointmark(workpt[0]) - in->firstnumber; -#ifdef SELF_CHECK - assert(worklist[idx] == 3); -#endif - worklist[idx] = 0; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertallsubfaces() Insert all subfaces, queue missing subfaces. // -// // -// Loop through all subfaces, insert each into the DT. If one already exists,// -// bond it to the tetrahedra having it. Otherwise, it is missing, infect it // -// and save it in 'missingshqueue'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::insertallsubfaces(queue* missingshqueue) -{ - triface searchtet; - face subloop; - - searchtet.tet = (tetrahedron *) NULL; - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - if (!insertsubface(&subloop, &searchtet)) { - if (b->verbose > 1) { - printf(" Queuing subface (%d, %d, %d).\n", pointmark(sorg(subloop)), - pointmark(sdest(subloop)), pointmark(sapex(subloop))); - } - sinfect(subloop); - missingshqueue->push(&subloop); - } - subloop.sh = shellfacetraverse(subfaces); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainedfacets() Recover subfaces in a Delaunay tetrahedralization. // -// // -// This routine creates a CDT by incrementally updating a DT D into a CDT T. // -// The process of recovering facets can be imagined by "merging" the surface // -// mesh F into D. At the beginning, F and D are completely seperated. Some // -// faces of them are matching some are not because they are crossed by some // -// tetrahedra of D. The non-matching subfaces will be forced to appear in T // -// by locally retetrahedralizing the regions where F and D are intersecting. // -// // -// When a subface s of F is found missing in D, probably some other subfaces // -// near to s are missing too. The set of adjoining coplanar missing faces // -// forms a missing region R (R may not simply connected). // -// // -// There are two possibilities can result a mssing region R: (1) Some edges // -// of D cross R; (2) No edge of D crosses R, but some faces of D spans R, ie,// -// D is locally degenerate at R. In case (1), D is modified so that it resp- // -// ects R (done by a cavity retetrahedralization algorithm). In case (2), F // -// is modified so that the set of subfaces of R matches faces in D (done by // -// a face rearrangment algorithm). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::constrainedfacets() -{ - queue *missingshqueue, *flipque; - list *missingshlist, *equatptlist; - list *boundedgelist, *crossedgelist, *crosstetlist; - list *crossshlist, *belowfacelist, *abovefacelist; - list *horizptlist, *belowptlist, *aboveptlist; - list *frontlist, *misfrontlist, *newtetlist; - triface searchtet, worktet; - face subloop, worksh; - int *worklist; - int i; - - if (!b->quiet) { - printf("Constraining facets.\n"); - } - - // Initialize queues. - missingshqueue = new queue(sizeof(face)); - flipque = new queue(sizeof(badface)); - // Initialize the working lists. - missingshlist = new list(sizeof(face), NULL); - boundedgelist = new list(sizeof(face), NULL); - crossedgelist = new list(sizeof(triface), NULL); - equatptlist = new list("point *"); - crossshlist = new list(sizeof(face), NULL); - crosstetlist = new list(sizeof(triface), NULL); - belowfacelist = new list(sizeof(triface), NULL); - abovefacelist = new list(sizeof(triface), NULL); - horizptlist = new list("point *"); - belowptlist = new list("point *"); - aboveptlist = new list("point *"); - frontlist = new list(sizeof(triface), NULL); - misfrontlist = new list(sizeof(triface), NULL); - newtetlist = new list(sizeof(triface), NULL); - // Initialize the array for marking vertices. - worklist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) worklist[i] = 0; - - // Compute a mapping from points to tetrahedra for fast searching. - makepoint2tetmap(); - - // Match subfaces in D, queue all missing subfaces. - insertallsubfaces(missingshqueue); - - // Recover all missing subfaces. - while (!missingshqueue->empty()) { - // Get a queued face s. - subloop = * (face *) missingshqueue->pop(); - // s may have been deleted in a face rearrangment operation. - if (isdead(&subloop)) continue; - // s may have been recovered in a previous missing region. - if (!sinfected(subloop)) continue; - // s may match a face in D now due to previous transformations. - if (insertsubface(&subloop, &searchtet)) { - suninfect(subloop); - continue; - } - if (b->verbose > 1) { - printf(" Recover subface (%d, %d, %d).\n", pointmark(sorg(subloop)), - pointmark(sdest(subloop)), pointmark(sapex(subloop))); - } - // Form the missing region R containing s. - formmissingregion(&subloop, missingshlist, equatptlist, worklist); - // Is R crossing by any tetrahedron? - if (scoutcrossingedge(missingshlist, boundedgelist, crossedgelist, - worklist)) { - // Form the cavity C containing R. - formcavity(missingshlist, crossedgelist, equatptlist, crossshlist, - crosstetlist, belowfacelist, abovefacelist, horizptlist, - belowptlist, aboveptlist, missingshqueue, worklist); - // Recover the above part of C. - delaunizecavity(crossshlist, abovefacelist, aboveptlist, horizptlist, - frontlist, misfrontlist, newtetlist, crosstetlist, - missingshqueue, flipque); - // Inverse the direction of subfaces in R. - for (i = 0; i < crossshlist->len(); i++) { - worksh = * (face *)(* crossshlist)[i]; - sesymself(worksh); - * (face *)(* crossshlist)[i] = worksh; - } - // Recover the below part of C. - delaunizecavity(crossshlist, belowfacelist, belowptlist, horizptlist, - frontlist, misfrontlist, newtetlist, crosstetlist, - missingshqueue, flipque); - // Delete tetrahedra in C. - for (i = 0; i < crosstetlist->len(); i++) { - worktet = * (triface *)(* crosstetlist)[i]; - tetrahedrondealloc(worktet.tet); - } - // There may have some un-recovered subfaces of R. Put them back into - // queue. Otherwise, they will be missing on the boundary. - for (i = 0; i < missingshlist->len(); i++) { - worksh = * (face *)(* missingshlist)[i]; - if (sinfected(worksh)) { - // An unrecovered subface, put it back into queue. - missingshqueue->push(&worksh); - } - } - crossshlist->clear(); - belowfacelist->clear(); - abovefacelist->clear(); - horizptlist->clear(); - belowptlist->clear(); - aboveptlist->clear(); - crosstetlist->clear(); - } else { - // No. Rearrange subfaces of F conforming to that of D in R. It can - // happen when the facet has non-coplanar vertices. - rearrangesubfaces(missingshlist, boundedgelist, equatptlist, worklist); - } - // Clear all working lists. - missingshlist->clear(); - boundedgelist->clear(); - crossedgelist->clear(); - equatptlist->clear(); - } - - // Subfaces have been merged into D. - checksubfaces = 1; - - if (b->verbose > 0) { - printf(" The biggest cavity: %d faces, %d vertices\n", maxcavfaces, - maxcavverts); - printf(" Enlarged %d times\n", expcavcount); - } - - delete missingshqueue; - delete flipque; - delete missingshlist; - delete boundedgelist; - delete crossedgelist; - delete equatptlist; - delete crossshlist; - delete crosstetlist; - delete belowfacelist; - delete abovefacelist; - delete horizptlist; - delete belowptlist; - delete aboveptlist; - delete frontlist; - delete misfrontlist; - delete newtetlist; - delete [] worklist; -} - -// -// End of facet recovery routines -// - -// -// Begin of carving out holes and concavities routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// infecthull() Virally infect all of the tetrahedra of the convex hull // -// that are not protected by subfaces. Where there are // -// subfaces, set boundary markers as appropriate. // -// // -// Memorypool 'viri' is used to return all the infected tetrahedra. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::infecthull(memorypool *viri) -{ - triface tetloop, tsymtet; - tetrahedron **deadtet; - face hullface; - // point horg, hdest, hapex; - - if (b->verbose > 0) { - printf(" Marking concavities for elimination.\n"); - } - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Is this tetrahedron on the hull? - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - sym(tetloop, tsymtet); - if (tsymtet.tet == dummytet) { - // Is the tetrahedron protected by a subface? - tspivot(tetloop, hullface); - if (hullface.sh == dummysh) { - // The tetrahedron is not protected; infect it. - if (!infected(tetloop)) { - infect(tetloop); - deadtet = (tetrahedron **) viri->alloc(); - *deadtet = tetloop.tet; - break; // Go and get next tet. - } - } else { - // The tetrahedron is protected; set boundary markers if appropriate. - if (shellmark(hullface) == 0) { - setshellmark(hullface, 1); - /* - horg = sorg(hullface); - hdest = sdest(hullface); - hapex = sapex(hullface); - if (pointmark(horg) == 0) { - setpointmark(horg, 1); - } - if (pointmark(hdest) == 0) { - setpointmark(hdest, 1); - } - if (pointmark(hapex) == 0) { - setpointmark(hapex, 1); - } - */ - } - } - } - } - tetloop.tet = tetrahedrontraverse(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// plague() Spread the virus from all infected tets to any neighbors not // -// protected by subfaces. // -// // -// This routine identifies all the tetrahedra that will die, and marks them // -// as infected. They are marked to ensure that each tetrahedron is added to // -// the virus pool only once, so the procedure will terminate. 'viri' returns // -// all infected tetrahedra which are outside the domian. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::plague(memorypool *viri) -{ - tetrahedron **virusloop; - tetrahedron **deadtet; - triface testtet, neighbor; - face neighsh, testseg; - face spinsh, casingin, casingout; - int firstdadsub; - int i; - - if (b->verbose > 0) { - printf(" Marking neighbors of marked tetrahedra.\n"); - } - firstdadsub = 0; - // Loop through all the infected tetrahedra, spreading the virus to - // their neighbors, then to their neighbors' neighbors. - viri->traversalinit(); - virusloop = (tetrahedron **) viri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - // Temporarily uninfect this tetrahedron, not necessary. - uninfect(testtet); - // Check each of the tetrahedron's four neighbors. - for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { - // Find the neighbor. - sym(testtet, neighbor); - // Check for a shell between the tetrahedron and its neighbor. - tspivot(testtet, neighsh); - // Check if the neighbor is nonexistent or already infected. - if ((neighbor.tet == dummytet) || infected(neighbor)) { - if (neighsh.sh != dummysh) { - // There is a subface separating the tetrahedron from its neighbor, - // but both tetrahedra are dying, so the subface dies too. - // Before deallocte this subface, dissolve the connections between - // other subfaces, subsegments and tetrahedra. - neighsh.shver = 0; - if (!firstdadsub) { - firstdadsub = 1; // Report the problem once. - if (!b->quiet) { - printf("Warning: Detecting an open face (%d, %d, %d).\n", - pointmark(sorg(neighsh)), pointmark(sdest(neighsh)), - pointmark(sapex(neighsh))); - } - } - // For keep the same enext() direction. - findedge(&testtet, sorg(neighsh), sdest(neighsh)); - for (i = 0; i < 3; i++) { - sspivot(neighsh, testseg); - if (testseg.sh != dummysh) { - // A subsegment is found at this side, dissolve this subface - // from the face link of this subsegment. - testseg.shver = 0; - spinsh = neighsh; - if (sorg(spinsh) != sorg(testseg)) { - sesymself(spinsh); - } - spivot(spinsh, casingout); - if (casingout.sh == spinsh.sh) { - // This is a trivial face link, only 'neighsh' itself, - // the subsegment at this side is also died. - shellfacedealloc(subsegs, testseg.sh); - } else { - spinsh = casingout; - do { - casingin = spinsh; - spivotself(spinsh); - } while (spinsh.sh != neighsh.sh); - // Set the link casingin->casingout. - sbond1(casingin, casingout); - // Bond the subsegment anyway. - ssbond(casingin, testseg); - } - } - senextself(neighsh); - enextself(testtet); - } - if (neighbor.tet != dummytet) { - // Make sure the subface doesn't get deallocated again later - // when the infected neighbor is visited. - tsdissolve(neighbor); - } - // This subface has been separated. - if (in->mesh_dim > 2) { - shellfacedealloc(subfaces, neighsh.sh); - } else { - // Dimension is 2. keep it for output. - // Dissolve tets at both sides of this subface. - stdissolve(neighsh); - sesymself(neighsh); - stdissolve(neighsh); - } - } - } else { // The neighbor exists and is not infected. - if (neighsh.sh == dummysh) { - // There is no subface protecting the neighbor, infect it. - infect(neighbor); - // Ensure that the neighbor's neighbors will be infected. - deadtet = (tetrahedron **) viri->alloc(); - *deadtet = neighbor.tet; - } else { // The neighbor is protected by a subface. - // Remove this tetrahedron from the subface. - stdissolve(neighsh); - // The subface becomes a boundary. Set markers accordingly. - if (shellmark(neighsh) == 0) { - setshellmark(neighsh, 1); - } - // This side becomes hull. Update the handle in dummytet. - dummytet[0] = encode(neighbor); - } - } - } - // Remark the tetrahedron as infected, so it doesn't get added to the - // virus pool again. - infect(testtet); - virusloop = (tetrahedron **) viri->traverse(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// regionplague() Spread regional attributes and/or volume constraints // -// (from a .poly file) throughout the mesh. // -// // -// This procedure operates in two phases. The first phase spreads an attri- // -// bute and/or a volume constraint through a (facet-bounded) region. The // -// second phase uninfects all infected tetrahedra, returning them to normal. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh:: -regionplague(memorypool *regionviri, REAL attribute, REAL volume) -{ - tetrahedron **virusloop; - tetrahedron **regiontet; - triface testtet, neighbor; - face neighsh; - - if (b->verbose > 1) { - printf(" Marking neighbors of marked tetrahedra.\n"); - } - // Loop through all the infected tetrahedra, spreading the attribute - // and/or volume constraint to their neighbors, then to their neighbors' - // neighbors. - regionviri->traversalinit(); - virusloop = (tetrahedron **) regionviri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - // Temporarily uninfect this tetrahedron, not necessary. - uninfect(testtet); - if (b->regionattrib) { - // Set an attribute. - setelemattribute(testtet.tet, in->numberoftetrahedronattributes, - attribute); - } - if (b->varvolume) { - // Set a volume constraint. - setvolumebound(testtet.tet, volume); - } - // Check each of the tetrahedron's four neighbors. - for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { - // Find the neighbor. - sym(testtet, neighbor); - // Check for a subface between the tetrahedron and its neighbor. - tspivot(testtet, neighsh); - // Make sure the neighbor exists, is not already infected, and - // isn't protected by a subface, or is protected by a nonsolid - // subface. - if ((neighbor.tet != dummytet) && !infected(neighbor) - && (neighsh.sh == dummysh)) { - // Infect the neighbor. - infect(neighbor); - // Ensure that the neighbor's neighbors will be infected. - regiontet = (tetrahedron **) regionviri->alloc(); - *regiontet = neighbor.tet; - } - } - // Remark the tetrahedron as infected, so it doesn't get added to the - // virus pool again. - infect(testtet); - virusloop = (tetrahedron **) regionviri->traverse(); - } - - // Uninfect all tetrahedra. - if (b->verbose > 1) { - printf(" Unmarking marked tetrahedra.\n"); - } - regionviri->traversalinit(); - virusloop = (tetrahedron **) regionviri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - uninfect(testtet); - virusloop = (tetrahedron **) regionviri->traverse(); - } - // Empty the virus pool. - regionviri->restart(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removeholetets() Remove tetrahedra which are outside the domain. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::removeholetets(memorypool* viri) -{ - tetrahedron **virusloop; - triface testtet, neighbor; - point checkpt; - int *tetspernodelist; - int i, j; - - if (b->verbose > 0) { - printf(" Deleting marked tetrahedra.\n"); - } - - // Create and initialize 'tetspernodelist'. - tetspernodelist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) tetspernodelist[i] = 0; - - // Loop the tetrahedra list, counter the number of tets sharing each node. - tetrahedrons->traversalinit(); - testtet.tet = tetrahedrontraverse(); - while (testtet.tet != (tetrahedron *) NULL) { - // Increment the number of sharing tets for each endpoint. - for (i = 0; i < 4; i++) { - j = pointmark((point) testtet.tet[4 + i]); - tetspernodelist[j]++; - } - testtet.tet = tetrahedrontraverse(); - } - - viri->traversalinit(); - virusloop = (tetrahedron **) viri->traverse(); - while (virusloop != (tetrahedron **) NULL) { - testtet.tet = *virusloop; - // Record changes in the number of boundary faces, and disconnect - // dead tetrahedra from their neighbors. - for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) { - sym(testtet, neighbor); - if (neighbor.tet == dummytet) { - // There is no neighboring tetrahedron on this face, so this face - // is a boundary face. This tetrahedron is being deleted, so this - // boundary face is deleted. - hullsize--; - } else { - // Disconnect the tetrahedron from its neighbor. - dissolve(neighbor); - // There is a neighboring tetrahedron on this face, so this face - // becomes a boundary face when this tetrahedron is deleted. - hullsize++; - } - } - // Check the four corners of this tet if they're isolated. - for (i = 0; i < 4; i++) { - checkpt = (point) testtet.tet[4 + i]; - j = pointmark(checkpt); - tetspernodelist[j]--; - if (tetspernodelist[j] == 0) { - // If it is added volume vertex or '-j' is not used, delete it. - if ((pointtype(checkpt) == FREEVOLVERTEX) || !b->nojettison) { - setpointtype(checkpt, UNUSEDVERTEX); - unuverts++; - } - } - } - // Return the dead tetrahedron to the pool of tetrahedra. - tetrahedrondealloc(testtet.tet); - virusloop = (tetrahedron **) viri->traverse(); - } - - delete [] tetspernodelist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// assignregionattribs() Assign each tetrahedron a region number. // -// // -// This routine is called when '-AA' switch is specified. Every tetrahedron // -// of a (bounded) region will get a integer number to that region. Default, // -// regions are numbered as 1, 2, 3, etc. However, if a number has already // -// been used (set by user in the region section in .poly or .smesh), it is // -// skipped and the next available number will be used. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::assignregionattribs() -{ - list *regionnumlist; - list *regiontetlist; - triface tetloop, regiontet, neightet; - face checksh; - bool flag; - int regionnum, num; - int attridx, count; - int i; - - if (b->verbose > 0) { - printf(" Assign region numbers.\n"); - } - - regionnumlist = new list(sizeof(int), NULL, 256); - regiontetlist = new list(sizeof(triface), NULL, 1024); - attridx = in->numberoftetrahedronattributes; - - // Loop through all tets. Infect tets which already have a region number, - // and save the used numbers in 'regionnumlist'. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - if (!infected(tetloop)) { - regionnum = (int) elemattribute(tetloop.tet, attridx); - if (regionnum != 0.0) { - // Found a numbered region tet. - infect(tetloop); - regiontetlist->append(&tetloop); - // Found and infect all tets in this region. - for (i = 0; i < regiontetlist->len(); i++) { - regiontet = * (triface *)(* regiontetlist)[i]; - for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) { - // Is there a boundary face? - tspivot(regiontet, checksh); - if (checksh.sh == dummysh) { - sym(regiontet, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { -#ifdef SELF_CHECK - // neightet should have the same region number. Check it. - num = (int) elemattribute(neightet.tet, attridx); - assert(num == regionnum); -#endif - infect(neightet); - regiontetlist->append(&neightet); - } - } - } - } - // Add regionnum to list if it is not exist. - flag = false; - for (i = 0; i < regionnumlist->len() && !flag; i++) { - num = * (int *)(* regionnumlist)[i]; - flag = (num == regionnum); - } - if (!flag) regionnumlist->append(®ionnum); - // Clear list for the next region. - regiontetlist->clear(); - } - } - tetloop.tet = tetrahedrontraverse(); - } - - if (b->verbose > 0) { - printf(" %d user-specified regions.\n", regionnumlist->len()); - } - - // Now loop the tets again. Assign region numbers to uninfected tets. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - regionnum = 1; // Start region number. - count = 0; - while (tetloop.tet != (tetrahedron *) NULL) { - if (!infected(tetloop)) { - // An unassigned region tet. - count++; - do { - flag = false; - // Check if the region number has been used. - for (i = 0; i < regionnumlist->len() && !flag; i++) { - num = * (int *)(* regionnumlist)[i]; - flag = (num == regionnum); - } - if (flag) regionnum++; - } while (flag); - setelemattribute(tetloop.tet, attridx, (REAL) regionnum); - infect(tetloop); - regiontetlist->append(&tetloop); - // Found and infect all tets in this region. - for (i = 0; i < regiontetlist->len(); i++) { - regiontet = * (triface *)(* regiontetlist)[i]; - for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) { - // Is there a boundary face? - tspivot(regiontet, checksh); - if (checksh.sh == dummysh) { - sym(regiontet, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { -#ifdef SELF_CHECK - // neightet should have not been assigned yet. Check it. - num = (int) elemattribute(neightet.tet, attridx); - assert(num == 0); -#endif - setelemattribute(neightet.tet, attridx, (REAL) regionnum); - infect(neightet); - regiontetlist->append(&neightet); - } - } - } - } - regiontetlist->clear(); - regionnum++; // The next region number. - } - tetloop.tet = tetrahedrontraverse(); - } - - // Uninfect all tets. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { -#ifdef SELF_CHECK - assert(infected(tetloop)); -#endif - uninfect(tetloop); - tetloop.tet = tetrahedrontraverse(); - } - - if (b->verbose > 0) { - printf(" %d regions are numbered.\n", count); - } - - delete regionnumlist; - delete regiontetlist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// carveholes() Find the holes and infect them. Find the volume // -// constraints and infect them. Infect the convex hull. // -// Spread the infection and kill tetrahedra. Spread the // -// volume constraints. // -// // -// This routine mainly calls other routines to carry out all these functions.// -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::carveholes() -{ - memorypool *holeviri, *regionviri; - tetrahedron *tptr, **holetet, **regiontet; - triface searchtet, *holetets, *regiontets; - enum locateresult intersect; - int i; - - if (!b->quiet) { - printf("Removing unwanted tetrahedra.\n"); - if (b->verbose && (in->numberofholes > 0)) { - printf(" Marking holes for elimination.\n"); - } - } - - // Initialize a pool of viri to be used for holes, concavities. - holeviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0); - // Mark as infected any unprotected tetrahedra on the boundary. - infecthull(holeviri); - - if (in->numberofholes > 0) { - // Allocate storage for the tetrahedra in which hole points fall. - holetets = (triface *) new triface[in->numberofholes]; - // Infect each tetrahedron in which a hole lies. - for (i = 0; i < 3 * in->numberofholes; i += 3) { - // Ignore holes that aren't within the bounds of the mesh. - if ((in->holelist[i] >= xmin) && (in->holelist[i] <= xmax) - && (in->holelist[i + 1] >= ymin) - && (in->holelist[i + 1] <= ymax) - && (in->holelist[i + 2] >= zmin) - && (in->holelist[i + 2] <= zmax)) { - searchtet.tet = dummytet; - // Find a tetrahedron that contains the hole. - intersect = locate(&in->holelist[i], &searchtet); - if ((intersect != OUTSIDE) && (!infected(searchtet))) { - // Record the tetrahedron for processing carve hole. - holetets[i / 3] = searchtet; - } - } - } - // Infect the hole tetrahedron. This is done by marking the tet as - // infected and including the tetrahedron in the virus pool. - for (i = 0; i < in->numberofholes; i++) { - infect(holetets[i]); - holetet = (tetrahedron **) holeviri->alloc(); - *holetet = holetets[i].tet; - } - // Free up memory. - delete [] holetets; - } - - // Mark as infected all tets of the holes and concavities. - plague(holeviri); - // The virus pool contains all outside tets now. - - // Is -A switch in use. - if (b->regionattrib) { - // Assign every tetrahedron a regional attribute of zero. - tetrahedrons->traversalinit(); - tptr = tetrahedrontraverse(); - while (tptr != (tetrahedron *) NULL) { - setelemattribute(tptr, in->numberoftetrahedronattributes, 0.0); - tptr = tetrahedrontraverse(); - } - } - - if (in->numberofregions > 0) { - if (!b->quiet) { - if (b->regionattrib) { - if (b->varvolume) { - printf("Spreading regional attributes and volume constraints.\n"); - } else { - printf("Spreading regional attributes.\n"); - } - } else { - printf("Spreading regional volume constraints.\n"); - } - } - // Allocate storage for the tetrahedra in which region points fall. - regiontets = (triface *) new triface[in->numberofregions]; - // Find the starting tetrahedron for each region. - for (i = 0; i < in->numberofregions; i++) { - regiontets[i].tet = dummytet; - // Ignore region points that aren't within the bounds of the mesh. - if ((in->regionlist[5 * i] >= xmin) - && (in->regionlist[5 * i] <= xmax) - && (in->regionlist[5 * i + 1] >= ymin) - && (in->regionlist[5 * i + 1] <= ymax) - && (in->regionlist[5 * i + 2] >= zmin) - && (in->regionlist[5 * i + 2] <= zmax)) { - searchtet.tet = dummytet; - // Find a tetrahedron that contains the region point. - intersect = locate(&in->regionlist[5 * i], &searchtet); - if ((intersect != OUTSIDE) && (!infected(searchtet))) { - // Record the tetrahedron for processing after the - // holes have been carved. - regiontets[i] = searchtet; - } - } - } - // Initialize a pool to be used for regional attrs, and/or regional - // volume constraints. - regionviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0); - // Find and set all regions. - for (i = 0; i < in->numberofregions; i++) { - if (regiontets[i].tet != dummytet) { - // Make sure the tetrahedron under consideration still exists. - // It may have been eaten by the virus. - if (!isdead(&(regiontets[i]))) { - // Put one tetrahedron in the virus pool. - infect(regiontets[i]); - regiontet = (tetrahedron **) regionviri->alloc(); - *regiontet = regiontets[i].tet; - // Apply one region's attribute and/or volume constraint. - regionplague(regionviri, in->regionlist[5 * i + 3], - in->regionlist[5 * i + 4]); - // The virus pool should be empty now. - } - } - } - // Free up memory. - delete [] regiontets; - delete regionviri; - } - - // Now acutually remove the outside and hole tets. - removeholetets(holeviri); - // The mesh is nonconvex now. - nonconvex = 1; - - if (b->regionattrib) { - if (b->regionattrib > 1) { - // -AA switch. Assign each tet a region number (> 0). - assignregionattribs(); - } - // Note the fact that each tetrahedron has an additional attribute. - in->numberoftetrahedronattributes++; - } - - // Free up memory. - delete holeviri; -} - -// -// End of carving out holes and concavities routines -// - -// -// Begin of boundary Steiner points removing routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// replacepolygonsubs() Substitute the subfaces of a polygon. // -// // -// 'oldshlist' (T_old) contains the old subfaces of P. It will be replaced // -// by 'newshlist' (T_new) of new subfaces. Each boundary edge of P is bonded // -// to 'dummysh' in T_new. // -// // -// Notice that Not every boundary edge of T_new is able to bond to a subface,// -// e.g., when it is a segment recovered by removing a Steiner point in it. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::replacepolygonsubs(list* oldshlist, list* newshlist) -{ - face newsh, oldsh, spinsh; - face casingout, casingin; - face checkseg; - point pa, pb; - int i, j, k, l; - - for (i = 0; i < newshlist->len(); i++) { - // Get a new subface s. - newsh = * (face *)(* newshlist)[i]; - // Check the three edges of s. - for (k = 0; k < 3; k++) { - spivot(newsh, casingout); - // Is it a boundary edge? - if (casingout.sh == dummysh) { - // Find the old subface s_o having the same edge as s. - pa = sorg(newsh); - pb = sdest(newsh); - for (j = 0; j < oldshlist->len(); j++) { - oldsh = * (face *)(* oldshlist)[j]; - for (l = 0; l < 3; l++) { - if (((sorg(oldsh) == pa) && (sdest(oldsh) == pb)) || - ((sorg(oldsh) == pb) && (sdest(oldsh) == pa))) break; - senextself(oldsh); - } - if (l < 3) break; - } - // Is there a matched edge? - if (j < oldshlist->len()) { - // Get the neighbor subface s_out. - spivot(oldsh, casingout); - sspivot(oldsh, checkseg); - if (checkseg.sh != dummysh) { - // A segment. Insert s into the face ring, ie, s_in -> s -> s_out. - if (oldsh.sh != casingout.sh) { - // s is not bonded to itself. - spinsh = casingout; - do { - casingin = spinsh; - spivotself(spinsh); - } while (sapex(spinsh) != sapex(oldsh)); - assert(casingin.sh != oldsh.sh); - // Bond s_in -> s -> s_out (and dissolve s_in -> s_old -> s_out). - sbond1(casingin, newsh); - sbond1(newsh, casingout); - } else { - // Bond newsh -> newsh. - sbond(newsh, newsh); - } - // Bond the segment. - ssbond(newsh, checkseg); - } else { - // Bond s <-> s_out (and dissolve s_out -> s_old). - sbond(newsh, casingout); - } - // Unbound oldsh to indicate it's neighbor has been replaced. - // It will be used to indentfy the edge in the inverse. - sdissolve(oldsh); - ssdissolve(oldsh); - } - } - // Go to the next edge of s. - senextself(newsh); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// orientnewsubs() Orient new subfaces facing to the inside of cavity. // -// // -// 'newshlist' contains new subfaces of the cavity C (created by re-triangu- // -// lation the polygon P). They're not necessary facing to the inside of C. // -// 'orientsh', faces to the inside of C, is used to adjust new subfaces. The // -// normal of the new subfaces is returned in 'norm'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::orientnewsubs(list* newshlist, face* orientsh, REAL* norm) -{ - face *newsh; - point pa, pb, pc; - REAL ref[3], ori, len; - int i; - - // Calculate the normal of 'orientsh'. - pa = sorg(*orientsh); - pb = sdest(*orientsh); - pc = sapex(*orientsh); - facenormal(pa, pb, pc, norm, &len); - for (i = 0; i < 3; i++) ref[i] = pa[i] + norm[i]; - for (i = 0; i < 3; i++) norm[i] /= len; - - // Orient new subfaces. Let the normal above each one. - for (i = 0; i < newshlist->len(); i++) { - newsh = (face *)(* newshlist)[i]; - pa = sorg(*newsh); - pb = sdest(*newsh); - pc = sapex(*newsh); - ori = orient3d(pa, pb, pc, ref); - assert(ori != 0.0); - if (ori > 0.0) { - sesymself(*newsh); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainedflip() Flip a non-constrained face. // -// // -// 'flipface' f (abc) is a face we want to flip. In addition, if 'front' is // -// given (not a NULL), f is a crossface. f may not be flippable if it is one // -// of the following cases: // -// (1) f has an aux subface attached; // -// (2) f is on the convex hull; // -// (3) f is not locally Delaunay (f must be recovered by a previous flip, // -// we should keep it, otherwise, we may fall into a flip loop); // -// (4) f is T32 at ab, but abd or abe has an aux subface attached; // -// (5) f is T22 or T44 at ab, but abd, or abe, or abf has an aux subface // -// attached; // -// (6) f is unflipable at ab, and abd, abe, ... are all unflippable due to // -// the cases (1) - (5). // -// If f is a crssface ('front' != NULL) and it is unflipable due to case (3),// -// (4), (5) and (6). Try to flip the next crossing face of front first. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::constrainedflip(triface* flipface, triface* front, - queue* flipque) -{ - triface symface, spintet; - face checksh; - point pa, pb, pc, pd, pe; - enum fliptype fc; - REAL sign; - bool doflip; - int ia, ib, ic, id, ie; - int i; - - // (1) Is f protected by an (auxilary) subface? - tspivot(*flipface, checksh); - if (checksh.sh != dummysh) return false; - // (2) Is f on the convex hull? - sym(*flipface, symface); - if (symface.tet == dummytet) return false; - // (3) Is f not locally Delaunay? - adjustedgering(*flipface, CCW); - pa = dest(*flipface); - pb = org(*flipface); - pc = apex(*flipface); - pd = oppo(*flipface); - pe = oppo(symface); - // if (symbolic) { - ia = pointmark(pa); - ib = pointmark(pb); - ic = pointmark(pc); - id = pointmark(pd); - ie = pointmark(pe); - sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie); - assert(sign != 0.0); - // } else { - // sign = insphere(pa, pb, pc, pd, pe); - // } - if (sign <= 0.0) { - // Get the fliptype of f. - checksubfaces = 0; // switch off subface test. - fc = categorizeface(*flipface); - checksubfaces = 1; // switch on subface test. - if (fc == T23) { - doflip = true; - // Avoid one tet created by the 2-3 flip is nearly degenerate. - /* pc = oppo(*flipface); - pd = oppo(symface); - adjustedgering(*flipface, CCW); - for (i = 0; i < 3; i++) { - pa = org(*flipface); - pb = dest(*flipface); - ori = orient3d(pa, pb, pc, pd); - if (iscoplanar(pa, pb, pc, pd, ori, b->epsilon)) { - doflip = false; break; - } - enextself(*flipface); - } */ - if (doflip) { - flip23(flipface, flipque); - return true; - } - } else if (fc == T32) { - // (4) Is abd, or abe protected? - doflip = true; - spintet = *flipface; - for (i = 0; i < 2; i++) { - fnextself(spintet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - doflip = false; break; // f is protected. Unflipable. - } - } - if (doflip) { - flip32(flipface, flipque); - return true; - } - } else if (fc == T22 || fc == T44) { - // (5) Is abd, abe, or abf protected? - doflip = true; - if (fc == T22) { - for (i = 0; i < 2; i++) { - spintet = *flipface; - if (i == 1) { - esymself(spintet); - } - fnextself(spintet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - doflip = false; break; // f is protected. Unflipable. - } - } - } else if (fc == T44) { - spintet = *flipface; - for (i = 0; i < 3; i++) { - fnextself(spintet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - doflip = false; break; // f is protected. Unflipable. - } - } - } - if (doflip) { - flip22(flipface, flipque); - return true; - } - } else if (fc == N32) { - // Is f a crossface? - if (front != (triface *) NULL) { - // (6) Is any obstacle face (abd, or abe, ...) flipable? - spintet = *flipface; - while (fnextself(spintet)) { - if (apex(spintet) == apex(*flipface)) break; - // Check if spintet is flipable, no recursive. - if (constrainedflip(&spintet, NULL, flipque)) { - // One obstacle face has been flipped. - return true; - } - // Unflipable. Go to the next obstacle face. - findedge(&spintet, org(*flipface), dest(*flipface)); - } - } - } - } - - // f is unflipable. Is f a crossface? - if (front != (triface *) NULL) { - // Look if there is another crossface. - pa = org(*front); - pb = dest(*front); - pc = apex(*front); - // sym(*flipface, symface); - // Have we reach the end of abc (We've started from edge ab). - if (oppo(symface) != pc) { - adjustedgering(symface, CCW); - for (i = 0; i < 3; i++) { - fnext(symface, spintet); - // Is c ahead of this face? - sign = orient3d(org(spintet), dest(spintet), apex(spintet), pc); - if (sign < 0.0) { - if (tritritest(&spintet, pa, pb, pc)) { - if (b->verbose > 2) { - printf(" Next crossface (%d, %d, %d).\n", - pointmark(org(spintet)), pointmark(dest(spintet)), - pointmark(apex(spintet))); - } - return constrainedflip(&spintet, front, flipque); - // return constrainedflip(&spintet, NULL, flipque); - } - } - enextself(symface); - } - } - } - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// recoverfront() Recover a missing front by flips. // -// // -// 'front' f is missing in D - it was crossed by faces of D. The cross faces // -// may be flippable, so f can be recovered by flipping them away. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::recoverfront(triface* front, list* newtetlist, queue* flipque) -{ - triface idfront, starttet, spintet; - point pa, pb, pc, pd, ref; - enum locateresult loc; - enum finddirectionresult col; - REAL ori, ori1, ori2, sign; - int hitbdry; - int i, j; - - // Find an existing edge of f in D to start with. - for (i = 0; i < 3; i++) { - pa = org(*front); - pb = dest(*front); - // Get a tet for searching. - idfront = recenttet; - // Make sure the tet is valid (flip32() may kill a tet). - if (isdead(&idfront)) { - // The tet is dead. Get a live tet in D. !!! - for (j = 0; j < newtetlist->len(); j++) { - recenttet = * (triface *)(* newtetlist)[j]; - if (!isdead(&recenttet)) break; - } - assert(j < newtetlist->len()); - } - loc = preciselocate(pa, &idfront, (long) newtetlist->len()); - if (loc != ONVERTEX) { - // Do a brute-force search in D. - for (j = 0; j < newtetlist->len(); j++) { - idfront = * (triface *)(* newtetlist)[j]; - if (isdead(&idfront)) continue; - if (findorg(&idfront, pa)) break; - } - assert(j < newtetlist->len()); // a must belong to one tet. - } - recenttet = idfront; - // Search for a tet having edge ab. - col = finddirection(&idfront, pb, (long) newtetlist->len()); - if (col == BELOWHULL) { - // Do a brute-force search in D. - for (j = 0; j < newtetlist->len(); j++) { - idfront = * (triface *)(* newtetlist)[j]; - if (isdead(&idfront)) continue; - if (findorg(&idfront, pa)) { - assert(org(idfront) == pa); - if (dest(idfront) == pb) { - col = RIGHTCOLLINEAR; break; - } else if (apex(idfront) == pb) { - col = LEFTCOLLINEAR; break; - } else if (oppo(idfront) == pb) { - col = TOPCOLLINEAR; break; - } - } - } - } - if (col == RIGHTCOLLINEAR) { - // b is just the destination. - } else if (col == LEFTCOLLINEAR) { - enext2self(idfront); - esymself(idfront); - } else if (col == TOPCOLLINEAR) { - fnextself(idfront); - enext2self(idfront); - esymself(idfront); - } - if (dest(idfront) == pb) break; // Found. - // Missing. Go to the next edge of f. - enextself(*front); - } - if (i == 3) { - // All three edges of f are missing - unrecoverable. - return false; - } - - // Search for a tet having f (abc). - pc = apex(*front); - spintet = idfront; - hitbdry = 0; - do { - if (apex(spintet) == pc) { - // Found abc. Insert an auxilary subface s at idfront. - insertauxsubface(front, &spintet); - return true; - } - if (!fnextself(spintet)) { - hitbdry ++; - if (hitbdry < 2) { - esym(idfront, spintet); - if (!fnextself(spintet)) { - hitbdry ++; - } - } - } - if (apex(spintet) == apex(idfront)) break; - } while (hitbdry < 2); - - // Search for a crossing face to flip. - pd = apex(idfront); - assert(pd != pc); - // Decide the orientation of d with abc. - ori = orient3d(pa, pb, pc, pd); - if (ori < 0.0) { - // d is above abc. Rotate downwards. - esym(idfront, starttet); - sign = -1.0; - } else if (ori > 0.0) { - // d is below abc. Rotate upwards. - starttet = idfront; - sign = 1.0; - } else { - assert(ori == 0.0); - // d is coplanar with abc. Do abc and abd intersect? - ref = oppo(idfront); - ori1 = orient3d(pa, pb, ref, pc); - ori2 = orient3d(pa, pb, ref, pd); - assert(ori1 * ori2 != 0.0); - if (ori1 * ori2 > 0) { - // abc and abd intersect. There're two possible intersections: - // ad and bc, or ac and bd. Find it out. - ori1 = orient3d(pb, pc, ref, pd); - ori2 = orient3d(pb, pc, ref, pa); - assert(ori1 * ori2 != 0.0); - if (ori1 * ori2 > 0) { - // ac intersects bd. - enextself(idfront); // go to edge bd. - } else { - // ad intersects bc. - enext2self(idfront); // go to edge ad. - } - adjustedgering(idfront, CCW); - fnextself(idfront); // face ade or bce need a 4-to-4 flip. - if (b->verbose > 2) { - printf(" Get crossface (%d, %d, %d).\n", pointmark(org(idfront)), - pointmark(dest(idfront)), pointmark(apex(idfront))); - } - if (constrainedflip(&idfront, front, flipque)) { - // A crossface has been flipped. Continue to recover f. - return recoverfront(front, newtetlist, flipque); - } - // Unable to recover f. - return false; // sign = 0.0; - } else { - // Not intersect. We can go either direction. - starttet = idfront; - if (fnextself(starttet)) { - // Choose to rotate upwards. - sign = 1.0; - } else { - // Hit convex hull. Choose to rotate downwrads. - esym(idfront, starttet); - sign = -1.0; - } - } - } - - assert(sign != 0.0); - if (sign == -1) { - // The edge ab has be changed. Reverse it. - pa = org(starttet); - pb = dest(starttet); - // The sign has been reversed as well. - sign = -sign; - } - // Rotate face abd around edge ab. Moreover, we've chosen the rotate - // direction such that no convex hull face will be reach. - spintet = starttet; - while (fnextself(spintet)) { - pd = apex(spintet); - assert(pd != pc); - // Check if the orientation of d (with abc) has changed. - ori = orient3d(pa, pb, pc, pd); - if (ori == 0.0) { - // abc and abd must coplanar intersect (4-to-4 flip is needed). - ref = oppo(spintet); - ori1 = orient3d(pb, pc, ref, pd); - ori2 = orient3d(pb, pc, ref, pa); - assert(ori1 * ori2 != 0.0); - if (ori1 * ori2 > 0) { - // ac intersects bd. - enextself(spintet); // go to edge bd. - } else { - // ad intersects bc. - enext2self(spintet); // go to edge ad. - } - adjustedgering(spintet, CCW); - fnextself(spintet); // face ade or bce need a 4-to-4 flip. - if (b->verbose > 2) { - printf(" Get crossface (%d, %d, %d).\n", pointmark(org(spintet)), - pointmark(dest(spintet)), pointmark(apex(spintet))); - } - if (constrainedflip(&spintet, front, flipque)) { - // A crossface has been flipped. Continue to recover f. - return recoverfront(front, newtetlist, flipque); - } - // Unable to recover f. - return false; // sign = 0.0; - } else if (ori * sign < 0.0) { - // Sign has changed. The face dea or deb must cross abc. - adjustedgering(spintet, CCW); - enextself(spintet); - for (i = 0; i < 2; i++) { - // Get the face dea or deb. - fnext(spintet, starttet); - if (tritritest(&starttet, pa, pb, pc)) { - if (b->verbose > 2) { - printf(" Get crossface (%d, %d, %d).\n", - pointmark(org(starttet)), pointmark(dest(starttet)), - pointmark(apex(starttet))); - } - if (constrainedflip(&starttet, front, flipque)) { - // A crossface has been flipped. Continue to recover f. - return recoverfront(front, newtetlist, flipque); - } - } - enextself(spintet); - } - // Unable to recover f. - return false; - } - } - // Impossible to be here. - assert(0); - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// repairflips() Flip non-Delaunay and non-constrained faces. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::repairflips(queue* flipque) -{ - badface *qface; - triface flipface, symface, spintet; - face checksh; - point pa, pb, pc, pd, pe; - enum fliptype fc; - REAL sign; - long flipcount; - bool doflip; - int ia, ib, ic, id, ie; - int i; - - if (b->verbose > 1) { - printf(" Repair flip %ld faces.\n", flipque->len()); - } - flipcount = flip23s + flip32s + flip22s + flip44s; - // Loop until the queue is empty. - while (!flipque->empty()) { - qface = (badface *) flipque->pop(); - flipface = qface->tt; - // Check the validity of this face. - if (isdead(&flipface) || flipface.tet == dummytet || - (org(flipface) != qface->forg) || - (dest(flipface) != qface->fdest) || - (apex(flipface) != qface->fapex) || - (oppo(flipface) == (point) NULL)) continue; - // (1) Is f protected by an (auxilary) subface? - tspivot(flipface, checksh); - if (checksh.sh != dummysh) continue; - // (2) Is f on the convex hull? - sym(flipface, symface); - if (symface.tet == dummytet) continue; - // For positive orientation that insphere() test requires. - adjustedgering(flipface, CW); - pa = org(flipface); - pb = dest(flipface); - pc = apex(flipface); - pd = oppo(flipface); - pe = oppo(symface); - // if (symbolic) { - ia = pointmark(pa); - ib = pointmark(pb); - ic = pointmark(pc); - id = pointmark(pd); - ie = pointmark(pe); - sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie); - assert(sign != 0.0); - // } else { - // sign = insphere(pa, pb, pc, pd, pe); - // } - if (sign > 0.0) { - // f is non-lcally Delaunay. Get the fliptype of f. - checksubfaces = 0; // switch off subface test. - fc = categorizeface(flipface); - checksubfaces = 1; // switch on subface test. - if (fc == T23) { - doflip = true; - // Avoid to create a nearly degenerate tet. - /* pc = oppo(flipface); - pd = oppo(symface); - adjustedgering(flipface, CCW); - for (i = 0; i < 3; i++) { - pa = org(flipface); - pb = dest(flipface); - ori = orient3d(pa, pb, pc, pd); - if (iscoplanar(pa, pb, pc, pd, ori, b->epsilon)) { - doflip = false; break; - } - enextself(flipface); - } */ - if (doflip) { - flip23(&flipface, flipque); - } - } else if (fc == T32) { - // (4) Is abd, or abe protected? - doflip = true; - spintet = flipface; - for (i = 0; i < 2; i++) { - fnextself(spintet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - doflip = false; break; // f is protected. Unflipable. - } - } - if (doflip) { - flip32(&flipface, flipque); - } - } else if (fc == T22 || fc == T44) { - // (5) Is abd, abe, or abf protected? - doflip = true; - if (fc == T22) { - for (i = 0; i < 2; i++) { - spintet = flipface; - if (i == 1) { - esymself(spintet); - } - fnextself(spintet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - doflip = false; break; // f is protected. Unflipable. - } - } - } else if (fc == T44) { - spintet = flipface; - for (i = 0; i < 3; i++) { - fnextself(spintet); - tspivot(spintet, checksh); - if (checksh.sh != dummysh) { - doflip = false; break; // f is protected. Unflipable. - } - } - } - if (doflip) { - flip22(&flipface, flipque); - } - } - } - } - flipcount = flip23s + flip32s + flip22s + flip44s - flipcount; - if (b->verbose > 1) { - printf(" %ld flips.\n", flipcount); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainedcavity() Tetrahedralize a cavity by constrained tetrahedra. // -// // -// The cavity C is bounded by faces F in 'floorlist' and 'ceillist'. 'ptlist'// -// V is the set of vertices of C. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::constrainedcavity(triface* oldtet, list* floorlist, - list* ceillist, list* ptlist, list* frontlist, list* misfrontlist, - list* newtetlist, queue* flipque) -{ - triface misfront, newtet; - long facenum; - int i; - - if (b->verbose > 1) { - printf(" Constrained cavity (%d floors, %d ceilings, %d vertices).\n", - floorlist->len(), ceillist->len(), ptlist->len()); - } - - // symbolic = 1; - - // Initialize the cavity C. - initializecavity(floorlist, ceillist, frontlist); - // Form the D of the vertices of C. - delaunizecavvertices(oldtet, ptlist, NULL, newtetlist, flipque); - - // Identify faces of C in D. - if (!identifyfronts(frontlist, misfrontlist, newtetlist)) { - // Some faces are missing. - recenttet = * (triface *)(* newtetlist)[0]; - assert((recenttet.tet != dummytet) && !isdead(&recenttet)); - // Try to recover missing faces by flips. - do { - facenum = misfrontlist->len(); - for (i = 0; i < misfrontlist->len(); i++) { - // Get a missing front f. - misfront = * (triface *)(* misfrontlist)[i]; - // Let f face toward the inside of C. - adjustedgering(misfront, CW); - if (b->verbose > 1) { - printf(" Recover face (%d, %d, %d).\n", pointmark(org(misfront)), - pointmark(dest(misfront)), pointmark(apex(misfront))); - } - if (recoverfront(&misfront, newtetlist, flipque)) { - // f has been recovered. - frontlist->append(&misfront); - misfrontlist->del(i, 0); i--; - } - // Flip non-locally non-constrained Delaunay faces. - repairflips(flipque); - } - // Have all faces been recovered? - if (misfrontlist->len() == 0) break; - // No! There are still un-recovered faces. Continue the loop if any - // face has been recovered. - } while (misfrontlist->len() < facenum); - // Retrieve new tets and purge dead tets in D. - retrievenewtets(newtetlist); - } - - // symbolic = 0; - - if (misfrontlist->len() == 0) { - // All fronts have identified in D. Get the shape of C by removing out - // tets of C. 'misfrontlist' is reused for removing out tets. - // Don't do flip since the new tets may get deleted later. - carvecavity(newtetlist, misfrontlist, NULL); - // Recover locally Delaunay faces. - // flip(flipque, NULL); - return true; - } else { - // Fail to tetrahedralize C. - // Remove aux subfaces. - detachauxsubfaces(newtetlist); - // Remove new tets. - for (i = 0; i < newtetlist->len(); i++) { - newtet = * (triface *)(* newtetlist)[i]; - assert(!isdead(&newtet)); - tetrahedrondealloc(newtet.tet); - } - newtetlist->clear(); - // Restore faces of C in frontlist. - for (i = 0; i < misfrontlist->len(); i++) { - misfront = * (triface *)(* misfrontlist)[i]; - frontlist->append(&misfront); - } - return false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// expandsteinercavity() Expand the cavity of a Steiner point. // -// // -// Expand the cavity C if there fronts (except fronts having subfaces) which // -// are either (nearly) coplanar or invisible by the Steiner point. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::expandsteinercavity(point steinpt, REAL eps, list* frontlist, - list* oldtetlist) -{ - triface front, symfront, newfront, oldfront; - face frontsh; - point pa, pb, pc; - REAL ori; - bool expflag, newflag; - int i, j; - - do { - expflag = false; - for (i = 0; i < frontlist->len(); i++) { - // Get a front f. - front = * (triface *)(* frontlist)[i]; - // f can be expanded if it is not a subface. - tspivot(front, frontsh); - if (frontsh.sh == dummysh) { - // Let f face to the inside of C. - adjustedgering(front, CW); - pa = org(front); - pb = dest(front); - pc = apex(front); - ori = orient3d(pa, pb, pc, steinpt); - if (ori != 0.0) { - if (iscoplanar(pa, pb, pc, steinpt, ori, eps)) { - ori = 0.0; // f is nearly coplanar with p. - } - } - if (ori >= 0.0) { - // f is either invisible or coplanar with p. - if (b->verbose > 2) { - printf(" Remove front (%d, %d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(pc)); - } - frontlist->del(i, 1); - expflag = true; - break; - } - } - } - if (expflag) { - assert(!infected(front) && (oppo(front) != NULL)); - // Expand C at f by including new fronts. - adjustedgering(front, CCW); - for (i = 0; i < 3; i++) { - newflag = true; - // Get a new boundary n of the cavity. - fnext(front, symfront); - tspivot(symfront, frontsh); - sym(symfront, newfront); - if (frontsh.sh == dummysh) { - assert(newfront.tet != dummytet); - // Is n a front of the unexp. cavity? - if (infected(newfront)) { - for (j = 0; j < frontlist->len(); j++) { - oldfront = * (triface *)(* frontlist)[j]; - if ((oldfront.tet == symfront.tet) && - (oldfront.loc == symfront.loc)) { - // n is not a front anymore. - if (b->verbose > 2) { - printf(" Remove front (%d, %d, %d).\n", - pointmark(org(oldfront)), pointmark(dest(oldfront)), - pointmark(apex(oldfront))); - } - frontlist->del(j, 1); - newflag = false; - break; - } - } - } - } else { - // n is a subface. - if (newfront.tet == dummytet) { - sesymself(frontsh); - // Create a fake tet to hold n. - maketetrahedron(&newfront); - setorg(newfront, sorg(frontsh)); - setdest(newfront, sdest(frontsh)); - setapex(newfront, sapex(frontsh)); - setoppo(newfront, (point) NULL); - tsbond(newfront, frontsh); - } else { - // n should not be a front of cavity yet. - assert(!infected(newfront)); - } - } - if (newflag) { - if (b->verbose > 2) { - printf(" Add front (%d, %d, %d).\n", pointmark(org(newfront)), - pointmark(dest(newfront)), pointmark(apex(newfront))); - } - frontlist->append(&newfront); - } - enextself(front); - } - // Add f into oldtetlist (to be deleted). - infect(front); - oldtetlist->append(&front); - expcavcount++; - } - } while (expflag); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// findrelocatepoint() Find new location for relocating a point. // -// // -// 'frontlist' contains the boundary faces of the cavity C. Some fronts are // -// visible by 'stpt' p, some are coplanar with p. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::findrelocatepoint(point sp, point np, REAL* n, - list* frontlist, list* oldtetlist) -{ - triface front; - point pa, pb, pc; - REAL tp[3], tvol, mvol; - REAL ori, eps; - bool visible; - int i, j, k; - - if (b->verbose > 1) { - printf(" Find new location for point %d.\n", pointmark(sp)); - } - - // Avoid compilation warnings. - tvol = mvol = 0.0; - visible = false; - - eps = b->epsilon; - // Initialize np far enough from p (outside C). - for (i = 0; i < 3; i++) np[i] = sp[i] + longest * n[i]; - // Let tp = np; - for (i = 0; i < 3; i++) tp[i] = np[i]; - // Interation to adjust np until it is visible by all fronts. - j = 0; - do { - for (i = 0; i < frontlist->len(); i++) { - // Get a front face f. - front = * (triface *)(* frontlist)[i]; - // Let f face to the interior of C. - adjustedgering(front, CW); - pa = org(front); - pb = dest(front); - pc = apex(front); - ori = orient3d(pa, pb, pc, np); - visible = (ori < 0.0); - if (!visible) { - // A front is invisible by np. Move it towards p along the normal. - for (i = 0; i < 3; i++) np[i] = sp[i] + 0.5 * (sp[i] - np[i]); - // Failed if tp = np. - if ((tp[0] == np[0]) && (tp[1] == np[1]) && (tp[2] == np[2])) { - // Try to expand the cavity. - expandsteinercavity(sp, eps, frontlist, oldtetlist); - eps *= 10.0; - if (eps > b->epsilon * 1000.0) { - // printf("Internal error: Fail to relocate pt %d.\n",pointmark(sp)); - // internalerror(); - return false; - } - // Restart the point relocation. - for (i = 0; i < 3; i++) np[i] = sp[i] + longest * n[i]; - } - if (j % 2) { - // Set tp = np (at every 2 steps) to catch the stop state. - for (i = 0; i < 3; i++) tp[i] = np[i]; - } - break; - } else { - // Save the smallest volume. - if (i == 0) { - mvol = fabs(ori); - } else { - mvol = fabs(ori) < mvol ? fabs(ori) : mvol; - } - } - } - j++; - } while (!visible); - - if (b->verbose > 1) { - printf(" %d iterations. minvol = %.12g.\n", j, mvol); - } - - // Continue to adjust np until the minimal volume of tets formed by - // fronts and np doesn't increase (all fronts are visible by np). - k = 0; - do { - j = 0; - do { - if (k == 0) { - // Initial tp := np + 0.9 * (p - np). Move toward p. - for (i = 0; i < 3; i++) tp[i] = sp[i] + 0.9 * (np[i] - sp[i]); - } else { - // Initial tp := np + 1.1 * (p - np). Move away from p. - for (i = 0; i < 3; i++) tp[i] = sp[i] + 1.1 * (np[i] - sp[i]); - } - // Get the minial volume formed by tp with one of the fronts. - for (i = 0; i < frontlist->len(); i++) { - // Get a front face f. - front = * (triface *)(* frontlist)[i]; - // Let f face to the interior of C. - adjustedgering(front, CW); - pa = org(front); - pb = dest(front); - pc = apex(front); - ori = orient3d(pa, pb, pc, tp); - visible = (ori < 0.0); - if (visible) { - // Save the smallest volume. - if (i == 0) { - tvol = fabs(ori); - } else { - tvol = fabs(ori) < tvol ? fabs(ori) : tvol; - } - } else { - // A front is invisible by tp. Stop. - tvol = 0.0; - break; - } - } - if (tvol > mvol) { - // Get a larger minimal volume. - for (i = 0; i < 3; i++) np[i] = tp[i]; - mvol = tvol; - } else { - // Minimal volume decreases. Stop. - break; - } - // Continue to adjust np. - j++; - } while (true); - // Has np been adjusted? - if (j > 0) break; - // Try to move np to anoter direction. - k++; - } while (k < 2); - - if (b->verbose > 1) { - printf(" %d adjust iterations. minvol = %.12g.\n", j, mvol); - } - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// relocatepoint() Relocate a point into the cavity. // -// // -// 'frontlist' contains the boundary faces of the cavity C. All fronts must // -// be visible by 'steinpt'. Some fronts may hold by 'fake' tets (they are // -// hull faces). Fake tets will be removed when they're finished. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist, - list* newtetlist, queue* flipque) -{ - triface front, newtet, newface, neightet; - face checksh; - point pa, pb; - REAL attrib, volume; - bool bdflag; - int i, j, k, l; - - if (b->verbose > 1) { - printf(" Insert Steiner point (%.12g, %.12g, %.12g) %d.\n", - steinpt[0], steinpt[1], steinpt[2], pointmark(steinpt)); - } - // Clear the list first. - newtetlist->clear(); - - // Create the tets formed by fronts and 'steinpt'. - for (i = 0; i < frontlist->len(); i++) { - // Get a front f. - front = * (triface *)(* frontlist)[i]; - // Let f face inside C. (f is a face of tet adjacent to C). - adjustedgering(front, CW); - if (b->verbose > 2) { - printf(" Get front (%d, %d, %d).\n", pointmark(org(front)), - pointmark(dest(front)), pointmark(apex(front))); - } - maketetrahedron(&newtet); - newtetlist->append(&newtet); - setorg(newtet, org(front)); - setdest(newtet, dest(front)); - setapex(newtet, apex(front)); - setoppo(newtet, steinpt); - if (oldtet != (triface *) NULL) { - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(oldtet->tet, j); - setelemattribute(newtet.tet, j, attrib); - } - if (b->varvolume) { - volume = volumebound(oldtet->tet); - setvolumebound(newtet.tet, volume); - } - } - // 'front' may be a 'fake' tet. - tspivot(front, checksh); - if (oppo(front) == (point) NULL) { - if (checksh.sh != dummysh) { - stdissolve(checksh); - } - // Dealloc the 'fake' tet. - tetrahedrondealloc(front.tet); - // This side (newtet) is a boundary face, let 'dummytet' bond to it. - // Otherwise, 'dummytet' may point to a dead tetrahedron after the - // old cavity tets are removed. - dummytet[0] = encode(newtet); - } else { - // Bond two tetrahedra, also dissolve the old bond at 'front'. - bond(newtet, front); - } - if (checksh.sh != dummysh) { - sesymself(checksh); - tsbond(newtet, checksh); - } - if (flipque != (queue *) NULL) { - // f may be non-locally Delaunay and flipable. - enqueueflipface(newtet, flipque); - } - // The three neighbors are open. Will be finished later. - } - - // Connect new tets in C. All connecting faces must contain 'steinpt'. - for (i = 0; i < newtetlist->len(); i++) { - newtet = * (triface *)(* newtetlist)[i]; - newtet.ver = 0; - for (j = 0; j < 3; j++) { - fnext(newtet, newface); - sym(newface, neightet); - if (neightet.tet == dummytet) { - // Find a neightet to connect it. - bdflag = false; - pa = org(newface); - pb = dest(newface); - assert(apex(newface) == steinpt); - for (k = i + 1; k < newtetlist->len() && !bdflag; k++) { - neightet = * (triface *)(* newtetlist)[k]; - neightet.ver = 0; - for (l = 0; l < 3; l++) { - if ((org(neightet) == pa && dest(neightet) == pb) || - (org(neightet) == pb && dest(neightet) == pa)) { - // Find the neighbor. - fnextself(neightet); - assert(apex(neightet) == steinpt); - // Now neightet is a face same as newface, bond them. - bond(newface, neightet); - bdflag = true; - break; - } - enextself(neightet); - } - } - assert(bdflag); - } - enextself(newtet); - } - // Let the corners of newtet point to it for fast searching. - pa = org(newtet); - setpoint2tet(pa, encode(newtet)); - pa = dest(newtet); - setpoint2tet(pa, encode(newtet)); - pa = apex(newtet); - setpoint2tet(pa, encode(newtet)); - pa = oppo(newtet); - setpoint2tet(pa, encode(newtet)); - } - - if (flipque != (queue *) NULL) { - // Recover locally Delaunay faces. - flip(flipque, NULL); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// findcollapseedge() Find collapseable edge to suppress an endpoint. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::findcollapseedge(point suppt, point *conpt, list* oldtetlist, - list* ptlist) -{ - triface front; - point pt, pa, pb, pc; - REAL *lenarray, ltmp, ori; - bool visflag; - int *idxarray, itmp; - int n, i, j; - - if (b->verbose > 2) { - printf(" Search an edge (in %d edges) for collapse %d.\n", - ptlist->len(), pointmark(suppt)); - } - - // Candidate edges are p to the points of B(p) (in 'ptlist'). - n = ptlist->len(); - lenarray = new REAL[n]; - idxarray = new int[n]; - // Sort the points of B(p) by distance to p. - for (i = 0; i < n; i++) { - pt = * (point *)(* ptlist)[i]; - lenarray[i] = distance(suppt, pt); - idxarray[i] = i; - } - // Bubble sort. - for (i = 0; i < n - 1; i++) { - for (j = 0; j < n - 1 - i; j++) { - if (lenarray[j + 1] < lenarray[j]) { // compare the two neighbors - ltmp = lenarray[j]; // swap a[j] and a[j + 1] - lenarray[j] = lenarray[j + 1]; - lenarray[j + 1] = ltmp; - itmp = idxarray[j]; // swap a[j] and a[j + 1] - idxarray[j] = idxarray[j + 1]; - idxarray[j + 1] = itmp; - } - } - } - // For each point q of B(p), test if the edge (p, q) can be collapseed. - for (i = 0; i < n; i++) { - pt = * (point *)(* ptlist)[idxarray[i]]; - // Is q visible by faces of B(p) not with q as a vertex. - lenarray[i] = 0.0; // zero volume. - visflag = true; - for (j = 0; j < oldtetlist->len() && visflag; j++) { - front = * (triface *)(* oldtetlist)[j]; - // Let f face to inside of B(p). - adjustedgering(front, CCW); - pa = org(front); - pb = dest(front); - pc = apex(front); - // Is f contains q? - if ((pa != pt) && (pb != pt) && (pc != pt)) { - ori = orient3d(pa, pb, pc, pt); - if (ori != 0.0) { - if (iscoplanar(pa, pb, pc, pt, ori, b->epsilon * 1e+2)) ori = 0.0; - } - visflag = ori < 0.0; - if (visflag) { - // Visible, set the smallest volume. - if (j == 0) { - lenarray[i] = fabs(ori); - } else { - lenarray[i] = fabs(ori) < lenarray[i] ? fabs(ori) : lenarray[i]; - } - } else { - // Invisible. Do not collapse (p, q). - lenarray[i] = 0.0; - } - } - } - if ((b->verbose > 2) && visflag) { - printf(" Got candidate %d vol(%g).\n", pointmark(pt), lenarray[i]); - } - } - - // Select the largest non-zero volume (result in ltmp). - ltmp = lenarray[0]; - itmp = idxarray[0]; - for (i = 1; i < n; i++) { - if (lenarray[i] != 0.0) { - if (lenarray[i] > ltmp) { - ltmp = lenarray[i]; - itmp = idxarray[i]; // The index to find the point. - } - } - } - - delete [] lenarray; - delete [] idxarray; - - if (ltmp == 0.0) { - // No edge can be collapseed. - *conpt = (point) NULL; - return false; - } else { - pt = * (point *)(* ptlist)[itmp]; - *conpt = pt; - return true; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// collapseedge() Remove a point by edge collapse. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::collapseedge(point suppt, point conpt, list* oldtetlist, - list* deadtetlist) -{ - triface oldtet, deadtet; - triface adjtet1, adjtet2; - face adjsh; - point pa, pb, pc; - int i, j; - - if (b->verbose > 2) { - printf(" Collapse edge (%d,%d).\n", pointmark(suppt), pointmark(conpt)); - } - - // Loop in B(p), replace p with np, queue dead tets, uninfect old tets. - for (i = 0; i < oldtetlist->len(); i++) { - oldtet = * (triface *)(* oldtetlist)[i]; // assert(infected(oldtet)); - uninfect(oldtet); - pa = org(oldtet); - pb = dest(oldtet); - pc = apex(oldtet); - assert(oppo(oldtet) == suppt); - setoppo(oldtet, conpt); - if ((pa == conpt) || (pb == conpt) || (pc == conpt)) { - deadtetlist->append(&oldtet); // a collpased tet. - } - } - // Loop in deadtetlist, glue adjacent tets of dead tets. - for (i = 0; i < deadtetlist->len(); i++) { - deadtet = * (triface *)(* deadtetlist)[i]; - // Get the adjacent tet n1 (outside B(p)). - sym(deadtet, adjtet1); - tspivot(deadtet, adjsh); - // Find the edge in deadtet opposite to conpt. - adjustedgering(deadtet, CCW); - for (j = 0; j < 3; j++) { - if (apex(deadtet) == conpt) break; - enextself(deadtet); - } - assert(j < 3); - // Get another adjacent tet n2. - fnext(deadtet, adjtet2); - symself(adjtet2); - assert(adjtet2.tet != dummytet); // n2 is inside B(p). - if (adjtet1.tet != dummytet) { - bond(adjtet1, adjtet2); // Bond n1 <--> n2. - } else { - dissolve(adjtet2); // Dissolve at n2. - dummytet[0] = encode(adjtet2); // Let dummytet holds n2. - } - if (adjsh.sh != dummysh) { - tsbond(adjtet2, adjsh); // Bond s <--> n2. - } - // Collapse deadtet. - tetrahedrondealloc(deadtet.tet); - } - deadtetlist->clear(); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// deallocfaketets() Deleted fake tets at fronts. // -// // -// This routine is only called when the findrelocatepoint() routine fails. // -// In other cases, the fake tets are removed automatically in carvecavity() // -// or relocatepoint(). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::deallocfaketets(list* frontlist) -{ - triface front, neightet; - face checksh; - bool infectflag; - int i; - - for (i = 0; i < frontlist->len(); i++) { - // Get a front f. - front = * (triface *)(* frontlist)[i]; - // Let f face inside C. (f is a face of tet adjacent to C). - adjustedgering(front, CW); - sym(front, neightet); - tspivot(front, checksh); - if (oppo(front) == (point) NULL) { - if (b->verbose > 2) { - printf(" Get fake tet (%d, %d, %d).\n", pointmark(org(front)), - pointmark(dest(front)), pointmark(apex(front))); - } - if (neightet.tet != dummytet) { - // The neightet may be infected. After dissolve it, the infect flag - // will be lost. Save the flag and restore it later. - infectflag = infected(neightet); - dissolve(neightet); - if (infectflag) { - infect(neightet); - } - } - if (checksh.sh != dummysh) { - infectflag = sinfected(checksh); - stdissolve(checksh); - if (infectflag) { - sinfect(checksh); - } - } - // Dealloc the 'fake' tet. - tetrahedrondealloc(front.tet); - // If 'neightet' is a hull face, let 'dummytet' bond to it. It is - // a 'dummytet' when this front was created from a new subface. - // In such case, it should not be bounded. - if (neightet.tet != dummytet) { - dummytet[0] = encode(neightet); - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// restorepolyhedron() Restore the tetrahedralization in a polyhedron. // -// // -// This routine is only called when the operation of suppressing a point is // -// aborted (eg., findrelocatepoint() routine fails). The polyhedron has been // -// remeshed by new tets. This routine restore the old tets in it. // -// // -// 'oldtetlist' contains the list of old tets. Each old tet t_o assumes that // -// it still connects to a tet t_b of the mesh, however, t_b does not connect // -// to t_o, this routine resets the connection such that t_b <--> t_o. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::restorepolyhedron(list* oldtetlist) -{ - triface oldtet, neightet, neineitet; - face checksh; - int i; - - for (i = 0; i < oldtetlist->len(); i++) { - // Get an old tet t_o. - oldtet = * (triface *)(* oldtetlist)[i]; - // Check the four sides of t_o. - for (oldtet.loc = 0; oldtet.loc < 4; oldtet.loc++) { - sym(oldtet, neightet); - tspivot(oldtet, checksh); - if (neightet.tet != dummytet) { - sym(neightet, neineitet); - if (neineitet.tet != oldtet.tet) { - // This face of t_o is a boundary of P. - bond(neightet, oldtet); - if (checksh.sh != dummysh) { - tsbond(oldtet, checksh); - } - } - } else { - // t_o has a hull face. It should be the boundary of P. -#ifdef SELF_CHECK - assert(checksh.sh != dummysh); - stpivot(checksh, neineitet); - assert(neineitet.tet != oldtet.tet); -#endif - tsbond(oldtet, checksh); - // Let dummytet[0] points to it. - dummytet[0] = encode(oldtet); - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// suppressfacetpoint() Suppress a point inside a facet. // -// // -// The point p inside a facet F will be suppressed from F by either being // -// deleted from the mesh or being relocated into the volume. // -// // -// 'supsh' is a subface f of F, and p = sapex(f); the other parameters are // -// working lists which are empty at the beginning and the end. // -// // -// 'optflag' is used for mesh optimization. If it is set, after removing p, // -// test the object function on each new tet, queue bad tets. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist, - list* misfrontlist, list* ptlist, list* conlist, memorypool* viri, - queue* flipque, bool noreloc, bool optflag) -{ - list *oldtetlist[2], *newtetlist[2]; - list *oldshlist, *newshlist; - triface oldtet, newtet; - face oldsh, newsh; - point suppt, newpt[2]; - point *cons; - REAL norm[3]; - bool success; - int shmark; - int i, j; - - suppt = sapex(*supsh); - if (b->verbose > 1) { - printf(" Suppress point %d in facet.\n", pointmark(suppt)); - } - - // Initialize working lists, variables. - for (i = 0; i < 2; i++) { - oldtetlist[i] = (list *) NULL; - newtetlist[i] = (list *) NULL; - newpt[i] = (point) NULL; - } - oldshlist = new list(sizeof(face), NULL, 256); - newshlist = new list(sizeof(face), NULL, 256); - success = true; // Assume p can be suppressed. - - // Find subs of C(p). - oldshlist->append(supsh); - formstarpolygon(suppt, oldshlist, ptlist); - // Get the edges of C(p). They form a closed polygon. - for (i = 0; i < oldshlist->len(); i++) { - oldsh = * (face *)(* oldshlist)[i]; - cons = (point *) conlist->append(NULL); - cons[0] = sorg(oldsh); - cons[1] = sdest(oldsh); - } - // Re-triangulate the old C(p). - shmark = shellmark(*supsh); - triangulate(shmark, b->epsilon, ptlist, conlist, 0, NULL, viri, flipque); - // Get new subs of C(p), remove protected segments. - retrievenewsubs(newshlist, true); - // Substitute the old C(p) with the new C(p) - replacepolygonsubs(oldshlist, newshlist); - // Clear work lists. - ptlist->clear(); - conlist->clear(); - flipque->clear(); - viri->restart(); - - // B(p) (tets with p as a vertex) has been separated into two parts - // (B_0(p) and B_1(p)) by F. Process them individually. - for (i = 0; i < 2 && success; i++) { - if (i == 1) sesymself(*supsh); - // Get a tet containing p. - stpivot(*supsh, oldtet); - // Is this part empty? - if (oldtet.tet == dummytet) continue; - // Allocate spaces for storing (old and new) B_i(p). - oldtetlist[i] = new list(sizeof(triface), NULL, 256); - newtetlist[i] = new list(sizeof(triface), NULL, 256); - // Form old B_i(p) in oldtetlist[i]. - assert(!isdead(&oldtet)); - oldtetlist[i]->append(&oldtet); - formstarpolyhedron(suppt, oldtetlist[i], ptlist, false); - // Infect the tets in old B_i(p) (they're going to be delete). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - infect(oldtet); - } - // Preparation for re-tetrahedralzing old B_i(p). - orientnewsubs(newshlist, supsh, norm); - // Tetrahedralize old B_i(p). - success = constrainedcavity(&oldtet, newshlist, oldtetlist[i], ptlist, - frontlist, misfrontlist, newtetlist[i], flipque); - // If p is not suppressed, do relocation if 'noreloc' is not set. - if (!success && !noreloc) { - // Try to relocate p into the old B_i(p). - makepoint(&(newpt[i])); - success = findrelocatepoint(suppt, newpt[i], norm, frontlist, - oldtetlist[i]); - // Initialize newpt = suppt. - // for (j = 0; j < 3; j++) newpt[i][j] = suppt[j]; - // success = smoothvolpoint(newpt[i], frontlist, true); - if (success) { - // p is relocated by newpt[i]. Now insert it. Don't do flip since - // the new tets may get deleted again. - relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL); - setpointtype(newpt[i], FREEVOLVERTEX); - relverts++; - } else { - // Fail to relocate p. Clean fake tets and quit this option. - deallocfaketets(frontlist); - pointdealloc(newpt[i]); - newpt[i] = (point) NULL; - assert(newtetlist[i]->len() == 0); - } - } - if (!success && noreloc) { - // Failed and no point relocation. Clean fake tets. - deallocfaketets(frontlist); - } - // Clear work lists. - ptlist->clear(); - frontlist->clear(); - misfrontlist->clear(); - flipque->clear(); - } - - if (success) { - // p has been removed! (Still in the pool). - setpointtype(suppt, UNUSEDVERTEX); - unuverts++; - // Delete old C(p). - for (i = 0; i < oldshlist->len(); i++) { - oldsh = * (face *)(* oldshlist)[i]; - if (i == 0) { - // Update the 'hullsize' if C(p) is on the hull. - stpivot(oldsh, oldtet); - if (oldtet.tet != dummytet) { - sesymself(oldsh); - stpivot(oldsh, oldtet); - } - if (oldtet.tet == dummytet) { - // A boundary face. Update the 'hullsize'. - j = oldshlist->len() - newshlist->len(); - assert(j > 0); - hullsize -= j; - } - } - shellfacedealloc(subfaces, oldsh.sh); - } - // Delete old B_i(p). - for (i = 0; i < 2; i++) { - if (oldtetlist[i] != (list *) NULL) { - // Delete tets of the old B_i(p). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - assert(!isdead(&oldtet)); - tetrahedrondealloc(oldtet.tet); - } - } - } - if (optflag) { - // Check for new bad-quality tets. - for (i = 0; i < 2; i++) { - if (newtetlist[i] != (list *) NULL) { - for (j = 0; j < newtetlist[i]->len(); j++) { - newtet = * (triface *)(* (newtetlist[i]))[j]; - if (!isdead(&newtet)) checktet4opt(&newtet, true); - } - } - } - } - } else { - // p is not suppressed. Recover the original state. - unsupverts++; - // Restore the old C(p). - replacepolygonsubs(newshlist, oldshlist); - // Delete subs of the new C(p) - for (i = 0; i < newshlist->len(); i++) { - newsh = * (face *)(* newshlist)[i]; - shellfacedealloc(subfaces, newsh.sh); - } - // Restore old B_i(p). - for (i = 0; i < 2; i++) { - if (oldtetlist[i] != (list *) NULL) { - // Uninfect tets of old B_i(p). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - assert(infected(oldtet)); - uninfect(oldtet); - } - // Has it been re-meshed? - if (newtetlist[i]->len() > 0) { - // Restore the old B_i(p). - restorepolyhedron(oldtetlist[i]); - // Delete tets of the new B_i(p); - for (j = 0; j < newtetlist[i]->len(); j++) { - newtet = * (triface *)(* (newtetlist[i]))[j]; - // Some new tets may already be deleted (by carvecavity()). - if (!isdead(&newtet)) { - tetrahedrondealloc(newtet.tet); - } - } - } - // Dealloc newpt[i] if it exists. - if (newpt[i] != (point) NULL) { - pointdealloc(newpt[i]); - relverts--; - } - } - } - } - - // Delete work lists. - delete oldshlist; - delete newshlist; - for (i = 0; i < 2; i++) { - if (oldtetlist[i] != (list *) NULL) { - delete oldtetlist[i]; - delete newtetlist[i]; - } - } - - return success; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// suppresssegpoint() Suppress a point on a segment. // -// // -// The point p on a segment S will be suppressed from S by either being // -// deleted from the mesh or being relocated into the volume. // -// // -// 'supseg' is the segment S, and p = sdest(S); the other parameters are // -// working lists which are empty at the beginning and the end. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist, - list* newsegshlist, list* frontlist, list* misfrontlist, list* ptlist, - list* conlist, memorypool* viri, queue* flipque, bool noreloc, bool optflag) -{ - list **oldtetlist, **newtetlist; - list **oldshlist, **newshlist; - list *pnewshlist, *dnewshlist; - triface oldtet, newtet; - face oldsh, newsh; - face startsh, spinsh, segsh1, segsh2; - face nsupseg, newseg, prevseg, nextseg; - point suppt, *newpt; - point pa, pb, *cons; - REAL pnorm[2][3], norm[3]; - bool success; - int shmark; - int n, i, j, k; - - // Get the Steiner point p. - assert(supseg->shver < 2); - suppt = sdest(*supseg); - // Find the segment ab split by p. - senext(*supseg, nsupseg); - spivotself(nsupseg); - assert(nsupseg.sh != dummysh); - nsupseg.shver = 0; - if (sorg(nsupseg) != suppt) sesymself(nsupseg); - assert(sorg(nsupseg) == suppt); - pa = sorg(*supseg); - pb = sdest(nsupseg); - if (b->verbose > 1) { - printf(" Remove point %d on segment (%d, %d).\n", - pointmark(suppt), pointmark(pa), pointmark(pb)); - } - - // Let startsh s containing p. - spivot(*supseg, startsh); - spinsh = startsh; - do { - // Save it in list. - spinshlist->append(&spinsh); - // Go to the next facet. - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - if (spinshlist->len() == 1) { - // This case has not handled yet. - // printf("Unhandled case: segment only belongs to one facet.\n"); - spinshlist->clear(); - unsupverts++; - return false; - } - - // Suppose ab is shared by n facets (n > 1), then there are n B(p) (tets - // with p as a vertex). Some B(p) may be empty, eg, outside. - n = spinshlist->len(); - oldtetlist = new list*[n]; - newtetlist = new list*[n]; - oldshlist = new list*[n]; - newshlist = new list*[n]; - newpt = new point[n]; - for (i = 0; i < n; i++) { - oldtetlist[i] = (list *) NULL; - newtetlist[i] = (list *) NULL; - oldshlist[i] = (list *) NULL; - newshlist[i] = (list *) NULL; - newpt[i] = (point) NULL; - } - - // Create a new segment ab (result in newseg). - makeshellface(subsegs, &newseg); - setsorg(newseg, pa); - setsdest(newseg, pb); - // ab gets the same mark and segment type as ap. - setshellmark(newseg, shellmark(*supseg)); - setshelltype(newseg, shelltype(*supseg)); - if (b->quality && varconstraint) { - // Copy the areabound into the new subsegment. - setareabound(newseg, areabound(*supseg)); - } - // Save the old connection at a. - senext2(*supseg, prevseg); - spivotself(prevseg); - if (prevseg.sh != dummysh) { - prevseg.shver = 0; - if (sdest(prevseg) != pa) sesymself(prevseg); - assert(sdest(prevseg) == pa); - senextself(prevseg); - senext2self(newseg); - sbond(newseg, prevseg); - newseg.shver = 0; - } - // Save the old connection at b. - senext(nsupseg, nextseg); - spivotself(nextseg); - if (nextseg.sh != dummysh) { - nextseg.shver = 0; - if (sorg(nextseg) != pb) sesymself(nextseg); - assert(sorg(nextseg) == pb); - senext2self(nextseg); - senextself(newseg); - sbond(newseg, nextseg); - newseg.shver = 0; - } - - // Re-triangulate C(p) (subs with p as a vertex) to remove p. - for (i = 0; i < spinshlist->len(); i++) { - spinsh = * (face *)(* spinshlist)[i]; - // Allocate spaces for C_i(p). - oldshlist[i] = new list(sizeof(face), NULL, 256); - newshlist[i] = new list(sizeof(face), NULL, 256); - // Get the subs of C_i(p). - oldshlist[i]->append(&spinsh); - formstarpolygon(suppt, oldshlist[i], ptlist); - // Find the edges of C_i(p). It DOES NOT form a closed polygon. - for (j = 0; j < oldshlist[i]->len(); j++) { - oldsh = * (face *)(* (oldshlist[i]))[j]; - cons = (point *) conlist->append(NULL); - cons[0] = sorg(oldsh); - cons[1] = sdest(oldsh); - } - // The C_i(p) isn't closed without ab. Add it to it. - cons = (point *) conlist->append(NULL); - cons[0] = pa; - cons[1] = pb; - // Re-triangulate C_i(p). - shmark = shellmark(spinsh); - triangulate(shmark, b->epsilon, ptlist, conlist, 0, NULL, viri, flipque); - // Get new subs of C_i(p), remove protected segments. - retrievenewsubs(newshlist[i], true); - // Substitute old C_i(p) with the new C_i(p). !IT IS NOT COMPLETE! - replacepolygonsubs(oldshlist[i], newshlist[i]); - // Find the new subface s having edge ab. - for (j = 0; j < newshlist[i]->len(); j++) { - segsh1 = * (face *)(* (newshlist[i]))[j]; - for (k = 0; k < 3; k++) { - if (((sorg(segsh1) == pa) && (sdest(segsh1) == pb)) || - ((sorg(segsh1) == pb) && (sdest(segsh1) == pa))) break; - senextself(segsh1); - } - if (k < 3) break; // Found. - } - assert(j < newshlist[i]->len()); // ab must exist. - // Bond s and ab together. The C_i(p) is completedly substituted. - ssbond(segsh1, newseg); - // Save s for forming the face ring of ab. - newsegshlist->append(&segsh1); - // Clear work lists. - ptlist->clear(); - conlist->clear(); - flipque->clear(); - viri->restart(); - } - // Form the face ring of ab. - for (i = 0; i < newsegshlist->len(); i++) { - segsh1 = * (face *)(* newsegshlist)[i]; - if ((i + 1) == newsegshlist->len()) { - segsh2 = * (face *)(* newsegshlist)[0]; - } else { - segsh2 = * (face *)(* newsegshlist)[i + 1]; - } - sbond1(segsh1, segsh2); - } - - // A work list for keeping subfaces from two facets. - dnewshlist = new list(sizeof(face), NULL, 256); - success = true; // Assume p is suppressable. - - // Suppress p in all B(p). B_i(p) is looped wrt the right-hand rule of ab. - for (i = 0; i < spinshlist->len() && success; i++) { - // Get an old subface s (ap) of a facet. - spinsh = * (face *)(* spinshlist)[i]; - // Let the edge direction of s be a->b. Hence all subfaces follow - // the right-hand rule of ab. - if (sorg(spinsh) != pa) sesymself(spinsh); - // Get a tet t of B_i(p). - stpivot(spinsh, oldtet); - // Is B_i(p) empty? - if (oldtet.tet == dummytet) continue; - // Allocate spaces for B_i(p). - oldtetlist[i] = new list(sizeof(triface), NULL, 256); - newtetlist[i] = new list(sizeof(triface), NULL, 256); - // Find all tets of old B_i(p). - oldtetlist[i]->append(&oldtet); - formstarpolyhedron(suppt, oldtetlist[i], ptlist, false); - // Infect tets of old B_i(p) (they're going to be deleted). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - infect(oldtet); - } - // Collect new subfaces (of two facets) bounded B_i(p). - for (k = 0; k < 2; k++) { - if ((i + k) < spinshlist->len()) { - pnewshlist = newshlist[i + k]; - segsh1 = * (face *)(* spinshlist)[i + k]; - } else { - pnewshlist = newshlist[0]; - segsh1 = * (face *)(* spinshlist)[0]; - } - // Adjust the orientation of segsh1 to face to the inside of C. - if (k == 0) { - if (sorg(segsh1) != pa) sesymself(segsh1); - assert(sorg(segsh1) == pa); - } else { - if (sdest(segsh1) != pa) sesymself(segsh1); - assert(sdest(segsh1) == pa); - } - // Preparation for re-tetrahedralzing old B_i(p). - orientnewsubs(pnewshlist, &segsh1, pnorm[k]); - for (j = 0; j < pnewshlist->len(); j++) { - dnewshlist->append((face *)(* pnewshlist)[j]); - } - } - // Tetrahedralize B_i(p). - success = constrainedcavity(&oldtet, dnewshlist, oldtetlist[i], ptlist, - frontlist, misfrontlist, newtetlist[i], flipque); - if (!success && !noreloc) { - // C must be finished by re-locating the steiner point. - makepoint(&(newpt[i])); - for (j = 0; j < 3; j++) norm[j] = 0.5 * (pnorm[0][j] + pnorm[1][j]); - success = findrelocatepoint(suppt, newpt[i], norm, frontlist, - oldtetlist[i]); - // for (j = 0; j < 3; j++) newpt[i][j] = suppt[j]; - // success = smoothvolpoint(newpt[i], frontlist, true); - if (success) { - // p is relocated by newpt[i]. Now insert it. Don't do flip since - // the new tets may get deleted again. - relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL); - setpointtype(newpt[i], FREEVOLVERTEX); - relverts++; - } else { - // Fail to relocate p. Clean fake tets and quit this option. - deallocfaketets(frontlist); - pointdealloc(newpt[i]); - newpt[i] = (point) NULL; - assert(newtetlist[i]->len() == 0); - } - } - if (!success && noreloc) { - // Failed and no point relocation. Clean fake tets. - deallocfaketets(frontlist); - } - // Clear work lists. - dnewshlist->clear(); - ptlist->clear(); - frontlist->clear(); - misfrontlist->clear(); - flipque->clear(); - } - - if (success) { - // p has been suppressed. (Still in the pool). - setpointtype(suppt, UNUSEDVERTEX); - unuverts++; - // Update the segmnet pointers saved in a and b. - setpoint2sh(pa, sencode(newseg)); - setpoint2sh(pb, sencode(newseg)); - // Delete old segments ap, pb. - shellfacedealloc(subsegs, supseg->sh); - shellfacedealloc(subsegs, nsupseg.sh); - // Delete subs of old C_i(p). - for (i = 0; i < spinshlist->len(); i++) { - for (j = 0; j < oldshlist[i]->len(); j++) { - oldsh = * (face *)(* (oldshlist[i]))[j]; - if (j == 0) { - // Update 'hullsize' if C_i(p) is on the hull. - stpivot(oldsh, oldtet); - if (oldtet.tet != dummytet) { - sesymself(oldsh); - stpivot(oldsh, oldtet); - } - if (oldtet.tet == dummytet) { - // Update 'hullsize'. - k = oldshlist[i]->len() - newshlist[i]->len(); - assert(k > 0); - hullsize -= k; - } - } - shellfacedealloc(subfaces, oldsh.sh); - } - } - // Delete tets old B_i(p). - for (i = 0; i < spinshlist->len(); i++) { - // Delete them if it is not empty. - if (oldtetlist[i] != (list *) NULL) { - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - assert(!isdead(&oldtet)); - tetrahedrondealloc(oldtet.tet); - } - } - } - if (optflag) { - for (i = 0; i < spinshlist->len(); i++) { - // Check for new bad-quality tets. - if (newtetlist[i] != (list *) NULL) { - for (j = 0; j < newtetlist[i]->len(); j++) { - newtet = * (triface *)(* (newtetlist[i]))[j]; - if (!isdead(&newtet)) checktet4opt(&newtet, true); - } - } - } - } - } else { - // p is not suppressed. Recover the original state. - unsupverts++; - // Restore old connection at a. - senext2(*supseg, prevseg); - spivotself(prevseg); - if (prevseg.sh != dummysh) { - prevseg.shver = 0; - if (sdest(prevseg) != pa) sesymself(prevseg); - assert(sdest(prevseg) == pa); - senextself(prevseg); - senext2self(*supseg); - sbond(*supseg, prevseg); - senextself(*supseg); // Restore original state. - assert(supseg->shver < 2); - } - // Restore old connection at b. - senext(nsupseg, nextseg); - spivotself(nextseg); - if (nextseg.sh != dummysh) { - nextseg.shver = 0; - if (sorg(nextseg) != pb) sesymself(nextseg); - assert(sorg(nextseg) == pb); - senext2self(nextseg); - senextself(nsupseg); - sbond(nsupseg, nextseg); - // nsupseg.shver = 0; - senext2self(nsupseg); // Restore original state - assert(nsupseg.shver < 2); - } - // Delete the new segment ab. - shellfacedealloc(subsegs, newseg.sh); - // Restore old C_i(p). - for (i = 0; i < spinshlist->len(); i++) { - replacepolygonsubs(newshlist[i], oldshlist[i]); - // Delete subs of the new C_i(p) - for (j = 0; j < newshlist[i]->len(); j++) { - newsh = * (face *)(* (newshlist[i]))[j]; - shellfacedealloc(subfaces, newsh.sh); - } - } - // Restore old B_i(p). - for (i = 0; i < spinshlist->len(); i++) { - if (oldtetlist[i] != (list *) NULL) { - // Uninfect tets of old B_i(p). - for (j = 0; j < oldtetlist[i]->len(); j++) { - oldtet = * (triface *)(* (oldtetlist[i]))[j]; - assert(infected(oldtet)); - uninfect(oldtet); - } - // Has it been re-meshed? - if (newtetlist[i]->len() > 0) { - // Restore the old B_i(p). - restorepolyhedron(oldtetlist[i]); - // Delete tets of the new B_i(p); - for (j = 0; j < newtetlist[i]->len(); j++) { - newtet = * (triface *)(* (newtetlist[i]))[j]; - // Some new tets may already be deleted (by carvecavity()). - if (!isdead(&newtet)) { - tetrahedrondealloc(newtet.tet); - } - } - } - // Dealloc newpt[i] if it exists. - if (newpt[i] != (point) NULL) { - pointdealloc(newpt[i]); - relverts--; - } - } - } - } - - // Delete work lists. - delete dnewshlist; - for (i = 0; i < spinshlist->len(); i++) { - delete oldshlist[i]; - delete newshlist[i]; - } - delete [] oldshlist; - delete [] newshlist; - for (i = 0; i < spinshlist->len(); i++) { - if (oldtetlist[i] != (list *) NULL) { - delete oldtetlist[i]; - delete newtetlist[i]; - } - } - delete [] oldtetlist; - delete [] newtetlist; - // Clear work lists. - newsegshlist->clear(); - spinshlist->clear(); - - return success; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// suppressvolpoint() Suppress a point inside mesh. // -// // -// The point p = org(suptet) is inside the mesh and will be suppressed from // -// the mesh. Note that p may not be suppressed. // -// // -// 'optflag' is used for mesh optimization. If it is set, after removing p, // -// test the object function on each new tet, queue bad tets. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::suppressvolpoint(triface* suptet, list* frontlist, - list* misfrontlist, list* ptlist, queue* flipque, bool optflag) -{ - list *myfrontlist, *mymisfrontlist, *myptlist; - list *oldtetlist, *newtetlist; - list *newshlist; // a dummy list. - queue *myflipque; - triface oldtet, newtet; - point suppt, conpt; - bool success; - int j; - - // Allocate spaces for storing (old and new) B(p). - oldtetlist = new list(sizeof(triface), NULL, 256); - newtetlist = new list(sizeof(triface), NULL, 256); - newshlist = new list(sizeof(face), NULL, 256); - // Allocate work lists if user doesn't supply them. - myfrontlist = mymisfrontlist = myptlist = (list *) NULL; - myflipque = (queue *) NULL; - if (frontlist == (list *) NULL) { - myfrontlist = new list(sizeof(triface), NULL, 256); - frontlist = myfrontlist; - mymisfrontlist = new list(sizeof(triface), NULL, 256); - misfrontlist = mymisfrontlist; - myptlist = new list(sizeof(point *), NULL, 256); - ptlist = myptlist; - myflipque = new queue(sizeof(badface)); - flipque = myflipque; - } - - suppt = org(*suptet); - oldtet = *suptet; - success = true; // Assume p can be suppressed. - - if (b->verbose > 1) { - printf(" Remove point %d in mesh.\n", pointmark(suppt)); - } - - // Form old B(p) in oldtetlist. - oldtetlist->append(&oldtet); - formstarpolyhedron(suppt, oldtetlist, ptlist, false); - // Infect the tets in old B(p) (they're going to be delete). - for (j = 0; j < oldtetlist->len(); j++) { - oldtet = * (triface *)(* oldtetlist)[j]; - infect(oldtet); - } - // Tetrahedralize old B(p). - success = constrainedcavity(&oldtet, newshlist, oldtetlist, ptlist, - frontlist, misfrontlist, newtetlist, flipque); - if (!success) { - // Unable to suppress p. - deallocfaketets(frontlist); - // Try to collapse an edge at p. - conpt = (point) NULL; - assert(newtetlist->len() == 0); - if (findcollapseedge(suppt, &conpt, oldtetlist, ptlist)) { - // Collapse the edge suppt->conpt. Re-use newtetlist. - collapseedge(suppt, conpt, oldtetlist, newtetlist); - // The oldtetlist contains newtetlist. - if (optflag) { - assert(newtetlist->len() == 0); - for (j = 0; j < oldtetlist->len(); j++) { - newtet = * (triface *)(* oldtetlist)[j]; - newtetlist->append(&newtet); - } - } - oldtetlist->clear(); // Do not delete them. - collapverts++; - success = true; - } - } - if (success) { - // p has been removed! (Still in the pool). - setpointtype(suppt, UNUSEDVERTEX); - unuverts++; - suprelverts++; - // Delete old B(p). - for (j = 0; j < oldtetlist->len(); j++) { - oldtet = * (triface *)(* oldtetlist)[j]; - assert(!isdead(&oldtet)); - tetrahedrondealloc(oldtet.tet); - } - if (optflag) { - // Check for new bad tets. - for (j = 0; j < newtetlist->len(); j++) { - newtet = * (triface *)(* newtetlist)[j]; - if (!isdead(&newtet)) checktet4opt(&newtet, true); - } - } - } else { - // p is not suppressed. Recover the original state. - // Uninfect tets of old B(p). - for (j = 0; j < oldtetlist->len(); j++) { - oldtet = * (triface *)(* oldtetlist)[j]; - assert(infected(oldtet)); - uninfect(oldtet); - } - } - - // Clear work lists. - ptlist->clear(); - frontlist->clear(); - misfrontlist->clear(); - flipque->clear(); - // Deallocate work lists. - if (myfrontlist != (list *) NULL) { - delete myfrontlist; - delete mymisfrontlist; - delete myptlist; - delete myflipque; - } - delete oldtetlist; - delete newtetlist; - delete newshlist; - - return success; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// smoothpoint() Smooth a volume/segment point. // -// // -// 'smthpt' (p) is inside the polyhedron (C) bounded by faces in 'starlist'. // -// This routine moves p inside C until an object function is maximized. // -// // -// Default, the CCW edge ring of the faces on C points to p. If 'invtori' is // -// TRUE, the orientation is inversed. // -// // -// If 'key' != NULL, it contains an object value to be improved. Current it // -// means the cosine of the largest dihedral angle. In such case, the point // -// is smoothed only if the final configuration improves the object value, it // -// is returned by the 'key'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::smoothpoint(point smthpt, point e1, point e2, list *starlist, - bool invtori, REAL *key) -{ - triface starttet; - point pa, pb, pc; - REAL fcent[3], startpt[3], nextpt[3], bestpt[3]; - REAL iniTmax, oldTmax, newTmax; - REAL ori, aspT, aspTmax, imprate; - REAL cosd, maxcosd; - bool segflag, randflag; //, subflag; - int numdirs; - int iter, i, j; - - // Is p a segment vertex? - segflag = (e1 != (point) NULL); - // Decide the number of moving directions. - numdirs = segflag ? 2 : starlist->len(); - randflag = numdirs > 10; - if (randflag) { - numdirs = 10; // Maximum 10 directions. - } - - // Calculate the initial object value (the largest aspect ratio). - for (i = 0; i < starlist->len(); i++) { - starttet = * (triface *)(* starlist)[i]; - adjustedgering(starttet, !invtori ? CCW : CW); - pa = org(starttet); - pb = dest(starttet); - pc = apex(starttet); - aspT = tetaspectratio(pa, pb, pc, smthpt); - if (i == 0) { - aspTmax = aspT; - } else { - aspTmax = aspT > aspTmax ? aspT : aspTmax; - } - } - iniTmax = aspTmax; - - if (b->verbose > 1) { - printf(" Smooth %s point %d (%g, %g, %g).\n", segflag ? "seg" : "vol", - pointmark(smthpt), smthpt[0], smthpt[1], smthpt[2]); - printf(" Initial max L/h = %g.\n", iniTmax); - } - for (i = 0; i < 3; i++) { - bestpt[i] = startpt[i] = smthpt[i]; - } - - // Do iteration until the new aspTmax does not decrease. - newTmax = iniTmax; - iter = 0; - while (true) { - // Find the best next location. - oldTmax = newTmax; - for (i = 0; i < numdirs; i++) { - // Calculate the moved point (saved in 'nextpt'). - if (!segflag) { - if (randflag) { - // Randomly pick a direction. - j = (int) randomnation(starlist->len()); - } else { - j = i; - } - starttet = * (triface *)(* starlist)[j]; - adjustedgering(starttet, !invtori ? CCW : CW); - pa = org(starttet); - pb = dest(starttet); - pc = apex(starttet); - for (j = 0; j < 3; j++) { - fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0; - } - } else { - for (j = 0; j < 3; j++) { - fcent[j] = (i == 0 ? e1[j] : e2[j]); - } - } - for (j = 0; j < 3; j++) { - nextpt[j] = startpt[j] + 0.01 * (fcent[j] - startpt[j]); - } - // Get the largest object value for the new location. - for (j = 0; j < starlist->len(); j++) { - starttet = * (triface *)(* starlist)[j]; - adjustedgering(starttet, !invtori ? CCW : CW); - pa = org(starttet); - pb = dest(starttet); - pc = apex(starttet); - ori = orient3d(pa, pb, pc, nextpt); - if (ori < 0.0) { - aspT = tetaspectratio(pa, pb, pc, nextpt); - if (j == 0) { - aspTmax = aspT; - } else { - aspTmax = aspT > aspTmax ? aspT : aspTmax; - } - } else { - // An invalid new tet. Discard this point. - aspTmax = newTmax; - } // if (ori < 0.0) - // Stop looping when the object value is bigger than before. - if (aspTmax >= newTmax) break; - } // for (j = 0; j < starlist->len(); j++) - if (aspTmax < newTmax) { - // Save the improved object value and the location. - newTmax = aspTmax; - for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; - } - } // for (i = 0; i < starlist->len(); i++) - // Does the object value improved much? - imprate = fabs(oldTmax - newTmax) / oldTmax; - if (imprate < 1e-3) break; - // Yes, move p to the new location and continue. - for (j = 0; j < 3; j++) startpt[j] = bestpt[j]; - iter++; - } // while (true) - - if (iter > 0) { - // The point is moved. - if (key) { - // Check if the quality is improved by the smoothed point. - maxcosd = 0.0; // = cos(90). - for (j = 0; j < starlist->len(); j++) { - starttet = * (triface *)(* starlist)[j]; - adjustedgering(starttet, !invtori ? CCW : CW); - pa = org(starttet); - pb = dest(starttet); - pc = apex(starttet); - tetalldihedral(pa, pb, pc, startpt, NULL, &cosd, NULL); - if (cosd < *key) { - // This quality will not be improved. Stop. - iter = 0; break; - } else { - // Remeber the worst quality value (of the new configuration). - maxcosd = maxcosd < cosd ? maxcosd : cosd; - } - } - if (iter > 0) *key = maxcosd; - } - } - - if (iter > 0) { - segflag ? smoothsegverts++ : smoothvolverts++; - for (i = 0; i < 3; i++) smthpt[i] = startpt[i]; - if (b->verbose > 1) { - printf(" Move to new location (%g, %g, %g).\n", smthpt[0], smthpt[1], - smthpt[2]); - printf(" Final max L/h = %g. (%d iterations)\n", newTmax, iter); - if (key) { - printf(" Max. dihed = %g (degree).\n", acos(*key) / PI * 180.0); - } - } - return true; - } else { - if (b->verbose > 1) { - printf(" Not smoothed.\n"); - } - return false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removesteiners() Delete or relocate Steiner points on facets. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::removesteiners(bool coarseflag) -{ - list *frontlist, *misfrontlist; - list *spinshlist, *newsegshlist; - list *ptlist, *conlist; - memorypool *viri; - queue *flipque; - triface checktet; - face shloop; - face segloop, nextseg; - point pa, neipt; - REAL len; - bool remflag; - int *worklist; - int oldnum, rmstein; - int i, j; - - if (!b->quiet) { - if (!coarseflag) { - printf("Removing Steiner points.\n"); - } else { - printf("Coarsening mesh.\n"); - } - } - - // Initialize work lists. - frontlist = new list(sizeof(triface), NULL); - misfrontlist = new list(sizeof(triface), NULL); - spinshlist = new list(sizeof(face), NULL); - newsegshlist = new list(sizeof(face), NULL); - ptlist = new list(sizeof(point *), NULL); - conlist = new list(sizeof(point *) * 2, NULL); - flipque = new queue(sizeof(badface)); - viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0); - oldnum = unuverts; - relverts = suprelverts = collapverts = unsupverts; - smoothvolverts = 0; - expcavcount = 0; - - // Suppress Steiner points inside facets. - do { - rmstein = unuverts; - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - remflag = false; - // Is s contains a Steiner point? - shloop.shver = 0; - for (i = 0; i < 3; i++) { - pa = sapex(shloop); - if (pointtype(pa) == FREESUBVERTEX) { - if (!coarseflag) { - // Remove it if it is not an input point. - j = pointmark(pa) - in->firstnumber; - if (j >= in->numberofpoints) { - if (b->nobisect == 1) { - // '-Y'. Remove p if s is a hull face. - stpivot(shloop, checktet); - if (checktet.tet != dummytet) { - sesymself(shloop); - stpivot(shloop, checktet); - } - remflag = (checktet.tet == dummytet); - } else { - // '-YY'. Remove p whatever s is a hull face or not. - remflag = true; - } - } - } else { - // Check if this vertex can be coarsed. - if (b->nobisect == 0) { - // Is a background mesh available? - if (b->metric) { - // assert(pa[pointmtrindex] > 0.0); - // Form the star of pa. - spinshlist->append(&shloop); - formstarpolygon(pa, spinshlist, ptlist); - len = 0.0; - for (j = 0; j < ptlist->len(); j++) { - neipt = * (point *)(* ptlist)[j]; - len += distance(pa, neipt); - } - len /= ptlist->len(); - // Carse it if the average edge length is small. - remflag = len < pa[pointmtrindex]; - spinshlist->clear(); - ptlist->clear(); - } else { - // Coarse it if (1) it is an input point and its pointmarker - // is zero, or (2) it is a Steiner point. - remflag = true; - j = pointmark(pa) - in->firstnumber; - if (j < in->numberofpoints) { - remflag = (in->pointmarkerlist[j] == 0); - } - } // if (b->metric) - } // if (b->nobisect == 0) - } // if (!coarseflag) - if (remflag) break; - } // if (pointtype(pa) == FREESUBVERTEX) - senextself(shloop); - } // for (i = 0; i < 3; i++) - if (remflag) { - suppressfacetpoint(&shloop, frontlist, misfrontlist, ptlist, conlist, - viri, flipque, coarseflag, false); - } - shloop.sh = shellfacetraverse(subfaces); - } - // Continue if any Steiner point has been removed. - } while (unuverts > rmstein); - - if (coarseflag) { - shellface **segsperverlist; - int *idx2seglist; - face seg1, seg2; - point e1, e2; - // Connecting collinear segments. Hence the segment vertices may be - // removed. In fact, this should be done by reconstructmesh(). - makesegmentmap(idx2seglist, segsperverlist); - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - for (i = 0; i < 2; i++) { - segloop.shver = i; - senext(segloop, nextseg); - spivotself(nextseg); - if ((nextseg.sh == dummysh) || (nextseg.sh > segloop.sh)) { - // No neighbor segment connection or haven't been processed yet. - pa = sdest(segloop); - j = pointmark(pa) - in->firstnumber; - if (idx2seglist[j + 1] - idx2seglist[j] == 2) { - // pa is shared by only two segments. Get the other one. - nextseg.sh = segsperverlist[idx2seglist[j]]; - if (nextseg.sh == segloop.sh) { - nextseg.sh = segsperverlist[idx2seglist[j] + 1]; - } - nextseg.shver = 0; - if (sorg(nextseg) != pa) sesymself(nextseg); - // Check if the two segments are collinear. - e1 = sorg(segloop); - e2 = sdest(nextseg); - if (iscollinear(e1, pa, e2, b->epsilon)) { - // Connect the two segments together. - if (b->verbose > 1) { - printf(" Glue two insegs (%d, %d) at %d.\n", pointmark(e1), - pointmark(e2), pointmark(pa)); - } - senext(segloop, seg1); - senext2(nextseg, seg2); - sbond(seg1, seg2); - } - } - } // if (nextseg.sh == dummysh) - } // for (i = 0; - segloop.sh = shellfacetraverse(subsegs); - } - delete [] segsperverlist; - delete [] idx2seglist; - } - - // Suppress Steiner points on segments. - do { - rmstein = unuverts; - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - remflag = false; - // for (i = 0; i < 2; i++) { - // Don't check the poinytype of pa, it may be a Steiner point but - // has type NACUTEVERTEX due to splitting a type-3 segment. - segloop.shver = 0; // segloop.shver = i; - senext(segloop, nextseg); - spivotself(nextseg); - if (nextseg.sh != dummysh) { - pa = sdest(segloop); // p is going to be checked for removal. - nextseg.shver = 0; - if (sorg(nextseg) != pa) sesymself(nextseg); - assert(sorg(nextseg) == pa); - if (!coarseflag) { - // try to remove it if it is not an input point. - j = pointmark(pa) - in->firstnumber; - if (j >= in->numberofpoints) { - if (b->nobisect == 1) { - // '-Y'. Remove p if it is on the hull. - sstpivot(&segloop, &checktet); - assert(checktet.tet != dummytet); - pa = apex(checktet); - do { - if (!fnextself(checktet)) { - // Meet a boundary face - p is on the hull. - remflag = true; break; - } - } while (pa != apex(checktet)); - } else { - // '-YY'. Remove p whatever it is on the hull or not. - remflag = true; - } - } - } else { - // Check if this vertex can be coarsed. - if (b->nobisect == 0) { - if (b->metric) { - // assert(pa[pointmtrindex] > 0.0); - len = 0.0; - neipt = sorg(segloop); - for (j = 0; j < 2; j++) { - len += distance(pa, neipt); - /*// Is neipt inside the sparse ball of pa? - if (len < pa[pointmtrindex]) { - // Yes, the local of pa is too dense, corse it. - remflag = true; break; - } */ - neipt = sdest(nextseg); - } - len /= 2.0; - // Carse it if the average edge lengh is small. - remflag = len < pa[pointmtrindex]; - } else { - // Coarse it if (1) it is an input point and its pointmarker - // is zero, or (2) it is a Steiner point. - remflag = true; - j = pointmark(pa) - in->firstnumber; - if (j < in->numberofpoints) { - remflag = (in->pointmarkerlist[j] == 0); - } - } // if (b->metric) - } // if (b->nobisect == 0) - } // if (!coarseflag) - } // if (nextseg.sh != dummysh) - // if (remflag) break; - // } // for (i = 0; i < 2; i++) - if (remflag) { - suppresssegpoint(&segloop, spinshlist, newsegshlist, frontlist, - misfrontlist, ptlist, conlist, viri, flipque, coarseflag, false); - } - segloop.sh = shellfacetraverse(subsegs); - } - // Continue if any Steiner point has been removed. - } while (unuverts > rmstein); - - if ((relverts > 0) || coarseflag) { - worklist = new int[points->items + 1]; - // Suppress relocated points & coarse free mesh points. - do { - // Initialize the work list. Each entry of the list counts how many - // times the point has been processed. - for (i = 0; i < points->items + 1; i++) worklist[i] = 0; - rmstein = unuverts; - tetrahedrons->traversalinit(); - checktet.tet = tetrahedrontraverse(); - while (checktet.tet != (tetrahedron *) NULL) { - remflag = false; - for (i = 0; i < 4; i++) { - pa = (point) checktet.tet[4 + i]; - if (pointtype(pa) == FREEVOLVERTEX) { - // NOTE. Chenge the number 3 will change the number n of removed - // Steiner points. In my test, n is larger when it is 1. 3 - // reduces n in a reasonable way (see example, mech_part, - // thepart), 5 results a larger n than 3 does. While the best - // result is no limit of this number, but it makes the code - // extremely slow. - if (worklist[pointmark(pa)] < 3) { - worklist[pointmark(pa)]++; - if (!coarseflag) { - // Remove p if it is a Steiner point. - if (pointmark(pa) >= (in->numberofpoints + in->firstnumber)) { - remflag = true; - } - } else { - if (b->metric) { - // assert(pa[pointmtrindex] > 0.0); - // Form the star of pa. - frontlist->append(&checktet); - formstarpolyhedron(pa, frontlist, ptlist, true); - len = 0.0; - for (j = 0; j < ptlist->len(); j++) { - neipt = * (point *)(* ptlist)[j]; - len += distance(pa, neipt); - } - len /= ptlist->len(); - // Carse it if the average edge length is small. - remflag = len < pa[pointmtrindex]; - frontlist->clear(); - ptlist->clear(); - } else { - // Coarse it if (1) it is an input point and its pointmarker - // is zero, or (2) it is a Steiner point. - remflag = true; - j = pointmark(pa) - in->firstnumber; - if (j < in->numberofpoints) { - remflag = (in->pointmarkerlist[j] == 0); - } - } // if (b->metric) - } // if (!coarseflag) - if (remflag) break; - } // if (worklist[pointmark(pa)] == 0) - } // if (pointtype(pa) == FREEVOLVERTEX) - } // for (i = 0; i < 4; i++) - if (remflag) { - findorg(&checktet, pa); - assert(org(checktet) == pa); - suppressvolpoint(&checktet, frontlist, misfrontlist, ptlist, flipque, - false); - } - checktet.tet = tetrahedrontraverse(); - } - // Continue if any relocated point has been suppressed. - } while (unuverts > rmstein); - - - // Smooth the unsuppressed points if it is not coarse mesh. - if (!coarseflag && (relverts > suprelverts)) { - if (b->verbose) { - printf(" Smoothing relocated points.\n"); - } - for (i = 0; i < points->items + 1; i++) worklist[i] = 0; - tetrahedrons->traversalinit(); - checktet.tet = tetrahedrontraverse(); - while (checktet.tet != (tetrahedron *) NULL) { - for (i = 0; i < 4; i++) { - pa = (point) checktet.tet[4 + i]; - if (pointtype(pa) == FREEVOLVERTEX) { - if (worklist[pointmark(pa)] == 0) { - worklist[pointmark(pa)] = 1; - if (pointmark(pa) >= (in->numberofpoints + in->firstnumber)) { - // Smooth pa. - findorg(&checktet, pa); - frontlist->append(&checktet); - formstarpolyhedron(pa, frontlist, NULL, false); - smoothpoint(pa, NULL, NULL, frontlist, false, NULL); - frontlist->clear(); - } - } // if (worklist[pointmark(pa)] == 0) - } // if (pointtype(pa) == FREEVOLVERTEX) - } // for (i = 0; i < 4; i++) - checktet.tet = tetrahedrontraverse(); - } - } - delete [] worklist; - } - - if (b->verbose > 0) { - if (!coarseflag) { - printf(" %d points removed from boundary", unuverts - oldnum); - if (expcavcount > 0) { - printf(" (%d cavity corrections)", expcavcount); - } - printf("\n"); - if (relverts > 0) { - printf(" %d points relocated (%d suppressed, %d collapsed).\n", - relverts, suprelverts - collapverts, collapverts); - if (smoothvolverts > 0) { - printf(" %d points are smoothed.\n", smoothvolverts); - } - } - if (unsupverts > 0) { - printf(" !! %d points are unsuppressed.\n", unsupverts); - } - } else { - printf(" %d points are removed.\n", unuverts - oldnum); - } - } - - // Delete work lists. - delete frontlist; - delete misfrontlist; - delete spinshlist; - delete newsegshlist; - delete ptlist; - delete conlist; - delete flipque; - delete viri; -} - -// -// End of boundary Steiner points removing routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// reconstructmesh() Reconstruct a tetrahedral mesh from a list of // -// tetrahedra and possibly a list of boundary faces. // -// // -// The list of tetrahedra is stored in 'in->tetrahedronlist', the list of // -// boundary faces is stored in 'in->trifacelist'. The tetrahedral mesh is // -// reconstructed in memorypool 'tetrahedrons', its boundary faces (subfaces) // -// are reconstructed in 'subfaces', its boundary edges (subsegments) are // -// reconstructed in 'subsegs'. If the -a switch is used, this procedure will // -// also read a list of REALs from 'in->tetrahedronvolumelist' and set a // -// maximum volume constraint on each tetrahedron. // -// // -// If the user has provided the boundary faces in 'in->trifacelist', they // -// will be inserted the mesh. Otherwise subfaces will be identified from the // -// mesh. All hull faces (including faces of the internal holes) will be // -// recognized as subfaces, internal faces between two tetrahedra which have // -// different attributes will also be recognized as subfaces. // -// // -// Subsegments will be identified after subfaces are reconstructed. Edges at // -// the intersections of non-coplanar subfaces are recognized as subsegments. // -// Edges between two coplanar subfaces with different boundary markers are // -// also recognized as subsegments. // -// // -// The facet index of each subface will be set automatically after we have // -// recovered subfaces and subsegments. That is, the set of subfaces, which // -// are coplanar and have the same boundary marker will be recognized as a // -// facet and has a unique index, stored as the facet marker in each subface // -// of the set, the real boundary marker of each subface will be found in // -// 'in->facetmarkerlist' by the index. Facet index will be used in Delaunay // -// refinement for detecting two incident facets. // -// // -// Points which are not corners of tetrahedra will be inserted into the mesh.// -// Return the number of faces on the hull after the reconstruction. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::reconstructmesh() -{ - tetrahedron **tetsperverlist; - shellface **facesperverlist; - triface tetloop, neightet, neineightet, spintet; - face subloop, neighsh, neineighsh, subseg; - face sface1, sface2; - point *idx2verlist; - point torg, tdest, tapex, toppo; - point norg, ndest, napex; - list *neighshlist, *markerlist; - REAL sign, attrib, volume; - REAL da1, da2; - bool bondflag, insertsegflag; - int *idx2tetlist; - int *idx2facelist; - int *worklist; - int facetidx, marker; - int iorg, idest, iapex, ioppo; - int inorg, indest, inapex; - int index, i, j; - - if (!b->quiet) { - printf("Reconstructing mesh.\n"); - } - - // Create a map from index to points. - makeindex2pointmap(idx2verlist); - - // Create the tetrahedra. - for (i = 0; i < in->numberoftetrahedra; i++) { - // Create a new tetrahedron and set its four corners, make sure that - // four corners form a positive orientation. - maketetrahedron(&tetloop); - index = i * in->numberofcorners; - // Although there may be 10 nodes, we only read the first 4. - iorg = in->tetrahedronlist[index] - in->firstnumber; - idest = in->tetrahedronlist[index + 1] - in->firstnumber; - iapex = in->tetrahedronlist[index + 2] - in->firstnumber; - ioppo = in->tetrahedronlist[index + 3] - in->firstnumber; - torg = idx2verlist[iorg]; - tdest = idx2verlist[idest]; - tapex = idx2verlist[iapex]; - toppo = idx2verlist[ioppo]; - sign = orient3d(torg, tdest, tapex, toppo); - if (sign > 0.0) { - norg = torg; torg = tdest; tdest = norg; - } else if (sign == 0.0) { - if (!b->quiet) { - printf("Warning: Tet %d is degenerate.\n", i + in->firstnumber); - } - } - setorg(tetloop, torg); - setdest(tetloop, tdest); - setapex(tetloop, tapex); - setoppo(tetloop, toppo); - // Temporarily set the vertices be type FREEVOLVERTEX, to indicate that - // they belong to the mesh. These types may be changed later. - setpointtype(torg, FREEVOLVERTEX); - setpointtype(tdest, FREEVOLVERTEX); - setpointtype(tapex, FREEVOLVERTEX); - setpointtype(toppo, FREEVOLVERTEX); - // Set element attributes if they exist. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - index = i * in->numberoftetrahedronattributes; - attrib = in->tetrahedronattributelist[index + j]; - setelemattribute(tetloop.tet, j, attrib); - } - // If -a switch is used (with no number follows) Set a volume - // constraint if it exists. - if (b->varvolume) { - if (in->tetrahedronvolumelist != (REAL *) NULL) { - volume = in->tetrahedronvolumelist[i]; - } else { - volume = -1.0; - } - setvolumebound(tetloop.tet, volume); - } - } - - // Set the connection between tetrahedra. - hullsize = 0l; - // Create a map from nodes to tetrahedra. - maketetrahedronmap(idx2tetlist, tetsperverlist); - // Initialize the worklist. - worklist = new int[points->items]; - for (i = 0; i < points->items; i++) worklist[i] = 0; - - // Loop all tetrahedra, bond two tetrahedra if they share a common face. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Loop the four sides of the tetrahedron. - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - sym(tetloop, neightet); - if (neightet.tet != dummytet) continue; // This side has finished. - torg = org(tetloop); - tdest = dest(tetloop); - tapex = apex(tetloop); - iorg = pointmark(torg) - in->firstnumber; - idest = pointmark(tdest) - in->firstnumber; - iapex = pointmark(tapex) - in->firstnumber; - worklist[iorg] = 1; - worklist[idest] = 1; - worklist[iapex] = 1; - bondflag = false; - // Search its neighbor in the adjacent tets of torg. - for (j = idx2tetlist[iorg]; j < idx2tetlist[iorg + 1] && !bondflag; - j++) { - if (tetsperverlist[j] == tetloop.tet) continue; // Skip myself. - neightet.tet = tetsperverlist[j]; - for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - sym(neightet, neineightet); - if (neineightet.tet == dummytet) { - norg = org(neightet); - ndest = dest(neightet); - napex = apex(neightet); - inorg = pointmark(norg) - in->firstnumber; - indest = pointmark(ndest) - in->firstnumber; - inapex = pointmark(napex) - in->firstnumber; - if ((worklist[inorg] + worklist[indest] + worklist[inapex]) == 3) { - // Find! Bond them together and break the loop. - bond(tetloop, neightet); - bondflag = true; - break; - } - } - } - } - if (!bondflag) { - hullsize++; // It's a hull face. - // Bond this side to outer space. - dummytet[0] = encode(tetloop); - if ((in->pointmarkerlist != (int *) NULL) && !b->coarse) { - // Set its three corners's markers be boundary (hull) vertices. - if (in->pointmarkerlist[iorg] == 0) { - in->pointmarkerlist[iorg] = 1; - } - if (in->pointmarkerlist[idest] == 0) { - in->pointmarkerlist[idest] = 1; - } - if (in->pointmarkerlist[iapex] == 0) { - in->pointmarkerlist[iapex] = 1; - } - } - } - worklist[iorg] = 0; - worklist[idest] = 0; - worklist[iapex] = 0; - } - tetloop.tet = tetrahedrontraverse(); - } - - // Subfaces will be inserted into the mesh. It has two phases: - // (1) Insert subfaces provided by user (in->trifacelist); - // (2) Create subfaces for hull faces (if they're not subface yet) and - // interior faces which separate two different materials. - - // Phase (1). Is there a list of user-provided subfaces? - if (in->trifacelist != (int *) NULL) { - // Recover subfaces from 'in->trifacelist'. - for (i = 0; i < in->numberoftrifaces; i++) { - index = i * 3; - iorg = in->trifacelist[index] - in->firstnumber; - idest = in->trifacelist[index + 1] - in->firstnumber; - iapex = in->trifacelist[index + 2] - in->firstnumber; - // Look for the location of this subface. - worklist[iorg] = 1; - worklist[idest] = 1; - worklist[iapex] = 1; - bondflag = false; - // Search its neighbor in the adjacent tets of torg. - for (j = idx2tetlist[iorg]; j < idx2tetlist[iorg + 1] && !bondflag; - j++) { - neightet.tet = tetsperverlist[j]; - for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { - norg = org(neightet); - ndest = dest(neightet); - napex = apex(neightet); - inorg = pointmark(norg) - in->firstnumber; - indest = pointmark(ndest) - in->firstnumber; - inapex = pointmark(napex) - in->firstnumber; - if ((worklist[inorg] + worklist[indest] + worklist[inapex]) == 3) { - bondflag = true; // Find! - break; - } - } - } - if (bondflag) { - // Create a new subface and insert it into the mesh. - makeshellface(subfaces, &subloop); - torg = idx2verlist[iorg]; - tdest = idx2verlist[idest]; - tapex = idx2verlist[iapex]; - setsorg(subloop, torg); - setsdest(subloop, tdest); - setsapex(subloop, tapex); - // Set the vertices be FREESUBVERTEX to indicate they belong to a - // facet of the domain. They may be changed later. - setpointtype(torg, FREESUBVERTEX); - setpointtype(tdest, FREESUBVERTEX); - setpointtype(tapex, FREESUBVERTEX); - if (in->trifacemarkerlist != (int *) NULL) { - setshellmark(subloop, in->trifacemarkerlist[i]); - } - adjustedgering(neightet, CCW); - findedge(&subloop, org(neightet), dest(neightet)); - tsbond(neightet, subloop); - sym(neightet, neineightet); - if (neineightet.tet != dummytet) { - sesymself(subloop); - tsbond(neineightet, subloop); - } - } else { - if (!b->quiet) { - printf("Warning: Subface %d is discarded.\n", i + in->firstnumber); - } - } - worklist[iorg] = 0; - worklist[idest] = 0; - worklist[iapex] = 0; - } - } - - // Phase (2). Indentify subfaces from the mesh. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Loop the four sides of the tetrahedron. - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - tspivot(tetloop, subloop); - if (subloop.sh != dummysh) continue; - bondflag = false; - sym(tetloop, neightet); - if (neightet.tet == dummytet) { - // It's a hull face. Insert a subface at here. - bondflag = true; - } else { - // It's an interior face. Insert a subface if two tetrahedra have - // different attributes (i.e., they belong to two regions). - if (in->numberoftetrahedronattributes > 0) { - if (elemattribute(neightet.tet, - in->numberoftetrahedronattributes - 1) != - elemattribute(tetloop.tet, - in->numberoftetrahedronattributes - 1)) { - bondflag = true; - } - } - } - if (bondflag) { - adjustedgering(tetloop, CCW); - makeshellface(subfaces, &subloop); - torg = org(tetloop); - tdest = dest(tetloop); - tapex = apex(tetloop); - setsorg(subloop, torg); - setsdest(subloop, tdest); - setsapex(subloop, tapex); - // Set the vertices be FREESUBVERTEX to indicate they belong to a - // facet of the domain. They may be changed later. - setpointtype(torg, FREESUBVERTEX); - setpointtype(tdest, FREESUBVERTEX); - setpointtype(tapex, FREESUBVERTEX); - tsbond(tetloop, subloop); - if (neightet.tet != dummytet) { - sesymself(subloop); - tsbond(neightet, subloop); - } - } - } - tetloop.tet = tetrahedrontraverse(); - } - - // Set the connection between subfaces. A subsegment may have more than - // two subfaces sharing it, 'neighshlist' stores all subfaces sharing - // one edge. - neighshlist = new list(sizeof(face), NULL); - // Create a map from nodes to subfaces. - makesubfacemap(idx2facelist, facesperverlist); - - // Loop over the set of subfaces, setup the connection between subfaces. - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - for (i = 0; i < 3; i++) { - spivot(subloop, neighsh); - if (neighsh.sh == dummysh) { - // This side is 'empty', operate on it. - torg = sorg(subloop); - tdest = sdest(subloop); - tapex = sapex(subloop); - neighshlist->append(&subloop); - iorg = pointmark(torg) - in->firstnumber; - // Search its neighbor in the adjacent list of torg. - for (j = idx2facelist[iorg]; j < idx2facelist[iorg + 1]; j++) { - neighsh.sh = facesperverlist[j]; - if (neighsh.sh == subloop.sh) continue; - neighsh.shver = 0; - if (isfacehasedge(&neighsh, torg, tdest)) { - findedge(&neighsh, torg, tdest); - // Insert 'neighsh' into 'neighshlist'. - if (neighshlist->len() < 2) { - neighshlist->append(&neighsh); - } else { - for (index = 0; index < neighshlist->len() - 1; index++) { - sface1 = * (face *)(* neighshlist)[index]; - sface2 = * (face *)(* neighshlist)[index + 1]; - da1 = facedihedral(torg, tdest, sapex(sface1), sapex(neighsh)); - da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2)); - if (da1 < da2) { - break; // Insert it after index. - } - } - neighshlist->insert(index + 1, &neighsh); - } - } - } - // Bond the subfaces in 'neighshlist'. - if (neighshlist->len() > 1) { - neighsh = * (face *)(* neighshlist)[0]; - for (j = 1; j <= neighshlist->len(); j++) { - if (j < neighshlist->len()) { - neineighsh = * (face *)(* neighshlist)[j]; - } else { - neineighsh = * (face *)(* neighshlist)[0]; - } - sbond1(neighsh, neineighsh); - neighsh = neineighsh; - } - } else { - // No neighbor subface be found, bond 'subloop' to itself. - sbond(subloop, subloop); - } - neighshlist->clear(); - } - senextself(subloop); - } - subloop.sh = shellfacetraverse(subfaces); - } - - // Segments will be introudced. Each segment has a unique marker (1-based). - marker = 1; - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - for (i = 0; i < 3; i++) { - sspivot(subloop, subseg); - if (subseg.sh == dummysh) { - // This side has no subsegment bonded, check it. - torg = sorg(subloop); - tdest = sdest(subloop); - tapex = sapex(subloop); - spivot(subloop, neighsh); - spivot(neighsh, neineighsh); - insertsegflag = false; - if (subloop.sh == neighsh.sh || subloop.sh != neineighsh.sh) { - // This side is either self-bonded or more than two subfaces, - // insert a subsegment at this side. - insertsegflag = true; - } else { - // Only two subfaces case. -#ifdef SELF_CHECK - assert(subloop.sh != neighsh.sh); -#endif - napex = sapex(neighsh); - sign = orient3d(torg, tdest, tapex, napex); - if (iscoplanar(torg, tdest, tapex, napex, sign, b->epsilon)) { - // Although they are coplanar, we still need to check if they - // have the same boundary marker. - insertsegflag = (shellmark(subloop) != shellmark(neighsh)); - } else { - // Non-coplanar. - insertsegflag = true; - } - } - if (insertsegflag) { - // Create a subsegment at this side. - makeshellface(subsegs, &subseg); - setsorg(subseg, torg); - setsdest(subseg, tdest); - // The two vertices have been marked as FREESUBVERTEX. Now mark - // them as NACUTEVERTEX. - setpointtype(torg, NACUTEVERTEX); - setpointtype(tdest, NACUTEVERTEX); - setshellmark(subseg, marker); - marker++; - // Bond all subfaces to this subsegment. - neighsh = subloop; - do { - ssbond(neighsh, subseg); - spivotself(neighsh); - } while (neighsh.sh != subloop.sh); - } - } - senextself(subloop); - } - subloop.sh = shellfacetraverse(subfaces); - } - // Remember the number of input segments. - insegments = subsegs->items; - // Find the acute vertices and set them be type ACUTEVERTEX. - - // Indentify facets and set the facet marker (1-based) for subfaces. - markerlist = new list("int"); - - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - // Only operate on uninfected subface, after operating, infect it. - if (!sinfected(subloop)) { - // A new facet is found. - marker = shellmark(subloop); - markerlist->append(&marker); - facetidx = markerlist->len(); // 'facetidx' starts from 1. - setshellmark(subloop, facetidx); - sinfect(subloop); - neighshlist->append(&subloop); - // Find out all subfaces of this facet (bounded by subsegments). - for (i = 0; i < neighshlist->len(); i++) { - neighsh = * (face *) (* neighshlist)[i]; - for (j = 0; j < 3; j++) { - sspivot(neighsh, subseg); - if (subseg.sh == dummysh) { - spivot(neighsh, neineighsh); - if (!sinfected(neineighsh)) { - // 'neineighsh' is in the same facet as 'subloop'. -#ifdef SELF_CHECK - assert(shellmark(neineighsh) == marker); -#endif - setshellmark(neineighsh, facetidx); - sinfect(neineighsh); - neighshlist->append(&neineighsh); - } - } - senextself(neighsh); - } - } - neighshlist->clear(); - } - subloop.sh = shellfacetraverse(subfaces); - } - // Uninfect all subfaces. - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { -#ifdef SELF_CHECK - assert(sinfected(subloop)); -#endif - suninfect(subloop); - subloop.sh = shellfacetraverse(subfaces); - } - // Save the facet markers in 'in->facetmarkerlist'. - in->numberoffacets = markerlist->len(); - in->facetmarkerlist = new int[in->numberoffacets]; - for (i = 0; i < in->numberoffacets; i++) { - marker = * (int *) (* markerlist)[i]; - in->facetmarkerlist[i] = marker; - } - // Initialize the 'facetabovepointlist'. - facetabovepointarray = new point[in->numberoffacets + 1]; - for (i = 0; i < in->numberoffacets + 1; i++) { - facetabovepointarray[i] = (point) NULL; - } - - // The mesh contains boundary now. - checksubfaces = 1; - // The mesh is nonconvex now. - nonconvex = 1; - - // Is there periodic boundary confitions? - if (checkpbcs) { - tetgenio::pbcgroup *pg; - pbcdata *pd; - // Initialize the global array 'subpbcgrouptable'. - createsubpbcgrouptable(); - // Loop for each pbcgroup i. - for (i = 0; i < in->numberofpbcgroups; i++) { - pg = &(in->pbcgrouplist[i]); - pd = &(subpbcgrouptable[i]); - // Find all subfaces of pd, set each subface's group id be i. - for (j = 0; j < 2; j++) { - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - facetidx = shellmark(subloop); - marker = in->facetmarkerlist[facetidx - 1]; - if (marker == pd->fmark[j]) { - setshellpbcgroup(subloop, i); - pd->ss[j] = subloop; - } - subloop.sh = shellfacetraverse(subfaces); - } - } - if (pg->pointpairlist != (int *) NULL) { - // Set the connections between pbc point pairs. - for (j = 0; j < pg->numberofpointpairs; j++) { - iorg = pg->pointpairlist[j * 2] - in->firstnumber; - idest = pg->pointpairlist[j * 2 + 1] - in->firstnumber; - torg = idx2verlist[iorg]; - tdest = idx2verlist[idest]; - setpoint2pbcpt(torg, tdest); - setpoint2pbcpt(tdest, torg); - } - } - } - // Create the global array 'segpbcgrouptable'. - createsegpbcgrouptable(); - } - - delete markerlist; - delete neighshlist; - delete [] worklist; - delete [] idx2tetlist; - delete [] tetsperverlist; - delete [] idx2facelist; - delete [] facesperverlist; - delete [] idx2verlist; - - return hullsize; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertconstrainedpoints() Insert a list of constrained points. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::insertconstrainedpoints(tetgenio *addio) -{ - queue *flipqueue; - triface searchtet; - face checksh, checkseg; - point newpoint; - enum locateresult loc; - REAL *attr; - bool insertflag; - int covertices, outvertices; - int index; - int i, j; - - if (!b->quiet) { - printf("Insert additional points into mesh.\n"); - } - // Initialize 'flipqueue'. - flipqueue = new queue(sizeof(badface)); - recenttet.tet = dummytet; - covertices = outvertices = 0; - - index = 0; - for (i = 0; i < addio->numberofpoints; i++) { - // Create a newpoint. - makepoint(&newpoint); - newpoint[0] = addio->pointlist[index++]; - newpoint[1] = addio->pointlist[index++]; - newpoint[2] = addio->pointlist[index++]; - // Read the add point attributes if current points have attributes. - if ((addio->numberofpointattributes > 0) && - (in->numberofpointattributes > 0)) { - attr = addio->pointattributelist + addio->numberofpointattributes * i; - for (j = 0; j < in->numberofpointattributes; j++) { - if (j < addio->numberofpointattributes) { - newpoint[3 + j] = attr[j]; - } - } - } - // Find the location of the inserted point. - searchtet = recenttet; - loc = locate(newpoint, &searchtet); - if (loc != ONVERTEX) { - loc = adjustlocate(newpoint, &searchtet, loc, b->epsilon2); - } - if (loc == OUTSIDE) { - loc = hullwalk(newpoint, &searchtet); - if (loc == OUTSIDE) { - // Perform a brute-force search. - tetrahedrons->traversalinit(); - searchtet.tet = tetrahedrontraverse(); - while (searchtet.tet != (tetrahedron *) NULL) { - loc = adjustlocate(newpoint, &searchtet, OUTSIDE, b->epsilon2); - if (loc != OUTSIDE) break; - searchtet.tet = tetrahedrontraverse(); - } - } - } - // Insert the point if it not lies outside or on a vertex. - insertflag = true; - switch (loc) { - case INTETRAHEDRON: - setpointtype(newpoint, FREEVOLVERTEX); - splittetrahedron(newpoint, &searchtet, flipqueue); - break; - case ONFACE: - tspivot(searchtet, checksh); - if (checksh.sh != dummysh) { - // It is a boundary face. Don't insert it if -Y option is used. - if (b->nobisect) { - insertflag = false; - } else { - setpointtype(newpoint, FREESUBVERTEX); - } - } else { - setpointtype(newpoint, FREEVOLVERTEX); - } - if (insertflag) { - splittetface(newpoint, &searchtet, flipqueue); - } - break; - case ONEDGE: - tsspivot(&searchtet, &checkseg); - if (checkseg.sh != dummysh) { - if (b->nobisect) { - insertflag = false; - } else { - setpointtype(newpoint, FREESEGVERTEX); - setpoint2sh(newpoint, sencode(checkseg)); - } - } else { - tspivot(searchtet, checksh); - if (checksh.sh != dummysh) { - if (b->nobisect) { - insertflag = false; - } else { - setpointtype(newpoint, FREESUBVERTEX); - } - } else { - setpointtype(newpoint, FREEVOLVERTEX); - } - } - if (insertflag) { - splittetedge(newpoint, &searchtet, flipqueue); - } - break; - case ONVERTEX: - insertflag = false; - covertices++; - break; - case OUTSIDE: - insertflag = false; - outvertices++; - break; - } - // Remember the tetrahedron for next point searching. - recenttet = searchtet; - if (!insertflag) { - pointdealloc(newpoint); - } else { - flip(flipqueue, NULL); - } - } - - if (b->verbose) { - if (covertices > 0) { - printf(" %d constrained points already exist.\n", covertices); - } - if (outvertices > 0) { - printf(" %d constrained points lie outside the mesh.\n", outvertices); - } - printf(" %d constrained points have been inserted.\n", - addio->numberofpoints - covertices - outvertices); - } - - delete flipqueue; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// p1interpolatebgm() Set pt size by p^1 interpolation in background mesh.// -// // -// On input, 'bgmtet' is a suggesting tet in background mesh for searching // -// 'pt'. It returns the tet containing 'pt'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::p1interpolatebgm(point pt, triface* bgmtet, long *scount) -{ - point bgmpt[4]; - enum locateresult loc; - REAL vol, volpt[4], weights[4]; - int i; - - loc = bgm->preciselocate(pt, bgmtet, bgm->tetrahedrons->items); - if (loc == OUTSIDE) { - loc = bgm->hullwalk(pt, bgmtet); - if (loc == OUTSIDE) { - // Perform a brute-force search. - if (b->verbose) { - printf("Warning: Global point location.\n"); - } - if (scount) (*scount)++; - bgm->tetrahedrons->traversalinit(); // in bgm - bgmtet->tet = bgm->tetrahedrontraverse(); // in bgm - while (bgmtet->tet != (tetrahedron *) NULL) { - loc = bgm->adjustlocate(pt, bgmtet, OUTSIDE, b->epsilon); - if (loc != OUTSIDE) break; - bgmtet->tet = bgm->tetrahedrontraverse(); // in bgm - } - } - } - if (loc != OUTSIDE) { - // Let p remember t. - setpoint2bgmtet(pt, encode(*bgmtet)); // in m - // get the corners of t. - for (i = 0; i < 4; i++) bgmpt[i] = (point) bgmtet->tet[4 + i]; - // Calculate the weighted coordinates of p in t. - vol = orient3d(bgmpt[0], bgmpt[1], bgmpt[2], bgmpt[3]); - volpt[0] = orient3d(pt, bgmpt[1], bgmpt[2], bgmpt[3]); - volpt[1] = orient3d(bgmpt[0], pt, bgmpt[2], bgmpt[3]); - volpt[2] = orient3d(bgmpt[0], bgmpt[1], pt, bgmpt[3]); - volpt[3] = orient3d(bgmpt[0], bgmpt[1], bgmpt[2], pt); - for (i = 0; i < 4; i++) weights[i] = fabs(volpt[i] / vol); - // Interpolate the solution for p. - for (i = 0; i < bgm->in->numberofpointmtrs; i++) { - pt[pointmtrindex + i] = weights[0] * bgmpt[0][bgm->pointmtrindex + i] - + weights[1] * bgmpt[1][bgm->pointmtrindex + i] - + weights[2] * bgmpt[2][bgm->pointmtrindex + i] - + weights[3] * bgmpt[3][bgm->pointmtrindex + i]; - } - } else { - setpoint2bgmtet(pt, (tetrahedron) NULL); // in m - } - return loc != OUTSIDE; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// interpolatesizemap() Interpolate the point sizes in the given size map.// -// // -// The size map is specified on each node of the background mesh. The points // -// of current mesh get their sizes by interpolating. // -// // -// This function operation on two meshes simultaneously, the current mesh m, // -// and the background mesh bgm. After this function, each point p in m will // -// have a pointer to a tet of bgm. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::interpolatesizemap() -{ - list *adjtetlist; - triface tetloop, neightet, bgmtet; - point searchpt; - long scount; - int *worklist; - int sepcount; - int i; - - if (b->verbose) { - printf(" Interpolating size map.\n"); - } - - worklist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) worklist[i] = 0; - sepcount = 0; - scount = 0l; - - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - if (!infected(tetloop)) { - // Find a new subdomain. - adjtetlist = new list(sizeof(triface), NULL, 1024); - infect(tetloop); - // Search the four corners in background mesh. - for (i = 0; i < 4; i++) { - searchpt = (point) tetloop.tet[4 + i]; - // Mark the point for avoiding multiple searchings. - // assert(worklist[pointmark(searchpt)] == 0); - worklist[pointmark(searchpt)] = 1; - // Does it contain a pointer to bgm tet? - bgm->decode(point2bgmtet(searchpt), bgmtet); - if (bgm->isdead(&bgmtet)) { - bgmtet = bgm->recenttet; - } - if (p1interpolatebgm(searchpt, &bgmtet, &scount)) { - bgm->recenttet = bgmtet; - } - } // for (i = 0; i < 4; i++) - // Collect all tets in this region. - adjtetlist->append(&tetloop); - // Collect the tets in the subdomain. - for (i = 0; i < adjtetlist->len(); i++) { - tetloop = * (triface *)(* adjtetlist)[i]; - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - sym(tetloop, neightet); - if ((neightet.tet != dummytet) && !infected(neightet)) { - // Only need to search for the opposite point. - searchpt = oppo(neightet); - if (worklist[pointmark(searchpt)] == 0) { - worklist[pointmark(searchpt)] = 1; - decode(point2bgmtet(searchpt), bgmtet); - if (bgm->isdead(&bgmtet)) { - bgmtet = bgm->recenttet; - } - if (p1interpolatebgm(searchpt, &bgmtet, &scount)) { - bgm->recenttet = bgmtet; - } - } - infect(neightet); - adjtetlist->append(&neightet); - } - } - } - // Increase the number of separated domains. - sepcount++; - delete adjtetlist; - } // if (!infect()) - tetloop.tet = tetrahedrontraverse(); - } - - // Unmark all tets. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - assert(infected(tetloop)); - uninfect(tetloop); - tetloop.tet = tetrahedrontraverse(); - } - delete [] worklist; - -#ifdef SELF_CHECK - if (b->verbose && scount > 0l) { - printf(" %ld brute-force searches.\n", scount); - } - if (b->verbose && sepcount > 0) { - printf(" %d separate domains.\n", sepcount); - } -#endif -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// duplicatebgmesh() Duplicate current mesh to background mesh. // -// // -// Current mesh 'this' is copied into 'this->bgm'.Both meshes share the same // -// input tetgenio object, 'this->in', same tetgenbehavior object 'this->b'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::duplicatebgmesh() -{ - triface tetloop, btetloop; - triface symtet, bsymtet; - face bhullsh, bneighsh; - point *idx2bplist, *tetptbaklist; - point ploop, bploop; - int idx, i; - - if (!b->quiet) { - printf("Duplicating background mesh.\n"); - } - - // The background mesh itself has no background mesh. - // assert(bgm->bgm == (tetgenmesh *) NULL); - // The space for metric tensor should be allocated. - // assert(bgm->sizeoftensor > 0); - - // Copy point list. - idx2bplist = new point[points->items + 1]; - idx = in->firstnumber; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - bgm->makepoint(&bploop); - // Copy coordinates, attributes. - for (i = 0; i < 3 + in->numberofpointattributes; i++) { - bploop[i] = ploop[i]; - } - // Transfer the metric tensor. - for (i = 0; i < bgm->sizeoftensor; i++) { - bploop[bgm->pointmtrindex + i] = ploop[pointmtrindex + i]; - // Metric tensor should have a positive value. - if (bploop[bgm->pointmtrindex + i] <= 0.0) { - printf("Error: Point %d has non-positive size %g (-m option).\n", - bgm->pointmark(bploop), bploop[bgm->pointmtrindex + i]); - terminatetetgen(1); - } - } - // Remember the point for searching. - idx2bplist[idx++] = bploop; - ploop = pointtraverse(); - } - - // Copy tetrahedra list. - tetptbaklist = new point[tetrahedrons->items + 1]; - idx = in->firstnumber; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - bgm->maketetrahedron(&btetloop); - // Set the four corners. - for (i = 0; i < 4; i++) { - ploop = (point) tetloop.tet[4 + i]; - bploop = idx2bplist[pointmark(ploop)]; - btetloop.tet[4 + i] = (tetrahedron) bploop; - } - // Remember the tet for setting neighbor connections. - tetptbaklist[idx++] = (point) tetloop.tet[4]; - tetloop.tet[4] = (tetrahedron) btetloop.tet; - tetloop.tet = tetrahedrontraverse(); - } - - // Set the connections between background tetrahedra. Create background - // hull subfaces. Create the map of point-to-bgmtet. - idx = in->firstnumber; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Get the corresponding background tet. - btetloop.tet = (tetrahedron *) tetloop.tet[4]; - // Set the four neighbors. - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - btetloop.loc = tetloop.loc; - sym(tetloop, symtet); - if ((symtet.tet != dummytet) && (symtet.tet > tetloop.tet)) { - // Operate on the un-connected interior face. - bsymtet.tet = (tetrahedron *) symtet.tet[4]; // The saved bgm tet. - bsymtet.loc = symtet.loc; - bgm->bond(btetloop, bsymtet); - } else if (symtet.tet == dummytet) { - // Create a subface in background mesh. - bgm->makeshellface(bgm->subfaces, &bhullsh); - bgm->adjustedgering(btetloop, CCW); // face to inside. - bgm->setsorg(bhullsh, bgm->org(btetloop)); - bgm->setsdest(bhullsh, bgm->dest(btetloop)); - bgm->setsapex(bhullsh, bgm->apex(btetloop)); - bgm->tsbond(btetloop, bhullsh); - // Remember a hull face for point location. - bgm->dummytet[0] = bgm->encode(btetloop); - } - } - // Restore the backup tet point. - tetloop.tet[4] = (tetrahedron) tetptbaklist[idx++]; - // Make the point-to-bgmtet map for size interpolation. - btetloop.loc = 0; - for (i = 0; i < 4; i++) { - ploop = (point) tetloop.tet[4 + i]; - setpoint2bgmtet(ploop, bgm->encode(btetloop)); - } - // Go to the next tet, btet. - tetloop.tet = tetrahedrontraverse(); - } - - // Connect bgm hull subfaces. Note: all hull subfaces form a 2-manifold. - bgm->subfaces->traversalinit(); - bhullsh.sh = bgm->shellfacetraverse(bgm->subfaces); - while (bhullsh.sh != (shellface *) NULL) { - bhullsh.shver = 0; - bgm->stpivot(bhullsh, btetloop); - assert(btetloop.tet != bgm->dummytet); - bgm->adjustedgering(btetloop, CCW); - for (i = 0; i < 3; i++) { - bgm->spivot(bhullsh, bneighsh); - if (bneighsh.sh == bgm->dummysh) { - // This side is open, operate on it. - bsymtet = btetloop; - while (bgm->fnextself(bsymtet)); - bgm->tspivot(bsymtet, bneighsh); - bgm->findedge(&bneighsh, bgm->sdest(bhullsh), bgm->sorg(bhullsh)); - bgm->sbond(bhullsh, bneighsh); - } - bgm->enextself(btetloop); - bgm->senextself(bhullsh); - } - bhullsh.sh = bgm->shellfacetraverse(bgm->subfaces); - } - - delete [] tetptbaklist; - delete [] idx2bplist; -} - -// -// Begin of Delaunay refinement routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// marksharpsegments() Mark sharp segments. // -// // -// A segment s is called sharp if it is in one of the two cases: // -// (1) There is a segment s' intersecting with s. The internal angle (*) // -// between s and s' is acute. // -// (2) There are two facets f1 and f2 intersecting at s. The internal // -// dihedral angle (*) between f1 and f2 is acute. // -// This routine finds the sharp segments and marked them as type SHARP. // -// The minimum angle between segments (minfaceang) and the minimum dihedral // -// angle between facets (minfacetdihed) are calulcated. // -// // -// (*) The internal angle (or dihedral) bewteen two features means the angle // -// inside the mesh domain. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::marksharpsegments(REAL sharpangle) -{ - triface adjtet; - face startsh, spinsh, neighsh; - face segloop, prevseg, nextseg; - point eorg, edest; - REAL ang, smallang; - bool issharp; - int sharpsegcount; - - if (b->verbose > 0) { - printf(" Marking sharp segments.\n"); - } - - smallang = sharpangle * PI / 180.; - sharpsegcount = 0; - eorg = edest = (point) NULL; // To avoid compiler warnings. - - // A segment s may have been split into many subsegments. Operate the one - // which contains the origin of s. Then mark the rest of subsegments. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - segloop.shver = 0; - senext2(segloop, prevseg); - spivotself(prevseg); - if (prevseg.sh == dummysh) { - // Operate on this seg s. - assert(shelltype(segloop) != SHARP); // It should be unmarked. - issharp = false; - spivot(segloop, startsh); - if (startsh.sh != dummysh) { - // First check if two facets form an acute dihedral angle at s. - eorg = sorg(segloop); - edest = sdest(segloop); - spinsh = startsh; - do { - if (sorg(spinsh) != eorg) { - sesymself(spinsh); - } - // Only do test when the spinsh is faceing inward. - stpivot(spinsh, adjtet); - if (adjtet.tet != dummytet) { - // Get the subface on the adjacent facet. - spivot(spinsh, neighsh); - // Do not calculate if it is self-bonded. - if (neighsh.sh != spinsh.sh) { - // Calculate the dihedral angle between the two subfaces. - ang = facedihedral(eorg, edest, sapex(spinsh), sapex(neighsh)); - // Only do check if a sharp angle has not been found. - if (!issharp) issharp = (ang < smallang); - // Remember the smallest facet dihedral angle. - minfacetdihed = minfacetdihed < ang ? minfacetdihed : ang; - } - } - // Go to the next facet. - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - // if (!issharp) { - // Second check if s forms an acute angle with another seg. - spinsh = startsh; - do { - if (sorg(spinsh) != eorg) { - sesymself(spinsh); - } - // Calculate the angle between s and s' of this facet. - neighsh = spinsh; - // Rotate edges around 'eorg' until meeting another seg s'. Such - // seg (s') must exist since the facet is segment-bounded. - // The sum of the angles of faces at 'eorg' gives the internal - // angle between the two segments. - ang = 0.0; - do { - ang += interiorangle(eorg, sdest(neighsh), sapex(neighsh), NULL); - senext2self(neighsh); - sspivot(neighsh, nextseg); - if (nextseg.sh != dummysh) break; - // Go to the next coplanar subface. - spivotself(neighsh); - assert(neighsh.sh != dummysh); - if (sorg(neighsh) != eorg) { - sesymself(neighsh); - } - } while (true); - // Only do check if a sharp angle has not been found. - if (!issharp) issharp = (ang < smallang); - // Remember the smallest input face angle. - minfaceang = minfaceang < ang ? minfaceang : ang; - // Go to the next facet. - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - // } - } - if (issharp) { - setshelltype(segloop, SHARP); - // Set the type for all subsegments at forwards. - senext(segloop, nextseg); - spivotself(nextseg); - while (nextseg.sh != dummysh) { - nextseg.shver = 0; - setshelltype(nextseg, SHARP); - senextself(nextseg); - spivotself(nextseg); - } - sharpsegcount++; - } - } - segloop.sh = shellfacetraverse(subsegs); - } - - // So far we have marked all segments which have an acute dihedral angle - // or whose ORIGINs have an acute angle. In the un-marked subsegments, - // there are possible ones whose DESTINATIONs have an acute angle. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - // Only operate if s is non-sharp and contains the dest. - segloop.shver = 0; - senext(segloop, nextseg); - spivotself(nextseg); - // if ((nextseg.sh == dummysh) && (shelltype(segloop) != SHARP)) { - if (nextseg.sh == dummysh) { - // issharp = false; - issharp = (shelltype(segloop) == SHARP); - spivot(segloop, startsh); - if (startsh.sh != dummysh) { - // Check if s forms an acute angle with another seg. - eorg = sdest(segloop); - spinsh = startsh; - do { - if (sorg(spinsh) != eorg) { - sesymself(spinsh); - } - // Calculate the angle between s and s' of this facet. - neighsh = spinsh; - ang = 0.0; - do { - ang += interiorangle(eorg, sdest(neighsh), sapex(neighsh), NULL); - senext2self(neighsh); - sspivot(neighsh, nextseg); - if (nextseg.sh != dummysh) break; - // Go to the next coplanar subface. - spivotself(neighsh); - assert(neighsh.sh != dummysh); - if (sorg(neighsh) != eorg) { - sesymself(neighsh); - } - } while (true); - // Only do check if a sharp angle has not been found. - if (!issharp) issharp = (ang < smallang); - // Remember the smallest input face angle. - minfaceang = minfaceang < ang ? minfaceang : ang; - // Go to the next facet. - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - } - if (issharp) { - setshelltype(segloop, SHARP); - // Set the type for all subsegments at backwards. - senext2(segloop, prevseg); - spivotself(prevseg); - while (prevseg.sh != dummysh) { - prevseg.shver = 0; - setshelltype(prevseg, SHARP); - senext2self(prevseg); - spivotself(prevseg); - } - sharpsegcount++; - } - } - segloop.sh = shellfacetraverse(subsegs); - } - - if ((b->verbose > 0) && (sharpsegcount > 0)) { - printf(" %d sharp segments.\n", sharpsegcount); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// decidefeaturepointsizes() Decide the sizes for all feature points. // -// // -// A feature point is a point on a sharp segment. Every feature point p will // -// be assigned a positive size which is the radius of the protecting ball. // -// // -// The size of a feature point may be specified by one of the following ways:// -// (1) directly specifying on an input vertex (by using .mtr file); // -// (2) imposing a fixed maximal volume constraint ('-a__' option); // -// (3) imposing a maximal volume constraint in a region ('-a' option); // -// (4) imposing a maximal area constraint on a facet (in .var file); // -// (5) imposing a maximal length constraint on a segment (in .var file); // -// (6) combining (1) - (5). // -// (7) automatically deriving a size if none of (1) - (6) is available. // -// In case (7),the size of p is set to be the smallest edge length among all // -// edges connecting at p. The final size of p is the minimum of (1) - (7). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::decidefeaturepointsizes() -{ - list *tetlist, *verlist; - shellface **segsperverlist; - triface starttet; - face shloop; - face checkseg, prevseg, nextseg, testseg; - point ploop, adjpt, e1, e2; - REAL lfs_0, len, vol, maxlen, varlen; - bool isfeature; - int *idx2seglist; - int featurecount; - int idx, i, j; - - if (b->verbose > 0) { - printf(" Deciding feature-point sizes.\n"); - } - - // Constructing a map from vertices to segments. - makesegmentmap(idx2seglist, segsperverlist); - // Initialize working lists. - tetlist = new list(sizeof(triface), NULL, 256); - verlist = new list(sizeof(point *), NULL, 256); - - if (b->fixedvolume) { - // A fixed volume constraint is imposed. This gives an upper bound of - // the maximal radius of the protect ball of a vertex. - maxlen = pow(6.0 * b->maxvolume, 1.0/3.0); - } - - if (!b->refine) { - // Initially correct types for Steiner points. - featurecount = 0; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - if (pointtype(ploop) == NACUTEVERTEX) { - if (point2sh(ploop) != (shellface) NULL) { - setpointtype(ploop, FREESEGVERTEX); - featurecount++; - } - } - ploop = pointtraverse(); - } -#ifdef SELF_CHECK - if ((b->verbose > 0) && (featurecount > 0)) { - printf(" %d Steiner points correction.\n", featurecount); - } -#endif - } - - // First only assign a size of p if p is not a Steiner point. The size of - // a Steiner point will be interpolated later from the endpoints of the - // segment on which it lies. - featurecount = 0; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - if (pointtype(ploop) != FREESEGVERTEX) { - // Is p a feature point? - isfeature = false; - idx = pointmark(ploop) - in->firstnumber; - for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; i++) { - checkseg.sh = segsperverlist[i]; - isfeature = (shelltype(checkseg) == SHARP); - } - // Decide the size of p if it is on a sharp segment. - if (isfeature) { - // Find a tet containing p (checkseg is a sharp seg which contains p). - sstpivot(&checkseg, &starttet); - // Form star(p). - tetlist->append(&starttet); - formstarpolyhedron(ploop, tetlist, verlist, true); - // Decide the size for p if no input size is given on input. - if (ploop[pointmtrindex] == 0.0) { - // Calculate lfs_0(p). - lfs_0 = longest; - for (i = 0; i < verlist->len(); i++) { - adjpt = * (point *)(* verlist)[i]; - if (pointtype(adjpt) == FREESEGVERTEX) { - // A Steiner point q. Find the seg it lies on. - sdecode(point2sh(adjpt), checkseg); - assert(checkseg.sh != dummysh); - checkseg.shver = 0; - // Find the origin of this seg. - prevseg = checkseg; - do { - senext2(prevseg, testseg); - spivotself(testseg); - if (testseg.sh == dummysh) break; - prevseg = testseg; // Go to the previous subseg. - prevseg.shver = 0; - } while (true); - // Find the dest of this seg. - nextseg = checkseg; - do { - senext(nextseg, testseg); - spivotself(testseg); - if (testseg.sh == dummysh) break; - nextseg = testseg; // Go to the next subseg. - nextseg.shver = 0; - } while (true); - e1 = sorg(prevseg); - e2 = sdest(nextseg); - // Check if p is the origin or the dest of this seg. - if (ploop == e1) { - // Set q to be the dest of this seg. - adjpt = e2; - } else if (ploop == e2) { - // Set q to be the org of this seg. - adjpt = e1; - } - } - len = distance(ploop, adjpt); - if (lfs_0 > len) lfs_0 = len; - } - ploop[pointmtrindex] = lfs_0; - } - if (b->fixedvolume) { - // A fixed volume constraint is imposed. Adjust H(p) <= maxlen. - if (ploop[pointmtrindex] > maxlen) { - ploop[pointmtrindex] = maxlen; - } - } - if (b->varvolume) { - // Variant volume constraints are imposed. Adjust H(p) <= varlen. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - vol = volumebound(starttet.tet); - if (vol > 0.0) { - varlen = pow(6 * vol, 1.0/3.0); - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; - } - } - } - } - // Clear working lists. - tetlist->clear(); - verlist->clear(); - featurecount++; - } else { - // NO feature point, set the size of p be zero. - ploop[pointmtrindex] = 0.0; - } - } // if (pointtype(ploop) != FREESEGVERTEX) { - ploop = pointtraverse(); - } - - if (b->verbose > 0) { - printf(" %d feature points.\n", featurecount); - } - - if (!b->refine) { - // Second only assign sizes for all Steiner points. A Steiner point p - // inserted on a sharp segment s is assigned a size by interpolating - // the sizes of the original endpoints of s. - featurecount = 0; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - if (pointtype(ploop) == FREESEGVERTEX) { - if (ploop[pointmtrindex] == 0.0) { - sdecode(point2sh(ploop), checkseg); - assert(checkseg.sh != dummysh); - if (shelltype(checkseg) == SHARP) { - checkseg.shver = 0; - // Find the origin of this seg. - prevseg = checkseg; - do { - senext2(prevseg, testseg); - spivotself(testseg); - if (testseg.sh == dummysh) break; - prevseg = testseg; // Go the previous subseg. - prevseg.shver = 0; - } while (true); - // Find the dest of this seg. - nextseg = checkseg; - do { - senext(nextseg, testseg); - spivotself(testseg); - if (testseg.sh == dummysh) break; - nextseg = testseg; // Go the next subseg. - nextseg.shver = 0; - } while (true); - e1 = sorg(prevseg); - e2 = sdest(nextseg); - len = distance(e1, e2); - lfs_0 = distance(e1, ploop); - // The following assert() happens when -Y option is used. - if (b->nobisect == 0) { - assert(lfs_0 < len); - } - ploop[pointmtrindex] = e1[pointmtrindex] - + (lfs_0 / len) * (e2[pointmtrindex] - e1[pointmtrindex]); - featurecount++; - } else { - // NO feature point, set the size of p be zero. - ploop[pointmtrindex] = 0.0; - } // if (shelltype(checkseg) == SHARP) - } // if (ploop[pointmtrindex] == 0.0) - } // if (pointtype(ploop) != FREESEGVERTEX) - ploop = pointtraverse(); - } - if ((b->verbose > 0) && (featurecount > 0)) { - printf(" %d Steiner feature points.\n", featurecount); - } - } - - if (varconstraint) { - // A .var file exists. Adjust feature sizes. - if (in->facetconstraintlist) { - // Have facet area constrains. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - varlen = areabound(shloop); - if (varlen > 0.0) { - // Check if the three corners are feature points. - varlen = sqrt(varlen); - for (j = 0; j < 3; j++) { - ploop = (point) shloop.sh[3 + j]; - isfeature = false; - idx = pointmark(ploop) - in->firstnumber; - for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; - i++) { - checkseg.sh = segsperverlist[i]; - isfeature = (shelltype(checkseg) == SHARP); - } - if (isfeature) { - assert(ploop[pointmtrindex] > 0.0); - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; - } - } - } // for (j = 0; j < 3; j++) - } - shloop.sh = shellfacetraverse(subfaces); - } - } - if (in->segmentconstraintlist) { - // Have facet area constrains. - subsegs->traversalinit(); - shloop.sh = shellfacetraverse(subsegs); - while (shloop.sh != (shellface *) NULL) { - varlen = areabound(shloop); - if (varlen > 0.0) { - // Check if the two endpoints are feature points. - for (j = 0; j < 2; j++) { - ploop = (point) shloop.sh[3 + j]; - isfeature = false; - idx = pointmark(ploop) - in->firstnumber; - for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; - i++) { - checkseg.sh = segsperverlist[i]; - isfeature = (shelltype(checkseg) == SHARP); - } - if (isfeature) { - assert(ploop[pointmtrindex] > 0.0); - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; - } - } - } // for (j = 0; j < 2; j++) - } - shloop.sh = shellfacetraverse(subsegs); - } - } - } // if (varconstraint) - - delete [] segsperverlist; - delete [] idx2seglist; - delete tetlist; - delete verlist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueueencsub() Add an encroached subface into the queue. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::enqueueencsub(face* testsub, point encpt, int quenumber, - REAL* cent) -{ - badface *encsub; - int i; - - encsub = (badface *) badsubfaces->alloc(); - encsub->ss = *testsub; - encsub->forg = sorg(*testsub); - encsub->fdest = sdest(*testsub); - encsub->fapex = sapex(*testsub); - encsub->foppo = (point) encpt; - for (i = 0; i < 3; i++) encsub->cent[i] = cent[i]; - encsub->nextitem = (badface *) NULL; - // Set the pointer of 'encsubseg' into 'testsub'. It has two purposes: - // (1) We can regonize it is encroached; (2) It is uniquely queued. - setshell2badface(encsub->ss, encsub); - // Add the subface to the end of a queue (quenumber = 2, high priority). - *subquetail[quenumber] = encsub; - // Maintain a pointer to the NULL pointer at the end of the queue. - subquetail[quenumber] = &encsub->nextitem; - if (b->verbose > 2) { - printf(" Queuing subface (%d, %d, %d) [%d].\n", pointmark(encsub->forg), - pointmark(encsub->fdest), pointmark(encsub->fapex), quenumber); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// dequeueencsub() Remove an enc-subface from the front of the queue. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::badface* tetgenmesh::dequeueencsub(int* pquenumber) -{ - badface *result; - int quenumber; - - // Look for a nonempty queue. - for (quenumber = 2; quenumber >= 0; quenumber--) { - result = subquefront[quenumber]; - if (result != (badface *) NULL) { - // Remove the badface from the queue. - subquefront[quenumber] = result->nextitem; - // Maintain a pointer to the NULL pointer at the end of the queue. - if (subquefront[quenumber] == (badface *) NULL) { - subquetail[quenumber] = &subquefront[quenumber]; - } - *pquenumber = quenumber; - return result; - } - } - return (badface *) NULL; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueuebadtet() Add a tetrahedron into the queue. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::enqueuebadtet(triface* testtet, REAL ratio2, REAL* cent) -{ - badface *newbadtet; - int queuenumber; - int i; - - // Allocate space for the bad tetrahedron. - newbadtet = (badface *) badtetrahedrons->alloc(); - newbadtet->tt = *testtet; - newbadtet->key = ratio2; - if (cent != NULL) { - for (i = 0; i < 3; i++) newbadtet->cent[i] = cent[i]; - } else { - for (i = 0; i < 3; i++) newbadtet->cent[i] = 0.0; - } - newbadtet->forg = org(*testtet); - newbadtet->fdest = dest(*testtet); - newbadtet->fapex = apex(*testtet); - newbadtet->foppo = oppo(*testtet); - newbadtet->nextitem = (badface *) NULL; - // Determine the appropriate queue to put the bad tetrahedron into. - if (ratio2 > b->goodratio) { - // queuenumber = (int) ((ratio2 - b->goodratio) / 0.5); - queuenumber = (int) (64.0 - 64.0 / ratio2); - // 'queuenumber' may overflow (negative) caused by a very large ratio. - if ((queuenumber > 63) || (queuenumber < 0)) { - queuenumber = 63; - } - } else { - // It's not a bad ratio; put the tet in the lowest-priority queue. - queuenumber = 0; - } - - // Are we inserting into an empty queue? - if (tetquefront[queuenumber] == (badface *) NULL) { - // Yes. Will this become the highest-priority queue? - if (queuenumber > firstnonemptyq) { - // Yes, this is the highest-priority queue. - nextnonemptyq[queuenumber] = firstnonemptyq; - firstnonemptyq = queuenumber; - } else { - // No. Find the queue with next higher priority. - i = queuenumber + 1; - while (tetquefront[i] == (badface *) NULL) { - i++; - } - // Mark the newly nonempty queue as following a higher-priority queue. - nextnonemptyq[queuenumber] = nextnonemptyq[i]; - nextnonemptyq[i] = queuenumber; - } - // Put the bad tetrahedron at the beginning of the (empty) queue. - tetquefront[queuenumber] = newbadtet; - } else { - // Add the bad tetrahedron to the end of an already nonempty queue. - tetquetail[queuenumber]->nextitem = newbadtet; - } - // Maintain a pointer to the last tetrahedron of the queue. - tetquetail[queuenumber] = newbadtet; - - if (b->verbose > 2) { - printf(" Queueing bad tet: (%d, %d, %d, %d), ratio %g, qnum %d.\n", - pointmark(newbadtet->forg), pointmark(newbadtet->fdest), - pointmark(newbadtet->fapex), pointmark(newbadtet->foppo), - sqrt(ratio2), queuenumber); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// dequeuebadtet() Remove a tetrahedron from the front of the queue. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::badface* tetgenmesh::topbadtetra() -{ - // Keep a record of which queue was accessed in case dequeuebadtetra() - // is called later. - recentq = firstnonemptyq; - // If no queues are nonempty, return NULL. - if (firstnonemptyq < 0) { - return (badface *) NULL; - } else { - // Return the first tetrahedron of the highest-priority queue. - return tetquefront[firstnonemptyq]; - } -} - -void tetgenmesh::dequeuebadtet() -{ - badface *deadbadtet; - int i; - - // If queues were empty last time topbadtetra() was called, do nothing. - if (recentq >= 0) { - // Find the tetrahedron last returned by topbadtetra(). - deadbadtet = tetquefront[recentq]; - // Remove the tetrahedron from the queue. - tetquefront[recentq] = deadbadtet->nextitem; - // If this queue is now empty, update the list of nonempty queues. - if (deadbadtet == tetquetail[recentq]) { - // Was this the highest-priority queue? - if (firstnonemptyq == recentq) { - // Yes; find the queue with next lower priority. - firstnonemptyq = nextnonemptyq[firstnonemptyq]; - } else { - // No; find the queue with next higher priority. - i = recentq + 1; - while (tetquefront[i] == (badface *) NULL) { - i++; - } - nextnonemptyq[i] = nextnonemptyq[recentq]; - } - } - // Return the bad tetrahedron to the pool. - badfacedealloc(badtetrahedrons, deadbadtet); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checkseg4encroach() Check a subsegment to see if it is encroached. // -// // -// A segment s is encroached if there is a vertex lies inside or on its dia- // -// metral circumsphere, i.e., s faces an angle theta >= 90 degrees. // -// // -// If 'testpt' (p) != NULL, only test if 'testseg' (s) is encroached by it, // -// else, check all apexes of faces around s. Return TRUE if s is encroached. // -// If and 'enqflag' is TRUE, add it into 'badsubsegs' if s is encroached. // -// // -// If 'prefpt' != NULL, it returns the reference point (defined in my paper) // -// if it exists. This point is will be used to split s. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::checkseg4encroach(face* testseg, point testpt, point* prefpt, - bool enqflag) -{ - badface *encsubseg; - triface starttet, spintet; - point eorg, edest, eapex, encpt; - REAL cent[3], radius, dist, diff; - REAL maxradius; - bool enq; - int hitbdry; - - enq = false; - eorg = sorg(*testseg); - edest = sdest(*testseg); - cent[0] = 0.5 * (eorg[0] + edest[0]); - cent[1] = 0.5 * (eorg[1] + edest[1]); - cent[2] = 0.5 * (eorg[2] + edest[2]); - radius = distance(cent, eorg); - - if (varconstraint && (areabound(*testseg) > 0.0)) { - enq = (2.0 * radius) > areabound(*testseg); - } - - if (!enq) { - maxradius = 0.0; - if (testpt == (point) NULL) { - // Check if it is encroached by traversing all faces containing it. - sstpivot(testseg, &starttet); - eapex = apex(starttet); - spintet = starttet; - hitbdry = 0; - do { - dist = distance(cent, apex(spintet)); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - if (diff <= 0.0) { - // s is encroached. - enq = true; - if (prefpt != (point *) NULL) { - // Find the reference point. - encpt = apex(spintet); - circumsphere(eorg, edest, encpt, NULL, NULL, &dist); - if (dist > maxradius) { - // Rememebr this point. - *prefpt = encpt; - maxradius = dist; - } - } else { - break; - } - } - if (!fnextself(spintet)) { - hitbdry++; - if (hitbdry < 2) { - esym(starttet, spintet); - if (!fnextself(spintet)) { - hitbdry++; - } - } - } - } while (apex(spintet) != eapex && (hitbdry < 2)); - } else { - // Only check if 'testseg' is encroached by 'testpt'. - dist = distance(cent, testpt); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - enq = (diff <= 0.0); - } - } - - if (enq && enqflag) { - if (b->verbose > 2) { - printf(" Queuing encroaching subsegment (%d, %d).\n", - pointmark(eorg), pointmark(edest)); - } - encsubseg = (badface *) badsubsegs->alloc(); - encsubseg->ss = *testseg; - encsubseg->forg = eorg; - encsubseg->fdest = edest; - encsubseg->foppo = (point) NULL; // Not used. - // Set the pointer of 'encsubseg' into 'testseg'. It has two purposes: - // (1) We can regonize it is encroached; (2) It is uniquely queued. - setshell2badface(encsubseg->ss, encsubseg); - } - - return enq; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checksub4encroach() Check a subface to see if it is encroached. // -// // -// A subface f is encroached if there is a vertex inside or on its diametral // -// circumsphere. // -// // -// If 'testpt (p) != NULL', test if 'testsub' (f) is encroached by it, else, // -// test if f is encroached by one of the two opposites of the adjacent tets. // -// Return TRUE if f is encroached and queue it if 'enqflag' is set. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::checksub4encroach(face* testsub, point testpt, bool enqflag) -{ - triface abuttet; - point pa, pb, pc, encpt; - REAL A[4][4], rhs[4], D; - REAL cent[3], area; - REAL radius, dist, diff; - bool enq; - int indx[4]; - int quenumber; - - enq = false; - radius = 0.0; - encpt = (point) NULL; - - pa = sorg(*testsub); - pb = sdest(*testsub); - pc = sapex(*testsub); - - // Compute the coefficient matrix A (3x3). - A[0][0] = pb[0] - pa[0]; - A[0][1] = pb[1] - pa[1]; - A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb) - A[1][0] = pc[0] - pa[0]; - A[1][1] = pc[1] - pa[1]; - A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc) - cross(A[0], A[1], A[2]); // vector V3 (V1 X V2) - - if (varconstraint && (areabound(*testsub) > 0.0)) { - // Check if the subface has too big area. - area = 0.5 * sqrt(dot(A[2], A[2])); - enq = area > areabound(*testsub); - if (enq) { - quenumber = 2; // A queue of subfaces having too big area. - } - } - - // Compute the right hand side vector b (3x1). - rhs[0] = 0.5 * dot(A[0], A[0]); - rhs[1] = 0.5 * dot(A[1], A[1]); - rhs[2] = 0.0; - // Solve the 3 by 3 equations use LU decomposition with partial pivoting - // and backward and forward substitute.. - if (lu_decmp(A, 3, indx, &D, 0)) { - lu_solve(A, 3, indx, rhs, 0); - cent[0] = pa[0] + rhs[0]; - cent[1] = pa[1] + rhs[1]; - cent[2] = pa[2] + rhs[2]; - radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); - } - - if (!enq) { - // Check if the subface is encroached. - if (testpt == (point) NULL) { - stpivot(*testsub, abuttet); - if (abuttet.tet != dummytet) { - dist = distance(cent, oppo(abuttet)); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - enq = (diff <= 0.0); - if (enq) encpt = oppo(abuttet); - } - if (!enq) { - sesymself(*testsub); - stpivot(*testsub, abuttet); - if (abuttet.tet != dummytet) { - dist = distance(cent, oppo(abuttet)); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - enq = (diff <= 0.0); - if (enq) encpt = oppo(abuttet); - } - } - } else { - dist = distance(cent, testpt); - diff = dist - radius; - if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding. - enq = (diff <= 0.0); - } - if (enq) { - quenumber = 0; // A queue of encroached subfaces. - } - } - - if (enq && enqflag) { - enqueueencsub(testsub, encpt, quenumber, cent); - } - - return enq; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checktet4badqual() Test a tetrahedron for quality measures. // -// // -// Tests a tetrahedron to see if it satisfies the minimum ratio condition // -// and the maximum volume condition. Tetrahedra that aren't upto spec are // -// added to the bad tetrahedron queue. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::checktet4badqual(triface* testtet, bool enqflag) -{ - point pa, pb, pc, pd, pe1, pe2; - REAL vda[3], vdb[3], vdc[3]; - REAL vab[3], vbc[3], vca[3]; - REAL N[4][3], A[4][4], rhs[4], D; - REAL elen[6], circumcent[3]; - REAL bicent[3], offcent[3]; - REAL volume, L, cosd; - REAL radius2, smlen2, ratio2; - REAL dist, sdist, split; - bool enq; - int indx[4]; - int sidx, i, j; - - pa = (point) testtet->tet[4]; - pb = (point) testtet->tet[5]; - pc = (point) testtet->tet[6]; - pd = (point) testtet->tet[7]; - - // Get the edge vectors vda: d->a, vdb: d->b, vdc: d->c. - // Set the matrix A = [vda, vdb, vdc]^T. - for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; - for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i]; - for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i]; - // Get the rest edge vectors - for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i]; - for (i = 0; i < 3; i++) vbc[i] = pc[i] - pb[i]; - for (i = 0; i < 3; i++) vca[i] = pa[i] - pc[i]; - - // Lu-decompose the matrix A. - lu_decmp(A, 3, indx, &D, 0); - // Get the volume of abcd. - volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; - if (volume < 0.0) volume = -volume; - // Check the radiu-edge ratio of the tet. - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - // Get the circumcenter. - for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i]; - // Get the square of the circumradius. - radius2 = dot(rhs, rhs); - // Find the square of the shortest edge length. - elen[0] = dot(vda, vda); - elen[1] = dot(vdb, vdb); - elen[2] = dot(vdc, vdc); - elen[3] = dot(vab, vab); - elen[4] = dot(vbc, vbc); - elen[5] = dot(vca, vca); - smlen2 = elen[0]; sidx = 0; - for (i = 1; i < 6; i++) { - if (smlen2 > elen[i]) { smlen2 = elen[i]; sidx = i; } - } - // Calculate the square of radius-edge ratio. - ratio2 = radius2 / smlen2; - // Check whether the ratio is smaller than permitted. - enq = ratio2 > b->goodratio; - if (!enq) { - // abcd has good ratio. - // ratio2 = 0.0; - // if (b->offcenter) { - // Test if it is a sliver. - // Compute the 4 face normals (N[0], ..., N[3]). - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; - // Normalized the normals. - for (i = 0; i < 4; i++) { - L = sqrt(dot(N[i], N[i])); - if (L > 0.0) { - for (j = 0; j < 3; j++) N[i][j] /= L; - } - } - // N[0] is the normal of face bcd. Test the dihedral angles at edge - // cd, bd, and bc to see if they are too small or too big. - for (i = 1; i < 4 && !enq; i++) { - cosd = -dot(N[0], N[i]); // Edge cd, bd, bc. - enq = cosd > cosmindihed; - } - if (!enq) { - for (i = 2; i < 4 && !enq; i++) { - cosd = -dot(N[1], N[i]); // Edge ad, ac - enq = cosd > cosmindihed; - } - if (!enq) { - cosd = -dot(N[2], N[3]); // Edge ab - enq = cosd > cosmindihed; - } - } - // } - } else if (b->offcenter) { - // abcd has bad-quality. Use off-center instead of circumcenter. - switch (sidx) { - case 0: // edge da. - pe1 = pd; pe2 = pa; break; - case 1: // edge db. - pe1 = pd; pe2 = pb; break; - case 2: // edge dc. - pe1 = pd; pe2 = pc; break; - case 3: // edge ab. - pe1 = pa; pe2 = pb; break; - case 4: // edge bc. - pe1 = pb; pe2 = pc; break; - case 5: // edge ca. - pe1 = pc; pe2 = pa; break; - default: - pe1 = pe2 = (point) NULL; // Avoid a compile warning. - } - // The shortest edge is e1->e2. - for (i = 0; i < 3; i++) bicent[i] = 0.5 * (pe1[i] + pe2[i]); - dist = distance(bicent, circumcent); - // sdist = sqrt(smlen2) * sin(PI / 3.0); // A icoso-triangle. - // The following formulae is from - sdist = b->alpha3 * (b->minratio+sqrt(b->goodratio-0.25))* sqrt(smlen2); - split = sdist / dist; - if (split > 1.0) split = 1.0; - // Get the off-center. - for (i = 0; i < 3; i++) { - offcent[i] = bicent[i] + split * (circumcent[i] - bicent[i]); - } - } - - if (!enq && (b->varvolume || b->fixedvolume)) { - // Check if the tet has too big volume. - enq = b->fixedvolume && (volume > b->maxvolume); - if (!enq && b->varvolume) { - enq = (volume > volumebound(testtet->tet)) && - (volumebound(testtet->tet) > 0.0); - } - } - - if (!enq) { - // Check if the user-defined sizing function is satisfied. - if (b->metric) { - // assert(b->alpha1 > 0.0); - sdist = sqrt(radius2) / b->alpha1; - for (i = 0; i < 4; i++) { - pa = (point) testtet->tet[4 + i]; - // Get the indicated size of p. - dist = pa[pointmtrindex]; // dist = b->alpha1 * pa[pointmtrindex]; - enq = ((dist < sdist) && (dist > 0.0)); - if (enq) break; // It is bad wrt. a node constraint. - // *** Experiment ! Stop test if c is inside H(a). - // if ((dist > 0.0) && (dist > sdist)) break; - } - // *** Experiment ! - // enq = (i == 4); // Does c lies outside all sparse-ball? - } // if (b->metric) - } - - if (enq && enqflag) { - if (b->offcenter && (ratio2 > b->goodratio)) { - for (i = 0; i < 3; i++) circumcent[i] = offcent[i]; - } - enqueuebadtet(testtet, ratio2, circumcent); - } - - return enq; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// acceptsegpt() Check if a segment point can be inserted or not. // -// // -// Segment(ab) is indicated to be split by a point p (\in ab). This routine // -// decides whether p can be inserted or not. // -// // -// p can not be inserted either the '-Y' option is used and ab is a hull // -// segment or '-YY' option is used. // -// // -// p can be inserted if it is in one of the following cases: // -// (1) if L = |a - b| is too long wrt the edge constraint; or // -// (2) if |x - p| > \alpha_2 H(x) for x = a, b; or // -// (3) if 'refpt' != NULL. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::acceptsegpt(point segpt, point refpt, face* splitseg) -{ - point p[2]; - REAL L, lfs; - int i, j; - - if (b->nobisect == 1) { - // '-Y'. It can not be split if it is on the hull. - triface spintet; - point pc; - sstpivot(splitseg, &spintet); - assert(spintet.tet != dummytet); - pc = apex(spintet); - do { - if (!fnextself(spintet)) { - // Meet a boundary face - s is on the hull. - return false; - } - } while (pc != apex(spintet)); - } else if (b->nobisect > 1) { - // '-YY'. Do not split it. - return false; - } - - p[0] = sorg(*splitseg); - p[1] = sdest(*splitseg); - if (varconstraint && (areabound(*splitseg) > 0)) { - lfs = areabound(*splitseg); - L = distance(p[0], p[1]); - if (L > lfs) { - return true; // case (1) - } - } - - j = 0; // Use j to count the number of inside balls. - for (i = 0; i < 2; i++) { - // Check if p is inside the protect ball of q. - if (p[i][pointmtrindex] > 0.0) { - lfs = b->alpha2 * p[i][pointmtrindex]; - L = distance(p[i], segpt); - if (L < lfs) j++; // p is inside ball. - } - } - if (j == 0) return true; // case (3). - - // If 'refpt' != NULL, force p to be inserted. - if (refpt != (point) NULL) { - cdtenforcesegpts++; - return true; - } - - // Do not split it. - rejsegpts++; - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// acceptfacpt() Check if a facet point can be inserted or not. // -// // -// 'subceillist' is CBC(p). 'verlist' (V) is empty on input, it returns the // -// set of vertices of CBC(p). // -// // -// p can not be inserted either the '-Y' option is used and the facet is on // -// the hull or '-YY' option is used. // -// // -// p can be inserted if |p - v| > \alpha_2 H(v), for all v \in V. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::acceptfacpt(point facpt, list* subceillist, list* verlist) -{ - face *testsh; - point p[2], ploop; - REAL L, lfs; - int idx, i, j; - - if (b->nobisect == 1) { - // '-Y'. p can not be inserted if CBC(p) is on the hull. - triface testtet; - testsh = (face *)(* subceillist)[0]; - stpivot(*testsh, testtet); - if (testtet.tet != dummytet) { - sesymself(*testsh); - stpivot(*testsh, testtet); - } - if (testtet.tet == dummytet) return false; - } else if (b->nobisect > 1) { - // '-YY'. Do not split s. - return false; - } - - // Collect the vertices of CBC(p), save them in V. - for (i = 0; i < subceillist->len(); i++) { - testsh = (face *)(* subceillist)[i]; - p[0] = sorg(*testsh); - p[1] = sdest(*testsh); - for (j = 0; j < 2; j++) { - idx = pointmark(p[j]); - if (idx >= 0) { - setpointmark(p[j], -idx - 1); - verlist->append(&(p[j])); - } - } - } - - j = 0; // Use j to count the number of inside balls. - for (i = 0; i < verlist->len(); i++) { - ploop = * (point *)(* verlist)[i]; - // Uninfect q. - idx = pointmark(ploop); - setpointmark(ploop, -(idx + 1)); - // Check if p is inside the protect ball of q. - if (ploop[pointmtrindex] > 0.0) { - lfs = b->alpha2 * ploop[pointmtrindex]; - L = distance(ploop, facpt); - if (L < lfs) j++; // p is inside ball. - } - } - verlist->clear(); - - if (j == 0) return true; // case (3). - - rejsubpts++; - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// acceptvolpt() Check if a volume point can be inserted or not. // -// // -// 'ceillist' is B(p). 'verlist' (V) is empty on input, it returns the set // -// of vertices of B(p). // -// // -// p can be split if |p - v| > \alpha_2 H(v), for all v \in V. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::acceptvolpt(point volpt, list* ceillist, list* verlist) -{ - triface* testtet; - point p[3], ploop; - REAL L, lfs; - int idx, i, j; - - // Collect the vertices of CBC(p), save them in V. - for (i = 0; i < ceillist->len(); i++) { - testtet = (triface *)(* ceillist)[i]; - p[0] = org(*testtet); - p[1] = dest(*testtet); - p[2] = apex(*testtet); - for (j = 0; j < 3; j++) { - idx = pointmark(p[j]); - if (idx >= 0) { - setpointmark(p[j], -idx - 1); - verlist->append(&(p[j])); - } - } - } - - j = 0; // Use j to counte the number of inside balls. - for (i = 0; i < verlist->len(); i++) { - ploop = * (point *)(* verlist)[i]; - // Uninfect q. - idx = pointmark(ploop); - setpointmark(ploop, -(idx + 1)); - // Check if p is inside the protect ball of q. - if (ploop[pointmtrindex] > 0.0) { - lfs = b->alpha2 * ploop[pointmtrindex]; - L = distance(ploop, volpt); - if (L < lfs) j++; // p is inside the protect ball. - } - } - verlist->clear(); - - if (j == 0) return true; // case (2). - - rejtetpts++; - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// getsplitpoint() Get the inserting point in a segment. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getsplitpoint(point e1, point e2, point refpt, point newpt) -{ - point ei, ej; - REAL split, L, d1, d2, ps, rs; - bool acutea, acuteb; - int i; - - if (refpt != (point) NULL) { - // Use the CDT rules to split the segment. - acutea = (pointtype(e1) == ACUTEVERTEX); - acuteb = (pointtype(e2) == ACUTEVERTEX); - if (acutea ^ acuteb) { - // Only one endpoint is acute. Use rule-2 or rule-3. - ei = acutea ? e1 : e2; - ej = acutea ? e2 : e1; - L = distance(ei, ej); - // Apply rule-2. - d1 = distance(ei, refpt); - split = d1 / L; - for (i = 0; i < 3; i++) newpt[i] = ei[i] + split * (ej[i] - ei[i]); - // Check if rule-3 is needed. - d2 = distance(refpt, newpt); - if (d2 > (L - d1)) { - // Apply rule-3. - if ((d1 - d2) > (0.5 * d1)) { - split = (d1 - d2) / L; - } else { - split = 0.5 * d1 / L; - } - for (i = 0; i < 3; i++) newpt[i] = ei[i] + split * (ej[i] - ei[i]); - if (b->verbose > 1) { - printf(" Found by rule-3:"); - } - r3count++; - } else { - if (b->verbose > 1) { - printf(" Found by rule-2:"); - } - r2count++; - } - if (b->verbose > 1) { - printf(" center %d, split = %.12g.\n", pointmark(ei), split); - } - // Add a random perturbation on newpt. - d1 = distance(ei, newpt); - d2 = distance(newpt, refpt); - ps = randgenerator(d2 * b->epsilon2 * 1e+2); - rs = ps / d1; - // Perturb newpt away from ei. - for (i = 0; i < 3; i++) newpt[i] = ei[i] + (1.0+rs) * (newpt[i] - ei[i]); - } else { - // Both endpoints are acute or not. Split it at the middle. - for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]); - // Add a random perturbation on newpt. - d1 = 0.5 * distance(e1, e2); - ps = randgenerator(d1 * b->epsilon2 * 1e+2); - rs = ps / d1; - for (i = 0; i < 3; i++) newpt[i] = e1[i] + (1.0+rs) * (newpt[i] - e1[i]); - } - } else { - // Split the segment at its midpoint. - for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]); - // Add a random perturbation on newpt. - d1 = 0.5 * distance(e1, e2); - ps = randgenerator(d1 * b->epsilon2 * 1e+2); - rs = ps / d1; - for (i = 0; i < 3; i++) newpt[i] = e1[i] + (1.0+rs) * (newpt[i] - e1[i]); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// shepardinterpolation() Interpolate the local size of a newpoint. // -// // -// The classical Shepard interoplation (inversed weighted distance) is used. // -// (With the choice p = 2). // -// // -// 'verlist' contains a list vertices neighboring to 'newpt'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::shepardinterpolate(point newpt, list *verlist) -{ - point neipt; - REAL *weights, sumweight; - REAL vec[3]; - int i, j; - - weights = new REAL[verlist->len()]; - sumweight = 0.0; - - // Calculate the weight of each point. - for (i = 0; i < verlist->len(); i++) { - neipt = * (point *)(* verlist)[i]; - for (j = 0; j < 3; j++) vec[j] = neipt[j] - newpt[j]; - weights[i] = 1.0 / dot(vec, vec); - sumweight += weights[i]; - } - // Interpolate. - newpt[pointmtrindex] = 0.0; - for (i = 0; i < verlist->len(); i++) { - neipt = * (point *)(* verlist)[i]; - newpt[pointmtrindex] += (weights[i] * neipt[pointmtrindex]) / sumweight; - } - - delete [] weights; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// setnewpointsize() Set the size for a new point. // -// // -// The size of the new point p is interpolated either from a background mesh // -// (b->bgmesh) or from the two input endpoints. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::setnewpointsize(point newpt, point e1, point e2) -{ - if (b->metric) { - // Interpolate the point size in a background mesh. - triface bgmtet; - // Get a tet in background mesh for locating p. - decode(point2bgmtet(e1), bgmtet); - p1interpolatebgm(newpt, &bgmtet, NULL); - } else { - if (e2 != (point) NULL) { - // Interpolate the size between the two endpoints. - REAL split, l, d; - l = distance(e1, e2); - d = distance(e1, newpt); - split = d / l; -#ifdef SELF_CHECK - // Check if e1 and e2 are endpoints of a sharp segment. - assert(e1[pointmtrindex] > 0.0); - assert(e2[pointmtrindex] > 0.0); -#endif - newpt[pointmtrindex] = (1.0 - split) * e1[pointmtrindex] - + split * e2[pointmtrindex]; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// splitencseg() Split an enc-seg and recover the Delaunayness by flips. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist, - list* sublist, list* verlist, queue* flipque, bool chkencsub, bool chkbadtet, - bool optflag) -{ - list *mytetlist; - queue *myflipque; - triface starttet; - face startsh, spinsh, checksh; - int i; - - if (optflag) { - mytetlist = new list(sizeof(triface), NULL, 1024); - myflipque = new queue(sizeof(badface)); - tetlist = mytetlist; - flipque = myflipque; - } - - // Use the base orientation (important in this routine). - splitseg->shver = 0; - // Insert p, this should always success. - sstpivot(splitseg, &starttet); - splittetedge(newpt, &starttet, flipque); - // Remove locally non-Delaunay faces by flipping. - flip(flipque, NULL); // lawson(NULL, flipque); - - if (!optflag) { - // Check the two new subsegs to see if they're encroached (not by p). - for (i = 0; i < 2; i++) { - if (!shell2badface(*splitseg)) { - checkseg4encroach(splitseg, NULL, NULL, true); - } - if (i == 1) break; // Two new segs have been checked. - senextself(*splitseg); - spivotself(*splitseg); -#ifdef SELF_CHECK - assert(splitseg->sh != (shellface *) NULL); -#endif - splitseg->shver = 0; - } - // Check the new subfaces to see if they're encroached (not by p). - if (chkencsub) { - spivot(*splitseg, startsh); - spinsh = startsh; - do { - sublist->append(&spinsh); - formstarpolygon(newpt, sublist, verlist); - for (i = 0; i < sublist->len(); i++) { - checksh = * (face *)(* sublist)[i]; - if (!shell2badface(checksh)) { - checksub4encroach(&checksh, NULL, true); - } - } - sublist->clear(); - if (verlist) verlist->clear(); - spivotself(spinsh); - } while (spinsh.sh != startsh.sh); - } - } // if (!optflag) - - // Collect the new tets connecting at p. - sstpivot(splitseg, &starttet); - tetlist->append(&starttet); - formstarpolyhedron(newpt, tetlist, verlist, true); - - if (!optflag) { - // Check if p encroaches adjacent segments. - tallencsegs(newpt, 1, &tetlist); - if (chkencsub) { - // Check if p encroaches adjacent subfaces. - tallencsubs(newpt, 1, &tetlist); - } - if (chkbadtet) { - // Check if there are new bad quality tets at p. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - checktet4badqual(&starttet, true); - } - } - tetlist->clear(); - } else { - // Check if new tets are non-optimal. - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - checktet4opt(&starttet, true); - } - delete mytetlist; - delete myflipque; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tallencsegs() Check for encroached segments and save them in list. // -// // -// If 'testpt' (p) != NULL, only check if segments are encroached by p, else,// -// check all the nearby mesh vertices. // -// // -// If 'ceillists' (B_i(p)) != NULL, there are 'n' B_i(p)s, only check the // -// segments which are on B_i(p)s, else, check the entire list of segments // -// (in the pool 'this->subsegs'). // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::tallencsegs(point testpt, int n, list **ceillists) -{ - list *ceillist; - triface ceiltet; - face checkseg; - long oldencnum; - int i, j, k; - - // Remember the current number of encroached segments. - oldencnum = badsubsegs->items; - - if (ceillists != (list **) NULL) { - for (k = 0; k < n; k++) { - ceillist = ceillists[k]; - // Check the segments on B_i(p). - for (i = 0; i < ceillist->len(); i++) { - ceiltet = * (triface *)(* ceillist)[i]; - ceiltet.ver = 0; - for (j = 0; j < 3; j++) { - tsspivot(&ceiltet, &checkseg); - if (checkseg.sh != dummysh) { - // Found a segment. Test it if it isn't in enc-list. - if (!shell2badface(checkseg)) { - checkseg4encroach(&checkseg, testpt, NULL, true); - } - } - enextself(ceiltet); - } - } - } - } else { - // Check the entire list of segments. - subsegs->traversalinit(); - checkseg.sh = shellfacetraverse(subsegs); - while (checkseg.sh != (shellface *) NULL) { - // Test it if it isn't in enc-list. - if (!shell2badface(checkseg)) { - checkseg4encroach(&checkseg, testpt, NULL, true); - } - checkseg.sh = shellfacetraverse(subsegs); - } - } - - return (badsubsegs->items > oldencnum); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tallencsubs() Find all encroached subfaces and save them in list. // -// // -// If 'testpt' (p) != NULL, only check if subfaces are encroached by p, else,// -// check the adjacent vertices of subfaces. // -// // -// If 'ceillists' (B_i(p)) != NULL, there are 'n' B_i(p)s, only check the // -// subfaces which are on B_i(p)s, else, check the entire list of subfaces // -// (in the pool 'this->subfaces'). // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::tallencsubs(point testpt, int n, list** ceillists) -{ - list *ceillist; - triface ceiltet; - face checksh; - long oldencnum; - int i, k; - - // Remember the current number of encroached segments. - oldencnum = badsubfaces->items; - - if (ceillists != (list **) NULL) { - for (k = 0; k < n; k++) { - ceillist = ceillists[k]; - // Check the subfaces on B_i(p). - for (i = 0; i < ceillist->len(); i++) { - ceiltet = * (triface *)(* ceillist)[i]; - tspivot(ceiltet, checksh); - if (checksh.sh != dummysh) { - // Found a subface. Test it if it isn't in enc-list. - if (!shell2badface(checksh)) { - checksub4encroach(&checksh, testpt, true); - } - } - } - } - } else { - // Check the entire list of subfaces. - subfaces->traversalinit(); - checksh.sh = shellfacetraverse(subfaces); - while (checksh.sh != (shellface *) NULL) { - // Test it if it isn't in enc-list. - if (!shell2badface(checksh)) { - checksub4encroach(&checksh, testpt, true); - } - checksh.sh = shellfacetraverse(subfaces); - } - } - - return (badsubfaces->items > oldencnum); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tallbadtetrahedrons() Queue all the bad-quality tetrahedra in the mesh.// -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tallbadtetrahedrons() -{ - triface tetloop; - - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - checktet4badqual(&tetloop, true); - tetloop.tet = tetrahedrontraverse(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// repairencsegs() Repair (split) all the encroached segments. // -// // -// Each encroached segment is repaired by splitting it - inserting a vertex // -// at or near its midpoint. Newly inserted vertices may encroach upon other // -// subsegments, these are also repaired. // -// // -// 'chkencsub' and 'chkbadtet' are two flags that specify whether one should // -// take note of new encroaced subfaces and bad quality tets that result from // -// inserting vertices to repair encroached subsegments. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::repairencsegs(bool chkencsub, bool chkbadtet) -{ - list **tetlists, **ceillists; - list **sublists, **subceillists; - list *tetlist, *sublist; - queue *flipque; - badface *encloop; - face splitseg, symsplitseg; - point newpt, sympt, refpt; - point e1, e2; - enum locateresult symloc; - int nmax, n, i, j; - - n = 0; - nmax = 128; - if (!b->fliprepair) { - tetlists = new list*[nmax]; - ceillists = new list*[nmax]; - sublists = new list*[nmax]; - subceillists = new list*[nmax]; - } else { - tetlist = new list(sizeof(triface), NULL, 1024); - sublist = new list(sizeof(face), NULL, 256); - flipque = new queue(sizeof(badface)); - } - - // Loop until the pool 'badsubsegs' is empty. Note that steinerleft == -1 - // if an unlimited number of Steiner points is allowed. - while ((badsubsegs->items > 0) && (steinerleft != 0)) { - badsubsegs->traversalinit(); - encloop = badfacetraverse(badsubsegs); - while ((encloop != (badface *) NULL) && (steinerleft != 0)) { - // Get an encroached subsegment s. - splitseg = encloop->ss; - // Clear the in-queue flag in s. - setshell2badface(splitseg, NULL); - if ((sorg(splitseg) == encloop->forg) && - (sdest(splitseg) == encloop->fdest)) { - if (b->verbose > 1) { - printf(" Get an enc-seg (%d, %d)\n", pointmark(encloop->forg), - pointmark(encloop->fdest)); - } - refpt = (point) NULL; - if (b->conformdel) { - // Look for a reference point. - checkseg4encroach(&splitseg, NULL, &refpt, false); - } - // Create the new point p (at the middle of s). - makepoint(&newpt); - getsplitpoint(encloop->forg, encloop->fdest, refpt, newpt); - setpointtype(newpt, FREESEGVERTEX); - setpoint2sh(newpt, sencode(splitseg)); - // Decide whether p can be inserted or not. - if (acceptsegpt(newpt, refpt, &splitseg)) { - // Is there periodic boundary condition? - if (checkpbcs) { - // Insert points on other segments of incident pbcgroups. - i = shellmark(splitseg) - 1; - for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) { - makepoint(&sympt); - symloc = getsegpbcsympoint(newpt, &splitseg, sympt, &symsplitseg, - segpglist[j]); - if (symloc == ONEDGE) { - if (symsplitseg.sh != splitseg.sh) { - // Insert sympt. - setpointtype(sympt, FREESEGVERTEX); - setpoint2sh(sympt, sencode(symsplitseg)); - // Save the endpoints of the seg for size interpolation. - e1 = sorg(symsplitseg); - if (shelltype(symsplitseg) == SHARP) { - e2 = sdest(symsplitseg); - } else { - e2 = (point) NULL; // No need to do size interpolation. - } - if (!b->fliprepair) { - // Form BC(symp), B(symp), CBC(symp)s, C(symp)s. - formbowatcavity(sympt, &symsplitseg, NULL, &n, &nmax, - sublists, subceillists, tetlists, ceillists); - // Validate BC(symp), B(symp), CBC(symp)s, C(symp)s. - if (trimbowatcavity(sympt, &symsplitseg, n, sublists, - subceillists, tetlists, ceillists, -1.0)) { - bowatinsertsite(sympt, &symsplitseg, n, sublists, - subceillists, tetlists, ceillists, NULL, flipque, - true, chkencsub, chkbadtet); - setnewpointsize(sympt, e1, e2); - if (steinerleft > 0) steinerleft--; - } else { - // p did not insert for invalid BC(symp). - pointdealloc(sympt); - } - // Free the memory allocated in formbowatcavity(). - releasebowatcavity(&symsplitseg, n, sublists, subceillists, - tetlists, ceillists); - } else { - splitencseg(sympt, &symsplitseg, tetlist, sublist, NULL, - flipque, chkencsub, chkbadtet, false); - setnewpointsize(sympt, e1, e2); - if (steinerleft > 0) steinerleft--; - } - } else { - // The sympt are on the same segment. It is possible when - // splitseg is the symmetric rotating axes. - pointdealloc(sympt); - } - } else if (symloc == ONVERTEX) { - // The sympt already exists. It is possible when two pbc - // groups are exactly the same. Omit this point. - pointdealloc(sympt); - } else { - // Do not isnert symp for unknown cases: ONFACE, OUTSIDE. - // assert(0); - pointdealloc(sympt); - } - } // for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) - } // if (checkpbcs) - // Save the endpoints of the seg for size interpolation. - e1 = sorg(splitseg); - if (shelltype(splitseg) == SHARP) { - e2 = sdest(splitseg); - } else { - e2 = (point) NULL; // No need to do size interoplation. - } - if (!b->fliprepair) { - // Form BC(p), B(p), CBC(p)s, and C(p)s. - formbowatcavity(newpt, &splitseg, NULL, &n, &nmax, sublists, - subceillists, tetlists, ceillists); - // Validate/update BC(p), B(p), CBC(p)s, and C(p)s. - if (trimbowatcavity(newpt, &splitseg, n, sublists, subceillists, - tetlists, ceillists, -1.0)) { - bowatinsertsite(newpt, &splitseg, n, sublists, subceillists, - tetlists, ceillists, NULL, flipque, true, - chkencsub, chkbadtet); - setnewpointsize(newpt, e1, e2); - if (steinerleft > 0) steinerleft--; - } else { - // p did not insert for invalid B(p). - pointdealloc(newpt); - } - // Free the memory allocated in formbowatcavity(). - releasebowatcavity(&splitseg, n, sublists, subceillists, tetlists, - ceillists); - } else { - splitencseg(newpt, &splitseg, tetlist, sublist, NULL, flipque, - chkencsub, chkbadtet, false); - setnewpointsize(newpt, e1, e2); - if (steinerleft > 0) steinerleft--; - } - } else { - // p did not accept for insertion. - pointdealloc(newpt); - } // if (checkseg4splitting(newpt, &splitseg)) - } // if ((encloop->forg == pa) && (encloop->fdest == pb)) - badfacedealloc(badsubsegs, encloop); // Remove this entry from list. - encloop = badfacetraverse(badsubsegs); // Get the next enc-segment. - } // while ((encloop != (badface *) NULL) && (steinerleft != 0)) - } // while ((badsubsegs->items > 0) && (steinerleft != 0)) - - if (!b->fliprepair) { - delete [] tetlists; - delete [] ceillists; - delete [] sublists; - delete [] subceillists; - } else { - delete tetlist; - delete sublist; - delete flipque; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// repairencsubs() Repair (split) all the encroached subfaces. // -// // -// Each encroached subface is repaired by splitting it - inserting a vertex // -// at or near its circumcenter. Newly inserted vertices may encroach upon // -// other subfaces, these are also repaired. // -// // -// 'chkbadtet' is a flag that specifies whether one should take note of new // -// bad quality tets that result from inserted vertices. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::repairencsubs(bool chkbadtet) -{ - list *tetlists[2], *ceillists[2]; - list *sublist, *subceillist; - list *verlist; - badface *encloop; - face splitsub, symsplitsub; - point newpt, sympt, e1; - enum locateresult loc, symloc; - bool reject; - long oldptnum; - int quenumber, n, i; - - n = 0; - sublist = (list *) NULL; - subceillist = (list *) NULL; - verlist = new list(sizeof(point *), NULL, 256); - - // Loop until the pool 'badsubfaces' is empty. Note that steinerleft == -1 - // if an unlimited number of Steiner points is allowed. - while ((badsubfaces->items > 0) && (steinerleft != 0)) { - // Get an encroached subface f. - encloop = dequeueencsub(&quenumber); - splitsub = encloop->ss; - // Clear the in-queue flag of f. - setshell2badface(splitsub, NULL); - // f may not be the same one when it was determined to be encroached. - if (!isdead(&splitsub) - && (sorg(splitsub) == encloop->forg) - && (sdest(splitsub) == encloop->fdest) - && (sapex(splitsub) == encloop->fapex)) { - if (b->verbose > 1) { - printf(" Dequeuing ensub (%d, %d, %d) [%d].\n", - pointmark(encloop->forg), pointmark(encloop->fdest), - pointmark(encloop->fapex), quenumber); - } - // Create a new point p at the circumcenter of f. - makepoint(&newpt); - for (i = 0; i < 3; i++) newpt[i] = encloop->cent[i]; - setpointtype(newpt, FREESUBVERTEX); - setpoint2sh(newpt, sencode(splitsub)); - // Set the abovepoint of f for point location. - abovepoint = facetabovepointarray[shellmark(splitsub)]; - if (abovepoint == (point) NULL) { - getfacetabovepoint(&splitsub); - } - // Locate p, start from f, stop at segment (1), use a tolerance to - // detect ONVERTEX or OUTSIDE case. Update f on return. - loc = locatesub(newpt, &splitsub, 1, b->epsilon * 1e+2); - if ((loc != ONVERTEX) && (loc != OUTSIDE)) { - // Form BC(p), B(p), CBC(p) and C(p). - formbowatcavity(newpt, NULL, &splitsub, &n, NULL, &sublist, - &subceillist, tetlists, ceillists); - // Check for encroached subsegments (on B(p)). - reject = tallencsegs(newpt, 2, ceillists); - // Execute point accept rule if p does not encroach upon any segment. - if (!reject) { - reject = !acceptfacpt(newpt, subceillist, verlist); - } - if (!reject) { - // Validate/update cavity. - reject = !trimbowatcavity(newpt, NULL, n, &sublist, &subceillist, - tetlists, ceillists, -1.0); - } - if (!reject) { - // CBC(p) should include s, so that s can be removed after CBC(p) - // is remeshed. However, if there are locally non-Delaunay faces - // and encroached subsegments, s may not be collected in CBC(p). - // p should not be inserted in such case. - reject = !sinfected(encloop->ss); - } - if (!reject) { - if (checkpbcs) { - if (shellpbcgroup(splitsub) >= 0) { - // Check for splitting of the symmetric subface of f. - makepoint(&sympt); - symloc = getsubpbcsympoint(newpt,&splitsub,sympt,&symsplitsub); - if (symloc != ONVERTEX) { - // Release CBC(p) and BC(p) and free the memory.. - releasebowatcavity(NULL, 2, &sublist, &subceillist, tetlists, - ceillists); - // Form CBC(symp), C(symp), BC(sympt) and B(sympt). - formbowatcavity(sympt, NULL, &symsplitsub, &n, NULL, &sublist, - &subceillist, tetlists, ceillists); - reject = tallencsegs(sympt, 2, ceillists); - if (!reject) { - reject = !acceptfacpt(sympt, subceillist, verlist); - } - if (!reject) { - reject = !trimbowatcavity(sympt,NULL,n,&sublist,&subceillist, - tetlists, ceillists, -1.0); - } - if (!reject) { - // Insert sympt. - setpoint2pbcpt(newpt, sympt); - setpoint2pbcpt(sympt, newpt); - setpointtype(sympt, FREESUBVERTEX); - setpoint2sh(sympt, sencode(symsplitsub)); - // Save a point for size interpolation. - e1 = sorg(symsplitsub); - bowatinsertsite(sympt, NULL, n, &sublist, &subceillist, - tetlists,ceillists,NULL,NULL,false,true,chkbadtet); - setnewpointsize(sympt, e1, NULL); - if (steinerleft > 0) steinerleft--; - // Release CBC(symp) and BC(symp) and free the memory.. - releasebowatcavity(NULL, n, &sublist, &subceillist, tetlists, - ceillists); - } else { - // symp is rejected for one of the following reasons: - // (1) BC(symp) is not valid; or - // (2) symp encroaches upon some subsegments (queued); or - // (3) symp is rejected by point accepting rule. - pointdealloc(sympt); - // Cavity will be released by the following code. - } - } else { - // Do not insert sympt for invalid PBC data. - pointdealloc(sympt); - // p is rejected due to symp. - reject = true; - } - } - } // if (checkpbcs) - } - if (!reject) { - // Insert p. - if (checkpbcs) { - if (shellpbcgroup(splitsub) >= 0) { - // Form CBC(p), C(p), BC(p) and B(p). - formbowatcavity(newpt, NULL, &splitsub, &n, NULL, &sublist, - &subceillist, tetlists, ceillists); - trimbowatcavity(newpt, NULL, n, &sublist, &subceillist, tetlists, - ceillists, -1.0); - } - } - // Save a point for size interpolation. - e1 = sorg(splitsub); - bowatinsertsite(newpt, NULL, n, &sublist, &subceillist, tetlists, - ceillists, NULL, NULL, true, true, chkbadtet); - setnewpointsize(newpt, e1, NULL); - if (steinerleft > 0) steinerleft--; - } else { - // p is rejected for the one of the following reasons: - // (1) BC(p) is not valid. - // (2) s does not in CBC(p). - // (3) p encroaches upon some segments (queued); or - // (4) p is rejected by point accepting rule, or - // (5) due to the rejection of symp (the PBC). - pointdealloc(newpt); - } // if (!reject) - // Release the cavity and free the memory. - releasebowatcavity(NULL,n,&sublist,&subceillist,tetlists,ceillists); - if (reject) { - // Are there queued encroached subsegments. - if (badsubsegs->items > 0) { - // Repair enc-subsegments. - oldptnum = points->items; - repairencsegs(true, chkbadtet); - if (points->items > oldptnum) { - // Some enc-subsegments got split. Try to repair f later. - splitsub = encloop->ss; - if (!isdead(&splitsub)) { - if (!shell2badface(splitsub)) { - checksub4encroach(&splitsub, NULL, true); - } - } - } - } - } - } else { - // Don't insert p for one of the following reasons: - // (1) Locate on an existing vertex; or - // (2) locate outside the domain. - // Case (1) should not be possible. If such vertex v exists, it is - // the circumcenter of f, ie., f is non-Delaunay. Either f was got - // split before by v, but survived after v was inserted, or the - // same for a f' which is nearly co-circular with f. Whatsoever, - // there are encroached segs by v, but the routine tallencsegs() - // did not find them out. - if (loc == ONVERTEX) { - printf("Internal error in repairencsubs():\n"); - printf(" During repairing encroached subface (%d, %d, %d)\n", - pointmark(encloop->forg), pointmark(encloop->fdest), - pointmark(encloop->fapex)); - printf(" New point %d is coincident with an existing vertex %d\n", - pointmark(newpt), pointmark(sorg(splitsub))); - internalerror(); - } - // Case (2) can happen when thers is a segment s which is close to f - // and is non-conforming Delaunay. The circumcenter of f encroaches - // upon s, but the circumcenter of s is rejected for insertion. - pointdealloc(newpt); - } // if ((loc != ONVERTEX) && (loc != OUTSIDE)) - } else { - if (!isdead(&splitsub)) { - // The subface has been changed, re-check it. - checksub4encroach(&splitsub, NULL, true); - } - } // if (!isdead(&splitsub) && (sorg(splitsub) == encloop->forg) && - // Remove this entry from list. - badfacedealloc(badsubfaces, encloop); - } // while ((badsubfaces->items > 0) && (steinerleft != 0)) - - delete verlist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// repairbadtets() Repair all bad-quality tetrahedra. // -// // -// All bad-quality tets are stored in pool 'badtetrahedrons'. Each bad tet // -// is repaired by inserting a point at or near its circumcenter. However, if // -// this point encroaches any subsegment or subface, it is not inserted. Ins- // -// tead the encroached segment and subface are split. Newly inserted points // -// may create other bad-quality tets, these are also repaired. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::repairbadtets() -{ - list *tetlist, *ceillist; - list *verlist; - badface *badtet; - triface starttet; - point newpt, e1; - enum locateresult loc; - bool reject; - long oldptnum; - int i; - - tetlist = new list(sizeof(triface), NULL, 1024); - ceillist = new list(sizeof(triface), NULL, 1024); - verlist = new list(sizeof(point *), NULL, 256); - - // Loop until pool 'badtetrahedrons' is empty. Note that steinerleft == -1 - // if an unlimited number of Steiner points is allowed. - while ((badtetrahedrons->items > 0) && (steinerleft != 0)) { - // Get a bad-quality tet t. - badtet = topbadtetra(); - // Make sure that the tet is still the same one when it was tested. - // Subsequent transformations may have made it a different tet. - if ((badtet != (badface *) NULL) && !isdead(&badtet->tt) - && org(badtet->tt) == badtet->forg - && dest(badtet->tt) == badtet->fdest - && apex(badtet->tt) == badtet->fapex - && oppo(badtet->tt) == badtet->foppo) { - if (b->verbose > 1) { - printf(" Dequeuing btet (%d, %d, %d, %d).\n", - pointmark(badtet->forg), pointmark(badtet->fdest), - pointmark(badtet->fapex), pointmark(badtet->foppo)); - } - // Create the new point p (at the circumcenter of t). - makepoint(&newpt); - for (i = 0; i < 3; i++) newpt[i] = badtet->cent[i]; - setpointtype(newpt, FREEVOLVERTEX); - // Locate p. - starttet = badtet->tt; - loc = preciselocate(newpt, &starttet, tetrahedrons->items); - if ((loc != ONVERTEX) && (loc != OUTSIDE)) { - // For BC(p) and B(p). - infect(starttet); - tetlist->append(&starttet); - formbowatcavityquad(newpt, tetlist, ceillist); - // Check for encroached subsegments. - reject = tallencsegs(newpt, 1, &ceillist); - if (!reject) { - // Check for encroached subfaces. - reject = tallencsubs(newpt, 1, &ceillist); - } - // Execute point accepting rule if p does not encroach upon any - // subsegment and subface. - if (!reject) { - reject = !acceptvolpt(newpt, ceillist, verlist); - } - if (!reject) { - reject = !trimbowatcavity(newpt, NULL, 1, NULL, NULL, &tetlist, - &ceillist, -1.0); - } - if (!reject) { - // BC(p) should include t, so that t can be removed after BC(p) is - // remeshed. However, if there are locally non-Delaunay faces - // and encroached subsegments/subfaces, t may not be collected - // in BC(p). p should not be inserted in such case. - reject = !infected(badtet->tt); - if (reject) outbowatcircumcount++; - } - if (!reject) { - // Save a point for size interpolation. - e1 = org(starttet); - // Insert p. - bowatinsertsite(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, - NULL, NULL, false, false, true); - setnewpointsize(newpt, e1, NULL); - if (steinerleft > 0) steinerleft--; - } else { - // p is rejected for one of the following reasons: - // (1) BC(p) is not valid. - // (2) t does not in BC(p). - // (3) p encroaches upon some segments; - // (4) p encroaches upon some subfaces; - // (5) p is rejected by the point accepting rule. - pointdealloc(newpt); - // Uninfect tets of BC(p). - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - uninfect(starttet); - } - } - tetlist->clear(); - ceillist->clear(); - // Split encroached subsegments/subfaces if there are. - if (reject) { - oldptnum = points->items; - if (badsubsegs->items > 0) { - repairencsegs(true, true); - } - if (badsubfaces->items > 0) { - repairencsubs(true); - } - if (points->items > oldptnum) { - // Some encroaching subsegments/subfaces got split. Re-queue the - // tet if it is still alive. - starttet = badtet->tt; - if (!isdead(&starttet)) { - checktet4badqual(&starttet, true); - } - } - } - } else { - // Do not insert p. The reason may be one of: - // (1) p is coincident (ONVERTEX) with an existing vertex; or - // (2) p is outside (OUTSIDE) the mesh. - // Case (1) should not be possible. If such vertex v exists, it is - // the circumcenter of t, ie., t is non-Delaunay. Either t was got - // split before by v, but survived after v was inserted, or the - // same for a t' which is nearly co-spherical with t. Whatsoever, - // there are encroached segments or subfaces by v but the routines - // tallencsegs() or tallencsubs() did not find them out. - if (loc == ONVERTEX) { - printf("Internal error in repairbadtets():\n"); - printf(" During repairing bad tet (%d, %d, %d, %d)\n", - pointmark(badtet->forg), pointmark(badtet->fdest), - pointmark(badtet->fapex), pointmark(badtet->foppo)); - printf(" New point %d is coincident with an existing vertex %d\n", - pointmark(newpt), pointmark(org(starttet))); - internalerror(); - } - // Case (2) can happen when there is a segment s (or subface f) which - // is close to f and is non-conforming Delaunay. The circumcenter - // of t encroaches upon s (or f), but the circumcenter of s (or f) - // is rejected for insertion. - pointdealloc(newpt); - } // if ((loc != ONVERTEX) && (loc != OUTSIDE)) - } // if (!isdead(&badtet->tt) && org(badtet->tt) == badtet->forg && - // Remove the tet from the queue. - dequeuebadtet(); - } // while ((badtetrahedrons->items > 0) && (steinerleft != 0)) - - delete tetlist; - delete ceillist; - delete verlist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// enforcequality() Refine the mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::enforcequality() -{ - long total, vertcount; - int i; - - if (!b->quiet) { - printf("Adding Steiner points to enforce quality.\n"); - } - - total = vertcount = 0l; - if (b->conformdel) { - r2count = r3count = 0l; - } - - // If both '-D' and '-r' options are used. - if (b->conformdel && b->refine) { - markacutevertices(65.0); - } - // If '-m' is not used. - if (!b->metric) { - // Find and mark all sharp segments. - marksharpsegments(65.0); - // Decide the sizes for feature points. - decidefeaturepointsizes(); - } - - // Initialize the pool of encroached subsegments. - badsubsegs = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); - // Looking for encroached subsegments. - tallencsegs(NULL, 0, NULL); - if (b->verbose && badsubsegs->items > 0) { - printf(" Splitting encroached subsegments.\n"); - } - vertcount = points->items; - // Fix encroached segments without noting any enc subfaces. - repairencsegs(false, false); - if (b->verbose > 0) { - printf(" %ld split points.\n", points->items - vertcount); - } - total += points->items - vertcount; - - // Initialize the pool of encroached subfaces. - badsubfaces = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0); - // Initialize the priority queues of badfaces. - for (i = 0; i < 3; i++) subquefront[i] = (badface *) NULL; - for (i = 0; i < 3; i++) subquetail[i] = &subquefront[i]; - // Looking for encroached subfaces. - tallencsubs(NULL, 0, NULL); - if (b->verbose && badsubfaces->items > 0) { - printf(" Splitting encroached subfaces.\n"); - } - vertcount = points->items; - // Fix encroached subfaces without noting bad tetrahedra. - repairencsubs(false); - if (b->verbose > 0) { - printf(" %ld split points.\n", points->items - vertcount); - } - total += points->items - vertcount; - // At this point, the mesh should be conforming Delaunay if no input - // angle is smaller than 90 degree. - - // Next, fix bad quality tetrahedra. - if ((b->minratio > 0.0) || b->varvolume || b->fixedvolume) { - // Initialize the pool of bad tets - badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0); - // Initialize the priority queues of bad tets. - for (i = 0; i < 64; i++) tetquefront[i] = (badface *) NULL; - firstnonemptyq = -1; - recentq = -1; - // Looking for bad quality tets. - cosmaxdihed = cos(b->maxdihedral * PI / 180.0); - cosmindihed = cos(b->mindihedral * PI / 180.0); - tallbadtetrahedrons(); - if (b->verbose && badtetrahedrons->items > 0) { - printf(" Splitting bad tetrahedra.\n"); - } - vertcount = points->items; - repairbadtets(); - if (b->verbose > 0) { - printf(" %ld refinement points.\n", points->items - vertcount); - } - total += points->items - vertcount; - delete badtetrahedrons; - } - - if (b->verbose > 0) { - printf(" Totally added %ld points.\n", total); - } - - delete badsubfaces; - delete badsubsegs; -} - -// -// End of Delaunay refinement routines -// - -// -// Begin of mesh optimization routines -// - -void tetgenmesh::dumpbadtets() -{ - FILE *fout; - badface *remtet; - - // Write out a file of remaining bad tets. - printf(" Writing bad tets to file bad-dump.lua.\n"); - fout = fopen("bad-dump.lua", "w"); - fprintf(fout, "-- %ld remaining bad tets (> %g degree).\n", - badtetrahedrons->items, b->maxdihedral); - badtetrahedrons->traversalinit(); - remtet = badfacetraverse(badtetrahedrons); - while (remtet != (badface *) NULL) { - if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg && - dest(remtet->tt) == remtet->fdest && - apex(remtet->tt) == remtet->fapex && - oppo(remtet->tt) == remtet->foppo) { - fprintf(fout, "p:draw_tet(%d, %d, %d, %d) -- %g\n", - pointmark(remtet->forg), pointmark(remtet->fdest), - pointmark(remtet->fapex), pointmark(remtet->foppo), - acos(remtet->key) * 180.0 / PI); - } - remtet = badfacetraverse(badtetrahedrons); - } - fclose(fout); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checktet4ill() Check a tet to see if it is illegal. // -// // -// A tet is "illegal" if it spans on one input facet. Save the tet in queue // -// if it is illegal and the flag 'enqflag' is set. // -// // -// Note: Such case can happen when the input facet has non-coplanar vertices // -// and the Delaunay tetrahedralization of the vertices may creat such tets. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::checktet4ill(triface* testtet, bool enqflag) -{ - badface *newbadtet; - triface checktet; - face checksh1, checksh2; - face checkseg; - bool illflag; - int i; - - illflag = false; - for (testtet->loc = 0; testtet->loc < 4; testtet->loc++) { - tspivot(*testtet, checksh1); - if (checksh1.sh != dummysh) { - testtet->ver = 0; - findedge(&checksh1, org(*testtet), dest(*testtet)); - for (i = 0; i < 3; i++) { - fnext(*testtet, checktet); - tspivot(checktet, checksh2); - if (checksh2.sh != dummysh) { - // Two subfaces share this edge. - sspivot(checksh1, checkseg); - if (checkseg.sh == dummysh) { - // The four corners of the tet are on one facet. Illegal! Try to - // flip the opposite edge of the current one. - enextfnextself(*testtet); - enextself(*testtet); - illflag = true; - break; - } - } - enextself(*testtet); - senextself(checksh1); - } - } - if (illflag) break; - } - - if (illflag && enqflag) { - // Allocate space for the bad tetrahedron. - newbadtet = (badface *) badtetrahedrons->alloc(); - newbadtet->tt = *testtet; - newbadtet->key = -1.0; // = 180 degree. - for (i = 0; i < 3; i++) newbadtet->cent[i] = 0.0; - newbadtet->forg = org(*testtet); - newbadtet->fdest = dest(*testtet); - newbadtet->fapex = apex(*testtet); - newbadtet->foppo = oppo(*testtet); - newbadtet->nextitem = (badface *) NULL; - if (b->verbose > 2) { - printf(" Queueing illtet: (%d, %d, %d, %d).\n", - pointmark(newbadtet->forg), pointmark(newbadtet->fdest), - pointmark(newbadtet->fapex), pointmark(newbadtet->foppo)); - } - } - - return illflag; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checktet4opt() Check a tet to see if it needs to be optimized. // -// // -// A tet t needs to be optimized if it fails to certain quality measures. // -// The only quality measure currently used is the maximal dihedral angle at // -// edges. The desired maximal dihedral angle is b->maxdihed (set by the '-s' // -// option. // -// // -// A tet may have one, two, or three big dihedral angles. Examples: Let the // -// tet t = abcd, and its four corners are nearly co-planar. Then t has one // -// big dihedral angle if d is very close to the edge ab; t has three big // -// dihedral angles if d's projection on the face abc is also inside abc, i.e.// -// the shape of t likes a hat; finally, t has two big dihedral angles if d's // -// projection onto abc is outside abc. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::checktet4opt(triface* testtet, bool enqflag) -{ - badface *newbadtet; - point pa, pb, pc, pd; - REAL N[4][3], len; - REAL cosd; - bool enq; - int i, j; - - enq = false; - pa = (point) testtet->tet[4]; - pb = (point) testtet->tet[5]; - pc = (point) testtet->tet[6]; - pd = (point) testtet->tet[7]; - // Compute the 4 face normals: N[0] cbd, N[1] acd, N[2] bad, N[3] abc. - tetallnormal(pa, pb, pc, pd, N, NULL); - // Normalize the normals. - for (i = 0; i < 4; i++) { - len = sqrt(dot(N[i], N[i])); - if (len != 0.0) { - for (j = 0; j < 3; j++) N[i][j] /= len; - } - } - // Find all large dihedral angles. - for (i = 0; i < 6; i++) { - // Locate the edge i and calculate the dihedral angle at the edge. - testtet->loc = 0; - testtet->ver = 0; - switch (i) { - case 0: // edge ab - cosd = -dot(N[2], N[3]); - break; - case 1: // edge cd - enextfnextself(*testtet); - enextself(*testtet); - cosd = -dot(N[0], N[1]); - break; - case 2: // edge bd - enextfnextself(*testtet); - enext2self(*testtet); - cosd = -dot(N[0], N[2]); - break; - case 3: // edge bc - enextself(*testtet); - cosd = -dot(N[0], N[3]); - break; - case 4: // edge ad - enext2fnextself(*testtet); - enextself(*testtet); - cosd = -dot(N[1], N[2]); - break; - case 5: // edge ac - enext2self(*testtet); - cosd = -dot(N[1], N[3]); - break; - } - if (cosd < cosmaxdihed) { - // A bigger dihedral angle. - if (enqflag) { - // Allocate space for the bad tetrahedron. - newbadtet = (badface *) badtetrahedrons->alloc(); - newbadtet->tt = *testtet; - newbadtet->key = cosd; - for (j = 0; j < 3; j++) newbadtet->cent[j] = 0.0; - newbadtet->forg = org(*testtet); - newbadtet->fdest = dest(*testtet); - newbadtet->fapex = apex(*testtet); - newbadtet->foppo = oppo(*testtet); - newbadtet->nextitem = (badface *) NULL; - if (b->verbose > 2) { - printf(" Queueing tet: (%d, %d, %d, %d), dihed %g (degree).\n", - pointmark(newbadtet->forg), pointmark(newbadtet->fdest), - pointmark(newbadtet->fapex), pointmark(newbadtet->foppo), - acos(cosd) * 180.0 / PI); - } - } - enq = true; - } - } - - return enq; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedge() Remove an edge // -// // -// 'remedge' is a tet (abcd) having the edge ab wanted to be removed. Local // -// reconnecting operations are used to remove edge ab. The following opera- // -// tion will be tryed. // -// // -// If ab is on the hull, and abc and abd are both hull faces. Then ab can be // -// removed by stripping abcd from the mesh. However, if ab is a segemnt, do // -// the operation only if 'b->optlevel' > 1 and 'b->nobisect == 0'. // -// // -// If ab is an internal edge, there are n tets contains it. Then ab can be // -// removed if there exists another m tets which can replace the n tets with- // -// out changing the boundary of the n tets. // -// // -// If 'optflag' is set. The value 'remedge->key' means cos(theta), where // -// 'theta' is the maximal dishedral angle at ab. In this case, even if the // -// n-to-m flip exists, it will not be performed if the maximum dihedral of // -// the new tets is larger than 'theta'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::removeedge(badface* remedge, bool optflag) -{ - triface abcd, badc; // Tet configuration at edge ab. - triface baccasing, abdcasing; - triface abtetlist[11]; // Old configuration at ab, save maximum 10 tets. - triface bftetlist[11]; // Old configuration at bf, save maximum 10 tets. - triface newtetlist[33]; // New configuration after removing ab. - face checksh; - enum fliptype fty; - REAL key; - bool remflag, subflag; - int n, n1, m, i, j; - - // First try to strip abcd from the mesh. This needs to check either ab - // or cd is on the hull. Try to strip it whichever is true. - abcd = remedge->tt; - adjustedgering(abcd, CCW); - i = 0; - do { - sym(abcd, baccasing); - // Is the tet on the hull? - if (baccasing.tet == dummytet) { - fnext(abcd, badc); - sym(badc, abdcasing); - if (abdcasing.tet == dummytet) { - // Strip the tet from the mesh -> ab is removed as well. - if (removetetbypeeloff(&abcd)) { - if (b->verbose > 1) { - printf(" Stripped tet from the mesh.\n"); - } - optcount[0]++; - return true; - } - } - } - // Check if the oppsite edge cd is on the hull. - enext2fnextself(abcd); - enext2self(abcd); - esymself(abcd); // --> cdab - i++; - } while (i < 2); - - // Get the tets configuration at ab. Collect maximum 10 tets. - subflag = false; - abcd = remedge->tt; - adjustedgering(abcd, CW); - n = 0; - abtetlist[n] = abcd; - do { - // Is the list full? - if (n == 10) break; - // Stop if a subface appears. - tspivot(abtetlist[n], checksh); - if (checksh.sh != dummysh) { - // ab is either a segment or a facet edge. The latter case is not - // handled yet! An edge flip is needed. - subflag = true; break; // return false; - } - // Get the next tet at ab. - fnext(abtetlist[n], abtetlist[n + 1]); - n++; - } while (apex(abtetlist[n]) != apex(abcd)); - - remflag = false; - key = remedge->key; - - if (subflag && optflag) { - abcd = remedge->tt; - adjustedgering(abcd, CCW); - // Try to flip face cda or cdb to improve quality. - for (j = 0; j < 2; j++) { - if (j == 0) { - enext2fnext(abcd, abtetlist[0]); // Goto cda. - } else { - enextfnext(abcd, abtetlist[0]); // Goto cdb. - } - fty = categorizeface(abtetlist[0]); - if (fty == T23) { - // A 2-to-3 flip is possible. - sym(abtetlist[0], abtetlist[1]); - assert(abtetlist[1].tet != dummytet); - n = 2; - m = 3; - remflag = removefacebyflip23(&key, abtetlist, newtetlist, NULL); - } else if (fty == T22) { - // A 2-to-2 or 4-to-4 flip is possible. - n = 2; - newtetlist[0] = abtetlist[0]; - adjustedgering(newtetlist[0], CW); - fnext(newtetlist[0], newtetlist[1]); - assert(newtetlist[1].tet != dummytet); - // May it is 4-to-4 flip. - if (fnext(newtetlist[1], newtetlist[2])) { - fnext(newtetlist[2], newtetlist[3]); - assert(newtetlist[3].tet != dummytet); - n = 4; - } - m = n; - remflag = removeedgebyflip22(&key, n, newtetlist, NULL); - } - // Has quality been improved? - if (remflag) { - if (b->verbose > 1) { - printf(" Done flip %d-to-%d. Qual: %g -> %g.\n", n, m, - acos(remedge->key) / PI * 180.0, acos(key) / PI * 180.0); - } - // Delete the old tets. Note, flip22() does not create new tets. - if (m == 3) { - for (i = 0; i < n; i++) { - tetrahedrondealloc(abtetlist[i].tet); - } - } - for (i = 0; i < m; i++) { - checktet4opt(&(newtetlist[i]), true); - } - optcount[1]++; - return true; - } - } // if (j = 0; j < 2; j++) - // Faces are not flipable. Return. - return false; - } - - // 2 <= n <= 10. - if (n == 3) { - // There are three tets at ab. Try to do a flip32 at ab. - remflag = removeedgebyflip32(&key, abtetlist, newtetlist, NULL); - } else if ((n == 4) || (n == 5) || (n == 6)) { - // Four tets case. Try to do edge transformation. - remflag = removeedgebytranNM(&key,n,abtetlist,newtetlist,NULL,NULL,NULL); - } else { - if (b->verbose > 1) { - printf(" !! Unhandled case: n = %d.\n", n); - } - } - if (remflag) { - optcount[n]++; - // Delete the old tets. - for (i = 0; i < n; i++) { - tetrahedrondealloc(abtetlist[i].tet); - } - m = (n - 2) * 2; // The numebr of new tets. - if (b->verbose > 1) { - printf(" Done flip %d-to-%d. ", n, m); - if (optflag) { - printf("Qual: %g -> %g.", acos(remedge->key) / PI * 180.0, - acos(key) / PI * 180.0); - } - printf("\n"); - } - } - - if (!remflag && (key == remedge->key) && (n < 7)) { - // Try to do a combination of flips. - n1 = 0; - remflag = removeedgebycombNM(&key, n, abtetlist, &n1, bftetlist, - newtetlist, NULL); - if (remflag) { - optcount[9]++; - // Delete the old tets. - for (i = 0; i < n; i++) { - tetrahedrondealloc(abtetlist[i].tet); - } - for (i = 0; i < n1; i++) { - if (!isdead(&(bftetlist[i]))) { - tetrahedrondealloc(bftetlist[i].tet); - } - } - m = ((n1 - 2) * 2 - 1) + (n - 3) * 2; // The number of new tets. - if (b->verbose > 1) { - printf(" Done flip %d-to-%d (n-1=%d, n1=%d). ", n+n1-2, m, n-1,n1); - if (optflag) { - printf("Qual: %g -> %g.", acos(remedge->key) / PI * 180.0, - acos(key) / PI * 180.0); - } - printf("\n"); - } - } - } - - if (remflag) { - // edge is removed. Test new tets for further optimization. - for (i = 0; i < m; i++) { - if (optflag) { - checktet4opt(&(newtetlist[i]), true); - } else { - checktet4ill(&(newtetlist[i]), true); - } - } - } - - return remflag; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// smoothsliver() Remove a sliver by smoothing a vertex of it. // -// // -// The 'slivtet' represents a sliver abcd, and ab is the current edge which // -// has a large dihedral angle (close to 180 degree). // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::smoothsliver(badface* remedge, list *starlist) -{ - triface checktet; - point smthpt; - bool smthed; - int idx, i, j; - - // Find a Steiner volume point and smooth it. - smthed = false; - for (i = 0; i < 4 && !smthed; i++) { - smthpt = (point) remedge->tt.tet[4 + i]; - // Is it a volume point? - if (pointtype(smthpt) == FREEVOLVERTEX) { - // Is it a Steiner point? - idx = pointmark(smthpt) - in->firstnumber; - if (!(idx < in->numberofpoints)) { - // Smooth a Steiner volume point. - starlist->append(&(remedge->tt.tet)); - formstarpolyhedron(smthpt, starlist, NULL, false); - smthed = smoothpoint(smthpt,NULL,NULL,starlist,false,&remedge->key); - // If it is smoothed. Queue new bad tets. - if (smthed) { - for (j = 0; j < starlist->len(); j++) { - checktet = * (triface *)(* starlist)[j]; - checktet4opt(&checktet, true); - } - } - starlist->clear(); - } - } - } - - /* Omit to smooth segment points. This may cause infinite loop. - if (smthed) { - return true; - } - face abseg, nextseg, prevseg; - point pt[2]; - // Check if ab is a segment. - tsspivot(slivtet, &abseg); - if (abseg.sh == dummysh) { - // ab is not a segment. Check if a or b is a Steiner segment point. - for (i = 0; i < 2 && !smthed; i++) { - smthpt = (i == 0 ? org(*slivtet) : dest(*slivtet)); - if (pointtype(smthpt) == FREESEGVERTEX) { - // Is it a Steiner point? - idx = pointmark(smthpt) - in->firstnumber; - if (!(idx < in->numberofpoints)) { - // Smooth a Steiner segment point. Get the segment. - sdecode(point2sh(smthpt), nextseg); - locateseg(smthpt, &nextseg); - assert(sorg(nextseg) == smthpt); - pt[0] = sdest(nextseg); - senext2(nextseg, prevseg); - spivotself(prevseg); - prevseg.shver = 0; - if (sorg(prevseg) == smthpt) sesymself(prevseg); - assert(sdest(prevseg) == smthpt); - pt[1] = sorg(prevseg); - starlist->append(slivtet); - formstarpolyhedron(smthpt, starlist, NULL, true); - smthed = smoothpoint(smthpt, pt[0], pt[1], starlist, false); - // If it is smoothed. Check if the tet is still a sliver. - if (smthed) checktet4opt(slivtet, true); - starlist->clear(); - } - } - } - } - */ - - return smthed; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// splitsliver() Remove a sliver by inserting a point. // -// // -// The 'remedge->tt' represents a sliver abcd, ab is the current edge which // -// has a large dihedral angle (close to 180 degree). // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::splitsliver(badface *remedge, list *tetlist, list *ceillist) -{ - triface starttet; - face checkseg; - point newpt, pt[4]; - bool remflag; - int i; - - starttet = remedge->tt; - - // Check if cd is a segment. - adjustedgering(starttet, CCW); - enextfnextself(starttet); - enextself(starttet); - tsspivot(&starttet, &checkseg); - if (b->nobisect == 0) { - if (checkseg.sh != dummysh) { - // cd is a segment. The seg will be split. BUT do not flip! Due to the - // exact predicates, lot of slivers ay be rsulted and hard to remove. - checkseg.shver = 0; - pt[0] = sorg(checkseg); - pt[1] = sdest(checkseg); - makepoint(&newpt); - getsplitpoint(pt[0], pt[1], NULL, newpt); - setpointtype(newpt, FREESEGVERTEX); - setpoint2sh(newpt, sencode(checkseg)); - // Insert p, this should always success. - sstpivot(&checkseg, &starttet); - splittetedge(newpt, &starttet, NULL); - // Collect the new tets connecting at p. - sstpivot(&checkseg, &starttet); - ceillist->append(&starttet); - formstarpolyhedron(newpt, ceillist, NULL, true); - setnewpointsize(newpt, pt[0], NULL); - if (steinerleft > 0) steinerleft--; - // Smooth p. - smoothpoint(newpt, pt[0], pt[1], ceillist, false, NULL); - // Queue new slivers. - for (i = 0; i < ceillist->len(); i++) { - starttet = * (triface *)(* ceillist)[i]; - checktet4opt(&starttet, true); - } - ceillist->clear(); - return true; - } - } - - // Get the four corners. - for (i = 0; i < 4; i++) { - pt[i] = (point) starttet.tet[4 + i]; - } - // Create the new point p (at the circumcenter of t). - makepoint(&newpt); - for (i = 0; i < 3; i++) { - newpt[i] = 0.25 * (pt[0][i] + pt[1][i] + pt[2][i] + pt[3][i]); - } - setpointtype(newpt, FREEVOLVERTEX); - - // Form the Bowyer-Watson cavity of p. - remflag = false; - infect(starttet); - tetlist->append(&starttet); - formbowatcavityquad(newpt, tetlist, ceillist); - if (trimbowatcavity(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, -1.0)) { - // Smooth p. - if (smoothpoint( newpt, NULL, NULL, ceillist, false, &remedge->key)) { - // Insert p. - bowatinsertsite(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, NULL, - NULL, false, false, false); - setnewpointsize(newpt, pt[0], NULL); - if (steinerleft > 0) steinerleft--; - // Queue new slivers. - for (i = 0; i < ceillist->len(); i++) { - starttet = * (triface *)(* ceillist)[i]; - checktet4opt(&starttet, true); - } - remflag = true; - } // if (smoothpoint) - } // if (trimbowatcavity) - - if (!remflag) { - // p is rejected for BC(p) is not valid. - pointdealloc(newpt); - // Uninfect tets of BC(p). - for (i = 0; i < tetlist->len(); i++) { - starttet = * (triface *)(* tetlist)[i]; - uninfect(starttet); - } - } - tetlist->clear(); - ceillist->clear(); - - return remflag; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tallslivers() Queue all the slivers in the mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tallslivers(bool optflag) -{ - triface tetloop; - - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - if (optflag) { - checktet4opt(&tetloop, true); - } else { - checktet4ill(&tetloop, true); - } - tetloop.tet = tetrahedrontraverse(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// optimizemesh() Improve mesh quality by mesh optimizations. // -// // -// Available mesh optimizing operations are: (1) multiple edge flips (3-to-2,// -// 4-to-4, 5-to-6, etc), (2) free vertex deletion, (3) new vertex insertion. // -// (1) is mandatory, while (2) and (3) are optionally. // -// // -// The variable 'b->optlevel' (set after '-s') determines the use of these // -// operations. If it is: 0, do no optimization; 1, only do (1) operation; 2, // -// do (1) and (2) operations; 3, do all operations. Deault, b->optlvel = 2. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::optimizemesh(bool optflag) -{ - list *splittetlist, *tetlist, *ceillist; - badface *remtet, *lastentry; - REAL maxdihed, objdihed, curdihed; - long oldnum; - int iter, i; - - if (!b->quiet) { - if (optflag) { - printf("Optimizing mesh.\n"); - } else { - printf("Repairing mesh.\n"); - } - } - -#ifdef SELF_CHECK - if (optflag && (b->verbose)) { - printf(" level = %d.\n", b->optlevel); - } -#endif - - // Initialize the pool of bad tets. - badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0); - if (optflag) { - cosmaxdihed = cos(b->maxdihedral * PI / 180.0); - cosmindihed = cos(b->mindihedral * PI / 180.0); - // The radian of the maximum dihedral angle. - maxdihed = b->maxdihedral / 180.0 * PI; - // A sliver has an angle large than 'objdihed' will be split. - objdihed = b->maxdihedral + 5.0; - if (objdihed < 170.0) objdihed = 170.0; - objdihed = objdihed / 180.0 * PI; - } - // Looking for non-optimal tets. - tallslivers(optflag); - - optcount[0] = 0l; // tet strip count. - optcount[1] = 0l; // face (2-3) and edge (2-2) flip count. - optcount[3] = optcount[4] = optcount[5] = optcount[6] = 0l; // edge flips. - optcount[9] = 0l; // combined flip count. - - // Perform edge flip to improve quality. - lastentry = (badface *) NULL; - // Loop until pool 'badtetrahedrons' is empty. - while (badtetrahedrons->items > 0) { - badtetrahedrons->traversalinit(); - remtet = badfacetraverse(badtetrahedrons); - while (remtet != (badface *) NULL) { - // Make sure that the tet is still the same one when it was tested. - // Subsequent transformations may have made it a different tet. - if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg && - dest(remtet->tt) == remtet->fdest && - apex(remtet->tt) == remtet->fapex && - oppo(remtet->tt) == remtet->foppo) { - if (b->verbose > 1) { - printf(" Repair tet (%d, %d, %d, %d) %g (degree).\n", - pointmark(remtet->forg), pointmark(remtet->fdest), - pointmark(remtet->fapex), pointmark(remtet->foppo), - acos(remtet->key) / PI * 180.0); - } - if (!removeedge(remtet, optflag)) { - // An unremoveable tet. Check if it forms a loop. - if (lastentry != (badface *) NULL) { - if (remtet == lastentry) break; - } else { - // Remember this tet as a breakpoint. - lastentry = remtet; - } - } else { - // Clear the breakpoint. - lastentry = (badface *) NULL; - // Remove the entry from the queue. - badfacedealloc(badtetrahedrons, remtet); - } - } else { - // Remove the entry from the queue. - badfacedealloc(badtetrahedrons, remtet); - } - remtet = badfacetraverse(badtetrahedrons); - } - // Stop if the above loop was out by force. - if (remtet != (badface *) NULL) break; - } - - if (b->verbose) { - if (optcount[0] > 0l) { - printf(" %ld tets are peeled off.\n", optcount[0]); - } - if (optcount[1] > 0l) { - printf(" %ld faces are flipped.\n", optcount[1]); - } - if (optcount[3] + optcount[4] + optcount[5] + optcount[6] + - optcount[9] > 0l) { - printf(" %ld edges are flipped.\n", optcount[3] + optcount[4] + - optcount[5] + optcount[6] + optcount[9]); - } - // if (badtetrahedrons->items > 0l) { - // printf(" %ld edges remain.\n", badtetrahedrons->items); - // } - } - - if ((badtetrahedrons->items > 0l) && optflag && (b->optlevel > 2)) { - splittetlist = new list(sizeof(badface), NULL, 256); - tetlist = new list(sizeof(triface), NULL, 256); - ceillist = new list(sizeof(triface), NULL, 256); - oldnum = points->items; - smoothsegverts = smoothvolverts = 0; - optcount[1] = 0l; - optcount[3] = optcount[4] = optcount[5] = optcount[6] = 0l; // edge flips. - optcount[9] = 0l; // combined flip count. - iter = 0; - - do { - // Form a list of slivers to be split and clean the pool. - badtetrahedrons->traversalinit(); - remtet = badfacetraverse(badtetrahedrons); - while (remtet != (badface *) NULL) { - splittetlist->append(remtet); - // Remove the entry from the queue. - badfacedealloc(badtetrahedrons, remtet); - remtet = badfacetraverse(badtetrahedrons); - } - for (i = 0; i < splittetlist->len(); i++) { - remtet = (badface *)(* splittetlist)[i]; - // Make sure that the tet is still the same one when it was tested. - // Subsequent transformations may have made it a different tet. - if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg && - dest(remtet->tt) == remtet->fdest && - apex(remtet->tt) == remtet->fapex && - oppo(remtet->tt) == remtet->foppo) { - // The sliver may get smoothed due to a neighboring tet. - curdihed = facedihedral(remtet->forg, remtet->fdest, remtet->fapex, - remtet->foppo); - // The dihedral angle of a tet must less than PI, correct it. - if (curdihed > PI) curdihed = 2 * PI - curdihed; - // Is it a large angle? - if (curdihed > objdihed) { - remtet->key = cos(curdihed); - if (b->verbose > 1) { - printf(" Get sliver (%d, %d, %d, %d) %g (degree).\n", - pointmark(remtet->forg), pointmark(remtet->fdest), - pointmark(remtet->fapex), pointmark(remtet->foppo), - acos(remtet->key) / PI * 180.0); - } - if (!removeedge(remtet, optflag)) { - if (!smoothsliver(remtet, tetlist)) { - splitsliver(remtet, tetlist, ceillist); - } - } - } - } - } - iter++; - } while ((badtetrahedrons->items > 0l) && (iter < b->optpasses)); - - if (b->verbose) { - printf(" %d passes.\n", iter); - if ((points->items - oldnum) > 0l) { - printf(" %ld points are inserted (%d on segment).\n", - points->items - oldnum, smoothsegverts); - } - if (optcount[1] > 0l) { - printf(" %ld faces are flipped.\n", optcount[1]); - } - if (optcount[3] + optcount[4] + optcount[5] + optcount[6] + - optcount[9] > 0l) { - printf(" %ld edges are flipped.\n", optcount[3] + optcount[4] + - optcount[5] + optcount[6] + optcount[9]); - } - // if (badtetrahedrons->items > 0l) { - // printf(" %ld edges remain.\n", badtetrahedrons->items); - // } - } - delete tetlist; - delete ceillist; - delete splittetlist; - } - - delete badtetrahedrons; - badtetrahedrons = (memorypool *) NULL; -} - -// -// End of mesh optimization routines -// - -// -// Begin of I/O rouitnes -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// transfernodes() Transfer nodes from 'io->pointlist' to 'this->points'. // -// // -// Initializing 'this->points'. Transferring all points from 'in->pointlist'// -// into it. All points are indexed (start from in->firstnumber). Each point // -// is initialized be UNUSEDVERTEX. The bounding box (xmin, xmax, ymin, ymax,// -// zmin, zmax) and the diameter (longest) of the point set are calculated. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::transfernodes() -{ - point pointloop; - REAL x, y, z; - int coordindex; - int attribindex; - int mtrindex; - int i, j; - - // Read the points. - coordindex = 0; - attribindex = 0; - mtrindex = 0; - for (i = 0; i < in->numberofpoints; i++) { - makepoint(&pointloop); - // Read the point coordinates. - x = pointloop[0] = in->pointlist[coordindex++]; - y = pointloop[1] = in->pointlist[coordindex++]; - z = pointloop[2] = in->pointlist[coordindex++]; - // Read the point attributes. - for (j = 0; j < in->numberofpointattributes; j++) { - pointloop[3 + j] = in->pointattributelist[attribindex++]; - } - // Read the point metric tensor. - for (j = 0; j < in->numberofpointmtrs; j++) { - pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; - } - // Determine the smallest and largests x, y and z coordinates. - if (i == 0) { - xmin = xmax = x; - ymin = ymax = y; - zmin = zmax = z; - } else { - xmin = (x < xmin) ? x : xmin; - xmax = (x > xmax) ? x : xmax; - ymin = (y < ymin) ? y : ymin; - ymax = (y > ymax) ? y : ymax; - zmin = (z < zmin) ? z : zmin; - zmax = (z > zmax) ? z : zmax; - } - } - // 'longest' is the largest possible edge length formed by input vertices. - x = xmax - xmin; - y = ymax - ymin; - z = zmax - zmin; - longest = sqrt(x * x + y * y + z * z); - if (longest == 0.0) { - printf("Error: The point set is trivial.\n"); - terminatetetgen(1); - } - // Two identical points are distinguished by 'lengthlimit'. - lengthlimit = longest * b->epsilon * 1e+2; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// jettisonnodes() Jettison unused or duplicated vertices. // -// // -// Unused points are those input points which are outside the mesh domain or // -// have no connection (isolated) to the mesh. Duplicated points exist for // -// example if the input PLC is read from a .stl mesh file (marked during the // -// Delaunay tetrahedralization step. This routine remove these points from // -// points list. All existing points are reindexed. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::jettisonnodes() -{ - point pointloop; - bool jetflag; - int oldidx, newidx; - int remcount; - - if (!b->quiet) { - printf("Jettisoning redundants points.\n"); - } - - points->traversalinit(); - pointloop = pointtraverse(); - oldidx = newidx = 0; // in->firstnumber; - remcount = 0; - while (pointloop != (point) NULL) { - jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || - (pointtype(pointloop) == UNUSEDVERTEX); - if (jetflag) { - // It is a duplicated point, delete it. - pointdealloc(pointloop); - remcount++; - } else { - // Re-index it. - setpointmark(pointloop, newidx + in->firstnumber); - if (in->pointmarkerlist != (int *) NULL) { - if (oldidx < in->numberofpoints) { - // Re-index the point marker as well. - in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx]; - } - } - newidx++; - } - oldidx++; - if (oldidx == in->numberofpoints) { - // Update the numbe of input points (Because some were removed). - in->numberofpoints -= remcount; - // Remember this number for output original input nodes. - jettisoninverts = remcount; - } - pointloop = pointtraverse(); - } - if (b->verbose) { - printf(" %d duplicated vertices 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. - points->deaditemstack = (void *) NULL; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// highorder() Create extra nodes for quadratic subparametric elements. // -// // -// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing // -// high-order nodes of each tetrahedron. This routine is used only when -o2 // -// switch is used. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::highorder() -{ - triface tetloop, worktet; - triface spintet, adjtet; - point torg, tdest, tapex; - point *extralist, *adjextralist; - point newpoint; - int hitbdry, ptmark; - int i, j; - - if (!b->quiet) { - printf("Adding vertices for second-order tetrahedra.\n"); - } - - // Initialize the 'highordertable'. - highordertable = new point[tetrahedrons->items * 6]; - if (highordertable == (point *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - - // The following line ensures that dead items in the pool of nodes cannot - // be allocated for the extra nodes associated with high order elements. - // This ensures that the primary nodes (at the corners of elements) will - // occur earlier in the output files, and have lower indices, than the - // extra nodes. - points->deaditemstack = (void *) NULL; - - // Assign an entry for each tetrahedron to find its extra nodes. At the - // mean while, initialize all extra nodes be NULL. - i = 0; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i]; - for (j = 0; j < 6; j++) { - highordertable[i + j] = (point) NULL; - } - i += 6; - tetloop.tet = tetrahedrontraverse(); - } - - // To create a unique node on each edge. Loop over all tetrahedra, and - // look at the six edges of each tetrahedron. If the extra node in - // the tetrahedron corresponding to this edge is NULL, create a node - // for this edge, at the same time, set the new node into the extra - // node lists of all other tetrahedra sharing this edge. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Get the list of extra nodes. - extralist = (point *) tetloop.tet[highorderindex]; - worktet.tet = tetloop.tet; - for (i = 0; i < 6; i++) { - if (extralist[i] == (point) NULL) { - // Operate on this edge. - worktet.loc = edge2locver[i][0]; - worktet.ver = edge2locver[i][1]; - // Create a new node on this edge. - torg = org(worktet); - tdest = dest(worktet); - // Create a new node in the middle of the edge. - newpoint = (point) points->alloc(); - // Interpolate its attributes. - for (j = 0; j < 3 + in->numberofpointattributes; j++) { - newpoint[j] = 0.5 * (torg[j] + tdest[j]); - } - ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1); - setpointmark(newpoint, ptmark); - // Add this node to its extra node list. - extralist[i] = newpoint; - // Set 'newpoint' into extra node lists of other tetrahedra - // sharing this edge. - tapex = apex(worktet); - spintet = worktet; - hitbdry = 0; - while (hitbdry < 2) { - if (fnextself(spintet)) { - // Get the extra node list of 'spintet'. - adjextralist = (point *) spintet.tet[highorderindex]; - // Find the index of its extra node list. - j = locver2edge[spintet.loc][spintet.ver]; - // Only set 'newpoint' into 'adjextralist' if it is a NULL. - // Because two faces can belong to the same tetrahedron. - if (adjextralist[j] == (point) NULL) { - adjextralist[j] = newpoint; - } - if (apex(spintet) == tapex) { - break; - } - } else { - hitbdry++; - if (hitbdry < 2) { - esym(worktet, spintet); - } - } - } - } - } - tetloop.tet = tetrahedrontraverse(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outnodes() Output the points to a .node file or a tetgenio structure. // -// // -// Note: each point has already been numbered on input (the first index is // -// 'in->firstnumber'). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outnodes(tetgenio* out) -{ - FILE *outfile; - char outnodefilename[FILENAMESIZE]; - shellface subptr; - triface adjtet; - face subloop; - point pointloop; - point *extralist, ep[3]; - int nextras, bmark, shmark, marker; - int coordindex, attribindex; - int pointnumber, firstindex; - int index, i; - - if (out == (tetgenio *) NULL) { - strcpy(outnodefilename, b->outfilename); - strcat(outnodefilename, ".node"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outnodefilename); - } else { - printf("Writing nodes.\n"); - } - } - - nextras = in->numberofpointattributes; - bmark = !b->nobound && in->pointmarkerlist; - - // Avoid compile warnings. - outfile = (FILE *) NULL; - marker = coordindex = 0; - - if (out == (tetgenio *) NULL) { - outfile = fopen(outnodefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outnodefilename); - terminatetetgen(1); - } - // Number of points, number of dimensions, number of point attributes, - // and number of boundary markers (zero or one). - fprintf(outfile, "%ld %d %d %d\n", points->items, 3, nextras, bmark); - } else { - // Allocate space for 'pointlist'; - out->pointlist = new REAL[points->items * 3]; - if (out->pointlist == (REAL *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - // Allocate space for 'pointattributelist' if necessary; - if (nextras > 0) { - out->pointattributelist = new REAL[points->items * nextras]; - if (out->pointattributelist == (REAL *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - // Allocate space for 'pointmarkerlist' if necessary; - if (bmark) { - out->pointmarkerlist = new int[points->items]; - if (out->pointmarkerlist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - out->numberofpoints = points->items; - out->numberofpointattributes = nextras; - coordindex = 0; - attribindex = 0; - } - - if (bmark && (b->plc || b->refine)) { - // Initialize the point2tet field of each point. - points->traversalinit(); - pointloop = pointtraverse(); - while (pointloop != (point) NULL) { - setpoint2tet(pointloop, (tetrahedron) NULL); - pointloop = pointtraverse(); - } - // Make a map point-to-subface. Hence a boundary point will get the - // facet marker from that facet where it lies on. - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - subloop.shver = 0; - // Check all three points of the subface. - for (i = 0; i < 3; i++) { - pointloop = (point) subloop.sh[3 + i]; - setpoint2tet(pointloop, (tetrahedron) sencode(subloop)); - } - if (b->order == 2) { - // '-o2' switch. Set markers for quadratic nodes of this subface. - stpivot(subloop, adjtet); - if (adjtet.tet == dummytet) { - sesymself(subloop); - stpivot(subloop, adjtet); - } - assert(adjtet.tet != dummytet); - extralist = (point *) adjtet.tet[highorderindex]; - switch (adjtet.loc) { - case 0: - ep[0] = extralist[0]; - ep[1] = extralist[1]; - ep[2] = extralist[2]; - break; - case 1: - ep[0] = extralist[0]; - ep[1] = extralist[4]; - ep[2] = extralist[3]; - break; - case 2: - ep[0] = extralist[1]; - ep[1] = extralist[5]; - ep[2] = extralist[4]; - break; - case 3: - ep[0] = extralist[2]; - ep[1] = extralist[3]; - ep[2] = extralist[5]; - break; - default: break; - } - for (i = 0; i < 3; i++) { - setpoint2tet(ep[i], (tetrahedron) sencode(subloop)); - } - } - subloop.sh = shellfacetraverse(subfaces); - } - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - - points->traversalinit(); - pointloop = pointtraverse(); - pointnumber = firstindex; // in->firstnumber; - index = 0; - while (pointloop != (point) NULL) { - if (bmark) { - // Default the vertex has a zero marker. - marker = 0; - // Is it an input vertex? - if (index < in->numberofpoints) { - // Input point's marker is directly copied to output. - marker = in->pointmarkerlist[index]; - } - // Is it a boundary vertex has marker zero? - if ((marker == 0) && (b->plc || b->refine)) { - subptr = (shellface) point2tet(pointloop); - if (subptr != (shellface) NULL) { - // Default a boundary vertex has marker 1. - marker = 1; - if (in->facetmarkerlist != (int *) NULL) { - // The vertex gets the marker from the facet it lies on. - sdecode(subptr, subloop); - shmark = shellmark(subloop); - marker = in->facetmarkerlist[shmark - 1]; - } - } - } - } - if (out == (tetgenio *) NULL) { - // Point number, x, y and z coordinates. - fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber, - pointloop[0], pointloop[1], pointloop[2]); - for (i = 0; i < nextras; i++) { - // Write an attribute. - fprintf(outfile, " %.17g", pointloop[3 + i]); - } - if (bmark) { - // Write the boundary marker. - fprintf(outfile, " %d", marker); - } - fprintf(outfile, "\n"); - } else { - // X, y, and z coordinates. - out->pointlist[coordindex++] = pointloop[0]; - out->pointlist[coordindex++] = pointloop[1]; - out->pointlist[coordindex++] = pointloop[2]; - // Point attributes. - for (i = 0; i < nextras; i++) { - // Output an attribute. - out->pointattributelist[attribindex++] = pointloop[3 + i]; - } - if (bmark) { - // Output the boundary marker. - out->pointmarkerlist[index] = marker; - } - } - pointloop = pointtraverse(); - pointnumber++; - index++; - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outmetrics() Output the metric to a file (*.mtr) or a tetgenio obj. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outmetrics(tetgenio* out) -{ - FILE *outfile; - char outmtrfilename[FILENAMESIZE]; - list *tetlist, *ptlist; - triface tetloop; - point ptloop, neipt; - REAL lave, len; // lmin, lmax, - int mtrindex; - int i; - - if (out == (tetgenio *) NULL) { - strcpy(outmtrfilename, b->outfilename); - strcat(outmtrfilename, ".mtr"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outmtrfilename); - } else { - printf("Writing metrics.\n"); - } - } - - // Avoid compile warnings. - outfile = (FILE *) NULL; - mtrindex = 0; - - if (out == (tetgenio *) NULL) { - outfile = fopen(outmtrfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outmtrfilename); - terminatetetgen(1); - } - // Number of points, number of point metrices, - // fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3); - fprintf(outfile, "%ld %d\n", points->items, 1); - } else { - // Allocate space for 'pointmtrlist' if necessary; - // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)]; - out->pointmtrlist = new REAL[points->items]; - if (out->pointmtrlist == (REAL *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - out->numberofpointmtrs = 1; // (sizeoftensor + 3); - mtrindex = 0; - } - - // Initialize the point2tet field of each point. - points->traversalinit(); - ptloop = pointtraverse(); - while (ptloop != (point) NULL) { - setpoint2tet(ptloop, (tetrahedron) NULL); - ptloop = pointtraverse(); - } - // Create the point-to-tet map. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - for (i = 0; i < 4; i++) { - ptloop = (point) tetloop.tet[4 + i]; - setpoint2tet(ptloop, encode(tetloop)); - } - tetloop.tet = tetrahedrontraverse(); - } - - tetlist = new list(sizeof(triface), NULL, 256); - ptlist = new list(sizeof(point *), NULL, 256); - - points->traversalinit(); - ptloop = pointtraverse(); - while (ptloop != (point) NULL) { - decode(point2tet(ptloop), tetloop); - if (!isdead(&tetloop)) { - // Form the star of p. - tetlist->append(&tetloop); - formstarpolyhedron(ptloop, tetlist, ptlist, true); - // lmin = longest; - // lmax = 0.0; - lave = 0.0; - for (i = 0; i < ptlist->len(); i++) { - neipt = * (point *)(* ptlist)[i]; - len = distance(ptloop, neipt); - // lmin = lmin < len ? lmin : len; - // lmax = lmax > len ? lmax : len; - lave += len; - } - lave /= ptlist->len(); - } - if (out == (tetgenio *) NULL) { - // for (i = 0; i < sizeoftensor; i++) { - // fprintf(outfile, "%-16.8e ", ptloop[pointmtrindex + i]); - // } - if (ptlist->len() > 0) { - // fprintf(outfile, "%-16.8e %-16.8e %-16.8e", lmin, lmax, lave); - fprintf(outfile, "%-16.8e ", lave); - } else { - fprintf(outfile, "0.0 "); // fprintf(outfile, "0.0 0.0 0.0"); - } - fprintf(outfile, "\n"); - } else { - // for (i = 0; i < sizeoftensor; i++) { - // out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i]; - // } - if (ptlist->len() > 0) { - // out->pointmtrlist[mtrindex++] = lmin; - // out->pointmtrlist[mtrindex++] = lmax; - out->pointmtrlist[mtrindex++] = lave; - } else { - // out->pointmtrlist[mtrindex++] = 0.0; - // out->pointmtrlist[mtrindex++] = 0.0; - out->pointmtrlist[mtrindex++] = 0.0; - } - } - tetlist->clear(); - ptlist->clear(); - ptloop = pointtraverse(); - } - - delete tetlist; - delete ptlist; - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outelements() Output the tetrahedra to an .ele file or a tetgenio // -// structure. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outelements(tetgenio* out) -{ - FILE *outfile; - char outelefilename[FILENAMESIZE]; - tetrahedron* tptr; - int *tlist; - REAL *talist; - int firstindex, shift; - int pointindex; - int attribindex; - point p1, p2, p3, p4; - point *extralist; - 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"); - } - } - - // Avoid compile warnings. - outfile = (FILE *) NULL; - tlist = (int *) NULL; - talist = (double *) NULL; - pointindex = attribindex = 0; - - 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", tetrahedrons->items, - b->order == 1 ? 4 : 10, eextras); - } else { - // Allocate memory for output tetrahedra. - out->tetrahedronlist = new int[tetrahedrons->items * - (b->order == 1 ? 4 : 10)]; - if (out->tetrahedronlist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - // Allocate memory for output tetrahedron attributes if necessary. - if (eextras > 0) { - out->tetrahedronattributelist = new REAL[tetrahedrons->items * eextras]; - if (out->tetrahedronattributelist == (REAL *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - out->numberoftetrahedra = tetrahedrons->items; - out->numberofcorners = b->order == 1 ? 4 : 10; - out->numberoftetrahedronattributes = eextras; - tlist = out->tetrahedronlist; - talist = out->tetrahedronattributelist; - pointindex = 0; - attribindex = 0; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - shift = 0; // Default no shiftment. - if ((in->firstnumber == 1) && (firstindex == 0)) { - shift = 1; // Shift the output indices by 1. - } - - tetrahedrons->traversalinit(); - tptr = tetrahedrontraverse(); - elementnumber = firstindex; // in->firstnumber; - while (tptr != (tetrahedron *) NULL) { - p1 = (point) tptr[4]; - p2 = (point) tptr[5]; - p3 = (point) tptr[6]; - p4 = (point) tptr[7]; - if (out == (tetgenio *) NULL) { - // Tetrahedron number, indices for four points. - fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, - pointmark(p1) - shift, pointmark(p2) - shift, - pointmark(p3) - shift, pointmark(p4) - shift); - if (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; - } - tptr = tetrahedrontraverse(); - elementnumber++; - } - if (b->neighout) { - // Set the outside element marker. - * (int *) (dummytet + elemmarkerindex) = -1; - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outfaces() Output all faces to a .face file or a tetgenio structure. // -// // -// This routines outputs all triangular faces (including outer boundary // -// faces and inner faces) of this mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outfaces(tetgenio* out) -{ - FILE *outfile; - char facefilename[FILENAMESIZE]; - int *elist; - int *emlist; - int neigh1, neigh2; - int index; - triface tface, tsymface; - face checkmark; - point torg, tdest, tapex; - long faces; - int bmark, faceid, marker; - int firstindex, shift; - int facenumber; - - if (out == (tetgenio *) NULL) { - strcpy(facefilename, b->outfilename); - strcat(facefilename, ".face"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", facefilename); - } else { - printf("Writing faces.\n"); - } - } - - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - emlist = (int *) NULL; - index = marker = 0; - - faces = (4l * tetrahedrons->items + hullsize) / 2l; - bmark = !b->nobound && in->facetmarkerlist; - - 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[subfaces->items * 2]; - if (out->adjtetlist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - out->numberoftrifaces = faces; - elist = out->trifacelist; - emlist = out->trifacemarkerlist; - index = 0; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - shift = 0; // Default no shiftment. - if ((in->firstnumber == 1) && (firstindex == 0)) { - shift = 1; // Shift the output indices by 1. - } - - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - facenumber = firstindex; // in->firstnumber; - // To loop over the set of faces, loop over all tetrahedra, and look at - // the four faces of each one. If there isn't another tetrahedron - // adjacent to this face, operate on the face. If there is another - // adjacent tetrahedron, operate on the face only if the current - // tetrahedron has a smaller pointer than its neighbor. This way, each - // face is considered only once. - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if ((tsymface.tet == dummytet) || (tface.tet < tsymface.tet)) { - torg = org(tface); - tdest = dest(tface); - tapex = apex(tface); - if (bmark) { - // Get the boundary marker of this face. If it is an inner face, - // it has no boundary marker, set it be zero. - if (b->useshelles) { - // Shell face is used. - tspivot(tface, checkmark); - if (checkmark.sh == dummysh) { - marker = 0; // It is an inner face. - } else { - faceid = shellmark(checkmark) - 1; - marker = in->facetmarkerlist[faceid]; - } - } else { - // Shell face is not used, only distinguish outer and inner face. - marker = tsymface.tet != dummytet ? 1 : 0; - } - } - if (b->neighout > 1) { - // '-nn' switch. Output adjacent tets indices. - neigh1 = * (int *)(tface.tet + elemmarkerindex); - if (tsymface.tet != dummytet) { - neigh2 = * (int *)(tsymface.tet + elemmarkerindex); - } else { - neigh2 = -1; - } - } - if (out == (tetgenio *) NULL) { - // Face number, indices of three vertices. - fprintf(outfile, "%5d %4d %4d %4d", facenumber, - pointmark(torg) - shift, pointmark(tdest) - shift, - pointmark(tapex) - shift); - if (bmark) { - // Output a boundary marker. - fprintf(outfile, " %d", marker); - } - if (b->neighout > 1) { - fprintf(outfile, " %5d %5d", neigh1, neigh2); - } - fprintf(outfile, "\n"); - } else { - // Output indices of three vertices. - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; - elist[index++] = pointmark(tapex) - shift; - if (bmark) { - emlist[facenumber - in->firstnumber] = marker; - } - if (b->neighout > 1) { - out->adjtetlist[(facenumber - in->firstnumber) * 2] = neigh1; - out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2; - } - } - facenumber++; - } - } - tface.tet = tetrahedrontraverse(); - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outhullfaces() Output outer boundary faces to a .face file or a // -// tetgenio structure. // -// // -// The normal of each face is arranged to point inside of the domain (use // -// right-hand rule). This routines will outputs convex hull faces if the // -// mesh is a Delaunay tetrahedralization. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outhullfaces(tetgenio* out) -{ - FILE *outfile; - char facefilename[FILENAMESIZE]; - int *elist; - int index; - triface tface, tsymface; - face checkmark; - point torg, tdest, tapex; - int firstindex, shift; - int facenumber; - - if (out == (tetgenio *) NULL) { - strcpy(facefilename, b->outfilename); - strcat(facefilename, ".face"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", facefilename); - } else { - printf("Writing faces.\n"); - } - } - - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - index = 0; - - if (out == (tetgenio *) NULL) { - outfile = fopen(facefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(1); - } - fprintf(outfile, "%ld 0\n", hullsize); - } else { - // Allocate memory for 'trifacelist'. - out->trifacelist = new int[hullsize * 3]; - if (out->trifacelist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - out->numberoftrifaces = hullsize; - elist = out->trifacelist; - index = 0; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - shift = 0; // Default no shiftment. - if ((in->firstnumber == 1) && (firstindex == 0)) { - shift = 1; // Shift the output indices by 1. - } - - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - facenumber = firstindex; // in->firstnumber; - // To loop over the set of hull faces, loop over all tetrahedra, and look - // at the four faces of each one. If there isn't another tetrahedron - // adjacent to this face, operate on the face. - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if (tsymface.tet == dummytet) { - torg = org(tface); - tdest = dest(tface); - tapex = apex(tface); - if (out == (tetgenio *) NULL) { - // Face number, indices of three vertices. - fprintf(outfile, "%5d %4d %4d %4d", facenumber, - pointmark(torg) - shift, pointmark(tdest) - shift, - pointmark(tapex) - shift); - fprintf(outfile, "\n"); - } else { - // Output indices of three vertices. - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; - elist[index++] = pointmark(tapex) - shift; - } - facenumber++; - } - } - tface.tet = tetrahedrontraverse(); - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outsubfaces() Output subfaces (i.e. boundary faces) to a .face file or // -// a tetgenio structure. // -// // -// The boundary faces are exist in 'subfaces'. For listing triangle vertices // -// in the same sense for all triangles in the mesh, the direction determined // -// by right-hand rule is pointer to the inside of the volume. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outsubfaces(tetgenio* out) -{ - FILE *outfile; - char facefilename[FILENAMESIZE]; - int *elist; - int *emlist; - int index, index1, index2; - triface abuttingtet; - face faceloop; - point torg, tdest, tapex; - int bmark, faceid, marker; - int firstindex, shift; - int neigh1, neigh2; - int facenumber; - - 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 subfaces.\n"); - } - } - - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - emlist = (int *) NULL; - index = index1 = index2 = 0; - faceid = marker = 0; - neigh1 = neigh2 = 0; - - 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", subfaces->items, bmark); - } else { - // Allocate memory for 'trifacelist'. - out->trifacelist = new int[subfaces->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[subfaces->items]; - if (out->trifacemarkerlist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - if (b->neighout > 1) { - // '-nn' switch. - out->adjtetlist = new int[subfaces->items * 2]; - if (out->adjtetlist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - out->numberoftrifaces = subfaces->items; - elist = out->trifacelist; - emlist = out->trifacemarkerlist; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - shift = 0; // Default no shiftment. - if ((in->firstnumber == 1) && (firstindex == 0)) { - shift = 1; // Shift the output indices by 1. - } - - subfaces->traversalinit(); - faceloop.sh = shellfacetraverse(subfaces); - facenumber = firstindex; // in->firstnumber; - while (faceloop.sh != (shellface *) NULL) { - stpivot(faceloop, abuttingtet); - if (abuttingtet.tet == dummytet) { - sesymself(faceloop); - stpivot(faceloop, abuttingtet); - } - if (abuttingtet.tet != dummytet) { - // If there is a tetrahedron containing this subface, orient it so - // that the normal of this face points to inside of the volume by - // right-hand rule. - adjustedgering(abuttingtet, CCW); - torg = org(abuttingtet); - tdest = dest(abuttingtet); - tapex = apex(abuttingtet); - } else { - // This may happen when only a surface mesh be generated. - torg = sorg(faceloop); - tdest = sdest(faceloop); - tapex = sapex(faceloop); - } - if (bmark) { - faceid = shellmark(faceloop) - 1; - marker = in->facetmarkerlist[faceid]; - } - if (b->neighout > 1) { - // '-nn' switch. Output adjacent tets indices. - neigh1 = -1; - stpivot(faceloop, abuttingtet); - if (abuttingtet.tet != dummytet) { - neigh1 = * (int *)(abuttingtet.tet + elemmarkerindex); - } - neigh2 = -1; - sesymself(faceloop); - stpivot(faceloop, abuttingtet); - if (abuttingtet.tet != dummytet) { - neigh2 = * (int *)(abuttingtet.tet + elemmarkerindex); - } - } - if (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; - } - } - facenumber++; - faceloop.sh = shellfacetraverse(subfaces); - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outedges() Output all edges to a .edge file or a structure. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outedges(tetgenio* out) -{ - FILE *outfile; - char edgefilename[FILENAMESIZE]; - int *elist, *emlist; - int index, index1; - triface tetloop, worktet, spintet; - face checksh; - point torg, tdest; - long faces, edges; - int firstindex, shift; - int edgenumber, faceid, marker; - int hitbdry, 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"); - } - } - - // Avoid compile warnings. - outfile = (FILE *) NULL; - elist = (int *) NULL; - emlist = (int *) NULL; - index = index1 = 0; - faceid = marker = 0; - - // Using the Euler formula (V-E+F-T=1) to get the total number of edges. - faces = (4l * tetrahedrons->items + hullsize) / 2l; - edges = points->items + faces - tetrahedrons->items - 1l; - - 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", edges, !b->nobound); - } else { - // Allocate memory for 'edgelist'. - out->edgelist = new int[edges * 2]; - if (out->edgelist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - if (!b->nobound) { - out->edgemarkerlist = new int[edges]; - } - out->numberofedges = edges; - elist = out->edgelist; - emlist = out->edgemarkerlist; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - shift = 0; // Default no shiftment. - if ((in->firstnumber == 1) && (firstindex == 0)) { - shift = 1; // Shift (reduce) the output indices by 1. - } - - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - edgenumber = firstindex; // in->firstnumber; - while (tetloop.tet != (tetrahedron *) NULL) { - // Count the number of Voronoi faces. Look at the six edges of each - // tetrahedron. Count the edge only if the tetrahedron's pointer is - // smaller than those of all other tetrahedra that share the edge. - worktet.tet = tetloop.tet; - for (i = 0; i < 6; i++) { - worktet.loc = edge2locver[i][0]; - worktet.ver = edge2locver[i][1]; - adjustedgering(worktet, CW); - spintet = worktet; - hitbdry = 0; - while (hitbdry < 2) { - if (fnextself(spintet)) { - if (apex(spintet) == apex(worktet)) break; - if (spintet.tet < worktet.tet) break; - } else { - hitbdry++; - if (hitbdry < 2) { - esym(worktet, spintet); - fnextself(spintet); // In the same tet. - } - } - } - // Count this edge if no adjacent tets are smaller than this tet. - if (spintet.tet >= worktet.tet) { - torg = org(worktet); - tdest = dest(worktet); - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%5d %4d %4d", edgenumber, - pointmark(torg) - shift, pointmark(tdest) - shift); - } else { - // Output three vertices of this face; - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; - } - if (!b->nobound) { - if (hitbdry > 0) { - // It is a boundary edge. Get the boundary marker of the facet - // containing this edge. Note there may have more than one - // facet, choose one arbitrarily. - if ((b->plc || b->refine) && in->facetmarkerlist) { - tspivot(spintet, checksh); - faceid = shellmark(checksh) - 1; - marker = in->facetmarkerlist[faceid]; - } else { - marker = 1; // Indicate it's a boundary edge. - } - } else { - marker = 0; - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", marker); - } else { - emlist[index1++] = marker; - } - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); - } - edgenumber++; - } - } - tetloop.tet = tetrahedrontraverse(); - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outsubsegments() Output segments to a .edge file or a structure. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outsubsegments(tetgenio* out) -{ - FILE *outfile; - 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", subsegs->items); - } else { - // Allocate memory for 'edgelist'. - out->edgelist = new int[subsegs->items * 2]; - if (out->edgelist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - out->numberofedges = subsegs->items; - elist = out->edgelist; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - shift = 0; // Default no shiftment. - if ((in->firstnumber == 1) && (firstindex == 0)) { - shift = 1; // Shift the output indices by 1. - } - - subsegs->traversalinit(); - edgeloop.sh = shellfacetraverse(subsegs); - edgenumber = firstindex; // in->firstnumber; - while (edgeloop.sh != (shellface *) NULL) { - torg = sorg(edgeloop); - tdest = sdest(edgeloop); - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%5d %4d %4d\n", edgenumber, - pointmark(torg) - shift, pointmark(tdest) - shift); - } else { - // Output three vertices of this face; - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; - } - edgenumber++; - edgeloop.sh = shellfacetraverse(subsegs); - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outneighbors() Output tet neighbors to a .neigh file or a structure. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outneighbors(tetgenio* out) -{ - FILE *outfile; - char neighborfilename[FILENAMESIZE]; - int *nlist; - int index; - triface tetloop, tetsym; - int neighbor1, neighbor2, neighbor3, neighbor4; - int firstindex; - int elementnumber; - - if (out == (tetgenio *) NULL) { - strcpy(neighborfilename, b->outfilename); - strcat(neighborfilename, ".neigh"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", neighborfilename); - } else { - printf("Writing neighbors.\n"); - } - } - - // Avoid compile warnings. - outfile = (FILE *) NULL; - nlist = (int *) NULL; - index = 0; - - 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", tetrahedrons->items, 4); - } else { - // Allocate memory for 'neighborlist'. - out->neighborlist = new int[tetrahedrons->items * 4]; - if (out->neighborlist == (int *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - nlist = out->neighborlist; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - elementnumber = firstindex; // in->firstnumber; - while (tetloop.tet != (tetrahedron *) NULL) { - tetloop.loc = 2; - sym(tetloop, tetsym); - neighbor1 = * (int *) (tetsym.tet + elemmarkerindex); - tetloop.loc = 3; - sym(tetloop, tetsym); - neighbor2 = * (int *) (tetsym.tet + elemmarkerindex); - tetloop.loc = 1; - sym(tetloop, tetsym); - neighbor3 = * (int *) (tetsym.tet + elemmarkerindex); - tetloop.loc = 0; - sym(tetloop, tetsym); - neighbor4 = * (int *) (tetsym.tet + elemmarkerindex); - if (out == (tetgenio *) NULL) { - // Tetrahedra number, neighboring tetrahedron numbers. - fprintf(outfile, "%4d %4d %4d %4d %4d\n", elementnumber, - neighbor1, neighbor2, neighbor3, neighbor4); - } else { - nlist[index++] = neighbor1; - nlist[index++] = neighbor2; - nlist[index++] = neighbor3; - nlist[index++] = neighbor4; - } - tetloop.tet = tetrahedrontraverse(); - elementnumber++; - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outvoronoi() Output the Voronoi diagram to .v.node, .v.edge, v.face, // -// and .v.cell. // -// // -// The Voronoi diagram is the geometric dual of the Delaunay triangulation. // -// The Voronoi vertices are the circumcenters of Delaunay tetrahedra. Each // -// Voronoi edge connects two Voronoi vertices at two sides of a common Dela- // -// unay face. At a face of convex hull, it becomes a ray (goto the infinity).// -// A Voronoi face is the convex hull of all Voronoi vertices around a common // -// Delaunay edge. It is a closed polygon for any interal Delaunay edge. At a // -// ridge, it is unbounded. Each Voronoi cell is the convex hull of all Vor- // -// onoi vertices around a common Delaunay vertex. It is a polytope for any // -// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay // -// vertex belonging to the convex hull. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outvoronoi(tetgenio* out) -{ - FILE *outfile; - char outfilename[FILENAMESIZE]; - tetgenio::voroedge *vedge; - tetgenio::vorofacet *vfacet; - list *tetlist, *ptlist; - triface tetloop, worktet, spintet; - point pt[4], ptloop, neipt; - REAL ccent[3], infvec[3], vec1[3], vec2[3], L; - long faces, edges; - int *tetfaceindexarray, *tetedgeindexarray; - int arraysize, *vertarray; - int vpointcount, vedgecount, vfacecount, tcount; - int index, shift; - int end1, end2; - int hitbdry, i, j, k; - - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - //NODES - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - // Output Voronoi vertices to .v.node file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.node"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi vertices.\n"); - } - } - - // Determine the first index (0 or 1). - shift = (b->zeroindex ? 0 : in->firstnumber); - // The number of Delaunay faces (= the number of Voronoi edges). - faces = (4l * tetrahedrons->items + hullsize) / 2l; - // The number of Delaunay edges (= the number of Voronoi faces). - edges = points->items + faces - tetrahedrons->items - 1; - outfile = (FILE *) NULL; // Avoid compile warnings. - - if (out == (tetgenio *) NULL) { - outfile = fopen(outfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(1); - } - // Number of voronoi points, 3 dim, no attributes, no marker. - //fprintf(outfile, "%ld 3 0 0\n", tetrahedrons->items); - fprintf(outfile, "View \"voronoi nodes\" {\n");//EMI - } else { - // Allocate space for 'vpointlist'. - out->numberofvpoints = (int) tetrahedrons->items; - out->vpointlist = new REAL[out->numberofvpoints * 3]; - if (out->vpointlist == (REAL *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - - // Loop the tetrahedronlist once, do the following: - // (1) Output Voronoi vertices (the circumcenter of the tetrahedron). - // (2) Make a map from points-to-tetrahedra (for Voronoi cells). - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - vpointcount = 0; - index = 0; - REAL *vlist; - REAL *radlist; - vlist = new REAL[(int)tetrahedrons->items * 3]; //for coords of circumcenters - radlist = new REAL[(int)tetrahedrons->items ]; //for radii of circumcenters - - while (tetloop.tet != (tetrahedron *) NULL) { - // Calculate the circumcenter. - for (i = 0; i < 4; i++) { - pt[i] = (point) tetloop.tet[4 + i]; - setpoint2tet(pt[i], encode(tetloop)); - } - REAL radius; - circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, &radius); - if (out == (tetgenio *) NULL) { - //fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift, - // ccent[0], ccent[1], ccent[2]); - fprintf(outfile,"SP(%g,%g,%g) {%g};\n", ccent[0], ccent[1], ccent[2], (double)radius);//EMI - int indexCenter = (vpointcount+shift);//EMI - vlist[indexCenter*3+0] = ccent[0];//EMI - vlist[indexCenter*3+1] = ccent[1];//EMI - vlist[indexCenter*3+2] = ccent[2];//EMI - radlist[indexCenter] = radius;//EMI - - } else { - out->vpointlist[index++] = ccent[0]; - out->vpointlist[index++] = ccent[1]; - out->vpointlist[index++] = ccent[2]; - } - // Remember the index of this element. - * (int *) (tetloop.tet + elemmarkerindex) = vpointcount; - vpointcount++; - tetloop.tet = tetrahedrontraverse(); - } - - // Set the outside element marker. - * (int *) (dummytet + elemmarkerindex) = -1; - - if (out == (tetgenio *) NULL) { - //fprintf(outfile, "# Generated by %s\n", b->commandline); - fprintf(outfile,"};\n"); //EMI - fclose(outfile); - } - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - //EDGES - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - // Output Voronoi edges to .v.edge file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.edge"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi edges.\n"); - } - } - - if (out == (tetgenio *) NULL) { - outfile = fopen(outfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(1); - } - // Number of Voronoi edges, no marker. - //sfprintf(outfile, "%ld 0\n", faces); - fprintf(outfile, "View \"voronoi edges\" {\n");//EMI - } else { - // Allocate space for 'vpointlist'. - out->numberofedges = (int) faces; - out->vedgelist = new tetgenio::voroedge[out->numberofvedges]; - } - - // Loop the tetrahedronlist once, output the Voronoi edges. The index of - // each Voronoi edge corresponding to the index of the Delaunay face. - // The four faces' indices of each tetrahedron are saved in the list - // 'tetfaceindexarray', in the entry of i, where i (0-based) is the - // index of this tetrahedron (= vpointcount). - tetfaceindexarray = new int[tetrahedrons->items * 4]; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - vedgecount = 0; - index = 0; - while (tetloop.tet != (tetrahedron *) NULL) { - // Count the number of Voronoi edges. Look at the four faces of each - // tetrahedron. Count the face if the tetrahedron's pointer is - // smaller than its neighbor's or the neighbor is outside. - end1 = * (int *) (tetloop.tet + elemmarkerindex); - for (i = 0; i < 4; i++) { - decode(tetloop.tet[i], worktet); - if ((worktet.tet == dummytet) || (tetloop.tet < worktet.tet)) { - if (out == (tetgenio *) NULL) { - //fprintf(outfile, "%4d %4d", vedgecount + shift, end1 + shift); //EMI - } else { - vedge = &(out->vedgelist[index++]); - vedge->v1 = end1 + shift; - } - end2 = * (int *) (worktet.tet + elemmarkerindex); - // Note that end2 may be -1 (worktet.tet is outside). - if (end2 == -1) { - // Calculate the out normal of this hull face. - worktet.tet = tetloop.tet; - worktet.loc = i; - worktet.ver = 1; // The CW edge ring. - pt[0] = org(worktet); - pt[1] = dest(worktet); - pt[2] = apex(worktet); - for (j = 0; j < 3; j++) vec1[j] = pt[1][j] - pt[0][j]; - for (j = 0; j < 3; j++) vec2[j] = pt[2][j] - pt[0][j]; - cross(vec1, vec2, infvec); - // Normalize it. - L = sqrt(infvec[0] * infvec[0] + infvec[1] * infvec[1] - + infvec[2] * infvec[2]); - if (L > 0) for (j = 0; j < 3; j++) infvec[j] /= L; - if (out == (tetgenio *) NULL) { - //fprintf(outfile, " -1");//EMI - //fprintf(outfile, " %g %g %g\n", infvec[0], infvec[1], infvec[2]);//EMI - } else { - vedge->v2 = -1; - vedge->vnormal[0] = infvec[0]; - vedge->vnormal[1] = infvec[1]; - vedge->vnormal[2] = infvec[2]; - } - } else { - if (out == (tetgenio *) NULL) { - //fprintf(outfile, " %4d\n", end2 + shift);//EMI - int v1 = (end1+shift); - int v2 = (end2+shift); - fprintf(outfile,"SL(%g,%g,%g,%g,%g,%g) {%g,%g};\n", - vlist[v1*3+0], vlist[v1*3+1], vlist[v1*3+2], - vlist[v2*3+0], vlist[v2*3+1], vlist[v2*3+2], - radlist[v1], radlist[v2]); - } else { - vedge->v2 = end2 + shift; - vedge->vnormal[0] = 0.0; - vedge->vnormal[1] = 0.0; - vedge->vnormal[2] = 0.0; - } - } - // Save the face index in this tet and its neighbor if exists. - tetfaceindexarray[end1 * 4 + i] = vedgecount; - if (end2 != -1) { - tetfaceindexarray[end2 * 4 + worktet.loc] = vedgecount; - } - vedgecount++; - } - } - tetloop.tet = tetrahedrontraverse(); - } - - if (out == (tetgenio *) NULL) { - //fprintf(outfile, "# Generated by %s\n", b->commandline); - fprintf(outfile,"};\n"); //EMI - fclose(outfile); - } - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // FACES - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - // Output Voronoi faces to .v.face file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.face"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi faces.\n"); - } - } - - if (out == (tetgenio *) NULL) { - outfile = fopen(outfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(1); - } - // Number of Voronoi faces. - fprintf(outfile, "%ld 0\n", edges); - } else { - out->numberofvfacets = edges; - out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets]; - if (out->vfacetlist == (tetgenio::vorofacet *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - - // Loop the tetrahedronlist once, Output Voronoi facets. The index of each - // Voronoi facet corresponding to the index of the Delaunay edge. The - // six edges' indices of each tetrahedron are saved in the list 'tetedge- - // indexarray', in the entry of i, where i (0-based) is the index of - // this tetrahedron (= vpointcount). - tetedgeindexarray = new int[tetrahedrons->items * 6]; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - vfacecount = 0; - while (tetloop.tet != (tetrahedron *) NULL) { - // Count the number of Voronoi faces. Look at the six edges of each - // tetrahedron. Count the edge only if the tetrahedron's pointer is - // smaller than those of all other tetrahedra that share the edge. - worktet = tetloop; - for (i = 0; i < 6; i++) { - worktet.loc = edge2locver[i][0]; - worktet.ver = edge2locver[i][1]; - // Now count the number of tets surrounding this edge. - tcount = 1; - adjustedgering(worktet, CW); - spintet = worktet; - hitbdry = 0; - while (hitbdry < 2) { - if (fnextself(spintet)) { - if (apex(spintet) == apex(worktet)) break; - if (spintet.tet < worktet.tet) break; - tcount++; - } else { - hitbdry++; - if (hitbdry < 2) { - esym(worktet, spintet); - fnextself(spintet); // In the same tet. - } - } - } - // Count this edge if no adjacent tets are smaller than this tet. - if (spintet.tet >= worktet.tet) { - // Get the two endpoints of this edge. - pt[0] = org(worktet); - pt[1] = dest(worktet); - end1 = pointmark(pt[0]) - in->firstnumber; - end2 = pointmark(pt[1]) - in->firstnumber; - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%4d %4d %4d %-2d ", vfacecount + shift, - end1 + shift, end2 + shift, tcount + (hitbdry > 0)); - } else { - vfacet = &(out->vfacetlist[vfacecount]); - vfacet->c1 = end1 + shift; - vfacet->c2 = end2 + shift; - vfacet->elist = new int[tcount + (hitbdry > 0) + 1]; - vfacet->elist[0] = tcount + (hitbdry > 0); - index = 1; - } - // If hitbdry > 0, then spintet is a hull face. - if (hitbdry > 0) { - // The edge list starts with a ray. - vpointcount = * (int *) (spintet.tet + elemmarkerindex); - vedgecount = tetfaceindexarray[vpointcount * 4 + spintet.loc]; - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", vedgecount + shift); - } else { - vfacet->elist[index++] = vedgecount + shift; - } - // Save this facet number in tet. - tetedgeindexarray[vpointcount * 6 + - locver2edge[spintet.loc][spintet.ver]] = vfacecount; - esymself(spintet); - fnextself(spintet); // In the same tet. - } - // Output internal Voronoi edges. - for (j = 0; j < tcount; j++) { - vpointcount = * (int *) (spintet.tet + elemmarkerindex); - vedgecount = tetfaceindexarray[vpointcount * 4 + spintet.loc]; - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", vedgecount + shift); - } else { - vfacet->elist[index++] = vedgecount + shift; - } - // Save this facet number in tet. - tetedgeindexarray[vpointcount * 6 + - locver2edge[spintet.loc][spintet.ver]] = vfacecount; - fnextself(spintet); - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); - } - vfacecount++; - } - } // if (i = 0; i < 6; i++) - tetloop.tet = tetrahedrontraverse(); - } - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // CELLS - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - // Output Voronoi cells to .v.cell file. - if (out == (tetgenio *) NULL) { - strcpy(outfilename, b->outfilename); - strcat(outfilename, ".v.cell"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", outfilename); - } else { - printf("Writing Voronoi cells.\n"); - } - } - - if (out == (tetgenio *) NULL) { - outfile = fopen(outfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(1); - } - // Number of Voronoi cells. - fprintf(outfile, "%ld\n", points->items); - } else { - out->numberofvcells = points->items; - out->vcelllist = new int*[out->numberofvcells]; - if (out->vcelllist == (int **) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - - // Loop through point list, for each point, output a Voronoi cell. - tetlist = new list(sizeof(triface), NULL, 256); - ptlist = new list(sizeof(point *), NULL, 256); - points->traversalinit(); - ptloop = pointtraverse(); - vpointcount = 0; - while (ptloop != (point) NULL) { - decode(point2tet(ptloop), tetloop); - // assert(!isdead(&tetloop)); - if (!isdead(&tetloop)) { - // Form the star of p. - tetlist->append(&tetloop); - formstarpolyhedron(ptloop, tetlist, ptlist, true); - tcount = ptlist->len(); - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%4d %-2d ", vpointcount + shift, tcount); - } else { - arraysize = tcount; - vertarray = out->vcelllist[vpointcount]; - vertarray = new int[arraysize + 1]; - vertarray[0] = arraysize; - index = 1; - } - // List Voronoi facets bounding this cell. - for (i = 0; i < ptlist->len(); i++) { - neipt = * (point *)(* ptlist)[i]; - // Find a tet in tetlist having edge (ptloop, neipt) -- Very Slow. - for (j = 0; j < tetlist->len(); j++) { - tetloop = * (triface *)(* tetlist)[j]; - for (k = 0; k < 6; k++) { - tetloop.loc = edge2locver[k][0]; - tetloop.ver = edge2locver[k][1]; - if (org(tetloop) == ptloop) { - if (dest(tetloop) == neipt) break; - } else if (org(tetloop) == neipt) { - if (dest(tetloop) == ptloop) break; - } - } - if (k < 6) break; // Found this edge. - } - assert(j < tetlist->len()); - // k is the right edge number. - end1 = * (int *) (tetloop.tet + elemmarkerindex); - vfacecount = tetedgeindexarray[end1 * 6 + k]; - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", vfacecount + shift); - } else { - vertarray[index++] = vfacecount + shift; - } - } // for (i = 0; i < ptlist->len(); i++) { - if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); - } - vpointcount++; - } - tetlist->clear(); - ptlist->clear(); - ptloop = pointtraverse(); - } - delete tetlist; - delete ptlist; - delete [] tetfaceindexarray; - delete [] tetedgeindexarray; - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outpbcnodes() Output pbc node pairs to a .pbc file or a structure. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outpbcnodes(tetgenio* out) -{ - FILE *outfile; - char pbcfilename[FILENAMESIZE]; - list *ptpairlist; - tetgenio::pbcgroup *pgi, *pgo; - pbcdata *pd; - face faceloop; - face checkseg, symseg; - point *ptpair, pa, pb; - enum locateresult loc; - REAL sympt[3], d1, d2; - int *worklist; - int firstindex, shift; - int index, idx; - int i, j, k, l; - - if (out == (tetgenio *) NULL) { - strcpy(pbcfilename, b->outfilename); - strcat(pbcfilename, ".pbc"); - } - - if (!b->quiet) { - if (out == (tetgenio *) NULL) { - printf("Writing %s.\n", pbcfilename); - } else { - printf("Writing pbc nodes.\n"); - } - } - - // Avoid compilation warnings. - outfile = (FILE *) NULL; - pgo = (tetgenio::pbcgroup *) NULL; - index = 0; - - if (out == (tetgenio *) NULL) { - outfile = fopen(pbcfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", pbcfilename); - terminatetetgen(1); - } - // Number of pbc groups. - fprintf(outfile, "# number of PBCs.\n"); - fprintf(outfile, "%d\n\n", in->numberofpbcgroups); - } else { - out->numberofpbcgroups = in->numberofpbcgroups; - // Allocate memory for 'out->pbcgrouplist'. - out->pbcgrouplist = new tetgenio::pbcgroup[in->numberofpbcgroups]; - // (Next line was a bug, reported by Murry Nigel). - if (out->pbcgrouplist == (tetgenio::pbcgroup *) NULL) { - printf("Error: Out of memory.\n"); - terminatetetgen(1); - } - } - - ptpairlist = new list(2 * sizeof(point *), NULL, 256); - worklist = new int[points->items + 1]; - for (i = 0; i < points->items + 1; i++) worklist[i] = 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. - } - - for (i = 0; i < in->numberofpbcgroups; i++) { - // Group i. - pgi = &(in->pbcgrouplist[i]); - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# PBC %d\n", in->firstnumber + i); - // Output facet markers. - fprintf(outfile, "%d %d\n", pgi->fmark1, pgi->fmark2); - // Output transformation matrix. - fprintf(outfile, "[\n"); - for (j = 0; j < 4; j++) { - fprintf(outfile, " %.12g %.12g %.12g %.12g\n", pgi->transmat[j][0], - pgi->transmat[j][1], pgi->transmat[j][2], pgi->transmat[j][3]); - } - fprintf(outfile, "]\n"); - } else { - pgo = &(out->pbcgrouplist[i]); - // Copy data from pgi to pgo. - pgo->fmark1 = pgi->fmark1; - pgo->fmark2 = pgi->fmark2; - for (j = 0; j < 4; j++) { - for (k = 0; k < 4; k++) pgo->transmat[j][k] = pgi->transmat[j][k]; - } - } - - // Find the point pairs of group i. - subfaces->traversalinit(); - faceloop.sh = shellfacetraverse(subfaces); - while (faceloop.sh != (shellface *) NULL) { - if (shellpbcgroup(faceloop) == i) { - // It is in group i. Operate on it if it has pgi->fmark1. - idx = shellmark(faceloop) - 1; - if (in->facetmarkerlist[idx] == pgi->fmark1) { - // Loop three edges of the subface. - for (j = 0; j < 3; j++) { - sspivot(faceloop, checkseg); - // Loop two vertices of the edge. - for (k = 0; k < 2; k++) { - if (k == 0) pa = sorg(faceloop); - else pa = sdest(faceloop); - if (worklist[pointmark(pa)] == 0) { - pb = (point) NULL; - if (checkseg.sh != dummysh) { - // pa is on a segment. Find pb. - // Find the incident pbcgroup of checkseg. - idx = shellmark(checkseg) - 1; - for (l = idx2segpglist[idx]; l < idx2segpglist[idx + 1]; - l++) { - pd = (pbcdata *)(* segpbcgrouptable)[segpglist[l]]; - if (((pd->fmark[0] == pgi->fmark1) && - (pd->fmark[1] == pgi->fmark2)) || - ((pd->fmark[0] == pgi->fmark2) && - (pd->fmark[1] == pgi->fmark1))) break; - } -#ifdef SELF_CHECK - assert(l < idx2segpglist[idx + 1]); -#endif - loc = getsegpbcsympoint(pa, &checkseg, sympt, &symseg, - segpglist[l]); - if (loc != ONVERTEX) { - // Not found a match point! It may be caused by the - // pair of input vertices don't have enough digits. - // Choose a near vertex. - d1 = distance(sympt, sorg(symseg)); - d2 = distance(sympt, sdest(symseg)); - if (d1 > d2) sesymself(symseg); - } - pb = sorg(symseg); - } else { - // Operate on pa if it is inside the facet. - if (pointtype(pa) == FREESUBVERTEX) { - pb = point2pbcpt(pa); - } - } - if (pb != (point) NULL) { - // Add the pair (pa, pb) into list. - ptpair = (point *) ptpairlist->append(NULL); - ptpair[0] = pa; - ptpair[1] = pb; - // Mark pa (avoid to operate on it later). - worklist[pointmark(pa)] = 1; - } - } - } - // Get the next edge. - senextself(faceloop); - } - } - } - faceloop.sh = shellfacetraverse(subfaces); - } - - // Output the list of pbc points. - if (out == (tetgenio *) NULL) { - fprintf(outfile, "%d\n", ptpairlist->len()); - } else { - pgo->numberofpointpairs = ptpairlist->len(); - pgo->pointpairlist = new int[pgo->numberofpointpairs * 2]; - index = 0; - } - for (j = 0; j < ptpairlist->len(); j++) { - ptpair = (point *)(* ptpairlist)[j]; - pa = ptpair[0]; - pb = ptpair[1]; - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %4d %4d\n", pointmark(pa) - shift, - pointmark(pb) - shift); - } else { - pgo->pointpairlist[index++] = pointmark(pa) - shift; - pgo->pointpairlist[index++] = pointmark(pb) - shift; - } - // Unmark pa. - worklist[pointmark(pa)] = 0; - } - if (out == (tetgenio *) NULL) { - fprintf(outfile, "\n"); - } - ptpairlist->clear(); - } - - delete [] worklist; - delete ptpairlist; - - if (out == (tetgenio *) NULL) { - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outsmesh() Write surface mesh to a .smesh file, which can be read and // -// tetrahedralized by TetGen. // -// // -// You can specify a filename (without suffix) in 'smfilename'. If you don't // -// supply a filename (let smfilename be NULL), the default name stored in // -// 'tetgenbehavior' will be used. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outsmesh(char* smfilename) -{ - FILE *outfile; - char nodfilename[FILENAMESIZE]; - char smefilename[FILENAMESIZE]; - face faceloop; - point p1, p2, p3; - int firstindex, shift; - int bmark; - int faceid, marker; - int i; - - if (smfilename != (char *) NULL && smfilename[0] != '\0') { - strcpy(smefilename, smfilename); - } else if (b->outfilename[0] != '\0') { - strcpy(smefilename, b->outfilename); - } else { - strcpy(smefilename, "unnamed"); - } - strcpy(nodfilename, smefilename); - strcat(smefilename, ".smesh"); - strcat(nodfilename, ".node"); - - if (!b->quiet) { - printf("Writing %s.\n", smefilename); - } - outfile = fopen(smefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", smefilename); - return; - } - - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - shift = 0; // Default no shiftment. - if ((in->firstnumber == 1) && (firstindex == 0)) { - shift = 1; // Shift the output indices by 1. - } - - fprintf(outfile, "# %s. TetGen's input file.\n", smefilename); - fprintf(outfile, "\n# part 1: node list.\n"); - fprintf(outfile, "0 3 0 0 # nodes are found in %s.\n", nodfilename); - - marker = 0; // avoid compile warning. - bmark = !b->nobound && in->facetmarkerlist; - - fprintf(outfile, "\n# part 2: facet list.\n"); - // Number of facets, boundary marker. - fprintf(outfile, "%ld %d\n", subfaces->items, bmark); - - subfaces->traversalinit(); - faceloop.sh = shellfacetraverse(subfaces); - while (faceloop.sh != (shellface *) NULL) { - p1 = sorg(faceloop); - p2 = sdest(faceloop); - p3 = sapex(faceloop); - if (bmark) { - faceid = shellmark(faceloop) - 1; - if (faceid >= 0) { - marker = in->facetmarkerlist[faceid]; - } else { - marker = 0; // This subface must be added manually later. - } - } - fprintf(outfile, "3 %4d %4d %4d", pointmark(p1) - shift, - pointmark(p2) - shift, pointmark(p3) - shift); - if (bmark) { - fprintf(outfile, " %d", marker); - } - fprintf(outfile, "\n"); - faceloop.sh = shellfacetraverse(subfaces); - } - - // Copy input holelist. - fprintf(outfile, "\n# part 3: hole list.\n"); - fprintf(outfile, "%d\n", in->numberofholes); - for (i = 0; i < in->numberofholes; i++) { - fprintf(outfile, "%d %g %g %g\n", i + in->firstnumber, - in->holelist[i * 3], in->holelist[i * 3 + 1], - in->holelist[i * 3 + 2]); - } - - // Copy input regionlist. - fprintf(outfile, "\n# part 4: region list.\n"); - fprintf(outfile, "%d\n", in->numberofregions); - for (i = 0; i < in->numberofregions; i++) { - fprintf(outfile, "%d %g %g %g %d %g\n", i + in->firstnumber, - in->regionlist[i * 5], in->regionlist[i * 5 + 1], - in->regionlist[i * 5 + 2], (int) in->regionlist[i * 5 + 3], - in->regionlist[i * 5 + 4]); - } - - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outmesh2medit() Write mesh to a .mesh file, which can be read and // -// rendered by Medit (a free mesh viewer from INRIA). // -// // -// You can specify a filename (without suffix) in 'mfilename'. If you don't // -// supply a filename (let mfilename be NULL), the default name stored in // -// 'tetgenbehavior' will be used. The output file will have the suffix .mesh.// -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outmesh2medit(char* mfilename) -{ - FILE *outfile; - char mefilename[FILENAMESIZE]; - tetrahedron* tetptr; - triface tface, tsymface; - face segloop, checkmark; - point ptloop, p1, p2, p3, p4; - long faces; - int pointnumber; - int i; - - if (mfilename != (char *) NULL && mfilename[0] != '\0') { - strcpy(mefilename, mfilename); - } else if (b->outfilename[0] != '\0') { - strcpy(mefilename, b->outfilename); - } else { - strcpy(mefilename, "unnamed"); - } - strcat(mefilename, ".mesh"); - - if (!b->quiet) { - printf("Writing %s.\n", mefilename); - } - outfile = fopen(mefilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", mefilename); - return; - } - - fprintf(outfile, "MeshVersionFormatted 1\n"); - fprintf(outfile, "\n"); - fprintf(outfile, "Dimension\n"); - fprintf(outfile, "3\n"); - fprintf(outfile, "\n"); - - fprintf(outfile, "\n# Set of mesh vertices\n"); - fprintf(outfile, "Vertices\n"); - fprintf(outfile, "%ld\n", points->items); - - points->traversalinit(); - ptloop = pointtraverse(); - pointnumber = 1; // Medit need start number form 1. - while (ptloop != (point) NULL) { - // Point coordinates. - fprintf(outfile, "%.17g %.17g %.17g", ptloop[0], ptloop[1], ptloop[2]); - if (in->numberofpointattributes > 0) { - // Write an attribute, ignore others if more than one. - fprintf(outfile, " %.17g\n", ptloop[3]); - } else { - fprintf(outfile, " 0\n"); - } - setpointmark(ptloop, pointnumber); - ptloop = pointtraverse(); - pointnumber++; - } - - // Compute the number of edges. - faces = (4l * tetrahedrons->items + hullsize) / 2l; - - fprintf(outfile, "\n# Set of Triangles\n"); - fprintf(outfile, "Triangles\n"); - fprintf(outfile, "%ld\n", faces); - - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - // To loop over the set of faces, loop over all tetrahedra, and look at - // the four faces of each tetrahedron. If there isn't another tetrahedron - // adjacent to the face, operate on the face. If there is another adj- - // acent tetrahedron, operate on the face only if the current tetrahedron - // has a smaller pointer than its neighbor. This way, each face is - // considered only once. - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if (tface.tet < tsymface.tet || tsymface.tet == dummytet) { - p1 = org (tface); - p2 = dest(tface); - p3 = apex(tface); - fprintf(outfile, "%5d %5d %5d", - pointmark(p1), pointmark(p2), pointmark(p3)); - fprintf(outfile, " 0\n"); - } - } - tface.tet = tetrahedrontraverse(); - } - - fprintf(outfile, "\n# Set of Tetrahedra\n"); - fprintf(outfile, "Tetrahedra\n"); - fprintf(outfile, "%ld\n", tetrahedrons->items); - - tetrahedrons->traversalinit(); - tetptr = tetrahedrontraverse(); - while (tetptr != (tetrahedron *) NULL) { - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; - p3 = (point) tetptr[6]; - p4 = (point) tetptr[7]; - fprintf(outfile, "%5d %5d %5d %5d", - pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); - if (in->numberoftetrahedronattributes > 0) { - fprintf(outfile, " %.17g", elemattribute(tetptr, 0)); - } else { - fprintf(outfile, " 0"); - } - fprintf(outfile, "\n"); - tetptr = tetrahedrontraverse(); - } - - fprintf(outfile, "\nCorners\n"); - fprintf(outfile, "%d\n", in->numberofpoints); - - for (i = 0; i < in->numberofpoints; i++) { - fprintf(outfile, "%4d\n", i + 1); - } - - if (b->useshelles) { - fprintf(outfile, "\nEdges\n"); - fprintf(outfile, "%ld\n", subsegs->items); - - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - p1 = sorg(segloop); - p2 = sdest(segloop); - fprintf(outfile, "%5d %5d", pointmark(p1), pointmark(p2)); - fprintf(outfile, " 0\n"); - segloop.sh = shellfacetraverse(subsegs); - } - } - - fprintf(outfile, "\nEnd\n"); - fclose(outfile); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outmesh2gid() Write mesh to a .ele.msh file and a .face.msh file, // -// which can be imported and rendered by Gid. // -// // -// You can specify a filename (without suffix) in 'gfilename'. If you don't // -// supply a filename (let gfilename be NULL), the default name stored in // -// 'tetgenbehavior' will be used. The suffixes (.ele.msh and .face.msh) will // -// be automatically added. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outmesh2gid(char* gfilename) -{ - FILE *outfile; - char gidfilename[FILENAMESIZE]; - tetrahedron* tetptr; - triface tface, tsymface; - face sface; - point ptloop, p1, p2, p3, p4; - int pointnumber; - int elementnumber; - - if (gfilename != (char *) NULL && gfilename[0] != '\0') { - strcpy(gidfilename, gfilename); - } else if (b->outfilename[0] != '\0') { - strcpy(gidfilename, b->outfilename); - } else { - strcpy(gidfilename, "unnamed"); - } - strcat(gidfilename, ".ele.msh"); - - if (!b->quiet) { - printf("Writing %s.\n", gidfilename); - } - outfile = fopen(gidfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", gidfilename); - return; - } - - fprintf(outfile, "mesh dimension = 3 elemtype tetrahedron nnode = 4\n"); - fprintf(outfile, "coordinates\n"); - - points->traversalinit(); - ptloop = pointtraverse(); - pointnumber = 1; // Gid need start number form 1. - while (ptloop != (point) NULL) { - // Point coordinates. - fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber, - ptloop[0], ptloop[1], ptloop[2]); - if (in->numberofpointattributes > 0) { - // Write an attribute, ignore others if more than one. - fprintf(outfile, " %.17g", ptloop[3]); - } - fprintf(outfile, "\n"); - setpointmark(ptloop, pointnumber); - ptloop = pointtraverse(); - pointnumber++; - } - - fprintf(outfile, "end coordinates\n"); - fprintf(outfile, "elements\n"); - - tetrahedrons->traversalinit(); - tetptr = tetrahedrontraverse(); - elementnumber = 1; - while (tetptr != (tetrahedron *) NULL) { - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; - p3 = (point) tetptr[6]; - p4 = (point) tetptr[7]; - fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, - pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); - if (in->numberoftetrahedronattributes > 0) { - fprintf(outfile, " %.17g", elemattribute(tetptr, 0)); - } - fprintf(outfile, "\n"); - tetptr = tetrahedrontraverse(); - elementnumber++; - } - - fprintf(outfile, "end elements\n"); - fclose(outfile); - - if (gfilename != (char *) NULL && gfilename[0] != '\0') { - strcpy(gidfilename, gfilename); - } else if (b->outfilename[0] != '\0') { - strcpy(gidfilename, b->outfilename); - } else { - strcpy(gidfilename, "unnamed"); - } - strcat(gidfilename, ".face.msh"); - - if (!b->quiet) { - printf("Writing %s.\n", gidfilename); - } - outfile = fopen(gidfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", gidfilename); - return; - } - - fprintf(outfile, "mesh dimension = 3 elemtype triangle nnode = 3\n"); - fprintf(outfile, "coordinates\n"); - - points->traversalinit(); - ptloop = pointtraverse(); - pointnumber = 1; // Gid need start number form 1. - while (ptloop != (point) NULL) { - // Point coordinates. - fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber, - ptloop[0], ptloop[1], ptloop[2]); - if (in->numberofpointattributes > 0) { - // Write an attribute, ignore others if more than one. - fprintf(outfile, " %.17g", ptloop[3]); - } - fprintf(outfile, "\n"); - setpointmark(ptloop, pointnumber); - ptloop = pointtraverse(); - pointnumber++; - } - - fprintf(outfile, "end coordinates\n"); - fprintf(outfile, "elements\n"); - - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - elementnumber = 1; - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) { - p1 = org(tface); - p2 = dest(tface); - p3 = apex(tface); - if (tsymface.tet == dummytet) { - // It's a hull face, output it. - fprintf(outfile, "%5d %d %d %d\n", elementnumber, - pointmark(p1), pointmark(p2), pointmark(p3)); - elementnumber++; - } else if (b->useshelles) { - // Only output it if it's a subface. - tspivot(tface, sface); - if (sface.sh != dummysh) { - fprintf(outfile, "%5d %d %d %d\n", elementnumber, - pointmark(p1), pointmark(p2), pointmark(p3)); - elementnumber++; - } - } - } - } - tface.tet = tetrahedrontraverse(); - } - - fprintf(outfile, "end elements\n"); - fclose(outfile); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// outmesh2off() Write the mesh to an .off file. // -// // -// .off, the Object File Format, is one of the popular file formats from the // -// Geometry Center's Geomview package (http://www.geomview.org). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::outmesh2off(char* ofilename) -{ - FILE *outfile; - char offfilename[FILENAMESIZE]; - triface tface, tsymface; - point ptloop, p1, p2, p3; - long faces; - int shift; - - if (ofilename != (char *) NULL && ofilename[0] != '\0') { - strcpy(offfilename, ofilename); - } else if (b->outfilename[0] != '\0') { - strcpy(offfilename, b->outfilename); - } else { - strcpy(offfilename, "unnamed"); - } - strcat(offfilename, ".off"); - - if (!b->quiet) { - printf("Writing %s.\n", offfilename); - } - outfile = fopen(offfilename, "w"); - if (outfile == (FILE *) NULL) { - printf("File I/O Error: Cannot create file %s.\n", offfilename); - return; - } - - // Calculate the number of triangular faces in the tetrahedral mesh. - faces = (4l * tetrahedrons->items + hullsize) / 2l; - - // Number of points, faces, and edges(not used, here show hullsize). - fprintf(outfile, "OFF\n%ld %ld %ld\n", points->items, faces, hullsize); - - // Write the points. - points->traversalinit(); - ptloop = pointtraverse(); - while (ptloop != (point) NULL) { - fprintf(outfile, " %.17g %.17g %.17g\n",ptloop[0], ptloop[1], ptloop[2]); - ptloop = pointtraverse(); - } - - // OFF always use zero as the first index. - shift = in->firstnumber == 1 ? 1 : 0; - - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - // To loop over the set of faces, loop over all tetrahedra, and look at - // the four faces of each tetrahedron. If there isn't another tetrahedron - // adjacent to the face, operate on the face. If there is another adj- - // acent tetrahedron, operate on the face only if the current tetrahedron - // has a smaller pointer than its neighbor. This way, each face is - // considered only once. - while (tface.tet != (tetrahedron *) NULL) { - for (tface.loc = 0; tface.loc < 4; tface.loc ++) { - sym(tface, tsymface); - if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) { - p1 = org(tface); - p2 = dest(tface); - p3 = apex(tface); - // Face number, indices of three vertexs. - fprintf(outfile, "3 %4d %4d %4d\n", pointmark(p1) - shift, - pointmark(p2) - shift, pointmark(p3) - shift); - } - } - tface.tet = tetrahedrontraverse(); - } - - fprintf(outfile, "# Generated by %s\n", b->commandline); - fclose(outfile); -} - -// -// End of I/O rouitnes -// - -// -// Begin of user interaction routines -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// internalerror() Ask the user to send me the defective product. Exit. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::internalerror() -{ - printf(" Please report this bug to sihang@mail.berlios.de. Include the\n"); - printf(" message above, your input data set, and the exact command\n"); - printf(" line you used to run this program, thank you.\n"); - terminatetetgen(2); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checkmesh() Test the mesh for topological consistency. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::checkmesh() -{ - triface tetraloop; - triface oppotet, oppooppotet; - point tetorg, tetdest, tetapex, tetoppo; - REAL oritest; - int horrors; - - if (!b->quiet) { - printf(" Checking consistency of mesh...\n"); - } - - horrors = 0; - // Run through the list of tetrahedra, checking each one. - tetrahedrons->traversalinit(); - tetraloop.tet = tetrahedrontraverse(); - while (tetraloop.tet != (tetrahedron *) NULL) { - // Check all four faces of the tetrahedron. - for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) { - tetorg = org(tetraloop); - tetdest = dest(tetraloop); - tetapex = apex(tetraloop); - tetoppo = oppo(tetraloop); - if (tetraloop.loc == 0) { // Only test for inversion once. - oritest = orient3d(tetorg, tetdest, tetapex, tetoppo); - if (oritest >= 0.0) { - printf(" !! !! %s ", oritest > 0.0 ? "Inverted" : "Degenerated"); - printtet(&tetraloop); - printf(" orient3d = %.17g.\n", oritest); - horrors++; - } - } - // Find the neighboring tetrahedron on this face. - sym(tetraloop, oppotet); - if (oppotet.tet != dummytet) { - // Check that the tetrahedron's neighbor knows it's a neighbor. - sym(oppotet, oppooppotet); - if ((tetraloop.tet != oppooppotet.tet) - || (tetraloop.loc != oppooppotet.loc)) { - printf(" !! !! Asymmetric tetra-tetra bond:\n"); - if (tetraloop.tet == oppooppotet.tet) { - printf(" (Right tetrahedron, wrong orientation)\n"); - } - printf(" First "); - printtet(&tetraloop); - printf(" Second (nonreciprocating) "); - printtet(&oppotet); - horrors++; - } - } - } - tetraloop.tet = tetrahedrontraverse(); - } - if (horrors == 0) { - if (!b->quiet) { - printf(" In my studied opinion, the mesh appears to be consistent.\n"); - } - } else if (horrors == 1) { - printf(" !! !! !! !! Precisely one festering wound discovered.\n"); - } else { - printf(" !! !! !! !! %d abominations witnessed.\n", horrors); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checkshells() Test the boundary mesh for topological consistency. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::checkshells() -{ - triface oppotet, oppooppotet, testtet; - face shloop, segloop, spin; - face testsh, testseg, testshsh; - point shorg, shdest, segorg, segdest; - REAL checksign; - bool same; - int horrors; - int i; - - if (!b->quiet) { - printf(" Checking consistency of the mesh boundary...\n"); - } - horrors = 0; - - // Run through the list of subfaces, checking each one. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - // Check two connected tetrahedra if they exist. - shloop.shver = 0; - stpivot(shloop, oppotet); - if (oppotet.tet != dummytet) { - tspivot(oppotet, testsh); - if (testsh.sh != shloop.sh) { - printf(" !! !! Wrong tetra-subface connection.\n"); - printf(" Tetra: "); - printtet(&oppotet); - printf(" Subface: "); - printsh(&shloop); - horrors++; - } - if (oppo(oppotet) != (point) NULL) { - adjustedgering(oppotet, CCW); - checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop), - oppo(oppotet)); - if (checksign >= 0.0) { - printf(" !! !! Wrong subface orientation.\n"); - printf(" Subface: "); - printsh(&shloop); - horrors++; - } - } - } - sesymself(shloop); - stpivot(shloop, oppooppotet); - if (oppooppotet.tet != dummytet) { - tspivot(oppooppotet, testsh); - if (testsh.sh != shloop.sh) { - printf(" !! !! Wrong tetra-subface connection.\n"); - printf(" Tetra: "); - printtet(&oppooppotet); - printf(" Subface: "); - printsh(&shloop); - horrors++; - } - if (oppotet.tet != dummytet) { - sym(oppotet, testtet); - if (testtet.tet != oppooppotet.tet) { - printf(" !! !! Wrong tetra-subface-tetra connection.\n"); - printf(" Tetra 1: "); - printtet(&oppotet); - printf(" Subface: "); - printsh(&shloop); - printf(" Tetra 2: "); - printtet(&oppooppotet); - horrors++; - } - } - if (oppo(oppooppotet) != (point) NULL) { - adjustedgering(oppooppotet, CCW); - checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop), - oppo(oppooppotet)); - if (checksign >= 0.0) { - printf(" !! !! Wrong subface orientation.\n"); - printf(" Subface: "); - printsh(&shloop); - horrors++; - } - } - } - // Check connection between subfaces. - shloop.shver = 0; - for (i = 0; i < 3; i++) { - shorg = sorg(shloop); - shdest = sdest(shloop); - sspivot(shloop, testseg); - if (testseg.sh != dummysh) { - segorg = sorg(testseg); - segdest = sdest(testseg); - same = ((shorg == segorg) && (shdest == segdest)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subface-subsegment connection.\n"); - printf(" Subface: "); - printsh(&shloop); - printf(" Subsegment: "); - printsh(&testseg); - horrors++; - } - } - spivot(shloop, testsh); - if (testsh.sh != dummysh) { - segorg = sorg(testsh); - segdest = sdest(testsh); - same = ((shorg == segorg) && (shdest == segdest)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subface-subface connection.\n"); - printf(" Subface 1: "); - printsh(&shloop); - printf(" Subface 2: "); - printsh(&testsh); - horrors++; - } - spivot(testsh, testshsh); - shorg = sorg(testshsh); - shdest = sdest(testshsh); - same = ((shorg == segorg) && (shdest == segdest)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subface-subface connection.\n"); - printf(" Subface 1: "); - printsh(&testsh); - printf(" Subface 2: "); - printsh(&testshsh); - horrors++; - } - if (testseg.sh == dummysh) { - if (testshsh.sh != shloop.sh) { - printf(" !! !! Wrong subface-subface connection.\n"); - printf(" Subface 1: "); - printsh(&shloop); - printf(" Subface 2: "); - printsh(&testsh); - horrors++; - } - } - } - senextself(shloop); - } - shloop.sh = shellfacetraverse(subfaces); - } - - // Run through the list of subsegs, checking each one. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - segorg = sorg(segloop); - segdest = sdest(segloop); - spivot(segloop, testsh); - if (testsh.sh == dummysh) { - printf(" !! !! Wrong subsegment-subface connection.\n"); - printf(" Subsegment: "); - printsh(&segloop); - horrors++; - segloop.sh = shellfacetraverse(subsegs); - continue; - } - shorg = sorg(testsh); - shdest = sdest(testsh); - same = ((shorg == segorg) && (shdest == segdest)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subsegment-subface connection.\n"); - printf(" Subsegment : "); - printsh(&segloop); - printf(" Subface : "); - printsh(&testsh); - horrors++; - segloop.sh = shellfacetraverse(subsegs); - continue; - } - // Check the connection of face loop around this subsegment. - spin = testsh; - i = 0; - do { - spivotself(spin); - shorg = sorg(spin); - shdest = sdest(spin); - same = ((shorg == segorg) && (shdest == segdest)) - || ((shorg == segdest) && (shdest == segorg)); - if (!same) { - printf(" !! !! Wrong subsegment-subface connection.\n"); - printf(" Subsegment : "); - printsh(&segloop); - printf(" Subface : "); - printsh(&testsh); - horrors++; - break; - } - i++; - } while (spin.sh != testsh.sh && i < 1000); - if (i >= 1000) { - printf(" !! !! Wrong subsegment-subface connection.\n"); - printf(" Subsegment : "); - printsh(&segloop); - horrors++; - } - segloop.sh = shellfacetraverse(subsegs); - } - if (horrors == 0) { - if (!b->quiet) { - printf(" Mesh boundaries connected correctly.\n"); - } - } else { - printf(" !! !! !! !! %d boundary connection viewed with horror.\n", - horrors); - return; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checkdelaunay() Ensure that the mesh is constrained Delaunay. // -// // -// If 'flipqueue' is not NULL, non-locally Delaunay faces are saved in it. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue) -{ - triface tetraloop; - triface oppotet; - face opposhelle; - point tetorg, tetdest, tetapex, tetoppo; - point oppooppo; - enum fliptype fc; - REAL sign; - int shouldbedelaunay; - int horrors; - - if (!b->quiet) { - printf(" Checking Delaunay property of the mesh...\n"); - } - horrors = 0; - // Run through the list of triangles, checking each one. - tetrahedrons->traversalinit(); - tetraloop.tet = tetrahedrontraverse(); - while (tetraloop.tet != (tetrahedron *) NULL) { - // Check all four faces of the tetrahedron. - for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) { - tetorg = org(tetraloop); - tetdest = dest(tetraloop); - tetapex = apex(tetraloop); - tetoppo = oppo(tetraloop); - sym(tetraloop, oppotet); - oppooppo = oppo(oppotet); - // Only do testif there is an adjoining tetrahedron whose pointer is - // larger (to ensure that each pair isn't tested twice). - shouldbedelaunay = (oppotet.tet != dummytet) - && (tetoppo != (point) NULL) - && (oppooppo != (point) NULL) - && (tetraloop.tet < oppotet.tet); - if (checksubfaces && shouldbedelaunay) { - // If a shell face separates the tetrahedra, then the face is - // constrained, so no local Delaunay test should be done. - tspivot(tetraloop, opposhelle); - if (opposhelle.sh != dummysh){ - shouldbedelaunay = 0; - } - } - if (shouldbedelaunay) { - sign = insphere(tetdest, tetorg, tetapex, tetoppo, oppooppo); - if ((sign > 0.0) && (eps > 0.0)) { - if (iscospheric(tetdest, tetorg, tetapex, tetoppo, oppooppo, sign, - eps)) sign = 0.0; - } - if (sign > 0.0) { - if (flipqueue) { - enqueueflipface(tetraloop, flipqueue); - } else { - printf(" !! Non-locally Delaunay face (%d, %d, %d) ", - pointmark(tetorg), pointmark(tetdest), pointmark(tetapex)); - fc = categorizeface(tetraloop); - switch (fc) { - case T23: printf("\"T23\""); break; - case T32: printf("\"T32\""); break; - case T22: printf("\"T22\""); break; - case T44: printf("\"T44\""); break; - case N32: printf("\"N32\""); break; - case N40: printf("\"N40\""); break; - case FORBIDDENFACE:printf("\"FORBIDDENFACE\""); break; - case FORBIDDENEDGE:printf("\"FORBIDDENEDGE\""); break; - } - printf("\n"); - } - horrors++; - } - } - } - tetraloop.tet = tetrahedrontraverse(); - } - if (flipqueue == (queue *) NULL) { - if (horrors == 0) { - if (!b->quiet) { - printf(" The mesh is %s.\n", - checksubfaces ? "constrained Delaunay" : "Delaunay"); - } - } else { - printf(" !! !! !! !! %d obscenities viewed with horror.\n", horrors); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checkconforming() Ensure that the mesh is conforming Delaunay. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::checkconforming() -{ - face segloop, shloop; - int encsubsegs, encsubfaces; - - if (!b->quiet) { - printf(" Checking conforming Delaunay property of mesh...\n"); - } - encsubsegs = encsubfaces = 0; - // Run through the list of subsegments, check each one. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - if (checkseg4encroach(&segloop, NULL, NULL, false)) { - printf(" !! !! Non-conforming subsegment: (%d, %d)\n", - pointmark(sorg(segloop)), pointmark(sdest(segloop))); - encsubsegs++; - } - segloop.sh = shellfacetraverse(subsegs); - } - // Run through the list of subfaces, check each one. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - if (checksub4encroach(&shloop, NULL, false)) { - printf(" !! !! Non-conforming subface: (%d, %d, %d)\n", - pointmark(sorg(shloop)), pointmark(sdest(shloop)), - pointmark(sapex(shloop))); - encsubfaces++; - } - shloop.sh = shellfacetraverse(subfaces); - } - if (encsubsegs == 0 && encsubfaces == 0) { - if (!b->quiet) { - printf(" The mesh is conforming Delaunay.\n"); - } - } else { - if (encsubsegs > 0) { - printf(" !! !! %d subsegments are non-conforming.\n", encsubsegs); - } - if (encsubfaces > 0) { - printf(" !! !! %d subfaces are non-conforming.\n", encsubfaces); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// algorithmicstatistics() Print statistics about the mesh algorithms. // -// // -/////////////////////////////////////////////////////////////////////////////// - -#ifdef SELF_CHECK - -void tetgenmesh::algorithmicstatistics() -{ - /* - printf("Algorithmic statistics:\n\n"); - printf(" Point location millisecond: %g\n", (REAL) tloctime * 1e+3); - printf(" Flip millisecond: %g\n", (REAL) tfliptime * 1e+3); - if (b->plc || b->refine) { - printf(" Number of facet above points calculations: %ld\n", abovecount); - } - if (b->plc) { - printf(" Segment split rules: R1 %ld, R2 %ld, R3 %ld\n", r1count, r2count, - r3count); - } - if (b->quality) { - printf(" Bowyer-Watson insertions: seg %ld, sub %ld, vol %ld.\n", - bowatsegcount, bowatsubcount, bowatvolcount); - printf(" Bowyer-Watson corrections: seg %ld, sub %ld, vol %ld\n", - updsegcount, updsubcount, updvolcount); - printf(" Bowyer-Watson failures: seg %ld, sub %ld, vol %ld\n", - failsegcount, failsubcount, failvolcount); - printf(" Number of repair flips: %ld.\n", repairflipcount); - printf(" Number of circumcenters outside Bowat-cav.: %ld.\n", - outbowatcircumcount); - if (b->conformdel) { - printf(" Segment split rules: R2 %ld, R3 %ld\n", r2count, r3count); - printf(" Number of CDT enforcement points: %ld.\n", cdtenforcesegpts); - } - printf(" Number of Rejections: seg %ld, sub %ld, tet %ld.\n", rejsegpts, - rejsubpts, rejtetpts); - if (b->optlevel) { - printf( - " Optimization flips: f32 %ld, f44 %ld, f56 %ld, f68 %ld, fnm %ld.\n", - optcount[3], optcount[4], optcount[5], optcount[6], optcount[9]); - printf(" Optimization segment deletions: %ld.\n", optcount[1]); - } - } - printf("\n"); - */ -} - -#endif // #ifdef SELF_CHECK - -/////////////////////////////////////////////////////////////////////////////// -// // -// qualitystatistics() Print statistics about the quality of the mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::qualitystatistics() -{ - triface tetloop, neightet; - point p[4]; - char sbuf[128]; - REAL radiusratiotable[12]; - REAL aspectratiotable[12]; - REAL A[4][4], rhs[4], D; - REAL V[6][3], N[4][3], H[4]; // edge-vectors, face-normals, face-heights. - REAL edgelength[6], alldihed[6], faceangle[3]; - REAL shortest, longest; - REAL smallestvolume, biggestvolume; - REAL smallestdiangle, biggestdiangle; - REAL smallestfaangle, biggestfaangle; - REAL tetvol, minaltitude; - REAL cirradius, minheightinv; // insradius; - REAL shortlen, longlen; - REAL tetaspect, tetradius; - REAL smalldiangle, bigdiangle; - REAL smallfaangle, bigfaangle; - int radiustable[12]; - int aspecttable[16]; - int dihedangletable[18]; - int faceangletable[18]; - int indx[4]; - int radiusindex; - int aspectindex; - int tendegree; - int i, j; - - printf("Mesh quality statistics:\n\n"); - - // Avoid compile warnings. - shortlen = longlen = 0.0; - smalldiangle = bigdiangle = 0.0; - - radiusratiotable[0] = 0.707; radiusratiotable[1] = 1.0; - radiusratiotable[2] = 1.1; radiusratiotable[3] = 1.2; - radiusratiotable[4] = 1.4; radiusratiotable[5] = 1.6; - radiusratiotable[6] = 1.8; radiusratiotable[7] = 2.0; - radiusratiotable[8] = 2.5; radiusratiotable[9] = 3.0; - radiusratiotable[10] = 10.0; radiusratiotable[11] = 0.0; - - aspectratiotable[0] = 1.5; aspectratiotable[1] = 2.0; - aspectratiotable[2] = 2.5; aspectratiotable[3] = 3.0; - aspectratiotable[4] = 4.0; aspectratiotable[5] = 6.0; - aspectratiotable[6] = 10.0; aspectratiotable[7] = 15.0; - aspectratiotable[8] = 25.0; aspectratiotable[9] = 50.0; - aspectratiotable[10] = 100.0; aspectratiotable[11] = 0.0; - - for (i = 0; i < 12; i++) radiustable[i] = 0; - for (i = 0; i < 12; i++) aspecttable[i] = 0; - for (i = 0; i < 18; i++) dihedangletable[i] = 0; - for (i = 0; i < 18; i++) faceangletable[i] = 0; - - minaltitude = xmax - xmin + ymax - ymin + zmax - zmin; - minaltitude = minaltitude * minaltitude; - shortest = minaltitude; - longest = 0.0; - smallestvolume = minaltitude; - biggestvolume = 0.0; - smallestdiangle = smallestfaangle = 180.0; - biggestdiangle = biggestfaangle = 0.0; - - // Loop all elements, calculate quality parameters for each element. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - - // Get four vertices: p0, p1, p2, p3. - for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i]; - // Set the edge vectors: V[0], ..., V[5] - for (i = 0; i < 3; i++) V[0][i] = p[0][i] - p[3][i]; // V[0]: p3->p0. - for (i = 0; i < 3; i++) V[1][i] = p[1][i] - p[3][i]; // V[1]: p3->p1. - for (i = 0; i < 3; i++) V[2][i] = p[2][i] - p[3][i]; // V[2]: p3->p2. - for (i = 0; i < 3; i++) V[3][i] = p[1][i] - p[0][i]; // V[3]: p0->p1. - for (i = 0; i < 3; i++) V[4][i] = p[2][i] - p[1][i]; // V[4]: p1->p2. - for (i = 0; i < 3; i++) V[5][i] = p[0][i] - p[2][i]; // V[5]: p2->p0. - // Set the matrix A = [V[0], V[1], V[2]]^T. - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) A[j][i] = V[j][i]; - } - // Decompose A just once. - lu_decmp(A, 3, indx, &D, 0); - // Get the tet volume. - tetvol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; - // Get the three faces normals. - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth face normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; - // Get the radius of the circumsphere. - for (i = 0; i < 3; i++) rhs[i] = 0.5 * dot(V[i], V[i]); - lu_solve(A, 3, indx, rhs, 0); - cirradius = sqrt(dot(rhs, rhs)); - // Normalize the face normals. - for (i = 0; i < 4; i++) { - // H[i] is the inverse of height of its corresponding face. - H[i] = sqrt(dot(N[i], N[i])); - for (j = 0; j < 3; j++) N[i][j] /= H[i]; - } - // Get the radius of the inscribed sphere. - // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]); - // Get the biggest H[i] (corresponding to the smallest height). - minheightinv = H[0]; - for (i = 1; i < 3; i++) { - if (H[i] > minheightinv) minheightinv = H[i]; - } - // Get the squares of the edge lengthes. - for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]); - // Get the dihedrals (in degree) at each edges. - j = 0; - for (i = 1; i < 4; i++) { - alldihed[j] = -dot(N[0], N[i]); // Edge cd, bd, bc. - if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. - else if (alldihed[j] > 1.0) alldihed[j] = 1; - alldihed[j] = acos(alldihed[j]) / PI * 180.0; - j++; - } - for (i = 2; i < 4; i++) { - alldihed[j] = -dot(N[1], N[i]); // Edge ad, ac. - if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. - else if (alldihed[j] > 1.0) alldihed[j] = 1; - alldihed[j] = acos(alldihed[j]) / PI * 180.0; - j++; - } - alldihed[j] = -dot(N[2], N[3]); // Edge ab. - if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding. - else if (alldihed[j] > 1.0) alldihed[j] = 1; - alldihed[j] = acos(alldihed[j]) / PI * 180.0; - - // Calculate the longest and shortest edge length. - for (i = 0; i < 6; i++) { - if (i == 0) { - shortlen = longlen = edgelength[i]; - } else { - shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen; - longlen = edgelength[i] > longlen ? edgelength[i] : longlen; - } - if (edgelength[i] > longest) { - longest = edgelength[i]; - } - if (edgelength[i] < shortest) { - shortest = edgelength[i]; - } - } - - // Calculate the largest and smallest volume. - if (tetvol < smallestvolume) { - smallestvolume = tetvol; - } - if (tetvol > biggestvolume) { - biggestvolume = tetvol; - } - - // Calculate the largest and smallest dihedral angles. - for (i = 0; i < 6; i++) { - if (i == 0) { - smalldiangle = bigdiangle = alldihed[i]; - } else { - smalldiangle = alldihed[i] < smalldiangle ? alldihed[i] : smalldiangle; - bigdiangle = alldihed[i] > bigdiangle ? alldihed[i] : bigdiangle; - } - if (alldihed[i] < smallestdiangle) { - smallestdiangle = alldihed[i]; - } - if (alldihed[i] > biggestdiangle) { - biggestdiangle = alldihed[i]; - } - } - // Accumulate the corresponding number in the dihedral angle histogram. - if (smalldiangle < 5.0) { - tendegree = 0; - } else if (smalldiangle >= 5.0 && smalldiangle < 10.0) { - tendegree = 1; - } else if (smalldiangle >= 80.0 && smalldiangle < 110.0) { - tendegree = 9; // Angles between 80 to 110 degree are in one entry. - } else { - tendegree = (int) (smalldiangle / 10.); - if (smalldiangle < 80.0) { - tendegree++; // In the left column. - } else { - tendegree--; // In the right column. - } - } - dihedangletable[tendegree]++; - if (bigdiangle >= 80.0 && bigdiangle < 110.0) { - tendegree = 9; // Angles between 80 to 110 degree are in one entry. - } else if (bigdiangle >= 170.0 && bigdiangle < 175.0) { - tendegree = 16; - } else if (bigdiangle >= 175.0) { - tendegree = 17; - } else { - tendegree = (int) (bigdiangle / 10.); - if (bigdiangle < 80.0) { - tendegree++; // In the left column. - } else { - tendegree--; // In the right column. - } - } - dihedangletable[tendegree]++; - - // Calulate the largest and smallest face angles. - tetloop.ver = 0; - for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) { - sym(tetloop, neightet); - // Only do the calulation once for a face. - if ((neightet.tet == dummytet) || (tetloop.tet < neightet.tet)) { - p[0] = org(tetloop); - p[1] = dest(tetloop); - p[2] = apex(tetloop); - faceangle[0] = interiorangle(p[0], p[1], p[2], NULL); - faceangle[1] = interiorangle(p[1], p[2], p[0], NULL); - faceangle[2] = PI - (faceangle[0] + faceangle[1]); - // Translate angles into degrees. - for (i = 0; i < 3; i++) { - faceangle[i] = (faceangle[i] * 180.0) / PI; - } - // Calculate the largest and smallest face angles. - for (i = 0; i < 3; i++) { - if (i == 0) { - smallfaangle = bigfaangle = faceangle[i]; - } else { - smallfaangle = faceangle[i] < smallfaangle ? - faceangle[i] : smallfaangle; - bigfaangle = faceangle[i] > bigfaangle ? faceangle[i] : bigfaangle; - } - if (faceangle[i] < smallestfaangle) { - smallestfaangle = faceangle[i]; - } - if (faceangle[i] > biggestfaangle) { - biggestfaangle = faceangle[i]; - } - } - tendegree = (int) (smallfaangle / 10.); - faceangletable[tendegree]++; - tendegree = (int) (bigfaangle / 10.); - faceangletable[tendegree]++; - } - } - - // Calculate aspect ratio and radius-edge ratio for this element. - tetradius = cirradius / sqrt(shortlen); - // tetaspect = sqrt(longlen) / (2.0 * insradius); - tetaspect = sqrt(longlen) * minheightinv; - aspectindex = 0; - while ((tetaspect > aspectratiotable[aspectindex]) && (aspectindex < 11)) { - aspectindex++; - } - aspecttable[aspectindex]++; - radiusindex = 0; - while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) { - radiusindex++; - } - radiustable[radiusindex]++; - - tetloop.tet = tetrahedrontraverse(); - } - - shortest = sqrt(shortest); - longest = sqrt(longest); - minaltitude = sqrt(minaltitude); - - printf(" Smallest volume: %16.5g | Largest volume: %16.5g\n", - smallestvolume, biggestvolume); - printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n", - shortest, longest); - sprintf(sbuf, "%.17g", biggestfaangle); - if (strlen(sbuf) > 8) { - sbuf[8] = '\0'; - } - printf(" Smallest facangle: %14.5g | Largest facangle: %s\n", - smallestfaangle, sbuf); - sprintf(sbuf, "%.17g", biggestdiangle); - if (strlen(sbuf) > 8) { - sbuf[8] = '\0'; - } - printf(" Smallest dihedral: %14.5g | Largest dihedral: %s\n\n", - smallestdiangle, sbuf); - - /* - printf(" Radius-edge ratio histogram:\n"); - printf(" < %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - radiusratiotable[0], radiustable[0], radiusratiotable[5], - radiusratiotable[6], radiustable[6]); - for (i = 1; i < 5; i++) { - printf(" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - radiusratiotable[i - 1], radiusratiotable[i], radiustable[i], - radiusratiotable[i + 5], radiusratiotable[i + 6], - radiustable[i + 6]); - } - printf(" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n", - radiusratiotable[4], radiusratiotable[5], radiustable[5], - radiusratiotable[10], radiustable[11]); - printf(" (A tetrahedron's radius-edge ratio is its radius of "); - printf("circumsphere divided\n"); - printf(" by its shortest edge length)\n\n"); - */ - - printf(" Aspect ratio histogram:\n"); - printf(" < %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - aspectratiotable[0], aspecttable[0], aspectratiotable[5], - aspectratiotable[6], aspecttable[6]); - for (i = 1; i < 5; i++) { - printf(" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", - aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i], - aspectratiotable[i + 5], aspectratiotable[i + 6], - aspecttable[i + 6]); - } - printf(" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n", - aspectratiotable[4], aspectratiotable[5], aspecttable[5], - aspectratiotable[10], aspecttable[11]); - printf(" (A tetrahedron's aspect ratio is its longest edge length"); - printf(" divided by its\n"); - printf(" smallest side height)\n\n"); - - printf(" Face angle histogram:\n"); - for (i = 0; i < 9; i++) { - printf(" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n", - i * 10, i * 10 + 10, faceangletable[i], - i * 10 + 90, i * 10 + 100, faceangletable[i + 9]); - } - if (minfaceang != PI) { - printf(" Minimum input face angle is %g (degree).\n", - minfaceang / PI * 180.0); - } - printf("\n"); - - printf(" Dihedral angle histogram:\n"); - // Print the three two rows: - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - 0, 5, dihedangletable[0], 80, 110, dihedangletable[9]); - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - 5, 10, dihedangletable[1], 110, 120, dihedangletable[10]); - // Print the third to seventh rows. - for (i = 2; i < 7; i++) { - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - (i - 1) * 10, (i - 1) * 10 + 10, dihedangletable[i], - (i - 1) * 10 + 110, (i - 1) * 10 + 120, dihedangletable[i + 9]); - } - // Print the last two rows. - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - 60, 70, dihedangletable[7], 170, 175, dihedangletable[16]); - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", - 70, 80, dihedangletable[8], 175, 180, dihedangletable[17]); - if (minfacetdihed != PI) { - printf(" Minimum input facet dihedral angle is %g (degree).\n", - minfacetdihed / PI * 180.0); - } - printf("\n"); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// statistics() Print all sorts of cool facts. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::statistics() -{ - printf("\nStatistics:\n\n"); - printf(" Input points: %d\n", in->numberofpoints + jettisoninverts); - if (b->refine) { - printf(" Input tetrahedra: %d\n", in->numberoftetrahedra); - } - if (b->plc) { - printf(" Input facets: %d\n", in->numberoffacets); - printf(" Input segments: %ld\n", insegments); - printf(" Input holes: %d\n", in->numberofholes); - printf(" Input regions: %d\n", in->numberofregions); - } - - printf("\n Mesh points: %ld\n", points->items); - printf(" Mesh tetrahedra: %ld\n", tetrahedrons->items); - if (b->plc || b->refine) { - printf(" Mesh triangles: %ld\n", (4l*tetrahedrons->items+hullsize)/2l); - } - if (b->plc || b->refine) { - printf(" Mesh subfaces: %ld\n", subfaces->items); - printf(" Mesh subsegments: %ld\n\n", subsegs->items); - } else { - printf(" Convex hull triangles: %ld\n\n", hullsize); - } - if (b->verbose > 0) { - qualitystatistics(); - unsigned long totalmeshbytes; - printf("Memory allocation statistics:\n\n"); - printf(" Maximum number of vertices: %ld\n", points->maxitems); - totalmeshbytes = points->maxitems * points->itembytes; - printf(" Maximum number of tetrahedra: %ld\n", tetrahedrons->maxitems); - totalmeshbytes += tetrahedrons->maxitems * tetrahedrons->itembytes; - if (subfaces != (memorypool *) NULL) { - printf(" Maximum number of subfaces: %ld\n", subfaces->maxitems); - totalmeshbytes += subfaces->maxitems * subfaces->itembytes; - } - if (subsegs != (memorypool *) NULL) { - printf(" Maximum number of segments: %ld\n", subsegs->maxitems); - totalmeshbytes += subsegs->maxitems * subsegs->itembytes; - } - printf(" Approximate heap memory used by the mesh (K bytes): %g\n\n", - (double) totalmeshbytes / 1024.0); -#ifdef SELF_CHECK - algorithmicstatistics(); -#endif - } -} - -// -// End of user interaction routines -// - -// -// Begin of constructor and destructor of tetgenmesh -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// ~tetgenmesh() Deallocte memory occupied by a tetgenmesh object. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::~tetgenmesh() -{ - bgm = (tetgenmesh *) NULL; - in = (tetgenio *) NULL; - b = (tetgenbehavior *) NULL; - - if (tetrahedrons != (memorypool *) NULL) { - delete tetrahedrons; - } - if (subfaces != (memorypool *) NULL) { - delete subfaces; - } - if (subsegs != (memorypool *) NULL) { - delete subsegs; - } - if (points != (memorypool *) NULL) { - delete points; - } - if (dummytetbase != (tetrahedron *) NULL) { - delete [] dummytetbase; - } - if (dummyshbase != (shellface *) NULL) { - delete [] dummyshbase; - } - if (facetabovepointarray != (point *) NULL) { - delete [] facetabovepointarray; - } - if (highordertable != (point *) NULL) { - delete [] highordertable; - } - if (subpbcgrouptable != (pbcdata *) NULL) { - delete [] subpbcgrouptable; - } - if (segpbcgrouptable != (list *) NULL) { - delete segpbcgrouptable; - delete [] idx2segpglist; - delete [] segpglist; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgenmesh() Initialize a tetgenmesh object. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::tetgenmesh() -{ - bgm = (tetgenmesh *) NULL; - in = (tetgenio *) NULL; - b = (tetgenbehavior *) NULL; - - tetrahedrons = (memorypool *) NULL; - subfaces = (memorypool *) NULL; - subsegs = (memorypool *) NULL; - points = (memorypool *) NULL; - badsubsegs = (memorypool *) NULL; - badsubfaces = (memorypool *) NULL; - badtetrahedrons = (memorypool *) NULL; - flipstackers = (memorypool *) NULL; - - dummytet = (tetrahedron *) NULL; - dummytetbase = (tetrahedron *) NULL; - dummysh = (shellface *) NULL; - dummyshbase = (shellface *) NULL; - - facetabovepointarray = (point *) NULL; - abovepoint = (point) NULL; - highordertable = (point *) NULL; - subpbcgrouptable = (pbcdata *) NULL; - segpbcgrouptable = (list *) NULL; - idx2segpglist = (int *) NULL; - segpglist = (int *) NULL; - - xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - longest = 0.0; - hullsize = 0l; - insegments = 0l; - pointmtrindex = 0; - pointmarkindex = 0; - point2simindex = 0; - point2pbcptindex = 0; - highorderindex = 0; - elemattribindex = 0; - volumeboundindex = 0; - shmarkindex = 0; - areaboundindex = 0; - checksubfaces = 0; - checksubsegs = 0; - checkpbcs = 0; - varconstraint = 0; - nonconvex = 0; - dupverts = 0; - unuverts = 0; - relverts = 0; - suprelverts = 0; - collapverts = 0; - unsupverts = 0; - jettisoninverts = 0; - symbolic = 1; - samples = 0l; - randomseed = 1l; - macheps = 0.0; - minfaceang = minfacetdihed = PI; - maxcavfaces = maxcavverts = 0; - expcavcount = 0; - abovecount = 0l; - bowatvolcount = bowatsubcount = bowatsegcount = 0l; - updvolcount = updsubcount = updsegcount = 0l; - repairflipcount = 0l; - outbowatcircumcount = 0l; - failvolcount = failsubcount = failsegcount = 0l; - r1count = r2count = r3count = 0l; - cdtenforcesegpts = 0l; - rejsegpts = rejsubpts = rejtetpts = 0l; - flip23s = flip32s = flip22s = flip44s = 0l; - tloctime = tfliptime = 0.0; -} - -// -// End of constructor and destructor of tetgenmesh -// - -// -// End of class 'tetgenmesh' implementation. -// - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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[14]; - - tv[0] = clock(); - - m.b = b; - m.in = in; - m.macheps = exactinit(); - m.steinerleft = b->steiner; - if (b->metric) { - m.bgm = new tetgenmesh(); - m.bgm->b = b; - m.bgm->in = bgmin; - m.bgm->macheps = exactinit(); - } - m.initializepools(); - m.transfernodes(); - - tv[1] = clock(); - - if (b->refine) { - m.reconstructmesh(); - } else { - m.delaunizevertices(); - } - - 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->metric) { - if (bgmin != (tetgenio *) NULL) { - m.bgm->initializepools(); - m.bgm->transfernodes(); - m.bgm->reconstructmesh(); - } else { - m.bgm->in = in; - m.bgm->initializepools(); - m.duplicatebgmesh(); - } - } - - tv[3] = clock(); - - if (!b->quiet) { - if (b->metric) { - printf("Background mesh reconstruct seconds: %g\n", - (tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (b->useshelles && !b->refine) { - m.meshsurface(); - if (b->diagnose != 1) { - m.markacutevertices(89.0); - m.incrperturbvertices(b->epsilon); - m.delaunizesegments(); - if (m.checkpbcs) { - long oldnum; - do { - oldnum = m.points->items; - m.incrperturbvertices(b->epsilon); - if (m.points->items > oldnum) { - oldnum = m.points->items; - m.delaunizesegments(); - } - } while (oldnum < m.points->items); - } - m.constrainedfacets(); - } else { - m.detectinterfaces(); - } - } - - tv[4] = clock(); - - if (!b->quiet) { - if (b->useshelles && !b->refine) { - if (b->diagnose != 1) { - printf("Segment and facet "); - } else { - printf("Intersection "); - } - printf("seconds: %g\n", (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (b->plc && !(b->diagnose == 1)) { - m.carveholes(); - } - - tv[5] = clock(); - - if (!b->quiet) { - if (b->plc && !(b->diagnose == 1)) { - printf("Hole seconds: %g\n", (tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC); - } - } - - if ((b->plc || b->refine) && !(b->diagnose == 1)) { - m.optimizemesh(false); - } - - tv[6] = clock(); - - if (!b->quiet) { - if ((b->plc || b->refine) && !(b->diagnose == 1)) { - printf("Repair seconds: %g\n", (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC); - } - } - - if ((b->plc && b->nobisect) && !(b->diagnose == 1)) { - m.removesteiners(false); - } - - tv[7] = clock(); - - if (!b->quiet) { - if ((b->plc && b->nobisect) && !(b->diagnose == 1)) { - printf("Steiner removal seconds: %g\n", - (tv[7] - tv[6]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (b->insertaddpoints && (addin != (tetgenio *) NULL)) { - if (addin->numberofpoints > 0) { - m.insertconstrainedpoints(addin); - } - } - - tv[8] = clock(); - - if (!b->quiet) { - if ((b->plc || b->refine) && (b->insertaddpoints)) { - printf("Constrained points seconds: %g\n", - (tv[8] - tv[7]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (b->metric) { - m.interpolatesizemap(); - } - - tv[9] = clock(); - - if (!b->quiet) { - if (b->metric) { - printf("Size interpolating seconds: %g\n", - (tv[9] - tv[8]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (b->coarse) { - m.removesteiners(true); - } - - tv[10] = clock(); - - if (!b->quiet) { - if (b->coarse) { - printf("Mesh coarsening seconds: %g\n", - (tv[10] - tv[9]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (b->quality) { - m.enforcequality(); - } - - tv[11] = clock(); - - if (!b->quiet) { - if (b->quality) { - printf("Quality seconds: %g\n", - (tv[11] - tv[10]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (b->quality && (b->optlevel > 0)) { - m.optimizemesh(true); - } - - tv[12] = clock(); - - if (!b->quiet) { - if (b->quality && (b->optlevel > 0)) { - printf("Optimize seconds: %g\n", - (tv[12] - tv[11]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (!b->nojettison && ((m.dupverts > 0) || (m.unuverts > 0) - || (b->refine && (in->numberofcorners == 10)))) { - m.jettisonnodes(); - } - - if (b->order > 1) { - m.highorder(); - } - - if (!b->quiet) { - printf("\n"); - } - - if (out != (tetgenio *) NULL) { - out->firstnumber = in->firstnumber; - out->mesh_dim = in->mesh_dim; - } - - if (b->nonodewritten || b->noiterationnum) { - if (!b->quiet) { - printf("NOT writing a .node file.\n"); - } - } else { - if (b->diagnose == 1) { - if (m.subfaces->items > 0l) { - m.outnodes(out); // Only output when self-intersecting faces exist. - } - } else { - m.outnodes(out); - if (b->quality || b->metric) { - // m.outmetrics(out); - } - } - } - - if (b->noelewritten) { - if (!b->quiet) { - printf("NOT writing an .ele file.\n"); - } - } else { - if (!(b->diagnose == 1)) { - if (m.tetrahedrons->items > 0l) { - m.outelements(out); - } - } - } - - if (b->nofacewritten) { - if (!b->quiet) { - printf("NOT writing an .face file.\n"); - } - } else { - if (b->facesout) { - if (m.tetrahedrons->items > 0l) { - m.outfaces(out); // Output all faces. - } - } else { - if (b->diagnose == 1) { - if (m.subfaces->items > 0l) { - m.outsubfaces(out); // Only output self-intersecting faces. - } - } else if (b->plc || b->refine) { - if (m.subfaces->items > 0l) { - m.outsubfaces(out); // Output boundary faces. - } - } else { - if (m.tetrahedrons->items > 0l) { - m.outhullfaces(out); // Output convex hull faces. - } - } - } - } - - if (m.checkpbcs) { - m.outpbcnodes(out); - } - - if (b->edgesout) { - if (b->edgesout > 1) { - m.outedges(out); // -ee, output all mesh edges. - } else { - m.outsubsegments(out); // -e, only output subsegments. - } - } - - if (!out && b->plc && - ((b->object == tetgenbehavior::OFF) || - (b->object == tetgenbehavior::PLY) || - (b->object == tetgenbehavior::STL))) { - m.outsmesh(b->outfilename); - } - - if (!out && b->meditview) { - m.outmesh2medit(b->outfilename); - } - - if (!out && b->gidview) { - m.outmesh2gid(b->outfilename); - } - - if (!out && b->geomview) { - m.outmesh2off(b->outfilename); - } - - if (b->neighout) { - m.outneighbors(out); - } - - if (b->voroout) { - m.outvoronoi(NULL); - //m.outvoronoi(out); - } - - tv[13] = clock(); - - if (!b->quiet) { - printf("\nOutput seconds: %g\n", - (tv[13] - tv[12]) / (REAL) CLOCKS_PER_SEC); - printf("Total running seconds: %g\n", - (tv[13] - tv[0]) / (REAL) CLOCKS_PER_SEC); - } - - if (b->docheck) { - m.checkmesh(); - if (m.checksubfaces) { - m.checkshells(); - } - if (b->docheck > 1) { - m.checkdelaunay(0.0, NULL); - if (b->docheck > 2) { - if (b->quality || b->refine) { - m.checkconforming(); - } - } - } - } - - if (!b->quiet) { - m.statistics(); - } - - if (b->metric) { - delete m.bgm; - } -} - -#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; - } - } - - 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 -} diff --git a/contrib/Tetgen1.4/tetgen.h b/contrib/Tetgen1.4/tetgen.h deleted file mode 100644 index 74106f92601af6ca5b6458882f7f7c1802d0aae5..0000000000000000000000000000000000000000 --- a/contrib/Tetgen1.4/tetgen.h +++ /dev/null @@ -1,1916 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// TetGen // -// // -// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // -// // -// Version 1.4 // -// April 16, 2007 // -// // -// Copyright (C) 2002--2007 // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// TetGen computes Delaunay tetrahedralizations, constrained Delaunay tetra- // -// hedralizations, and quality Delaunay tetrahedral meshes. The latter are // -// nicely graded and whose tetrahedra have radius-edge ratio bounded. Such // -// meshes are suitable for finite element and finite volume methods. // -// // -// TetGen incorporates a suit of geometrical and mesh generation algorithms. // -// A brief description of algorithms used in TetGen is found in the first // -// section of the user's manual. References are given for users who are // -// interesting in these approaches. The main references are given below: // -// // -// The efficient Delaunay tetrahedralization algorithm is: H. Edelsbrunner // -// and N. R. Shah, "Incremental Topological Flipping Works for Regular // -// Triangulations". Algorithmica 15: 223--241, 1996. // -// // -// The constrained Delaunay tetrahedralization algorithm is described in: // -// H. Si and K. Gaertner, "Meshing Piecewise Linear Complexes by Constr- // -// ained Delaunay Tetrahedralizations". In Proceeding of the 14th Inter- // -// national Meshing Roundtable. September 2005. // -// // -// The mesh refinement algorithm is from: Hang Si, "Adaptive Tetrahedral // -// Mesh Generation by Constrained Delaunay Refinement". WIAS Preprint No. // -// 1176, Berlin 2006. // -// // -// The mesh data structure of TetGen is a combination of two types of mesh // -// data structures. The tetrahedron-based mesh data structure introduced // -// by Shewchuk is eligible for tetrahedralization algorithms. The triangle // -// -edge data structure developed by Muecke is adopted for representing // -// boundary elements: subfaces and subsegments. // -// // -// J. R. Shewchuk, "Delaunay Refinement Mesh Generation". PhD thesis, // -// Carnegie Mellon University, Pittsburgh, PA, 1997. // -// // -// E. P. Muecke, "Shapes and Implementations in Three-Dimensional // -// Geometry". PhD thesis, Univ. of Illinois, Urbana, Illinois, 1993. // -// // -// The research of mesh generation is definitly on the move. Many State-of- // -// the-art algorithms need implementing and evaluating. I heartily welcome // -// any new algorithm especially for generating quality conforming Delaunay // -// meshes and anisotropic conforming Delaunay meshes. // -// // -// TetGen is supported by the "pdelib" project of Weierstrass Institute for // -// Applied Analysis and Stochastics (WIAS) in Berlin. It is a collection // -// of software components for solving non-linear partial differential // -// equations including 2D and 3D mesh generators, sparse matrix solvers, // -// and scientific visualization tools, etc. For more information please // -// visit: http://www.wias-berlin.de/software/pdelib. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgen.h // -// // -// Header file of the TetGen library. Also is the user-level header file. // -// // -/////////////////////////////////////////////////////////////////////////////// - -// Here are the most general used head files for C/C++ programs. - -#include <stdio.h> // Standard IO: FILE, NULL, EOF, printf(), ... -#include <stdlib.h> // Standard lib: abort(), system(), getenv(), ... -#include <string.h> // String lib: strcpy(), strcat(), strcmp(), ... -#include <math.h> // Math lib: sin(), sqrt(), pow(), ... -#include <time.h> // Defined type clock_t, constant CLOCKS_PER_SEC. -#include <assert.h> - -/////////////////////////////////////////////////////////////////////////////// -// // -// TetGen Library Overview // -// // -// TetGen library is comprised by several data types and global functions. // -// // -// There are three main data types: tetgenio, tetgenbehavior, and tetgenmesh.// -// Tetgenio is used to pass data into and out of TetGen library; tetgenbeha- // -// vior keeps the runtime options and thus controls the behaviors of TetGen; // -// tetgenmesh, the biggest data type I've ever defined, contains mesh data // -// structures and mesh traversing and transformation operators. The meshing // -// algorithms are implemented on top of it. These data types are defined as // -// C++ classes. // -// // -// There are few global functions. tetrahedralize() is provided for calling // -// TetGen from another program. Two functions: orient3d() and insphere() are // -// incorporated from a public C code provided by Shewchuk. They performing // -// exact geometrical tests. // -// // -/////////////////////////////////////////////////////////////////////////////// - -#ifndef tetgenH -#define tetgenH - -// To compile TetGen as a library instead of an executable program, define -// the TETLIBRARY symbol. - -// #define TETLIBRARY - -// Uncomment the following line to disable assert macros. These macros 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 - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgenio Passing data into and out of the library of TetGen. // -// // -// The tetgenio data structure is actually a collection of arrays of points, // -// facets, tetrahedra, and so forth. The library will read and write these // -// arrays according to the options specified in tetgenbehavior structure. // -// // -// If you want to program with the library of TetGen, it's necessary for you // -// to understand this data type,while the other two structures can be hidden // -// through calling the global function "tetrahedralize()". Each array corre- // -// sponds to a list of data in the file formats of TetGen. It is necessary // -// to understand TetGen's input/output file formats (see user's manual). // -// // -// 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 initialized be '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. // -// Other routines are provided mainly for debugging purpose. // -// // -/////////////////////////////////////////////////////////////////////////////// - -class tetgenio { - - public: - - // Maximum number of characters in a file name (including the null). - enum {FILENAMESIZE = 1024}; - - // Maxi. numbers of chars in a line read from a file (incl. the null). - enum {INPUTLINESIZE = 1024}; - - // The polygon data structure. A "polygon" is a planar polygon. It can - // be arbitrary shaped (convex or non-convex) and bounded by non- - // crossing segments, i.e., the number of vertices it has indictes the - // same number of edges. - // 'vertexlist' is a list of vertex indices (integers), its length is - // indicated by 'numberofvertices'. The vertex indices are 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 data structure. A "facet" is a planar facet. It is used - // to represent a planar straight line graph (PSLG) in two dimension. - // A PSLG contains a list of polygons. It also may conatin holes in it, - // indicated by a list of hole points (their coordinates). - 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': An array of point coordinates. The first point's x - // coordinate is at index [0] and its y coordinate at index [1], its - // z coordinate is at index [2], followed by the coordinates of the - // remaining points. Each point occupies three REALs. - // 'pointattributelist': An array of point attributes. Each point's - // attributes occupy 'numberofpointattributes' REALs. - // 'pointmtrlist': An array of metric tensors at points. Each point's - // tensor occupies 'numberofpointmtr' REALs. - // `pointmarkerlist': An array of point markers; one int per point. - REAL *pointlist; - REAL *pointattributelist; - REAL *pointmtrlist; - int *pointmarkerlist; - int numberofpoints; - int numberofpointattributes; - int numberofpointmtrs; - - // `elementlist': An array of element (triangle or tetrahedron) corners. - // The first element's first corner is at index [0], followed by its - // other corners in counterclockwise order, followed by any other - // nodes if the element represents a nonlinear element. Each element - // occupies `numberofcorners' ints. - // `elementattributelist': An array of element attributes. Each - // element's attributes occupy `numberofelementattributes' REALs. - // `elementconstraintlist': An array of constraints, i.e. triangle's - // area or tetrahedron's volume; one REAL per element. Input only. - // `neighborlist': An array of element neighbors; 3 or 4 ints per - // element. Output only. - int *tetrahedronlist; - REAL *tetrahedronattributelist; - REAL *tetrahedronvolumelist; - int *neighborlist; - int numberoftetrahedra; - int numberofcorners; - int numberoftetrahedronattributes; - - // `facetlist': An array of facets. Each entry is a structure of facet. - // `facetmarkerlist': An array of facet markers; one int per facet. - facet *facetlist; - int *facetmarkerlist; - int numberoffacets; - - // `holelist': An array of holes. The first hole's x, y and z - // coordinates are at indices [0], [1] and [2], followed by the - // remaining holes. Three REALs per hole. - REAL *holelist; - int numberofholes; - - // `regionlist': An array of regional attributes and volume constraints. - // The first constraint's x, y and z coordinates are at indices [0], - // [1] and [2], followed by the regional attribute at index [3], foll- - // owed by the maximum volume at index [4]. Five REALs per constraint. - // Note that each regional attribute is used only if you select the `A' - // switch, and each volume constraint is used only if you select the - // `a' switch (with no number following). - REAL *regionlist; - int numberofregions; - - // `facetconstraintlist': An array of facet 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': An 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': An array of periodic boundary condition groups. - pbcgroup *pbcgrouplist; - int numberofpbcgroups; - - // `trifacelist': An array of triangular face endpoints. The first - // face's endpoints are at indices [0], [1] and [2], followed by the - // remaining faces. Three ints per face. - // `adjtetlist': An array of adjacent tetrahedra to the faces of - // trifacelist. Each face has at most two adjacent tets, the first - // face's adjacent tets are at [0], [1]. Two ints per face. A '-1' - // indicates outside (no adj. tet). This list is output when '-nn' - // switch is used. - // `trifacemarkerlist': An array of face markers; one int per face. - int *trifacelist; - int *adjtetlist; - int *trifacemarkerlist; - int numberoftrifaces; - - // `edgelist': An array of edge endpoints. The first edge's endpoints - // are at indices [0] and [1], followed by the remaining edges. Two - // ints per edge. - // `edgemarkerlist': An array of edge markers; one int per edge. - int *edgelist; - int *edgemarkerlist; - int numberofedges; - - // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). - // 'vedgelist': An array of Voronoi edges. Each entry is a 'voroedge'. - // 'vfacetlist': An array of Voronoi facets. Each entry is a 'vorofacet'. - // 'vcelllist': An array of Voronoi cells. Each entry is an array of - // indices pointing into 'vfacetlist'. The 0th entry is used to store - // the length of this array. - REAL *vpointlist; - voroedge *vedgelist; - vorofacet *vfacetlist; - int **vcelllist; - int numberofvpoints; - int numberofvedges; - int numberofvfacets; - int numberofvcells; - - public: - - // Initialize routine. - void initialize(); - void deinitialize(); - - // Input & output routines. - bool load_node_call(FILE* infile, int markers, char* nodefilename); - bool load_node(char* filename); - bool load_pbc(char* filename); - bool load_var(char* filename); - bool load_mtr(char* filename); - bool load_poly(char* filename); - bool load_off(char* filename); - bool load_ply(char* filename); - bool load_stl(char* filename); - bool load_medit(char* filename); - bool load_plc(char* filename, int object); - bool load_tetmesh(char* filename); - bool load_voronoi(char* filename); - void save_nodes(char* filename); - void save_elements(char* filename); - void save_faces(char* filename); - void save_edges(char* filename); - void save_neighbors(char* filename); - void save_poly(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, 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. // -// // -// parse_commandline() provides an simple interface to set the vaules of the // -// variables. It accepts the standard parameters (e.g., 'argc' and 'argv') // -// that pass to C/C++ main() function. Alternatively a string which contains // -// the command line options can be used as its parameter. // -// // -// You don't need to understand this data type. It can be implicitly called // -// by the global function "tetrahedralize()" defined below. The necessary // -// thing you need to know is the meaning of command line switches of TetGen. // -// They are described in the third section of the user's manual. // -// // -/////////////////////////////////////////////////////////////////////////////// - -class tetgenbehavior { - - public: - - // Labels define the objects which are acceptable by TetGen. They are - // recognized by the file extensions. - // - 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 object. - - enum objecttype {NONE, NODES, POLY, OFF, PLY, STL, MEDIT, MESH}; - - // Variables of command line switches. Each variable corresponds to a - // switch and will be initialized. The meanings of these switches - // are explained in the user's manul. - - int plc; // '-p' switch, 0. - int quality; // '-q' switch, 0. - int refine; // '-r' switch, 0. - int coarse; // '-R' switch, 0. - int metric; // '-m' switch, 0. - int varvolume; // '-a' switch without number, 0. - int fixedvolume; // '-a' switch with number, 0. - int insertaddpoints; // '-i' switch, 0. - int regionattrib; // '-A' switch, 0. - int conformdel; // '-D' switch, 0. - int diagnose; // '-d' switch, 0. - int zeroindex; // '-z' switch, 0. - int optlevel; // number specified after '-s' switch, 3. - int optpasses; // number specified after '-ss' switch, 5. - int order; // element order, specified after '-o' switch, 1. - int facesout; // '-f' switch, 0. - int edgesout; // '-e' switch, 0. - int neighout; // '-n' switch, 0. - int voroout; // '-v',switch, 0. - int meditview; // '-g' switch, 0. - int gidview; // '-G' switch, 0. - int geomview; // '-O' switch, 0. - int nobound; // '-B' switch, 0. - int nonodewritten; // '-N' switch, 0. - int noelewritten; // '-E' switch, 0. - int nofacewritten; // '-F' switch, 0. - int noiterationnum; // '-I' switch, 0. - int nomerge; // '-M',switch, 0. - int nobisect; // count of how often '-Y' switch is selected, 0. - int noflip; // do not perform flips. '-X' switch. 0. - int nojettison; // do not jettison redundants nodes. '-J' switch. 0. - int steiner; // number after '-S' switch. 0. - int fliprepair; // '-X' switch, 1. - int offcenter; // '-R' switch, 0. - int docheck; // '-C' switch, 0. - int quiet; // '-Q' switch, 0. - int verbose; // count of how often '-V' switch is selected, 0. - int useshelles; // '-p', '-r', '-q', '-d', or '-R' switch, 0. - REAL minratio; // number after '-q' switch, 2.0. - REAL goodratio; // number calculated from 'minratio', 0.0. - REAL minangle; // minimum angle bound, 20.0. - REAL goodangle; // cosine squared of minangle, 0.0. - REAL maxvolume; // number after '-a' switch, -1.0. - REAL mindihedral; // number after '-qq' switch, 5.0. - REAL maxdihedral; // number after '-qqq' switch, 165.0. - REAL alpha1; // number after '-m' switch, sqrt(2). - REAL alpha2; // number after '-mm' switch, 1.0. - REAL alpha3; // number after '-mmm' switch, 0.6. - REAL epsilon; // number after '-T' switch, 1.0e-8. - REAL epsilon2; // number after '-TT' switch, 1.0e-5. - enum objecttype object; // determined by -p, or -r switch. NONE. - - // Variables used to save command line switches and in/out file names. - char commandline[1024]; - char infilename[1024]; - char outfilename[1024]; - char addinfilename[1024]; - char bgmeshfilename[1024]; - - tetgenbehavior(); - ~tetgenbehavior() {} - - 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); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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); - -/////////////////////////////////////////////////////////////////////////////// -// // -// The tetgenmesh data type // -// // -// Includes data types and mesh routines for creating tetrahedral meshes and // -// Delaunay tetrahedralizations, mesh input & output, and so on. // -// // -// 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. // -// // -// It's not necessary to understand this type. There is a global function // -// "tetrahedralize()" (defined at the end of this file) implicitly creates // -// the object and calls its member functions according to the command line // -// switches you specified. // -// // -/////////////////////////////////////////////////////////////////////////////// - -class tetgenmesh { - - public: - - // Maximum number of characters in a file name (including the null). - enum {FILENAMESIZE = 1024}; - - // For efficiency, a variety of data structures are allocated in bulk. - // The following constants determine how many of each structure is - // allocated at once. - enum {VERPERBLOCK = 4092, SUBPERBLOCK = 4092, ELEPERBLOCK = 8188}; - - // Used for the point location scheme of Mucke, Saias, and Zhu, to - // decide how large a random sample of tetrahedra to inspect. - enum {SAMPLEFACTOR = 11}; - - // Labels that signify two edge rings of a triangle defined in Muecke's - // triangle-edge data structure, one (CCW) traversing edges in count- - // erclockwise direction and one (CW) in clockwise direction. - enum {CCW = 0, CW = 1}; - - // Labels that signify whether a record consists primarily of pointers - // or of floating-point words. Used to make decisions about data - // alignment. - enum wordtype {POINTER, FLOATINGPOINT}; - - // Labels that signify the type of a vertex. An UNUSEDVERTEX is a vertex - // read from input (.node file or tetgenio structure) or an isolated - // vertex (outside the mesh). It is the default type for a newpoint. - enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, NACUTEVERTEX, ACUTEVERTEX, - FREESEGVERTEX, FREESUBVERTEX, FREEVOLVERTEX, DEADVERTEX = -32768}; - - // Labels that signify the type of a subface/subsegment. - enum shestype {NSHARP, SHARP}; - - // Labels that signify the type of flips can be applied on a face. - // A flipable face has the one of the types T23, T32, T22, and T44. - // Types N32, N40 are unflipable. - enum fliptype {T23, T32, T22, T44, N32, N40, FORBIDDENFACE, FORBIDDENEDGE}; - - // Labels that signify the result of triangle-triangle intersection test. - // Two triangles are DISJOINT, or adjoint at a vertex SHAREVERTEX, or - // adjoint at an edge SHAREEDGE, or coincident SHAREFACE or INTERSECT. - enum interresult {DISJOINT, SHAREVERTEX, SHAREEDGE, SHAREFACE, INTERSECT}; - - // Labels that signify the result of point location. The result of a - // search indicates that the point falls inside a tetrahedron, inside - // a triangle, on an edge, on a vertex, or outside the mesh. - enum locateresult {INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, OUTSIDE}; - - // Labels that signify the result of vertex insertion. The result - // indicates that the vertex was inserted with complete success, was - // inserted but encroaches upon a subsegment, was not inserted because - // it lies on a segment, or was not inserted because another vertex - // occupies the same location. - enum insertsiteresult {SUCCESSINTET, SUCCESSONFACE, SUCCESSONEDGE, - DUPLICATEPOINT, OUTSIDEPOINT}; - - // Labels that signify the result of direction finding. The result - // indicates that a segment connecting the two query points accross - // an edge of the direction triangle/tetrahedron, across a face of - // the direction tetrahedron, along the left edge of the direction - // triangle/tetrahedron, along the right edge of the direction - // triangle/tetrahedron, or along the top edge of the tetrahedron. - enum finddirectionresult {ACROSSEDGE, ACROSSFACE, LEFTCOLLINEAR, - RIGHTCOLLINEAR, TOPCOLLINEAR, BELOWHULL}; - -/////////////////////////////////////////////////////////////////////////////// -// // -// The basic mesh element 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. A tetrahedralization of a 3D point set comprises // -// tetrahedra and points; a surface mesh of a 3D domain comprises subfaces // -// subsegments and points. The elements of all the four types consist of a // -// tetrahedral mesh of a 3D domain. However, TetGen uses three data types: // -// 'tetrahedron', 'shellface', and 'point'. A 'tetrahedron' is a tetrahedron;// -// while a 'shellface' can be either a subface or a subsegment; and a 'point'// -// is a point. These three data types, linked by pointers comprise a mesh. // -// // -// A tetrahedron primarily consists of a list of 4 pointers to its corners, // -// a list of 4 pointers to its adjoining tetrahedra, a list of 4 pointers to // -// its adjoining subfaces (when subfaces are needed). Optinoally, (depending // -// on the selected switches), it may contain an arbitrary number of user- // -// defined floating-point attributes, an optional maximum volume constraint // -// (for -a switch), and a pointer to a list of high-order nodes (-o2 switch).// -// Since the size of a tetrahedron is not determined until running time, it // -// is not simply declared as a structure. // -// // -// The data structure of tetrahedron also stores the geometrical information.// -// Let t be a tetrahedron, v0, v1, v2, and v3 be the 4 nodes corresponding // -// to the order of their storage in t. v3 always has a negative orientation // -// with respect to v0, v1, v2 (ie,, v3 lies above the oriented plane passes // -// through v0, v1, v2). Let the 4 faces of t be f0, f1, f2, and f3. Vertices // -// of each face are stipulated as follows: f0 (v0, v1, v2), f1 (v0, v3, v1), // -// f2 (v1, v3, v2), f3 (v2, v3, v0). // -// // -// A subface has 3 pointers to vertices, 3 pointers to adjoining subfaces, 3 // -// pointers to adjoining subsegments, 2 pointers to adjoining tetrahedra, a // -// boundary marker(an integer). Like a tetrahedron, the pointers to vertices,// -// subfaces, and subsegments are ordered in a way that indicates their geom- // -// etric relation. Let s be a subface, v0, v1 and v2 be the 3 nodes corres- // -// ponding to the order of their storage in s, e0, e1 and e2 be the 3 edges,// -// then we have: e0 (v0, v1), e1 (v1, v2), e2 (v2, v0). // -// // -// A subsegment has exactly the same data fields as a subface has, but only // -// uses some of them. It has 2 pointers to its endpoints, 2 pointers to its // -// adjoining (and collinear) subsegments, a pointer to a subface containing // -// it (there may exist any number of subfaces having it, choose one of them // -// arbitrarily). The geometric relation between its endpoints and adjoining // -// subsegments is kept with respect to the storing order of its endpoints. // -// // -// The data structure of point is relatively simple. A point is a list of // -// floating-point numbers, starting with the x, y, and z coords, followed by // -// an arbitrary number of optional user-defined floating-point attributes, // -// an integer boundary marker, an integer for the point type, and a pointer // -// to a tetrahedron (used for speeding up point location). // -// // -// For a tetrahedron on a boundary (or a hull) of the mesh, some or all of // -// the adjoining tetrahedra may not be present. For an interior tetrahedron, // -// often no neighboring subfaces are present, Such absent tetrahedra and // -// subfaces are never represented by the NULL pointers; they are represented // -// by two special records: `dummytet', the tetrahedron fills "outer space", // -// and `dummysh', the vacuous subfaces which are omnipresent. // -// // -// Tetrahedra and adjoining subfaces are glued together through the pointers // -// saved in each data fields of them. Subfaces and adjoining subsegments are // -// connected in the same fashion. However, there are no pointers directly // -// gluing tetrahedra and adjoining subsegments. For the purpose of saving // -// space, the connections between tetrahedra and subsegments are entirely // -// mediated through subfaces. The following part explains how subfaces are // -// connected in TetGen. // -// // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// // -// The subface-subface and subface-subsegment connections // -// // -// Adjoining subfaces sharing a common edge are connected in such a way that // -// they form a face ring around the edge. It is indeed a single linked list // -// which is cyclic, e.g., one can start from any subface in it and traverse // -// back. When the edge is not a subsegment, the ring only has two coplanar // -// subfaces which are pointing to each other. Otherwise, the face ring may // -// have any number of subfaces (and are not all coplanar). // -// // -// How is the face ring formed? Let s be a subsegment, f is one of subfaces // -// containing s as an edge. The direction of s is stipulated from its first // -// endpoint to its second (according to their storage in s). Once the dir of // -// s is determined, the other two edges of f are oriented to follow this dir.// -// The "directional normal" N_f is a vector formed from any point in f and a // -// points orthogonally above f. // -// // -// The face ring of s is a cyclic ordered set of subfaces containing s, i.e.,// -// F(s) = {f1, f2, ..., fn}, n >= 1. Where the order is defined as follows: // -// let fi, fj be two faces in F(s), the "normal-angle", NAngle(i,j) (range // -// from 0 to 360 degree) is the angle between the N_fi and N_fj; then fi is // -// in front of fj (or symbolically, fi < fj) if there exists another fk in // -// F(s), and NAangle(k, i) < NAngle(k, j). The face ring of s is: f1 < f2 < // -// ... < fn < f1. // -// // -// The easiest way to imagine how a face ring is formed is to use the right- // -// hand rule. Make a fist using your right hand with the thumb pointing to // -// the direction of the subsegment. The face ring is connected following the // -// direction of your fingers. // -// // -// The subface and subsegment are also connected through pointers stored in // -// their own data fields. Every subface has a pointer to its adjoining sub- // -// segment. However, a subsegment only has one pointer to a subface which is // -// containing it. Such subface can be chosen arbitrarily, other subfaces are // -// found through the face ring. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // The tetrahedron data structure. Fields of a tetrahedron contains: - // - a list of four adjoining tetrahedra; - // - a list of four vertices; - // - a list of four subfaces (optional, used for -p switch); - // - a list of user-defined floating-point attributes (optional); - // - a volume constraint (optional, used for -a switch); - // - an integer of element marker (optional, used for -n switch); - // - a pointer to a list of high-ordered nodes (optional, -o2 switch); - - typedef REAL **tetrahedron; - - // The shellface data structure. Fields of a shellface contains: - // - a list of three adjoining subfaces; - // - a list of three vertices; - // - a list of two adjoining tetrahedra; - // - a list of three adjoining subsegments; - // - a pointer to a badface containing it (used for -q); - // - an area constraint (optional, used for -q); - // - an integer for boundary marker; - // - an integer for type: SHARPSEGMENT, NONSHARPSEGMENT, ...; - // - an integer for pbc group (optional, if in->pbcgrouplist exists); - - typedef REAL **shellface; - - // The point data structure. It is actually an array of REALs: - // - x, y and z coordinates; - // - a list of user-defined point attributes (optional); - // - a list of REALs of a user-defined metric tensor (optional); - // - a pointer to a simplex (tet, tri, edge, or vertex); - // - a pointer to a parent (or duplicate) point; - // - a pointer to a tet in background mesh (optional); - // - a pointer to another pbc point (optional); - // - an integer for boundary marker; - // - an integer for verttype: INPUTVERTEX, FREEVERTEX, ...; - - typedef REAL *point; - -/////////////////////////////////////////////////////////////////////////////// -// // -// The mesh handle (triface, face) data types // -// // -// 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. // -// // -// Muecke's "triangle-edge" data structure is the prototype for these data // -// types. It allows a universal representation for every tetrahedron, // -// triangle, edge and vertex. For understanding the following descriptions // -// of these handle data structures, readers are required to read both the // -// introduction and implementation detail of "triangle-edge" data structure // -// in Muecke's thesis. // -// // -// A 'triface' represents a face of a tetrahedron and an oriented edge of // -// the face simultaneously. It has a pointer 'tet' to a tetrahedron, an // -// integer 'loc' (range from 0 to 3) as the face index, and an integer 'ver' // -// (range from 0 to 5) as the edge version. A face of the tetrahedron can be // -// uniquly determined by the pair (tet, loc), and an oriented edge of this // -// face can be uniquly determined by the triple (tet, loc, ver). Therefore, // -// different usages of one triface are possible. If we only use the pair // -// (tet, loc), it refers to a face, and if we add the 'ver' additionally to // -// the pair, it is an oriented edge of this face. // -// // -// A 'face' represents a subface and an oriented edge of it simultaneously. // -// It has a pointer 'sh' to a subface, an integer 'shver'(range from 0 to 5) // -// as the edge version. The pair (sh, shver) determines a unique oriented // -// edge of this subface. A 'face' is also used to represent a subsegment, // -// in this case, 'sh' points to the subsegment, and 'shver' indicates the // -// one of two orientations of this subsegment, hence, it only can be 0 or 1. // -// // -// Mesh navigation and updating are accomplished through a set of mesh // -// manipulation primitives which operate on trifaces and faces. They are // -// introduced below. // -// // -/////////////////////////////////////////////////////////////////////////////// - - 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; - } - }; - - 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);} - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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; - point noppo; - struct badface *previtem, *nextitem; - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// The pbcdata structure // -// // -// A pbcdata stores data of a periodic boundary condition defined on a pair // -// of facets or segments. Let f1 and f2 define a pbcgroup. 'fmark' saves the // -// facet markers of f1 and f2; 'ss' contains two subfaces belong to f1 and // -// f2, respectively. Let s1 and s2 define a segment pbcgroup. 'segid' are // -// the segment ids of s1 and s2; 'ss' contains two segments belong to s1 and // -// s2, respectively. 'transmat' are two transformation matrices. transmat[0] // -// transforms a point of f1 (or s1) into a point of f2 (or s2), transmat[1] // -// does the inverse. // -// // -/////////////////////////////////////////////////////////////////////////////// - - struct pbcdata { - int fmark[2]; - int segid[2]; - face ss[2]; - REAL transmat[2][4][4]; - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// The list, link and queue data structures // -// // -// These data types are used to manipulate a set of (same-typed) data items. // -// For a given set S = {a, b, c, ...}, a list stores the elements of S in a // -// piece of continuous memory. It allows quickly accessing each element of S,// -// thus is suitable for storing a fix-sized set. While a link stores its // -// elements incontinuously. It allows quickly inserting or deleting an item, // -// thus is suitable for storing a size-variable set. A queue is basically a // -// special case of a link where one data element joins the link at the end // -// and leaves in an ordered fashion at the other end. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // The compfunc data type. "compfunc" is a pointer to a linear-order - // function, which takes two 'void*' arguments and returning an 'int'. - // - // A function: int cmp(const T &, const T &), is said to realize a - // linear order on the type T if there is a linear order <= on T such - // that for all x and y in T satisfy the following relation: - // -1 if x < y. - // comp(x, y) = 0 if x is equivalent to y. - // +1 if x > y. - typedef int (*compfunc) (const void *, const void *); - - // The predefined compare functions for primitive data types. They - // take two pointers of the corresponding date type, perform the - // comparation, and return -1, 0 or 1 indicating the default linear - // order of them. - static int compare_2_ints(const void* x, const void* y); - static int compare_2_longs(const void* x, const void* y); - static int compare_2_unsignedlongs(const void* x, const void* y); - - // The function used to determine the size of primitive data types and - // set the corresponding predefined linear order functions for them. - static void set_compfunc(char* str, int* itembytes, compfunc* pcomp); - -/////////////////////////////////////////////////////////////////////////////// -// // -// List data structure. // -// // -// A 'list' is an array of items with automatically reallocation of memory. // -// It behaves like an array. // -// // -// 'base' is the starting address of the array; The memory unit in list is // -// byte, i.e., sizeof(char). 'itembytes' is the size of each item in byte, // -// so that the next item in list will be found at the next 'itembytes' // -// counted from the current position. // -// // -// 'items' is the number of items stored in list. 'maxitems' indicates how // -// many items can be stored in this list. 'expandsize' is the increasing // -// size (items) when the list is full. // -// // -// 'comp' is a pointer pointing to a linear order function for the list. // -// default it is set to 'NULL'. // -// // -// The index of list always starts from zero, i.e., for a list L contains // -// n elements, the first element is L[0], and the last element is L[n-1]. // -// This feature lets lists like C/C++ arrays. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class list { - - public: - - char *base; - int itembytes; - int items, maxitems, expandsize; - compfunc comp; - - public: - - list(int itbytes, compfunc pcomp, int mitems = 256, int exsize = 128) { - listinit(itbytes, pcomp, mitems, exsize); - } - list(char* str, int mitems = 256, int exsize = 128) { - set_compfunc(str, &itembytes, &comp); - listinit(itembytes, comp, mitems, exsize); - } - ~list() { free(base); } - - void *operator[](int i) { return (void *) (base + i * itembytes); } - - void listinit(int itbytes, compfunc pcomp, int mitems, int exsize); - void setcomp(compfunc compf) { comp = compf; } - void clear() { items = 0; } - int len() { return items; } - void *append(void* appitem); - void *insert(int pos, void* insitem); - void del(int pos, int order); - int hasitem(void* checkitem); - void sort(); - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Memorypool data structure. // -// // -// A type used to allocate memory. (It is incorporated from Shewchuk's // -// Triangle program) // -// // -// 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; - - public: - - memorypool(); - memorypool(int, int, enum wordtype, int); - ~memorypool(); - - void poolinit(int, int, enum wordtype, int); - void restart(); - void *alloc(); - void dealloc(void*); - void traversalinit(); - void *traverse(); - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Link data structure. // -// // -// A 'link' is a double linked nodes. It uses the memorypool data structure // -// for memory management. Following is an image of a link. // -// // -// head-> ____0____ ____1____ ____2____ _________<-tail // -// |__next___|--> |__next___|--> |__next___|--> |__NULL___| // -// |__NULL___|<-- |__prev___|<-- |__prev___|<-- |__prev___| // -// | | |_ _| |_ _| | | // -// | | |_ Data1 _| |_ Data2 _| | | // -// |_________| |_________| |_________| |_________| // -// // -// The unit size for storage is size of pointer, which may be 4-byte (in 32- // -// bit machine) or 8-byte (in 64-bit machine). The real size of an item is // -// stored in 'linkitembytes'. // -// // -// 'head' and 'tail' are pointers pointing to the first and last nodes. They // -// do not conatin data (See above). // -// // -// 'nextlinkitem' is a pointer pointing to a node which is the next one will // -// be traversed. 'curpos' remembers the position (1-based) of the current // -// traversing node. // -// // -// 'linkitems' indicates how many items in link. Note it is different with // -// 'items' of memorypool. // -// // -// The index of link starts from 1, i.e., for a link K contains n elements, // -// the first element of the link is K[1], and the last element is K[n]. // -// See the above figure. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class link : public memorypool { - - public: - - void **head, **tail; - void *nextlinkitem; - int linkitembytes; - int linkitems; - int curpos; - compfunc comp; - - public: - - link(int _itembytes, compfunc _comp, int itemcount) { - linkinit(_itembytes, _comp, itemcount); - } - link(char* str, int itemcount) { - set_compfunc(str, &linkitembytes, &comp); - linkinit(linkitembytes, comp, itemcount); - } - - void linkinit(int _itembytes, compfunc _comp, int itemcount); - void setcomp(compfunc compf) { comp = compf; } - void rewind() { nextlinkitem = *head; curpos = 1; } - void goend() { nextlinkitem = *(tail + 1); curpos = linkitems; } - long len() { return linkitems; } - void clear(); - bool move(int numberofnodes); - bool locate(int pos); - void *add(void* newitem); - void *insert(int pos, void* insitem); - void *deletenode(void** delnode); - void *del(int pos); - void *getitem(); - void *getnitem(int pos); - int hasitem(void* checkitem); - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Queue data structure. // -// // -// A 'queue' is basically a link. Following is an image of a queue. // -// ___________ ___________ ___________ // -// Pop() <-- |_ _|<--|_ _|<--|_ _| <-- Push() // -// |_ Data0 _| |_ Data1 _| |_ Data2 _| // -// |___________| |___________| |___________| // -// queue head queue tail // -// // -/////////////////////////////////////////////////////////////////////////////// - - class queue : public link { - - public: - - queue(int bytes, int count = 256) : link(bytes, NULL, count) {} - bool empty() { return linkitems == 0; } - void *push(void* newitem) {return link::add(newitem);} - void *pop() {return link::deletenode((void **) *head);} - // Stack is implemented as a single link list. - void *stackpush() { - void **newnode = (void **) alloc(); - // if (newitem != (void *) NULL) { - // memcpy((void *)(newnode + 2), newitem, linkitembytes); - // } - void **nextnode = (void **) *head; - *head = (void *) newnode; - *newnode = (void *) nextnode; - linkitems++; - return (void *)(newnode + 2); - } - void *stackpop() { - void **deadnode = (void **) *head; - *head = *deadnode; - linkitems--; - return (void *)(deadnode + 2); - } - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Global variables used for miscellaneous purposes. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // Pointer to the input data (a set of nodes, a PLC, or a mesh). - tetgenio *in; - // Pointer to the options (and filenames). - tetgenbehavior *b; - // Pointer to a background mesh (contains size specification map). - tetgenmesh *bgm; - - // Variables used to allocate and access memory for tetrahedra, subfaces - // subsegments, points, encroached subfaces, encroached subsegments, - // bad-quality tetrahedra, and so on. - memorypool *tetrahedrons; - memorypool *subfaces; - memorypool *subsegs; - memorypool *points; - memorypool *badsubsegs; - memorypool *badsubfaces; - memorypool *badtetrahedrons; - memorypool *flipstackers; - - // Pointer to the 'tetrahedron' that occupies all of "outer space". - tetrahedron *dummytet; - tetrahedron *dummytetbase; // Keep base address so we can free it later. - - // Pointer to the omnipresent subface. Referenced by any tetrahedron, - // or subface that isn't connected to a subface at that location. - shellface *dummysh; - shellface *dummyshbase; // Keep base address so we can free it later. - - // A point above the plane in which the facet currently being used lies. - // It is used as a reference point for orient3d(). - point *facetabovepointarray, abovepoint; - - // Array (size = numberoftetrahedra * 6) for storing high-order nodes of - // tetrahedra (only used when -o2 switch is selected). - point *highordertable; - - // Arrays for storing and searching pbc data. 'subpbcgrouptable', (size - // is numberofpbcgroups) for pbcgroup of subfaces. 'segpbcgrouptable', - // a list for pbcgroup of segments. Because a segment can have several - // pbcgroup incident on it, its size is unknown on input, it will be - // found in 'createsegpbcgrouptable()'. - pbcdata *subpbcgrouptable; - list *segpbcgrouptable; - // A map for searching the pbcgroups of a given segment. 'idx2segpglist' - // (size = number of input segments + 1), and 'segpglist'. - int *idx2segpglist, *segpglist; - - // Queues that maintain the bad (badly-shaped or too large) tetrahedra. - // The tails are pointers to the pointers that have to be filled in to - // enqueue an item. The queues are ordered from 63 (highest priority) - // to 0 (lowest priority). - badface *subquefront[3], **subquetail[3]; - badface *tetquefront[64], *tetquetail[64]; - int nextnonemptyq[64]; - int firstnonemptyq, recentq; - - // Pointer to a recently visited tetrahedron. Improves point location - // if proximate points are inserted sequentially. - triface recenttet; - - REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. - REAL longest; // The longest possible edge length. - REAL lengthlimit; // The limiting length of a new edge. - long hullsize; // Number of faces of convex hull. - long insegments; // Number of input segments. - int steinerleft; // Number of Steiner points not yet used. - int sizeoftensor; // Number of REALs per metric tensor. - int pointmtrindex; // Index to find the metric tensor of a point. - int point2simindex; // Index to find a simplex adjacent to a point. - int pointmarkindex; // Index to find boundary marker of a point. - int point2pbcptindex; // Index to find a pbc point to a point. - int highorderindex; // Index to find extra nodes for highorder elements. - int elemattribindex; // Index to find attributes of a tetrahedron. - int volumeboundindex; // Index to find volume bound of a tetrahedron. - int elemmarkerindex; // Index to find marker of a tetrahedron. - int shmarkindex; // Index to find boundary marker of a subface. - int areaboundindex; // Index to find area bound of a subface. - int checksubfaces; // Are there subfaces in the mesh yet? - int checksubsegs; // Are there subsegs in the mesh yet? - int checkpbcs; // Are there periodic boundary conditions? - int varconstraint; // Are there variant (node, seg, facet) constraints? - int nonconvex; // Is current mesh non-convex? - int dupverts; // Are there duplicated vertices? - int unuverts; // Are there unused vertices? - int relverts; // The number of relocated vertices. - int suprelverts; // The number of suppressed relocated vertices. - int collapverts; // The number of collapsed relocated vertices. - int unsupverts; // The number of unsuppressed vertices. - int smoothsegverts; // The number of smoothed vertices. - int smoothvolverts; // The number of smoothed vertices. - int jettisoninverts; // The number of jettisoned input vertices. - int symbolic; // Use symbolic insphere test. - long samples; // Number of random samples for point location. - unsigned long randomseed; // Current random number seed. - REAL macheps; // The machine epsilon. - REAL cosmaxdihed, cosmindihed; // The cosine values of max/min dihedral. - REAL minfaceang, minfacetdihed; // The minimum input (dihedral) angles. - int maxcavfaces, maxcavverts; // The size of the largest cavity. - int expcavcount; // The times of expanding cavitys. - long abovecount; // Number of abovepoints calculation. - long bowatvolcount, bowatsubcount, bowatsegcount; // Bowyer-Watsons. - long updvolcount, updsubcount, updsegcount; // Bow-Wat cavities updates. - long failvolcount, failsubcount, failsegcount; // Bow-Wat fails. - long repairflipcount; // Number of flips for repairing segments. - long outbowatcircumcount; // Number of circumcenters outside Bowat-cav. - long r1count, r2count, r3count; // Numbers of edge splitting rules. - long cdtenforcesegpts; // Number of CDT enforcement points. - long rejsegpts, rejsubpts, rejtetpts; // Number of rejected points. - long optcount[10]; // Numbers of various optimizing operations. - long flip23s, flip32s, flip22s, flip44s; // Number of flips performed. - REAL tloctime, tfliptime; // Time (microseconds) of point location. - -/////////////////////////////////////////////////////////////////////////////// -// // -// Fast lookup tables for mesh manipulation primitives. // -// // -// Mesh manipulation primitives (given below) are basic operations on mesh // -// data structures. They answer basic queries on mesh handles, such as "what // -// is the origin (or destination, or apex) of the face?", "what is the next // -// (or previous) edge in the edge ring?", and "what is the next face in the // -// face ring?", and so on. // -// // -// The implementation of teste basic queries can take advangtage of the fact // -// that the mesh data structures additionally store geometric informations. // -// For example, we have ordered the 4 vertices (from 0 to 3) and the 4 faces // -// (from 0 to 3) of a tetrahedron, and for each face of the tetrahedron, a // -// sequence of vertices has stipulated, therefore the origin of any face of // -// the tetrahedron can be quickly determined by a table 'locver2org', which // -// takes the index of the face and the edge version as inputs. A list of // -// fast lookup tables are defined below. They're just like global variables. // -// These tables are initialized at the runtime. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // For enext() primitive, uses 'ver' as the index. - static int ve[6]; - - // For org(), dest() and apex() primitives, uses 'ver' as the index. - static int vo[6], vd[6], va[6]; - - // For org(), dest() and apex() primitives, uses 'loc' as the first - // index and 'ver' as the second index. - static int locver2org[4][6]; - static int locver2dest[4][6]; - static int locver2apex[4][6]; - - // For oppo() primitives, uses 'loc' as the index. - static int loc2oppo[4]; - - // For fnext() primitives, uses 'loc' as the first index and 'ver' as - // the second index, returns an array containing a new 'loc' and a - // new 'ver'. Note: Only valid for 'ver' equals one of {0, 2, 4}. - static int locver2nextf[4][6][2]; - - // The edge number (from 0 to 5) of a tet is defined as follows: - static int locver2edge[4][6]; - static int edge2locver[6][2]; - - // For enumerating three edges of a triangle. - static int plus1mod3[3]; - static int minus1mod3[3]; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh manipulation primitives // -// // -// A serial of mesh operations such as topological maintenance, navigation, // -// local modification, etc., is accomplished through a set of mesh manipul- // -// ation primitives. These primitives are indeed very simple functions which // -// take one or two handles ('triface's and 'face's) as parameters, perform // -// basic operations such as "glue two tetrahedra at a face", "return the // -// origin of a tetrahedron", "return the subface adjoining at the face of a // -// tetrahedron", and so on. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // Primitives for tetrahedra. - inline void decode(tetrahedron ptr, triface& t); - inline tetrahedron encode(triface& t); - inline void sym(triface& t1, triface& t2); - inline void symself(triface& t); - inline void bond(triface& t1, triface& t2); - inline void dissolve(triface& t); - inline point org(triface& t); - inline point dest(triface& t); - inline point apex(triface& t); - inline point oppo(triface& t); - inline void setorg(triface& t, point pointptr); - inline void setdest(triface& t, point pointptr); - inline void setapex(triface& t, point pointptr); - inline void setoppo(triface& t, point pointptr); - inline void esym(triface& t1, triface& t2); - inline void esymself(triface& t); - inline void enext(triface& t1, triface& t2); - inline void enextself(triface& t); - inline void enext2(triface& t1, triface& t2); - inline void enext2self(triface& t); - inline bool fnext(triface& t1, triface& t2); - inline bool fnextself(triface& t); - inline void enextfnext(triface& t1, triface& t2); - inline void enextfnextself(triface& t); - inline void enext2fnext(triface& t1, triface& t2); - inline void enext2fnextself(triface& t); - inline void infect(triface& t); - inline void uninfect(triface& t); - inline bool infected(triface& t); - inline REAL elemattribute(tetrahedron* ptr, int attnum); - inline void setelemattribute(tetrahedron* ptr, int attnum, REAL value); - inline REAL volumebound(tetrahedron* ptr); - inline void setvolumebound(tetrahedron* ptr, REAL value); - - // Primitives for subfaces and subsegments. - inline void sdecode(shellface sptr, face& s); - inline shellface sencode(face& s); - inline void spivot(face& s1, face& s2); - inline void spivotself(face& s); - inline void sbond(face& s1, face& s2); - inline void sbond1(face& s1, face& s2); - inline void sdissolve(face& s); - inline point sorg(face& s); - inline point sdest(face& s); - inline point sapex(face& s); - inline void setsorg(face& s, point pointptr); - inline void setsdest(face& s, point pointptr); - inline void setsapex(face& s, point pointptr); - inline void sesym(face& s1, face& s2); - inline void sesymself(face& s); - inline void senext(face& s1, face& s2); - inline void senextself(face& s); - inline void senext2(face& s1, face& s2); - inline void senext2self(face& s); - inline void sfnext(face&, face&); - inline void sfnextself(face&); - inline badface* shell2badface(face& s); - inline void setshell2badface(face& s, badface* value); - inline REAL areabound(face& s); - inline void setareabound(face& s, REAL value); - inline int shellmark(face& s); - inline void setshellmark(face& s, int value); - inline enum shestype shelltype(face& s); - inline void setshelltype(face& s, enum shestype value); - inline int shellpbcgroup(face& s); - inline void setshellpbcgroup(face& s, int value); - inline void sinfect(face& s); - inline void suninfect(face& s); - inline bool sinfected(face& s); - - // Primitives for interacting tetrahedra and subfaces. - inline void tspivot(triface& t, face& s); - inline void stpivot(face& s, triface& t); - inline void tsbond(triface& t, face& s); - inline void tsdissolve(triface& t); - inline void stdissolve(face& s); - - // Primitives for interacting subfaces and subsegs. - inline void sspivot(face& s, face& edge); - inline void ssbond(face& s, face& edge); - inline void ssdissolve(face& s); - - inline void tsspivot1(triface& t, face& seg); - inline void tssbond1(triface& t, face& seg); - inline void tssdissolve1(triface& t); - - // Primitives for points. - inline int pointmark(point pt); - inline void setpointmark(point pt, int value); - inline enum verttype pointtype(point pt); - inline void setpointtype(point pt, enum verttype value); - inline tetrahedron point2tet(point pt); - inline void setpoint2tet(point pt, tetrahedron value); - inline shellface point2sh(point pt); - inline void setpoint2sh(point pt, shellface value); - inline point point2ppt(point pt); - inline void setpoint2ppt(point pt, point value); - inline tetrahedron point2bgmtet(point pt); - inline void setpoint2bgmtet(point pt, tetrahedron value); - inline point point2pbcpt(point pt); - inline void setpoint2pbcpt(point pt, point value); - - // Advanced primitives. - inline void adjustedgering(triface& t, int direction); - inline void adjustedgering(face& s, int direction); - inline bool isdead(triface* t); - inline bool isdead(face* s); - inline bool isfacehaspoint(triface* t, point testpoint); - inline bool isfacehaspoint(face* t, point testpoint); - inline bool isfacehasedge(face* s, point tend1, point tend2); - inline bool issymexist(triface* t); - void getnextsface(face*, face*); - void tsspivot(triface*, face*); - void sstpivot(face*, triface*); - bool findorg(triface* t, point dorg); - bool findorg(face* s, point dorg); - void findedge(triface* t, point eorg, point edest); - void findedge(face* s, point eorg, point edest); - void findface(triface *fface, point forg, point fdest, point fapex); - void getonextseg(face* s, face* lseg); - void getseghasorg(face* sseg, point dorg); - point getsubsegfarorg(face* sseg); - point getsubsegfardest(face* sseg); - void printtet(triface*); - void printsh(face*); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Triangle-triangle intersection test // -// // -// The triangle-triangle intersection test is implemented with exact arithm- // -// etic. It exactly tells whether or not two triangles in three dimensions // -// intersect. Before implementing this test myself, I tried two C codes // -// (implemented by Thomas Moeller and Philippe Guigue, respectively), which // -// are all public available. However both of them failed frequently. Another // -// unconvenience is both codes only tell whether or not the two triangles // -// intersect without distinguishing the cases whether they exactly intersect // -// in interior or they just share a vertex or share an edge. The two latter // -// cases are acceptable and should return not intersection in TetGen. // -// // -/////////////////////////////////////////////////////////////////////////////// - - enum interresult edge_vert_col_inter(REAL*, REAL*, REAL*); - enum interresult edge_edge_cop_inter(REAL*, REAL*, REAL*, REAL*, REAL*); - enum interresult tri_vert_cop_inter(REAL*, REAL*, REAL*, REAL*, REAL*); - enum interresult tri_edge_cop_inter(REAL*, REAL*, REAL*,REAL*,REAL*,REAL*); - enum interresult tri_edge_inter_tail(REAL*, REAL*, REAL*, REAL*, REAL*, - REAL, REAL); - enum interresult tri_edge_inter(REAL*, REAL*, REAL*, REAL*, REAL*); - enum interresult tri_tri_inter(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); - - // Geometric predicates - REAL insphere_sos(REAL*, REAL*, REAL*, REAL*, REAL*, int, int,int,int,int); - bool iscollinear(REAL*, REAL*, REAL*, REAL eps); - bool iscoplanar(REAL*, REAL*, REAL*, REAL*, REAL vol6, REAL eps); - bool iscospheric(REAL*, REAL*, REAL*, REAL*, REAL*, REAL vol24, REAL eps); - - // Linear algebra functions - inline REAL dot(REAL* v1, REAL* v2); - inline void cross(REAL* v1, REAL* v2, REAL* n); - bool lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N); - void lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N); - - // Geometric quantities calculators. - inline REAL distance(REAL* p1, REAL* p2); - REAL shortdistance(REAL* p, REAL* e1, REAL* e2); - REAL shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3); - REAL interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n); - void projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj); - void projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj); - void facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen); - void edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n); - REAL facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2); - void tetalldihedral(point, point, point, point, REAL*, REAL*, REAL*); - void tetallnormal(point, point, point, point, REAL N[4][3], REAL* volume); - REAL tetaspectratio(point, point, point, point); - bool circumsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius); - void inscribedsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius); - void rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2); - void spherelineint(REAL* p1, REAL* p2, REAL* C, REAL R, REAL p[7]); - void linelineint(REAL *p1,REAL *p2, REAL *p3, REAL *p4, REAL p[7]); - void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); - - // Memory managment routines. - void dummyinit(int, int); - void initializepools(); - void tetrahedrondealloc(tetrahedron*); - tetrahedron *tetrahedrontraverse(); - 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*); - - // Mesh items searching routines. - void makepoint2tetmap(); - void makeindex2pointmap(point*& idx2verlist); - void makesegmentmap(int*& idx2seglist, shellface**& segsperverlist); - void makesubfacemap(int*& idx2facelist, shellface**& facesperverlist); - void maketetrahedronmap(int*& idx2tetlist, tetrahedron**& tetsperverlist); - - // Point location routines. - unsigned long randomnation(unsigned int choices); - REAL distance2(tetrahedron* tetptr, point p); - enum locateresult preciselocate(point searchpt, triface* searchtet, long); - enum locateresult locate(point searchpt, triface* searchtet); - enum locateresult adjustlocate(point, triface*, enum locateresult, REAL); - enum locateresult hullwalk(point searchpt, triface* hulltet); - enum locateresult locatesub(point searchpt, face* searchsh, int, REAL); - enum locateresult adjustlocatesub(point, face*, enum locateresult, REAL); - enum locateresult locateseg(point searchpt, face* searchseg); - enum locateresult adjustlocateseg(point, face*, enum locateresult, REAL); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh Local Transformation Operators // -// // -// These operators (including flips, insert & remove vertices and so on) are // -// used to transform (or replace) a set of mesh elements into another set of // -// mesh elements. // -// // -/////////////////////////////////////////////////////////////////////////////// - - // Mesh transformation routines. - enum fliptype categorizeface(triface& horiz); - void enqueueflipface(triface& checkface, queue* flipqueue); - void enqueueflipedge(face& checkedge, queue* flipqueue); - void flip23(triface* flipface, queue* flipqueue); - void flip32(triface* flipface, queue* flipqueue); - void flip22(triface* flipface, queue* flipqueue); - void flip22sub(face* flipedge, queue* flipqueue); - long flip(queue* flipqueue, badface **plastflip); - long lawson(list *misseglist, queue* flipqueue); - void undoflip(badface *lastflip); - long flipsub(queue* flipqueue); - bool removetetbypeeloff(triface *striptet); - bool removefacebyflip23(REAL *key, triface*, triface*, queue*); - bool removeedgebyflip22(REAL *key, int, triface*, queue*); - bool removeedgebyflip32(REAL *key, triface*, triface*, queue*); - bool removeedgebytranNM(REAL*,int,triface*,triface*,point,point,queue*); - bool removeedgebycombNM(REAL*,int,triface*,int*,triface*,triface*,queue*); - - void splittetrahedron(point newpoint, triface* splittet, queue* flipqueue); - void unsplittetrahedron(triface* splittet); - void splittetface(point newpoint, triface* splittet, queue* flipqueue); - void unsplittetface(triface* splittet); - void splitsubface(point newpoint, face* splitface, queue* flipqueue); - void unsplitsubface(face* splitsh); - void splittetedge(point newpoint, triface* splittet, queue* flipqueue); - void unsplittetedge(triface* splittet); - void splitsubedge(point newpoint, face* splitsh, queue* flipqueue); - void unsplitsubedge(face* splitsh); - enum insertsiteresult insertsite(point newpoint, triface* searchtet, - bool approx, queue* flipqueue); - void undosite(enum insertsiteresult insresult, triface* splittet, - point torg, point tdest, point tapex, point toppo); - void closeopenface(triface* openface, queue* flipque); - void inserthullsite(point inspoint, triface* horiz, queue* flipque); - - void formbowatcavitysub(point, face*, list*, list*); - void formbowatcavityquad(point, list*, list*); - void formbowatcavitysegquad(point, list*, list*); - void formbowatcavity(point bp, face* bpseg, face* bpsh, int* n, int* nmax, - list** sublists, list** subceillists, list** tetlists, - list** ceillists); - void releasebowatcavity(face*, int, list**, list**, list**, list**); - bool validatebowatcavityquad(point bp, list* ceillist, REAL maxcosd); - void updatebowatcavityquad(list* tetlist, list* ceillist); - void updatebowatcavitysub(list* sublist, list* subceillist, int* cutcount); - bool trimbowatcavity(point bp, face* bpseg, int n, list** sublists, - list** subceillists, list** tetlists,list** ceillists, - REAL maxcosd); - void bowatinsertsite(point bp, face* splitseg, int n, list** sublists, - list** subceillists, list** tetlists, - list** ceillists, list* verlist, queue* flipque, - bool chkencseg, bool chkencsub, bool chkbadtet); - - // Delaunay tetrahedralization routines. - void formstarpolyhedron(point pt, list* tetlist, list* verlist, bool); - bool unifypoint(point testpt, triface*, enum locateresult, REAL); - void incrflipdelaunay(triface*, point*, long, bool, bool, REAL, queue*); - long delaunizevertices(); - - // Surface triangulation routines. - void formstarpolygon(point pt, list* trilist, list* verlist); - void getfacetabovepoint(face* facetsh); - void collectcavsubs(point newpoint, list* cavsublist); - void collectvisiblesubs(int shmark, point inspoint, face* horiz, queue*); - void incrflipdelaunaysub(int shmark, REAL eps, list*, int, REAL*, queue*); - enum finddirectionresult finddirectionsub(face* searchsh, point tend); - void insertsubseg(face* tri); - bool scoutsegmentsub(face* searchsh, point tend); - void flipedgerecursive(face* flipedge, queue* flipqueue); - void constrainededge(face* startsh, point tend, queue* flipqueue); - void recoversegment(point tstart, point tend, queue* flipqueue); - void infecthullsub(memorypool* viri); - void plaguesub(memorypool* viri); - void carveholessub(int holes, REAL* holelist, memorypool* viri); - void triangulate(int shmark, REAL eps, list* ptlist, list* conlist, - int holes, REAL* holelist, memorypool* viri, queue*); - void retrievenewsubs(list* newshlist, bool removeseg); - void unifysegments(); - void mergefacets(queue* flipqueue); - long meshsurface(); - - // Detect intersecting facets of PLC. - void interecursive(shellface** subfacearray, int arraysize, int axis, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax, int* internum); - void detectinterfaces(); - - // Periodic boundary condition supporting routines. - void createsubpbcgrouptable(); - void getsubpbcgroup(face* pbcsub, pbcdata** pd, int *f1, int *f2); - enum locateresult getsubpbcsympoint(point, face*, point, face*); - void createsegpbcgrouptable(); - enum locateresult getsegpbcsympoint(point, face*, point, face*, int); - - // Vertex perturbation routines. - REAL randgenerator(REAL range); - bool checksub4cocir(face* testsub, REAL eps, bool once, bool enqflag); - void tallcocirsubs(REAL eps, bool enqflag); - bool tallencsegsfsubs(point testpt, list* cavsublist); - void collectflipedges(point inspoint, face* splitseg, queue* flipqueue); - void perturbrepairencsegs(queue* flipqueue); - void perturbrepairencsubs(list* cavsublist, queue* flipqueue); - void incrperturbvertices(REAL eps); - - // Segment recovery routines. - void markacutevertices(REAL acuteangle); - enum finddirectionresult finddirection(triface* searchtet, point, long); - void getsearchtet(point p1, point p2, triface* searchtet, point* tend); - bool isedgeencroached(point p1, point p2, point testpt, bool degflag); - point scoutrefpoint(triface* searchtet, point tend); - point getsegmentorigin(face* splitseg); - point getsplitpoint(face* splitseg, point refpoint); - bool insertsegment(face *insseg, list *misseglist); - void tallmissegs(list *misseglist); - void delaunizesegments(); - - // Facets recovery routines. - bool insertsubface(face* insertsh, triface* searchtet); - bool tritritest(triface* checktet, point p1, point p2, point p3); - void initializecavity(list* floorlist, list* ceillist, list* frontlist); - void delaunizecavvertices(triface*, list*, list*, list*, queue*); - void retrievenewtets(list* newtetlist); - void insertauxsubface(triface* front, triface* idfront); - bool scoutfront(triface* front, triface* idfront, list* newtetlist); - void gluefronts(triface* front, triface* front1); - bool identifyfronts(list* frontlist, list* misfrontlist, list* newtetlist); - void detachauxsubfaces(list* newtetlist); - void expandcavity(list* frontlist, list* misfrontlist, list* newtetlist, - list* crosstetlist, queue* missingshqueue, queue*); - void carvecavity(list* newtetlist, list* outtetlist, queue* flipque); - void delaunizecavity(list* floorlist, list* ceillist, list* ceilptlist, - list* floorptlist, list* frontlist,list* misfrontlist, - list* newtetlist, list* crosstetlist, queue*, queue*); - void formmissingregion(face* missingsh, list* missingshlist, - list* equatptlist, int* worklist); - void formcavity(list* missingshlist, list* crossedgelist, - list* equatptlist, list* crossshlist, list* crosstetlist, - list* belowfacelist, list* abovefacelist, - list* horizptlist, list* belowptlist, list* aboveptlist, - queue* missingshqueue, int* worklist); - bool scoutcrossingedge(list* missingshlist, list* boundedgelist, - list* crossedgelist, int* worklist); - void rearrangesubfaces(list* missingshlist, list* boundedgelist, - list* equatptlist, int* worklist); - void insertallsubfaces(queue* missingshqueue); - void constrainedfacets(); - - // Carving out holes and concavities routines. - void infecthull(memorypool *viri); - void plague(memorypool *viri); - void regionplague(memorypool *viri, REAL attribute, REAL volume); - void removeholetets(memorypool *viri); - void assignregionattribs(); - void carveholes(); - - // Steiner points removing routines. - void replacepolygonsubs(list* oldshlist, list* newshlist); - void orientnewsubs(list* newshlist, face* orientsh, REAL* norm); - bool constrainedflip(triface* flipface, triface* front, queue* flipque); - bool recoverfront(triface* front, list* newtetlist, queue* flipque); - void repairflips(queue* flipque); - bool constrainedcavity(triface* oldtet, list* floorlist, list* ceillist, - list* ptlist, list* frontlist, list* misfrontlist, - list* newtetlist, queue* flipque); - void expandsteinercavity(point steinpt, REAL eps, list* frontlist, list*); - bool findrelocatepoint(point sp, point np, REAL* n, list*, list*); - void relocatepoint(point steinpt, triface* oldtet, list*, list*, queue*); - bool findcollapseedge(point suppt, point* conpt, list* oldtetlist, list*); - void collapseedge(point suppt, point conpt, list* oldtetlist, list*); - void deallocfaketets(list* frontlist); - void restorepolyhedron(list* oldtetlist); - bool suppressfacetpoint(face* supsh, list* frontlist, list* misfrontlist, - list* ptlist, list* conlist, memorypool* viri, - queue* flipque, bool noreloc, bool optflag); - bool suppresssegpoint(face* supseg, list* spinshlist, list* newsegshlist, - list* frontlist, list* misfrontlist, list* ptlist, - list* conlist, memorypool* viri, queue* flipque, - bool noreloc, bool optflag); - bool suppressvolpoint(triface* suptet, list* frontlist, list* misfrontlist, - list* ptlist, queue* flipque, bool optflag); - bool smoothpoint(point smthpt, point, point, list *starlist, bool, REAL*); - void removesteiners(bool coarseflag); - - // Mesh reconstruction routines. - long reconstructmesh(); - // Constrained points insertion routines. - void insertconstrainedpoints(tetgenio *addio); - // Background mesh operations. - bool p1interpolatebgm(point pt, triface* bgmtet, long *scount); - void interpolatesizemap(); - void duplicatebgmesh(); - - // Delaunay refinement routines. - void marksharpsegments(REAL sharpangle); - void decidefeaturepointsizes(); - void enqueueencsub(face* ss, point encpt, int quenumber, REAL* cent); - badface* dequeueencsub(int* quenumber); - void enqueuebadtet(triface* tt, REAL key, REAL* cent); - badface* topbadtetra(); - void dequeuebadtet(); - bool checkseg4encroach(face* testseg, point testpt, point*, bool enqflag); - bool checksub4encroach(face* testsub, point testpt, bool enqflag); - bool checktet4badqual(triface* testtet, bool enqflag); - bool acceptsegpt(point segpt, point refpt, face* splitseg); - bool acceptfacpt(point facpt, list* subceillist, list* verlist); - bool acceptvolpt(point volpt, list* ceillist, list* verlist); - void getsplitpoint(point e1, point e2, point refpt, point newpt); - void shepardinterpolate(point newpt, list* verlist); - void setnewpointsize(point newpt, point e1, point e2); - void splitencseg(point, face*, list*, list*, list*,queue*,bool,bool,bool); - bool tallencsegs(point testpt, int n, list** ceillists); - bool tallencsubs(point testpt, int n, list** ceillists); - void tallbadtetrahedrons(); - void repairencsegs(bool chkencsub, bool chkbadtet); - void repairencsubs(bool chkbadtet); - void repairbadtets(); - void enforcequality(); - - // Mesh optimization routines. - void dumpbadtets(); - bool checktet4ill(triface* testtet, bool enqflag); - bool checktet4opt(triface* testtet, bool enqflag); - bool removeedge(badface* remedge, bool optflag); - bool smoothsliver(badface* remedge, list *starlist); - bool splitsliver(badface* remedge, list *tetlist, list *ceillist); - void tallslivers(bool optflag); - void optimizemesh(bool optflag); - - // I/O routines - void transfernodes(); - void jettisonnodes(); - void highorder(); - void outnodes(tetgenio* out); - void outmetrics(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); - void outpbcnodes(tetgenio* out); - void outsmesh(char* smfilename); - void outmesh2medit(char* mfilename); - void outmesh2gid(char* gfilename); - void outmesh2off(char* ofilename); - - // User interaction routines. - void internalerror(); - void checkmesh(); - void checkshells(); - void checkdelaunay(REAL eps, queue* flipqueue); - void checkconforming(); - void algorithmicstatistics(); - void qualitystatistics(); - void statistics(); - - public: - - // Constructor and destructor. - tetgenmesh(); - ~tetgenmesh(); - -}; // End of class tetgenmesh. - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedralize() Interface for using TetGen's library to generate // -// Delaunay tetrahedralizations, constrained Delaunay // -// tetrahedralizations, quality tetrahedral meshes. // -// // -// 'in' is an object of 'tetgenio' 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