commit 8731e9d63373944b41b33ae9535b6d843f800a9c Author: Hang Si Date: Fri Apr 15 23:35:00 2011 +0200 TetGen version 1.4.3 - Most recent variant of 1.4.3 version from www.wias-berlin.de - Also found as upstream version for Ubunto distribution: https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/tetgen/1.4.3-1/tetgen_1.4.3.orig.tar.gz diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..65e5262 --- /dev/null +++ b/LICENSE @@ -0,0 +1,66 @@ +TetGen License +-------------- + +The software (TetGen) is licensed under the terms of the MIT license +with the following exceptions: + +Distribution of modified versions of this code is permissible UNDER +THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE +SAME SOURCE FILES tetgen.h AND tetgen.cxx REMAIN UNDER COPYRIGHT OF +THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY +AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE +MODIFICATIONS. + +Distribution of this code for any commercial purpose is permissible +ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNER. + +The full license text is reproduced below. + +This means that TetGen is no free software, but for private, research, +and educational purposes it can be used at absolutely no cost and +without further arrangements. + + +For details, see http://tetgen.berlios.de + +============================================================================== + +TetGen +A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator +Version 1.4 (Released on January 14, 2006). + +Copyright 2002, 2004, 2005, 2006 +Hang Si +Rathausstr. 9, 10178 Berlin, Germany +si@wias-berlin.de + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +Distribution of modified versions of this code is permissible UNDER +THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE +SAME SOURCE FILES tetgen.h AND tetgen.cxx REMAIN UNDER COPYRIGHT OF +THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY +AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE +MODIFICATIONS. + +Distribution of this code for any commercial purpose is permissible +ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNER. + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +============================================================================== \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..50a2c3f --- /dev/null +++ b/README @@ -0,0 +1,16 @@ +This is TetGen version 1.4.3 (released on September 6, 2009, updated on December 13, 2010, updated on January 19, 2011, updated on April 15, 2011) + +Please see the documentation of TetGen for compiling and using TetGen. +It is available at the following link: + + 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 + +Thank you and enjoy! + +Hang Si +January 19, 2011 diff --git a/example.poly b/example.poly new file mode 100644 index 0000000..d49c54c --- /dev/null +++ b/example.poly @@ -0,0 +1,132 @@ +# +# example.poly - Sample file of TetGen. +# +# A .poly file describes a piecewise linear complex (PLC) +# This file represents a compensated magic tee junction. +# +# The source file is from: Vali Catina +# + +# Part 1 - node list +54 3 0 0 + 1 -13.716000000000001 -5.0800000000000001 0 + 2 -13.716000000000001 5.0800000000000001 0 + 3 -11.43 5.0800000000000001 0 + 4 11.43 -5.0800000000000001 0 + 5 11.43 7.3659999999999997 0 + 6 -11.43 7.3659999999999997 0 + 7 0.95105651629515364 -0.37999999999999989 0 + 8 -0.95105651629515364 -0.37999999999999989 0 + 9 -0.95105651629515364 4.6200000000000001 0 + 10 0.95105651629515364 4.6200000000000001 0 + 11 -0.95105651629515353 4.6200000000000001 -0.30901699437494756 + 12 -0.58778525229247303 4.6200000000000001 -0.80901699437494756 + 13 1.2246063538223773e-16 4.6200000000000001 -1 + 14 0.58778525229247325 4.6200000000000001 -0.80901699437494734 + 15 0.95105651629515364 4.6200000000000001 -0.30901699437494734 + 16 -0.95105651629515353 -0.37999999999999989 -0.30901699437494756 + 17 -0.58778525229247303 -0.37999999999999989 -0.80901699437494756 + 18 1.2246063538223773e-16 -0.37999999999999989 -1 + 19 0.58778525229247325 -0.37999999999999989 -0.80901699437494734 + 20 0.95105651629515364 -0.37999999999999989 -0.30901699437494734 + 21 -1.5874999999999999 -0.37999999999999989 -2.9160938800395356e-16 + 22 -1.5098022196185561 -0.37999999999999989 -0.49056447857022928 + 23 -1.2843144785702287 -0.37999999999999989 -0.93310908801430126 + 24 -0.93310908801430081 -0.37999999999999989 -1.2843144785702292 + 25 -0.49056447857022878 -0.37999999999999989 -1.5098022196185563 + 26 1.9440625866930238e-16 -0.37999999999999989 -1.5874999999999999 + 27 0.49056447857022917 -0.37999999999999989 -1.5098022196185561 + 28 0.93310908801430115 -0.37999999999999989 -1.284314478570229 + 29 1.284314478570229 -0.37999999999999989 -0.93310908801430092 + 30 1.5098022196185563 -0.37999999999999989 -0.49056447857022889 + 31 1.5874999999999999 -0.37999999999999989 9.7203129334651192e-17 + 32 -1.5874999999999999 -5.0800000000000001 -2.9160938800395356e-16 + 33 -1.5098022196185561 -5.0800000000000001 -0.49056447857022928 + 34 -1.2843144785702287 -5.0800000000000001 -0.93310908801430126 + 35 -0.93310908801430081 -5.0800000000000001 -1.2843144785702292 + 36 -0.49056447857022878 -5.0800000000000001 -1.5098022196185563 + 37 1.9440625866930238e-16 -5.0800000000000001 -1.5874999999999999 + 38 0.49056447857022917 -5.0800000000000001 -1.5098022196185561 + 39 0.93310908801430115 -5.0800000000000001 -1.284314478570229 + 40 1.284314478570229 -5.0800000000000001 -0.93310908801430092 + 41 1.5098022196185563 -5.0800000000000001 -0.49056447857022889 + 42 1.5874999999999999 -5.0800000000000001 9.7203129334651192e-17 + 43 -11.43 7.3659999999999997 -5.0800000000000001 + 44 11.43 7.3659999999999997 -5.0800000000000001 + 45 -11.43 5.0800000000000001 -5.0800000000000001 + 46 11.43 5.0800000000000001 -5.0800000000000001 + 47 -13.716000000000001 5.0800000000000001 -11.43 + 48 -13.716000000000001 -5.0800000000000001 -11.43 + 49 -11.43 -5.0800000000000001 -11.43 + 50 -11.43 5.0800000000000001 -11.43 + 51 -11.43 -5.0800000000000001 -13.715999999999999 + 52 11.43 -5.0800000000000001 -13.715999999999999 + 53 11.43 5.0800000000000001 -13.715999999999999 + 54 -11.43 5.0800000000000001 -13.715999999999999 + +# Part 2 - facet list +29 1 +1 0 1 +14 1 2 3 6 5 4 42 31 7 10 9 8 21 32 +1 0 1 +7 9 10 15 14 13 12 11 +1 0 1 +4 8 9 11 16 +1 0 1 +4 11 12 17 16 +1 0 1 +4 12 13 18 17 +1 0 1 +4 13 14 19 18 +1 0 1 +4 14 15 20 19 +1 0 1 +4 10 7 20 15 +1 0 1 +18 7 31 30 29 28 27 26 25 24 23 22 21 8 16 17 18 19 20 +1 0 1 +4 21 22 33 32 +1 0 1 +4 22 23 34 33 +1 0 1 +4 23 24 35 34 +1 0 1 +4 24 25 36 35 +1 0 1 +4 25 26 37 36 +1 0 1 +4 26 27 38 37 +1 0 1 +4 27 28 39 38 +1 0 1 +4 28 29 40 39 +1 0 1 +4 29 30 41 40 +1 0 1 +4 30 31 42 41 +1 0 1 +4 43 45 46 44 +1 0 1 +4 5 6 43 44 +1 0 1 +6 4 5 44 46 53 52 +1 0 1 +4 6 3 45 43 +1 0 1 +4 47 48 49 50 +1 0 1 +8 3 2 47 50 54 53 46 45 +1 0 1 +17 42 4 52 51 49 48 1 32 33 34 35 36 37 38 39 40 41 +1 0 1 +4 2 1 48 47 +1 0 1 +4 50 49 51 54 +1 0 1 +4 54 51 52 53 + +# Part 3 - hole list +0 + +# Part 4 - region list +0 diff --git a/makefile b/makefile new file mode 100644 index 0000000..5c886a3 --- /dev/null +++ b/makefile @@ -0,0 +1,71 @@ +############################################################################### +# # +# makefile for TetGen # +# # +# Type "make" to compile TetGen into an executable program (tetgen). # +# Type "make tetlib" to compile TetGen into a library (libtet.a). # +# Type "make distclean" to delete all object (*.o) files. # +# # +############################################################################### + +# CXX should be set to the name of your favorite C++ compiler. +# =========================================================== + +CXX = g++ +#CXX = icpc +#CXX = CC + +# CXXFLAGS is the level of optimiztion, default is -O. One should try +# -O2, -O3 ... to find the best optimization level. +# =================================================================== + +CXXFLAGS = -g + +# PREDCXXFLAGS is for compiling J. Shewchuk's predicates. It should +# always be equal to -O0 (no optimization). Otherwise, TetGen may not +# work properly. + +PREDCXXFLAGS = -O0 + +# SWITCHES is a list of switches to compile TetGen. +# ================================================= +# +# By default, TetGen uses double precision floating point numbers. If you +# prefer single precision, use the -DSINGLE switch. +# +# The source code of TetGen includes a lot of assertions, which are mainly +# used for catching bugs at that places. These assertions somewhat slow +# down the speed of TetGen. They can be skipped by define the -DNDEBUG +# switch. + +SWITCHES = -Wall -DSELF_CHECK + +# SWITCHES = -Wall -Wabi -Wctor-dtor-privacy \ +# -Woverloaded-virtual -Wno-pmf-conversions -Wsign-promo \ +# -Wsynth -Wchar-subscripts -Wconversion -Wsign-compare \ +# -Wcomment -Wimplicit -Wmissing-braces -Wparentheses \ +# -Wreturn-type -Wswitch -Wswitch-default \ +# -Wswitch-enum -Wtrigraphs -W -DSELF_CHECK + +# RM should be set to the name of your favorite rm (file deletion program). + +RM = /bin/rm + +# The action starts here. + +tetgen: tetgen.cxx predicates.o + $(CXX) $(CXXFLAGS) $(SWITCHES) -o tetgen tetgen.cxx predicates.o -lm + +tetlib: tetgen.cxx predicates.o + $(CXX) $(CXXFLAGS) $(SWITCHES) -DTETLIBRARY -c tetgen.cxx + ar r libtet.a tetgen.o predicates.o + +predicates.o: predicates.cxx + $(CXX) $(PREDCXXFLAGS) -c predicates.cxx + +clean: + $(RM) *.o *.a tetgen *~ + + + + diff --git a/predicates.cxx b/predicates.cxx new file mode 100644 index 0000000..bc0bd39 --- /dev/null +++ b/predicates.cxx @@ -0,0 +1,4187 @@ +/*****************************************************************************/ +/* */ +/* Routines for Arbitrary Precision Floating-point Arithmetic */ +/* and Fast Robust Geometric Predicates */ +/* (predicates.c) */ +/* */ +/* May 18, 1996 */ +/* */ +/* Placed in the public domain by */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This file contains C implementation of algorithms for exact addition */ +/* and multiplication of floating-point numbers, and predicates for */ +/* robustly performing the orientation and incircle tests used in */ +/* computational geometry. The algorithms and underlying theory are */ +/* described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- */ +/* Point Arithmetic and Fast Robust Geometric Predicates." Technical */ +/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */ +/* University, Pittsburgh, Pennsylvania, May 1996. (Submitted to */ +/* Discrete & Computational Geometry.) */ +/* */ +/* This file, the paper listed above, and other information are available */ +/* from the Web page http://www.cs.cmu.edu/~quake/robust.html . */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* Using this code: */ +/* */ +/* First, read the short or long version of the paper (from the Web page */ +/* above). */ +/* */ +/* Be sure to call exactinit() once, before calling any of the arithmetic */ +/* functions or geometric predicates. Also be sure to turn on the */ +/* optimizer when compiling this file. */ +/* */ +/* */ +/* Several geometric predicates are defined. Their parameters are all */ +/* points. Each point is an array of two or three floating-point */ +/* numbers. The geometric predicates, described in the papers, are */ +/* */ +/* orient2d(pa, pb, pc) */ +/* orient2dfast(pa, pb, pc) */ +/* orient3d(pa, pb, pc, pd) */ +/* orient3dfast(pa, pb, pc, pd) */ +/* incircle(pa, pb, pc, pd) */ +/* incirclefast(pa, pb, pc, pd) */ +/* insphere(pa, pb, pc, pd, pe) */ +/* inspherefast(pa, pb, pc, pd, pe) */ +/* */ +/* Those with suffix "fast" are approximate, non-robust versions. Those */ +/* without the suffix are adaptive precision, robust versions. There */ +/* are also versions with the suffices "exact" and "slow", which are */ +/* non-adaptive, exact arithmetic versions, which I use only for timings */ +/* in my arithmetic papers. */ +/* */ +/* */ +/* An expansion is represented by an array of floating-point numbers, */ +/* sorted from smallest to largest magnitude (possibly with interspersed */ +/* zeros). The length of each expansion is stored as a separate integer, */ +/* and each arithmetic function returns an integer which is the length */ +/* of the expansion it created. */ +/* */ +/* Several arithmetic functions are defined. Their parameters are */ +/* */ +/* e, f Input expansions */ +/* elen, flen Lengths of input expansions (must be >= 1) */ +/* h Output expansion */ +/* b Input scalar */ +/* */ +/* The arithmetic functions are */ +/* */ +/* grow_expansion(elen, e, b, h) */ +/* grow_expansion_zeroelim(elen, e, b, h) */ +/* expansion_sum(elen, e, flen, f, h) */ +/* expansion_sum_zeroelim1(elen, e, flen, f, h) */ +/* expansion_sum_zeroelim2(elen, e, flen, f, h) */ +/* fast_expansion_sum(elen, e, flen, f, h) */ +/* fast_expansion_sum_zeroelim(elen, e, flen, f, h) */ +/* linear_expansion_sum(elen, e, flen, f, h) */ +/* linear_expansion_sum_zeroelim(elen, e, flen, f, h) */ +/* scale_expansion(elen, e, b, h) */ +/* scale_expansion_zeroelim(elen, e, b, h) */ +/* compress(elen, e, h) */ +/* */ +/* All of these are described in the long version of the paper; some are */ +/* described in the short version. All return an integer that is the */ +/* length of h. Those with suffix _zeroelim perform zero elimination, */ +/* and are recommended over their counterparts. The procedure */ +/* fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on */ +/* processors that do not use the round-to-even tiebreaking rule) is */ +/* recommended over expansion_sum_zeroelim(). Each procedure has a */ +/* little note next to it (in the code below) that tells you whether or */ +/* not the output expansion may be the same array as one of the input */ +/* expansions. */ +/* */ +/* */ +/* If you look around below, you'll also find macros for a bunch of */ +/* simple unrolled arithmetic operations, and procedures for printing */ +/* expansions (commented out because they don't work with all C */ +/* compilers) and for generating random floating-point numbers whose */ +/* significand bits are all random. Most of the macros have undocumented */ +/* requirements that certain of their parameters should not be the same */ +/* variable; for safety, better to make sure all the parameters are */ +/* distinct variables. Feel free to send email to jrs@cs.cmu.edu if you */ +/* have questions. */ +/* */ +/*****************************************************************************/ + +#include +#include +#include +#ifdef CPU86 +#include +#endif /* CPU86 */ +#ifdef LINUX +#include +#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/tetgen.cxx b/tetgen.cxx new file mode 100644 index 0000000..255a13f --- /dev/null +++ b/tetgen.cxx @@ -0,0 +1,34926 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// TetGen // +// // +// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// // +// Version 1.4 // +// September 6, December 13, 2010 // +// January 19, April 15, 2011 +// // +// Copyright (C) 2002--2011 // +// Hang Si // +// Research Group: Numerical Mathematics and Scientific Computing // +// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // +// Mohrenstr. 39, 10117 Berlin, Germany // +// si@wias-berlin.de // +// // +// TetGen is 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" + +//// io_cxx /////////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_node_call() Read a list of points from a file. // +// // +// 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 used in error messages. // +// // +// The 'firstnumber' (0 or 1) is automatically determined by the number of // +// the first index of the first point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_node_call(FILE* infile, int markers, char* infilename) +{ + char inputline[INPUTLINESIZE]; + char *stringptr; + REAL x, y, z, attrib; + int firstnode, currentmarker; + int index, attribindex; + int i, j; + + // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'. + pointlist = new REAL[numberofpoints * 3]; + if (pointlist == (REAL *) NULL) { + terminatetetgen(1); + } + if (numberofpointattributes > 0) { + pointattributelist = new REAL[numberofpoints * numberofpointattributes]; + if (pointattributelist == (REAL *) NULL) { + terminatetetgen(1); + } + } + if (markers) { + pointmarkerlist = new int[numberofpoints]; + if (pointmarkerlist == (int *) NULL) { + terminatetetgen(1); + } + } + + // 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 points from a .node file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_node(char* filebasename) +{ + FILE *infile; + char innodefilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + bool okflag; + int markers; + + // Assembling the actual file names we want to open. + strcpy(innodefilename, filebasename); + strcat(innodefilename, ".node"); + + // Try to open a .node file. + infile = fopen(innodefilename, "r"); + if (infile == (FILE *) NULL) { + printf("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); + // Does this file contain an index colume? + stringptr = strstr(inputline, "rbox"); + if (stringptr == NULL) { + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = inputline; + numberofpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + mesh_dim = 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; + } + + // Load the list of nodes. + okflag = load_node_call(infile, markers, innodefilename); + + fclose(infile); + return okflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_var() Load constraints applied on facets, segments, and nodes // +// from a .var file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_var(char* filebasename) +{ + FILE *infile; + char varfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + int index; + int i; + + // Variant constraints are saved in file "filename.var". + strcpy(varfilename, filebasename); + strcat(varfilename, ".var"); + infile = fopen(varfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", varfilename); + } else { + // No such file. Ignore it without a message. + return false; + } + + // Read the facet constraint section. + stringptr = readnumberline(inputline, infile, varfilename); + if (*stringptr != '\0') { + numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0); + } else { + numberoffacetconstraints = 0; + } + if (numberoffacetconstraints > 0) { + // Initialize 'facetconstraintlist'. + facetconstraintlist = new REAL[numberoffacetconstraints * 2]; + index = 0; + for (i = 0; i < numberoffacetconstraints; i++) { + stringptr = readnumberline(inputline, infile, varfilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: facet constraint %d has no facet marker.\n", + firstnumber + i); + break; + } else { + facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: facet constraint %d has no maximum area bound.\n", + firstnumber + i); + break; + } else { + facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + if (i < numberoffacetconstraints) { + // This must be caused by an error. + fclose(infile); + return false; + } + } + + // Read the segment constraint section. + stringptr = readnumberline(inputline, infile, varfilename); + if (*stringptr != '\0') { + numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0); + } else { + numberofsegmentconstraints = 0; + } + if (numberofsegmentconstraints > 0) { + // Initialize 'segmentconstraintlist'. + segmentconstraintlist = new REAL[numberofsegmentconstraints * 3]; + index = 0; + for (i = 0; i < numberofsegmentconstraints; i++) { + stringptr = readnumberline(inputline, infile, varfilename); + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no frist endpoint.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no second endpoint.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: segment constraint %d has no maximum length bound.\n", + firstnumber + i); + break; + } else { + segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + } + if (i < numberofsegmentconstraints) { + // This must be caused by an error. + fclose(infile); + return false; + } + } + + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_mtr() Load a size specification map from a .mtr file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_mtr(char* filebasename) +{ + FILE *infile; + char mtrfilename[FILENAMESIZE]; + char inputline[INPUTLINESIZE]; + char *stringptr; + REAL mtr; + int mtrindex; + int i, j; + + strcpy(mtrfilename, filebasename); + strcat(mtrfilename, ".mtr"); + infile = fopen(mtrfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", mtrfilename); + } else { + // No such file. Return. + return false; + } + + // Read 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) { + terminatetetgen(1); + } + mtrindex = 0; + for (i = 0; i < numberofpoints; i++) { + // Read metrics. + stringptr = readnumberline(inputline, infile, mtrfilename); + for (j = 0; j < numberofpointmtrs; j++) { + if (*stringptr == '\0') { + printf("Error: Metric %d is missing value #%d in %s.\n", + i + firstnumber, j + 1, mtrfilename); + terminatetetgen(1); + } + mtr = (REAL) strtod(stringptr, &stringptr); + pointmtrlist[mtrindex++] = mtr; + stringptr = findnextnumber(stringptr); + } + } + + fclose(infile); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_poly() Load a PL complex from a .poly or a .smesh file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_poly(char* filebasename) +{ + FILE *infile, *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, filebasename); + strcpy(inpolyfilename, filebasename); + strcpy(insmeshfilename, filebasename); + 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(filebasename); + + // Try to load a .mtr file if it exists. + load_mtr(filebasename); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_off() Load a polyhedron from a .off file. // +// // +// The .off format is one of file formats of the Geomview, an interactive // +// program for viewing and manipulating geometric objects. More information // +// is available form: http://www.geomview.org. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_off(char* filebasename) +{ + FILE *fp; + tetgenio::facet *f; + tetgenio::polygon *p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp; + double *coord; + int nverts = 0, iverts = 0; + int nfaces = 0, ifaces = 0; + int nedges = 0; + int line_count = 0, i; + + strncpy(infilename, filebasename, 1024 - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".off") != 0) { + strcat(infilename, ".off"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("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 from a .ply file. // +// // +// This is a simplified version of reading .ply files, which only reads the // +// set of vertices and the set of faces. Other informations (such as color, // +// material, texture, etc) in .ply file are ignored. Complete routines for // +// reading and writing ,ply files are available from: http://www.cc.gatech. // +// edu/projects/large_models/ply.html. Except the header section, ply file // +// format has exactly the same format for listing vertices and polygons as // +// off file format. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_ply(char* filebasename) +{ + FILE *fp; + tetgenio::facet *f; + tetgenio::polygon *p; + char infilename[FILENAMESIZE]; + char buffer[INPUTLINESIZE]; + char *bufferp, *str; + double *coord; + int endheader = 0, format = 0; + int nverts = 0, iverts = 0; + int nfaces = 0, ifaces = 0; + int line_count = 0, i; + + strncpy(infilename, filebasename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".ply") != 0) { + strcat(infilename, ".ply"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // 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 from a .stl file. // +// // +// The .stl or stereolithography format is an ASCII or binary file used in // +// manufacturing. It is a list of the triangular surfaces that describe a // +// computer generated solid model. This is the standard input for most rapid // +// prototyping machines. // +// // +// Comment: A .stl file many contain many duplicated points. They will be // +// unified during the Delaunay tetrahedralization process. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_stl(char* filebasename) +{ + FILE *fp; + tetgenmesh::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, filebasename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) { + strcat(infilename, ".stl"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // STL file has no number of points available. Use a list to read points. + plist = new tetgenmesh::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 from a .mesh file. // +// // +// The .mesh format is the file format of Medit, a user-friendly interactive // +// mesh viewer 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* filebasename) +{ + 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, filebasename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) { + strcat(infilename, ".mesh"); + } + + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // Default uses the index starts from '1'. + firstnumber = 1; + + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + if (*bufferp == '#') continue; // A comment line is skipped. + if (dimension == 0) { + // Find if it is the keyword "Dimension". + str = strstr(bufferp, "Dimension"); + if (!str) str = strstr(bufferp, "dimension"); + if (!str) str = strstr(bufferp, "DIMENSION"); + if (str) { + // Read the dimensions + bufferp = findnextnumber(str); // Skip field "Dimension". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + dimension = (int) strtol(bufferp, &bufferp, 0); + if (dimension != 2 && dimension != 3) { + printf("Unknown dimension in file on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + mesh_dim = dimension; + } + } + if (nverts == 0) { + // Find if it is the keyword "Vertices". + str = strstr(bufferp, "Vertices"); + if (!str) str = strstr(bufferp, "vertices"); + if (!str) str = strstr(bufferp, "VERTICES"); + if (str) { + // Read the number of vertices. + bufferp = findnextnumber(str); // Skip field "Vertices". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + nverts = (int) strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + } + // Read the follwoing node list. + for (i = 0; i < nverts; i++) { + bufferp = readline(buffer, fp, &line_count); + if (bufferp == NULL) { + printf("Unexpected end of file on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + // Read vertex coordinates + coord = &pointlist[i * 3]; + for (j = 0; j < 3; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + if ((j < 2) || (dimension == 3)) { + coord[j] = (REAL) strtod(bufferp, &bufferp); + } else { + assert((j == 2) && (dimension == 2)); + coord[j] = 0.0; + } + bufferp = findnextnumber(bufferp); + } + } + continue; + } + } + if (nfaces == 0) { + // Find if it is the keyword "Triangles" or "Quadrilaterals". + corners = 0; + str = strstr(bufferp, "Triangles"); + if (!str) str = strstr(bufferp, "triangles"); + if (!str) str = strstr(bufferp, "TRIANGLES"); + if (str) { + corners = 3; + } else { + str = strstr(bufferp, "Quadrilaterals"); + if (!str) str = strstr(bufferp, "quadrilaterals"); + if (!str) str = strstr(bufferp, "QUADRILATERALS"); + if (str) { + corners = 4; + } + } + if (corners == 3 || corners == 4) { + // Read the number of triangles (or quadrilaterals). + bufferp = findnextnumber(str); // Skip field "Triangles". + if (*bufferp == '\0') { + // Read a non-empty line. + bufferp = readline(buffer, fp, &line_count); + } + nfaces = strtol(bufferp, &bufferp, 0); + // Allocate memory for 'tetgenio' + if (nfaces > 0) { + if (numberoffacets > 0) { + // facetlist has already been allocated. Enlarge arrays. + tmpflist = new tetgenio::facet[numberoffacets + nfaces]; + tmpfmlist = new int[numberoffacets + nfaces]; + // Copy the data of old arrays into new arrays. + for (i = 0; i < numberoffacets; i++) { + f = &(tmpflist[i]); + tetgenio::init(f); + *f = facetlist[i]; + tmpfmlist[i] = facetmarkerlist[i]; + } + // Release old arrays. + delete [] facetlist; + delete [] facetmarkerlist; + // Remember the new arrays. + facetlist = tmpflist; + facetmarkerlist = tmpfmlist; + } else { + // This is the first time to allocate facetlist. + facetlist = new tetgenio::facet[nfaces]; + facetmarkerlist = new int[nfaces]; + } + } + // Read the following list of faces. + for (i = numberoffacets; i < numberoffacets + nfaces; i++) { + bufferp = readline(buffer, fp, &line_count); + if (bufferp == NULL) { + printf("Unexpected end of file on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + f = &facetlist[i]; + tetgenio::init(f); + // In .mesh format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + tetgenio::init(p); + p->numberofvertices = corners; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + // Read the vertices of the face. + for (j = 0; j < corners; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading face on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + p->vertexlist[j] = (int) strtol(bufferp, &bufferp, 0); + if (firstnumber == 1) { + // Check if a '0' index appears. + if (p->vertexlist[j] == 0) { + // The first index is set to be 0. + firstnumber = 0; + } + } + bufferp = findnextnumber(bufferp); + } + // Read the marker of the face if it exists. + facetmarkerlist[i] = 0; + if (*bufferp != '\0') { + facetmarkerlist[i] = (int) strtol(bufferp, &bufferp, 0); + } + } + // Have read in a list of triangles/quads. + numberoffacets += nfaces; + nfaces = 0; + } + } + // if (nverts > 0 && nfaces > 0) break; // Ignore other data. + } + + // Close file + fclose(fp); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). // +// // +// This function is contributed by: Bryn Lloyd, Computer Vision Laborator, // +// ETH, Zuerich. May 7, 2007. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_vtk(char* filebasename) +{ + FILE *fp; + tetgenio::facet *f; + tetgenio::polygon *p; + char infilename[FILENAMESIZE]; + char line[INPUTLINESIZE]; + char mode[128], id[256], fmt[64]; + char *bufferp; + double *coord; + float _x, _y, _z; + int nverts = 0; + int nfaces = 0; + int line_count = 0; + int dummy; + int id1, id2, id3; + int nn = -1; + int nn_old = -1; + int i, j; + bool ImALittleEndian = !testIsBigEndian(); + + strncpy(infilename, filebasename, FILENAMESIZE - 1); + infilename[FILENAMESIZE - 1] = '\0'; + if (infilename[0] == '\0') { + printf("Error: No filename.\n"); + return false; + } + if (strcmp(&infilename[strlen(infilename) - 4], ".vtk") != 0) { + strcat(infilename, ".vtk"); + } + if (!(fp = fopen(infilename, "r"))) { + printf("Error: Unable to open file %s\n", infilename); + return false; + } + printf("Opening %s.\n", infilename); + + // Default uses the index starts from '0'. + firstnumber = 0; + strcpy(mode, "BINARY"); + + while((bufferp = readline(line, fp, &line_count)) != NULL) { + if(strlen(line) == 0) continue; + //swallow lines beginning with a comment sign or white space + if(line[0] == '#' || line[0]=='\n' || line[0] == 10 || line[0] == 13 || + line[0] == 32) continue; + + sscanf(line, "%s", id); + if(!strcmp(id, "ASCII")) { + strcpy(mode, "ASCII"); + } + + if(!strcmp(id, "POINTS")) { + sscanf(line, "%s %d %s", id, &nverts, fmt); + if (nverts > 0) { + numberofpoints = nverts; + pointlist = new REAL[nverts * 3]; + } + + if(!strcmp(mode, "BINARY")) { + for(i = 0; i < nverts; i++) { + coord = &pointlist[i * 3]; + if(!strcmp(fmt, "double")) { + fread((char*)(&(coord[0])), sizeof(double), 1, fp); + fread((char*)(&(coord[1])), sizeof(double), 1, fp); + fread((char*)(&(coord[2])), sizeof(double), 1, fp); + if(ImALittleEndian){ + swapBytes((unsigned char *) &(coord[0]), sizeof(coord[0])); + swapBytes((unsigned char *) &(coord[1]), sizeof(coord[1])); + swapBytes((unsigned char *) &(coord[2]), sizeof(coord[2])); + } + } else if(!strcmp(fmt, "float")) { + fread((char*)(&_x), sizeof(float), 1, fp); + fread((char*)(&_y), sizeof(float), 1, fp); + fread((char*)(&_z), sizeof(float), 1, fp); + if(ImALittleEndian){ + swapBytes((unsigned char *) &_x, sizeof(_x)); + swapBytes((unsigned char *) &_y, sizeof(_y)); + swapBytes((unsigned char *) &_z, sizeof(_z)); + } + coord[0] = double(_x); + coord[1] = double(_y); + coord[2] = double(_z); + } else { + printf("Error: Only float or double formats are supported!\n"); + return false; + } + } + } else if(!strcmp(mode, "ASCII")) { + for(i = 0; i < nverts; i++){ + bufferp = readline(line, fp, &line_count); + if (bufferp == NULL) { + printf("Unexpected end of file on line %d in file %s\n", + line_count, infilename); + fclose(fp); + return false; + } + // Read vertex coordinates + coord = &pointlist[i * 3]; + for (j = 0; j < 3; j++) { + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line"); + printf(" %d in file %s\n", line_count, infilename); + fclose(fp); + return false; + } + coord[j] = (REAL) strtod(bufferp, &bufferp); + bufferp = findnextnumber(bufferp); + } + } + } + continue; + } + + if(!strcmp(id, "POLYGONS")) { + sscanf(line, "%s %d %d", id, &nfaces, &dummy); + if (nfaces > 0) { + numberoffacets = nfaces; + facetlist = new tetgenio::facet[nfaces]; + } + + if(!strcmp(mode, "BINARY")) { + for(i = 0; i < nfaces; i++){ + fread((char*)(&nn), sizeof(int), 1, fp); + if(ImALittleEndian){ + swapBytes((unsigned char *) &nn, sizeof(nn)); + } + if (i == 0) + nn_old = nn; + if (nn != nn_old) { + printf("Error: No mixed cells are allowed.\n"); + return false; + } + + if(nn == 3){ + fread((char*)(&id1), sizeof(int), 1, fp); + fread((char*)(&id2), sizeof(int), 1, fp); + fread((char*)(&id3), sizeof(int), 1, fp); + if(ImALittleEndian){ + swapBytes((unsigned char *) &id1, sizeof(id1)); + swapBytes((unsigned char *) &id2, sizeof(id2)); + swapBytes((unsigned char *) &id3, sizeof(id3)); + } + f = &facetlist[i]; + init(f); + // In .off format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Set number of vertices + p->numberofvertices = 3; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = id1; + p->vertexlist[1] = id2; + p->vertexlist[2] = id3; + } else { + printf("Error: Only triangles are supported\n"); + return false; + } + } + } else if(!strcmp(mode, "ASCII")) { + for(i = 0; i < nfaces; i++) { + bufferp = readline(line, fp, &line_count); + nn = (int) strtol(bufferp, &bufferp, 0); + if (i == 0) + nn_old = nn; + if (nn != nn_old) { + printf("Error: No mixed cells are allowed.\n"); + return false; + } + + if (nn == 3) { + bufferp = findnextnumber(bufferp); // Skip the first field. + id1 = (int) strtol(bufferp, &bufferp, 0); + bufferp = findnextnumber(bufferp); + id2 = (int) strtol(bufferp, &bufferp, 0); + bufferp = findnextnumber(bufferp); + id3 = (int) strtol(bufferp, &bufferp, 0); + f = &facetlist[i]; + init(f); + // In .off format, each facet has one polygon, no hole. + f->numberofpolygons = 1; + f->polygonlist = new tetgenio::polygon[1]; + p = &f->polygonlist[0]; + init(p); + // Set number of vertices + p->numberofvertices = 3; + // Allocate memory for face vertices + p->vertexlist = new int[p->numberofvertices]; + p->vertexlist[0] = id1; + p->vertexlist[1] = id2; + p->vertexlist[2] = id3; + } else { + printf("Error: Only triangles are supported.\n"); + return false; + } + } + } + + fclose(fp); + return true; + } + + if(!strcmp(id,"LINES") || !strcmp(id,"CELLS")){ + printf("Warning: load_vtk(): cannot read formats LINES, CELLS.\n"); + } + } // while () + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_plc() Load a piecewise linear complex from file(s). // +// // +// 'object' indicates which file format is used to describ the plc. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_plc(char* filebasename, int object) +{ + enum tetgenbehavior::objecttype type; + + type = (enum tetgenbehavior::objecttype) object; + switch (type) { + case tetgenbehavior::NODES: + return load_node(filebasename); + case tetgenbehavior::POLY: + return load_poly(filebasename); + case tetgenbehavior::OFF: + return load_off(filebasename); + case tetgenbehavior::PLY: + return load_ply(filebasename); + case tetgenbehavior::STL: + return load_stl(filebasename); + case tetgenbehavior::MEDIT: + return load_medit(filebasename); + case tetgenbehavior::VTK: + return load_vtk(filebasename); + default: + return load_poly(filebasename); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// load_tetmesh() Load a tetrahedral mesh from files. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenio::load_tetmesh(char* filebasename) +{ + 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, filebasename); + strcpy(inelefilename, filebasename); + strcpy(infacefilename, filebasename); + strcpy(inedgefilename, filebasename); + strcpy(involfilename, filebasename); + 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) { + terminatetetgen(1); + } + // Allocate memory for output tetrahedron attributes if necessary. + if (numberoftetrahedronattributes > 0) { + tetrahedronattributelist = new REAL[numberoftetrahedra * + numberoftetrahedronattributes]; + if (tetrahedronattributelist == (REAL *) NULL) { + terminatetetgen(1); + } + } + } + // Read the list of tetrahedra. + index = 0; + attribindex = 0; + for (i = 0; i < numberoftetrahedra; i++) { + // Read tetrahedron index and the tetrahedron's corners. + stringptr = readnumberline(inputline, infile, infilename); + for (j = 0; j < numberofcorners; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Tetrahedron %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Tetrahedron %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(1); + } + tetrahedronlist[index++] = corner; + } + // Read the tetrahedron's attributes. + for (j = 0; j < numberoftetrahedronattributes; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + attrib = 0.0; + } else { + attrib = (REAL) strtod(stringptr, &stringptr); + } + tetrahedronattributelist[attribindex++] = attrib; + } + } + fclose(infile); + } + } // 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) { + terminatetetgen(1); + } + if (markers) { + trifacemarkerlist = new int[numberoftrifaces]; + if (trifacemarkerlist == (int *) NULL) { + terminatetetgen(1); + } + } + } + // Read the list of faces. + index = 0; + for (i = 0; i < numberoftrifaces; i++) { + // Read face index and the face's three corners. + stringptr = readnumberline(inputline, infile, infilename); + for (j = 0; j < 3; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Face %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Face %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(1); + } + trifacelist[index++] = corner; + } + // Read the boundary marker if it exists. + if (markers) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + attrib = 0.0; + } else { + attrib = (REAL) strtod(stringptr, &stringptr); + } + trifacemarkerlist[i] = (int) attrib; + } + } + fclose(infile); + } + + // 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) { + terminatetetgen(1); + } + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + markers = 0; // Default value. + } else { + markers = (int) strtol (stringptr, &stringptr, 0); + } + if (markers > 0) { + edgemarkerlist = new int[numberofedges]; + } + } + // Read the list of faces. + index = 0; + for (i = 0; i < numberofedges; i++) { + // Read face index and the edge's two endpoints. + stringptr = readnumberline(inputline, infile, infilename); + for (j = 0; j < 2; j++) { + stringptr = findnextnumber(stringptr); + if (*stringptr == '\0') { + printf("Error: Edge %d is missing vertex %d in %s.\n", + i + firstnumber, j + 1, infilename); + terminatetetgen(1); + } + corner = (int) strtol(stringptr, &stringptr, 0); + if (corner < firstnumber || corner >= numberofpoints + firstnumber) { + printf("Error: Edge %d has an invalid vertex index.\n", + i + firstnumber); + terminatetetgen(1); + } + edgelist[index++] = corner; + } + // Read the edge marker if it has. + if (markers) { + stringptr = findnextnumber(stringptr); + edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0); + } + } + fclose(infile); + } + + // Read the volume constraints from a .vol file if it exists. + infilename = involfilename; + infile = fopen(infilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", infilename); + // Read number of tetrahedra. + stringptr = readnumberline(inputline, infile, infilename); + volelements = (int) strtol (stringptr, &stringptr, 0); + if (volelements != numberoftetrahedra) { + printf("Warning: %s and %s disagree on number of tetrahedra.\n", + inelefilename, involfilename); + volelements = 0; + } + if (volelements > 0) { + tetrahedronvolumelist = new REAL[volelements]; + if (tetrahedronvolumelist == (REAL *) NULL) { + 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(filebasename); + + // Try to read a .pbc file if it exists. + // load_pbc(filebasename); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_nodes() Save points to a .node file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_nodes(char* filebasename) +{ + FILE *fout; + char outnodefilename[FILENAMESIZE]; + char outmtrfilename[FILENAMESIZE]; + int i, j; + + sprintf(outnodefilename, "%s.node", filebasename); + printf("Saving nodes to %s\n", outnodefilename); + fout = fopen(outnodefilename, "w"); + fprintf(fout, "%d %d %d %d\n", numberofpoints, mesh_dim, + numberofpointattributes, pointmarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberofpoints; i++) { + if (mesh_dim == 2) { + fprintf(fout, "%d %.16g %.16g", i + firstnumber, pointlist[i * 3], + pointlist[i * 3 + 1]); + } else { + fprintf(fout, "%d %.16g %.16g %.16g", i + firstnumber, + pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]); + } + for (j = 0; j < numberofpointattributes; j++) { + fprintf(fout, " %.16g", + pointattributelist[i * numberofpointattributes + j]); + } + if (pointmarkerlist != NULL) { + fprintf(fout, " %d", pointmarkerlist[i]); + } + fprintf(fout, "\n"); + } + fclose(fout); + + // If the point metrics exist, output them to a .mtr file. + if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) NULL)) { + sprintf(outmtrfilename, "%s.mtr", filebasename); + printf("Saving metrics to %s\n", outmtrfilename); + fout = fopen(outmtrfilename, "w"); + fprintf(fout, "%d %d\n", numberofpoints, numberofpointmtrs); + for (i = 0; i < numberofpoints; i++) { + for (j = 0; j < numberofpointmtrs; j++) { + fprintf(fout, "%.16g ", pointmtrlist[i * numberofpointmtrs + j]); + } + fprintf(fout, "\n"); + } + fclose(fout); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_elements() Save elements to a .ele file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_elements(char* filebasename) +{ + FILE *fout; + char outelefilename[FILENAMESIZE]; + int i, j; + + sprintf(outelefilename, "%s.ele", filebasename); + printf("Saving elements to %s\n", outelefilename); + fout = fopen(outelefilename, "w"); + if (mesh_dim == 3) { + fprintf(fout, "%d %d %d\n", numberoftetrahedra, numberofcorners, + numberoftetrahedronattributes); + for (i = 0; i < numberoftetrahedra; i++) { + fprintf(fout, "%d", i + firstnumber); + for (j = 0; j < numberofcorners; j++) { + fprintf(fout, " %5d", tetrahedronlist[i * numberofcorners + j]); + } + for (j = 0; j < numberoftetrahedronattributes; j++) { + fprintf(fout, " %g", + tetrahedronattributelist[i * numberoftetrahedronattributes + j]); + } + fprintf(fout, "\n"); + } + } else { + // Save a two-dimensional mesh. + fprintf(fout, "%d %d %d\n",numberoftrifaces,3,trifacemarkerlist ? 1 : 0); + for (i = 0; i < numberoftrifaces; i++) { + fprintf(fout, "%d", i + firstnumber); + for (j = 0; j < 3; j++) { + fprintf(fout, " %5d", trifacelist[i * 3 + j]); + } + if (trifacemarkerlist != NULL) { + fprintf(fout, " %d", trifacemarkerlist[i]); + } + fprintf(fout, "\n"); + } + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_faces() Save faces to a .face file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_faces(char* filebasename) +{ + FILE *fout; + char outfacefilename[FILENAMESIZE]; + int i; + + sprintf(outfacefilename, "%s.face", filebasename); + printf("Saving faces to %s\n", outfacefilename); + fout = fopen(outfacefilename, "w"); + fprintf(fout, "%d %d\n", numberoftrifaces, + trifacemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberoftrifaces; i++) { + fprintf(fout, "%d %5d %5d %5d", i + firstnumber, trifacelist[i * 3], + trifacelist[i * 3 + 1], trifacelist[i * 3 + 2]); + if (trifacemarkerlist != NULL) { + fprintf(fout, " %d", trifacemarkerlist[i]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_edges() Save egdes to a .edge file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_edges(char* filebasename) +{ + FILE *fout; + char outedgefilename[FILENAMESIZE]; + int i; + + sprintf(outedgefilename, "%s.edge", filebasename); + printf("Saving edges to %s\n", outedgefilename); + fout = fopen(outedgefilename, "w"); + fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberofedges; i++) { + fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2], + edgelist[i * 2 + 1]); + if (edgemarkerlist != NULL) { + fprintf(fout, " %d", edgemarkerlist[i]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_neighbors() Save egdes to a .neigh file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_neighbors(char* filebasename) +{ + FILE *fout; + char outneighborfilename[FILENAMESIZE]; + int i; + + sprintf(outneighborfilename, "%s.neigh", filebasename); + printf("Saving neighbors to %s\n", outneighborfilename); + fout = fopen(outneighborfilename, "w"); + fprintf(fout, "%d %d\n", numberoftetrahedra, mesh_dim + 1); + for (i = 0; i < numberoftetrahedra; i++) { + if (mesh_dim == 2) { + fprintf(fout, "%d %5d %5d %5d", i + firstnumber, neighborlist[i * 3], + neighborlist[i * 3 + 1], neighborlist[i * 3 + 2]); + } else { + fprintf(fout, "%d %5d %5d %5d %5d", i + firstnumber, + neighborlist[i * 4], neighborlist[i * 4 + 1], + neighborlist[i * 4 + 2], neighborlist[i * 4 + 3]); + } + fprintf(fout, "\n"); + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// save_poly() Save segments or facets to a .poly file. // +// // +// It only save the facets, holes and regions. No .node file is saved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenio::save_poly(char* filebasename) +{ + FILE *fout; + facet *f; + polygon *p; + char outpolyfilename[FILENAMESIZE]; + int i, j, k; + + sprintf(outpolyfilename, "%s.poly", filebasename); + printf("Saving poly to %s\n", outpolyfilename); + fout = fopen(outpolyfilename, "w"); + + // The zero indicates that the vertices are in a separate .node file. + // Followed by number of dimensions, number of vertex attributes, + // and number of boundary markers (zero or one). + fprintf(fout, "%d %d %d %d\n", 0, mesh_dim, numberofpointattributes, + pointmarkerlist != NULL ? 1 : 0); + + // Save segments or facets. + if (mesh_dim == 2) { + // Number of segments, number of boundary markers (zero or one). + fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberofedges; i++) { + fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2], + edgelist[i * 2 + 1]); + if (edgemarkerlist != NULL) { + fprintf(fout, " %d", edgemarkerlist[i]); + } + fprintf(fout, "\n"); + } + } else { + // Number of facets, number of boundary markers (zero or one). + fprintf(fout, "%d %d\n", numberoffacets, facetmarkerlist != NULL ? 1 : 0); + for (i = 0; i < numberoffacets; i++) { + f = &(facetlist[i]); + fprintf(fout, "%d %d %d # %d\n", f->numberofpolygons,f->numberofholes, + facetmarkerlist != NULL ? facetmarkerlist[i] : 0, i + firstnumber); + // Output polygons of this facet. + for (j = 0; j < f->numberofpolygons; j++) { + p = &(f->polygonlist[j]); + fprintf(fout, "%d ", p->numberofvertices); + for (k = 0; k < p->numberofvertices; k++) { + if (((k + 1) % 10) == 0) { + fprintf(fout, "\n "); + } + fprintf(fout, " %d", p->vertexlist[k]); + } + fprintf(fout, "\n"); + } + // Output holes of this facet. + for (j = 0; j < f->numberofholes; j++) { + fprintf(fout, "%d %.12g %.12g %.12g\n", j + firstnumber, + f->holelist[j * 3], f->holelist[j * 3 + 1], f->holelist[j * 3 + 2]); + } + } + } + + // Save holes. + fprintf(fout, "%d\n", numberofholes); + for (i = 0; i < numberofholes; i++) { + // Output x, y coordinates. + fprintf(fout, "%d %.12g %.12g", i + firstnumber, holelist[i * mesh_dim], + holelist[i * mesh_dim + 1]); + if (mesh_dim == 3) { + // Output z coordinate. + fprintf(fout, " %.12g", holelist[i * mesh_dim + 2]); + } + fprintf(fout, "\n"); + } + + // Save regions. + fprintf(fout, "%d\n", numberofregions); + for (i = 0; i < numberofregions; i++) { + if (mesh_dim == 2) { + // Output the index, x, y coordinates, attribute (region number) + // and maximum area constraint (maybe -1). + fprintf(fout, "%d %.12g %.12g %.12g %.12g\n", i + firstnumber, + regionlist[i * 4], regionlist[i * 4 + 1], + regionlist[i * 4 + 2], regionlist[i * 4 + 3]); + } else { + // Output the index, x, y, z coordinates, attribute (region number) + // and maximum volume constraint (maybe -1). + fprintf(fout, "%d %.12g %.12g %.12g %.12g %.12g\n", i + firstnumber, + regionlist[i * 5], regionlist[i * 5 + 1], + regionlist[i * 5 + 2], regionlist[i * 5 + 3], + regionlist[i * 5 + 4]); + } + } + + fclose(fout); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; +} + +//// //// +//// //// +//// io_cxx /////////////////////////////////////////////////////////////////// + +//// behavior_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// syntax() Print list of command line switches. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenbehavior::syntax() +{ + printf(" tetgen [-prq_a_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 Refines mesh (to improve mesh quality).\n"); + printf(" -a Applies a maximum tetrahedron volume constraint.\n"); + printf(" -A Assigns attributes to tetrahedra in different regions.\n"); + printf(" -i Inserts a list of additional points into mesh.\n"); + printf(" -M No merge of coplanar facets.\n"); + printf(" -Y No splitting of input boundaries (facets and segments).\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.\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(" -K Outputs mesh to .vtk file for viewing by Paraview.\n"); + printf(" -J No jettison of unused vertices from output .node file.\n"); + printf(" -B Suppresses output of boundary information.\n"); + printf(" -N Suppresses output of .node file.\n"); + printf(" -E Suppresses output of .ele file.\n"); + printf(" -F Suppresses output of .face file.\n"); + printf(" -I Suppresses mesh iteration numbers.\n"); + printf(" -C Checks the consistency of the final mesh.\n"); + printf(" -Q Quiet: No terminal output except errors.\n"); + printf(" -V Verbose: Detailed information, more terminal output.\n"); + printf(" -h Help: A brief instruction for using TetGen.\n"); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// usage() Print a brief instruction for using TetGen. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenbehavior::usage() +{ + printf("TetGen\n"); + printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); + printf("Triangulator\n"); + //versioninfo(); + printf("Version 1.4.3 (January 19, 2011).\n"); + printf("\n"); + printf("Copyright (C) 2002 - 2011\n"); + printf("Hang Si\n"); + printf("Mohrenstr. 39, 10117 Berlin, Germany\n"); + printf("si@wias-berlin.de\n"); + printf("\n"); + printf("What Can TetGen Do?\n"); + printf("\n"); + printf(" TetGen generates exact Delaunay tetrahedralizations, exact\n"); + printf(" constrained Delaunay tetrahedralizations, and quality "); + printf("tetrahedral\n meshes. The latter are nicely graded and whose "); + printf("tetrahedra have\n radius-edge ratio bounded, thus are suitable "); + printf("for finite element and\n finite volume analysis.\n"); + printf("\n"); + printf("Command Line Syntax:\n"); + printf("\n"); + printf(" Below is the 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 \n"); + terminatetetgen(0); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// parse_commandline() Read the command line, identify switches, and set // +// up options and file names. // +// // +// 'argc' and 'argv' are the same parameters passed to the function main() // +// of a C/C++ program. They together represent the command line user invoked // +// from an environment in which TetGen is running. // +// // +// When TetGen is invoked from an environment. 'argc' is nonzero, switches // +// and input filename should be supplied as zero-terminated strings in // +// argv[0] through argv[argc - 1] and argv[0] shall be the name used to // +// invoke TetGen, i.e. "tetgen". Switches are previously started with a // +// dash '-' to identify them from the input filename. // +// // +// When TetGen is called from within another program. 'argc' is set to zero. // +// switches are given in one zero-terminated string (no previous dash is // +// required.), and 'argv' is a pointer points to this string. No input // +// filename is required (usually the input data has been directly created by // +// user in the 'tetgenio' structure). A default filename 'tetgen-tmpfile' // +// will be created for debugging output purpose. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenbehavior::parse_commandline(int argc, char **argv) +{ + int startindex; + int increment; + int meshnumber; + int scount; + 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++; + } else if (argv[i][j] == 'R') { + coarse = 1; + } else if (argv[i][j] == 'q') { + quality++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + if (quality == 1) { + minratio = (REAL) strtod(workstring, (char **) NULL); + } else if (quality == 2) { + mindihedral = (REAL) strtod(workstring, (char **) NULL); + } else if (quality == 3) { + maxdihedral = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'm') { + metric++; + 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] == 'u') { + // Set the maximum btree node size, -u0 means do not use btree. + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + max_btreenode_size = (int) strtol(workstring, (char **) NULL, 0); + } + if (max_btreenode_size == 0) { + btree = 0; + } + } else if (argv[i][j] == '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] == 'K') { + vtkview = 1; + } else if (argv[i][j] == 'M') { + nomerge = 1; + } else if (argv[i][j] == 'Y') { + nobisect++; + } else if (argv[i][j] == 'J') { + nojettison = 1; + } else if (argv[i][j] == 'B') { + nobound = 1; + } else if (argv[i][j] == 'N') { + nonodewritten = 1; + } else if (argv[i][j] == 'E') { + noelewritten = 1; + if (argv[i][j + 1] == '2') { + j++; + noelewritten = 2; + } + } else if (argv[i][j] == 'F') { + nofacewritten = 1; + } else if (argv[i][j] == 'I') { + noiterationnum = 1; + } else if (argv[i][j] == 'o') { + if (argv[i][j + 1] == '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] == 'h') || (argv[i][j] == 'H') || + (argv[i][j] == '?')) { + usage(); + } else { + printf("Warning: Unknown switch -%c.\n", argv[i][j]); + } + } + } + + if (startindex == 0) { + // Set a temporary filename for debugging output. + strcpy(infilename, "tetgen-tmpfile"); + } else { + if (infilename[0] == '\0') { + // No input file name. Print the syntax and exit. + syntax(); + terminatetetgen(0); + } + // Recognize the object from file extension if it is available. + if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) { + infilename[strlen(infilename) - 5] = '\0'; + object = NODES; + } else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) { + infilename[strlen(infilename) - 5] = '\0'; + object = POLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) { + infilename[strlen(infilename) - 6] = '\0'; + object = POLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) { + infilename[strlen(infilename) - 4] = '\0'; + object = OFF; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) { + infilename[strlen(infilename) - 4] = '\0'; + object = PLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) { + infilename[strlen(infilename) - 4] = '\0'; + object = STL; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) { + infilename[strlen(infilename) - 5] = '\0'; + object = MEDIT; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) { + infilename[strlen(infilename) - 4] = '\0'; + object = VTK; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) { + infilename[strlen(infilename) - 4] = '\0'; + object = MESH; + refine = 1; + } + } + plc = plc || diagnose; + useshelles = plc || refine || coarse || quality; + goodratio = minratio; + goodratio *= goodratio; + + // Detect improper combinations of switches. + if (plc && refine) { + printf("Error: Switch -r cannot use together with -p.\n"); + return false; + } + if (refine && (plc || noiterationnum)) { + printf("Error: Switches %s cannot use together with -r.\n", + "-p, -d, and -I"); + return false; + } + if (diagnose && (quality || insertaddpoints || (order == 2) || neighout + || docheck)) { + printf("Error: Switches %s cannot use together with -d.\n", + "-q, -i, -o2, -n, and -C"); + return false; + } + + // Be careful not to allocate space for element area constraints that + // will never be assigned any value (other than the default -1.0). + if (!refine && !plc) { + varvolume = 0; + } + // Be careful not to add an extra attribute to each element unless the + // input supports it (PLC in, but not refining a preexisting mesh). + if (refine || !plc) { + regionattrib = 0; + } + // If '-a' or '-aa' is in use, enable '-q' option too. + if (fixedvolume || varvolume) { + if (quality == 0) { + quality = 1; + } + } + // Calculate the goodangle for testing bad subfaces. + goodangle = cos(minangle * tetgenmesh::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; +} + +//// //// +//// //// +//// behavior_cxx ///////////////////////////////////////////////////////////// + +//// prim_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + +// 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 (a -> b) + {0, 2}, // 1 v1 -> v2 (b -> c) + {0, 4}, // 2 v2 -> v0 (c -> a) + {1, 0}, // 3 v0 -> v3 (a -> d) + {1, 2}, // 4 v1 -> v3 (b -> d + {2, 2} // 5 v2 -> v3 (c -> d) +}; + +int tetgenmesh::locpivot[4][3] = { + {1, 2, 3}, + {0, 2, 3}, + {0, 1, 3}, + {0, 1, 2} +}; + +int tetgenmesh::locverpivot[4][6][2] = { + {{2, 3}, {2, 3}, {1, 3}, {1, 3}, {1, 2}, {1, 2}}, + {{0, 2}, {0, 2}, {0, 3}, {0, 3}, {2, 3}, {2, 3}}, + {{0, 3}, {0, 3}, {0, 1}, {0, 1}, {1, 3}, {1, 3}}, + {{0, 1}, {0, 1}, {0, 2}, {0, 2}, {1, 2}, {1, 2}} +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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)); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// point2tetorg(), point2shorg(), point2segorg() // +// // +// Return a tet, a subface, or a subsegment whose origin is the given point. // +// These routines assume the maps between points to tets (subfaces, segments // +// ) have been built and maintained. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::point2tetorg(point pa, triface& searchtet) +{ + int i; + + // Search a tet whose origin is pa. + decode(point2tet(pa), searchtet); + if (searchtet.tet == NULL) { + printf("Internal error: %d contains bad tet pointer.\n", pointmark(pa)); + terminatetetgen(2); + } + for (i = 4; i < 8; i++) { + if ((point) searchtet.tet[i] == pa) { + // Found. Set pa as its origin. + switch (i) { + case 4: searchtet.loc = 0; searchtet.ver = 0; break; + case 5: searchtet.loc = 0; searchtet.ver = 2; break; + case 6: searchtet.loc = 0; searchtet.ver = 4; break; + case 7: searchtet.loc = 1; searchtet.ver = 2; break; + } + break; + } + } + if (i == 8) { + printf("Internal error: %d contains bad tet pointer.\n", pointmark(pa)); + terminatetetgen(2); + } +} + +void tetgenmesh::point2shorg(point pa, face& searchsh) +{ + sdecode(point2sh(pa), searchsh); + if (searchsh.sh == NULL) { + printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa)); + terminatetetgen(2); + } + if (((point) searchsh.sh[3]) == pa) { + searchsh.shver = 0; + } else if (((point) searchsh.sh[4]) == pa) { + searchsh.shver = 2; + } else if (((point) searchsh.sh[5]) == pa) { + searchsh.shver = 4; + } else { + printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa)); + terminatetetgen(2); + } +} + +void tetgenmesh::point2segorg(point pa, face& searchsh) +{ + sdecode(point2seg(pa), searchsh); + if (searchsh.sh == NULL) { + printf("Internal error: %d contains bad seg pointer.\n", pointmark(pa)); + terminatetetgen(2); + } + if (((point) searchsh.sh[3]) == pa) { + searchsh.shver = 0; + } else if (((point) searchsh.sh[4]) == pa) { + searchsh.shver = 1; + } else { + printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa)); + terminatetetgen(2); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// findorg() Find a point in the given tet 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 tet 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"); + terminatetetgen(2); +} + +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"); + terminatetetgen(2); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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"); + terminatetetgen(2); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + shellface *shells; + point tmppt; + face checksh; + int facecount; + + printf("Tetra x%lx with loc(%i) and ver(%i):", + (uintptr_t)(tface->tet), tface->loc, tface->ver); + if (infected(*tface)) { + printf(" (infected)"); + } + if (marktested(*tface)) { + printf(" (marked)"); + } + 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 { + if (!isdead(&prtface)) { + printf(" [%i] x%lx loc(%i).", facecount, + (uintptr_t)(prtface.tet), prtface.loc); + if (infected(prtface)) { + printf(" (infected)"); + } + printf("\n"); + } else { + printf(" [%i] NULL\n", facecount); + } + } + 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], (uintptr_t)(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], (uintptr_t)(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], (uintptr_t)(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], (uintptr_t)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + + if (b->useshelles) { + if (tface->tet[8] != NULL) { + shells = (shellface *) tface->tet[8]; + for (facecount = 0; facecount < 6; facecount++) { + sdecode(shells[facecount], checksh); + if (checksh.sh != dummysh) { + printf(" [%d] x%lx %d.", facecount, (uintptr_t) checksh.sh, + checksh.shver); + } else { + printf(" [%d] NULL.", facecount); + } + if (locver2edge[tface->loc][tface->ver] == facecount) { + printf(" (*)"); // It is the current edge. + } + printf("\n"); + } + } + if (tface->tet[9] != NULL) { + shells = (shellface *) tface->tet[9]; + for (facecount = 0; facecount < 4; facecount++) { + sdecode(shells[facecount], checksh); + if (checksh.sh != dummysh) { + printf(" [%d] x%lx %d.", facecount, (uintptr_t) checksh.sh, + checksh.shver); + } else { + printf(" [%d] NULL.", facecount); + } + if (tface->loc == facecount) { + printf(" (*)"); // It is the current face. + } + printf("\n"); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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:", + (uintptr_t)(sface->sh), sface->shver, shellmark(*sface)); + } else { + printf("Subsegment x%lx, ver %d, mark %d:", + (uintptr_t)(sface->sh), sface->shver, shellmark(*sface)); + } + if (sinfected(*sface)) { + printf(" (infected)"); + } + if (smarktested(*sface)) { + printf(" (marked)"); + } + 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", (uintptr_t)(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", (uintptr_t)(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", (uintptr_t)(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], (uintptr_t)(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], (uintptr_t)(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], (uintptr_t)(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", + (uintptr_t)(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", + (uintptr_t)(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", + (uintptr_t)(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", + (uintptr_t)(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", + (uintptr_t)(prtsh.sh), prtsh.shver); + } + } +} + +//// //// +//// //// +//// prim_cxx ///////////////////////////////////////////////////////////////// + +//// mempool_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// restart() Deallocate all objects in this pool. // +// // +// The pool returns to a fresh state, like after it was initialized, except // +// that no memory is freed to the operating system. Rather, the previously // +// allocated blocks are ready to be used. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::arraypool::restart() +{ + objects = 0l; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// poolinit() Initialize an arraypool for allocation of objects. // +// // +// Before the pool may be used, it must be initialized by this procedure. // +// After initialization, memory can be allocated and freed in this pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) +{ + // Each object must be at least one byte long. + objectbytes = sizeofobject > 1 ? sizeofobject : 1; + + log2objectsperblock = log2objperblk; + // Compute the number of objects in each block. + objectsperblock = ((int) 1) << log2objectsperblock; + + // No memory has been allocated. + totalmemory = 0l; + // The top array has not been allocated yet. + toparray = (char **) NULL; + toparraylen = 0; + + // Ready all indices to be allocated. + restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// arraypool() The constructor and destructor. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk) +{ + poolinit(sizeofobject, log2objperblk); +} + +tetgenmesh::arraypool::~arraypool() +{ + int i; + + // Has anything been allocated at all? + if (toparray != (char **) NULL) { + // Walk through the top array. + for (i = 0; i < toparraylen; i++) { + // Check every pointer; NULLs may be scattered randomly. + if (toparray[i] != (char *) NULL) { + // Free an allocated block. + free((void *) toparray[i]); + } + } + // Free the top array. + free((void *) toparray); + } + + // The top array is no longer allocated. + toparray = (char **) NULL; + toparraylen = 0; + objects = 0; + totalmemory = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getblock() Return (and perhaps create) the block containing the object // +// with a given index. // +// // +// This function takes care of allocating or resizing the top array if nece- // +// ssary, and of allocating the block if it hasn't yet been allocated. // +// // +// Return a pointer to the beginning of the block (NOT the object). // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* tetgenmesh::arraypool::getblock(int objectindex) +{ + char **newarray; + char *block; + int newsize; + int topindex; + int i; + + // Compute the index in the top array (upper bits). + topindex = objectindex >> log2objectsperblock; + // Does the top array need to be allocated or resized? + if (toparray == (char **) NULL) { + // Allocate the top array big enough to hold 'topindex', and NULL out + // its contents. + newsize = topindex + 128; + toparray = (char **) malloc((size_t) (newsize * sizeof(char *))); + toparraylen = newsize; + for (i = 0; i < newsize; i++) { + toparray[i] = (char *) NULL; + } + // Account for the memory. + totalmemory = newsize * (unsigned long) sizeof(char *); + } else if (topindex >= toparraylen) { + // Resize the top array, making sure it holds 'topindex'. + newsize = 3 * toparraylen; + if (topindex >= newsize) { + newsize = topindex + 128; + } + // Allocate the new array, copy the contents, NULL out the rest, and + // free the old array. + newarray = (char **) malloc((size_t) (newsize * sizeof(char *))); + for (i = 0; i < toparraylen; i++) { + newarray[i] = toparray[i]; + } + for (i = toparraylen; i < newsize; i++) { + newarray[i] = (char *) NULL; + } + free(toparray); + // Account for the memory. + totalmemory += (newsize - toparraylen) * sizeof(char *); + toparray = newarray; + toparraylen = newsize; + } + + // Find the block, or learn that it hasn't been allocated yet. + block = toparray[topindex]; + if (block == (char *) NULL) { + // Allocate a block at this index. + block = (char *) malloc((size_t) (objectsperblock * objectbytes)); + toparray[topindex] = block; + // Account for the memory. + totalmemory += objectsperblock * objectbytes; + } + + // Return a pointer to the block. + return block; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lookup() Return the pointer to the object with a given index, or NULL // +// if the object's block doesn't exist yet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::arraypool::lookup(int objectindex) +{ + char *block; + int topindex; + + // Has the top array been allocated yet? + if (toparray == (char **) NULL) { + return (void *) NULL; + } + + // Compute the index in the top array (upper bits). + topindex = objectindex >> log2objectsperblock; + // Does the top index fit in the top array? + if (topindex >= toparraylen) { + return (void *) NULL; + } + + // Find the block, or learn that it hasn't been allocated yet. + block = toparray[topindex]; + if (block == (char *) NULL) { + return (void *) NULL; + } + + // Compute a pointer to the object with the given index. Note that + // 'objectsperblock' is a power of two, so the & operation is a bit mask + // that preserves the lower bits. + return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// newindex() Allocate space for a fresh object from the pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::arraypool::newindex(void **newptr) +{ + void *newobject; + int newindex; + + // Allocate an object at index 'firstvirgin'. + newindex = objects; + newobject = (void *) (getblock(objects) + + (objects & (objectsperblock - 1)) * objectbytes); + objects++; + + // If 'newptr' is not NULL, use it to return a pointer to the object. + if (newptr != (void **) NULL) { + *newptr = newobject; + } + return newindex; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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) +{ + itembytes = itbytes; + comp = pcomp; + maxitems = mitems; + expandsize = exsize; + base = (char *) malloc(maxitems * itembytes); + if (base == (char *) NULL) { + 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) { + 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) { + 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// memorypool() The constructors of memorypool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::memorypool::memorypool() +{ + firstblock = nowblock = (void **) NULL; + nextitem = (void *) NULL; + deaditemstack = (void *) NULL; + pathblock = (void **) NULL; + pathitem = (void *) NULL; + itemwordtype = POINTER; + alignbytes = 0; + itembytes = itemwords = 0; + itemsperblock = 0; + items = maxitems = 0l; + unallocateditems = 0; + pathitemsleft = 0; +} + +tetgenmesh::memorypool:: +memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment) +{ + poolinit(bytecount, itemcount, wtype, alignment); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// ~memorypool() Free to the operating system all memory taken by a pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::memorypool::~memorypool() +{ + while (firstblock != (void **) NULL) { + nowblock = (void **) *(firstblock); + free(firstblock); + firstblock = nowblock; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// poolinit() Initialize a pool of memory for allocation of items. // +// // +// A `pool' is created whose records have size at least `bytecount'. Items // +// will be allocated in `itemcount'-item blocks. Each item is assumed to be // +// a collection of words, and either pointers or floating-point values are // +// assumed to be the "primary" word type. (The "primary" word type is used // +// to determine alignment of items.) If `alignment' isn't zero, all items // +// will be `alignment'-byte aligned in memory. `alignment' must be either a // +// multiple or a factor of the primary word size; powers of two are safe. // +// `alignment' is normally used to create a few unused bits at the bottom of // +// each item's pointer, in which information may be stored. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool:: +poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) +{ + int wordsize; + + // Initialize values in the pool. + itemwordtype = wtype; + wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL); + // Find the proper alignment, which must be at least as large as: + // - The parameter `alignment'. + // - The primary word type, to avoid unaligned accesses. + // - sizeof(void *), so the stack of dead items can be maintained + // without unaligned accesses. + if (alignment > wordsize) { + alignbytes = alignment; + } else { + alignbytes = wordsize; + } + if ((int) sizeof(void *) > alignbytes) { + alignbytes = (int) sizeof(void *); + } + itemwords = ((bytecount + alignbytes - 1) / alignbytes) + * (alignbytes / wordsize); + itembytes = itemwords * wordsize; + itemsperblock = itemcount; + + // Allocate a block of items. Space for `itemsperblock' items and one + // pointer (to point to the next block) are allocated, as well as space + // to ensure alignment of the items. + firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + + alignbytes); + if (firstblock == (void **) NULL) { + terminatetetgen(1); + } + // Set the next block pointer to NULL. + *(firstblock) = (void *) NULL; + restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// restart() Deallocate all items in this pool. // +// // +// The pool is returned to its starting state, except that no memory is // +// freed to the operating system. Rather, the previously allocated blocks // +// are ready to be reused. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::restart() +{ + // unsigned long alignptr; + uintptr_t alignptr; + + items = 0; + maxitems = 0; + + // Set the currently active block. + nowblock = firstblock; + // Find the first item in the pool. Increment by the size of (void *). + // alignptr = (unsigned long) (nowblock + 1); + alignptr = (uintptr_t) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + // nextitem = (void *) + // (alignptr + (unsigned long) alignbytes - + // (alignptr % (unsigned long) alignbytes)); + nextitem = (void *) + (alignptr + (uintptr_t) alignbytes - + (alignptr % (uintptr_t) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + // The stack of deallocated items is empty. + deaditemstack = (void *) NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// alloc() Allocate space for an item. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::memorypool::alloc() +{ + void *newitem; + void **newblock; + // unsigned long alignptr; + uintptr_t alignptr; + + // First check the linked list of dead items. If the list is not + // empty, allocate an item from the list rather than a fresh one. + if (deaditemstack != (void *) NULL) { + newitem = deaditemstack; // Take first item in list. + deaditemstack = * (void **) deaditemstack; + } else { + // Check if there are any free items left in the current block. + if (unallocateditems == 0) { + // Check if another block must be allocated. + if (*nowblock == (void *) NULL) { + // Allocate a new block of items, pointed to by the previous block. + newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + + alignbytes); + if (newblock == (void **) NULL) { + terminatetetgen(1); + } + *nowblock = (void *) newblock; + // The next block pointer is NULL. + *newblock = (void *) NULL; + } + // Move to the new block. + nowblock = (void **) *nowblock; + // Find the first item in the block. + // Increment by the size of (void *). + // alignptr = (unsigned long) (nowblock + 1); + alignptr = (uintptr_t) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + // nextitem = (void *) + // (alignptr + (unsigned long) alignbytes - + // (alignptr % (unsigned long) alignbytes)); + nextitem = (void *) + (alignptr + (uintptr_t) alignbytes - + (alignptr % (uintptr_t) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + } + // Allocate a new item. + newitem = nextitem; + // Advance `nextitem' pointer to next free item in block. + if (itemwordtype == POINTER) { + nextitem = (void *) ((void **) nextitem + itemwords); + } else { + nextitem = (void *) ((REAL *) nextitem + itemwords); + } + unallocateditems--; + maxitems++; + } + items++; + return newitem; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dealloc() Deallocate space for an item. // +// // +// The deallocated space is stored in a queue for later reuse. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::dealloc(void *dyingitem) +{ + // Push freshly killed item onto stack. + *((void **) dyingitem) = deaditemstack; + deaditemstack = dyingitem; + items--; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// traversalinit() Prepare to traverse the entire list of items. // +// // +// This routine is used in conjunction with traverse(). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::traversalinit() +{ + // unsigned long alignptr; + uintptr_t alignptr; + + // Begin the traversal in the first block. + pathblock = firstblock; + // Find the first item in the block. Increment by the size of (void *). + // alignptr = (unsigned long) (pathblock + 1); + alignptr = (uintptr_t) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + // pathitem = (void *) + // (alignptr + (unsigned long) alignbytes - + // (alignptr % (unsigned long) alignbytes)); + pathitem = (void *) + (alignptr + (uintptr_t) alignbytes - + (alignptr % (uintptr_t) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// traverse() Find the next item in the list. // +// // +// This routine is used in conjunction with traversalinit(). Be forewarned // +// that this routine successively returns all items in the list, including // +// deallocated ones on the deaditemqueue. It's up to you to figure out which // +// ones are actually dead. It can usually be done more space-efficiently by // +// a routine that knows something about the structure of the item. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::memorypool::traverse() +{ + void *newitem; + // unsigned long alignptr; + uintptr_t alignptr; + + // Stop upon exhausting the list of items. + if (pathitem == nextitem) { + return (void *) NULL; + } + // Check whether any untraversed items remain in the current block. + if (pathitemsleft == 0) { + // Find the next block. + pathblock = (void **) *pathblock; + // Find the first item in the block. Increment by the size of (void *). + // alignptr = (unsigned long) (pathblock + 1); + alignptr = (uintptr_t) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + // pathitem = (void *) + // (alignptr + (unsigned long) alignbytes - + // (alignptr % (unsigned long) alignbytes)); + pathitem = (void *) + (alignptr + (uintptr_t) alignbytes - + (alignptr % (uintptr_t) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; + } + newitem = pathitem; + // Find the next item in the block. + if (itemwordtype == POINTER) { + pathitem = (void *) ((void **) pathitem + itemwords); + } else { + pathitem = (void *) ((REAL *) pathitem + itemwords); + } + pathitemsleft--; + return newitem; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 > 2) { + 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(); + } +} + +void tetgenmesh::makepoint2segmap() +{ + face segloop; + point *ppt; + + if (b->verbose > 2) { + printf(" Constructing mapping from points to segments.\n"); + } + + segloop.shver = 0; + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + ppt = (point *) &(segloop.sh[3]); + setpoint2seg(ppt[0], sencode(segloop)); + setpoint2seg(ppt[1], sencode(segloop)); + segloop.sh = shellfacetraverse(subsegs); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makeindex2pointmap() Create a map from index to vertices. // +// // +// 'idx2verlist' returns the created map. Traverse all vertices, a pointer // +// to each vertex is set into the array. The pointer to the first vertex is // +// saved in 'idx2verlist[0]'. Don't forget to minus 'in->firstnumber' when // +// to get the vertex form its index. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makeindex2pointmap(point*& idx2verlist) +{ + point pointloop; + int idx; + + if (b->verbose > 1) { + printf(" Constructing mapping from indices to points.\n"); + } + + idx2verlist = new point[points->items]; + + points->traversalinit(); + pointloop = pointtraverse(); + idx = 0; + while (pointloop != (point) NULL) { + idx2verlist[idx] = pointloop; + idx++; + pointloop = pointtraverse(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makesegmentmap(), makesubfacemap(), maketetrahedronmap() // +// // +// Create a map from vertex indices to segments, subfaces, and tetrahedra // +// sharing at the same vertices. // +// // +// The map is stored in two arrays: 'idx2___list' and '___sperverlist', they // +// form a sparse matrix whose size is (n+1)x(n+1), where n is the number of // +// segments, subfaces, or tetrahedra. 'idx2___list' contains row information // +// and '___sperverlist' contains all non-zero elements. The i-th entry of // +// 'idx2___list' is the starting position of i-th row's non-zero elements in // +// '___sperverlist'. The number of elements of i-th row is (i+1)-th entry // +// minus i-th entry of 'idx2___list'. // +// // +// 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 > 1) { + 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; +} + +void tetgenmesh::makesubfacemap(int*& idx2facelist, + shellface**& facesperverlist) +{ + shellface *shloop; + int i, j, k; + + if (b->verbose > 1) { + 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; +} + +void tetgenmesh::maketetrahedronmap(int*& idx2tetlist, + tetrahedron**& tetsperverlist) +{ + tetrahedron *tetloop; + int i, j, k; + + if (b->verbose > 1) { + 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 ] = NULL; + dummytet[9 ] = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 || b->voroout) { + // Increase the point size by four pointers, which are: + // - a pointer to a tet, read by point2tet(); + // - a pointer to a subface, read by point2sh(); + // - a pointer to a subsegment, read by point2seg(); + // - a pointer to a parent point, read by point2ppt()). + if (b->metric) { + // Increase one pointer to a tet of the background mesh. + pointsize = (point2simindex + 5) * sizeof(tetrahedron); + } else { + pointsize = (point2simindex + 4) * sizeof(tetrahedron); + } + // The index within each point at which a pbc point is found. + point2pbcptindex = (pointsize + sizeof(tetrahedron) - 1) + / sizeof(tetrahedron); + if (checkpbcs) { + // Increase the size by one pointer to a corresponding pbc point, + // read by point2pbcpt(). + pointsize = (point2pbcptindex + 1) * sizeof(tetrahedron); + } + } else { + // Increase the point size by FOUR pointer, which are: + // - a pointer to a tet, read by point2tet(); + // - a pointer to a subface, read by point2sh(); -- !! Unused !! + // - a pointer to a subsegment, read by point2seg(); -- !! Unused !! + // - a pointer to a parent point, read by point2ppt()). -- Used by btree. + pointsize = (point2simindex + 4) * 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); // Wrong for 64 bit. + pointsize = (pointmarkindex + 2) * sizeof(tetrahedron); + // 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); + + if (b->useshelles) { + dummypoint = (point) new char[pointsize]; // For abovepoint. + } + + // 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 (or six pointers to subsegments (used in + // segment recovery only)). + elesize = (8 + b->useshelles * 2) * sizeof(tetrahedron); + // If Voronoi diagram is wanted, make sure we have additional space. + if (b->voroout) { + 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); + elemmarkerindex = (elesize + sizeof(int) - 1) / sizeof(int); + // if (b->neighout || b->voroout) { + // elesize = (elemmarkerindex + 1) * sizeof(int); + // Allocate one slot for the element marker. The actual need isa size + // of an integer. We allocate enough space (a pointer) for alignment + // for 64 bit system. Thanks Liu Yang (LORIA/INRIA) for reporting + // this problem. + elesize = elesize + sizeof(tetrahedron); + // } + // 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 pool for tet-subseg connections. + tet2segpool = new memorypool(6*sizeof(shellface), SUBPERBLOCK, POINTER, 0); + // Initialize the pool for tet-subface connections. + tet2subpool = new memorypool(4*sizeof(shellface), SUBPERBLOCK, POINTER, 0); + // Initialize arraypools for segment & facet recovery. + subsegstack = new arraypool(sizeof(face), 10); + subfacstack = new arraypool(sizeof(face), 10); + // Initialize 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; + + if (b->useshelles) { + // Dealloc the space to subfaces/subsegments. + if (dyingtetrahedron[8] != NULL) { + tet2segpool->dealloc((shellface *) dyingtetrahedron[8]); + } + if (dyingtetrahedron[9] != NULL) { + tet2subpool->dealloc((shellface *) dyingtetrahedron[9]); + } + } + + tetrahedrons->dealloc((void *) dyingtetrahedron); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() +{ + tetrahedron *newtetrahedron; + + do { + newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; + } + } while (newtetrahedron[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 ] = NULL; + newtet->tet[9 ] = NULL; + } + for (int i = 0; i < in->numberoftetrahedronattributes; i++) { + setelemattribute(newtet->tet, i, 0.0); + } + if (b->varvolume) { + setvolumebound(newtet->tet, -1.0); + } + // Initialize the marker (for flags). + setelemmarker(newtet->tet, 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); + } + // Clear the infection and marktest bits. + suninfect(*newface); + sunmarktest(*newface); + // 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); + setpoint2seg(*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); + // Clear the point flags. + puninfect(*pnewpoint); + //punmarktest(*pnewpoint); +} + +//// //// +//// //// +//// mempool_cxx ////////////////////////////////////////////////////////////// + +//// geom_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + +// PI is the ratio of a circle's circumference to its diameter. + +REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tri_edge_2d() Triangle-edge coplanar intersection test. // +// // +// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // +// Q) in a plane in 3D, and tests if they intersect each other. Return 1 if // +// they are intersected, i.e., T \cap E is not empty, otherwise, return 0. // +// // +// If the point 'R' is not NULL, it lies strictly above T [A, B, C]. // +// // +// If T1 and T2 intersect each other (return 1), they may intersect in diff- // +// erent ways. If 'level' > 0, their intersection type will be reported in // +// combinations of 'types' and 'pos'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, + point R, int level, int *types, int *pos) +{ + point U[3], V[3]; // The permuted vectors of points. + int pu[3], pv[3]; // The original positions of points. + REAL sA, sB, sC; + REAL s1, s2, s3, s4; + int z1; + + if (R == NULL) { + REAL n[3], len; + // Calculate a lift point, saved in dummypoint. + facenormal2(A, B, C, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(A, B); + len += DIST(B, C); + len += DIST(C, A); + len /= 3.0; + R = dummypoint; + R[0] = A[0] + len * n[0]; + R[1] = A[1] + len * n[1]; + R[2] = A[2] + len * n[2]; + } + + // Test A's, B's, and C's orientations wrt plane PQR. + sA = orient3d(P, Q, R, A); + sB = orient3d(P, Q, R, B); + sC = orient3d(P, Q, R, C); + orient3dcount+=3; + + if (b->verbose > 2) { + printf(" Tri-edge-2d (%d %d %d)-(%d %d)-(%d) (%c%c%c)", pointmark(A), + pointmark(B), pointmark(C), pointmark(P), pointmark(Q), pointmark(R), + sA > 0 ? '+' : (sA < 0 ? '-' : '0'), sB>0 ? '+' : (sB<0 ? '-' : '0'), + sC>0 ? '+' : (sC<0 ? '-' : '0')); + } + // triedgcopcount++; + + if (sA < 0) { + if (sB < 0) { + if (sC < 0) { // (---). + return 0; + } else { + if (sC > 0) { // (--+). + // All points are in the right positions. + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { // (--0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (-+-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (-++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { // (-+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } + } + } else { + if (sC < 0) { // (-0-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (-0+). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } else { // (-00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } + } + } + } + } else { + if (sA > 0) { + if (sB < 0) { + if (sC < 0) { // (+--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (+-+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { // (+-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (++-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { + if (sC > 0) { // (+++). + return 0; + } else { // (++0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } + } + } else { // (+0#) + if (sC < 0) { // (+0-). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { + if (sC > 0) { // (+0+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (+00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } + } + } else { + if (sB < 0) { + if (sC < 0) { // (0--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (0-+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { // (0-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (0+-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } else { + if (sC > 0) { // (0++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (0+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } else { // (00#) + if (sC < 0) { // (00-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } else { + if (sC > 0) { // (00+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } else { // (000) + // Not possible unless ABC is degenerate. + z1 = 4; + } + } + } + } + } + } + + s1 = orient3d(U[0], U[2], R, V[1]); // A, C, R, Q + s2 = orient3d(U[1], U[2], R, V[0]); // B, C, R, P + orient3dcount+=2; + + if (b->verbose > 2) { + printf(" Tri-edge-2d (%d %d %d)-(%d %d %d) (%d) (%c%c)\n", + pointmark(U[0]), pointmark(U[1]), pointmark(U[2]), pointmark(V[0]), + pointmark(V[1]), pointmark(V[2]), z1, s1>0 ? '+' : (s1<0 ? '-' : '0'), + s2>0 ? '+' : (s2<0 ? '-' : '0')); + } + assert(z1 != 4); // SELF_CHECK + + if (s1 > 0) { + return 0; + } + if (s2 < 0) { + return 0; + } + + if (level == 0) { + return 1; // They are intersected. + } + + if (z1 == 1) { + if (s1 == 0) { // (0###) + // C = Q. + types[0] = (int) SHAREVERTEX; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { // (#0##) + // C = P. + types[0] = (int) SHAREVERTEX; + pos[0] = pu[2]; // C + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } else { // (-+##) + // C in [P, Q]. + types[0] = (int) INTERVERT; + pos[0] = pu[2]; // C + pos[1] = pv[0]; // [P, Q] + types[1] = (int) DISJOINT; + } + } + return 1; + } + + s3 = orient3d(U[0], U[2], R, V[0]); // A, C, R, P + s4 = orient3d(U[1], U[2], R, V[1]); // B, C, R, Q + orient3dcount+=2; + + if (z1 == 0) { // (tritri-03) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [k, l] (-+++). + types[0] = (int) INTEREDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [k, l] (-++0). + types[0] = (int) INTEREDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [k, l] (-++-). + types[0] = (int) INTEREDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) INTEREDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = k, [P, Q] in [k, l] (-+0+). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [k, l] (-+00). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { + // P = k, [P, Q] contains [k, l] (-+0-). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) INTEREDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [k, l] (-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [k, l] (-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [k, l] (-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) INTEREDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = l (#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = k (0####) + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } else if (z1 == 2) { // (tritri-23) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [A, l] (-+++). + types[0] = (int) INTERVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [A, l] (-++0). + types[0] = (int) INTERVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, l] (-++-). + types[0] = (int) INTERVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) INTEREDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = A, [P, Q] in [A, l] (-+0+). + types[0] = (int) SHAREVERTEX; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, l] (-+00). + types[0] = (int) SHAREVERTEX; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // Q = l, [P, Q] in [A, l] (-+0-). + types[0] = (int) SHAREVERTEX; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) INTEREDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, l] (-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [A, l] (-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, l] (-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) INTEREDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = l (#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = A (0###). + types[0] = (int) SHAREVERTEX; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } else if (z1 == 3) { // (tritri-33) + if (s1 < 0) { + if (s3 > 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // [P, Q] overlaps [A, B] (-+++). + types[0] = (int) INTERVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] contains [A, B] (-++0). + types[0] = (int) INTERVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) SHAREVERTEX; + pos[2] = pu[1]; // B + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, B] (-++-). + types[0] = (int) INTERVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) INTERVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + assert(s2 > 0); // SELF_CHECK + if (s4 > 0) { + // P = A, [P, Q] in [A, B] (-+0+). + types[0] = (int) SHAREVERTEX; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, B] (-+00). + types[0] = (int) SHAREEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { // s4 < 0 + // P= A, [P, Q] in [A, B] (-+0-). + types[0] = (int) SHAREVERTEX; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) INTERVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, B] (-+-+). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] in [A, B] (-+-0). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) SHAREVERTEX; + pos[2] = pu[1]; // B + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, B] (-+--). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) INTERVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = B (#0##). + types[0] = (int) SHAREVERTEX; + pos[0] = pu[1]; // B + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = A (0###). + types[0] = (int) SHAREVERTEX; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tri_edge_test() Triangle-edge intersection test. // +// // +// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // +// Q) in 3D, and tests if they intersect each other. Return 1 if they are // +// intersected, i.e., T \cap E is not empty, otherwise, return 0. // +// // +// If the point 'R' is not NULL, it lies strictly above the plane defined by // +// A, B, C. It is used in test when T and E are coplanar. // +// // +// If T1 and T2 intersect each other (return 1), they may intersect in diff- // +// erent ways. If 'level' > 0, their intersection type will be reported in // +// combinations of 'types' and 'pos'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, + point R, int level, int *types, int *pos) +{ + point U[3], V[3]; //, Ptmp; + int pu[3], pv[3]; //, itmp; + REAL sP, sQ, s1, s2, s3; + int z1; + + // Test the locations of P and Q with respect to ABC. + sP = orient3d(A, B, C, P); + sQ = orient3d(A, B, C, Q); + orient3dcount+=2; + + if (b->verbose > 2) { + printf(" Tri-edge (%d %d %d)-(%d %d) (%c%c).\n", pointmark(A), + pointmark(B), pointmark(C), pointmark(P), pointmark(Q), + sP>0 ? '+' : (sP<0 ? '-' : '0'), sQ>0 ? '+' : (sQ<0 ? '-' : '0')); + } + // triedgcount++; + + if (sP < 0) { + if (sQ < 0) { // (--) disjoint + return 0; + } else { + if (sQ > 0) { // (-+) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { // (-0) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { + if (sP > 0) { // (+-) + if (sQ < 0) { + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { + if (sQ > 0) { // (++) disjoint + return 0; + } else { // (+0) + SETVECTOR3(U, B, A, C); // A and B are flipped. + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 1, 0, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { // sP == 0 + if (sQ < 0) { // (0-) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { + if (sQ > 0) { // (0+) + SETVECTOR3(U, B, A, C); // A and B are flipped. + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 1, 0, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (00) + // A, B, C, P, and Q are coplanar. + z1 = 2; + } + } + } + } + + if (z1 == 2) { + // The triangle and the edge are coplanar. + return tri_edge_2d(A, B, C, P, Q, R, level, types, pos); + } + + s1 = orient3d(U[0], U[1], V[0], V[1]); orient3dcount++; + if (s1 < 0) { + return 0; + } + + s2 = orient3d(U[1], U[2], V[0], V[1]); orient3dcount++; + if (s2 < 0) { + return 0; + } + + s3 = orient3d(U[2], U[0], V[0], V[1]); orient3dcount++; + if (s3 < 0) { + return 0; + } + + if (b->verbose > 2) { + printf(" Tri-edge (%d %d %d)-(%d %d) (%c%c%c).\n", pointmark(U[0]), + pointmark(U[1]), pointmark(U[2]), pointmark(V[0]), pointmark(V[1]), + s1>0 ? '+' : (s1<0 ? '-' : '0'), s2>0 ? '+' : (s2<0 ? '-' : '0'), + s3>0 ? '+' : (s3<0 ? '-' : '0')); + } + + if (level == 0) { + return 1; // The are intersected. + } + + types[1] = (int) DISJOINT; // No second intersection point. + + if (z1 == 0) { + if (s1 > 0) { + if (s2 > 0) { + if (s3 > 0) { // (+++) + // [P, Q] passes interior of [A, B, C]. + types[0] = (int) INTERFACE; + pos[0] = 3; // interior of [A, B, C] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (++0) + // [P, Q] intersects [C, A]. + types[0] = (int) INTEREDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = 0; // [P, Q] + } + } else { // s2 == 0 + if (s3 > 0) { // (+0+) + // [P, Q] intersects [B, C]. + types[0] = (int) INTEREDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (+00) + // [P, Q] passes C. + types[0] = (int) INTERVERT; + pos[0] = pu[2]; // C + pos[1] = 0; // [P, Q] + } + } + } else { // s1 == 0 + if (s2 > 0) { + if (s3 > 0) { // (0++) + // [P, Q] intersects [A, B]. + types[0] = (int) INTEREDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (0+0) + // [P, Q] passes A. + types[0] = (int) INTERVERT; + pos[0] = pu[0]; // A + pos[1] = 0; // [P, Q] + } + } else { // s2 == 0 + if (s3 > 0) { // (00+) + // [P, Q] passes B. + types[0] = (int) INTERVERT; + pos[0] = pu[1]; // B + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (000) + // Impossible. + assert(0); + } + } + } + } else { // z1 == 1 + if (s1 > 0) { + if (s2 > 0) { + if (s3 > 0) { // (+++) + // Q lies in [A, B, C]. + types[0] = (int) TOUCHFACE; + pos[0] = 0; // [A, B, C] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (++0) + // Q lies on [C, A]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + } + } else { // s2 == 0 + if (s3 > 0) { // (+0+) + // Q lies on [B, C]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (+00) + // Q = C. + types[0] = (int) SHAREVERTEX; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + } + } + } else { // s1 == 0 + if (s2 > 0) { + if (s3 > 0) { // (0++) + // Q lies on [A, B]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (0+0) + // Q = A. + types[0] = (int) SHAREVERTEX; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + } + } else { // s2 == 0 + if (s3 > 0) { // (00+) + // Q = B. + types[0] = (int) SHAREVERTEX; + pos[0] = pu[1]; // B + pos[1] = pv[1]; // Q + } else { // s3 == 0 (000) + // Impossible. + assert(0); + } + } + } + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// incircle3d() 3D in-circle test. // +// // +// Return a negative value if pd is inside the circumcircle of the triangle // +// pa, pb, and pc. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) +{ + REAL area2[2], n1[3], n2[3], c[3]; + REAL sign, r, d; + + // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. + facenormal2(pa, pb, pc, n1, 1); + area2[0] = DOT(n1, n1); + facenormal2(pb, pa, pd, n2, 1); + area2[1] = DOT(n2, n2); + + if (area2[0] > area2[1]) { + // Choose [a, b, c] as the base triangle. + circumsphere(pa, pb, pc, NULL, c, &r); + d = DIST(c, pd); + } else { + // Choose [b, a, d] as the base triangle. + if (area2[1] > 0) { + circumsphere(pb, pa, pd, NULL, c, &r); + d = DIST(c, pc); + } else { + // The four points are collinear. This case only happens on the boundary. + return 0; // Return "not inside". + } + } + + sign = d - r; + if (fabs(sign) / r < b->epsilon) { + sign = 0; + } + + return sign; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insphere_s() Insphere test with symbolic perturbation. // +// // +// Given four points pa, pb, pc, and pd, test if the point pe lies inside or // +// outside the circumscirbed sphere of the four points. Here we assume that // +// the orientation of the sequence {pa, pb, pc, pd} is negative (NOT zero), // +// i.e., pd lies at the negative side of the plane defined by pa, pb, and pc.// +// // +// Return a positive value (> 0) if pe lies outside, a negative value (< 0) // +// if pe lies inside the sphere, the returned value will not be zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) +{ + REAL sign; + + inspherecount++; + + sign = insphere(pa, pb, pc, pd, pe); + if (sign != 0.0) { + return sign; + } + + insphere_sos_count++; + + // Symbolic perturbation. + point pt[5], swappt; + REAL oriA, oriB; + int swaps, count; + int n, i; + + pt[0] = pa; + pt[1] = pb; + pt[2] = pc; + pt[3] = pd; + pt[4] = pe; + + // Sort the five points such that their indices are in the increasing + // order. An optimized bubble sort algorithm is used, i.e., it has + // the worst case O(n^2) runtime, but it is usually much faster. + swaps = 0; // Record the total number of swaps. + n = 5; + do { + count = 0; + n = n - 1; + for (i = 0; i < n; i++) { + if (pointmark(pt[i]) > pointmark(pt[i+1])) { + swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; + count++; + } + } + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); + if (oriA != 0.0) { + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriA = -oriA; + return oriA; + } + + oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + assert(oriB != 0.0); // SELF_CHECK + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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]; +} + +// 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; +} + +// 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]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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); + facenormal2(f1, f2, f3, fnormal, 1); + len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] + + fnormal[2]*fnormal[2]); + fnormal[0] /= len; + fnormal[1] /= len; + fnormal[2] /= len; + // Get the vector v1 = |p - f1|. + v1[0] = p[0] - f1[0]; + v1[1] = p[1] - f1[1]; + v1[2] = p[2] - f1[2]; + // Get the project distance. + dist = dot(fnormal, v1); + + // Get the project point. + prj[0] = p[0] - dist * fnormal[0]; + prj[1] = p[1] - dist * fnormal[1]; + prj[2] = p[2] - dist * fnormal[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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)); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// facenormal() Calculate the normal of the face. // +// // +// The normal of the face abc can be calculated by the cross product of 2 of // +// its 3 edge vectors. A better choice of two edge vectors will reduce the // +// numerical error during the calculation. Burdakov proved that the optimal // +// basis problem is equivalent to the minimum spanning tree problem with the // +// edge length be the functional, see Burdakov, "A greedy algorithm for the // +// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // +// short edges in abc are chosen for the calculation. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::facenormal2(point pa, point pb, point pc, REAL *n, int pivot) +{ + REAL v1[3], v2[3], v3[3], *pv1, *pv2; + REAL L1, L2, L3; + + v1[0] = pb[0] - pa[0]; // edge vector v1: a->b + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + v2[0] = pa[0] - pc[0]; // edge vector v2: c->a + v2[1] = pa[1] - pc[1]; + v2[2] = pa[2] - pc[2]; + + // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). + if (pivot > 0) { + // Choose edge vectors by Burdakov's algorithm. + v3[0] = pc[0] - pb[0]; // edge vector v3: b->c + v3[1] = pc[1] - pb[1]; + v3[2] = pc[2] - pb[2]; + L1 = DOT(v1, v1); + L2 = DOT(v2, v2); + L3 = DOT(v3, v3); + // Sort the three edge lengths. + if (L1 < L2) { + if (L2 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v3; pv2 = v1; // n = v3 x (-v1). + } + } else { + if (L1 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v2; pv2 = v3; // n = v2 x (-v3). + } + } + } else { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } + + // Calculate the face normal. + CROSS(pv1, pv2, n); + // Inverse the direction; + n[0] = -n[0]; + n[1] = -n[1]; + n[2] = -n[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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], vol, cosd, len; + int f1, f2, i, j; + + vol = 0; // Check if the tet is valid or not. + + // Get four normals of faces of the tet. + tetallnormal(pa, pb, pc, pd, N, &vol); + + if (vol == 0.0) { + // This tet is not valid. + if (cosdd != NULL) { + for (i = 0; i < 6; i++) { + cosdd[i] = -1.0; // 180 degree. + } + } + // This tet has zero volume. + if (cosmaxd != NULL) { + *cosmaxd = -1.0; // 180 degree. + } + if (cosmind != NULL) { + *cosmind = 1.0; // 0 degree. + } + return; + } + + // 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]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. + facenormal2(pa, pb, pc, n, 1); + // 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; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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: // +// - ONVERTEX, the search point lies on the origin of 'searchtet'. // +// - ONEDGE, the search point lies on an edge of 'searchtet'. // +// - ONFACE, the search point lies on a face of 'searchtet'. // +// - INTET, the search point lies in the interior of 'searchtet'. // +// - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a // +// hull tetrahedron whose base face is visible by the search point. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +// If '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(searchtet->tet != dummytet); +#endif + + 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 + + backtracetet = *searchtet; // Initialize backtracetet. + + // Define 'tetnumber' for exit the loop when it's running endless. + tetnumber = 0l; + while ((maxtetnumber > 0l) && (tetnumber <= maxtetnumber)) { + ptloc_count++; // Algorithimic count. + // 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 that 'searchpt' will not on edge + // (forg, fdest) or on vertex forg or fdest. + // 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. + // 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++; + } + + if (!b->quiet && b->verbose) { + printf("Warning: Point location stopped after searching %ld tets.\n", + maxtetnumber); + } + // terminatetetgen(2); + return OUTSIDE; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// randomsample() Randomly sample the tetrahedra for point loation. // +// // +// This routine implements Muecke's Jump-and-walk point location algorithm. // +// It improves the simple walk-through by "jumping" to a good starting point // +// via random sampling. Searching begins from one of handles: the input // +// 'searchtet', a recently encountered tetrahedron 'recenttet', or from one // +// chosen from a random sample. The choice is made by determining which one // +// 's origin is closest to the point we are searcing for. Having chosen the // +// starting tetrahedron, the simple Walk-through algorithm is executed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::randomsample(point searchpt, triface *searchtet) +{ + tetrahedron *firsttet, *tetptr; + void **sampleblock; + long sampleblocks, samplesperblock, samplenum; + long tetblocks, i, j; + uintptr_t 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); + } + + // Note 'searchtet' may be dead (chnaged in constrainedcavity2()). + if (!isdead(searchtet)) { + // Get the distance from the suggested starting tet to the point we seek. + searchdist = distance2(searchtet->tet, searchpt); + } else { + searchdist = longest * longest; + } + + // 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 = (uintptr_t) (sampleblock + 1); + firsttet = (tetrahedron *) + (alignptr + (uintptr_t) tetrahedrons->alignbytes + - (alignptr % (uintptr_t) tetrahedrons->alignbytes)); + for (j = 0; j < samplesperblock; j++) { + if (i == tetblocks - 1) { + // This is the last block. + samplenum = randomnation((int) + (tetrahedrons->maxitems - (i * 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; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// locate() Find a simplex containing a given point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, + triface *searchtet) +{ + // Randomly sample for a good starting tet. + randomsample(searchpt, searchtet); + // Call simple walk-through to locate the point. + return preciselocate(searchpt, searchtet, tetrahedrons->items); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// locate2() Find a simplex containing a given point. // +// // +// Another implementation of the Walk-through point location algorithm. // +// See the comments of preciselocate(). // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::locate2(point searchpt, + triface* searchtet, arraypool *histtetarray) +{ + triface neightet, backtracetet, *parytet; + point torg, tdest, tapex, toppo, ntoppo; + enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; + REAL ori, oriorg, oridest, oriapex; + REAL searchdist, dist; + enum locateresult loc; + int i; + + if (searchtet->tet == dummytet) { + // A hull tet. Choose the neighbor of its base face. + searchtet->loc = 0; + symself(*searchtet); + } + + // Stay in the 0th edge ring. + searchtet->ver = 0; + + // Let searchtet be the face such that 'searchpt' lies above to it. + for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) { + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + ori = orient3d(torg, tdest, tapex, searchpt); orient3dcount++; + if (ori < 0.0) break; + } + if (!(searchtet->loc < 4)) { + // Either 'searchtet' is a very flat tet, or the 'searchpt' lies in + // infinity, or both of them. Return OUTSIDE. + return OUTSIDE; + } + + if (histtetarray != NULL) { + // Remember all the tets we've visited. + assert(histtetarray->objects == 0l); + infect(*searchtet); + histtetarray->newindex((void **) &parytet); + *parytet = *searchtet; + } + + loc = OUTSIDE; // Set a default return value. + + // Walk through tetrahedra to locate the point. + while (true) { + + ptloc_count++; // Algorithimic count. + + toppo = oppo(*searchtet); + + // Check if the vertex is we seek. + if (toppo == searchpt) { + // Adjust the origin of searchtet to be searchpt. + fnextself(*searchtet); + esymself(*searchtet); + enext2self(*searchtet); + loc = ONVERTEX; // return ONVERTEX; + break; + } + + // We enter from serarchtet's base face. There are three other faces in + // searchtet (all connecting to toppo), which one is the exit? + oriorg = orient3d(tdest, tapex, toppo, searchpt); + oridest = orient3d(tapex, torg, toppo, searchpt); + oriapex = orient3d(torg, tdest, toppo, searchpt); + orient3dcount+=3; + + // Now decide which face to move. It is possible there are more than one + // faces are viable moves. Use the opposite points of thier neighbors + // to discriminate, i.e., we choose the face whose opposite point has + // the shortest distance to searchpt. + if (oriorg < 0) { + if (oridest < 0) { + if (oriapex < 0) { + // Any of the three faces is a viable move. + nextmove = ORGMOVE; + enextfnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + searchdist = NORM2(searchpt[0] - ntoppo[0], + searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext2fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = DESTMOVE; + searchdist = dist; + } + fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = APEXMOVE; + searchdist = dist; + } + } else { + // Two faces, opposite to origin and destination, are viable. + nextmove = ORGMOVE; + enextfnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + searchdist = NORM2(searchpt[0] - ntoppo[0], + searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext2fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = DESTMOVE; + searchdist = dist; + } + } + } else { + if (oriapex < 0) { + // Two faces, opposite to origin and apex, are viable. + nextmove = ORGMOVE; + enextfnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + searchdist = NORM2(searchpt[0] - ntoppo[0], + searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = APEXMOVE; + searchdist = dist; + } + } else { + // Only the face opposite to origin is viable. + nextmove = ORGMOVE; + } + } + } else { + if (oridest < 0) { + if (oriapex < 0) { + // Two faces, opposite to destination and apex, are viable. + nextmove = DESTMOVE; + enext2fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + searchdist = NORM2(searchpt[0] - ntoppo[0], + searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + ntoppo = oppo(neightet); + dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1], + searchpt[2] - ntoppo[2]); + } else { + dist = searchdist; + } + if (dist < searchdist) { + nextmove = APEXMOVE; + searchdist = dist; + } + } else { + // Only the face opposite to destination is viable. + nextmove = DESTMOVE; + } + } else { + if (oriapex < 0) { + // Only the face opposite to apex is viable. + nextmove = APEXMOVE; + } else { + // The point we seek must be on the boundary of or inside this + // tetrahedron. Check for boundary cases. + if (oriorg == 0) { + // Go to the face opposite to origin. + enextfnextself(*searchtet); + if (oridest == 0) { + enextself(*searchtet); // edge apex->oppo + if (oriapex == 0) { + enextself(*searchtet); // oppo is duplicated with p. + loc = ONVERTEX; // return ONVERTEX; + break; + } + loc = ONEDGE; // return ONEDGE; + break; + } + if (oriapex == 0) { + enext2self(*searchtet); + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oridest == 0) { + // Go to the face opposite to destination. + enext2fnextself(*searchtet); + if (oriapex == 0) { + enextself(*searchtet); + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oriapex == 0) { + // Go to the face opposite to apex + fnextself(*searchtet); + loc = ONFACE; // return ONFACE; + break; + } + loc = INTETRAHEDRON; // return INTETRAHEDRON; + break; + } + } + } + + // Move to the selected face. + if (nextmove == ORGMOVE) { + enextfnextself(*searchtet); + } else if (nextmove == DESTMOVE) { + enext2fnextself(*searchtet); + } else { + fnextself(*searchtet); + } + // Move to the adjacent tetrahedron (maybe a hull tetrahedron). + backtracetet = *searchtet; + symself(*searchtet); + if (searchtet->tet == dummytet) { + *searchtet = backtracetet; + loc = OUTSIDE; // return OUTSIDE; + break; + } + + if (histtetarray != NULL) { + // Check if we have run into a loop. + if (infected(*searchtet)) { + // We have visited this tet. A potential loop is found. + loc = OUTSIDE; + break; + } else { + // Remember this tet. + infect(*searchtet); + histtetarray->newindex((void **) &parytet); + *parytet = *searchtet; + } + } + + // Retreat the three vertices of the base face. + searchtet->ver = 0; + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + + } // while (true) + + if (histtetarray != NULL) { + // Unmark the visited tets. + for (i = 0; i < (int) histtetarray->objects; i++) { + parytet = (triface *) fastlookup(histtetarray, i); + uninfect(*parytet); + } + histtetarray->restart(); + } + + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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(). // +// // +/////////////////////////////////////////////////////////////////////////////// + +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, pp[3]; + enum locateresult loc; + REAL prjpt[3]; + REAL ori; + int i, j; + + 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) { + // searchpt is outside the hull face. Project it on the face. + travtet.ver = 1; + pp[0] = org(travtet); + pp[1] = dest(travtet); + pp[2] = apex(travtet); + projpt2face(searchpt, pp[0], pp[1], pp[2], prjpt); + // check if project point inside the hull face. + for (j = 0; j < 3; j++) { + ori = orient3d(pp[j], pp[(j+1)%3], searchpt, prjpt); + if (ori < 0.0) break; // Stop if it lies ouside. + } + if (ori >= 0.0) { + // Yes, return this tet. + *hulltet = travtet; + } + // 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'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +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; +} + +//// //// +//// //// +//// geom_cxx ///////////////////////////////////////////////////////////////// + +//// flip_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 > 1) { + 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); + } + 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); + } + } + if (checksubsegs) { + for (i = 0; i < 6; i++) { + edab.loc = edge2locver[i][0]; + edab.ver = edge2locver[i][1]; + tssdissolve1(edab); + } + for (i = 0; i < 6; i++) { + edbc.loc = edge2locver[i][0]; + edbc.ver = edge2locver[i][1]; + tssdissolve1(edbc); + } + 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); + } + + // Update point-to-tet map. + setpoint2tet(pa, encode(edab)); + setpoint2tet(pb, encode(edab)); + setpoint2tet(pc, encode(edbc)); + setpoint2tet(pd, encode(edab)); + setpoint2tet(pe, encode(edab)); + + 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 > 1) { + 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); + } + 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.loc = i; + tsdissolve(abcd); + } + for (i = 0; i < 4; i ++) { + bace.loc = i; + tsdissolve(bace); + } + 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); + } + } + if (checksubsegs) { + for (i = 0; i < 6; i++) { + abcd.loc = edge2locver[i][0]; + abcd.ver = edge2locver[i][1]; + tssdissolve1(abcd); + } + for (i = 0; i < 6; i++) { + bace.loc = edge2locver[i][0]; + bace.ver = edge2locver[i][1]; + tssdissolve1(bace); + } + 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); + } + + // Update point-to-tet map. + setpoint2tet(pa, encode(abcd)); + setpoint2tet(pb, encode(abcd)); + setpoint2tet(pc, encode(abcd)); + setpoint2tet(pd, encode(abcd)); + setpoint2tet(pe, encode(bace)); + + 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 > 1) { + printf(" Flip edge (%d, %d) to (%d, %d) %s.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), mirrorflag ? "T44" : "T22"); + } + 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); + } + 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); + } + 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); + } + } + 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); + } + } + 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); + } + + // Update point-to-tet map. + setpoint2tet(pa, encode(abce)); + setpoint2tet(pb, encode(bade)); + setpoint2tet(pc, encode(abce)); + setpoint2tet(pd, encode(bade)); + setpoint2tet(pe, encode(abce)); + if (mirrorflag) { + setpoint2tet(pf, encode(bacf)); + } + + // 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 > 1) { + printf(" Flip subedge (%d, %d) to (%d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + + // Unmark the flipped subfaces (used in mesh refinement). 2009-08-17. + sunmarktest(abc); + sunmarktest(bad); + + // 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. + sdissolve(oldbc); // 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. + sdissolve(oldca); // 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. + sdissolve(oldad); // 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. + sdissolve(olddb); // 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); + + // Update the point-to-subface map. + // Comemnt: After the flip, abc becomes dca, bad becodes cdb. + setpoint2sh(pa, sencode(abc)); // dca + setpoint2sh(pb, sencode(bad)); // cdb + setpoint2sh(pc, sencode(bad)); + setpoint2sh(pd, sencode(bad)); + + if (flipqueue != (queue *) NULL) { + enqueueflipedge(bccasout, flipqueue); + enqueueflipedge(cacasout, flipqueue); + enqueueflipedge(adcasout, flipqueue); + enqueueflipedge(dbcasout, flipqueue); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lawson3d() Perform 3D Lawson flips on non-Delaunay faces/edges. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long tetgenmesh::lawson3d(queue* flipqueue) +{ + badface *qface; + triface flipface, symface, flipedge; + triface neighface, symneighface; + face checksh, checkseg; + face neighsh, symneighsh; + point pa, pb, pc, pd, pe; + point end1, end2; + REAL sign, ori1, ori2, ori3; + REAL ori4, len, vol; + long flipcount; + int copflag; + int i; + + if (b->verbose > 1) { + printf(" Lawson flip: %ld faces.\n", flipqueue->len()); + } + flipcount = flip23s + flip32s + flip22s + flip44s; + + // Loop until the queue is empty. + while (!flipqueue->empty()) { + qface = (badface *) flipqueue->pop(); + flipface = qface->tt; + if (isdead(&flipface)) continue; + if (flipface.tet == dummytet) continue; + // Do not flip it if it is a subface. + tspivot(flipface, checksh); + if (checksh.sh != dummysh) 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)) { + flipface.ver = 0; // CCW. + pa = org(flipface); + pb = dest(flipface); + pc = apex(flipface); + pd = oppo(flipface); + pe = oppo(symface); + sign = insphere_s(pb, pa, pc, pd, pe); + assert(sign != 0.0); + + if (sign > 0.0) { + // flipface is not locally Delaunay. Try to flip it. + ori1 = orient3d(pa, pb, pd, pe); + ori2 = orient3d(pb, pc, pd, pe); + ori3 = orient3d(pc, pa, pd, pe); + + flipedge = flipface; // Initialize flipedge. + copflag = 0; + + // Find a suitable flip. + if (ori1 > 0) { + if (ori2 > 0) { + if (ori3 > 0) { // (+++) + // A 2-to-3 flip is found. + // Do not flip it if it is a subface. + // tspivot(flipface, checksh); + // if (checksh.sh == dummysh) { + // Do not flip it if it will create a tet spanning two + // "coplanar" subfaces. We treat this case as either + // a 2-to-2 or a 4-to-4 flip. + for (i = 0; i < 3; i++) { + tsspivot(&flipface, &checkseg); + if (checkseg.sh == dummysh) { + fnext(flipface, neighface); + tspivot(neighface, neighsh); + if (neighsh.sh != dummysh) { + // Check if there exist another subface. + symedge(flipface, symface); + fnext(symface, symneighface); + tspivot(symneighface, symneighsh); + if (symneighsh.sh != dummysh) { + // Do not flip this face. Try to do a 2-to-2 or a + // 4-to-4 flip instead. + flipedge = flipface; + copflag = 1; + break; + } + } + } + enextself(flipface); + } + if (i == 3) { + // Do not flip if it will create a nearly degenerate tet + // at a segment. Once we created such a tet, it may + // prevent you to split the segment later. An example + // is in dump-.lua + for (i = 0; i < 3; i++) { + tsspivot(&flipface, &checkseg); + if (checkseg.sh != dummysh) { + end1 = (point) checkseg.sh[3]; + end2 = (point) checkseg.sh[4]; + ori4 = orient3d(end1, end2, pd, pe); + len = distance(end1, end2); + vol = len * len * len; + // Is it nearly degnerate? + if ((fabs(ori4) / vol) < b->epsilon) { + flipedge = flipface; + copflag = 0; + break; + } + } + enextself(flipface); + } + if (i == 3) { + flip23(&flipface, flipqueue); + continue; + } + } + // } + } else { + if (ori3 < 0) { // (++-) + // Try to flip edge [c, a]. + flipedge.ver = 4; + copflag = 0; + } else { // (++0) + // A 2-to-2 or 4-to-4 flip at edge [c, a]. + flipedge.ver = 4; + copflag = 1; + } + } + } else { + if (ori2 < 0) { + if (ori3 > 0) { // (+-+) + // Try to flip edge [b, c]. + flipedge.ver = 2; + copflag = 0; + } else { + if (ori3 < 0) { // (+--) + // Not possible when pe is inside the circumsphere of + // the tet [pa.pb, pc, pd]. + assert(0); + } else { // (+-0) + assert(0); // The same reason as above. + } + } + } else { // ori2 == 0 + if (ori3 > 0) { // (+0+) + // A 2-to-2 or 4-to-4 flip at edge [b, c]. + flipedge.ver = 2; + copflag = 1; + } else { + if (ori3 < 0) { // (+0-) + // Not possible when pe is inside the circumsphere of + // the tet [pa.pb, pc, pd]. + assert(0); + } else { // (+00) + assert(0); // The same reason as above. + } + } + } + } + } else { + if (ori1 < 0) { + if (ori2 > 0) { + if (ori3 > 0) { // (-++) + // Try to flip edge [a, b]. + flipedge.ver = 0; + copflag = 0; + } else { + if (ori3 < 0) { // (-+-) + // Not possible when pe is inside the circumsphere of + // the tet [pa.pb, pc, pd]. + assert(0); + } else { // (-+0) + assert(0); // The same reason as above. + } + } + } else { + if (ori2 < 0) { + if (ori3 > 0) { // (--+) + // Not possible when pe is inside the circumsphere of + // the tet [pa.pb, pc, pd]. + assert(0); + } else { + if (ori3 < 0) { // (---) + assert(0); + } else { // (--0) + assert(0); + } + } + } else { // ori2 == 0 + if (ori3 > 0) { // (-0+) + assert(0); + } else { + if (ori3 < 0) { // (-0-) + assert(0); + } else { // (-00) + assert(0); + } + } + } + } + } else { // ori1 == 0 + if (ori2 > 0) { + if (ori3 > 0) { // (0++) + // A 2-to-2 or 4-to-4 flip at edge [a, b]. + flipedge.ver = 0; + copflag = 1; + } else { + if (ori3 < 0) { // (0+-) + assert(0); + } else { // (0+0) + assert(0); + } + } + } else { + if (ori2 < 0) { + if (ori3 > 0) { // (0-+) + assert(0); + } else { + if (ori3 < 0) { // (0--) + assert(0); + } else { // (0-0) + assert(0); + } + } + } else { + if (ori3 > 0) { // (00+) + assert(0); + } else { + if (ori3 < 0) { // (00-) + assert(0); + } else { // (000) + assert(0); + } + } + } + } + } + } + + // An edge (flipedge) is going to be flipped. + // Do not flip it it is a subsegment. + tsspivot(&flipedge, &checkseg); + if (checkseg.sh == dummysh) { + symedge(flipedge, symface); + if (copflag == 0) { + // Check if a 3-to-2 flip is possible. + tfnext(flipedge, neighface); + if (neighface.tet != dummytet) { + // Check if neighface is a subface. + tspivot(neighface, neighsh); + if (neighsh.sh == dummysh) { + tfnext(symface, symneighface); + if (neighface.tet == symneighface.tet) { + // symneighface should not be a subface. Check it. + tspivot(symneighface, symneighsh); + assert(symneighsh.sh == dummysh); + // Found a 3-to-2 flip. + flip32(&flipedge, flipqueue); + } + } else { + // neighsh is a subface. Check a potential 4-to-4 flip. + tfnext(symface, symneighface); + tspivot(symneighface, symneighsh); + if (symneighsh.sh != dummysh) { + if (oppo(neighface) == oppo(symneighface)) { + // Found a 4-to-4 flip. + flip22(&flipedge, flipqueue); + } + } + } + } else { + // neightface is a hull face. Since flipedge is not a segment + // and this edge is locally non-convex. + tfnext(symface, symneighface); + // symneighface should also be a hull face. + if (symneighface.tet == dummytet) { + // Force a 2-to-2 flip (recovery of Delaunay). + flip22(&flipedge, flipqueue); + } + } + } else { + // Check if a 2-to-2 or 4-to-4 flip is possible. + tfnext(flipedge, neighface); + tfnext(symface, symneighface); + if (neighface.tet != dummytet) { + if (symneighface.tet != dummytet) { + if (oppo(neighface) == oppo(symneighface)) { + // Found a 4-to-4 flip. + flip22(&flipedge, flipqueue); + } + } + } else { + if (symneighface.tet == dummytet) { + // Found a 2-to-2 flip. + flip22(&flipedge, flipqueue); + } + } + } + } + + } // if (sign > 0) + } + } // while (!flipqueue->empty()) + + flipcount = flip23s + flip32s + flip22s + flip44s - flipcount; + if (b->verbose > 1) { + printf(" %ld flips.\n", flipcount); + } + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lawson() Perform lawson flips on non-Delaunay edges. // +// // +// 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::lawson(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, maxflips; + int i; + + if (b->verbose > 1) { + printf(" Lawson flip: %ld edges.\n", flipqueue->len()); + } + + if (b->diagnose) { + maxflips = (int) ((flipqueue->len() + 1l) * 3l); + maxflips *= maxflips; + } else { + maxflips = -1; + } + edgeflips = 0; + + while (!flipqueue->empty() && maxflips != 0) { + 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 (maxflips > 0) maxflips--; + } + } + + if (!maxflips && !b->quiet) { + printf("Warning: Maximal number of flips reached !\n"); + } + + 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). // +// // +// 'adjtetlist[2]' returns the two new boundary faces (in tet) 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 *adjtetlist) +{ + 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); + + adjtetlist[0] = cdbcasing; + adjtetlist[1] = dcacasing; + + 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. // +// Comment: the edge ab is in CW edge ring of the three faces: abc, abd, and // +// abe. (2009-06-29) // +// // +// 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, checkseg; + 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; + } + } + + // Comment: This edge must not be fixed. It has been checked before. + if (doflip && (elemfliplist != NULL)) { + // Regist this flip. + if (!registerelemflip(T32, pa, pb, dummypoint, pc, pd, pe)) { + // Detected a potential flip loop. Don't do it. + return false; + } + } + + 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[21]; // Temporary max 20 tets configuration. + triface newfront, oldfront, adjfront; + face checksh; + point pa, pb, p[21]; + REAL ori, cosmaxd, d1, d2; + REAL tmpkey; + REAL attrib, volume; + bool doflip, copflag, success; + int i, j, k; + + // Maximum 20 tets. + assert(n < 20); // n <= b->maxflipedgelinksize + // 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 && (elemfliplist != NULL)) { + // Comment: The flipping face must be not fixed. This case has been + // tested during collecting the face ring of this edge. + // Do not flip this face if it has been registered before. + if (!registerelemflip(T23, pa, pb, p[0], p[1], p[n-1], dummypoint)) { + doflip = false; // Do not flip this face. + } + } + 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. + if (elemfliplist != NULL) { + // Remove the last registered 2-to-3 flip. + elemfliplist->objects--; + } + 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[21]; + triface newfront, oldfront, adjfront; + face checksh; + point pa, pb, p[21]; + REAL ori, tmpkey, tmpkey2; + REAL attrib, volume; + bool doflip, success; + int twice, count; + int i, j, k, m; + + // point *ppt; // Used together with fixededgelist. + long bakflipcount; // Used for elemfliplist. + + // Maximal 20 tets in Star(ab). + assert(n < 20); // n <= b->maxflipedgelinksize + + // 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 ((fixededgelist != NULL) && (ori <= 0.0)) { + // b.p_0 is either N32 or N44. Do not flip a fixed edge. + if (check4fixededge(pb, p[0])) { + ori = 1.0; // Do not flip this edge. Skip it. + } + } + 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 == 20) 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. + if (!fnext(bftetlist[*n1], bftetlist[(*n1) + 1])) { + // Meet a boundary face. Do not flip. + doflip = false; break; + } + (*n1)++; + } while (apex(bftetlist[*n1]) != pa); + // 2 < n1 <= b->maxflipedgelinksize. + 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 <= b->maxflipedgelinksize)) { + // 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 (elemfliplist != NULL) { + // Remember the current registered flips. + bakflipcount = elemfliplist->objects; + } + 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. + if (elemfliplist != NULL) { + // Restore the registered flips. + elemfliplist->objects = bakflipcount; + } + 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 (bccasout.sh != dummysh) { + // 'oldbc' is not self-bonded. + spinsh = bccasout; + do { + bccasin = spinsh; + spivotself(spinsh); + } while (spinsh.sh != oldbc.sh); + } + ssdissolve(oldbc); + } + spivot(oldca, cacasout); + sspivot(oldca, ca); + if (ca.sh != dummysh) { + if (cacasout.sh != dummysh) { + // 'oldca' is not self-bonded. + spinsh = cacasout; + do { + cacasin = spinsh; + spivotself(spinsh); + } while (spinsh.sh != oldca.sh); + } + 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. + sdissolve(bcv); // 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. + sdissolve(cav); // 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool 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); + break; + } + volume = orient3d(pb, n2, n1, newpoint); + if (volume >= 0.0) { + //printf("Internal error in splittetedge(): volume = %.12g.\n", volume); + break; + } +//#endif + } + + if (i < wrapcount) { + // Do not insert this point. It will result inverted or degenerated tet. + // Restore have updated tets in "bots". + for (; i >= 0; i--) { + setoppo(bots[i], pb); + } + // Deallocate tets in "newtops". + for (i = 0; i < wrapcount; i++) { + tetrahedrondealloc(newtops[i].tet); + } + delete [] newtops; + delete [] bots; + return false; + } + + // 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; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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); + } + // Unmark the face for splitting (used for refinement) 2009-08-17. + sunmarktest(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 (bccasout.sh != dummysh) { + // '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. + sdissolve(vbc); // 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) { + // Unmark the segment for mesh optimization. 2009-08-17. + sunmarktest(ab); + // 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); + if (spinabc.sh == dummysh) { + break; // There's only one facet at the segment.rr + } + } 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); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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)); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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) { + // Some subfaces have been removed from the cavity. + if (checkpbcs) { + // Check if the facet has a PBC defined. + checksh = * (face *)(* sublist)[0]; + if (shellpbcgroup(checksh) >= 0) { + // Yes, A PBC facet. Remove all subfaces -- Do not insert the point. + for (i = 0; i < sublist->len(); i++) { + checksh = * (face *)(* sublist)[i]; + suninfect(checksh); + // Remove both side tets from the cavity. + for (j = 0; j < 2; j++) { + stpivot(checksh, adjtet); + if (adjtet.tet != dummytet) { + if (infected(adjtet)) { + uninfect(adjtet); + (*cutcount)++; + } + } + sesymself(checksh); + } + } + remcount += sublist->len(); + sublist->clear(); + } + } + 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 (casingout.sh != dummysh) { // 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. + sdissolve(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. + lawson3d(flipque); + } +} + +//// //// +//// //// +//// flip_cxx ///////////////////////////////////////////////////////////////// + +//// delaunay_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// btree_sort() Sort vertices using a binary space partition (bsp) tree. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::btree_sort(point* vertexarray, int arraysize, int axis, + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax, + int depth) +{ + point *leftarray, *rightarray; + point **pptary, swapvert; + REAL split; + bool lflag, rflag; + int i, j, k; + + if (b->verbose > 2) { + printf(" Depth %d, %d verts. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n", + depth, arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax, + axis == 0 ? "x" : (axis == 1 ? "y" : "z")); + } + + if (depth > max_btree_depth) { + max_btree_depth = depth; + } + + if (axis == 0) { + // Split along x-axis. + split = 0.5 * (bxmin + bxmax); + } else if (axis == 1) { + // Split along y-axis. + split = 0.5 * (bymin + bymax); + } else { + // Split along z-axis. + split = 0.5 * (bzmin + bzmax); + } + + i = 0; + j = arraysize - 1; + + // Partition the vertices into left- and right-arraies. + do { + for (; i < arraysize; i++) { + if (vertexarray[i][axis] >= split) { + break; + } + } + for (; j >= 0; j--) { + if (vertexarray[j][axis] < split) { + break; + } + } + // Is the partition finished? + if (i == (j + 1)) { + break; + } + // Swap i-th and j-th vertices. + swapvert = vertexarray[i]; + vertexarray[i] = vertexarray[j]; + vertexarray[j] = swapvert; + // Continue patitioning the array; + } while (true); + + if (b->verbose > 2) { + printf(" leftsize = %d, rightsize = %d\n", i, arraysize - i); + } + lflag = rflag = false; + + // if (depth < max_tree_depth) { + if (i > b->max_btreenode_size) { + // Recursively partition the left array (length = i). + if (axis == 0) { // x + btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, split, bymin, + bymax, bzmin, bzmax, depth + 1); + } else if (axis == 1) { // y + btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin, + split, bzmin, bzmax, depth + 1); + } else { // z + btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin, + bymax, bzmin, split, depth + 1); + } + } else { + lflag = true; + } + if ((arraysize - i) > b->max_btreenode_size) { + // Recursively partition the right array (length = arraysize - i). + if (axis == 0) { // x + btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, split, + bxmax, bymin, bymax, bzmin, bzmax, depth + 1); + } else if (axis == 1) { // y + btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin, + bxmax, split, bymax, bzmin, bzmax, depth + 1); + } else { // z + btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin, + bxmax, bymin, bymax, split, bzmax, depth + 1); + } + } else { + rflag = true; + } + // } else { + // // Both left and right are done. + // lflag = rflag = true; + // } + + if (lflag && (i > 0)) { + // Remember the maximal length of the partitions. + if (i > max_btreenode_size) { + max_btreenode_size = i; + } + // Allocate space for the left array (use the first entry to save + // the length of this array). + leftarray = new point[i + 1]; + leftarray[0] = (point) i; // The array lenth. + // Put all points in this array. + for (k = 0; k < i; k++) { + leftarray[k + 1] = vertexarray[k]; + setpoint2ppt(leftarray[k + 1], (point) leftarray); + } + // Save this array in list. + btreenode_list->newindex((void **) &pptary); + *pptary = leftarray; + } + + // Get the length of the right array. + j = arraysize - i; + if (rflag && (j > 0)) { + if (j > max_btreenode_size) { + max_btreenode_size = j; + } + // Allocate space for the right array (use the first entry to save + // the length of this array). + rightarray = new point[j + 1]; + rightarray[0] = (point) j; // The array lenth. + // Put all points in this array. + for (k = 0; k < j; k++) { + rightarray[k + 1] = vertexarray[i + k]; + setpoint2ppt(rightarray[k + 1], (point) rightarray); + } + // Save this array in list. + btreenode_list->newindex((void **) &pptary); + *pptary = rightarray; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// btree_insert() Add a vertex into a tree node. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::btree_insert(point insertpt) +{ + point *ptary; + long arylen; // The array lenhgth is saved in ptary[0]. + + // Get the tree node (save in this point). + ptary = (point *) point2ppt(insertpt); + // Get the current array length. + arylen = (long) ptary[0]; + // Insert the point into the node. + ptary[arylen + 1] = insertpt; + // Increase the array length by 1. + ptary[0] = (point) (arylen + 1); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// btree_search() Search a near point for an inserting point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::btree_search(point insertpt, triface* searchtet) +{ + point *ptary; + point nearpt, candpt; + REAL dist2, mindist2; + int ptsamples, ptidx; + long arylen; + int i; + + // Get the tree node (save in this point). + ptary = (point *) point2ppt(insertpt); + // Get the current array length. + arylen = (long) ptary[0]; + + if (arylen == 0) { + searchtet->tet = NULL; + return; + } + + if (arylen < 10) { + ptsamples = arylen; + } else { + ptsamples = 10; // Take at least 10 samples. + // The number of random samples taken is proportional to the third root + // of the number of points in the cell. + while (ptsamples * ptsamples * ptsamples < arylen) { + ptsamples++; + } + } + + // Select "good" candidate using k random samples, taking the closest one. + mindist2 = 1.79769E+308; // The largest double value (8 byte). + nearpt = NULL; + + for (i = 0; i < ptsamples; i++) { + ptidx = randomnation((unsigned long) arylen); + candpt = ptary[ptidx + 1]; + dist2 = (candpt[0] - insertpt[0]) * (candpt[0] - insertpt[0]) + + (candpt[1] - insertpt[1]) * (candpt[1] - insertpt[1]) + + (candpt[2] - insertpt[2]) * (candpt[2] - insertpt[2]); + if (dist2 < mindist2) { + mindist2 = dist2; + nearpt = candpt; + } + } + + if (b->verbose > 1) { + printf(" Get point %d (cell size %ld).\n", pointmark(nearpt), arylen); + } + + decode(point2tet(nearpt), *searchtet); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// ordervertices() Order the vertices for incremental inserting. // +// // +// We assume the vertices have been sorted by a binary tree. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::ordervertices(point* vertexarray, int arraysize) +{ + point **ipptary, **jpptary, *swappptary; + point *ptary; + long arylen; + int index, i, j; + + // First pick one vertex from each tree node. + for (i = 0; i < (int) btreenode_list->objects; i++) { + ipptary = (point **) fastlookup(btreenode_list, i); + ptary = *ipptary; + vertexarray[i] = ptary[1]; // Skip the first entry. + } + + index = i; + // Then put all other points in the array node by node. + for (i = (int) btreenode_list->objects - 1; i >= 0; i--) { + // Randomly pick a tree node. + j = randomnation(i + 1); + // Save the i-th node. + ipptary = (point **) fastlookup(btreenode_list, i); + // Get the j-th node. + jpptary = (point **) fastlookup(btreenode_list, j); + // Order the points in the node. + ptary = *jpptary; + arylen = (long) ptary[0]; + for (j = 2; j <= arylen; j++) { // Skip the first point. + vertexarray[index] = ptary[j]; + index++; + } + // Clear this tree node. + ptary[0] = (point) 0; + // Swap i-th node to j-th node. + swappptary = *ipptary; + *ipptary = *jpptary; // [i] <= [j] + *jpptary = swappptary; // [j] <= [i] + } + + // Make sure we've done correctly. + assert(index == arraysize); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertvertexbw() Insert a vertex using the Boywer-Watson algorithm. // +// // +// The point p will be first located in T. 'searchtet' is a suggested start- // +// tetrahedron, it can be NULL. Note that p may lies outside T. In such case,// +// the convex hull of T will be updated to include p as a vertex. // +// // +// If 'bwflag' is TRUE, the Bowyer-Watson algorithm is used to recover the // +// Delaunayness of T. Otherwise, do nothing with regard to the Delaunayness // +// T (T may be non-Delaunay after this function). // +// // +// If 'visflag' is TRUE, force to check the visibility of the boundary faces // +// of cavity. This is needed when T is not Delaunay. // +// // +// If 'noencflag' is TRUE, only insert the new point p if it does not cause // +// any existing (sub)segment be non-Delaunay. This option only is checked // +// when the global variable 'checksubsegs' is set. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::insertvertexbw(point insertpt, + triface *searchtet, bool bwflag, bool visflag, bool noencsegflag, + bool noencsubflag) +{ + triface neightet, spintet, newtet, neineitet; + triface *cavetet, *parytet, *parytet1; + face checksh, *pssub; + face checkseg, *paryseg; + point pa, pb, pc, *ppt; + enum locateresult loc; + REAL attrib, volume; + REAL sign, ori; + long tetcount; + bool enqflag; + int hitbdry; + int i, j; + + arraypool *swaplist; // for updating cavity. + long updatecount; + + if (b->verbose > 1) { + printf(" Insert point %d\n", pointmark(insertpt)); + } + + tetcount = ptloc_count; + updatecount = 0; + + // Locate the point. + if (searchtet->tet == NULL) { + if (btreenode_list) { // default option + // Use bsp-tree to select a starting tetrahedron. + btree_search(insertpt, searchtet); + } else { // -u0 option + // Randomly select a starting tetrahedron. + randomsample(insertpt, searchtet); + } + loc = preciselocate(insertpt, searchtet, tetrahedrons->items); + } else { + // Start from 'searchtet'. + loc = locate2(insertpt, searchtet, NULL); + } + + if (b->verbose > 1) { + printf(" Walk distance (# tets): %ld\n", ptloc_count - tetcount); + } + + if (ptloc_max_count < (ptloc_count - tetcount)) { + ptloc_max_count = (ptloc_count - tetcount); + } + + if (b->verbose > 1) { + printf(" Located (%d) tet (%d, %d, %d, %d).\n", (int) loc, + pointmark(org(*searchtet)), pointmark(dest(*searchtet)), + pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); + } + + if (loc == ONVERTEX) { + // The point already exists. Mark it and do nothing on it. + if (b->object != tetgenbehavior::STL) { + if (!b->quiet) { + printf("Warning: Point #%d is duplicated with Point #%d. Ignored!\n", + pointmark(insertpt), pointmark(org(*searchtet))); + } + } + setpoint2ppt(insertpt, org(*searchtet)); + setpointtype(insertpt, DUPLICATEDVERTEX); + dupverts++; + return loc; + } + + tetcount = 0l; // The number of deallocated tets. + + // Create the initial boundary of the cavity. + if (loc == INTETRAHEDRON) { + // Add four boundary faces of this tet into list. + neightet.tet = searchtet->tet; + for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + tetcount++; + flip14count++; + } else if (loc == ONFACE) { + // Add at most six boundary faces into list. + neightet.tet = searchtet->tet; + for (i = 0; i < 3; i++) { + neightet.loc = locpivot[searchtet->loc][i]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + tetcount++; + decode(searchtet->tet[searchtet->loc], spintet); + if (spintet.tet != dummytet) { + neightet.tet = spintet.tet; + for (i = 0; i < 3; i++) { + neightet.loc = locpivot[spintet.loc][i]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + tetcount++; + } else { + // Split a hull face into three hull faces. + hullsize += 2; + } + flip26count++; + } else if (loc == ONEDGE) { + // Add all adjacent boundary tets into list. + spintet = *searchtet; + pc = apex(spintet); + hitbdry = 0; + do { + tetcount++; + neightet.tet = spintet.tet; + neightet.loc = locverpivot[spintet.loc][spintet.ver][0]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + neightet.loc = locverpivot[spintet.loc][spintet.ver][1]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + // Go to the next tet (may be dummytet). + tfnext(spintet, neightet); + if (neightet.tet == dummytet) { + hitbdry++; + if (hitbdry == 2) break; + esym(*searchtet, spintet); // Go to another direction. + tfnext(spintet, neightet); + if (neightet.tet == dummytet) break; + } + spintet = neightet; + } while (apex(spintet) != pc); + // Update hull size if it is a hull edge. + if (hitbdry > 0) { + // Split a hull edge deletes two hull faces, adds four new hull faces. + hullsize += 2; + } + flipn2ncount++; + } else if (loc == OUTSIDE) { + // p lies outside the convex hull. Enlarge the convex hull by including p. + if (b->verbose > 1) { + printf(" Insert a hull vertex.\n"); + } + // 'searchtet' refers to a hull face which is visible by p. + adjustedgering(*searchtet, CW); + // Create the first tet t (from f and p). + maketetrahedron(&newtet); + setorg (newtet, org(*searchtet)); + setdest(newtet, dest(*searchtet)); + setapex(newtet, apex(*searchtet)); + setoppo(newtet, insertpt); + for (i = 0; i < in->numberoftetrahedronattributes; i++) { + attrib = elemattribute(searchtet->tet, i); + setelemattribute(newtet.tet, i, attrib); + } + if (b->varvolume) { + volume = volumebound(searchtet->tet); + setvolumebound(newtet.tet, volume); + } + // Connect t to T. + bond(newtet, *searchtet); + // Removed a hull face, added three "new hull faces". + hullsize += 2; + + // Add a cavity boundary face. + cavetetlist->newindex((void **) &parytet); + *parytet = newtet; + // Add a cavity tet. + infect(newtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = newtet; + tetcount++; + + // Add three "new hull faces" into list (re-use cavebdrylist). + newtet.ver = 0; + for (i = 0; i < 3; i++) { + fnext(newtet, neightet); + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + enextself(newtet); + } + + // Find all actual new hull faces. + for (i = 0; i < (int) cavebdrylist->objects; i++) { + // Get a queued "new hull face". + parytet = (triface *) fastlookup(cavebdrylist, i); + // Every "new hull face" must have p as its apex. + assert(apex(*parytet) == insertpt); + assert((parytet->ver & 1) == 1); // It's CW edge ring. + // Check if it is still a hull face. + sym(*parytet, neightet); + if (neightet.tet == dummytet) { + // Yes, get its adjacent hull face (at its edge). + esym(*parytet, neightet); + while (1) { + fnextself(neightet); + // Does its adjacent tet exist? + sym(neightet, neineitet); + if (neineitet.tet == dummytet) break; + symedgeself(neightet); + } + // neightet is an adjacent hull face. + pc = apex(neightet); + if (pc != insertpt) { + // Check if p is visible by the hull face ('neightet'). + pa = org(neightet); + pb = dest(neightet); + ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; + if (ori < 0) { + // Create a new tet adjacent to neightet. + maketetrahedron(&newtet); + setorg (newtet, pa); + setdest(newtet, pb); + setapex(newtet, pc); + setoppo(newtet, insertpt); + for (j = 0; j < in->numberoftetrahedronattributes; j++) { + attrib = elemattribute(neightet.tet, j); + setelemattribute(newtet.tet, j, attrib); + } + if (b->varvolume) { + volume = volumebound(neightet.tet); + setvolumebound(newtet.tet, volume); + } + bond(newtet, neightet); + fnext(newtet, neineitet); + bond(neineitet, *parytet); + // Comment: We removed two hull faces, and added two "new hull + // faces", hence hullsize remains unchanged. + // Add a cavity boundary face. + cavetetlist->newindex((void **) &parytet1); + *parytet1 = newtet; + // Add a cavity tet. + infect(newtet); + caveoldtetlist->newindex((void **) &parytet1); + *parytet1 = newtet; + tetcount++; + // Add two "new hull faces" into list. + enextself(newtet); + for (j = 0; j < 2; j++) { + fnext(newtet, neineitet); + cavebdrylist->newindex((void **) &parytet1); + *parytet1 = neineitet; + enextself(newtet); + } + } + } else { + // Two hull faces matched. Bond the two adjacent tets. + bond(*parytet, neightet); + hullsize -= 2; + } + } // if (neightet.tet == dummytet) + } // i + cavebdrylist->restart(); + inserthullcount++; + } + + if (!bwflag) return loc; + + // Form the Boywer-Watson cavity. + for (i = 0; i < (int) cavetetlist->objects; i++) { + // Get a cavity boundary face. + parytet = (triface *) fastlookup(cavetetlist, i); + assert(parytet->tet != dummytet); + assert(infected(*parytet)); // The tet is inside the cavity. + enqflag = false; + // Get the adjacent tet. + sym(*parytet, neightet); + if (neightet.tet != dummytet) { + if (!infected(neightet)) { + if (!marktested(neightet)) { + ppt = (point *) &(neightet.tet[4]); + sign = insphere_s(ppt[0], ppt[1], ppt[2], ppt[3], insertpt); + enqflag = (sign < 0.0); + // Avoid redundant insphere tests. + marktest(neightet); + } + } else { + enqflag = true; + } + } + if (enqflag) { // Found a tet in the cavity. + if (!infected(neightet)) { // Avoid to add it multiple times. + // Put other three faces in check list. + neineitet.tet = neightet.tet; + for (j = 0; j < 3; j++) { + neineitet.loc = locpivot[neightet.loc][j]; + cavetetlist->newindex((void **) &parytet1); + *parytet1 = neineitet; + } + infect(neightet); + caveoldtetlist->newindex((void **) &parytet1); + *parytet1 = neightet; + tetcount++; + } + } else { + // Found a boundary face of the cavity. + if (neightet.tet == dummytet) { + // Check for a possible flat tet (see m27.node, use -J option). + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + ori = orient3d(pa, pb, pc, insertpt); + if (ori != 0) { + cavebdrylist->newindex((void **) &parytet1); + *parytet1 = *parytet; + // futureflip = flippush(futureflip, parytet, insertpt); + } + } else { + cavebdrylist->newindex((void **) &parytet1); + *parytet1 = *parytet; + } + } + } // i + + if (b->verbose > 1) { + printf(" Cavity formed: %ld tets, %ld faces.\n", tetcount, + cavebdrylist->objects); + } + + totaldeadtets += tetcount; + totalbowatcavsize += cavebdrylist->objects; + if (maxbowatcavsize < (long) cavebdrylist->objects) { + maxbowatcavsize = cavebdrylist->objects; + } + + if (checksubsegs || noencsegflag) { + // Check if some (sub)segments are inside the cavity. + for (i = 0; i < (int) caveoldtetlist->objects; i++) { + parytet = (triface *) fastlookup(caveoldtetlist, i); + for (j = 0; j < 6; j++) { + parytet->loc = edge2locver[j][0]; + parytet->ver = edge2locver[j][1]; + tsspivot1(*parytet, checkseg); + if ((checkseg.sh != dummysh) && !sinfected(checkseg)) { + // Check if this segment is inside the cavity. + spintet = *parytet; + pa = apex(spintet); + enqflag = true; + hitbdry = 0; + while (1) { + tfnextself(spintet); + if (spintet.tet == dummytet) { + hitbdry++; + if (hitbdry == 2) break; + esym(*parytet, spintet); + tfnextself(spintet); + if (spintet.tet == dummytet) break; + } + if (!infected(spintet)) { + enqflag = false; break; // It is not inside. + } + if (apex(spintet) == pa) break; + } + if (enqflag) { + if (b->verbose > 1) { + printf(" Queue a missing segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + sinfect(checkseg); // Only save it once. + subsegstack->newindex((void **) &paryseg); + *paryseg = checkseg; + } + } + } + } + } + + if (noencsegflag && (subsegstack->objects > 0)) { + // Found encroached subsegments! Do not insert this point. + for (i = 0; i < (int) caveoldtetlist->objects; i++) { + parytet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*parytet); + unmarktest(*parytet); + } + // Unmark cavity neighbor tets (outside the cavity). + for (i = 0; i < (int) cavebdrylist->objects; i++) { + parytet = (triface *) fastlookup(cavebdrylist, i); + sym(*parytet, neightet); + if (neightet.tet != dummytet) { + unmarktest(neightet); + } + } + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + return ENCSEGMENT; + } + + if (checksubfaces || noencsubflag) { + // Check if some subfaces are inside the cavity. + for (i = 0; i < (int) caveoldtetlist->objects; i++) { + parytet = (triface *) fastlookup(caveoldtetlist, i); + neightet.tet = parytet->tet; + for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { + tspivot(neightet, checksh); + if (checksh.sh != dummysh) { + sym(neightet, neineitet); + // Do not check it if it is a hull tet. + if (neineitet.tet != dummytet) { + if (infected(neineitet)) { + if (b->verbose > 1) { + printf(" Queue a missing subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + tsdissolve(neineitet); // Disconnect a tet-sub bond. + stdissolve(checksh); // Disconnect the sub-tet bond. + sesymself(checksh); + stdissolve(checksh); + // Add the missing subface into list. + subfacstack->newindex((void **) &pssub); + *pssub = checksh; + } + } + } + } + } + } + + if (noencsubflag && (subfacstack->objects > 0)) { + // Found encroached subfaces! Do not insert this point. + /*for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); // Unmark it. + } + if (bwflag && (futureflip != NULL)) { + flippool->restart(); + futureflip = NULL; + } + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + return ENCFACE; + */ + } + + if (visflag) { + // If T is not a Delaunay triangulation, the formed cavity may not be + // star-shaped (fig/dump-cavity-case8). Validation is needed. + cavetetlist->restart(); // Re-use it. + for (i = 0; i < (int) cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + if (infected(*cavetet)) { + sym(*cavetet, neightet); + if (neightet.tet == dummytet || !infected(neightet)) { + if (neightet.tet != dummytet) { + cavetet->ver = 4; // CCW edge ring. + pa = dest(*cavetet); + pb = org(*cavetet); + pc = apex(*cavetet); + ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; + assert(ori != 0.0); // SELF_CHECK + enqflag = (ori > 0.0); + } else { + enqflag = true; // A hull face. + } + if (enqflag) { + // This face is valid, save it. + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + if (b->verbose > 1) { + printf(" Cut tet (%d, %d, %d, %d)\n", pointmark(pb), + pointmark(pa), pointmark(pc), pointmark(oppo(*cavetet))); + } + uninfect(*cavetet); + unmarktest(*cavetet); + if (neightet.tet != dummytet) { + unmarktest(neightet); + } + updatecount++; + // Add three new faces to find new boundaries. + for (j = 0; j < 3; j++) { + fnext(*cavetet, neineitet); + sym(neineitet, neightet); + if (neightet.tet != dummytet) { + if (infected(neightet)) { + neightet.ver = 4; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + } else { + unmarktest(neightet); + } + } + enextself(*cavetet); + } + } + } else { + // This face is not on the cavity boundary anymore. + unmarktest(*cavetet); + } + } else { + assert(!marktested(*cavetet)); + } + } + if (updatecount > 0) { + // Update the cavity boundary faces (fig/dump-cavity-case9). + cavebdrylist->restart(); + for (i = 0; i < (int) cavetetlist->objects; i++) { + cavetet = (triface *) fastlookup(cavetetlist, i); + // 'cavetet' was boundary face of the cavity. + if (infected(*cavetet)) { + sym(*cavetet, neightet); + if ((neightet.tet != dummytet) || !infected(neightet)) { + // It is a cavity boundary face. + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + // Not a cavity boundary face. + unmarktest(*cavetet); + } + } else { + assert(!marktested(*cavetet)); + } + } + // Update the list of old tets. + cavetetlist->restart(); + for (i = 0; i < (int) caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if (infected(*cavetet)) { + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } + assert((int) cavetetlist->objects < i); + // Swap 'cavetetlist' and 'caveoldtetlist'. + swaplist = caveoldtetlist; + caveoldtetlist = cavetetlist; + cavetetlist = swaplist; + if (b->verbose > 1) { + printf(" Size of the updated cavity: %d faces %d tets.\n", + (int) cavebdrylist->objects, (int) caveoldtetlist->objects); + } + } + } + + // Re-use this list for new cavity faces. + cavetetlist->restart(); + + // Create new tetrahedra in the Bowyer-Watson cavity and Connect them. + for (i = 0; i < (int) cavebdrylist->objects; i++) { + parytet = (triface *) fastlookup(cavebdrylist, i); + assert(infected(*parytet)); // The tet is inside the cavity. + parytet->ver = 0; // In CCW edge ring. + maketetrahedron(&newtet); + setorg (newtet, org(*parytet)); + setdest(newtet, dest(*parytet)); + setapex(newtet, apex(*parytet)); + setoppo(newtet, insertpt); + for (j = 0; j < in->numberoftetrahedronattributes; j++) { + attrib = elemattribute(parytet->tet, j); + setelemattribute(newtet.tet, j, attrib); + } + if (b->varvolume) { + volume = volumebound(parytet->tet); + setvolumebound(newtet.tet, volume); + } + // Bond the new tet to the adjacent tet outside the cavity. + sym(*parytet, neightet); + if (neightet.tet != dummytet) { + // The tet was marked (to avoid redundant insphere tests). + unmarktest(neightet); + bond(newtet, neightet); + } else { + // Bond newtet to dummytet. + dummytet[0] = encode(newtet); + } + // mark the other three faces of this tet as "open". + neightet.tet = newtet.tet; + for (j = 0; j < 3; j++) { + neightet.tet[locpivot[0][j]] = NULL; + } + // Let the oldtet knows newtet (for connecting adjacent new tets). + parytet->tet[parytet->loc] = encode(newtet); + if (checksubsegs) { + // newtet and parytet share at the same edge. + for (j = 0; j < 3; j++) { + tsspivot1(*parytet, checkseg); + if (checkseg.sh != dummysh) { + if (sinfected(checkseg)) { + // This subsegment is not missing. Unmark it. + if (b->verbose > 1) { + printf(" Dequeue a segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + suninfect(checkseg); // Dequeue a non-missing segment. + } + tssbond1(newtet, checkseg); + } + enextself(*parytet); + enextself(newtet); + } + } + if (checksubfaces) { + // Bond subface to the new tet. + tspivot(*parytet, checksh); + if (checksh.sh != dummysh) { + tsbond(newtet, checksh); + // The other-side-connection of checksh should be no change. + } + } + } // i + + // Set a handle for speeding point location. + recenttet = newtet; + setpoint2tet(insertpt, encode(newtet)); + + // Connect adjacent new tetrahedra together. Here we utilize the connections + // of the old cavity tets to find the new adjacent tets. + for (i = 0; i < (int) cavebdrylist->objects; i++) { + parytet = (triface *) fastlookup(cavebdrylist, i); + decode(parytet->tet[parytet->loc], newtet); + // assert(org(newtet) == org(*parytet)); // SELF_CHECK + // assert((newtet.ver & 1) == 0); // in CCW edge ring. + for (j = 0; j < 3; j++) { + fnext(newtet, neightet); // Go to the "open" face. + if (neightet.tet[neightet.loc] == NULL) { + spintet = *parytet; + while (1) { + fnextself(spintet); + symedgeself(spintet); + if (spintet.tet == dummytet) break; + if (!infected(spintet)) break; + } + if (spintet.tet != dummytet) { + // 'spintet' is the adjacent tet of the cavity. + fnext(spintet, neineitet); + assert(neineitet.tet[neineitet.loc] == NULL); // SELF_CHECK + bond(neightet, neineitet); + } else { + // This side is a hull face. + neightet.tet[neightet.loc] = (tetrahedron) dummytet; + dummytet[0] = encode(neightet); + } + } + setpoint2tet(org(newtet), encode(newtet)); + enextself(newtet); + enextself(*parytet); + } + } + + // Delete the old cavity tets. + for (i = 0; i < (int) caveoldtetlist->objects; i++) { + parytet = (triface *) fastlookup(caveoldtetlist, i); + tetrahedrondealloc(parytet->tet); + } + + // Set the point type. + if (pointtype(insertpt) == UNUSEDVERTEX) { + setpointtype(insertpt, FREEVOLVERTEX); + } + + if (btreenode_list) { + btree_insert(insertpt); + } + + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +// If the input point set is degenerate, i.e., all points are collinear or // +// are coplanar, then no 3D DT is created and return FALSE. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool 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; + REAL attrib, volume; + int i, j; + + // 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 false; + } 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 false; + } 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) { + return false; + } 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; + } + + if (det > 0.0) { + // For keeping the positive orientation. + swappt = insertarray[0]; + insertarray[0] = insertarray[1]; + insertarray[1] = swappt; + } + + // Create the initial tet. + if (b->verbose > 1) { + printf(" Create the first tet (%d, %d, %d, %d).\n", + pointmark(insertarray[0]), pointmark(insertarray[1]), + pointmark(insertarray[2]), pointmark(lastpt)); + } + + maketetrahedron(&newtet); + 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); + recenttet = newtet; + // Update the point-to-tet map. + setpoint2tet(insertarray[0], encode(newtet)); + setpoint2tet(insertarray[1], encode(newtet)); + setpoint2tet(insertarray[2], encode(newtet)); + setpoint2tet(lastpt, 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 > 1) { + printf(" Incrementally inserting points.\n"); + } + + // Insert the rest of points, one by one. + for (; i < arraysize; i++) { + if (jump) { + searchtet.tet = NULL; + } else { + searchtet = recenttet; + } + loc = insertvertexbw(insertarray[i],&searchtet,true,false,false,false); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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() +{ + point *insertarray; + long arraysize; + bool success; + int i, j; + + if (!b->quiet) { + printf("Constructing Delaunay tetrahedralization.\n"); + } + + if (b->btree) { + btreenode_list = new arraypool(sizeof(point*), 10); + max_btreenode_size = 0; + max_btree_depth = 0; + } + + if (cavetetlist == NULL) { + cavetetlist = new arraypool(sizeof(triface), 10); + cavebdrylist = new arraypool(sizeof(triface), 10); + caveoldtetlist = new arraypool(sizeof(triface), 10); + } + + // Prepare the array of points for inserting. + arraysize = points->items; + insertarray = new point[arraysize]; + + points->traversalinit(); + if (b->btree) { // -u option. + // Use the input order. + for (i = 0; i < arraysize; i++) { + insertarray[i] = pointtraverse(); + } + if (b->verbose) { + printf(" Sorting vertices by a bsp-tree.\n"); + } + // Sort the points using a binary tree recursively. + btree_sort(insertarray, in->numberofpoints, 0, xmin, xmax, ymin, ymax, + zmin, zmax, 0); + if (b->verbose) { + printf(" Number of tree nodes: %ld.\n", btreenode_list->objects); + printf(" Maximum tree node size: %d.\n", max_btreenode_size); + printf(" Maximum tree depth: %d.\n", max_btree_depth); + } + // Order the sorted points. + ordervertices(insertarray, in->numberofpoints); + } else { + if (b->verbose) { + printf(" Permuting vertices.\n"); + } + // Randomize the point order. + for (i = 0; i < arraysize; i++) { + j = (int) randomnation(i + 1); // 0 <= j <= i; + insertarray[i] = insertarray[j]; + insertarray[j] = pointtraverse(); + } + } + + if (b->verbose) { + printf(" Incrementally inserting vertices.\n"); + } + + // Form the DT by incremental flip Delaunay algorithm. + success = incrflipdelaunay(NULL, insertarray, arraysize, true, b->plc, + 0.0, NULL); + + if (b->btree) { + point **pptary; + for (i = 0; i < (int) btreenode_list->objects; i++) { + pptary = (point **) fastlookup(btreenode_list, i); + delete [] *pptary; + } + delete btreenode_list; + btreenode_list = NULL; + } + + delete [] insertarray; + + if (!success) { + return 0l; + } else { + return hullsize; + } +} + +//// //// +//// //// +//// delaunay_cxx ///////////////////////////////////////////////////////////// + +//// surface_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// sinsertvertex() Insert a vertex into a triangulation of a facet. // +// // +// The new point (p) will be located. Searching from 'splitsh'. If 'splitseg'// +// is not NULL, p is on a segment, no search is needed. // +// // +// If 'cflag' is not TRUE, the triangulation may be not convex. Don't insert // +// p if it is found in outside. // +// // +// Comment: This routine assumes the 'abovepoint' of this facet has been set,// +// i.e., the routine getabovepoint() has been executed before it is called. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::sinsertvertex(point insertpt, + face *splitsh, face *splitseg, bool bwflag, bool cflag) +{ + face *abfaces, *parysh, *pssub; + face neighsh, newsh, casout, casin; + face aseg, bseg, aoutseg, boutseg; + face checkseg; + triface neightet; + point pa, pb, pc, *ppt; + enum locateresult loc; + REAL sign, ori, area; + int n, s, i, j; + + if (splitseg != NULL) { + spivot(*splitseg, *splitsh); + loc = ONEDGE; + } else { + // Locate the point, '1' means the flag stop-at-segment is on. + loc = locatesub(insertpt, splitsh, 1, 0); + } + + // Return if p lies on a vertex. + if (loc == ONVERTEX) return loc; + + if (loc == OUTSIDE) { + // Return if 'cflag' is not set. + if (!cflag) return loc; + } + + if (loc == ONEDGE) { + if (splitseg == NULL) { + // Do not split a segment. + sspivot(*splitsh, checkseg); + if (checkseg.sh != dummysh) return loc; // return ONSUBSEG; + // Check if this edge is on the hull. + spivot(*splitsh, neighsh); + if (neighsh.sh == dummysh) { + // A convex hull edge. The new point is on the hull. + loc = OUTSIDE; + } + } + } + + if (b->verbose > 1) { + pa = sorg(*splitsh); + pb = sdest(*splitsh); + pc = sapex(*splitsh); + printf(" Insert point %d (%d, %d, %d) loc %d\n", pointmark(insertpt), + pointmark(pa), pointmark(pb), pointmark(pc), (int) loc); + } + + // Does 'insertpt' lie on a segment? + if (splitseg != NULL) { + splitseg->shver = 0; + pa = sorg(*splitseg); + // Count the number of faces at segment [a, b]. + n = 0; + neighsh = *splitsh; + do { + spivotself(neighsh); + n++; + } while ((neighsh.sh != dummysh) && (neighsh.sh != splitsh->sh)); + // n is at least 1. + abfaces = new face[n]; + // Collect faces at seg [a, b]. + abfaces[0] = *splitsh; + if (sorg(abfaces[0]) != pa) sesymself(abfaces[0]); + for (i = 1; i < n; i++) { + spivot(abfaces[i - 1], abfaces[i]); + if (sorg(abfaces[i]) != pa) sesymself(abfaces[i]); + } + } + + // Initialize the cavity. + if (loc == ONEDGE) { + smarktest(*splitsh); + caveshlist->newindex((void **) &parysh); + *parysh = *splitsh; + if (splitseg != NULL) { + for (i = 1; i < n; i++) { + smarktest(abfaces[i]); + caveshlist->newindex((void **) &parysh); + *parysh = abfaces[i]; + } + } else { + spivot(*splitsh, neighsh); + if (neighsh.sh != dummysh) { + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } + } else if (loc == ONFACE) { + smarktest(*splitsh); + caveshlist->newindex((void **) &parysh); + *parysh = *splitsh; + } else { // loc == OUTSIDE; + // This is only possible when T is convex. + assert(cflag); // SELF_CHECK + // Adjust 'abovepoint' to be above the 'splitsh'. 2009-07-21. + ori = orient3d(sorg(*splitsh), sdest(*splitsh), sapex(*splitsh), + abovepoint); + assert(ori != 0); + if (ori > 0) { + sesymself(*splitsh); + } + // Assume p is on top of the edge ('splitsh'). Find a right-most edge + // which is visible by p. + neighsh = *splitsh; + while (1) { + senext2self(neighsh); + spivot(neighsh, casout); + if (casout.sh == dummysh) { + // A convex hull edge. Is it visible by p. + pa = sorg(neighsh); + pb = sdest(neighsh); + ori = orient3d(pa, pb, abovepoint, insertpt); + if (ori < 0) { + *splitsh = neighsh; // Update 'splitsh'. + } else { + break; // 'splitsh' is the right-most visible edge. + } + } else { + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; + } + } + // Create new triangles for all visible edges of p (from right to left). + casin.sh = dummysh; // No adjacent face at right. + pa = sorg(*splitsh); + pb = sdest(*splitsh); + while (1) { + // Create a new subface on top of the (visible) edge. + makeshellface(subfaces, &newsh); + // setshvertices(newsh, pb, pa, insertpt); + setsorg(newsh, pb); + setsdest(newsh, pa); + setsapex(newsh, insertpt); + setshellmark(newsh, shellmark(*splitsh)); + if (b->quality && varconstraint) { + area = areabound(*splitsh); + setareabound(newsh, area); + } + // Connect the new subface to the bottom subfaces. + sbond1(newsh, *splitsh); + sbond1(*splitsh, newsh); + // Connect the new subface to its right-adjacent subface. + if (casin.sh != dummysh) { + senext(newsh, casout); + sbond1(casout, casin); + sbond1(casin, casout); + } + // The left-adjacent subface has not been created yet. + senext2(newsh, casin); + // Add the new face into list. + smarktest(newsh); + caveshlist->newindex((void **) &parysh); + *parysh = newsh; + // Move to the convex hull edge at the left of 'splitsh'. + neighsh = *splitsh; + while (1) { + senextself(neighsh); + spivot(neighsh, casout); + if (casout.sh == dummysh) { + *splitsh = neighsh; + break; + } + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; + } + // A convex hull edge. Is it visible by p. + pa = sorg(*splitsh); + pb = sdest(*splitsh); + ori = orient3d(pa, pb, abovepoint, insertpt); + if (ori >= 0) break; + } + } + + // Form the Bowyer-Watson cavity. + for (i = 0; i < (int) caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + for (j = 0; j < 3; j++) { + sspivot(*parysh, checkseg); + if (checkseg.sh == dummysh) { + spivot(*parysh, neighsh); + if (neighsh.sh != dummysh) { + if (!smarktested(neighsh)) { + if (bwflag) { + pa = sorg(neighsh); + pb = sdest(neighsh); + pc = sapex(neighsh); + sign = incircle3d(pa, pb, pc, insertpt); + if (sign < 0) { + smarktest(neighsh); + caveshlist->newindex((void **) &pssub); + *pssub = neighsh; + } + } else { + sign = 1; // A boundary edge. + } + } else { + sign = -1; // Not a boundary edge. + } + } else { + if (loc == OUTSIDE) { + // It is a boundary edge if it does not contain insertp. + if ((sorg(*parysh)==insertpt) || (sdest(*parysh)==insertpt)) { + sign = -1; // Not a boundary edge. + } else { + sign = 1; // A boundary edge. + } + } else { + sign = 1; // A boundary edge. + } + } + } else { + sign = 1; // A segment! + } + if (sign >= 0) { + // Add a boundary edge. + caveshbdlist->newindex((void **) &pssub); + *pssub = *parysh; + } + senextself(*parysh); + } + } + + // Creating new subfaces. + for (i = 0; i < (int) caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + sspivot(*parysh, checkseg); + if ((parysh->shver & 01) != 0) sesymself(*parysh); + pa = sorg(*parysh); + pb = sdest(*parysh); + // Create a new subface. + makeshellface(subfaces, &newsh); + // setshvertices(newsh, pa, pb, insertpt); + setsorg(newsh, pa); + setsdest(newsh, pb); + setsapex(newsh, insertpt); + setshellmark(newsh, shellmark(*parysh)); + if (b->quality && varconstraint) { + area = areabound(*parysh); + setareabound(newsh, area); + } + // Connect newsh to outer subfaces. + spivot(*parysh, casout); + if (casout.sh != dummysh) { + if (casout.sh != parysh->sh) { // It is not self-bonded. + casin = casout; + if (checkseg.sh != dummysh) { + spivot(casin, neighsh); + while (neighsh.sh != parysh->sh) { + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(newsh, casout); + sbond1(casin, newsh); + } else { + // This side is empty. + } + } else { + // This is a hull side. Save it in dummysh[0] (it will be used by + // the routine locatesub()). 2009-07-20. + dummysh[0] = sencode(newsh); + } + if (checkseg.sh != dummysh) { + ssbond(newsh, checkseg); + } + // Connect oldsh <== newsh (for connecting adjacent new subfaces). + sbond1(*parysh, newsh); + } + + // Set a handle for searching. + // recentsh = newsh; + + // Connect adjacent new subfaces together. + for (i = 0; i < (int) caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + sspivot(*parysh, checkseg); + spivot(*parysh, newsh); // The new subface [a, b, p]. + senextself(newsh); // At edge [b, p]. + spivot(newsh, neighsh); + if (neighsh.sh == dummysh) { + // Find the adjacent new subface at edge [b, p]. + pb = sdest(*parysh); + neighsh = *parysh; + while (1) { + senextself(neighsh); + spivotself(neighsh); + if (neighsh.sh == dummysh) break; + if (!smarktested(neighsh)) break; + if (sdest(neighsh) != pb) sesymself(neighsh); + } + if (neighsh.sh != dummysh) { + // Now 'neighsh' is a new subface at edge [b, #]. + if (sorg(neighsh) != pb) sesymself(neighsh); + assert(sorg(neighsh) == pb); // SELF_CHECK + assert(sapex(neighsh) == insertpt); // SELF_CHECK + senext2self(neighsh); // Go to the open edge [p, b]. + spivot(neighsh, casout); // SELF_CHECK + assert(casout.sh == dummysh); // SELF_CHECK + sbond(newsh, neighsh); + } else { + assert(loc == OUTSIDE); // SELF_CHECK + // It is a hull edge. 2009-07-21 + dummysh[0] = sencode(newsh); + } + } + spivot(*parysh, newsh); // The new subface [a, b, p]. + senext2self(newsh); // At edge [p, a]. + spivot(newsh, neighsh); + if (neighsh.sh == dummysh) { + // Find the adjacent new subface at edge [p, a]. + pa = sorg(*parysh); + neighsh = *parysh; + while (1) { + senext2self(neighsh); + spivotself(neighsh); + if (neighsh.sh == dummysh) break; + if (!smarktested(neighsh)) break; + if (sorg(neighsh) != pa) sesymself(neighsh); + } + if (neighsh.sh != dummysh) { + // Now 'neighsh' is a new subface at edge [#, a]. + if (sdest(neighsh) != pa) sesymself(neighsh); + assert(sdest(neighsh) == pa); // SELF_CHECK + assert(sapex(neighsh) == insertpt); // SELF_CHECK + senextself(neighsh); // Go to the open edge [a, p]. + spivot(neighsh, casout); // SELF_CHECK + assert(casout.sh == dummysh); // SELF_CHECK + sbond(newsh, neighsh); + } else { + assert(loc == OUTSIDE); // SELF_CHECK + // It is a hull edge. 2009-07-21 + dummysh[0] = sencode(newsh); + } + } + } + + if (splitseg != NULL) { + // Split the segment [a, b]. + aseg = *splitseg; + pa = sorg(aseg); + pb = sdest(aseg); + if (b->verbose > 1) { + printf(" Split seg (%d, %d) by %d.\n", pointmark(pa), pointmark(pb), + pointmark(insertpt)); + } + // Insert the new point p. + makeshellface(subsegs, &bseg); + // setshvertices(bseg, insertpt, pb, NULL); + setsorg(bseg, insertpt); + setsdest(bseg, pb); + setsapex(bseg, NULL); + setsdest(aseg, insertpt); + setshellmark(bseg, shellmark(aseg)); + // This is done outside this routine (at where newpt was created). + // setpoint2sh(insertpt, sencode(aseg)); + if (b->quality && varconstraint) { + setareabound(bseg, areabound(aseg)); + } + // Update the point-to-seg map. + setpoint2seg(pb, sencode(bseg)); + setpoint2seg(insertpt, sencode(bseg)); + // Connect [p, b]<->[b, #]. + senext(aseg, aoutseg); + spivotself(aoutseg); + if (aoutseg.sh != dummysh) { + senext(bseg, boutseg); + sbond(boutseg, aoutseg); + } + // Connect [a, p] <-> [p, b]. + senext(aseg, aoutseg); + senext2(bseg, boutseg); + sbond(aoutseg, boutseg); + // Connect subsegs [a, p] and [p, b] to the true new subfaces. + for (i = 0; i < n; i++) { + spivot(abfaces[i], newsh); // The faked new subface. + if (sorg(newsh) != pa) sesymself(newsh); + senext2(newsh, neighsh); // The edge [p, a] in newsh + spivot(neighsh, casout); + ssbond(casout, aseg); + senext(newsh, neighsh); // The edge [b, p] in newsh + spivot(neighsh, casout); + ssbond(casout, bseg); + } + if (n > 1) { + // Create the two face rings at [a, p] and [p, b]. + for (i = 0; i < n; i++) { + spivot(abfaces[i], newsh); // The faked new subface. + if (sorg(newsh) != pa) sesymself(newsh); + spivot(abfaces[(i + 1) % n], neighsh); // The next faked new subface. + if (sorg(neighsh) != pa) sesymself(neighsh); + senext2(newsh, casout); // The edge [p, a] in newsh. + senext2(neighsh, casin); // The edge [p, a] in neighsh. + spivotself(casout); + spivotself(casin); + sbond1(casout, casin); // Let the i's face point to (i+1)'s face. + senext(newsh, casout); // The edge [b, p] in newsh. + senext(neighsh, casin); // The edge [b, p] in neighsh. + spivotself(casout); + spivotself(casin); + sbond1(casout, casin); + } + } else { + // Only one subface contains this segment. + // assert(n == 1); + spivot(abfaces[0], newsh); // The faked new subface. + if (sorg(newsh) != pa) sesymself(newsh); + senext2(newsh, casout); // The edge [p, a] in newsh. + spivotself(casout); + sdissolve(casout); // Disconnect to faked subface. + senext(newsh, casout); // The edge [b, p] in newsh. + spivotself(casout); + sdissolve(casout); // Disconnect to faked subface. + } + // Delete the faked new subfaces. + for (i = 0; i < n; i++) { + spivot(abfaces[i], newsh); // The faked new subface. + shellfacedealloc(subfaces, newsh.sh); + } + if (checksubsegs) { + // Add two subsegs into stack (for recovery). + if (!sinfected(aseg)) { + s = randomnation(subsegstack->objects + 1); + subsegstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(subsegstack, s); + sinfect(aseg); + parysh = (face *) fastlookup(subsegstack, s); + *parysh = aseg; + } + assert(!sinfected(bseg)); // SELF_CHECK + s = randomnation(subsegstack->objects + 1); + subsegstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(subsegstack, s); + sinfect(bseg); + parysh = (face *) fastlookup(subsegstack, s); + *parysh = bseg; + } + delete [] abfaces; + } + + if (checksubfaces) { + // Add all new subfaces into list. + for (i = 0; i < (int) caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, newsh); // The new subface [a, b, p]. + // Some new subfaces may get deleted (when 'splitseg' is a segment). + if (!isdead(&newsh)) { + if (b->verbose > 1) { + printf(" Queue a new subface (%d, %d, %d).\n", + pointmark(sorg(newsh)), pointmark(sdest(newsh)), + pointmark(sapex(newsh))); + } + subfacstack->newindex((void **) &pssub); + *pssub = newsh; + } + } + } + + // Update the point-to-subface map. + for (i = 0; i < (int) caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, newsh); // The new subface [a, b, p]. + // Some new subfaces may get deleted (when 'splitseg' is a segment). + if (!isdead(&newsh)) { + ppt = (point *) &(newsh.sh[3]); + for (j = 0; j < 3; j++) { + setpoint2sh(ppt[j], sencode(newsh)); + } + } + } + + // Delete the old subfaces. + for (i = 0; i < (int) caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (checksubfaces) { + // Disconnect in the neighbor tets. + for (j = 0; j < 2; j++) { + stpivot(*parysh, neightet); + if (neightet.tet != dummytet) { + tsdissolve(neightet); + // symself(neightet); + // tsdissolve(neightet); + } + sesymself(*parysh); + } + } + shellfacedealloc(subfaces, parysh->sh); + } + + // Clean the working lists. + caveshlist->restart(); + caveshbdlist->restart(); + + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. + point2tetorg(p1, adjtet); + assert(org(adjtet) == p1); + 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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). // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool 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 false; + + // 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). + if ((eps == 0.0) || (epscount > 3)) { + printf("Warning: Discard an invalid facet.\n"); + printf(" #%d (%d, %d, %d", shmark, pointmark(insertarray[0]), + pointmark(insertarray[1]), pointmark(insertarray[2])); + if (ptlist->len() > 3) { + printf(", ..."); + } + printf(") looks like a line.\n"); + // terminatetetgen(1); + return false; + } + // 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); + + // Update the point-to-subface map. + for (i = 0; i < 3; i++) { + setpoint2sh(insertarray[i], 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 = sinsertvertex(insertarray[i], &startsh, NULL, true, true); + // The point-to-subface map has been updated. + // Set p_i's type FREESUBVERTEX if it has no type yet. + if (pointtype(insertarray[i]) == FREEVOLVERTEX) { + setpointtype(insertarray[i], FREESUBVERTEX); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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)); + terminatetetgen(2); + } + 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)); + terminatetetgen(2); + } + 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. + point2shorg(tstart, searchsh); + // 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. + lawson(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); + spivot(hulltri, nexttri); + while (nexttri.sh != dummysh) { + if (sorg(nexttri) != sdest(hulltri)) { + sesymself(nexttri); + } + senext(nexttri, hulltri); + spivot(hulltri, 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; + point *ppt; + int i, j; + + // 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); + // Update the point-to-subface map. 2009-07-21. + ppt = (point *) &(neighbor.sh[3]); + for (j = 0; j < 3; j++) { + setpoint2sh(ppt[j], sencode(neighbor)); + } + } + } + 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. + if (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]; + if (sfacelist->len() > 1) { + for (k = 1; k <= sfacelist->len(); k++) { + if (k < sfacelist->len()) { + sface1 = *(face *)(* sfacelist)[k]; + } else { + sface1 = *(face *)(* sfacelist)[0]; // Form a face loop. + } + // Comment: For detecting invalid PLC, here we could check if the + // two subfaces "sface" and "sface1" are identical (skipped). + 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; + } + } else { + // This segment belongs to only on subface. + sdissolve(sface); + } + // 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// assignsegmentmarkers() Assign markers given in "in->edgemarkerlist". // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::assignsegmentmarkers() +{ + shellface **segsperverlist; + face sseg; + bool isseg; + int *idx2seglist; + int end1, end2, tend1, tend2; + int index, i, j; + + if (b->verbose > 0) { + printf(" Assigning segment markers.\n"); + } + + assert(in->edgemarkerlist != NULL); + makesegmentmap(idx2seglist, segsperverlist); + + for (i = 0; i < in->numberofedges; i++) { + end1 = in->edgelist[i * 2]; + end2 = in->edgelist[i * 2 + 1]; + index = end1 - in->firstnumber; + for (j = idx2seglist[index]; j < idx2seglist[index + 1]; j++) { + sseg.sh = segsperverlist[j]; + sseg.shver = 0; + isseg = false; + tend1 = pointmark(sorg(sseg)); + tend2 = pointmark(sdest(sseg)); + if (tend1 == end1) { + if (tend2 == end2) isseg = true; + } else if (tend1 == end2) { + if (tend2 == end1) isseg = true; + } + if (isseg) { + setshellmark(sseg, in->edgemarkerlist[i]); + break; + } + } + } + + delete [] idx2seglist; + delete [] segsperverlist; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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); + if (parentsh.sh != dummysh) { + // This segment is not dangling. + spivot(parentsh, neighsh); + if (neighsh.sh != dummysh) { + // This segment belongs to at least two facets. + 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); + } + } + } + } + } // neighsh.sh != dummysh + } // parentsh.sh != dummysh + segloop.sh = shellfacetraverse(subsegs); + } + + if (!flipqueue->empty()) { + // Restore the Delaunay property in the facet triangulation. + lawson(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; + + caveshlist = new arraypool(sizeof(face), 10); + caveshbdlist = new arraypool(sizeof(face), 10); + + // 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(); + } + + delete caveshlist; + delete caveshbdlist; + caveshlist = NULL; + caveshbdlist = NULL; + + // Unify segments in 'subsegs', remove redundant segments. Face links + // of segments are also built. + unifysegments(); + /*if (in->numberofedges > 0) { + if (in->edgemarkerlist != NULL) { + assignsegmentmarkers(); + } + }*/ + + // 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); + } + + // Create the point-to-segment map. + makepoint2segmap(); + + delete [] idx2verlist; + delete [] worklist; + delete ptlist; + delete conlist; + delete flipqueue; + delete viri; + + return subsegs->items; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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) { + terminatetetgen(1); + } + rightarray = new shellface*[arraysize]; + if (rightarray == NULL) { + terminatetetgen(1); + } + leftsize = rightsize = 0; + + if (axis == 0) { + // Split along x-axis. + split = 0.5 * (bxmin + bxmax); + } else if (axis == 1) { + // Split along y-axis. + split = 0.5 * (bymin + bymax); + } else { + // Split along z-axis. + split = 0.5 * (bzmin + bzmax); + } + + for (i = 0; i < arraysize; i++) { + sface1.sh = subfacearray[i]; + p1 = (point) sface1.sh[3]; + p2 = (point) sface1.sh[4]; + p3 = (point) sface1.sh[5]; + toleft = toright = false; + if (p1[axis] < split) { + toleft = true; + if (p2[axis] >= split || p3[axis] >= split) { + toright = true; + } + } else if (p1[axis] > split) { + toright = true; + if (p2[axis] <= split || p3[axis] <= split) { + toleft = true; + } + } else { + // p1[axis] == split; + toleft = true; + toright = true; + } + // At least one is true; +#ifdef SELF_CHECK + assert(!(toleft == false && toright == false)); +#endif + if (toleft) { + leftarray[leftsize] = sface1.sh; + leftsize++; + } + if (toright) { + rightarray[rightsize] = sface1.sh; + rightsize++; + } + } + + if (leftsize < arraysize && rightsize < arraysize) { + // Continue to partition the input set. Now 'subfacearray' has been + // split into two sets, it's memory can be freed. 'leftarray' and + // 'rightarray' will be freed in the next recursive (after they're + // partitioned again or performing tests). + delete [] subfacearray; + // Continue to split these two sets. + if (axis == 0) { + interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax, + bzmin, bzmax, internum); + interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax, + bzmin, bzmax, internum); + } else if (axis == 1) { + interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split, + bzmin, bzmax, internum); + interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax, + bzmin, bzmax, internum); + } else { + interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax, + bzmin, split, internum); + interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax, + split, bzmax, internum); + } + } else { + if (b->verbose > 1) { + printf(" Checking intersecting faces.\n"); + } + // Perform a brute-force compare on the set. + for (i = 0; i < arraysize; i++) { + sface1.sh = subfacearray[i]; + p1 = (point) sface1.sh[3]; + p2 = (point) sface1.sh[4]; + p3 = (point) sface1.sh[5]; + for (j = i + 1; j < arraysize; j++) { + sface2.sh = subfacearray[j]; + p4 = (point) sface2.sh[3]; + p5 = (point) sface2.sh[4]; + p6 = (point) sface2.sh[5]; + intersect = 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(); + } +} + +//// //// +//// //// +//// surface_cxx ////////////////////////////////////////////////////////////// + +//// constrained_cxx ////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// finddirection() Find the tet on the path from one point to another. // +// // +// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, // +// 'searchtet' contains a tet on the path, its origin does not change. // +// // +// The return value indicates one of the following cases (let 'searchtet' be // +// abcd, a is the origin of the path): // +// - ACROSSVERT, edge ab is collinear with the path; // +// - ACROSSEDGE, edge bc intersects with the path; // +// - ACROSSFACE, face bcd intersects with the path. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// - BELOWHULL2, the mesh is non-convex and the searching for the path has // +// got stucked at a non-convex boundary face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::interresult tetgenmesh::finddirection2(triface* searchtet, + point endpt) +{ + triface neightet; + point pa, pb, pc, pd, pn; + enum {HMOVE, RMOVE, LMOVE} nextmove; + enum {HCOPLANE, RCOPLANE, LCOPLANE, NCOPLANE} cop; + REAL hori, rori, lori; + REAL dmin, dist; + + assert((searchtet->tet != NULL) && (searchtet->tet != dummytet)); + + // The origin is fixed. + pa = org(*searchtet); + if (searchtet->ver & 01) { + // Switch to the 0th edge ring. + esymself(*searchtet); + enextself(*searchtet); + } + pb = dest(*searchtet); + if (pb == endpt) { + // pa->pb is the search edge. + return INTERVERT; + } + pc = apex(*searchtet); + if (pc == endpt) { + // pa->pc is the search edge. + enext2self(*searchtet); + esymself(*searchtet); + return INTERVERT; + } + + // Walk through tets at pa until the right one is found. + while (1) { + + pd = oppo(*searchtet); + + if (b->verbose > 2) { + printf(" From tet (%d, %d, %d, %d) to %d.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(endpt)); + } + + // Check whether the opposite vertex is 'endpt'. + if (pd == endpt) { + // pa->pd is the search edge. + fnextself(*searchtet); + enext2self(*searchtet); + esymself(*searchtet); + return INTERVERT; + } + + // Now assume that the base face abc coincides with the horizon plane, + // and d lies above the horizon. The search point 'endpt' may lie + // above or below the horizon. We test the orientations of 'endpt' + // with respect to three planes: abc (horizon), bad (right plane), + // and acd (left plane). + hori = orient3d(pa, pb, pc, endpt); + rori = orient3d(pb, pa, pd, endpt); + lori = orient3d(pa, pc, pd, endpt); + orient3dcount += 3; + + // Now decide the tet to move. It is possible there are more than one + // tet are viable moves. Use the opposite points of thier neighbors + // to discriminate, i.e., we choose the tet whose opposite point has + // the shortest distance to 'endpt'. + if (hori > 0) { + if (rori > 0) { + if (lori > 0) { + // Any of the three neighbors is a viable move. + nextmove = HMOVE; + sym(*searchtet, neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = RMOVE; + dmin = dist; + } + enext2fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = LMOVE; + dmin = dist; + } + } else { + // Two tets, below horizon and below right, are viable. + nextmove = HMOVE; + sym(*searchtet, neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = RMOVE; + dmin = dist; + } + } + } else { + if (lori > 0) { + // Two tets, below horizon and below left, are viable. + nextmove = HMOVE; + sym(*searchtet, neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext2fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = LMOVE; + dmin = dist; + } + } else { + // The tet below horizon is chosen. + nextmove = HMOVE; + } + } + } else { + if (rori > 0) { + if (lori > 0) { + // Two tets, below right and below left, are viable. + nextmove = RMOVE; + fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + enext2fnext(*searchtet, neightet); + symself(neightet); + if (neightet.tet != dummytet) { + pn = oppo(neightet); + dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]); + } else { + dist = dmin; + } + if (dist < dmin) { + nextmove = LMOVE; + dmin = dist; + } + } else { + // The tet below right is chosen. + nextmove = RMOVE; + } + } else { + if (lori > 0) { + // The tet below left is chosen. + nextmove = LMOVE; + } else { + // 'endpt' lies either on the plane(s) or across face bcd. + if (hori == 0) { + if (rori == 0) { + // pa->'endpt' is COLLINEAR with pa->pb. + return INTERVERT; + } + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pc. + enext2self(*searchtet); + esymself(*searchtet); + return INTERVERT; + } + // pa->'endpt' crosses the edge pb->pc. + // enextself(*searchtet); + // return INTEREDGE; + cop = HCOPLANE; + break; + } + if (rori == 0) { + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pd. + fnextself(*searchtet); // face abd. + enext2self(*searchtet); + esymself(*searchtet); + return INTERVERT; + } + // pa->'endpt' crosses the edge pb->pd. + // fnextself(*searchtet); // face abd. + // enextself(*searchtet); + // return INTEREDGE; + cop = RCOPLANE; + break; + } + if (lori == 0) { + // pa->'endpt' crosses the edge pc->pd. + // enext2fnextself(*searchtet); // face cad + // enext2self(*searchtet); + // return INTEREDGE; + cop = LCOPLANE; + break; + } + // pa->'endpt' crosses the face bcd. + // enextfnextself(*searchtet); + // return INTERFACE; + cop = NCOPLANE; + break; + } + } + } + + // Move to the next tet, fix pa as its origin. + if (nextmove == RMOVE) { + tfnextself(*searchtet); + } else if (nextmove == LMOVE) { + enext2self(*searchtet); + tfnextself(*searchtet); + enextself(*searchtet); + } else { // HMOVE + symedgeself(*searchtet); + enextself(*searchtet); + } + // Assume convex case, we should not move to outside. + if (searchtet->tet == dummytet) { + // This should only happen when the domain is non-convex. + return BELOWHULL2; + } + assert(org(*searchtet) == pa); // SELF_CHECK + pb = dest(*searchtet); + pc = apex(*searchtet); + + } // while (1) + + // Either case INTEREDGE or INTERFACE. + /*if (b->epsilon > 0) { + // Use tolerance to re-evaluate the orientations. + if (cop != HCOPLANE) { + if (iscoplanar(pa, pb, pc, endpt, hori)) hori = 0; + } + if (cop != RCOPLANE) { + if (iscoplanar(pb, pa, pd, endpt, rori)) rori = 0; + } + if (cop != LCOPLANE) { + if (iscoplanar(pa, pc, pd, endpt, lori)) lori = 0; + } + // It is not possible that all orientations are zero. + assert(!((hori == 0) && (rori == 0) && (lori == 0))); // SELF_CHECK + }*/ + + // Now decide the degenerate cases. + if (hori == 0) { + if (rori == 0) { + // pa->'endpt' is COLLINEAR with pa->pb. + return INTERVERT; + } + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pc. + enext2self(*searchtet); + esymself(*searchtet); + return INTERVERT; + } + // pa->'endpt' crosses the edge pb->pc. + return INTEREDGE; + } + if (rori == 0) { + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pd. + fnextself(*searchtet); // face abd. + enext2self(*searchtet); + esymself(*searchtet); + return INTERVERT; + } + // pa->'endpt' crosses the edge pb->pd. + fnextself(*searchtet); // face abd. + esymself(*searchtet); + enextself(*searchtet); + return INTEREDGE; + } + if (lori == 0) { + // pa->'endpt' crosses the edge pc->pd. + enext2fnextself(*searchtet); // face cad + esymself(*searchtet); + return INTEREDGE; + } + // pa->'endpt' crosses the face bcd. + return INTERFACE; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// finddirection3() Used when finddirection2() returns BELOWHULL2. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::interresult tetgenmesh::finddirection3(triface* searchtet, + point endpt) +{ + arraypool *startetlist; + triface *parytet, oppoface, neightet; + point startpt, pa, pb, pc; + enum interresult dir; + int types[2], poss[4]; + int pos, i, j; + + startetlist = new arraypool(sizeof(triface), 8); + startpt = org(*searchtet); + infect(*searchtet); + startetlist->newindex((void **) &parytet); + *parytet = *searchtet; + + if (b->verbose > 1) { + printf(" Search path (%d, %d) under non-convexity.\n", + pointmark(startpt), pointmark(endpt)); + } + + for (i = 0; i < (int) startetlist->objects; i++) { + parytet = (triface *) fastlookup(startetlist, i); + *searchtet = *parytet; + // assert(org(*searchtet) == startpt); + adjustedgering(*searchtet, CCW); + if (org(*searchtet) != startpt) { + enextself(*searchtet); + assert(org(*searchtet) == startpt); + } + // Go to the opposite face of startpt. + enextfnext(*searchtet, oppoface); + esymself(oppoface); + pa = org(oppoface); + pb = dest(oppoface); + pc = apex(oppoface); + // Check if face [a, b, c] intersects the searching path. + if (tri_edge_test(pa, pb, pc, startpt, endpt, NULL, 1, types, poss)) { + // They intersect. Get the type of intersection. + dir = (enum interresult) types[0]; + pos = poss[0]; + break; + } else { + dir = DISJOINT; + } + // Get the neighbor tets. + for (j = 0; j < 3; j++) { + if (j == 0) { + symedge(*searchtet, neightet); + } else if (j == 1) { + fnext(*searchtet, neightet); + symedgeself(neightet); + } else { + enext2fnext(*searchtet, neightet); + symedgeself(neightet); + } + if (neightet.tet != dummytet) { + if (!infected(neightet)) { + if (org(neightet) != startpt) esymself(neightet); + infect(neightet); + startetlist->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + } + + for (i = 0; i < (int) startetlist->objects; i++) { + parytet = (triface *) fastlookup(startetlist, i); + uninfect(*parytet); + } + delete startetlist; + + if (dir == INTERVERT) { + // This path passing a vertex of the face [a, b, c]. + if (pos == 0) { + // The path acrosses pa. + enext2self(*searchtet); + esymself(*searchtet); + } else if (pos == 1) { + // The path acrosses pa. + } else { // pos == 2 + // The path acrosses pc. + fnextself(*searchtet); + enext2self(*searchtet); + esymself(*searchtet); + } + return INTERVERT; + } + if (dir == INTEREDGE) { + // This path passing an edge of the face [a, b, c]. + if (pos == 0) { + // The path intersects [pa, pb]. + } else if (pos == 1) { + // The path intersects [pb, pc]. + fnextself(*searchtet); + enext2self(*searchtet); + esymself(*searchtet); + } else { // pos == 2 + // The path intersects [pc, pa]. + enext2fnextself(*searchtet); + esymself(*searchtet); + } + return INTEREDGE; + } + if (dir == INTERFACE) { + return INTERFACE; + } + + // The path does not intersect any tet at pa. + return BELOWHULL2; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scoutsegment() Look for a given segment in the tetrahedralization T. // +// // +// Search an edge in the tetrahedralization that matches the given segmment. // +// If such an edge exists, the segment is 'locked' at the edge. 'searchtet' // +// returns this (constrained) edge. Otherwise, the segment is missing. // +// // +// The returned value indicates one of the following cases: // +// - SHAREEDGE, the segment exists and is inserted in T; // +// - INTERVERT, the segment intersects a vertex ('refpt'). // +// - INTEREDGE, the segment intersects an edge (in 'searchtet'). // +// - INTERFACE, the segment crosses a face (in 'searchtet'). // +// // +// If the returned value is INTEREDGE or INTERFACE, i.e., the segment is // +// missing, 'refpt' returns the reference point for splitting thus segment, // +// 'searchtet' returns a tet containing the 'refpt'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg, + triface* searchtet, point* refpt) +{ + triface neightet, reftet; + face splitsh, checkseg; + point startpt, endpt; + point pa, pb, pc, pd; + enum interresult dir; + REAL angmax, ang; + long facecount; + int hitbdry; + int types[2], poss[4]; + int pos, i; + + // Is 'searchtet' a valid handle? + if ((searchtet->tet == NULL) || (searchtet->tet == dummytet)) { + startpt = sorg(*sseg); + point2tetorg(startpt, *searchtet); + } else { + startpt = sorg(*sseg); + } + assert(org(*searchtet) == startpt); // SELF_CHECK + endpt = sdest(*sseg); + + if (b->verbose > 1) { + printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt)); + } + + dir = finddirection2(searchtet, endpt); + + if (dir == INTERVERT) { + pd = dest(*searchtet); + if (pd == endpt) { + // Found! Insert the segment. + tsspivot1(*searchtet, checkseg); // SELF_CHECK + if (checkseg.sh == dummysh) { + neightet = *searchtet; + hitbdry = 0; + do { + tssbond1(neightet, *sseg); + tfnextself(neightet); + if (neightet.tet == dummytet) { + hitbdry++; + if (hitbdry == 2) break; + esym(*searchtet, neightet); + tfnextself(neightet); + if (neightet.tet == dummytet) break; + } + } while (neightet.tet != searchtet->tet); + } else { + // Collision! This can happy during facet recovery. + // See fig/dump-cavity-case19, -case20. + assert(checkseg.sh == sseg->sh); // SELF_CHECK + } + // The job is done. + return SHAREEDGE; + } else { + // A point is on the path. + *refpt = pd; + return INTERVERT; + } + } + + if (b->verbose > 1) { + printf(" Scout ref point of seg (%d, %d).\n", pointmark(startpt), + pointmark(endpt)); + } + facecount = across_face_count; + + enextfnextself(*searchtet); // Go to the opposite face. + symedgeself(*searchtet); // Enter the adjacent tet. + + pa = org(*searchtet); + angmax = interiorangle(pa, startpt, endpt, NULL); + *refpt = pa; + pb = dest(*searchtet); + ang = interiorangle(pb, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pb; + } + + // Check whether two segments are intersecting. + if (dir == INTEREDGE) { + tsspivot1(*searchtet, checkseg); + if (checkseg.sh != dummysh) { + printf("Error: Invalid PLC. Two segments intersect.\n"); + startpt = getsubsegfarorg(sseg); + endpt = getsubsegfardest(sseg); + pa = getsubsegfarorg(&checkseg); + pb = getsubsegfardest(&checkseg); + printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(startpt), + pointmark(endpt), pointmark(pa), pointmark(pb)); + terminatetetgen(3); + } + across_edge_count++; + } + + pc = apex(*searchtet); + ang = interiorangle(pc, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pc; + } + reftet = *searchtet; // Save the tet containing the refpt. + + // Search intersecting faces along the segment. + while (1) { + + pd = oppo(*searchtet); + + if (b->verbose > 2) { + printf(" Passing face (%d, %d, %d, %d), dir(%d).\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), (int) dir); + } + across_face_count++; + + // Stop if we meet 'endpt'. + if (pd == endpt) break; + + ang = interiorangle(pd, startpt, endpt, NULL); + if (ang > angmax) { + angmax = ang; + *refpt = pd; + reftet = *searchtet; + } + + // Find a face intersecting the segment. + if (dir == INTERFACE) { + // One of the three oppo faces in 'searchtet' intersects the segment. + neightet.tet = searchtet->tet; + neightet.ver = 0; + for (i = 0; i < 3; i++) { + neightet.loc = locpivot[searchtet->loc][i]; + pa = org(neightet); + pb = dest(neightet); + pc = apex(neightet); + pd = oppo(neightet); // The above point. + if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) { + dir = (enum interresult) types[0]; + pos = poss[0]; + break; + } else { + dir = DISJOINT; + pos = 0; + } + } + assert(dir != DISJOINT); // SELF_CHECK + } else { // dir == ACROSSEDGE + // Check the two opposite faces (of the edge) in 'searchtet'. + neightet = *searchtet; + neightet.ver = 0; + for (i = 0; i < 2; i++) { + neightet.loc = locverpivot[searchtet->loc][searchtet->ver][i]; + pa = org(neightet); + pb = dest(neightet); + pc = apex(neightet); + pd = oppo(neightet); // The above point. + if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) { + dir = (enum interresult) types[0]; + pos = poss[0]; + break; + } else { + dir = DISJOINT; + pos = 0; + } + } + if (dir == DISJOINT) { + // No intersection. Go to the next tet. + dir = INTEREDGE; + tfnextself(*searchtet); + continue; + } + } + + if (dir == INTERVERT) { + // This segment passing a vertex. Choose it and return. + for (i = 0; i < pos; i++) { + enextself(neightet); + } + pd = org(neightet); + if (b->verbose > 2) { + angmax = interiorangle(pd, startpt, endpt, NULL); + } + *refpt = pd; + break; + } + if (dir == INTEREDGE) { + // Get the edge intersects with the segment. + for (i = 0; i < pos; i++) { + enextself(neightet); + } + } + // Go to the next tet. + symedge(neightet, *searchtet); + + if (dir == INTEREDGE) { + // Check whether two segments are intersecting. + tsspivot1(*searchtet, checkseg); + if (checkseg.sh != dummysh) { + printf("Error: Invalid PLC! Two segments intersect.\n"); + startpt = getsubsegfarorg(sseg); + endpt = getsubsegfardest(sseg); + pa = getsubsegfarorg(&checkseg); + pb = getsubsegfardest(&checkseg); + printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(startpt), + pointmark(endpt), pointmark(pa), pointmark(pb)); + terminatetetgen(3); + } + across_edge_count++; + } + + } // while (1) + + // dir is either ACROSSVERT, or ACROSSEDGE, or ACROSSFACE. + if (b->verbose > 2) { + printf(" Refpt %d (%g), visited %ld faces.\n", pointmark(*refpt), + angmax / PI * 180.0, across_face_count - facecount); + } + if (across_face_count - facecount > across_max_count) { + across_max_count = across_face_count - facecount; + } + + *searchtet = reftet; + return dir; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getsegmentsplitpoint() Calculate a split point in the given segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt) +{ + point ei, ej, ek; + REAL split, L, d, d1, d2, d3; + int stype, sign; + int i; + + // Decide the type of this segment. + sign = 1; + ei = sorg(*sseg); + ej = sdest(*sseg); + + if (pointtype(ei) == ACUTEVERTEX) { + if (pointtype(ej) == ACUTEVERTEX) { + // Both ei and ej are ACUTEVERTEX. + stype = 0; + } else { + // ej is either a NACUTEVERTEX or a STEINERVERTEX. + stype = 1; + } + } else { + if (pointtype(ei) == NACUTEVERTEX) { + if (pointtype(ej) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + if (pointtype(ej) == NACUTEVERTEX) { + // Both ei and ej are non-acute. + stype = 0; + } else { + // ej is a STEINERVETEX. + ek = getsubsegfardest(sseg); + if (pointtype(ek) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + stype = 0; + } + } + } + } else { + // ei is a STEINERVERTEX. + if (pointtype(ej) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + ek = getsubsegfarorg(sseg); + if (pointtype(ej) == NACUTEVERTEX) { + if (pointtype(ek) == ACUTEVERTEX) { + stype = 1; + } else { + stype = 0; + } + } else { + // Both ei and ej are STEINERVETEXs. ei has priority. + if (pointtype(ek) == ACUTEVERTEX) { + stype = 1; + } else { + ek = getsubsegfardest(sseg); + if (pointtype(ek) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + stype = 0; + } + } + } + } + } + } + + // Adjust the endpoints: ei, ej. + if (sign == -1) { + sesymself(*sseg); + ei = sorg(*sseg); + ej = sdest(*sseg); + } + + if (b->verbose > 1) { + printf(" Split a type-%d seg(%d, %d) ref(%d)", stype, + pointmark(ei), pointmark(ej), pointmark(refpt)); + if (stype) { + ek = getsubsegfarorg(sseg); + printf(" ek(%d)", pointmark(ek)); + } + printf(".\n"); + } + + // Calculate the split point. + if (stype == 0) { + // Use rule-1. + L = DIST(ei, ej); + d1 = DIST(ei, refpt); + d2 = DIST(ej, refpt); + if (d1 < d2) { + // Choose ei as center. + if (d1 < 0.5 * L) { + split = d1 / L; + // Adjust split if it is close to middle. (2009-02-01) + if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ei[i] + split * (ej[i] - ei[i]); + } + } else { + // Choose ej as center. + if (d2 < 0.5 * L) { + split = d2 / L; + // Adjust split if it is close to middle. (2009-02-01) + if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ej[i] + split * (ei[i] - ej[i]); + } + } + r1count++; + } else { + // Use rule-2. + ek = getsubsegfarorg(sseg); + L = DIST(ek, ej); + d = DIST(ek, refpt); + split = d / L; + for (i = 0; i < 3; i++) { + vt[i] = ek[i] + split * (ej[i] - ek[i]); + } + d1 = DIST(vt, refpt); + d2 = DIST(vt, ej); + if (d1 > d2) { + // Use rule-3. + d3 = DIST(ei, refpt); + if (d1 < 0.5 * d3) { + split = (d - d1) / L; + } else { + split = (d - 0.5 * d3) / L; + } + for (i = 0; i < 3; i++) { + vt[i] = ek[i] + split * (ej[i] - ek[i]); + } + } + d1 > d2 ? r3count++ : r2count++; + } + + if (b->verbose > 1) { + printf(" split (%g), vt (%g, %g, %g).\n", split, vt[0], vt[1], vt[2]); + } +} + +void tetgenmesh::getsegmentsplitpoint3(face* seg, point refpt, REAL* steinpt) +{ + point ei, ej; + REAL Li, Lj, L; + REAL t; + int i; + + ei = sorg(*seg); + ej = sdest(*seg); + + if (b->verbose > 1) { + printf(" Get Steiner point on seg (%d, %d).\n", pointmark(ei), + pointmark(ej)); + } + + if (refpt != NULL) { + // Let ei be the closer one to refpt. + Li = distance(ei, refpt); + Lj = distance(ej, refpt); + if (Li > Lj) { + // Swap ei and ej; + sesymself(*seg); + ei = sorg(*seg); + ej = sdest(*seg); + L = Li; + Li = Lj; + Lj = L; + } + if (pointtype(ei) == ACUTEVERTEX) { + // Cut the segment by a sphere centered at ei with radius Li. + L = distance(ei, ej); + t = Li / L; // t \in (0, 1). + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + t * (ej[i] - ei[i]); + } + // Re-use Li and Lj; + Li = distance(steinpt, refpt); + Lj = distance(steinpt, ej); + if (Li > Lj) { + // Avoid to create a very short edge at ej. + t = 0.5; + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + t * (ej[i] - ei[i]); + } + r3count++; + } else { + r2count++; + } + } else { + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); + // Only for report. + L = distance(ei, ej); + Li = distance(steinpt, ei); + t = Li / L; + r1count++; + } + } else { + // Split the point at the middle. + t = 0.5; + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + t * (ej[i] - ei[i]); + } + r1count++; + } // if (refpt == NULL) + + if (pointtype(steinpt) == UNUSEDVERTEX) { + setpointtype(steinpt, FREESEGVERTEX); + } + + if (b->verbose > 2) { + printf(" Split at t(%g).\n", t); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// delaunizesegments() Recover segments in a Delaunay tetrahedralization. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::delaunizesegments2() +{ + triface searchtet; + face splitsh; + face *psseg, sseg; // *parysh; + point refpt, newpt; + enum interresult dir; + bool visflag; + + if (b->verbose) { + printf(" Delaunizing segments.\n"); + } + + // Loop until 'subsegstack' is empty. + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + psseg = (face *) fastlookup(subsegstack, subsegstack->objects); + sseg = *psseg; + + if (!sinfected(sseg)) continue; // Not a missing segment. + suninfect(sseg); + + // Insert the segment. + searchtet.tet = NULL; + dir = scoutsegment2(&sseg, &searchtet, &refpt); + + if (dir != SHAREEDGE) { + // The segment is missing, split it. + spivot(sseg, splitsh); + if (dir != INTERVERT) { + // Create the new point. + makepoint(&newpt); + getsegmentsplitpoint3(&sseg, refpt, newpt); + setpointtype(newpt, FREESEGVERTEX); + setpoint2sh(newpt, sencode(sseg)); + // Split the segment by newpt. + sinsertvertex(newpt, &splitsh, &sseg, true, false); + // Insert newpt into the DT. If 'checksubfaces == 1' the current + // mesh is constrained Delaunay (but may not Delaunay). + visflag = (checksubfaces == 1); + insertvertexbw(newpt, &searchtet, true, visflag, false, false); + } else { + /*if (getpointtype(refpt) != ACUTEVERTEX) { + setpointtype(refpt, RIDGEVERTEX); + } + // Split the segment by refpt. + sinsertvertex(refpt, &splitsh, &sseg, true, false);*/ + printf("Error: Invalid PLC! A point and a segment intersect.\n"); + point pa, pb; + pa = getsubsegfarorg(&sseg); + pb = getsubsegfardest(&sseg); + printf(" Point: %d. Segment: (%d, %d).\n", pointmark(refpt), + pointmark(pa), pointmark(pb)); + terminatetetgen(3); + } + } + } + + if (b->verbose) { + printf(" %ld protecting points.\n", r1count + r2count + r3count); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scoutsubface() Look for a given subface in the tetrahedralization T. // +// // +// 'ssub' is the subface, denoted as abc. If abc exists in T, it is 'locked' // +// at the place where the two tets sharing at it. // +// // +// 'convexflag' indicates the current mesh is convex (1) or non-convex (0). // +// // +// The returned value indicates one of the following cases: // +// - SHAREFACE, abc exists and is inserted; // +// - TOUCHEDGE, a vertex (the origin of 'searchtet') lies on ab. // +// - EDGETRIINT, all three edges of abc are missing. // +// - ACROSSTET, a tet (in 'searchtet') crosses the facet containg abc. // +// // +// If the retunred value is ACROSSTET, the subface is missing. 'searchtet' // +// returns a tet which shares the same edge as 'pssub'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::interresult tetgenmesh::scoutsubface(face* pssub, + triface* searchtet, int convexflag) +{ + triface spintet; + face checksh; + point pa, pb, pc, pd; + enum interresult dir; + int hitbdry; + int i; + + if ((searchtet->tet == NULL) || (searchtet->tet == dummytet)) { + // Search an edge of 'ssub' in tetrahedralization. + pssub->shver = 0; + for (i = 0; i < 3; i++) { + pa = sorg(*pssub); + pb = sdest(*pssub); + // Get a tet whose origin is pa. + point2tetorg(pa, *searchtet); + // Search the edge from pa->pb. + dir = finddirection2(searchtet, pb); + if (dir == INTERVERT) { + if (dest(*searchtet) == pb) { + // Found the edge. Break the loop. + break; + } else { + // A vertex lies on the search edge. Return it. + enextself(*searchtet); + return TOUCHEDGE; + } + } else if (dir == BELOWHULL2) { + if (convexflag > 0) { + assert(0); + } + // The domain is non-convex, and we got stucked at a boundary face. + point2tetorg(pa, *searchtet); + dir = finddirection3(searchtet, pb); + if (dir == INTERVERT) { + if (dest(*searchtet) == pb) { + // Found the edge. Break the loop. + break; + } else { + // A vertex lies on the search edge. Return it. + enextself(*searchtet); + return TOUCHEDGE; + } + } + } + senextself(*pssub); + } + if (i == 3) { + // None of the three edges exists. + return EDGETRIINT; // ab intersects the face in 'searchtet'. + } + } else { + // 'searchtet' holds the current edge of 'pssub'. + pa = org(*searchtet); + pb = dest(*searchtet); + } + + pc = sapex(*pssub); + + if (b->verbose > 1) { + printf(" Scout subface (%d, %d, %d) (%ld).\n", pointmark(pa), + pointmark(pb), pointmark(pc), subfacstack->objects); + } + + // Searchtet holds edge pa->pb. Search a face with apex pc. + spintet = *searchtet; + pd = apex(spintet); + hitbdry = 0; + while (1) { + if (pd == pc) { + // Found! Insert the subface. + tspivot(spintet, checksh); // SELF_CHECK + if (checksh.sh == dummysh) { + // Comment: here we know that spintet and pssub refer to the same + // edge and the same DIRECTION: pa->pb. + if ((spintet.ver & 1) == 1) { + // Stay in CCW edge ring. + esymself(spintet); + } + if (sorg(*pssub) != org(spintet)) { + sesymself(*pssub); + } + tsbond(spintet, *pssub); + symself(spintet); + if (spintet.tet != dummytet) { + tspivot(spintet, checksh); // SELF_CHECK + assert(checksh.sh == dummysh); // SELF_CHECK + sesymself(*pssub); + tsbond(spintet, *pssub); + } + return SHAREFACE; + } else { + *searchtet = spintet; + if (checksh.sh != pssub->sh) { + // Another subface is laready inserted. + // Comment: This is possible when there are faked tets. + return COLLISIONFACE; + } else { + // The subface has already been inserted (when you do check). + return SHAREFACE; + } + } + } + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry == 2) break; + esym(*searchtet, spintet); + if (!fnextself(spintet)) break; + } + pd = apex(spintet); + if (pd == apex(*searchtet)) break; + } + + return INTERTET; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scoutcrosstet() Scout a tetrahedron across a facet. // +// // +// A subface (abc) of the facet (F) is given in 'pssub', 'searchtet' holds // +// the edge ab, it is the tet starting the search. 'facpoints' contains all // +// points which are co-facet with a, b, and c. // +// // +// The subface (abc) was produced by a 2D CDT algorithm under the Assumption // +// that F is flat. In real data, however, F may not be strictly flat. Hence // +// a tet (abde) that crosses abc may be in one of the two cases: (i) abde // +// intersects F in its interior, or (ii) abde intersects F on its boundary. // +// In case (i) F (or part of it) is missing in DT and needs to be recovered. // +// In (ii) F is not missing, the surface mesh of F needs to be adjusted. // +// // +// This routine distinguishes the two cases by the returned value, which is // +// - INTERTET, if it is case (i), 'searchtet' is abde, d and e lies below // +// and above abc, respectively, neither d nor e is dummypoint; or // +// - INTERFACE, if it is case (ii), 'searchtet' is abde, where the face // +// abd intersects abc, i.e., d is co-facet with abc, e may be co-facet // +// with abc or dummypoint. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::interresult tetgenmesh::scoutcrosstet(face *pssub, + triface* searchtet, arraypool* facpoints) +{ + triface spintet, crossface; + point pa, pb, pc, pd, pe; + REAL ori, ori1, len, n[3]; + REAL r, dr, drmin; + bool cofacetflag; + int hitbdry; + int i; + + if (facpoints != NULL) { + // Infect all vertices of the facet. + for (i = 0; i < (int) facpoints->objects; i++) { + pd = * (point *) fastlookup(facpoints, i); + pinfect(pd); + } + } + + // Search an edge crossing the facet containing abc. + if (searchtet->ver & 01) { + esymself(*searchtet); // Adjust to 0th edge ring. + sesymself(*pssub); + } + + pa = sorg(*pssub); + pb = sdest(*pssub); + pc = sapex(*pssub); + + // 'searchtet' refers to edge pa->pb. + assert(org(*searchtet) == pa); + assert(dest(*searchtet) == pb); + + // Search an apex lies below the subface. Note that such apex may not + // exist which indicates there is a co-facet apex. + cofacetflag = false; + pd = apex(*searchtet); + spintet = *searchtet; + hitbdry = 0; + while (1) { + ori = orient3d(pa, pb, pc, pd); + if ((ori != 0) && pinfected(pd)) { + ori = 0; // Force d be co-facet with abc. + } + if (ori > 0) { + break; // Found a lower point (the apex of spintet). + } + // Go to the next face. + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry == 2) { + cofacetflag = true; break; // Not found. + } + esym(*searchtet, spintet); + if (!fnextself(spintet)) { + cofacetflag = true; break; // Not found. + } + } + pd = apex(spintet); + if (pd == apex(*searchtet)) { + cofacetflag = true; break; // Not found. + } + } + + if (!cofacetflag) { + if (hitbdry > 0) { + // The edge direction is reversed, which means we have to reverse + // the face rotation direction to find the crossing edge d->e. + esymself(spintet); + } + // Keep the edge a->b be in the CCW edge ring of spintet. + if (spintet.ver & 1) { + symedgeself(spintet); + assert(spintet.tet != dummytet); + } + // Search a tet whose apex->oppo crosses the face [a, b, c]. + // -- spintet is a face [a, b, d]. + // -- the apex (d) of spintet is below [a, b, c]. + while (1) { + pe = oppo(spintet); + ori = orient3d(pa, pb, pc, pe); + if ((ori != 0) && pinfected(pe)) { + ori = 0; // Force it to be a coplanar point. + } + if (ori == 0) { + cofacetflag = true; + break; // Found a co-facet point. + } + if (ori < 0) { + *searchtet = spintet; + break; // Found. edge [d, e]. + } + // Go to the next tet. + tfnextself(spintet); + if (spintet.tet == dummytet) { + cofacetflag = true; + break; // There is a co-facet point. + } + } + // Now if "cofacetflag != true", searchtet contains a cross tet (abde), + // where d and e lie below and above abc, respectively, and + // orient3d(a, b, d, e) < 0. + } + + if (cofacetflag) { + // There are co-facet points. Calculate a point above the subface. + facenormal2(pa, pb, pc, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(pa, pb); + len += DIST(pb, pc); + len += DIST(pc, pa); + len /= 3.0; + dummypoint[0] = pa[0] + len * n[0]; + dummypoint[1] = pa[1] + len * n[1]; + dummypoint[2] = pa[2] + len * n[2]; + // Search a co-facet point d, s.t. (i) [a, b, d] intersects [a, b, c], + // AND (ii) a, b, c, d has the closet circumradius of [a, b, c]. + // NOTE: (ii) is needed since there may be several points satisfy (i). + // For an example, see file2.poly. + circumsphere(pa, pb, pc, NULL, n, &r); + crossface.tet = NULL; + pe = apex(*searchtet); + spintet = *searchtet; + hitbdry = 0; + while (1) { + pd = apex(spintet); + ori = orient3d(pa, pb, pc, pd); + if ((ori == 0) || pinfected(pd)) { + ori1 = orient3d(pa, pb, dummypoint, pd); + if (ori1 > 0) { + // [a, b, d] intersects with [a, b, c]. + if (pinfected(pd)) { + len = DIST(n, pd); + dr = fabs(len - r); + if (crossface.tet == NULL) { + // This is the first cross face. + crossface = spintet; + drmin = dr; + } else { + if (dr < drmin) { + crossface = spintet; + drmin = dr; + } + } + } else { + assert(ori == 0); // SELF_CHECK + // Found a coplanar but not co-facet point (pd). + printf("Error: Invalid PLC! A point and a subface intersect\n"); + // get_origin_facet_corners(pssub, &pa, &pb, &pc); + printf(" Point %d. Subface (#%d) (%d, %d, %d)\n", + pointmark(pd), shellmark(*pssub), pointmark(pa), pointmark(pb), + pointmark(pc)); + terminatetetgen(3); + } + } + } + // Go to the next face. + if (!fnextself(spintet)) { + hitbdry++; + if (hitbdry == 2) break; + esym(*searchtet, spintet); + if (!fnextself(spintet)) break; + } + if (apex(spintet) == pe) { + break; + } + } + if(crossface.tet == NULL) { + assert(crossface.tet != NULL); // Not handled yet. + } + *searchtet = crossface; + dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; + } + + if (cofacetflag) { + if (b->verbose > 1) { + printf(" Found a co-facet face (%d, %d, %d) op (%d).\n", + pointmark(pa), pointmark(pb), pointmark(apex(*searchtet)), + pointmark(oppo(*searchtet))); + } + if (facpoints != NULL) { + // Unmark all facet vertices. + for (i = 0; i < (int) facpoints->objects; i++) { + pd = * (point *) fastlookup(facpoints, i); + puninfect(pd); + } + } + // Comment: Now no vertex is infected. + /*if (getpointtype(apex(*searchtet)) == VOLVERTEX) { + // A vertex lies on the facet. + enext2self(*searchtet); // org(*searchtet) == pd + return TOUCHFACE; + }*/ + return INTERFACE; + } else { + // Return a crossing tet. + if (b->verbose > 1) { + printf(" Found a crossing tet (%d, %d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(apex(*searchtet)), pointmark(pe)); + } + // Comment: if facpoints != NULL, co-facet vertices are stll infected. + // They will be uninfected in formcavity(); + return INTERTET; // abc intersects the volume of 'searchtet'. + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversubfacebyflips() Recover a subface by flips in the surface mesh. // +// // +// A subface [a, b, c] ('pssub') intersects with a face [a, b, d] ('cross- // +// face'), where a, b, c, and d belong to the same facet. It indicates that // +// the face [a, b, d] should appear in the surface mesh. // +// // +// This routine recovers [a, b, d] in the surface mesh through a sequence of // +// 2-to-2 flips. No Steiner points is needed. 'pssub' returns [a, b, d]. // +// // +// If 'facfaces' is not NULL, all flipped subfaces are queued for recovery. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::recoversubfacebyflips(face* pssub, triface* crossface, + arraypool *facfaces) +{ + triface neightet; + face flipfaces[2], *parysh; + face checkseg; + point pa, pb, pc, pd, pe; + REAL ori, len, n[3]; + + // Get the missing subface is [a, b, c]. + pa = sorg(*pssub); + pb = sdest(*pssub); + pc = sapex(*pssub); + + // The crossface is [a, b, d, e]. + // assert(org(*crossface) == pa); + // assert(dest(*crossface) == pb); + pd = apex(*crossface); + pe = dummypoint; // oppo(*crossface); + + if (pe == dummypoint) { + // Calculate a point above the faces. + facenormal2(pa, pb, pd, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(pa, pb); + len += DIST(pb, pd); + len += DIST(pd, pa); + len /= 3.0; + pe[0] = pa[0] + len * n[0]; + pe[1] = pa[1] + len * n[1]; + pe[2] = pa[2] + len * n[2]; + } + + // Adjust face [a, b, c], so that edge [b, c] crosses edge [a, d]. + ori = orient3d(pb, pc, pe, pd); + assert(ori != 0); // SELF_CHECK + + if (ori > 0) { + // Swap a and b. + sesymself(*pssub); + esymself(*crossface); // symedgeself(*crossface); + pa = sorg(*pssub); + pb = sdest(*pssub); + if (pe == dummypoint) { + pe[0] = pe[1] = pe[2] = 0; + } + pe = dummypoint; // oppo(*crossface); + } + + while (1) { + + // Flip edge [b, c] to edge [a, d]. + senext(*pssub, flipfaces[0]); + sspivot(flipfaces[0], checkseg); // SELF_CHECK + assert(checkseg.sh == dummysh); // SELF_CHECK + spivot(flipfaces[0], flipfaces[1]); + + stpivot(flipfaces[1], neightet); + if (neightet.tet != dummytet) { + // A recovered subface, clean sub<==>tet connections. + tsdissolve(neightet); + symself(neightet); + tsdissolve(neightet); + stdissolve(flipfaces[1]); + sesymself(flipfaces[1]); + stdissolve(flipfaces[1]); + sesymself(flipfaces[1]); + // flipfaces[1] refers to edge [b, c] (either b->c or c->b). + } + + flip22sub(&(flipfaces[0]), NULL); + flip22count++; + + // Comment: now flipfaces[0] is [d, a, b], flipfaces[1] is [a, d, c]. + + // Add them into list (make ensure that they must be recovered). + facfaces->newindex((void **) &parysh); + *parysh = flipfaces[0]; + facfaces->newindex((void **) &parysh); + *parysh = flipfaces[1]; + + // Find the edge [a, b]. + senext(flipfaces[0], *pssub); + assert(sorg(*pssub) == pa); // SELF_CHECK + assert(sdest(*pssub) == pb); // SELF_CHECK + + pc = sapex(*pssub); + if (pc == pd) break; + + if (pe == dummypoint) { + // Calculate a point above the faces. + facenormal2(pa, pb, pd, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(pa, pb); + len += DIST(pb, pd); + len += DIST(pd, pa); + len /= 3.0; + pe[0] = pa[0] + len * n[0]; + pe[1] = pa[1] + len * n[1]; + pe[2] = pa[2] + len * n[2]; + } + + while (1) { + ori = orient3d(pb, pc, pe, pd); + assert(ori != 0); // SELF_CHECK + if (ori > 0) { + senext2self(*pssub); + spivotself(*pssub); + if (sorg(*pssub) != pa) sesymself(*pssub); + pb = sdest(*pssub); + pc = sapex(*pssub); + continue; + } + break; + } + } + + if (pe == dummypoint) { + pe[0] = pe[1] = pe[2] = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// formcavity() Form the cavity of a missing region. // +// // +// A missing region R is a set of co-facet (co-palanr) subfaces. 'pssub' is // +// a missing subface [a, b, c]. 'crosstets' contains only one tet, [a, b, d, // +// e], where d and e lie below and above [a, b, c], respectively. Other // +// crossing tets are sought from this tet and saved in 'crosstets'. // +// // +// The cavity C is divided into two parts by R,one at top and one at bottom. // +// 'topfaces' and 'botfaces' return the upper and lower boundary faces of C. // +// 'toppoints' contains vertices of 'crosstets' in the top part of C, and so // +// does 'botpoints'. Both 'toppoints' and 'botpoints' contain vertices of R. // +// // +// NOTE: 'toppoints' may contain points which are not vertices of any top // +// faces, and so may 'botpoints'. Such points may belong to other facets and // +// need to be present after the recovery of this cavity (P1029.poly). // +// // +// A pair of boundary faces: 'firsttopface' and 'firstbotface', are saved. // +// They share the same edge in the boundary of the missing region. // +// // +// 'facpoints' contains all vertices of the facet containing R. They are // +// used for searching the crossing tets. On input all vertices are infected. // +// They are uninfected after the cavity is formed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::formcavity(face *pssub, arraypool* crosstets, + arraypool* topfaces, arraypool* botfaces, arraypool* toppoints, + arraypool* botpoints, arraypool* facpoints, arraypool* facfaces) +{ + arraypool *crossedges; + triface *parytet, crosstet, spintet, neightet, faketet; + face neighsh, checksh, *parysh; + face checkseg; + point pa, pb, pc, pf, pg; + point pd, pe; + point *ppt; + // REAL ori; + int i, j; + + // For triangle-edge test. + enum interresult dir; + int types[2], poss[4]; + + // Get the missing subface abc. + pa = sorg(*pssub); + pb = sdest(*pssub); + pc = sapex(*pssub); + + // Comment: Now all facet vertices are infected. + + // Get a crossing tet abde. + parytet = (triface *) fastlookup(crosstets, 0); // face abd. + // The edge de crosses the facet. d lies below abc. + enext2fnext(*parytet, crosstet); + enext2self(crosstet); + esymself(crosstet); // the edge d->e at face [d,e,a] + infect(crosstet); + *parytet = crosstet; // Save it in list. + + // Temporarily re-use 'topfaces' for storing crossing edges. + crossedges = topfaces; + crossedges->newindex((void **) &parytet); + *parytet = crosstet; + + // Collect all crossing tets. Each cross tet is saved in the standard + // form deab, where de is a corrsing edge, orient3d(d,e,a,b) < 0. + // NOTE: hull tets may be collected. See fig/dump-cavity-case2a(b).lua. + // Make sure that neither d nor e is dummypoint. + for (i = 0; i < (int) crossedges->objects; i++) { + crosstet = * (triface *) fastlookup(crossedges, i); + // It may already be tested. + if (!edgemarked(crosstet)) { + // Collect all tets sharing at the edge. + pg = apex(crosstet); + spintet = crosstet; + while (1) { + // Mark this edge as tested. + markedge(spintet); + if (!infected(spintet)) { + infect(spintet); + crosstets->newindex((void **) &parytet); + *parytet = spintet; + } + // Go to the neighbor tet. + tfnextself(spintet); + if (spintet.tet != dummytet) { + // Check the validity of the PLC. + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + printf("Error: Invalid PLC! Two subfaces intersect.\n"); + printf(" 1st (#%4d): (%d, %d, %d)\n", shellmark(*pssub), + pointmark(pa), pointmark(pb), pointmark(pc)); + printf(" 2nd (#%4d): (%d, %d, %d)\n", shellmark(checksh), + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + terminatetetgen(3); + } + } else { + // Encounter a boundary face. + assert(0); // Not handled yet. + } + if (apex(spintet) == pg) break; + } + // Detect new cross edges. + // Comment: A crossing edge must intersect one missing subface of + // this facet. We do edge-face tests. + pd = org(spintet); + pe = dest(spintet); + while (1) { + // Remember: spintet is edge d->e, d lies below [a, b, c]. + pf = apex(spintet); + // if (pf != dummypoint) { // Do not grab a hull edge. + if (!pinfected(pf)) { + for (j = 0; j < (int) facfaces->objects; j++) { + parysh = (face *) fastlookup(facfaces, j); + pa = sorg(*parysh); + pb = sdest(*parysh); + pc = sapex(*parysh); + // Check if pd->pf crosses the facet. + if (tri_edge_test(pa, pb, pc, pd, pf, NULL, 1, types, poss)) { + dir = (enum interresult) types[0]; + if ((dir == INTEREDGE) || (dir == INTERFACE)) { + // The edge d->f corsses the facet. + enext2fnext(spintet, neightet); + esymself(neightet); // d->f. + // pd must lie below the subface. + break; + } + } + // Check if pe->pf crosses the facet. + if (tri_edge_test(pa, pb, pc, pe, pf, NULL, 1, types, poss)) { + dir = (enum interresult) types[0]; + if ((dir == INTEREDGE) || (dir == INTERFACE)) { + // The edge f->e crosses the face. + enextfnext(spintet, neightet); + esymself(neightet); // f->e. + // pf must lie below the subface. + break; + } + } + } + // There must exist a crossing edge. + assert(j < (int) facfaces->objects); + /*// There exist a crossing edge, either d->f, or f->e. + ori = orient3d(pa, pb, pc, pf); + if (ori == 0) { + printf("Error: Invalid PLC! Point and subface intersect.\n"); + printf(" Point %d, subface (#%4d): (%d, %d, %d)\n", + pointmark(pf), shellmark(*pssub), pointmark(pa), + pointmark(pb), pointmark(pc)); + terminatetetgen(3); + } + if (ori < 0) { + // The edge d->f corsses the facet. + enext2fnext(spintet, neightet); + esymself(neightet); // d->f. + } else { + // The edge f->e crosses the face. + enextfnext(spintet, neightet); + esymself(neightet); // f->e. + } + */ + if (!edgemarked(neightet)) { + // Add a new cross edge. + crossedges->newindex((void **) &parytet); + *parytet = neightet; + } + } + // } + tfnextself(spintet); + if (spintet.tet == dummytet) { + // Encounter a boundary face. + assert(0); // Not handled yet. + } + if (apex(spintet) == pg) break; + } + } + } + + // Unmark all facet vertices. + for (i = 0; i < (int) facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + puninfect(*ppt); + } + + // Comments: Now no vertex is marked. Next we will mark vertices which + // belong to the top and bottom boundary faces of the cavity and put + // them in 'toppopints' and 'botpoints', respectively. + + // All cross tets are found. Unmark cross edges. + for (i = 0; i < (int) crossedges->objects; i++) { + crosstet = * (triface *) fastlookup(crossedges, i); + if (edgemarked(crosstet)) { + // Add the vertices of the cross edge [d, e] in lists. It must be + // that d lies below the facet (i.e., its a bottom vertex). + // Note that a cross edge contains no dummypoint. + pf = org(crosstet); + // assert(pf != dummypoint); // SELF_CHECK + if (!pinfected(pf)) { + pinfect(pf); + botpoints->newindex((void **) &ppt); // Add a bottom vertex. + *ppt = pf; + } + pf = dest(crosstet); + // assert(pf != dummypoint); // SELF_CHECK + if (!pinfected(pf)) { + pinfect(pf); + toppoints->newindex((void **) &ppt); // Add a top vertex. + *ppt = pf; + } + // Unmark this edge in all tets containing it. + pg = apex(crosstet); + spintet = crosstet; + while (1) { + assert(edgemarked(spintet)); // SELF_CHECK + unmarkedge(spintet); + tfnextself(spintet); // Go to the neighbor tet. + if (spintet.tet == dummytet) { + assert(0); // Not handled yet. + } + if (apex(spintet) == pg) break; + } + } + } + + if (b->verbose > 1) { + printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", + crosstets->objects, crossedges->objects); + } + crossedges->restart(); + + // Find a pair of cavity boundary faces from the top and bottom sides of + // the facet each, and they share the same edge. Save them in the + // global variables: firsttopface, firstbotface. They will be used in + // fillcavity() for gluing top and bottom new tets. + for (i = 0; i < (int) crosstets->objects; i++) { + crosstet = * (triface *) fastlookup(crosstets, i); + enextfnext(crosstet, spintet); + enextself(spintet); + symedge(spintet, neightet); + // if (!infected(neightet)) { + if ((neightet.tet == dummytet) || !infected(neightet)) { + // A top face. + if (neightet.tet == dummytet) { + // Create a fake tet to hold the boundary face. + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, dummypoint); + bond(faketet, spintet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(faketet, checksh); + } + for (j = 0; j < 3; j++) { // Bond segments. + tsspivot1(spintet, checkseg); + if (checkseg.sh != dummysh) { + tssbond1(faketet, checkseg); + } + enextself(spintet); + enextself(faketet); + } + firsttopface = faketet; + } else { + firsttopface = neightet; + } + } else { + continue; // Go to the next cross tet. + } + enext2fnext(crosstet, spintet); + enext2self(spintet); + symedge(spintet, neightet); + // if (!infected(neightet)) { + if ((neightet.tet == dummytet) || !infected(neightet)) { + // A bottom face. + if (neightet.tet == dummytet) { + // Create a fake tet to hold the boundary face. + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, dummypoint); + bond(spintet, faketet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(faketet, checksh); + } + for (j = 0; j < 3; j++) { // Bond segments. + tsspivot1(spintet, checkseg); + if (checkseg.sh != dummysh) { + tssbond1(faketet, checkseg); + } + enextself(spintet); + enextself(faketet); + } + firstbotface = faketet; + } else { + firstbotface = neightet; + } + } else { + continue; + } + break; + } + assert(i < (int) crosstets->objects); // SELF_CHECK + + // Collect the top and bottom faces and the middle vertices. Since all top + // and bottom vertices have been marked in above. Unmarked vertices are + // middle vertices. + // NOTE 1: Hull tets may be collected. Process them as normal one. + // (see fig/dump-cavity-case2.lua.) + // NOTE 2: Some previously recovered subfaces may be completely + // contained in a cavity (see fig/dump-cavity-case6.lua). In such case, + // we create two faked tets to hold this subface, one at each side. + // The faked tets will be removed in fillcavity(). + for (i = 0; i < (int) crosstets->objects; i++) { + crosstet = * (triface *) fastlookup(crosstets, i); + enextfnext(crosstet, spintet); + enextself(spintet); + symedge(spintet, neightet); + // if (!infected(neightet)) { + if ((neightet.tet == dummytet) || !infected(neightet)) { + // A top face. + topfaces->newindex((void **) &parytet); + if (neightet.tet == dummytet) { + // Create a fake tet to hold the boundary face. + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, dummypoint); + bond(spintet, faketet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(faketet, checksh); + } + for (j = 0; j < 3; j++) { // Bond segments. + tsspivot1(spintet, checkseg); + if (checkseg.sh != dummysh) { + tssbond1(faketet, checkseg); + } + enextself(spintet); + enextself(faketet); + } + *parytet = faketet; + } else { + *parytet = neightet; + } + } else { + if ((neightet.tet != dummytet) && infected(neightet)) { + // Check if this side is a subface. + tspivot(spintet, neighsh); + if (neighsh.sh != dummysh) { + // Found a subface (inside the cavity)! + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, dummypoint); + marktest(faketet); // To distinguish it from other faked tets. + sesymself(neighsh); + tsbond(faketet, neighsh); // Let it hold the subface. + for (j = 0; j < 3; j++) { // Bond segments. + tsspivot1(spintet, checkseg); + if (checkseg.sh != dummysh) { + tssbond1(faketet, checkseg); + } + enextself(spintet); + enextself(faketet); + } + // Add a top face (at faked tet). + topfaces->newindex((void **) &parytet); + *parytet = faketet; + } + } + } + enext2fnext(crosstet, spintet); + enext2self(spintet); + symedge(spintet, neightet); + // if (!infected(neightet)) { + if ((neightet.tet == dummytet) || !infected(neightet)) { + // A bottom face. + botfaces->newindex((void **) &parytet); + if (neightet.tet == dummytet) { + // Create a fake tet to hold the boundary face. + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, dummypoint); + bond(spintet, faketet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(faketet, checksh); + } + for (j = 0; j < 3; j++) { // Bond segments. + tsspivot1(spintet, checkseg); + if (checkseg.sh != dummysh) { + tssbond1(faketet, checkseg); + } + enextself(spintet); + enextself(faketet); + } + *parytet = faketet; + } else { + *parytet = neightet; + } + } else { + if ((neightet.tet != dummytet) && infected(neightet)) { + tspivot(spintet, neighsh); + if (neighsh.sh != dummysh) { + // Found a subface (inside the cavity)! + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, dummypoint); + marktest(faketet); // To distinguish it from other faked tets. + sesymself(neighsh); + tsbond(faketet, neighsh); // Let it hold the subface. + for (j = 0; j < 3; j++) { // Bond segments. + tsspivot1(spintet, checkseg); + if (checkseg.sh != dummysh) { + tssbond1(faketet, checkseg); + } + enextself(spintet); + enextself(faketet); + } + // Add a bottom face (at faked tet). + botfaces->newindex((void **) &parytet); + *parytet = faketet; + } + } + } + // Add middle vertices if there are (skip dummypoint). + pf = org(spintet); + if (!pinfected(pf)) { + // if (pf != dummypoint) { + pinfect(pf); + botpoints->newindex((void **) &ppt); // Add a bottom vertex. + *ppt = pf; + toppoints->newindex((void **) &ppt); // Add a top vertex. + *ppt = pf; + // } + } + pf = dest(spintet); + if (!pinfected(pf)) { + // if (pf != dummypoint) { + pinfect(pf); + botpoints->newindex((void **) &ppt); // Add a bottom vertex. + *ppt = pf; + toppoints->newindex((void **) &ppt); // Add a top vertex. + *ppt = pf; + // } + } + } + + // Unmark all collected top, bottom, and middle vertices. + for (i = 0; i < (int) toppoints->objects; i++) { + ppt = (point *) fastlookup(toppoints, i); + puninfect(*ppt); + } + for (i = 0; i < (int) botpoints->objects; i++) { + ppt = (point *) fastlookup(botpoints, i); + puninfect(*ppt); + } + // Comments: Now no vertex is marked. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// delaunizecavity() Fill a cavity by Delaunay tetrahedra. // +// // +// The tetrahedralizing cavity is the half (top or bottom part) of the whole // +// cavity. The boundary faces of the half cavity are given in 'cavfaces', // +// the bounday faces of the internal facet are not given. These faces will // +// be recovered later in fillcavity(). // +// // +// This routine first constructs the DT of the vertices by the Bowyer-Watson // +// algorithm. Then it identifies the boundary faces of the cavity in DT. // +// The DT is returned in 'newtets'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, + arraypool *cavshells, arraypool *newtets, arraypool *crosstets, + arraypool *misfaces) +{ + triface *parytet, searchtet, neightet, spintet, *parytet1; + triface newtet, faketet; + face checksh, tmpsh, *parysh; + face checkseg; + point pa, pb, pc, pd, pt[3], *parypt; + // badface *newflipface; + enum interresult dir; + REAL ori; + // int miscount; + int i, j, k; + + if (b->verbose > 1) { + printf(" Delaunizing cavity: %ld points, %ld faces.\n", + cavpoints->objects, cavfaces->objects); + } + + // Get four non-coplanar points (no dummypoint). + parytet = (triface *) fastlookup(cavfaces, 0); + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + pinfect(pa); + pinfect(pb); + pinfect(pc); + pd = NULL; + for (i = 1; i < (int) cavfaces->objects; i++) { + parytet = (triface *) fastlookup(cavfaces, i); + pt[0] = org(*parytet); + pt[1] = dest(*parytet); + pt[2] = apex(*parytet); + for (j = 0; j < 3; j++) { + // if (pt[j] != dummypoint) { // Do not include a hull point. + if (!pinfected(pt[j])) { + ori = orient3d(pa, pb, pc, pt[j]); + if (ori != 0) { + pd = pt[j]; + if (ori > 0) { // Swap pa and pb. + pt[j] = pa; pa = pb; pb = pt[j]; + } + break; + } + } + // } + } + if (pd != NULL) break; + } + assert(i < (int) cavfaces->objects); // SELF_CHECK + pinfect(pd); + + // Create an init DT. + // initialDT(pa, pb, pc, pd); + // Create the initial tet. + maketetrahedron(&newtet); + if (b->verbose > 2) { + printf(" Create the first tet (%d, %d, %d, %d).\n", + pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd)); + } + setorg(newtet, pa); + setdest(newtet, pb); + setapex(newtet, pc); + setoppo(newtet, pd); + // Update the point-to-tet map. + setpoint2tet(pa, encode(newtet)); + setpoint2tet(pb, encode(newtet)); + setpoint2tet(pc, encode(newtet)); + setpoint2tet(pd, encode(newtet)); + // Bond to 'dummytet' for point location. + dummytet[0] = encode(newtet); + recenttet = newtet; + // At init, all faces of this tet are hull faces. + hullsize = 4; + + for (i = 0; i < (int) cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + assert(pt[0] != dummypoint); // SELF_CHECK + if (!pinfected(pt[0])) { + searchtet = recenttet; + insertvertexbw(pt[0], &searchtet, true, false, false, false); + } else { + puninfect(pt[0]); // It is already inserted. + } + } + // Comment: All vertices of the cavity are NOT marked. + + while (1) { + + // Identify boundary faces. Remember interior tets. Save missing faces. + // For each identified boundary face in the new DT, we insert a subface + // temporarily at that place. The subface also contains a pointer to + // the adjacent tet outside of the cavity. We save the temp subface + // with its side facing to the interior of the cavity. + for (i = 0; i < (int) cavfaces->objects; i++) { + parytet = (triface *) fastlookup(cavfaces, i); + // Skip an interior face (due to the enlargement of the cavity). + if (infected(*parytet)) continue; + // Choose the CCW edge ring. + parytet->ver = 4; + pt[0] = org(*parytet); + pt[1] = dest(*parytet); + pt[2] = apex(*parytet); + // Create a temp subface. + makeshellface(subfaces, &tmpsh); + // setshvertices(tmpsh, pt[0], pt[1], pt[2]); + setsorg(tmpsh, pt[0]); + setsdest(tmpsh, pt[1]); + setsapex(tmpsh, pt[2]); + // Comment: This side of tmpsh faces to the outside of the cavity. + // Insert tmpsh in DT. + searchtet.tet = NULL; + dir = scoutsubface(&tmpsh, &searchtet, 1); + if (dir == SHAREFACE) { + // Let tmpsh face to the interior tet of the cavity. + if (sorg(tmpsh) == pt[0]) { + sesymself(tmpsh); + } + assert(sorg(tmpsh) == pt[1]); + assert(sdest(tmpsh) == pt[0]); + } else if (dir == COLLISIONFACE) { + // A subface is already inserted. This case can only happen when there + // exist a subface inside the cavity, and two faked tets were created + // for protecting such a subface (see fig/dum-cavity-case6). + assert(oppo(*parytet) == dummypoint); + assert(marktested(*parytet)); + // This subface is redundant. But it is needed here (to remember the + // faked tet and the real subface which is inside the cavity). + if ((searchtet.ver & 01) != 0) esymself(searchtet); + // Adjust the searchtet to edge pt[1]->pt[0]. + if (org(searchtet) != pt[1]) { + symedgeself(searchtet); + assert(org(searchtet) == pt[1]); // SELF_CHECK + } + assert(dest(searchtet) == pt[0]); // SELF_CHECK + // Only connect: tmpsh<--searchtet. So stpivot() works. + sesymself(tmpsh); + tmpsh.sh[6 + EdgeRing(tmpsh.shver)] = (shellface) encode(searchtet); + } else { + if (b->verbose > 1) { + printf(" p:draw_subface(%d, %d, %d) -- %d is missing\n", + pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i); + } + shellfacedealloc(subfaces, tmpsh.sh); + // Save this face in list. + misfaces->newindex((void **) &parytet1); + *parytet1 = *parytet; + continue; + } + // Remember the boundary tet in tmpsh (use the adjacent subface slot). + tmpsh.sh[0] = (shellface) encode(*parytet); + // Save this subface. + cavshells->newindex((void **) &parysh); + *parysh = tmpsh; + } + + if (misfaces->objects > 0) { + // Removing tempoaray subfaces. + for (i = 0; i < (int) cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + stpivot(*parysh, neightet); + tsdissolve(neightet); // Detach it from adj. tets. + symself(neightet); + tsdissolve(neightet); + shellfacedealloc(subfaces, parysh->sh); + } + cavshells->restart(); + + // Infect the points which are of the cavity for detecting new + // cavity point due to the enlargement. + for (i = 0; i < (int) cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + pinfect(pt[0]); // Mark it as inserted. + } + + // Enlarge the cavity. + for (i = 0; i < (int) misfaces->objects; i++) { + // Get a missing face. + parytet = (triface *) fastlookup(misfaces, i); + if (!infected(*parytet)) { + if (oppo(*parytet) == dummypoint) { + printf("Internal error: A convex hull is missing.\n"); + terminatetetgen(2); + } + // Put it into crossing tet list. + infect(*parytet); + crosstets->newindex((void **) &parytet1); + *parytet1 = *parytet; + // Insert the opposite point if it is not in DT. + pd = oppo(*parytet); + if (!pinfected(pd)) { + if (b->verbose > 1) { + printf(" Insert the opposite point %d.\n", pointmark(pd)); + } + pinfect(pd); + cavpoints->newindex((void **) &parypt); + *parypt = pd; + searchtet = recenttet; + insertvertexbw(pd, &searchtet, true, false, false, false); + } + // Check for a missing subface. + tspivot(*parytet, checksh); + if (checksh.sh != dummysh) { + if (b->verbose > 1) { + printf(" Queue a subface x%lx (%d, %d, %d).\n", + (unsigned long) checksh.sh, pointmark(sorg(checksh)), + pointmark(sdest(checksh)), pointmark(sapex(checksh))); + } + stdissolve(checksh); + sesymself(checksh); + stdissolve(checksh); + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + // Add three opposite faces into the boundary list. + for (j = 0; j < 3; j++) { + fnext(*parytet, spintet); + symedge(spintet, neightet); + if ((neightet.tet == dummytet) || !infected(neightet)) { + if (b->verbose > 1) { + printf(" Add a cavface (%d, %d, %d).\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet))); + } + cavfaces->newindex((void **) &parytet1); + if (neightet.tet == dummytet) { + maketetrahedron(&faketet); // Create a faked tet. + setorg(faketet, org(spintet)); + setdest(faketet, dest(spintet)); + setapex(faketet, apex(spintet)); + setoppo(faketet, dummypoint); + bond(spintet, faketet); + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tspivot(faketet, checksh); + } + for (k = 0; k < 3; k++) { + tsspivot1(spintet, checkseg); + if (checkseg.sh != dummysh) { + tssbond1(faketet, checkseg); + } + enextself(spintet); + enextself(faketet); + } + *parytet1 = faketet; + } else { + *parytet1 = neightet; + } + } else { + // Check if a subface is missing again. + tspivot(neightet, checksh); + if (checksh.sh != dummysh) { + if (b->verbose > 1) { + printf(" Queue a subface x%lx (%d, %d, %d).\n", + (unsigned long) checksh.sh, pointmark(sorg(checksh)), + pointmark(sdest(checksh)), pointmark(sapex(checksh))); + } + stdissolve(checksh); + sesymself(checksh); + stdissolve(checksh); + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + enextself(*parytet); + } // j + } // if (!infected(parytet)) + } + + // Uninfect the points which are of the cavity. + for (i = 0; i < (int) cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + puninfect(pt[0]); + } + + misfaces->restart(); + cavityexpcount++; + continue; + } + + break; + + } // while (1) + + // Collect all tets of the DT. All new tets are marktested. + marktest(recenttet); + newtets->newindex((void **) &parytet); + *parytet = recenttet; + for (i = 0; i < (int) newtets->objects; i++) { + searchtet = * (triface *) fastlookup(newtets, i); + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, neightet); + if (neightet.tet != dummytet) { + if (!marktested(neightet)) { + marktest(neightet); + newtets->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + } + + cavpoints->restart(); + // Comment: Now no vertex is marked. + cavfaces->restart(); + + if (cavshells->objects > (long) maxcavsize) { + maxcavsize = cavshells->objects; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// fillcavity() Fill new tets into the cavity. // +// // +// The new tets are stored in two disjoint sets(which share the same facet). // +// 'topfaces' and 'botfaces' are the boundaries of these two sets, respect- // +// ively. 'midfaces' is empty on input, and will store faces in the facet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, + arraypool* midfaces, arraypool* facpoints) +{ + arraypool *cavshells; + triface *parytet, bdrytet, toptet, bottet, neightet, midface, spintet; + face checksh, *parysh; + face checkseg; + point pa, pb, pc, pf, pg; + REAL ori, len, n[3]; + bool mflag, bflag; + int i, j, k; + + // Connect newtets to tets outside the cavity. + for (k = 0; k < 2; k++) { + cavshells = (k == 0 ? topshells : botshells); + if (cavshells != NULL) { + for (i = 0; i < (int) cavshells->objects; i++) { + // Get a temp subface. + parysh = (face *) fastlookup(cavshells, i); + // Get the boundary tet outsode the cavity. + decode(parysh->sh[0], bdrytet); + pa = sorg(*parysh); + pb = sdest(*parysh); + // Fix bdrytet at the edge pb->pa. + bdrytet.ver = 0; + for (j = 0; j < 3; j++) { + if (org(bdrytet) == pb) break; + enextself(bdrytet); + } + assert(j < 3); + assert(dest(bdrytet) == pa); + // pa = org(bdrytet); + // pb = dest(bdrytet); + pc = apex(bdrytet); + // Get the adjacent new tet which is in the cavity. + stpivot(*parysh, neightet); + // Fix neightet at the edge pa->pb. + neightet.ver = 0; + for (j = 0; j < 3; j++) { + if (org(neightet) == pa) break; + enextself(neightet); + } + assert(j < 3); + assert(dest(neightet) == pb); // SELF_CHECK + // Mark neightet as an interior tet of this cavity, 2009-04-24. + if (!infected(neightet)) { + infect(neightet); + } + // Comment: bdrytet may be a faked tet, Bond it if it is not + // marktested, i.e., it is not created for holding an interor + // subface. The connections will be used in fillcavity for + // finding middle faces. + if (!marktested(bdrytet)) { + // Bond the two tets. + bond(bdrytet, neightet); + // } else { + // A new boundary face. + // dummytet[0] = encode(neightet); + } + // Bond a subface (if it exists). + tspivot(bdrytet, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(neightet, checksh); // Also cleared the pointer to tmpsh. + } else { + tsdissolve(neightet); // No subface, clear the pointer to tmpsh. + } + // Bond subsegments + for (j = 0; j < 3; j++) { + tsspivot1(bdrytet, checkseg); + if (checkseg.sh != dummysh) { + spintet = neightet; + while (1) { + tssbond1(spintet, checkseg); + tfnextself(spintet); + if (spintet.tet == dummytet) break; // Outside the cavity. + if (!marktested(spintet)) break; // Outside the cavity. + if (spintet.tet == neightet.tet) break; // Turn back. + } + } + enextself(bdrytet); + enext2self(neightet); + } + // Update the point-to-tets map. + setpoint2tet(pa, encode(neightet)); + setpoint2tet(pb, encode(neightet)); + setpoint2tet(pc, encode(neightet)); + // Delete the temp subface. + // shellfacedealloc(subfacepool, parysh->sh); + // if (oppo(bdrytet) == dummypoint) { + // Delete a faked tet. + // tetrahedrondealloc(bdrytet.tet); + // } + } + } // if (cavshells != NULL) + } + + mflag = true; // Initialize it. + + if (midfaces != NULL) { + + // Mark all facet vertices for finding middle subfaces. + for (i = 0; i < (int) facpoints->objects; i++) { + pf = * (point *) fastlookup(facpoints, i); + pinfect(pf); + } + + // The first pair of top and bottom tets share the same edge [a, b]. + // toptet = * (triface *) fastlookup(topfaces, 0); + if (infected(firsttopface)) { + // The cavity was enlarged. This tet is included in the interior + // (as those of a crossing tet). Find the updated top boundary face + // by rotating the faces around this edge (until an uninfect tet). + pa = apex(firsttopface); + while (1) { + tfnextself(firsttopface); + assert(firsttopface.tet != dummytet); + if (!infected(firsttopface)) break; + assert(apex(firsttopface) != pa); // SELF_CHECK + } + } + toptet = firsttopface; + symedgeself(toptet); + assert(marktested(toptet)); // It must be a new tet. + // Search a subface from the top mesh. + while (1) { + fnextself(toptet); // The next face in the same tet. + pc = apex(toptet); + if (pinfected(pc)) break; // [a,b,c] is a subface. + symedgeself(toptet); // Go to the same face in the adjacent tet. + assert(toptet.tet != dummytet); + } + // Search the subface [a,b,c] in the bottom mesh. + // bottet = * (triface *) fastlookup(botfaces, 0); + if (infected(firstbotface)) { + pa = apex(firstbotface); + while (1) { + tfnextself(firstbotface); + assert(firstbotface.tet != dummytet); + if (!infected(firstbotface)) break; + assert(apex(firstbotface) != pa); // SELF_CHECK + } + } + bottet = firstbotface; + symedgeself(bottet); + assert(marktested(bottet)); // It must be a new tet. + while (1) { + fnextself(bottet); // The next face in the same tet. + pf = apex(bottet); + if (pf == pc) break; // Face matched. + if (pinfected(pf)) { + mflag = false; break; // Not matched. + } + symedgeself(bottet); + assert(bottet.tet != dummytet); + } + if (mflag) { + // Connect the two tets together. + bond(toptet, bottet); + // Both are interior tets. + infect(toptet); + infect(bottet); + // Add this face into search list. + // esymself(toptet); // Choose the 0th edge ring. + markface(toptet); + midfaces->newindex((void **) &parytet); + *parytet = toptet; + } + + // Match pairs of subfaces (middle faces), connect top and bottom tets. + for (i = 0; i < (int) midfaces->objects && mflag; i++) { + // Get a matched middle face [a, b, c] + midface = * (triface *) fastlookup(midfaces, i); + // It is inside the cavity. + assert(marktested(midface)); // SELF_CHECK + // Check the neighbors at edges [b, c] and [c, a]. + midface.ver = 0; + for (j = 0; j < 3 && mflag; j++) { + pg = apex(midface); + toptet = midface; + bflag = false; + while (1) { + // Go to the next face in the same tet. + fnextself(toptet); + pc = apex(toptet); + if (pinfected(pc)) { + break; // Find a subface. + } + // if (pc == dummypoint) { + // break; // Find a subface. + // } + /* if (pc == pg) { + // The adjacent face is not a middle face. + bflag = true; break; + }*/ + symedgeself(toptet); + assert(toptet.tet != dummytet); // The adjacent tet must exist. + // Do we walk outside the cavity? + if (!marktested(toptet)) { + // Yes, the adjacent face is not a middle face. + bflag = true; break; + } + } + if (!bflag) { + // assert(marktested(toptet)); // SELF_CHECK + if (!facemarked(toptet)) { + symedge(midface, bottet); + while (1) { + fnextself(bottet); + pf = apex(bottet); + if (pf == pc) break; // Face matched. + if (pinfected(pf)) { + mflag = false; break; // Not matched. + } + symedgeself(bottet); + assert(bottet.tet != dummytet); // The adjacent tet must exist. + } + if (mflag) { + if (marktested(bottet)) { + // Connect two tets together. + bond(toptet, bottet); + // Both are interior tets. + infect(toptet); + infect(bottet); + // Add this face into list. + // esymself(toptet); + markface(toptet); + midfaces->newindex((void **) &parytet); + *parytet = toptet; + } else { + // The 'bottet' is not inside the cavity! + // This case can happen when the cavity was enlarged, and the + // 'toptet' is a co-facet (sub)face adjacent to the missing + // region, and it is a boundary face of the top cavity. + // So the toptet and bottet should be bonded already through + // a temp subface. See fig/dump-cavity-case18. Check it. + symedge(toptet, neightet); + assert(neightet.tet == bottet.tet); // SELF_CHECK + assert(neightet.loc == bottet.loc); // SELF_CHECK + // Do not add this face into 'midfaces'. + } + } + } + } + enextself(midface); // Go to the next edge. + } // j + } // i + + } // if (midfaces != NULL) + + if (mflag) { + if (midfaces != NULL) { + if (b->verbose > 1) { + printf(" Found %ld middle subfaces.\n", midfaces->objects); + } + if (midfaces->objects > (long) maxregionsize) { + maxregionsize = (long) midfaces->objects; + } + // Unmark middle faces. + for (i = 0; i < (int) midfaces->objects; i++) { + // Get a matched middle face [a, b, c] + midface = * (triface *) fastlookup(midfaces, i); + assert(facemarked(midface)); // SELF_CHECK + unmarkface(midface); + } + } + } else { + // Faces at top and bottom are not matched. There exists non-Delaunay + // subedges. See fig/dump-cavity-case5.lua. + pa = org(toptet); + pb = dest(toptet); + pc = apex(toptet); + pf = apex(bottet); + if (0) { // if (b->verbose > 1) { + printf(" p:draw_tet(%d, %d, %d, %d) -- top tet.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(oppo(toptet))); + printf(" p:draw_tet(%d, %d, %d, %d) -- bot tet.\n", + pointmark(org(bottet)), pointmark(dest(bottet)), + pointmark(apex(bottet)), pointmark(oppo(bottet))); + } + // Calculate a point above the faces. + facenormal2(pa, pb, pc, n, 1); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(pa, pb); + len += DIST(pb, pc); + len += DIST(pc, pa); + len /= 3.0; + dummypoint[0] = pa[0] + len * n[0]; + dummypoint[1] = pa[1] + len * n[1]; + dummypoint[2] = pa[2] + len * n[2]; + // Find the crossing edges. + ori = orient3d(pb, pc, dummypoint, pf); + assert(ori != 0); // SELF_CHECK + if (ori < 0) { + // The top edge [b, c] intersects the bot edge [a, f]. + enextself(toptet); + enextself(bottet); + } else { + // The top edge [c, a] intersects the bot edge [f, b]. + enext2self(toptet); + enext2self(bottet); + } + // Split one of the edges, choose the one has longer length. + n[0] = DIST(org(toptet), dest(toptet)); + n[1] = DIST(org(bottet), dest(bottet)); + if (n[0] > n[1]) { + pf = org(toptet); + pg = dest(toptet); + } else { + pf = org(bottet); + pg = dest(bottet); + } + if (b->verbose > 1) { + printf(" Found a non-Delaunay edge (%d, %d)\n", pointmark(pf), + pointmark(pg)); + } + // Create the midpoint of the non-Delaunay edge. + for (i = 0; i < 3; i++) { + dummypoint[i] = 0.5 * (pf[i] + pg[i]); + } + // Set a tet for searching the new point. + recenttet = firsttopface; + // dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; + ndelaunayedgecount++; + } + + if (facpoints != NULL) { + // Unmark all facet vertices. + for (i = 0; i < (int) facpoints->objects; i++) { + pf = * (point *) fastlookup(facpoints, i); + puninfect(pf); + } + } + + // Delete the temp subfaces and faked tets. + for (k = 0; k < 2; k++) { + cavshells = (k == 0 ? topshells : botshells); + if (cavshells != NULL) { + for (i = 0; i < (int) cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + decode(parysh->sh[0], bdrytet); + if (oppo(bdrytet) == dummypoint) { + sym(bdrytet, neightet); + if (neightet.tet != dummytet) { + // This side is a hull face (not an interior subface). + dissolve(neightet); + dummytet[0] = encode(neightet); + tspivot(neightet, checksh); + if (checksh.sh != dummysh) { + assert(checksh.sh != parysh->sh); + // Dis-coonection tet-subface bond. + sesymself(checksh); + stdissolve(checksh); + } + } + // Delete a faked tet. + tetrahedrondealloc(bdrytet.tet); + } + shellfacedealloc(subfaces, parysh->sh); + } + } + } + + topshells->restart(); + if (botshells != NULL) { + botshells->restart(); + } + if (midfaces != NULL) { + midfaces->restart(); + } + // Comment: Now no vertex is marked. + + return mflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// carvecavity() Delete old tets and outer new tets of the cavity. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, + arraypool *botnewtets) +{ + arraypool *newtets; + triface *parytet, *pnewtet, neightet; + face checkseg; //, *parysh; + // int hitbdry; + int i, j, k; + + /*// NOTE: Some subsegments may contained inside the cavity. They must be + // queued for recovery. See fig/dump-cavity-case20. + for (i = 0; i < (int) crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + assert(infected(*parytet)); // SELF_CHECK + if (parytet->tet[8] != NULL) { + for (j = 0; j < 6; j++) { + parytet->loc = edge2locver[j][0]; + parytet->ver = edge2locver[j][1]; + tsspivot1(*parytet, checkseg); + if (checkseg.sh != dummysh) { + if (!sinfected(checkseg)) { + // It is not queued yet. + neightet = *parytet; + hitbdry = 0; + while (1) { + tfnextself(neightet); + if (neightet.tet == dummytet) { + hitbdry++; + if (hitbdry == 2) break; + esym(*parytet, neightet); + tfnextself(neightet); + if (neightet.tet == dummytet) break; + } + if (!infected(neightet)) break; + if (apex(neightet) == apex(*parytet)) break; + } + if (infected(neightet)) { + if (b->verbose > 1) { + printf(" Queue a missing segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + sinfect(checkseg); + subsegstack->newindex((void **) &parysh); + *parysh = checkseg; + } + } + } + } + } + }*/ + + // Delete the old tets in cavity. + for (i = 0; i < (int) crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + tetrahedrondealloc(parytet->tet); + } + crosstets->restart(); // crosstets will be re-used. + + // Collect infected new tets in cavity. + for (k = 0; k < 2; k++) { + newtets = (k == 0 ? topnewtets : botnewtets); + if (newtets != NULL) { + for (i = 0; i < (int) newtets->objects; i++) { + parytet = (triface *) fastlookup(newtets, i); + if (infected(*parytet)) { + crosstets->newindex((void **) &pnewtet); + *pnewtet = *parytet; + } + } + } + } + // Collect all new tets in cavity. + for (i = 0; i < (int) crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + if (i == 0) { + recenttet = *parytet; // Remember a live handle. + } + for (j = 0; j < 4; j++) { + decode(parytet->tet[j], neightet); + if (marktested(neightet)) { // Is it a new tet? + if (!infected(neightet)) { + // Find an interior tet. + assert(neightet.tet != dummytet); // SELF_CHECK + infect(neightet); + crosstets->newindex((void **) &pnewtet); + *pnewtet = neightet; + } + } + } + } + + // Delete outer new tets (those new tets which are not infected). + for (k = 0; k < 2; k++) { + newtets = (k == 0 ? topnewtets : botnewtets); + if (newtets != NULL) { + for (i = 0; i < (int) newtets->objects; i++) { + parytet = (triface *) fastlookup(newtets, i); + if (infected(*parytet)) { + // This is an interior tet. + uninfect(*parytet); + unmarktest(*parytet); + } else { + // An outer tet. Delete it. + tetrahedrondealloc(parytet->tet); + } + } + } + } + + crosstets->restart(); + topnewtets->restart(); + if (botnewtets != NULL) { + botnewtets->restart(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// restorecavity() Reconnect old tets and delete new tets of the cavity. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, + arraypool *botnewtets) +{ + triface *parytet, neightet; + face checksh; + point *ppt; + int i, j; + + // Reconnect crossing tets to cavity boundary. + for (i = 0; i < (int) crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + assert(infected(*parytet)); // SELF_CHECK + if (i == 0) { + recenttet = *parytet; // Remember a live handle. + } + parytet->ver = 0; + for (parytet->loc = 0; parytet->loc < 4; parytet->loc++) { + sym(*parytet, neightet); + // The neighbor may be a deleted faked tet. + if (isdead(&neightet) || (neightet.tet == dummytet)) { + dissolve(*parytet); // Detach a faked tet. + // Remember a boundary tet. + dummytet[0] = encode(*parytet); + } else if (!infected(neightet)) { + bond(*parytet, neightet); + tspivot(*parytet, checksh); + if (checksh.sh != dummysh) { + tsbond(*parytet, checksh); + } + } + } + // Update the point-to-tet map. + parytet->loc = 0; + ppt = (point *) &(parytet->tet[4]); + for (j = 0; j < 4; j++) { + setpoint2tet(ppt[j], encode(*parytet)); + } + } + + // Uninfect all crossing tets. + for (i = 0; i < (int) crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + uninfect(*parytet); + } + + // Delete new tets. + for (i = 0; i < (int) topnewtets->objects; i++) { + parytet = (triface *) fastlookup(topnewtets, i); + tetrahedrondealloc(parytet->tet); + } + + if (botnewtets != NULL) { + for (i = 0; i < (int) botnewtets->objects; i++) { + parytet = (triface *) fastlookup(botnewtets, i); + tetrahedrondealloc(parytet->tet); + } + } + + crosstets->restart(); + topnewtets->restart(); + if (botnewtets != NULL) { + botnewtets->restart(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// splitsubedge() Split a non-Delaunay edge (not a segment) in the // +// surface mesh of a facet. // +// // +// The new point 'newpt' will be inserted in the tetrahedral mesh if it does // +// not cause any existing (sub)segments become non-Delaunay. Otherwise, the // +// new point is not inserted and one of such subsegments will be split. // +// // +// Next,the actual inserted new point is also inserted into the surface mesh.// +// Non-Delaunay segments and newly created subfaces are queued for recovery. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::splitsubedge(point newpt, face *searchsh, arraypool *facfaces, + arraypool *facpoints) +{ + // queue *flipqueue; + triface searchtet; + face splitsh; + face *psseg, sseg; // *parysh; + point pa, pb; + enum locateresult loc; + int s, i; + + // Try to insert the point. Do not insert if it will encroach any segment + // (noencsegflag is TRUE). Queue encroacged subfaces. + assert(subsegstack->objects == 0l); // SELF_CHECK + searchtet = recenttet; // Start search it from recentet + // loc = insertvertexbw(newpt, &searchtet, true, true, true, false); + // Always insert this point, missing segments are queued. 2009-06-11. + loc = insertvertexbw(newpt, &searchtet, true, true, false, false); + + if (loc == ENCSEGMENT) { + // Some segments are encroached. Randomly pick one to split. + assert(subsegstack->objects > 0l); + s = randomnation(subsegstack->objects); + psseg = (face *) fastlookup(subsegstack, s); + sseg = *psseg; + pa = sorg(sseg); + pb = sdest(sseg); + for (i = 0; i < 3; i++) newpt[i] = 0.5 * (pa[i] + pb[i]); + setpointtype(newpt, FREESEGVERTEX); + setpoint2sh(newpt, sencode(sseg)); + // Uninfect all queued segments. + for (i = 0; i < (int) subsegstack->objects; i++) { + psseg = (face *) fastlookup(subsegstack, i); + suninfect(*psseg); + } + subsegstack->restart(); // Clear the queue. + // Split the segment. Two subsegments are queued. + sinsertvertex(newpt, searchsh, &sseg, true, false); + // Insert the point. Missing segments are queued. + searchtet = recenttet; // Start search it from recentet + insertvertexbw(newpt, &searchtet, true, true, false, false); + } else { + /*// Calc an above point for point location in surface triangulation. + calculateabovepoint(facpoints, NULL, NULL, NULL); + // Insert the new point on facet. New subfaces are queued for reocvery. + loc = sinsertvertex(newpt, searchsh, NULL, true, false); + if (loc == OUTSIDE) { + assert(0); // Not handled yet. + } + // Clear the above point. + dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; + */ + // Set the abovepoint of f for point location. + abovepoint = facetabovepointarray[shellmark(*searchsh)]; + if (abovepoint == (point) NULL) { + getfacetabovepoint(searchsh); + } + // Insert the new point on facet. New subfaces are queued for reocvery. + loc = sinsertvertex(newpt, searchsh, NULL, true, false); + if (loc == OUTSIDE) { + assert(0); // Not handled yet. + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// constrainedfacets() Recover subfaces saved in 'subfacestack'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::constrainedfacets2() +{ + arraypool *crosstets, *topnewtets, *botnewtets; + arraypool *topfaces, *botfaces, *midfaces; + arraypool *topshells, *botshells, *facfaces; + arraypool *toppoints, *botpoints, *facpoints; + triface *parytet, searchtet, neightet; + face *pssub, ssub, neighsh; + face checkseg; + point *ppt, pt, newpt; + enum interresult dir; + bool success, delaunayflag; + long bakflip22count; + long cavitycount; + int facetcount; + int bakhullsize; + int s, i, j; + + if (b->verbose) { + printf(" Constraining facets.\n"); + } + + // Initialize arrays. + crosstets = new arraypool(sizeof(triface), 10); + topnewtets = new arraypool(sizeof(triface), 10); + botnewtets = new arraypool(sizeof(triface), 10); + topfaces = new arraypool(sizeof(triface), 10); + botfaces = new arraypool(sizeof(triface), 10); + midfaces = new arraypool(sizeof(triface), 10); + toppoints = new arraypool(sizeof(point), 8); + botpoints = new arraypool(sizeof(point), 8); + facpoints = new arraypool(sizeof(point), 8); + facfaces = new arraypool(sizeof(face), 10); + topshells = new arraypool(sizeof(face), 10); + botshells = new arraypool(sizeof(face), 10); + + bakflip22count = flip22count; + cavitycount = 0; + facetcount = 0; + + // Loop until 'subfacstack' is empty. + while (subfacstack->objects > 0l) { + subfacstack->objects--; + pssub = (face *) fastlookup(subfacstack, subfacstack->objects); + ssub = *pssub; + + if (ssub.sh[3] == NULL) continue; // Skip a dead subface. + + stpivot(ssub, neightet); + if (neightet.tet == dummytet) { + sesymself(ssub); + stpivot(ssub, neightet); + } + + if (neightet.tet == dummytet) { + // Find an unrecovered subface. + smarktest(ssub); + facfaces->newindex((void **) &pssub); + *pssub = ssub; + // Get all subfaces and vertices of the same facet. + for (i = 0; i < (int) facfaces->objects; i++) { + ssub = * (face *) fastlookup(facfaces, i); + for (j = 0; j < 3; j++) { + sspivot(ssub, checkseg); + if (checkseg.sh == dummysh) { + spivot(ssub, neighsh); + assert(neighsh.sh != dummysh); // SELF_CHECK + if (!smarktested(neighsh)) { + // It may be already recovered. + stpivot(neighsh, neightet); + if (neightet.tet == dummytet) { + sesymself(neighsh); + stpivot(neighsh, neightet); + } + if (neightet.tet == dummytet) { + // Add it into list. + smarktest(neighsh); + facfaces->newindex((void **) &pssub); + *pssub = neighsh; + } + } + } + pt = sorg(ssub); + if (!pinfected(pt)) { + pinfect(pt); + facpoints->newindex((void **) &ppt); + *ppt = pt; + } + senextself(ssub); + } // j + } // i + // Have found all facet subfaces (vertices). Uninfect them. + for (i = 0; i < (int) facfaces->objects; i++) { + pssub = (face *) fastlookup(facfaces, i); + sunmarktest(*pssub); + } + for (i = 0; i < (int) facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + puninfect(*ppt); + } + if (b->verbose > 1) { + printf(" Recover facet #%d: %ld subfaces, %ld vertices.\n", + facetcount + 1, facfaces->objects, facpoints->objects); + } + facetcount++; + + // Loop until 'facfaces' is empty. + while (facfaces->objects > 0l) { + // Get the last subface of this array. + facfaces->objects--; + pssub = (face *) fastlookup(facfaces, facfaces->objects); + ssub = *pssub; + + stpivot(ssub, neightet); + if (neightet.tet == dummytet) { + sesymself(ssub); + stpivot(ssub, neightet); + } + + if (neightet.tet != dummytet) continue; // Not a missing subface. + + // Insert the subface. + searchtet.tet = NULL; + dir = scoutsubface(&ssub, &searchtet, 1); + if (dir == SHAREFACE) continue; // The subface is inserted. + assert(dir != COLLISIONFACE); // SELF_CHECK + + // Not exist. Push the subface back into stack. + s = randomnation(facfaces->objects + 1); + facfaces->newindex((void **) &pssub); + *pssub = * (face *) fastlookup(facfaces, s); + * (face *) fastlookup(facfaces, s) = ssub; + + if (dir == EDGETRIINT) continue; // All three edges are missing. + + // Search for a crossing tet. + dir = scoutcrosstet(&ssub, &searchtet, facpoints); + + if (dir == INTERTET) { + // Recover subfaces by local retetrahedralization. + cavitycount++; + bakhullsize = hullsize; + checksubsegs = checksubfaces = 0; + crosstets->newindex((void **) &parytet); + *parytet = searchtet; + // Form a cavity of crossing tets. + formcavity(&ssub, crosstets, topfaces, botfaces, toppoints, + botpoints, facpoints, facfaces); + delaunayflag = true; + // Tetrahedralize the top part. Re-use 'midfaces'. + success = delaunizecavity(toppoints, topfaces, topshells, + topnewtets, crosstets, midfaces); + if (success) { + // Tetrahedralize the bottom part. Re-use 'midfaces'. + success = delaunizecavity(botpoints, botfaces, botshells, + botnewtets, crosstets, midfaces); + if (success) { + // Fill the cavity with new tets. + success = fillcavity(topshells, botshells, midfaces, facpoints); + if (success) { + // Delete old tets and outer new tets. + carvecavity(crosstets, topnewtets, botnewtets); + } + } else { + delaunayflag = false; + } + } else { + delaunayflag = false; + } + if (!success) { + // Restore old tets and delete new tets. + restorecavity(crosstets, topnewtets, botnewtets); + } + /*if (!delaunayflag) { + dump_facetof(&ssub, "facet1.lua"); + while (futureflip != NULL) { + formedgecavity(futureflip->forg, futureflip->fdest, crosstets, + topfaces, toppoints); + crosstets->restart(); + topfaces->restart(); + toppoints->restart(); + futureflip = futureflip->nextitem; + } + flippool->restart(); + outnodes(0); + checkmesh(); + checkshells(1); + assert(0); // Stop the program. + }*/ + hullsize = bakhullsize; + checksubsegs = checksubfaces = 1; + } else if (dir == INTERFACE) { + // Recover subfaces by flipping edges in surface mesh. + recoversubfacebyflips(&ssub, &searchtet, facfaces); + success = true; + } else { // dir == TOUCHFACE + assert(0); + } + if (!success) break; + } // while + + if (facfaces->objects > 0l) { + // Found a non-Delaunay edge, split it (or a segment close to it). + // Create a new point at the middle of this edge, its coordinates + // were saved in dummypoint in 'fillcavity()'. + makepoint(&newpt); + for (i = 0; i < 3; i++) newpt[i] = dummypoint[i]; + setpointtype(newpt, FREESUBVERTEX); + setpoint2sh(newpt, sencode(ssub)); + dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; + // Insert the new point. Starting search it from 'ssub'. + splitsubedge(newpt, &ssub, facfaces, facpoints); + facfaces->restart(); + } + // Clear the list of facet vertices. + facpoints->restart(); + + // Some subsegments may be queued, recover them. + if (subsegstack->objects > 0l) { + b->verbose--; // Suppress the message output. + delaunizesegments2(); + b->verbose++; + } + // Now the mesh should be constrained Delaunay. + } // if (neightet.tet == NULL) + } + + if (b->verbose) { + printf(" %ld subedge flips.\n", flip22count - bakflip22count); + printf(" %ld cavities remeshed.\n", cavitycount); + } + + // Delete arrays. + delete crosstets; + delete topnewtets; + delete botnewtets; + delete topfaces; + delete botfaces; + delete midfaces; + delete toppoints; + delete botpoints; + delete facpoints; + delete facfaces; + delete topshells; + delete botshells; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// formskeleton() Form a constrained tetrahedralization. // +// // +// The segments and facets of a PLS will be recovered. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::formskeleton(clock_t& tv) +{ + triface searchtet; + face *pssub, ssub; + int s, i; + + if (!b->quiet) { + printf("Recovering boundaries.\n"); + } + + caveshlist = new arraypool(sizeof(face), 10); + caveshbdlist = new arraypool(sizeof(face), 10); + + // Put all segments into the list. + if (b->nojettison == 1) { // '-J' option (for debug) + // The sequential order. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + ssub.sh = shellfacetraverse(subsegs); + sinfect(ssub); // Only save it once. + subsegstack->newindex((void **) &pssub); + *pssub = ssub; + } + } else { + // Randomly order the segments. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + s = randomnation(i + 1); + // Move the s-th seg to the i-th. + subsegstack->newindex((void **) &pssub); + *pssub = * (face *) fastlookup(subsegstack, s); + // Put i-th seg to be the s-th. + ssub.sh = shellfacetraverse(subsegs); + sinfect(ssub); // Only save it once. + pssub = (face *) fastlookup(subsegstack, s); + *pssub = ssub; + } + } + + // Segments will be introduced. + checksubsegs = 1; + // Recover segments. + delaunizesegments2(); + + tv = clock(); + + // Randomly order the subfaces. + subfaces->traversalinit(); + for (i = 0; i < subfaces->items; i++) { + s = randomnation(i + 1); + // Move the s-th subface to the i-th. + subfacstack->newindex((void **) &pssub); + *pssub = * (face *) fastlookup(subfacstack, s); + // Put i-th subface to be the s-th. + ssub.sh = shellfacetraverse(subfaces); + pssub = (face *) fastlookup(subfacstack, s); + *pssub = ssub; + } + + // Subfaces will be introduced. + checksubfaces = 1; + // Recover facets. + constrainedfacets2(); + + delete caveshlist; + delete caveshbdlist; + caveshlist = NULL; + caveshbdlist = NULL; + + // Detach all segments from tets. + tetrahedrons->traversalinit(); + searchtet.tet = tetrahedrontraverse(); + while (searchtet.tet != (tetrahedron *) NULL) { + if (searchtet.tet[8] != NULL) { + for (i = 0; i < 6; i++) { + searchtet.loc = edge2locver[i][0]; + searchtet.ver = edge2locver[i][1]; + tssdissolve1(searchtet); + } + searchtet.tet[8] = NULL; + } + searchtet.tet = tetrahedrontraverse(); + } + // Now no segment is bonded to tets. + checksubsegs = 0; + // Delete the memory. + tet2segpool->restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 > 1) { + 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 > 1) { + 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) || (casingout.sh == dummysh)) { + // 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 > 1) { + 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 > 1) { + 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) { + 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) { + 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 exterior tetrahedra.\n"); + if ((b->verbose > 1) && (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->verbose > 1) { + 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; + + // Update the point-to-tet map. + makepoint2tetmap(); + + 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; +} + +//// //// +//// //// +//// constrained_cxx ////////////////////////////////////////////////////////// + +//// steiner_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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, list *ptlist, list* glueshlist) +{ + triface neightet, casingtet; + triface faketet; + face worksh; + point *ppt; + int i, j; + + // Infect all points of the re-triangulated cavity. + for (i = 0; i < ptlist->len(); i++) { + ppt = (point *)(* ptlist)[i]; + pinfect(*ppt); + } + + // 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 + // Do not insert it if some of its vertices are not in Mesh. + ppt = (point *) &(worksh.sh[3]); + for (j = 0; j < 3; j++) { + if (!pinfected(ppt[j])) break; + } + if (j < 3) { + // Found a subface lies outside the cavity. See an example in + // dump-SteinerRemoval-case2.lua. + // Add this subface in glueshlist (to process it later). + glueshlist->append(&worksh); + // Do not add this face into frontlist. + continue; + } + // 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); + } + } + + // Uninfect all points of the re-triangulated cavity. + for (i = 0; i < ptlist->len(); i++) { + ppt = (point *)(* ptlist)[i]; + puninfect(*ppt); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::delaunizecavvertices(triface* oldtet, list* floorptlist, + list* ceilptlist, list* newtetlist, queue* flipque) +{ + point *insertarray; + triface bakhulltet, newtet; + long bakhullsize; + long arraysize; + bool success; + 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. + success = incrflipdelaunay(oldtet, insertarray, arraysize, false, false, + 0.0, flipque); + + delete [] insertarray; + + if (success) { + // 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++; + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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); + } + // It is possible that all tets are deleted. Check it. 2009-07-27. + if (newtetlist->len() == 0) { + // We must add a live tet to the list for the retrieving. + decode(dummytet[0], searchtet); + assert(searchtet.tet != dummytet); + assert(!isdead(&searchtet)); + infect(searchtet); + newtetlist->append(&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); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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) +{ + triface spintet; + face checksh; // For debug. + point pa, pb, pc; + enum finddirectionresult col; + int hitbdry; + + // Let the front we're searching is abc. + pa = org(*front); + pb = dest(*front); + + point2tetorg(pa, *idfront); + assert(org(*idfront) == pa); + recenttet = *idfront; + + // Search a tet having edge ab. + col = finddirection(idfront, pb, tetrahedrons->items); + 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); + } else if (col == BELOWHULL) { + // This front must be a dangling subface outside the cavity. + // See an example in dump-SteinerRemoval-case2.lua. + assert(0); + } + + 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 > 1) { + 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. // +// // +// A tet containing this front and not in the cavity is added into 'gluetet- // +// list' (either f or f1). It will be used to maintain the point-to-tet map. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::gluefronts(triface* front, triface* front1, list* gluetetlist, + list *glueshlist) +{ + 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); + // Save this subface if it is not a temp subface. In case the mesh cavity + // fails, we need to restore the original state. + if (!isdead(&consh)) { + // Save this subface into list. + glueshlist->append(&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. + // Add f into list. + gluetetlist->append(front); + } + // 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); + // Save this subface if it is not a temp subface. In case the mesh + // cavity fails, we need to restore the original state. + if (!isdead(&consh)) { + // Save this subface into list. + glueshlist->append(&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. + // Save this subface if it is not a temp subface. In case the mesh + // cavity fails, we need to restore the original state. + if (!isdead(&consh)) { + // Save this subface into list. + glueshlist->append(&consh); + } + } + // Add f1 into list. + gluetetlist->append(front1); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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* gluetetlist, list* glueshlist) +{ + triface front, front1, tfront; + triface idfront, neightet; + face auxsh, checksh; + int len, i, j; + + misfrontlist->clear(); + + // Identify all fronts in D. + for (i = 0; i < frontlist->len(); i++) { + // Get a front f. + front = * (triface *)( *frontlist)[i]; + if (scoutfront(&front, &idfront)) { + // Found f. Insert an aux subface s. + tspivot(idfront, auxsh); + if (auxsh.sh != dummysh) { // Does s already exist? + // 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, gluetetlist, glueshlist); + } 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, gluetetlist, glueshlist); + } 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); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::carvecavity(list* newtetlist, list* outtetlist, + list* gluetetlist, queue* flipque) +{ + triface newtet, neightet, front, intet, outtet, oldtet; + face auxsh, consh; + point pa, pb, pc; + point pointptr; + REAL ori; + bool success; + int i; + + // Clear work list. + outtetlist->clear(); + success = true; + + // Classify in- and out- tets in D. Mark and queue classified out-tets. + for (i = 0; i < newtetlist->len() && success; i++) { + // Get a new tet t. + newtet = * (triface *)(* newtetlist)[i]; + assert(!isdead(&newtet)); + // Skip an infected tet (it's an out tet). + if (!infected(newtet)) { + // Look for aux subfaces attached at t. + for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) { + tspivot(newtet, auxsh); + if (auxsh.sh != dummysh) { + // Get the front f. + decode((tetrahedron) auxsh.sh[0], front); + // Let f face to the inside of C. + adjustedgering(front, CW); + pa = org(front); + pb = dest(front); + pc = apex(front); + // Has this side a neighbor n? + sym(newtet, neightet); + if ((neightet.tet != dummytet) && !infected(neightet)) { + // Classify t and n (one is "in" and another is "out"). + ori = orient3d(pa, pb, pc, oppo(newtet)); + if (ori == 0.0) { + printf("Internal error at front %d.\n", i); + assert(0); + } + if (ori < 0.0) { + // t is in-tet. n is out-tet. + outtet = neightet; + intet = newtet; + } else { + // n is in-tet. t is out-tet. + outtet = newtet; + intet = neightet; + } + if (!infected(outtet)) { + // Check the special case: if this tet is protected by four + // subfaces, i.e., all 4 faces of this tet are fronts. + // See an example in dbg/dump-SteinerRemoval-case3.lua + neightet = outtet; + for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { + tspivot(neightet, auxsh); + if (auxsh.sh == dummysh) break; + } + if (neightet.loc < 4) { + // It is an outside tet. Add it into list. + infect(outtet); + outtetlist->append(&outtet); + } + } + } else { + intet = newtet; + } + // Make sure that the intet is not iversed. + ori = orient3d(pa, pb, pc, oppo(intet)); + assert(ori != 0); + if (ori > 0) { + // Found an inversed inside tet. Stop and return. + if (b->verbose > 1) { + printf(" Intet x%lx %d (%d, %d, %d, %d) is iversed.\n", + (unsigned long) intet.tet, intet.loc, pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(oppo(intet))); + } + success = false; + break; + } + } else { + // This side is not protected. Check if it is a hull face. + // Comment: This check is necessary. It is possible that all + // protected subfaces have been moved in 'gluetetlist'. + // If so, without this check, the newtets become orphans + // and remain in the output. 2009-07-29. + sym(newtet, neightet); + if (neightet.tet == dummytet) { + // Found an out tet. + if (!infected(newtet)) { + infect(newtet); + outtetlist->append(&newtet); + } + break; + } + } + } // for (newtet.loc) + } // if (!infected) + } + + if (!success) { + // Found inversed tet. The carvecavity failed. + for (i = 0; i < outtetlist->len(); i++) { + outtet = * (triface *)(* outtetlist)[i]; + uninfect(outtet); + } + outtetlist->clear(); + return false; + } + + // 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]; + assert(!isdead(&outtet)); + // 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. + if (infected(neightet)) { + printf("Error: A front face (%d, %d, %d) x%lx got deleted.\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet)), (unsigned long) auxsh.sh); + printf(" p:draw_tet(%d, %d, %d, %d) -- in\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet)), pointmark(oppo(neightet))); + printf(" p:draw_tet(%d, %d, %d, %d) -- out\n", + pointmark(org(outtet)), pointmark(dest(outtet)), + pointmark(apex(outtet)), pointmark(oppo(outtet))); + assert(0); + } + // 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); + } + // Update point-to-tet map. + pointptr = org(front); + setpoint2tet(pointptr, encode(newtet)); + pointptr = dest(front); + setpoint2tet(pointptr, encode(newtet)); + pointptr = apex(front); + setpoint2tet(pointptr, encode(newtet)); + // 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); + } + // Detach the fake tet from its old cavity tet. This is necessary + // in case the mesh of other cavities are failed, and we have to + // restore the original status. 2009-07-24. + sym(front, oldtet); + if (oldtet.tet != dummytet) { + assert(infected(oldtet)); + dissolve(oldtet); + } + // 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. + + // Maintain point-to-tet map. + for (i = 0; i < gluetetlist->len(); i++) { + // Get a new tet t. + newtet = * (triface *)(* gluetetlist)[i]; + if (isdead(&newtet)) { + assert(0); + } + pointptr = org(newtet); + setpoint2tet(pointptr, encode(newtet)); + pointptr = dest(newtet); + setpoint2tet(pointptr, encode(newtet)); + pointptr = apex(newtet); + setpoint2tet(pointptr, encode(newtet)); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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) { + if (casingout.sh != dummysh) { + if (oldsh.sh == casingout.sh) { + // A subface is self-bounded. Not possible. + assert(0); // DEBUG + } + // A segment. Insert s into the face ring, ie, s_in->s->s_out. + 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 { + sbond(newsh, casingout); + } + // 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, l; + int i; + + // Calculate the normal of 'orientsh'. + pa = sorg(*orientsh); + pb = sdest(*orientsh); + pc = sapex(*orientsh); + // facenormal(pa, pb, pc, norm, &len); + facenormal2(pa, pb, pc, norm, 1); + // for (i = 0; i < 3; i++) ref[i] = pa[i] + norm[i]; + len = sqrt(norm[0]*norm[0]+norm[1]*norm[1]+norm[2]*norm[2]); + for (i = 0; i < 3; i++) norm[i] /= len; + // Get the longest edge length of [a, b, c] + len = distance(pa, pb); + l = distance(pb, pc); + if (len < l) len = l; + l = distance(pc, pa); + if (len < l) len = l; + // Calculate a local above point. + for (i = 0; i < 3; i++) ref[i] = pa[i] + len * norm[i]; + + // 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); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// registerelemflip() Register an elementary flip T23, T32, or T22. // +// // +// If return TRUE, the flip is registered, otherwise, return FALSE, which // +// means a conflict, this flip already exists. // +// // +// Depending on the flip type, not all input points are used, those unused // +// points are set to be dummypoint for easily processing. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::registerelemflip(enum fliptype ft, point pa1, point pb1, + point pc1, point pa2, point pb2, point pc2) +{ + elemflip *ef; + bool rflag; + int i; + + rflag = false; // The flip has not registed yet. + + pinfect(pa1); + pinfect(pb1); + pinfect(pc1); + pinfect(pa2); + pinfect(pb2); + pinfect(pc2); + + // Search the list for a registered flip. + for (i = 0; i < (int) elemfliplist->objects; i++) { + ef = (elemflip *) fastlookup(elemfliplist, i); + if (ef->ft == ft) { + rflag = (pinfected(ef->pset1[0]) && pinfected(ef->pset1[1]) && + pinfected(ef->pset1[2])); + if (rflag) { + rflag = (pinfected(ef->pset2[0]) && pinfected(ef->pset2[1]) && + pinfected(ef->pset2[2])); + if (rflag) { + break; // This flip has been registed before. + } + } + } + } + + puninfect(pa1); + puninfect(pb1); + puninfect(pc1); + puninfect(pa2); + puninfect(pb2); + puninfect(pc2); + + if (rflag) { + if (b->verbose > 1) { + printf(" Flip: %s", ft == T23 ? "T23" : (ft == T32 ? "T32" : "T22")); + printf(" (%d, %d, %d) - (%d, %d, %d) is registered.\n", pointmark(pa1), + pointmark(pb1), pointmark(pc1), pointmark(pa2), pointmark(pb2), + pointmark(pc2)); + } + return false; + } + + // Register this flip. + elemfliplist->newindex((void **) &ef); + ef->ft = ft; + ef->pset1[0] = pa1; + ef->pset1[1] = pb1; + ef->pset1[2] = pc1; + ef->pset2[0] = pa2; + ef->pset2[1] = pb2; + ef->pset2[2] = pc2; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// check4fixededge() Check if the given edge [a, b] is a fixed edge. // +// // +// A fixed edge is saved in the "fixededgelist". Return TRUE if [a, b] has // +// already existed in the list, otherwise, return FALSE. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::check4fixededge(point pa, point pb) +{ + point *ppt; + int i; + + pinfect(pa); + pinfect(pb); + + for (i = 0; i < (int) fixededgelist->objects; i++) { + ppt = (point *) fastlookup(fixededgelist, i); + if (pinfected(ppt[0]) && pinfected(ppt[1])) { + if (b->verbose > 1) { + printf(" Edge (%d, %d) is fixed.\n", pointmark(pa), + pointmark(pb)); + } + break; // This edge already exists. + } + } + + puninfect(pa); + puninfect(pb); + + return i < (int) fixededgelist->objects; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removeedgebyflips() Remove an edge by flips. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::removeedgebyflips(triface* remedge, int *flipcount) +{ + triface abcd; //, badc; // Tet configuration at edge ab. + // triface baccasing, abdcasing; + triface abtetlist[21]; // Old configuration at ab, save maximum 20 tets. + triface bftetlist[21]; // Old configuration at bf, save maximum 20 tets. + triface newtetlist[90]; // New configuration after removing ab. + face checksh; + point pa, pb; + bool remflag, subflag; + int n, n1, m, i; //, j; + + triface newtet; // For update point-to-tet map. + point *ppt; + int j; + + pa = org(*remedge); + pb = dest(*remedge); + + if (b->verbose > 1) { + printf(" Remove edge (%d, %d).\n", pointmark(pa), pointmark(pb)); + } + + // Get the tets configuration at ab. Collect maximum 10 tets. + subflag = false; + abcd = *remedge; + adjustedgering(abcd, CW); + n = 0; + abtetlist[n] = abcd; + do { + // Is the list full? + if (n == 20) 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. + if (b->verbose > 1) { + printf(" Can't remove a fixed face (%d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(apex(abtetlist[n]))); + } + subflag = true; break; // return false; + } + // Get the next tet at ab. + if (!fnext(abtetlist[n], abtetlist[n + 1])) { + // This edge is on the hull (2-to-2 flip case). + subflag = true; break; // assert(0); // Not handled yet. + } + n++; + } while (apex(abtetlist[n]) != apex(abcd)); + + if (subflag) { + // The face link contains subfaces, stop. + return false; + } + + // Do not flip this edge if it has been fixed (for recovering a face). + if (check4fixededge(pa, pb)) { + return false; + } + + // 2 < n < 20. + if (n == 3) { + // There are three tets at ab. Try to do a flip32 at ab. + remflag = removeedgebyflip32(NULL, abtetlist, newtetlist, NULL); + } else if ((n > 3) && (n <= b->maxflipedgelinksize)) { + // Four tets case. Try to do edge transformation. + remflag = removeedgebytranNM(NULL,n,abtetlist,newtetlist,NULL,NULL,NULL); + } else { + if (b->verbose > 1) { + printf(" !! Unhandled case: n = %d.\n", n); + } + remflag = false; + } + + if (remflag) { + // 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", n, m); + } + // Update the point-to-tet map + for (i = 0; i < m; i++) { + newtet = newtetlist[i]; + ppt = (point *) &(newtet.tet[4]); + for (j = 0; j < 4; j++) { + setpoint2tet(ppt[j], encode(newtet)); + } + } + *flipcount = *flipcount + 1; + return true; + } + + if (n <= b->maxflipedgelinksize) { + // Try to do a combination of flips. + n1 = 0; + remflag = removeedgebycombNM(NULL, n, abtetlist, &n1, bftetlist, + newtetlist, NULL); + if (remflag) { + // 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); + printf("\n"); + } + // Update the point-to-tet map + for (i = 0; i < m; i++) { + newtet = newtetlist[i]; + ppt = (point *) &(newtet.tet[4]); + for (j = 0; j < 4; j++) { + setpoint2tet(ppt[j], encode(newtet)); + } + } + *flipcount = *flipcount + 1; + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removefacebyflips() Remove a face by a sequence of flips. // +// // +// The face should not be a subface. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::removefacebyflips(triface* remface, int *flipcount) +{ + triface neightet, checkface1, checkface2; + face checksh1, checksh2; + point pa, pb, pc, pd, pe; + enum interresult dir; + REAL ori; + int types[2], poss[4]; + int i; + + adjustedgering(*remface, CCW); + sym(*remface, neightet); + if (neightet.tet == dummytet) { + // A boundary face. It is not flipable. + return false; + } + pd = oppo(*remface); + pe = oppo(neightet); + + if (b->verbose > 1) { + printf(" Remove face (%d, %d, %d) %d, %d\n", pointmark(org(*remface)), + pointmark(dest(*remface)), pointmark(apex(*remface)), + pointmark(pd), pointmark(pe)); + } + + // Do not remove this face if it is a fixed face. + tspivot(*remface, checksh1); + if (checksh1.sh != dummysh) { + if (b->verbose > 1) { + printf(" Can't remove a fixed face (%d, %d, %d)\n", + pointmark(org(*remface)), pointmark(dest(*remface)), + pointmark(apex(*remface))); + } + return false; + } + + // Check if edge [d, e] intersects the flip face [a, b, c]. + for (i = 0; i < 3; i++) { + pa = org(*remface); + pb = dest(*remface); + pc = apex(*remface); + ori = orient3d(pa, pb, pd, pe); + if (ori <= 0) break; // Coplanar or Above. + enextself(*remface); + } + + if (i == 3) { + // A 2-to-3 flip is found. + // Regist the flipping face. + if (!registerelemflip(T23, pa, pb, pc, pd, pe, dummypoint)) { + // Detected a potential flip loop. + return false; + } + // Do a 2-to-3 flip. + flip23(remface, NULL); + *flipcount = *flipcount + 1; + return true; + } + + if (ori == 0) { + // Check if [a, b] is a hull edge. If so a flip22() could apply. + fnext(*remface, checkface1); + tspivot(checkface1, checksh1); + symedge(*remface, neightet); + fnext(neightet, checkface2); + tspivot(checkface2, checksh2); + // First check if it is a protected face. + if ((checksh1.sh == dummysh) && (checksh2.sh == dummysh)) { + // Check if it is a hull face. + symself(checkface1); + symself(checkface2); + if ((checkface1.tet == dummytet) && (checkface2.tet == dummytet)) { + // Check if the edge [a, b] intersects [d, e] in its interior. + if (tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss)) { + dir = (enum interresult) types[0]; + if (dir == INTEREDGE) { + // Edge [d, e] intersects [a, b, c]. A flip 2-to-2 is found. + // Check if the edge [a, b] is a fixed one (for recovering a face). + if (check4fixededge(pa, pb)) { + // [a, b] is a fixed edge. Stop the flip. + return false; + } + // Regist this flip. + if (!registerelemflip(T22, pa, pb, dummypoint, pd, pe, + dummypoint)) { + // Detected a potential flip loop. + return false; + } + // We can flip this edge [a, b]. + flip22(remface, NULL); + *flipcount = *flipcount + 1; + return true; + } else { + // Either a or b is collinear with edge [d, e]. NOT flipable. + assert(dir == INTERVERT); + return false; + } + } else { + // [a,b] does not intersect with [d, e]. Don't do a 2-to-2 flip. + // See an example in dbg/dump-nflip22-case.lua + return false; + } + } + } else { + if (b->verbose > 1) { + printf(" Can't remove a fixed face (%d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(apex(neightet))); + } + return false; + } + } + + // The edge [d, e] does not intersect [a, b, c]. Try to flip edge [a, b]. + // Comment: We've found that the edge [a, b] is locally non-convex. It + // must be an interior edge. + return removeedgebyflips(remface, flipcount); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoveredgebyflips() Recover edge [a, b] by a sequence of flips. // +// // +// The edge to be recovered is from a = org(*searchtet) to b. Return TRUE if // +// the edge [a, b] is recovered, and retruned in 'searchtet', otherwise, // +// return FALSE. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::recoveredgebyflips(triface *searchtet,point pb,int *flipcount) +{ + triface remface; + point pa; + enum interresult dir; + bool success; + + pa = org(*searchtet); + + if (b->verbose > 1) { + printf(" Recover edge (%d, %d)\n", pointmark(pa), pointmark(pb)); + } + + assert(elemfliplist->objects == 0l); + + while (1) { + + // Go to the intersected face, try to flip it. + enextfnext(*searchtet, remface); + + // Try to remove this crossing face. + success = removefacebyflips(&remface, flipcount); + if (!success) break; + + point2tetorg(pa, *searchtet); + assert(org(*searchtet) == pa); + dir = finddirection2(searchtet, pb); + if (dir == INTERVERT) { + break; // The edge has found. + } + + } // while (1) + + // Clear the recorded flip list. + elemfliplist->restart(); + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoverfacebyflips() Recover a missing face by a sequence of flips. // +// // +// Assume that at least one of the edges of the face exists in the current // +// mesh. The face is recovered by continusly removing all crossing edges of // +// this face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::recoverfacebyflips(triface* front, int *flipcount) +{ + triface searchtet, spintet, bdrytet; + triface remedge, remface; + point pa, pb, pc, pd, pe, *ppt; + enum interresult dir; + bool success; + int hitbdry; + int i; + + if (b->verbose > 1) { + printf(" Recover face (%d, %d, %d)\n", pointmark(org(*front)), + pointmark(dest(*front)), pointmark(apex(*front))); + } + + assert(fixededgelist->objects == 0l); + + // First recover three edges of this face. + for (i = 0; i < 3; i++) { + pa = org(*front); + pb = dest(*front); + pc = apex(*front); // The apex. + point2tetorg(pa, searchtet); + assert(org(searchtet) == pa); + dir = finddirection2(&searchtet, pb); + if (dir == BELOWHULL2) { + // Detect an outside front. This happens when a new subface created by + // re-triangulating is actually outside the cavity. The gluefront() + // is not called in this case. There must be an existing face which + // lies outside the cavity that matches this subface. + // An example is dump-SteinerRemoval-case2.lua. 2009-07-06. + // fixededgelist->restart(); + // return false; + assert(0); + } + if (dir != INTERVERT) { + if (!recoveredgebyflips(&searchtet, pb, flipcount)) { + // Failed to recover edge [a, b], so does the face. + fixededgelist->restart(); + return false; + } + } + // DIR == INTERVERT + if (dest(searchtet) != pb) { + // There eixsts a collonear edge but not the desired one. + // Failed to recover edge [a, b], so does the face. + fixededgelist->restart(); + return false; + } + // Save this edge to avoid flipping it away. + fixededgelist->newindex((void **) &ppt); + ppt[0] = pa; + ppt[1] = pb; + // Go to the next edge. + enextself(*front); + } + + assert(elemfliplist->objects == 0l); + + while (1) { + + success = false; + + // Adjust edge [a->b] to be in the CCW edge ring. + adjustedgering(searchtet, CCW); + if (org(searchtet) != pa) { + fnextself(searchtet); + esymself(searchtet); + } + assert(org(searchtet) == pa); + assert(dest(searchtet) == pb); + + spintet = searchtet; + hitbdry = 0; + do { + if (apex(spintet) == pc) { + // Found abc. Insert an auxilary subface s at idfront. + insertauxsubface(front, &spintet); + success = true; + break; // return true; + } + if (!fnextself(spintet)) { + bdrytet = spintet; // Save the boundary face. + hitbdry ++; + if (hitbdry < 2) { + esym(searchtet, spintet); + if (!fnextself(spintet)) { + hitbdry++; + } + } + } + if (apex(spintet) == apex(searchtet)) break; + } while (hitbdry < 2); + + if (success) break; + + if (hitbdry > 0) { + // Adjust searchtet to be the boundary face at edge [pa->pb]. + searchtet = bdrytet; + adjustedgering(searchtet, CCW); + pa = org(searchtet); + pb = dest(searchtet); + } + + // Comments: + // Remember that 'front' is the face [a, b, c]. 'spintet' is a tet + // [a, b, d, e] at edge [a->b]. + // We first check if the edge [d, e] intersects face [a, b, c], if it + // does, we try to flip the edge [d, e] away. + // We then check if the edge [b, c] intersects face [a, d, e], if it + // does, we try to flip the face [a, d, e] away. + // We then check if the edge [a, c] intersects face [b, d, e], if it + // does, we try to flip the face [b, d, e] away. + + // Search a crossing edge/face and try to flip it away. + remedge.tet = remface.tet = NULL; + spintet = searchtet; + hitbdry = 0; + do { + pd = apex(spintet); + pe = oppo(spintet); + // Check if edge [d, e] intersects [a, b, c]. + if (tri_edge_test(pa, pb, pc, pd, pe, NULL, 0, NULL, NULL)) { + remedge = spintet; + enextfnextself(remedge); + enextself(remedge); // Edge [pd->pe]. + break; + } + // Check if [b, c] intersects [a, d, e]. + if (!iscollinear(pa, pd, pe, b->epsilon)) { + if (tri_edge_test(pa, pd, pe, pb, pc, NULL, 0, NULL, NULL)) { + remface = spintet; + enext2fnextself(remface); // Face [a, d, e]. + break; + } + } + if (!iscollinear(pb, pd, pe, b->epsilon)) { + // Check if [a, c] intersects [b, d, e]. + if (tri_edge_test(pb, pd, pe, pa, pc, NULL, 0, NULL, NULL)) { + remface = spintet; + enextfnextself(remface); // Face [b, d, e]. + break; + } + } + tfnextself(spintet); + if (spintet.tet == dummytet) { + break; // Meet boundary. + } + if (apex(spintet) == apex(searchtet)) break; + } while (hitbdry < 2); + + if (remedge.tet != NULL) { + // Try to remove this crossing edge. + success = removeedgebyflips(&remedge, flipcount); + } else if (remface.tet != NULL) { + /*// Do not flip it if it is a subface. + tspivot(remface, checksh); + if (checksh.sh != dummysh) { + return false; + }*/ + success = removefacebyflips(&remface, flipcount); + } else { + // Haven't found a crossing edge or face. + success = false; + } + + if (!success) break; + + point2tetorg(pa, searchtet); + assert(org(searchtet) == pa); + dir = finddirection2(&searchtet, pb); + assert(dest(searchtet) == pb); + + } // while (1) + + // Clear the recored flips. + elemfliplist->restart(); + // Clear the fixed edges. + fixededgelist->restart(); + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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, list* gluetetlist, list* glueshlist, queue* flipque) +{ + triface misfront, newtet; + point pointptr; // Used with gluetetlist. + bool success; + int misfacecount; + int flipcount, totalflipcount; + int i; + + if (b->verbose > 1) { + printf(" Constrained cavity (%d floors, %d ceilings, %d vertices).\n", + floorlist->len(), ceillist->len(), ptlist->len()); + } + + // Initialize the cavity C. + initializecavity(floorlist, ceillist, frontlist, ptlist, glueshlist); + // Form the D of the vertices of C. + success = delaunizecavvertices(oldtet, ptlist, NULL, newtetlist, flipque); + + if (success) { + // Identify faces of C in D. + if (!identifyfronts(frontlist, misfrontlist, gluetetlist, glueshlist)) { + // Some faces are missing. + totalflipcount = 0; + // Try to recover missing faces by flips. + do { + misfacecount = misfrontlist->len(); + flipcount = 0; + 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 (recoverfront(&misfront, newtetlist, flipque)) { + if (recoverfacebyflips(&misfront, &flipcount)) { + // f has been recovered. + frontlist->append(&misfront); + misfrontlist->del(i, 0); i--; + } + } + totalflipcount += flipcount; + // Have all faces been recovered? + if (misfrontlist->len() == 0) break; + // Continue the loop if some missing faces have been recovered. + } while (misfacecount > misfrontlist->len()); + // Retrieve new tets and purge dead tets in D. + retrievenewtets(newtetlist); + } + success = (misfrontlist->len() == 0); + } // if (success) + + if (success) { + // 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. + if (carvecavity(newtetlist, misfrontlist, gluetetlist, NULL)) { + return true; + } + } + + // { + // 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); + } + // Some fronts have been removed from the front list (in gluefronts()). + // Maintain point-to-tet map. + for (i = 0; i < gluetetlist->len(); i++) { + // Get a tet t which lies outside the current cavity. + newtet = * (triface *)(* gluetetlist)[i]; + if (isdead(&newtet)) { + assert(0); + } + pointptr = org(newtet); + setpoint2tet(pointptr, encode(newtet)); + pointptr = dest(newtet); + setpoint2tet(pointptr, encode(newtet)); + pointptr = apex(newtet); + setpoint2tet(pointptr, encode(newtet)); + } + return false; + // } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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::findrelocatepoint2(point steinpt, point relocatept, + REAL* normal, list* frontlist, list* oldtetlist) +{ + triface oldtet, front; + point pa, pb, pc; + REAL farpt[3], fcent[3], candpt[3], searchpt[3]; + REAL dist, factor; + REAL ori, stepx, minvol, minvol1; + bool stopflag; + // int iter; + int i, j; + + if (b->verbose > 1) { + printf(" Find new location for point %d.\n", pointmark(steinpt)); + } + + // Calculate a far enough point on the normal direction. + for (i = 0; i < 3; i++) { + farpt[i] = steinpt[i] + longest * normal[i]; + } + + // Find the face in oldtet intersecting with the line steinpt->farpt. + for (i = 0; i < oldtetlist->len(); i++) { + oldtet = * (triface *)(* oldtetlist)[i]; + pa = org(oldtet); + pb = dest(oldtet); + pc = apex(oldtet); + if (tri_edge_test(pa, pb, pc, steinpt, farpt, farpt, 0, NULL, NULL)) { + // projpt2face(steinpt, pa, pb, pc, fcent); + // Find the intersected face. Calculate the intersection. + planelineint(pa, pb, pc, steinpt, farpt, fcent, &factor); + if (factor != 0) { // assert(factor != 0); + if (b->verbose > 1) { + printf("p:show_vector(%g, %g, %g, %g, %g, %g) -- L\n", steinpt[0], + steinpt[1], steinpt[2], fcent[0], fcent[1], fcent[2]); + } + break; + } + } + } + // There must be an interseced face. + if (i == oldtetlist->len()) { + return false; // assert(0); + } + + // Start search a relocating point which maximize the min. vol. + dist = distance(steinpt, fcent); + stepx = dist / 100.0; // Divide the segment into 100 pieces. + minvol = 0; + stopflag = false; + for (i = 1; i < 100 && !stopflag; i++) { + // Calculate a candidate point. + for (j = 0; j < 3; j++) { + candpt[j] = steinpt[j] + (stepx * (double) i) * (fcent[j] - steinpt[j]); + } + minvol1 = 0; + for (j = 0; j < frontlist->len(); j++) { + front = * (triface *)(* frontlist)[j]; + // Let f face inside C. (f is a face of tet adjacent to C). + adjustedgering(front, CW); + pa = org(front); + pb = dest(front); + pc = apex(front); + ori = orient3d(pa, pb, pc, candpt); + if (ori >= 0) { + // An invisible front. (1) fails. + stopflag = true; + break; + } + if (j == 0) { + minvol1 = -ori; + } else { + if (minvol1 > (-ori)) { + minvol1 = -ori; + } + } + } // j + if (!stopflag) { + if (minvol < minvol1) { + // The min. vol. is improved by this candidate. Choose it. + for (j = 0; j < 3; j++) searchpt[j] = candpt[j]; + minvol = minvol1; + } else { + // The min. vol. begins to decrease. Stop the search. + stopflag = true; + } + } + } // i + + if (minvol > 0) { + // We've found a valid relocation. Choose it. + if (b->verbose > 1) { + printf("p:show_vector(%g, %g, %g, %g, %g, %g) -- Relo\n", steinpt[0], + steinpt[1], steinpt[2], searchpt[0], searchpt[1], searchpt[2]); + } + for (i = 0; i < 3; i++) relocatept[i] = searchpt[i]; + return true; + } else { + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool 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); + } + } + // Update the point-to-tet map. + pa = org(front); + setpoint2tet(pa, encode(newtet)); + pa = dest(front); + setpoint2tet(pa, encode(newtet)); + pa = apex(front); + setpoint2tet(pa, encode(newtet)); + setpoint2tet(steinpt, encode(newtet)); + // 'front' may be a 'fake' tet. + tspivot(front, checksh); + if (oppo(front) == (point) NULL) { + if (checksh.sh != dummysh) { + stdissolve(checksh); + } + // Detach the fake tet from its old cavity tet. This is necessary + // in case the mesh of other cavities are failed, and we have to + // restore the original status. 2009-07-28. + sym(front, neightet); + if (neightet.tet != dummytet) { + assert(infected(neightet)); + dissolve(neightet); + } + // 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'. + // The following code should be re-written. 2009-07-30. + 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); + } + } + if (!bdflag) { + assert(0); // break; // The relocation failed. + } + } + enextself(newtet); + } + if (j < 3) break; + } + + if (i < newtetlist->len()) { + // Relocation failed. Delete new tets. + for (i = 0; i < newtetlist->len(); i++) { + newtet = * (triface *)(* newtetlist)[i]; + tetrahedrondealloc(newtet.tet); + } + return false; + } + + if (flipque != (queue *) NULL) { + // Recover locally Delaunay faces. + lawson3d(flipque); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. + } else { + // A non-collapse tet. Update point-to-tet map. + setpoint2tet(pa, encode(oldtet)); + setpoint2tet(pb, encode(oldtet)); + setpoint2tet(pc, encode(oldtet)); + setpoint2tet(conpt, encode(oldtet)); + } + } + // 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; + point *ppt; + int i, j; + + 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) { + assert(!isdead(&neightet)); // SELF_CHECK 2009-07-24 + 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. + tsbond(oldtet, checksh); + // Let dummytet[0] points to it. + dummytet[0] = encode(oldtet); + } + } + // Update the point-to-tet map. + ppt = (point *) &(oldtet.tet[4]); + for (j = 0; j < 4; j++) { + setpoint2tet(ppt[j], 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; + list *gluetetlist; + list *glueshlist; + triface oldtet, newtet, neightet; + face oldsh, newsh; + point suppt, newpt[2]; + point *cons, *ppt; + enum interresult dir; + REAL norm[3]; + bool success; + int bakchecksubfaces; + 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; + } + gluetetlist = new list(sizeof(triface), NULL, 256); + glueshlist = new list(sizeof(face), NULL, 256); + oldshlist = new list(sizeof(face), NULL, 256); + newshlist = new list(sizeof(face), NULL, 256); + success = true; // Assume p can be suppressed. + + bakchecksubfaces = checksubfaces; + checksubfaces = 0; + + // 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(); + + checksubfaces = bakchecksubfaces; + + // 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], gluetetlist, glueshlist, + 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]); + success = findrelocatepoint2(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(); + // Do not clear gluetetlist. gluetetlist->clear(); + // Do not clear glueshlist. + 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); + } + } + } + } + // Insert outside new subfaces (if there are). 2009-07-08. + for (i = 0; i < glueshlist->len(); i++) { + newsh = * (face *)(* glueshlist)[i]; + // Insert it into mesh (it may be already inserted). + newtet.tet = NULL; + // The mesh may be non-convex (set convexflag = 0). + dir = scoutsubface(&newsh, &newtet, 0); + if (dir != SHAREFACE) { + assert(0); + } + } + } 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); + } + // Delete new subfaces in glueshlist. 2009-07-07 + for (i = 0; i < glueshlist->len(); i++) { + newsh = * (face *)(* glueshlist)[i]; + for (j = 0; j < 2; j++) { + stpivot(newsh, oldtet); + if (oldtet.tet != dummytet) { + tsdissolve(oldtet); + } + sesymself(newsh); + } + 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--; + } + } + } + // Detach new subfaces attached to glue tets. 2009-07-10. + for (i = 0; i < gluetetlist->len(); i++) { + oldtet = * (triface *)(* gluetetlist)[i]; + if (!isdead(&oldtet)) { + // It contains a new subface which has already been deleted (in above). + tspivot(oldtet, newsh); + assert(isdead(&newsh)); + tsdissolve(oldtet); + sym(oldtet, neightet); + if (neightet.tet != dummytet) { + tsdissolve(neightet); + } + } + } + // Update the point-to-subface map. 2009-07-22. + for (i = 0; i < oldshlist->len(); i++) { + oldsh = * (face *)(* oldshlist)[i]; + ppt = (point *) &(oldsh.sh[3]); + for (j = 0; j < 3; j++) { + setpoint2sh(ppt[j], sencode(oldsh)); + } + } + } + + // Delete work lists. + delete oldshlist; + delete newshlist; + for (i = 0; i < 2; i++) { + if (oldtetlist[i] != (list *) NULL) { + delete oldtetlist[i]; + delete newtetlist[i]; + } + } + delete gluetetlist; + delete glueshlist; + + 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; + list *gluetetlist; + list *glueshlist; + triface oldtet, newtet, neightet, spintet; + face oldsh, newsh, *worksharray; + face startsh, spinsh, segsh1, segsh2; + face nsupseg, newseg, prevseg, nextseg; + point suppt, *newpt; + point pa, pb, pc, pd, *cons, *ppt; + enum interresult dir; + REAL pnorm[2][3], norm[3], len; + bool success; + int bakchecksubfaces; + 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 { + // Adjust spinsh to be the edge [pa, suppt]. + findedge(&spinsh, pa, suppt); + // Save it in list. + spinshlist->append(&spinsh); + // Go to the next facet. + spivotself(spinsh); + if (spinsh.sh == dummysh) break; + } while (spinsh.sh != startsh.sh); + + n = spinshlist->len(); + + if (n > 2) { + // Order the subfaces to be counterclockwise around edge [pa, suppt]. + worksharray = new face[n]; // Temporarily use it. + for (i = 0; i < n; i++) { + worksharray[i] = * (face *)(* spinshlist)[i]; + sinfect(worksharray[i]); + } + spinshlist->clear(); // Clear the list for the re-ordering. + for (i = 0; i < n; i++) { + worksharray[i] = * (face *)(* spinshlist)[i]; + if (sinfected(worksharray[i])) { + // Collect subfaces at this segment. + startsh = worksharray[i]; + stpivot(startsh, neightet); + if (neightet.tet == dummytet) { + sesymself(startsh); + stpivot(startsh, neightet); + assert(neightet.tet != dummytet); + } + // Adjust neightet to be the edge [pa, suppt]. + findedge(&neightet, pa, suppt); + // Adjust neightet to be the boundary face (if there exists). + spintet = neightet; + while (1) { + if (!fnextself(spintet)) { + esymself(spintet); + break; + } + if (apex(spintet) == apex(neightet)) break; + } + // Start from spintet, collect all subfaces at this segment. + neightet = spintet; + pc = org(spintet); + pd = dest(spintet); + // [pc, pd] is the rotating edge (axis). It may be either + // [pa, suppt] or [suppt, pa]. + while (1) { + tspivot(spintet, spinsh); + if (spinsh.sh != dummysh) { + assert(sinfected(spinsh)); + suninfect(spinsh); + // Let spinsh be the same oriented edge as spintet. + findedge(&spinsh, pc, pd); + spinshlist->append(&spinsh); + } + if (!fnextself(spintet)) break; + if (apex(spintet) == apex(neightet)) break; + } + } + assert(!sinfected(worksharray[i])); + } // i + delete [] worksharray; + } + + 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; + } + gluetetlist = new list(sizeof(triface), NULL, 256); + glueshlist = new list(sizeof(face), NULL, 256); + + // 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; + } + + bakchecksubfaces = checksubfaces; + checksubfaces = 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); + } + + checksubfaces = bakchecksubfaces; + + // 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); + // spinsh has been directed. Do not change its orientation now. + // 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); + }*/ + if (k == 0) { + // segsh1 has already been directed pointing to the inside of C. + } else { + // Reverse the direction of segsh1. + sesymself(segsh1); + } + // its orientation now. + // 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], gluetetlist, glueshlist, + 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]); + // Normialize the normal. + len = sqrt(norm[0] * norm[0] + norm[1] * norm[1] + norm[2] * norm[2]); + assert(len != 0); + for (j = 0; j < 3; j++) norm[j] /= len; + // success = findrelocatepoint(suppt, newpt[i], norm, frontlist, + // oldtetlist[i]); + success = findrelocatepoint2(suppt, newpt[i], norm, frontlist, + oldtetlist[i]); + // success = findrelocatepoint3(suppt, pa, pb, 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. + if (relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL)) { + setpointtype(newpt[i], FREEVOLVERTEX); + relverts++; + } else { + // The faked tets are deleted in above route. + pointdealloc(newpt[i]); + newpt[i] = (point) NULL; + newtetlist[i]->clear(); + success = false; + } + } 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(); + // Do not clear gluetetlist. // gluetetlist->clear(); + // Do not clear glueshlist. + flipque->clear(); + } + + if (success) { + // p has been suppressed. (Still in the pool). + setpointtype(suppt, UNUSEDVERTEX); + unuverts++; + // Update the point-to-seg map. + setpoint2seg(pa, sencode(newseg)); + setpoint2seg(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); + } + } + } + } + // Insert outside new subfaces (if there are). 2009-07-08. + for (i = 0; i < glueshlist->len(); i++) { + newsh = * (face *)(* glueshlist)[i]; + // Insert it into mesh (it may be already inserted). + newtet.tet = NULL; + // The mesh may be non-convex (set convexflag = 0). + dir = scoutsubface(&newsh, &newtet, 0); + if (dir != SHAREFACE) { + assert(0); + } + } + } 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); + } + } + // Delete new subfaces in glueshlist. 2009-07-07. + for (i = 0; i < glueshlist->len(); i++) { + newsh = * (face *)(* glueshlist)[i]; + if (!isdead(&newsh)) { + // Disconnect adjacent tets. + for (j = 0; j < 2; j++) { + stpivot(newsh, oldtet); + if (oldtet.tet != dummytet) { + tsdissolve(oldtet); + } + sesymself(newsh); + } + 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--; + } + } + } + // Detach new subfaces attached to glue tets. 2009-07-10. + for (i = 0; i < gluetetlist->len(); i++) { + oldtet = * (triface *)(* gluetetlist)[i]; + if (!isdead(&oldtet)) { + // It contains a new subface which has already been deleted (in above). + tspivot(oldtet, newsh); + assert(isdead(&newsh)); + tsdissolve(oldtet); + sym(oldtet, neightet); + if (neightet.tet != dummytet) { + tsdissolve(neightet); + } + } + } + // Update the point-to-subface map. 2009-07-22. + for (i = 0; i < spinshlist->len(); i++) { + for (j = 0; j < oldshlist[i]->len(); j++) { + oldsh = * (face *)(* oldshlist[i])[j]; + ppt = (point *) &(oldsh.sh[3]); + for (k = 0; k < 3; k++) { + setpoint2sh(ppt[k], sencode(oldsh)); + } + } + } + } + + // Delete work lists. + delete [] newpt; // BUG fixed. Thanks dmyan, June 23, 2007. + 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; + delete gluetetlist; + delete glueshlist; + // 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 *gluetetlist; + list *glueshlist; + list *newshlist; // a dummy list. + queue *myflipque; + triface oldtet, newtet; + point suppt, conpt, *ppt; + bool success; + int j, k; + + // Allocate spaces for storing (old and new) B(p). + oldtetlist = new list(sizeof(triface), NULL, 256); + newtetlist = new list(sizeof(triface), NULL, 256); + gluetetlist = new list(sizeof(triface), NULL, 256); + glueshlist = new list(sizeof(face), 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, gluetetlist, glueshlist, 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); + // Update the point-to-tet map. + ppt = (point *) &(oldtet.tet[4]); + for (k = 0; k < 4; k++) { + setpoint2tet(ppt[k], encode(oldtet)); + } + } + } + + // Clear work lists. + ptlist->clear(); + frontlist->clear(); + misfrontlist->clear(); + gluetetlist->clear(); + glueshlist->clear(); + flipque->clear(); + // Deallocate work lists. + if (myfrontlist != (list *) NULL) { + delete myfrontlist; + delete mymisfrontlist; + delete myptlist; + delete myflipque; + } + delete oldtetlist; + delete newtetlist; + delete gluetetlist; + delete glueshlist; + delete newshlist; + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removesteiners2() Remove Steiner points. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::removesteiners2() +{ + list *frontlist, *misfrontlist; + list *spinshlist, *newsegshlist; + list *ptlist, *conlist; + memorypool *viri; + queue *flipque; + triface searchtet, checktet; + face searchsh; + face searchseg; + point pa, pt; + enum verttype vtype; + bool remflag, success; //, optflag; + int oldnum, rmstein; + int unsupbdrycount; // Count the unsuppressed boundary Steiner points. + int iter, i, j; + + if (!b->quiet) { + printf("Removing Steiner points.\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); + + caveshlist = new arraypool(sizeof(face), 10); + caveshbdlist = new arraypool(sizeof(face), 10); + + oldnum = unuverts; + relverts = suprelverts = collapverts = unsupverts; + + iter = 0; + i = 0; + + do { // iter + unsupbdrycount = 0; + // Initialize the two arrays (global values). + fixededgelist = new arraypool(sizeof(point) * 2, 8); + elemfliplist = new arraypool(sizeof(elemflip), 8); + + do { // i + rmstein = unuverts; + points->traversalinit(); + pa = pointtraverse(); + while (pa != NULL) { + j = pointmark(pa) - in->firstnumber; + if (j >= in->numberofpoints) { + // pa is not an input points. + vtype = pointtype(pa); + if ((vtype == FREESEGVERTEX) || (vtype == FREESUBVERTEX) || + (vtype == FREEVOLVERTEX)) { + i++; + if (b->verbose > 1) { + printf(" Removing %d-th Steiner point %d.\n", i, pointmark(pa)); + } + } + if (vtype == FREESEGVERTEX) { + remflag = false; + // pa is not an input point. + if (b->nobisect == 1) { + point2segorg(pa, searchseg); + sstpivot(&searchseg, &checktet); + assert(checktet.tet != dummytet); + pt = apex(checktet); + do { + if (!fnextself(checktet)) { + // Meet a boundary face - p is on the hull. + remflag = true; + break; + } + } while (apex(checktet) != pt); + } else { + // '-YY'. Remove p whatever s is a hull face or not. + remflag = true; + } + if (remflag) { + point2segorg(pa, searchseg); + sesymself(searchseg); // pa = sdest(); + success = suppresssegpoint(&searchseg, spinshlist, newsegshlist, + frontlist, misfrontlist, ptlist, conlist, viri, flipque, + false, false); + } + } else if (vtype == FREESUBVERTEX) { + remflag = false; + // pa is not an input point. + if (b->nobisect == 1) { + // '-Y'. Remove p if s is a hull face. + point2shorg(pa, searchsh); + stpivot(searchsh, checktet); + if (checktet.tet != dummytet) { + sesymself(searchsh); + stpivot(searchsh, checktet); + } + remflag = (checktet.tet == dummytet); + } else { + // '-YY'. Remove p whatever s is a hull face or not. + remflag = true; + } + if (remflag) { + point2shorg(pa, searchsh); + senextself(searchsh); // pa = sapex(); + success = suppressfacetpoint(&searchsh, frontlist, misfrontlist, + ptlist, conlist, viri, flipque, false, false); + } + } else if (vtype == FREEVOLVERTEX) { + // pa is not an input point. + point2tetorg(pa, searchtet); + success = suppressvolpoint(&searchtet, frontlist, misfrontlist, + ptlist, flipque, false); + } + } // if (j >= in->numberofpoints) + pa = pointtraverse(); + } + // Continue if any Steiner point has been removed. + } while (unuverts > rmstein); + + delete fixededgelist; + delete elemfliplist; + fixededgelist = NULL; + elemfliplist = NULL; + + if (b->optlevel > 0) { // b->optlevel is set by -s. + // Improve the local mesh quality at relocated Steiner points. + b_steinerflag = true; + optimizemesh2(true); + b_steinerflag = false; + + // Smooth the relocated vertices (also count unsupressed vertices). + points->traversalinit(); + pa = pointtraverse(); + while (pa != NULL) { + j = pointmark(pa) - in->firstnumber; + if (j >= in->numberofpoints) { + // pa is not an input point. + vtype = pointtype(pa); + if (vtype == FREEVOLVERTEX) { + point2tetorg(pa, searchtet); + frontlist->append(&searchtet); + formstarpolyhedron(pa, frontlist, NULL, false); + smoothpoint(pa, NULL, NULL, frontlist, false, NULL); + frontlist->clear(); + } else if (vtype == FREESEGVERTEX) { + remflag = false; + // pa is not an input point. + if (b->nobisect == 1) { + point2segorg(pa, searchseg); + sstpivot(&searchseg, &checktet); + assert(checktet.tet != dummytet); + pt = apex(checktet); + do { + if (!fnextself(checktet)) { + // Meet a boundary face - p is on the hull. + remflag = true; + break; + } + } while (apex(checktet) != pt); + } else { + // '-YY'. Remove p whatever s is a hull face or not. + remflag = true; + } + if (remflag) { + unsupbdrycount++; + } + } else if (vtype == FREESUBVERTEX) { + remflag = false; + // pa is not an input point. + if (b->nobisect == 1) { + // '-Y'. Remove p if s is a hull face. + point2shorg(pa, searchsh); + stpivot(searchsh, checktet); + if (checktet.tet != dummytet) { + sesymself(searchsh); + stpivot(searchsh, checktet); + } + remflag = (checktet.tet == dummytet); + } else { + // '-YY'. Remove p whatever s is a hull face or not. + remflag = true; + } + if (remflag) { + unsupbdrycount++; + } + } + } + pa = pointtraverse(); + } + } + + if (unsupbdrycount == 0) { + break; // No unsupressed boundary points left. + } + iter++; + } while ((b->optlevel > 0) && (iter < b->optpasses)); + // Comment: default b->optpasses is 3, it can be set by -ss option. + + if (b->verbose > 0) { + printf(" %d points removed from boundary.\n", unuverts - oldnum); + // if (relverts > 0) { + printf(" %d points relocated (%d suppressed, %d collapsed).\n", + relverts, suprelverts - collapverts, collapverts); + if (unsupverts > 0) { + printf(" %d points were unsuppressed.\n", unsupverts); + } + if (unsupbdrycount > 0) { + printf(" !! %d points remain in the boundary.\n", unsupbdrycount); + } + printf(" %d points remain in the interior.\n", relverts-suprelverts); + // } + } + + /*// DEBUG Dump extremly bad tets. + badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0); + cosmaxdihed = cos(179.999 * PI / 180.0); + cosmindihed = cos(0.1 * PI / 180.0); + tallslivers(true); + dumpbadtets(); + delete badtetrahedrons; + badtetrahedrons = NULL; + // DEBUG END */ + + delete caveshlist; + delete caveshbdlist; + caveshlist = NULL; + caveshbdlist = NULL; + + // Delete work lists. + delete frontlist; + delete misfrontlist; + delete spinshlist; + delete newsegshlist; + delete ptlist; + delete conlist; + delete flipque; + delete viri; +} + +//// //// +//// //// +//// steiner_cxx ////////////////////////////////////////////////////////////// + +//// reconstruct_cxx ////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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(3); + } + // Two identical points are distinguished by 'lengthlimit'. + lengthlimit = longest * b->epsilon * 1e+2; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// reconstructmesh() Reconstruct a tetrahedral mesh. // +// // +// The list of tetrahedra will be read from 'in->tetrahedronlist'. If 'in-> // +// trifacelist' is not empty, boundary faces (faces with a non-zero marker) // +// from this list will be inserted into the mesh. In addition, this routine // +// automatically detects boundary faces (subfaces): all hull faces will be // +// recognized as subfaces, internal faces between two tetrahedra which have // +// different region 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; + face sface1, sface2; + face checkseg, subseg; + point *idx2verlist; + point torg, tdest, tapex, toppo; + point norg, 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 pivot, ipivot, isum; + int maxbandwidth; + int index, i, j, k; + + 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; + maxbandwidth = 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; + // Pick the vertex which has the lowest degree. + if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > + (idx2tetlist[idest + 1] - idx2tetlist[idest])) { + if ((idx2tetlist[idest + 1] - idx2tetlist[idest]) > + (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { + pivot = iapex; + } else { + pivot = idest; + } + } else { + if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > + (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { + pivot = iapex; + } else { + pivot = iorg; + } + } + if ((idx2tetlist[pivot + 1] - idx2tetlist[pivot]) > maxbandwidth) { + maxbandwidth = idx2tetlist[pivot + 1] - idx2tetlist[pivot]; + } + bondflag = false; + // Search its neighbor in the adjacent tets of the pivoted vertex. + for (j = idx2tetlist[pivot]; j < idx2tetlist[pivot + 1] && !bondflag; + j++) { + // Quickly check if this tet contains the neighbor. + isum = 0; + for (k = 0; k < 4; k++) { + norg = (point) tetsperverlist[j][4 + k]; + ipivot = pointmark(norg) - in->firstnumber; + isum += worklist[ipivot]; + } + if (isum != 3) continue; + if (tetsperverlist[j] == tetloop.tet) continue; // Skip myself. + // This tet contains its neighbor, find the face and bond them. + neightet.tet = tetsperverlist[j]; + for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { + norg = oppo(neightet); + ipivot = pointmark(norg) - in->firstnumber; + if (worklist[ipivot] == 0) { + // Find! Bond them together and break the loop. +#ifdef SELF_CHECK + sym(neightet, neineightet); + assert(neineightet.tet == dummytet); +#endif + 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(); + } + + if (b->verbose) { + printf(" Maximal vertex degree = %d.\n", maxbandwidth); + } + + // 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; + // Pick the vertex which has the lowest degree. + if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > + (idx2tetlist[idest + 1] - idx2tetlist[idest])) { + if ((idx2tetlist[idest + 1] - idx2tetlist[idest]) > + (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { + pivot = iapex; + } else { + pivot = idest; + } + } else { + if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > + (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) { + pivot = iapex; + } else { + pivot = iorg; + } + } + bondflag = false; + // Search its neighbor in the adjacent tets of torg. + for (j = idx2tetlist[pivot]; j < idx2tetlist[pivot + 1] && !bondflag; + j++) { + // Quickly check if this tet contains the neighbor. + isum = 0; + for (k = 0; k < 4; k++) { + norg = (point) tetsperverlist[j][4 + k]; + ipivot = pointmark(norg) - in->firstnumber; + isum += worklist[ipivot]; + } + if (isum != 3) continue; + neightet.tet = tetsperverlist[j]; + for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) { + norg = oppo(neightet); + ipivot = pointmark(norg) - in->firstnumber; + if (worklist[ipivot] == 0) { + 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. + sdissolve(subloop); // 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); + if (neighsh.sh == dummysh) { + break; // Only one facet case. + } + } 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(sizeof(int), NULL, 256); + + 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); + setpoint2sh(newpoint, sencode(checksh)); + } + } else { + setpointtype(newpoint, FREEVOLVERTEX); + } + if (insertflag) { + splittetface(newpoint, &searchtet, flipqueue); + } + break; + case ENCSEGMENT: + case ONEDGE: + tsspivot(&searchtet, &checkseg); + if (checkseg.sh != dummysh) { + if (b->nobisect) { + insertflag = false; + } else { + setpointtype(newpoint, FREESEGVERTEX); + setpoint2seg(newpoint, sencode(checkseg)); + } + } else { + tspivot(searchtet, checksh); + if (checksh.sh != dummysh) { + if (b->nobisect) { + insertflag = false; + } else { + setpointtype(newpoint, FREESUBVERTEX); + setpoint2sh(newpoint, sencode(checksh)); + } + } 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 { + lawson3d(flipqueue); + } + } + + 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->quiet && 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(3); + } + } + // 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; +} + +//// //// +//// //// +//// reconstruct_cxx ////////////////////////////////////////////////////////// + +//// refine_cxx /////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. + 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 != dummysh) && (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); + if (spinsh.sh == dummysh) break; // A single subface case. + } 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); + if (spinsh.sh == dummysh) break; // A single subface case. + } while (spinsh.sh != startsh.sh); + // } + } + if (issharp) { + setshelltype(segloop, SHARP); + // Set the type for all subsegments at forwards. + edest = sdest(segloop); + senext(segloop, nextseg); + spivotself(nextseg); + while (nextseg.sh != dummysh) { + setshelltype(nextseg, SHARP); + // Adjust the direction of nextseg. + nextseg.shver = 0; + if (sorg(nextseg) != edest) { + sesymself(nextseg); + } + assert(sorg(nextseg) == edest); + edest = sdest(nextseg); + // Go the next connected subsegment at edest. + senextself(nextseg); + spivotself(nextseg); + } + 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); + if (spinsh.sh == dummysh) break; // A single subface case. + } while (spinsh.sh != startsh.sh); + } + if (issharp) { + setshelltype(segloop, SHARP); + // Set the type for all subsegments at backwards. + eorg = sorg(segloop); + senext2(segloop, prevseg); + spivotself(prevseg); + while (prevseg.sh != dummysh) { + setshelltype(prevseg, SHARP); + // Adjust the direction of prevseg. + prevseg.shver = 0; + if (sdest(prevseg) != eorg) { + sesymself(prevseg); + } + assert(sdest(prevseg) == eorg); + eorg = sorg(prevseg); + // Go to the next connected subsegment at eorg. + 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); + } + + // 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; + 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(point2seg(adjpt), checkseg); + assert(checkseg.sh != dummysh); + checkseg.shver = 0; + // Find the origin of this seg. + prevseg = checkseg; + e1 = sorg(prevseg); + do { + senext2(prevseg, testseg); + spivotself(testseg); + if (testseg.sh == dummysh) break; + // Go to the previous subseg. + prevseg = testseg; + // Adjust the direction of the previous subsegment. + prevseg.shver = 0; + if (sdest(prevseg) != e1) { + sesymself(prevseg); + } + assert(sdest(prevseg) == e1); + e1 = sorg(prevseg); + } while (true); + // Find the dest of this seg. + nextseg = checkseg; + e2 = sdest(nextseg); + do { + senext(nextseg, testseg); + spivotself(testseg); + if (testseg.sh == dummysh) break; + // Go to the next subseg. + nextseg = testseg; + // Adjust the direction of the nextseg. + nextseg.shver = 0; + if (sorg(nextseg) != e2) { + sesymself(nextseg); + } + assert(sorg(nextseg) == e2); + e2 = sdest(nextseg); + } 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 > 1) { + 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(point2seg(ploop), checkseg); + assert(checkseg.sh != dummysh); + if (shelltype(checkseg) == SHARP) { + checkseg.shver = 0; + // Find the origin of this seg. + prevseg = checkseg; + e1 = sorg(prevseg); + do { + senext2(prevseg, testseg); + spivotself(testseg); + if (testseg.sh == dummysh) break; + prevseg = testseg; // Go the previous subseg. + // Adjust the direction of this subsegmnt. + prevseg.shver = 0; + if (sdest(prevseg) != e1) { + sesymself(prevseg); + } + assert(sdest(prevseg) == e1); + e1 = sorg(prevseg); + } while (true); + // Find the dest of this seg. + nextseg = checkseg; + e2 = sdest(nextseg); + do { + senext(nextseg, testseg); + spivotself(testseg); + if (testseg.sh == dummysh) break; + nextseg = testseg; // Go the next subseg. + // Adjust the direction of this subsegment. + nextseg.shver = 0; + if (sorg(nextseg) != e2) { + sesymself(nextseg); + } + assert(sorg(nextseg) == e2); + e2 = sdest(nextseg); + } 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 > 1) && (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; + + if (!smarktested(*testsub)) { + if (!shell2badface(*testsub)) { + 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); + } + } + } else { + if (b->verbose > 2) { + printf(" Ignore an encroached subface (%d, %d, %d).\n", + pointmark(sorg(*testsub)), pointmark(sdest(*testsub)), + pointmark(sapex(*testsub))); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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) { + // This segment is encroached and should be repaired. + if (!smarktested(*testseg)) { + if (!shell2badface(*testseg)) { // Is it not queued yet? + 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); + } + } else { + // This segment has been rejected for splitting. Do not queue it. + if (b->verbose > 2) { + printf(" Ignore a rejected encroaching subsegment (%d, %d).\n", + pointmark(eorg), pointmark(edest)); + } + } + } + + 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) { + if (in->tetunsuitable != NULL) { + // Execute the user-defined meshing sizing evaluation. + pa = (point) testtet->tet[4]; + pb = (point) testtet->tet[5]; + pc = (point) testtet->tet[6]; + pd = (point) testtet->tet[7]; + enq = (*(in->tetunsuitable))(pa, pb, pc, pd, elen, volume); + } else { + // 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; + + // This segment must have not been checked (and rejected) yet. + assert(!smarktested(*splitseg)); + + 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; + 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); + } + } 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]); + } + } else { + // Split the segment at its midpoint. + for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool 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); + if (splittetedge(newpt, &starttet, flipque)) { + // Remove locally non-Delaunay faces by flipping. + lawson3d(flipque); + } else { + if (optflag) { + delete mytetlist; + delete myflipque; + } + return false; + } + + 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); + if (spinsh.sh == dummysh) { + break; // There's only one facet having this segment. + } + } 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; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + int enccount; // long oldencnum; + int i, j, k; + + // Remember the current number of encroached segments. + // oldencnum = badsubsegs->items; + + // Count the number of encroached segments. + enccount = 0; + + 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)) { + if (checkseg4encroach(&checkseg, testpt, NULL, true)) { + enccount++; + } + // } + } + 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)) { + if (checkseg4encroach(&checkseg, testpt, NULL, true)) { + enccount++; + } + // } + checkseg.sh = shellfacetraverse(subsegs); + } + } + + // return (badsubsegs->items > oldencnum); + return enccount > 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + int enccount; //long oldencnum; + int i, k; + + // Remember the current number of encroached segments. + // oldencnum = badsubfaces->items; + + enccount = 0; // Count the number encroached subfaces. + + 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)) { + if (checksub4encroach(&checksh, testpt, true)) { + enccount++; + } + //} + } + } + } + } 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)) { + if (checksub4encroach(&checksh, testpt, true)) { + enccount++; + } + // } + checksh.sh = shellfacetraverse(subfaces); + } + } + + //return (badsubfaces->items > oldencnum); + return enccount > 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + point newpt, refpt; + point e1, e2; + int nmax, n; + + 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); + setpoint2seg(newpt, sencode(splitseg)); + // Decide whether p can be inserted or not. + if (acceptsegpt(newpt, refpt, &splitseg)) { + // 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 { + if (splitencseg(newpt, &splitseg, tetlist, sublist, NULL, flipque, + chkencsub, chkbadtet, false)) { + setnewpointsize(newpt, e1, e2); + if (steinerleft > 0) steinerleft--; + } else { + // Fail to split the segment. It MUST be caused by a very flat + // tet connected at the splitting segment. We do not handle + // this case yet. Hopefully, the later repairs will remove + // the flat tet and hence the segment can be split later. + pointdealloc(newpt); + } + } + } else { + // This segment can not be split for not meeting the rules in + // acceptsegpt(). Mark it to avoid re-checking it later. + smarktest(splitseg); + // 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, e1; + enum locateresult loc; + REAL normal[3], len; + bool reject; + long oldptnum, oldencsegnum; + 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); + // Calculate an abovepoint in dummypoint. + facenormal2(encloop->forg, encloop->fdest, encloop->fapex, normal, 1); + len = sqrt(DOT(normal, normal)); + normal[0] /= len; + normal[1] /= len; + normal[2] /= len; + len = DIST(encloop->forg, encloop->fdest); + len += DIST(encloop->fdest, encloop->fapex); + len += DIST(encloop->fapex, encloop->forg); + len /= 3.0; + dummypoint[0] = encloop->forg[0] + len * normal[0]; + dummypoint[1] = encloop->forg[1] + len * normal[1]; + dummypoint[2] = encloop->forg[2] + len * normal[2]; + abovepoint = dummypoint; + } + // 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)). + oldencsegnum = badsubsegs->items; + reject = tallencsegs(newpt, 2, ceillists); + if (reject && (oldencsegnum == badsubsegs->items)) { + // 'newpt' encroaches upon some subsegments. But none of them can + // be split. So this subface can't be split as well. Mark it to + // avoid re-checking it later. + smarktest(encloop->ss); + } + // Execute point accept rule if p does not encroach upon any segment. + if (!reject) { + reject = !acceptfacpt(newpt, subceillist, verlist); + if (reject) { + // 'newpt' lies in some protecting balls. This subface can't be + // split. Mark it to avoid re-checking it later. + smarktest(encloop->ss); + } + } + 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) { + // 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))); + terminatetetgen(2); + } + assert(loc == OUTSIDE); + // The circumcenter lies outside of the facet. Mark it to avoid + // rechecking it later. + smarktest(encloop->ss); + // 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; + arraypool *histtetarray; + 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); + + histtetarray = new arraypool(sizeof(triface), 8); + + // 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); + loc = locate2(newpt, &starttet, histtetarray); + if (b->verbose > 1) { + printf(" loc = %d.\n", (int) loc); + } + 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))); + terminatetetgen(2); + }*/ + // 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; + delete histtetarray; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; +} + +//// //// +//// //// +//// refine_cxx /////////////////////////////////////////////////////////////// + +//// optimize_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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->maxdihedal' (set by the // +// '-qqq' 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; + int count; + int i, j; + + 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; + } + } + + count = 0; + + // 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. + count++; + 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); + } + } + } + } + + return count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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[21]; // Old configuration at ab, save maximum 20 tets. + triface bftetlist[21]; // Old configuration at bf, save maximum 20 tets. + triface newtetlist[90]; // New configuration after removing ab. + face checksh; + //enum fliptype fty; + REAL key; + bool remflag, subflag; + int n, n1, m, i, j, k; + + triface newtet; + point *ppt; + + // 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); + k = 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, newtetlist)) { + if (b->verbose > 1) { + printf(" Stripped tet from the mesh.\n"); + } + optcount[0]++; + opt_tet_peels++; + // edge is removed. Test new tets for further optimization. + for (i = 0; i < 2; i++) { + if (optflag) { + checktet4opt(&(newtetlist[i]), true); + } else { + checktet4ill(&(newtetlist[i]), true); + } + } + // Update the point-to-tet map + for (i = 0; i < 2; i++) { + newtet = newtetlist[i]; + ppt = (point *) &(newtet.tet[4]); + for (j = 0; j < 4; j++) { + setpoint2tet(ppt[j], encode(newtet)); + } + } + return true; + } + } + } + // Check if the oppsite edge cd is on the hull. + enext2fnextself(abcd); + enext2self(abcd); + esymself(abcd); // --> cdab + k++; + } while (k < 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 == 20) 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); + } + // Update the point-to-tet map + for (i = 0; i < m; i++) { + newtet = newtetlist[i]; + ppt = (point *) &(newtet.tet[4]); + for (j = 0; j < 4; j++) { + setpoint2tet(ppt[j], encode(newtet)); + } + } + optcount[1]++; + opt_face_flips++; + return true; + } + } // j + */ + // Faces are not flipable. Return. + return false; + } + + // 2 < n < 20. + 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 > 3) && (n <= b->maxflipedgelinksize)) { + // 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 <= b->maxflipedgelinksize)) { + // 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); + } + } + // Update the point-to-tet map + for (i = 0; i < m; i++) { + newtet = newtetlist[i]; + ppt = (point *) &(newtet.tet[4]); + for (j = 0; j < 4; j++) { + setpoint2tet(ppt[j], encode(newtet)); + } + } + opt_edge_flips++; + } + + return remflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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) { + if (segflag) smoothsegverts++; + 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; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + + // Let 'remedge->tt' be the edge [a, b]. + starttet = remedge->tt; + + // Go to the opposite edge [c, d]. + adjustedgering(starttet, CCW); + enextfnextself(starttet); + enextself(starttet); + + // Check if cd is a segment. + tsspivot(&starttet, &checkseg); + if (b->nobisect == 0) { + if (checkseg.sh != dummysh) { + // cd is a segment. The seg will be split. + checkseg.shver = 0; + pt[0] = sorg(checkseg); + pt[1] = sdest(checkseg); + makepoint(&newpt); + getsplitpoint(pt[0], pt[1], NULL, newpt); + setpointtype(newpt, FREESEGVERTEX); + setpoint2seg(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; + } + } + + // Create the new point p (at the circumcenter of t). + makepoint(&newpt); + /*// Get the four corners. + for (i = 0; i < 4; i++) { + pt[i] = (point) starttet.tet[4 + i]; + } + for (i = 0; i < 3; i++) { + newpt[i] = 0.25 * (pt[0][i] + pt[1][i] + pt[2][i] + pt[3][i]); + }*/ + pt[0] = org(starttet); + pt[1] = dest(starttet); + for (i = 0; i < 3; i++) { + newpt[i] = 0.5 * (pt[0][i] + pt[1][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() Improving the mesh quality. // +// // +// 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::optimizemesh2(bool optflag) +{ + list *splittetlist, *tetlist, *ceillist; + badface *remtet, *newbadtet; + REAL maxdihed, objdihed, cosobjdihed; + long oldflipcount, newflipcount; + long oldpointcount; + int slivercount; + int optpasscount; + int iter, i; + + // Cosines of the six dihedral angles of the tet [a, b, c, d]. + // From cosdd[0] to cosdd[5]: ab, bc, ca, ad, bd, cd. + REAL cosdd[6]; + //int j; + + if (!b->quiet) { + if (optflag) { + if (b_steinerflag) { + // This routine is called from removesteiners2(); + } else { + printf("Optimizing mesh.\n"); + } + } else { + printf("Repairing mesh.\n"); + } + } + + if (optflag) { + if (b_steinerflag) { + // This routine is called from removesteiners2(); + cosmaxdihed = cos(179.0 * PI / 180.0); + cosmindihed = cos(1.0 * PI / 180.0); + // The radian of the maximum dihedral angle. + maxdihed = 179.0 / 180.0 * PI; + } else { + 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 < 175.0) objdihed = 175.0; + objdihed = objdihed / 180.0 * PI; + cosobjdihed = cos(objdihed); + } + } + + // Initialize the pool of bad tets. + badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0); + // Looking for non-optimal tets. + tallslivers(optflag); + + oldpointcount = points->items; + opt_tet_peels = opt_face_flips = opt_edge_flips = 0l; + oldflipcount = newflipcount = 0l; + smoothsegverts = 0l; + optpasscount = 0; + + if (optflag && (b->verbose)) { + printf(" level = %d.\n", b->optlevel); + } + + // Start the mesh optimization iteration. + do { + + if (optflag && (b->verbose > 1)) { + printf(" level = %d.\n", b->optlevel); + } + + // Improve the mesh quality by flips. + iter = 0; + do { + oldflipcount = newflipcount; + // Loop in the list of bad tets. + 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)) { + 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)) { + // Remove the badtet from the list. + badfacedealloc(badtetrahedrons, remtet); + } + } else { + // Remove the badtet from the list. + badfacedealloc(badtetrahedrons, remtet); + } + remtet = badfacetraverse(badtetrahedrons); + } + iter++; + if (iter > 10) break; // Stop at 10th iterations. + // Count the total number of flips. + newflipcount = opt_tet_peels + opt_face_flips + opt_edge_flips; + // Continue if there are bad tets and new flips. + } while ((badtetrahedrons->items > 0) && (newflipcount > oldflipcount)); + + if (b_steinerflag) { + // This routine was called from removesteiner2(). Do not repair + // the bad tets by splitting. + badtetrahedrons->restart(); + } + + if ((badtetrahedrons->items > 0l) && optflag && (b->optlevel > 2)) { + // Get a list of slivers and try to split them. + splittetlist = new list(sizeof(badface), NULL, 256); + tetlist = new list(sizeof(triface), NULL, 256); + ceillist = new list(sizeof(triface), NULL, 256); + + // Form a list of slivers to be split and clean the pool. + badtetrahedrons->traversalinit(); + remtet = badfacetraverse(badtetrahedrons); + while (remtet != (badface *) NULL) { + splittetlist->append(remtet); + remtet = badfacetraverse(badtetrahedrons); + } + // Clean the pool of bad tets. + badtetrahedrons->restart(); + slivercount = 0; + for (i = 0; i < splittetlist->len(); i++) { + remtet = (badface *)(* splittetlist)[i]; + if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg && + dest(remtet->tt) == remtet->fdest && + apex(remtet->tt) == remtet->fapex && + oppo(remtet->tt) == remtet->foppo) { + // Calculate the six dihedral angles of this tet. + adjustedgering(remtet->tt, CCW); + remtet->forg = org(remtet->tt); + remtet->fdest = dest(remtet->tt); + remtet->fapex = apex(remtet->tt); + remtet->foppo = oppo(remtet->tt); + tetalldihedral(remtet->forg, remtet->fdest, remtet->fapex, + remtet->foppo, cosdd, NULL, NULL); + // Is it a large angle? + if (cosdd[0] < cosobjdihed) { + slivercount++; + remtet->key = cosdd[0]; + if (b->verbose > 1) { + printf(" Split 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 (b->verbose && ((acos(cosdd[0]) / PI * 180) > 179)) { + // For DEBUG only. + printf(" p:draw_tet(%d, %d, %d, %d) -- %d (", + pointmark(remtet->forg), pointmark(remtet->fdest), + pointmark(remtet->fapex), pointmark(remtet->foppo), + slivercount); + // Print the 6 dihedral angles. + for (j = 0; j < 5; j++) { + printf("%4.1f, ", acos(cosdd[j]) / PI * 180.0); + } + printf("%4.1f)\n", acos(cosdd[5]) / PI * 180.0); + }*/ + // Queue this tet. + newbadtet = (badface *) badtetrahedrons->alloc(); + *newbadtet = *remtet; + // Try to remove this tet. + if (!smoothsliver(remtet, tetlist)) { + splitsliver(remtet, tetlist, ceillist); + } + } + } + } // i + + delete splittetlist; + delete tetlist; + delete ceillist; + } + + optpasscount++; + } while ((badtetrahedrons->items > 0) && (optpasscount < b->optpasses)); + + if (b->verbose) { + if (opt_tet_peels > 0l) { + printf(" %ld tet removals.\n", opt_tet_peels); + } + if (opt_face_flips > 0l) { + printf(" %ld face flips.\n", opt_face_flips); + } + if (opt_edge_flips > 0l) { + printf(" %ld edge flips.\n", opt_edge_flips); + } + if ((points->items - oldpointcount) > 0l) { + printf(" %ld point insertions", points->items - oldpointcount); + if (smoothsegverts > 0) { + printf(" (%d on segment)", smoothsegverts); + } + printf("\n"); + } + } + + delete badtetrahedrons; + badtetrahedrons = (memorypool *) NULL; +} + +//// //// +//// //// +//// optimize_cxx ///////////////////////////////////////////////////////////// + +//// output_cxx /////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// jettisonnodes() Jettison unused or duplicated vertices. // +// // +// Unused points are those input points which are outside the mesh domain or // +// have no connection (isolated) to the mesh. Duplicated points exist for // +// example if the input PLC is read from a .stl mesh file (marked during the // +// Delaunay tetrahedralization step. This routine remove these points from // +// points list. All existing points are reindexed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::jettisonnodes() +{ + point pointloop; + bool jetflag; + int oldidx, newidx; + int remcount; + + if (!b->quiet) { + printf("Jettisoning redundants points.\n"); + } + + points->traversalinit(); + pointloop = pointtraverse(); + oldidx = newidx = 0; // in->firstnumber; + remcount = 0; + while (pointloop != (point) NULL) { + jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || + (pointtype(pointloop) == UNUSEDVERTEX); + if (jetflag) { + // It is a duplicated point, delete it. + pointdealloc(pointloop); + remcount++; + } else { + // Re-index it. + setpointmark(pointloop, newidx + in->firstnumber); + if (in->pointmarkerlist != (int *) NULL) { + if (oldidx < in->numberofpoints) { + // Re-index the point marker as well. + in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx]; + } + } + newidx++; + } + oldidx++; + if (oldidx == in->numberofpoints) { + // Update the numbe of input points (Because some were removed). + in->numberofpoints -= remcount; + // Remember this number for output original input nodes. + jettisoninverts = remcount; + } + pointloop = pointtraverse(); + } + if (b->verbose) { + printf(" %d duplicated vertices 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) { + 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(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// numberedges() Count the number of edges, save in "meshedges". // +// // +// This routine is called when '-p' or '-r', and '-E' options are used. The // +// total number of edges depends on the genus of the input surface mesh. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::numberedges() +{ + triface tetloop, worktet, spintet; + int hitbdry, i; + + if (!b->plc && !b->refine) { + // Using the Euler formula (V-E+F-T=1) to get the total number of edges. + long faces = (4l * tetrahedrons->items + hullsize) / 2l; + meshedges = points->items + faces - tetrahedrons->items - 1l; + return; + } + + meshedges = 0l; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + 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) { + meshedges++; + } + } + 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(3); + } + // 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) { + terminatetetgen(1); + } + // Allocate space for 'pointattributelist' if necessary; + if (nextras > 0) { + out->pointattributelist = new REAL[points->items * nextras]; + if (out->pointattributelist == (REAL *) NULL) { + terminatetetgen(1); + } + } + // Allocate space for 'pointmarkerlist' if necessary; + if (bmark) { + out->pointmarkerlist = new int[points->items]; + if (out->pointmarkerlist == (int *) NULL) { + 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(3); + } + // Number of points, number of point metrices, + // fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3); + fprintf(outfile, "%ld %d\n", points->items, 1); + } else { + // Allocate space for 'pointmtrlist' if necessary; + // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)]; + out->pointmtrlist = new REAL[points->items]; + if (out->pointmtrlist == (REAL *) NULL) { + terminatetetgen(1); + } + out->numberofpointmtrs = 1; // (sizeoftensor + 3); + mtrindex = 0; + } + + // 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; + triface worktet, spintet; + int *tlist; + REAL *talist; + int firstindex, shift; + int pointindex; + int attribindex; + point p1, p2, p3, p4; + point *extralist; + int elementnumber; + int eextras; + int hitbdry, 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(3); + } + // 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) { + 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) { + 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. + } + + // Count the total edge numbers. + meshedges = 0l; + + tetrahedrons->traversalinit(); + tptr = tetrahedrontraverse(); + elementnumber = firstindex; // in->firstnumber; + while (tptr != (tetrahedron *) NULL) { + if (b->noelewritten == 2) { + // Reverse the orientation, such that Orient3D() > 0. + p1 = (point) tptr[5]; + p2 = (point) tptr[4]; + } else { + 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; + setelemmarker(tptr, elementnumber); + } + // 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 = tptr; + 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) { + meshedges++; + } + } + tptr = tetrahedrontraverse(); + elementnumber++; + } + if (b->neighout) { + // Set the outside element marker. + //* (int *) (dummytet + elemmarkerindex) = -1; + setelemmarker(dummytet, -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(3); + } + fprintf(outfile, "%ld %d\n", faces, bmark); + } else { + // Allocate memory for 'trifacelist'. + out->trifacelist = new int[faces * 3]; + if (out->trifacelist == (int *) NULL) { + terminatetetgen(1); + } + // Allocate memory for 'trifacemarkerlist' if necessary. + if (bmark) { + out->trifacemarkerlist = new int[faces]; + if (out->trifacemarkerlist == (int *) NULL) { + terminatetetgen(1); + } + } + if (b->neighout > 1) { + // '-nn' switch. + out->adjtetlist = new int[faces * 2]; + if (out->adjtetlist == (int *) NULL) { + 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); + neigh1 = getelemmarker(tface.tet); + if (tsymface.tet != dummytet) { + //neigh2 = * (int *)(tsymface.tet + elemmarkerindex); + neigh2 = getelemmarker(tsymface.tet); + } else { + neigh2 = -1; + } + } + if (out == (tetgenio *) NULL) { + // Face number, indices of three vertices. + fprintf(outfile, "%5d %4d %4d %4d", facenumber, + pointmark(torg) - shift, pointmark(tdest) - shift, + pointmark(tapex) - shift); + if (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(3); + } + fprintf(outfile, "%ld 0\n", hullsize); + } else { + // Allocate memory for 'trifacelist'. + out->trifacelist = new int[hullsize * 3]; + if (out->trifacelist == (int *) NULL) { + 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 faces.\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(3); + } + // 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) { + terminatetetgen(1); + } + if (bmark) { + // Allocate memory for 'trifacemarkerlist'. + out->trifacemarkerlist = new int[subfaces->items]; + if (out->trifacemarkerlist == (int *) NULL) { + terminatetetgen(1); + } + } + if (b->neighout > 1) { + // '-nn' switch. + out->adjtetlist = new int[subfaces->items * 2]; + if (out->adjtetlist == (int *) NULL) { + terminatetetgen(1); + } + } + out->numberoftrifaces = subfaces->items; + elist = out->trifacelist; + emlist = out->trifacemarkerlist; + } + + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + subfaces->traversalinit(); + faceloop.sh = shellfacetraverse(subfaces); + facenumber = firstindex; // in->firstnumber; + while (faceloop.sh != (shellface *) NULL) { + stpivot(faceloop, abuttingtet); + if (abuttingtet.tet == 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); + neigh1 = getelemmarker(abuttingtet.tet); + } + neigh2 = -1; + sesymself(faceloop); + stpivot(faceloop, abuttingtet); + if (abuttingtet.tet != dummytet) { + //neigh2 = * (int *)(abuttingtet.tet + elemmarkerindex); + neigh2 = getelemmarker(abuttingtet.tet); + } + } + 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 checkseg; + point torg, tdest; + 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; + + if (out == (tetgenio *) NULL) { + outfile = fopen(edgefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", edgefilename); + terminatetetgen(3); + } + // Write the number of edges, boundary markers (0 or 1). + fprintf(outfile, "%ld %d\n", meshedges, !b->nobound); + } else { + // Allocate memory for 'edgelist'. + out->edgelist = new int[meshedges * 2]; + if (out->edgelist == (int *) NULL) { + terminatetetgen(1); + } + if (!b->nobound) { + out->edgemarkerlist = new int[meshedges]; + } + out->numberofedges = meshedges; + 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) { + // Check if the edge is a segment. + tsspivot(&worktet, &checkseg); + if (checkseg.sh != dummysh) { + marker = shellmark(checkseg); + if (marker == 0) { // Does it have no marker? + marker = 1; // Set the default marker for this segment. + } + } else { + marker = 0; // It's not a segment. + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %d", marker); + } else { + emlist[index1++] = marker; + } + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, "\n"); + } + edgenumber++; + } + } + tetloop.tet = tetrahedrontraverse(); + } + + if (out == (tetgenio *) NULL) { + fprintf(outfile, "# Generated by %s\n", b->commandline); + fclose(outfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outsubsegments() Output 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(3); + } + // 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) { + 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(3); + } + // 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) { + 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); + neighbor1 = getelemmarker(tetsym.tet); + tetloop.loc = 3; + sym(tetloop, tetsym); + //neighbor2 = * (int *) (tetsym.tet + elemmarkerindex); + neighbor2 = getelemmarker(tetsym.tet); + tetloop.loc = 1; + sym(tetloop, tetsym); + //neighbor3 = * (int *) (tetsym.tet + elemmarkerindex); + neighbor3 = getelemmarker(tetsym.tet); + tetloop.loc = 0; + sym(tetloop, tetsym); + //neighbor4 = * (int *) (tetsym.tet + elemmarkerindex); + neighbor4 = getelemmarker(tetsym.tet); + 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. // +// // +// Comment: Special thanks to Victor Liu for finding and fixing few bugs. // +// // +/////////////////////////////////////////////////////////////////////////////// + +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; + + // 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; + edges = meshedges; + 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(3); + } + // Number of voronoi points, 3 dim, no attributes, no marker. + fprintf(outfile, "%ld 3 0 0\n", tetrahedrons->items); + } else { + // Allocate space for 'vpointlist'. + out->numberofvpoints = (int) tetrahedrons->items; + out->vpointlist = new REAL[out->numberofvpoints * 3]; + if (out->vpointlist == (REAL *) NULL) { + 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; + 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)); + } + circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift, + ccent[0], ccent[1], ccent[2]); + } else { + out->vpointlist[index++] = ccent[0]; + out->vpointlist[index++] = ccent[1]; + out->vpointlist[index++] = ccent[2]; + } + // 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); + fclose(outfile); + } + + // Output Voronoi edges to .v.edge file. + if (out == (tetgenio *) NULL) { + strcpy(outfilename, b->outfilename); + strcat(outfilename, ".v.edge"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outfilename); + } else { + printf("Writing Voronoi edges.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(outfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outfilename); + terminatetetgen(3); + } + // Number of Voronoi edges, no marker. + fprintf(outfile, "%ld 0\n", faces); + } else { + // Allocate space for 'vpointlist'. + out->numberofvedges = (int) faces; + out->vedgelist = new tetgenio::voroedge[out->numberofvedges]; + } + + // 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); + } 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"); + fprintf(outfile, " %g %g %g\n", infvec[0], infvec[1], infvec[2]); + } else { + vedge->v2 = -1; + vedge->vnormal[0] = infvec[0]; + vedge->vnormal[1] = infvec[1]; + vedge->vnormal[2] = infvec[2]; + } + } else { + if (out == (tetgenio *) NULL) { + fprintf(outfile, " %4d\n", end2 + shift); + } else { + vedge->v2 = end2 + shift; + vedge->vnormal[0] = 0.0; + vedge->vnormal[1] = 0.0; + vedge->vnormal[2] = 0.0; + } + } + // Save the 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); + fclose(outfile); + } + + // Output Voronoi faces to .v.face file. + if (out == (tetgenio *) NULL) { + strcpy(outfilename, b->outfilename); + strcat(outfilename, ".v.face"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outfilename); + } else { + printf("Writing Voronoi faces.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(outfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outfilename); + terminatetetgen(3); + } + // Number of Voronoi faces. + fprintf(outfile, "%ld 0\n", edges); + } else { + out->numberofvfacets = edges; + out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets]; + if (out->vfacetlist == (tetgenio::vorofacet *) NULL) { + terminatetetgen(1); + } + } + + // 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); + } + + // Output Voronoi cells to .v.cell file. + if (out == (tetgenio *) NULL) { + strcpy(outfilename, b->outfilename); + strcat(outfilename, ".v.cell"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outfilename); + } else { + printf("Writing Voronoi cells.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(outfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outfilename); + terminatetetgen(3); + } + // Number of Voronoi cells. + fprintf(outfile, "%ld\n", points->items); + } else { + out->numberofvcells = points->items; + out->vcelllist = new int*[out->numberofvcells]; + if (out->vcelllist == (int **) NULL) { + 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 = new int[arraysize + 1]; + out->vcelllist[vpointcount] = vertarray; + 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); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outmesh2vtk() Save mesh to file in VTK Legacy format. // +// // +// This function was contributed by Bryn Llyod from ETH, 2007. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::outmesh2vtk(char* ofilename) +{ + FILE *outfile; + char vtkfilename[FILENAMESIZE]; + point pointloop; + tetrahedron* tptr; + double x, y, z; + int n1, n2, n3, n4; + int nnodes = 4; + int celltype = 10; + + int NEL = tetrahedrons->items; + int NN = points->items; + + if (ofilename != (char *) NULL && ofilename[0] != '\0') { + strcpy(vtkfilename, ofilename); + } else if (b->outfilename[0] != '\0') { + strcpy(vtkfilename, b->outfilename); + } else { + strcpy(vtkfilename, "unnamed"); + } + strcat(vtkfilename, ".vtk"); + + if (!b->quiet) { + printf("Writing %s.\n", vtkfilename); + } + outfile = fopen(vtkfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", vtkfilename); + return; + } + + //always write big endian + //bool ImALittleEndian = !testIsBigEndian(); + + fprintf(outfile, "# vtk DataFile Version 2.0\n"); + fprintf(outfile, "Unstructured Grid\n"); + fprintf(outfile, "ASCII\n"); // BINARY + fprintf(outfile, "DATASET UNSTRUCTURED_GRID\n"); + fprintf(outfile, "POINTS %d double\n", NN); + + points->traversalinit(); + pointloop = pointtraverse(); + for(int id=0; idtraversalinit(); + tptr = tetrahedrontraverse(); + //elementnumber = firstindex; // in->firstnumber; + if (b->order == 2) { + printf(" Write VTK not implemented for order 2 elements \n"); + return; + } + while (tptr != (tetrahedron *) NULL) { + point p1 = (point) tptr[4]; + point p2 = (point) tptr[5]; + point p3 = (point) tptr[6]; + point p4 = (point) tptr[7]; + n1 = pointmark(p1) - in->firstnumber; + n2 = pointmark(p2) - in->firstnumber; + n3 = pointmark(p3) - in->firstnumber; + n4 = pointmark(p4) - in->firstnumber; + //if(ImALittleEndian){ + // swapBytes((unsigned char *) &nnodes, sizeof(nnodes)); + // swapBytes((unsigned char *) &n1, sizeof(n1)); + // swapBytes((unsigned char *) &n2, sizeof(n2)); + // swapBytes((unsigned char *) &n3, sizeof(n3)); + // swapBytes((unsigned char *) &n4, sizeof(n4)); + //} + //fwrite((char*)(&nnodes),sizeof(int), 1, outfile); + //fwrite((char*)(&n1),sizeof(int), 1, outfile); + //fwrite((char*)(&n2),sizeof(int), 1, outfile); + //fwrite((char*)(&n3),sizeof(int), 1, outfile); + //fwrite((char*)(&n4),sizeof(int), 1, outfile); + fprintf(outfile, "%d %4d %4d %4d %4d\n", nnodes, n1, n2, n3, n4); + tptr = tetrahedrontraverse(); + } + fprintf(outfile, "\n"); + + fprintf(outfile, "CELL_TYPES %d\n", NEL); + for(int tid=0; tidquiet) { + 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 if it is a dead tet. + if (!isdead(&oppotet)) { + // 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++; + } + } else { + printf(" !! !! A dead neighbor:\n"); + printtet(&tetraloop); + horrors++; + } + } + } + /*if (infected(tetraloop)) { + pts = (point *) &(tetraloop.tet[4]); + printf(" !! tet (%d, %d, %d, %d) is infected.\n", pointmark(pts[0]), + pointmark(pts[1]), pointmark(pts[2]), pointmark(pts[3])); + horrors++; + } + if (marktested(tetraloop)) { + pts = (point *) &(tetraloop.tet[4]); + printf(" !! tet (%d, %d, %d, %d) is marktested.\n", pointmark(pts[0]), + pointmark(pts[1]), pointmark(pts[2]), pointmark(pts[3])); + 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); + } + + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkshells() Test the boundary mesh for topological consistency. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int 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, j; + + 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) { + // Check if the tet and the face have the same vertices. + for (i = 0; i < 3; i++) { + pinfect((point) shloop.sh[3 + i]); + } + for (j = 0; j < 3; j++) { + if (org(oppotet) == NULL) break; + if (!pinfected(org(oppotet))) break; + enextself(oppotet); + } + for (i = 0; i < 3; i++) { + puninfect((point) shloop.sh[3 + i]); + } + if (j < 3) { + printf(" !! !! Wrong subface-tet connection.\n"); + printf(" p:draw_subface(%d, %d, %d).\n", pointmark(sorg(shloop)), + pointmark(sdest(shloop)), pointmark(sapex(shloop))); + printf(" p:draw_tet(%d, %d, %d, %d).\n", + pointmark(org(oppotet)), pointmark(dest(oppotet)), + pointmark(apex(oppotet)), pointmark(oppo(oppotet))); + horrors++; + } + 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) { + // Check if the subface is self-bonded. + if (testsh.sh == shloop.sh) { + printf(" !! !! Subface is self-bonded.\n"); + printsh(&shloop); + horrors++; + } + 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); + } + if (sinfected(shloop)) { + printf(" !! subface (%d, %d, %d) is infected.\n", + pointmark(sorg(shloop)), pointmark(sdest(shloop)), + pointmark(sapex(shloop))); + horrors++; + } + if (!b->quality) { + // During refinement, subfaces/subsegs rejected to be split were + // marktested. In other cases, they should be not. + if (smarktested(shloop)) { + printf(" !! subface (%d, %d, %d) is marktested.\n", + pointmark(sorg(shloop)), pointmark(sdest(shloop)), + pointmark(sapex(shloop))); + horrors++; + } + } + shloop.sh = shellfacetraverse(subfaces); + } + + if (horrors > 0) { + return horrors; + } + + // 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); + if (spin.sh != dummysh) { + 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++; + } else { + break; + } + } 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 horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checksegments() Check the connections between tetrahedra and segments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checksegments() +{ + triface tetloop, neightet; + face sseg, checkseg; + point pa, pb; + int hitbdry; + int horrors, i; + + if (!b->quiet) { + printf(" Checking tet-seg connections...\n"); + } + + horrors = 0; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != NULL) { + // Loop the six edges of the tet. + if (tetloop.tet[8] != NULL) { + for (i = 0; i < 6; i++) { + tetloop.loc = edge2locver[i][0]; + tetloop.ver = edge2locver[i][1]; + tsspivot1(tetloop, sseg); + if (sseg.sh != dummysh) { + // Check if they are the same edge. + sseg.shver = 0; + pa = (point) sorg(sseg); + pb = (point) sdest(sseg); + if (!(((org(tetloop) == pa) && (dest(tetloop) == pb)) || + ((org(tetloop) == pb) && (dest(tetloop) == pa)))) { + printf(" !! Wrong tet-seg connection.\n"); + printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", + (unsigned long) tetloop.tet, pointmark(org(tetloop)), + pointmark(dest(tetloop)), pointmark(apex(tetloop)), + pointmark(oppo(tetloop)), (unsigned long) sseg.sh, + pointmark(pa), pointmark(pb)); + horrors++; + } else { + // Loop all tets sharing at this edge. + neightet = tetloop; + hitbdry = 0; + do { + tsspivot1(neightet, checkseg); + if (checkseg.sh != sseg.sh) { + printf(" !! Wrong tet-seg connection.\n"); + printf(" Tet: x%lx (%d, %d, %d, %d) - ", + (unsigned long) tetloop.tet, pointmark(org(tetloop)), + pointmark(dest(tetloop)), pointmark(apex(tetloop)), + pointmark(oppo(tetloop))); + if (checkseg.sh != NULL) { + printf("Seg x%lx (%d, %d).\n", (unsigned long) checkseg.sh, + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } else { + printf("Seg: NULL.\n"); + } + horrors++; + } + tfnextself(neightet); + if (neightet.tet == dummytet) { + hitbdry++; + if (hitbdry == 2) break; + esym(tetloop, neightet); + tfnextself(neightet); + if (neightet.tet == dummytet) break; + } + } while (neightet.tet != tetloop.tet); + } + } + } + } + tetloop.tet = tetrahedrontraverse(); + } + + if (horrors == 0) { + printf(" Segments are connected properly.\n"); + } else { + printf(" !! !! !! !! Found %d missing connections.\n", horrors); + } + + return horrors; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkdelaunay() Ensure that the mesh is constrained Delaunay. // +// // +// If 'flipqueue' is not NULL, non-locally Delaunay faces are saved in it. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue) +{ + triface tetraloop; + triface oppotet; + face opposhelle; + point tetorg, tetdest, tetapex, tetoppo; + point oppooppo; + 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); + } + 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); + } + } + + return 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::algorithmicstatistics() +{ + printf("Algorithmic statistics:\n\n"); + + printf(" Number of orient3d tests: %ld\n", orient3dcount); + printf(" Number of insphere tests: %ld\n", inspherecount); + printf(" Number of symbolic insphere tests: %ld\n", insphere_sos_count); + printf(" Number of visited tets in point location: %ld\n", ptloc_count); + printf(" Maximal number of tets per point location: %ld\n",ptloc_max_count); + printf(" Number of hull sites: %ld\n", inserthullcount); + printf(" Number of 1-to-4 flips: %ld\n", flip14count); + printf(" Number of 2-to-6 flips: %ld\n", flip26count); + printf(" Number of n-t-2n flips: %ld\n", flipn2ncount); + + if (!b->plc) { + if (1) { + printf(" Number of deleted tets: %ld\n", totaldeadtets); + printf(" Number of created tets: %ld\n", totalbowatcavsize); + printf(" Maximum number of tets per new point: %ld\n", maxbowatcavsize); + // printf(" Number of 3-to-2 flips: %ld\n", flip32count); + } else { + // printf(" Number of 3-to-2 flips: %ld\n", flip32count); + // printf(" Number of 2-to-3 flips: %ld\n", flip23count); + // printf(" Number of n-to-m flips: %ld\n", flipnmcount); + // printf(" Total number of primitive flips: %ld\n", + // flip23count + flip32count); + } + } + + if (b->plc) { + printf(" Number of 2-to-2 flips: %ld\n", flip22count); + // printf(" Number of tri-edge inter (coplanar) tests: %ld (%ld)\n", + // triedgcount, triedgcopcount); + printf(" Number of crossed faces (edges) in scout segs: %ld (%ld)\n", + across_face_count, across_edge_count); + printf(" Maximal number of crossed faces per segment: %ld\n", + across_max_count); + printf(" Number of rule-1 points: %ld\n", r1count); + printf(" Number of rule-2 points: %ld\n", r2count); + printf(" Number of rule-3 points: %ld\n", r3count); + printf(" Maximal size of a missing region: %ld\n", maxregionsize); + printf(" Maximal size of a recovered cavity: %ld\n", maxcavsize); + printf(" Number of non-Delaunay edges: %ld\n", ndelaunayedgecount); + printf(" Number of cavity expansions: %ld\n", cavityexpcount); + } + + // printf(" Total point location time (millisec): %g\n", tloctime * 1e+3); + // printf(" Total point insertion time (millisec): %g\n",tinserttime*1e+3); + // if (b->bowyerwatson == 0) { + // printf(" Total flip time (millisec): %g\n", tfliptime * 1e+3); + // } + + printf("\n"); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// qualitystatistics() Print statistics about the quality of the mesh. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::qualitystatistics() +{ + triface tetloop, neightet; + point p[4]; + char sbuf[128]; + REAL radiusratiotable[12]; + REAL aspectratiotable[12]; + REAL A[4][4], rhs[4], D; + REAL V[6][3], N[4][3], H[4]; // edge-vectors, face-normals, face-heights. + REAL edgelength[6], alldihed[6], faceangle[3]; + REAL shortest, longest; + REAL smallestvolume, biggestvolume; + REAL smallestratio, biggestratio; + REAL smallestdiangle, biggestdiangle; + REAL smallestfaangle, biggestfaangle; + REAL 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; + smallestratio = minaltitude; + biggestratio = 0.0; + smallestdiangle = smallestfaangle = 180.0; + biggestdiangle = biggestfaangle = 0.0; + + // Loop all elements, calculate quality parameters for each element. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + + // Get four vertices: p0, p1, p2, p3. + for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i]; + // 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; + // Remember the largest and smallest aspect ratio.. + if (tetaspect < smallestratio) { + smallestratio = tetaspect; + } + if (tetaspect > biggestratio) { + biggestratio = tetaspect; + } + // Accumulate the corresponding number in the aspect ratio histogram. + aspectindex = 0; + while ((tetaspect > aspectratiotable[aspectindex]) && (aspectindex < 11)) { + aspectindex++; + } + aspecttable[aspectindex]++; + radiusindex = 0; + while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) { + radiusindex++; + } + radiustable[radiusindex]++; + + tetloop.tet = tetrahedrontraverse(); + } + + shortest = sqrt(shortest); + longest = sqrt(longest); + minaltitude = sqrt(minaltitude); + + printf(" Smallest volume: %16.5g | Largest volume: %16.5g\n", + smallestvolume, biggestvolume); + printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n", + shortest, longest); + printf(" Smallest aspect ratio: %9.5g | Largest aspect ratio: %9.5g\n", + smallestratio, biggestratio); + sprintf(sbuf, "%.17g", biggestfaangle); + if (strlen(sbuf) > 8) { + sbuf[8] = '\0'; + } + printf(" Smallest facangle: %14.5g | Largest facangle: %s\n", + smallestfaangle, sbuf); + sprintf(sbuf, "%.17g", biggestdiangle); + if (strlen(sbuf) > 8) { + sbuf[8] = '\0'; + } + printf(" Smallest dihedral: %14.5g | Largest dihedral: %s\n\n", + smallestdiangle, sbuf); + + /* + printf(" 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); + printf(" Mesh faces: %ld\n", (4l * tetrahedrons->items + hullsize) / 2l); + printf(" Mesh edges: %ld\n", meshedges); + + if (b->plc || b->refine) { + printf(" Mesh boundary faces: %ld\n", subfaces->items); + printf(" Mesh boundary edges: %ld\n\n", subsegs->items); + } else { + printf(" Convex hull faces: %ld\n\n", hullsize); + } + + if (b->verbose > 0) { + if (b->plc || b->refine) { + qualitystatistics(); + } + // algorithmicstatistics(); + } +} + +//// //// +//// //// +//// report_cxx /////////////////////////////////////////////////////////////// + +//// main_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedralize() The interface for users using TetGen library to // +// generate tetrahedral meshes with all features. // +// // +// The sequence is roughly as follows. Many of these steps can be skipped, // +// depending on the command line switches. // +// // +// - Initialize constants and parse the command line. // +// - Read the vertices from a file and either // +// - tetrahedralize them (no -r), or // +// - read an old mesh from files and reconstruct it (-r). // +// - Insert the PLC segments and facets (-p). // +// - Read the holes (-p), regional attributes (-pA), and regional volume // +// constraints (-pa). Carve the holes and concavities, and spread the // +// regional attributes and volume constraints. // +// - Enforce the constraints on minimum quality bound (-q) and maximum // +// volume (-a). Also enforce the conforming Delaunay property (-q and -a). // +// - Promote the mesh's linear tetrahedra to higher order elements (-o). // +// - Write the output files and print the statistics. // +// - Check the consistency and Delaunay property of the mesh (-C). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, + tetgenio *addin, tetgenio *bgmin) +{ + tetgenmesh m; + // Variables for timing the performance of TetGen (defined in time.h). + clock_t tv[17]; + + tv[0] = clock(); + + m.b = b; + m.in = in; + 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(); + if (m.hullsize == 0l) { + printf("The input point set does not span a 3D subspace.\n"); + return; + } + } + + 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(); + tv[14] = clock(); + if (b->diagnose != 1) { + m.markacutevertices(60.0); + m.formskeleton(tv[15]); + } else { + m.detectinterfaces(); + } + } + + tv[4] = clock(); + + if (!b->quiet) { + if (b->useshelles && !b->refine) { + if (b->diagnose != 1) { + printf("Boundary recovery "); + } else { + printf("Intersection "); + } + printf("seconds: %g\n", (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); + /*if ((b->diagnose != 1) && (b->verbose > 0)) { + printf(" Surface mesh seconds: %g\n", + (tv[14] - tv[3]) / (REAL) CLOCKS_PER_SEC); + printf(" Segment recovery seconds: %g\n", + (tv[15] - tv[14]) / (REAL) CLOCKS_PER_SEC); + printf(" Facet recovery seconds: %g\n", + (tv[4] - tv[15]) / (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.optimizemesh2(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.removesteiners2(); + } + + 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.removesteiners2(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.optimizemesh2(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->metric) { //if (b->quality && b->metric) { + m.outmetrics(out); + } + } + } + + if (b->noelewritten == 1) { + if (!b->quiet) { + printf("NOT writing an .ele file.\n"); + } + m.numberedges(); + } 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 (!out && b->vtkview) { + m.outmesh2vtk(b->outfilename); + } + + if (b->neighout) { + m.outneighbors(out); + } + + if (b->voroout) { + 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) { + if (m.checkdelaunay(0.0, NULL) > 0) { + assert(0); + } + 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(0); + } + if (b.refine) { + if (!in.load_tetmesh(b.infilename)) { + terminatetetgen(3); + } + } else { + if (!in.load_plc(b.infilename, (int) b.object)) { + terminatetetgen(3); + } + } + 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 +} + +//// //// +//// //// +//// main_cxx ///////////////////////////////////////////////////////////////// + diff --git a/tetgen.h b/tetgen.h new file mode 100644 index 0000000..69f4a21 --- /dev/null +++ b/tetgen.h @@ -0,0 +1,3431 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// TetGen // +// // +// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// // +// Version 1.4 // +// September 6, December 13, 2010 // +// January 19, 2011 // +// // +// Copyright (C) 2002--2011 // +// Hang Si // +// Research Group: Numerical Mathematics and Scientific Computing // +// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // +// Mohrenstr. 39, 10117 Berlin, Germany // +// si@wias-berlin.de // +// // +// TetGen is 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 is a library to generate tetrahedral meshes for 3D domains. It's // +// main purpose is to generate suitable tetrahedral meshes for numerical // +// simulations using 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". International // +// Journal for Numerical Methods in Engineering, 75(7): 856--880, 2008. // +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef tetgenH +#define tetgenH + +#include +#include +#include +#include +#include +#include + +// The types 'intptr_t' and 'uintptr_t' are signed and unsigned integer types, +// respectively. They are guaranteed to be the same width as a pointer. +// They are defined in by the C99 Standard. +// However, Microsoft Visual C++ doesn't ship with this header file yet. We +// need to define them. (Thanks to Steven G. Johnson from MIT for the +// following piece of code.) + +// Define the _MSC_VER symbol if you are using Microsoft Visual C++. + +// #define _MSC_VER + +// Define the _WIN64 symbol if you are running TetGen on Win64. + +// #define _WIN64 + +#ifdef _MSC_VER // Microsoft Visual C++ +# ifdef _WIN64 + typedef __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +# else // not _WIN64 + typedef int intptr_t; + typedef unsigned int uintptr_t; +# endif +#else // not Visual C++ +# include +#endif + +// 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 a bit. + +// #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 + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class tetgenio // +// // +// The interface for 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(). // +// // +// tetgenio ontains routines for reading and writing TetGen's files, i.e., // +// .node, .poly, .smesh, .ele, .face, and .edge files. Both the library of // +// TetGen and TetView use these routines to process input files. // +// // +/////////////////////////////////////////////////////////////////////////////// + +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" describes a simple polygon + // (no holes). It is not necessarily convex. Each polygon contains a + // number of corners (points) and the same number of sides (edges). + // Note that the points of the polygon must be given in either counter- + // clockwise or clockwise order and they form a ring, so every two + // consective points forms an edge of the polygon. + 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" describes a facet. Each facet is + // a polygonal region possibly with holes, edges, and points in it. + 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; + + // A callback function for mesh refinement. + typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL); + + // Items are numbered starting from 'firstnumber' (0 or 1), default is 0. + int firstnumber; + + // Dimension of the mesh (2 or 3), default is 3. + int mesh_dim; + + // Does the lines in .node file contain index or not, default is 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 (at index [0]) is the facet + // marker (cast it to int), the second (at index [1]) is its maximum + // area bound. + REAL *facetconstraintlist; + int numberoffacetconstraints; + + // `segmentconstraintlist': An array of segment max. length constraints. + // Three REALs per constraint. The first two (at indcies [0] and [1]) + // are the indices of the endpoints of the segment, the third (at index + // [2]) is its maximum length bound. + 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; + + // A callback function. + TetSizeFunc tetunsuitable; + + // Input & output routines. + bool load_node_call(FILE* infile, int markers, char* nodefilename); + bool load_node(char* filebasename); + bool load_var(char*); + bool load_mtr(char*); + bool load_poly(char*); + bool load_pbc(char*); + bool load_off(char*); + bool load_ply(char*); + bool load_stl(char*); + bool load_medit(char*); + bool load_vtk(char*); + bool load_plc(char*, int); + bool load_tetmesh(char*); + void save_nodes(char*); + void save_elements(char*); + void save_faces(char*); + void save_edges(char*); + void save_neighbors(char*); + void save_poly(char*); + + // Read line and parse string functions. + char *readline(char* string, FILE* infile, int *linenumber); + char *findnextfield(char* string); + char *readnumberline(char* string, FILE* infile, char* infilename); + char *findnextnumber(char* string); + + // Initialize routine. + void 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; + + tetunsuitable = NULL; + } + + // Free the memory allocated in 'tetgenio'. + void deinitialize() + { + facet *f; + polygon *p; + pbcgroup *pg; + int i, j; + + // This routine assumes that the memory was allocated by + // C++ memory allocation operator 'new'. + + 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; + } + } + + // Constructor & destructor. + tetgenio() {initialize();} + ~tetgenio() {deinitialize();} +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class tetgenbehavior // +// // +// The object holding a collection of options controlling TetGen's behavior. // +// See "command line switches" in User's manual. // +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +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, VTK, MESH}; + + // Variables of command line switches. Each variable corresponds to a + // switch and will be initialized. + + 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 btree; // -u, 1. + int max_btreenode_size; // number after -u, 100. + int optlevel; // number specified after '-s' switch, 3. + int optpasses; // number specified after '-ss' switch, 3. + 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 vtkview; // '-K' 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. + int maxflipedgelinksize; // The maximum flippable edge link size 10. + 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]; + + void syntax(); + void usage(); + + // Command line parse routine. + bool parse_commandline(int argc, char **argv); + bool parse_commandline(char *switches) { + return parse_commandline(0, &switches); + } + + // Initialize all variables. + tetgenbehavior() + { + plc = 0; + 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; + btree = 1; + max_btreenode_size = 100; + facesout = 0; + edgesout = 0; + neighout = 0; + voroout = 0; + meditview = 0; + gidview = 0; + geomview = 0; + vtkview = 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; + maxflipedgelinksize = 10; + epsilon = 1.0e-8; + epsilon2 = 1.0e-5; + object = NONE; + + commandline[0] = '\0'; + infilename[0] = '\0'; + outfilename[0] = '\0'; + addinfilename[0] = '\0'; + bgmeshfilename[0] = '\0'; + } + + ~tetgenbehavior() + { + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class tetgenmesh // +// // +// The object to store, generate, and refine a tetrahedral mesh. // +// // +// It implements the mesh data structures and functions to create and update // +// a tetrahedral mesh according to the specified options. // +// // +/////////////////////////////////////////////////////////////////////////////// + +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 (see Muecke's thesis). + enum {CCW = 0, CW = 1}; + + // Labels that signify whether a record consists primarily of pointers + // or of floating-point words. Used for data alignment. + enum wordtype {POINTER, FLOATINGPOINT}; + + // Labels that signify the type of a vertex. + 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. + enum fliptype {T23, T32, T22, T44, N32, N40, FORBIDDENFACE, FORBIDDENEDGE}; + + // Labels that signify the result of triangle-triangle intersection test. + enum interresult {DISJOINT, INTERSECT, SHAREVERTEX, SHAREEDGE, SHAREFACE, + TOUCHEDGE, TOUCHFACE, INTERVERT, INTEREDGE, INTERFACE, INTERTET, + TRIEDGEINT, EDGETRIINT, COLLISIONFACE, INTERSUBSEG, INTERSUBFACE, + BELOWHULL2}; + + // Labels that signify the result of point location. + enum locateresult {INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, OUTSIDE, + ENCSEGMENT}; + + // Labels that signify the result of vertex insertion. + enum insertsiteresult {SUCCESSINTET, SUCCESSONFACE, SUCCESSONEDGE, + DUPLICATEPOINT, OUTSIDEPOINT}; + + // Labels that signify the result of direction finding. + enum finddirectionresult {ACROSSEDGE, ACROSSFACE, LEFTCOLLINEAR, + RIGHTCOLLINEAR, TOPCOLLINEAR, BELOWHULL}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh elements // +// // +// 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. // +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh handles // +// // +// Two special data types, 'triface' and 'face' are defined for maintaining // +// and updating meshes. They are like pointers (or handles), which allow you // +// to hold one particular part of the mesh, i.e., a tetrahedron, a triangle, // +// an edge and a vertex. However, these data types do not themselves store // +// any part of the mesh. The mesh is made of the data types defined above. // +// // +// 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; + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Elementary flip data structure // +// // +// A data structure to record three types of elementary flips, which are // +// 2-to-3, 3-to-2, and 2-to-2 flips. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class elemflip { + + public: + + enum fliptype ft; // ft \in {T23, T32, T22}. + point pset1[3]; + point pset2[3]; + + elemflip() { + ft = T23; // Default. + pset1[0] = pset1[1] = pset1[2] = (point) NULL; + pset2[0] = pset2[1] = pset2[2] = (point) NULL; + } + + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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]; + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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]; + + // The map from a given face ('loc') to the other three faces in the tet. + // and the map from a given face's edge ('loc', 'ver') to other two + // faces in the tet opposite to this edge. (used in speeding the Bowyer- + // Watson cavity construction). + static int locpivot[4][3]; + static int locverpivot[4][6][2]; + + // 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 symedge(triface& t1, triface& t2); + inline void symedgeself(triface& t); + inline void tfnext(triface& t1, triface& t2); + inline void tfnextself(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 REAL elemattribute(tetrahedron* ptr, int attnum); + inline void setelemattribute(tetrahedron* ptr, int attnum, REAL value); + inline REAL volumebound(tetrahedron* ptr); + inline void setvolumebound(tetrahedron* ptr, REAL value); + inline int getelemmarker(tetrahedron* ptr); + inline void setelemmarker(tetrahedron* ptr, int value); + inline void infect(triface& t); + inline void uninfect(triface& t); + inline bool infected(triface& t); + inline void marktest(triface& t); + inline void unmarktest(triface& t); + inline bool marktested(triface& t); + inline void markface(triface& t); + inline void unmarkface(triface& t); + inline bool facemarked(triface& t); + inline void markedge(triface& t); + inline void unmarkedge(triface& t); + inline bool edgemarked(triface& t); + + // 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 void pinfect(point pt); + inline void puninfect(point pt); + inline bool pinfected(point pt); + inline tetrahedron point2tet(point pt); + inline void setpoint2tet(point pt, tetrahedron value); + inline shellface point2sh(point pt); + inline void setpoint2sh(point pt, shellface value); + inline shellface point2seg(point pt); + inline void setpoint2seg(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*); + void point2tetorg(point, triface&); + void point2shorg(point, face&); + void point2segorg(point, face&); + 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 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*); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Arraypool // +// // +// Each arraypool contains an array of pointers to a number of blocks. Each // +// block contains the same fixed number of objects. Each index of the array // +// addesses a particular object in the pool. The most significant bits add- // +// ress the index of the block containing the object. The less significant // +// bits address this object within the block. // +// // +// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // +// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // +// of allocated objects; 'totalmemory' is the totoal memorypool in bytes. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class arraypool { + + public: + + int objectbytes; + int objectsperblock; + int log2objectsperblock; + int toparraylen; + char **toparray; + long objects; + unsigned long totalmemory; + + void restart(); + void poolinit(int sizeofobject, int log2objperblk); + char* getblock(int objectindex); + void* lookup(int objectindex); + int newindex(void **newptr); + + arraypool(int sizeofobject, int log2objperblk); + ~arraypool(); + }; + +// fastlookup() -- A fast, unsafe operation. Return the pointer to the object +// with a given index. Note: The object's block must have been allocated, +// i.e., by the function newindex(). + +#define fastlookup(pool, index) \ + (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ + ((index) & ((pool)->objectsperblock - 1)) * (pool)->objectbytes) + + +// 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. +// A 'compfunc' is a pointer to a linear-order function. + + typedef int (*compfunc) (const void *, const void *); + +/////////////////////////////////////////////////////////////////////////////// +// // +// List // +// // +// An array of items with automatically reallocation of memory. // +// // +// 'base' is the starting address of the array. 'itembytes' is the size of // +// each item in byte. // +// // +// '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. // +// // +// 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]. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class list { + + public: + + char *base; + int itembytes; + int items, maxitems, expandsize; + compfunc comp; + + list(int itbytes, compfunc pcomp, int mitems = 256, int exsize = 128) { + listinit(itbytes, pcomp, 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); + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Memorypool // +// // +// A type used to allocate memory. // +// // +// 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; + + 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(); + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Queue // +// // +// A 'queue' is a FIFO data structure. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class queue : public memorypool { + + public: + + void **head, **tail; + int linkitembytes; + int linkitems; // Not count 'head' and 'tail'. + + queue(int bytecount, int itemcount = 256) { + linkitembytes = bytecount; + poolinit(bytecount + sizeof(void *), itemcount, POINTER, 0); + head = (void **) alloc(); + tail = (void **) alloc(); + *head = (void *) tail; + *tail = NULL; + linkitems = 0; + } + + void clear() { + // Reset the pool. + restart(); + // Initialize all variables. + head = (void **) alloc(); + tail = (void **) alloc(); + *head = (void *) tail; + *tail = NULL; + linkitems = 0; + } + + long len() { return linkitems; } + bool empty() { return linkitems == 0; } + + void *push(void* newitem) { + void **newnode = tail; + if (newitem != (void *) NULL) { + memcpy((void *)(newnode + 1), newitem, linkitembytes); + } + tail = (void **) alloc(); + *tail = NULL; + *newnode = (void *) tail; + linkitems++; + return (void *)(newnode + 1); + } + + void *pop() { + if (linkitems > 0) { + void **deadnode = (void **) *head; + *head = *deadnode; + dealloc((void *) deadnode); + linkitems--; + return (void *)(deadnode + 1); + } else { + return NULL; + } + } + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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*); + + void makepoint2tetmap(); + void makepoint2segmap(); + void makeindex2pointmap(point*&); + void makesegmentmap(int*&, shellface**&); + void makesubfacemap(int*&, shellface**&); + void maketetrahedronmap(int*&, tetrahedron**&); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Geometric functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + // PI is the ratio of a circle's circumference to its diameter. + static REAL PI; + + // Triangle-triangle intersection test + 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*); + int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); + int tri_edge_test(point, point, point, point, point, point, int, int*, int*); + + // Geometric tests + REAL incircle3d(point pa, point pb, point pc, point pd); + REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); + 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 calculations + 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 facenormal2(point pa, point pb, point pc, REAL *n, int pivot); + 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 planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); + + // Point location routines. + unsigned long randomnation(unsigned int choices); + REAL distance2(tetrahedron* tetptr, point p); + void randomsample(point searchpt, triface *searchtet); + enum locateresult locate(point searchpt, triface* searchtet); + enum locateresult locate2(point searchpt, triface* searchtet, arraypool*); + enum locateresult preciselocate(point searchpt, triface* searchtet, long); + 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 update functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + void enqueueflipface(triface&, queue*); + void enqueueflipedge(face&, queue*); + void flip23(triface*, queue*); + void flip32(triface*, queue*); + void flip22(triface*, queue*); + void flip22sub(face*, queue*); + long lawson3d(queue* flipqueue); + long lawson(queue* flipqueue); + + bool removetetbypeeloff(triface *striptet, triface*); + 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, triface*, queue*); + void splittetface(point, triface*, queue*); + void splitsubface(point, face*, queue*); + bool splittetedge(point, triface*, queue*); + void splitsubedge(point, face*, queue*); + + void formstarpolyhedron(point pt, list* tetlist, list* verlist, bool); + 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 functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Point sorting routines. + void btree_sort(point*, int, int, REAL, REAL, REAL, REAL, REAL, REAL, int); + void btree_insert(point insertpt); + void btree_search(point searchpt, triface* searchtet); + void ordervertices(point* vertexarray, int arraysize); + + enum locateresult insertvertexbw(point insertpt, triface *searchtet, + bool bwflag, bool visflag, + bool noencsegflag, bool noencsubflag); + bool unifypoint(point testpt, triface*, enum locateresult, REAL); + bool incrflipdelaunay(triface*, point*, long, bool, bool, REAL, queue*); + long delaunizevertices(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Surface triangulation functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + enum locateresult sinsertvertex(point insertpt, face *splitsh,face *splitseg, + bool bwflag, bool cflag); + void formstarpolygon(point pt, list* trilist, list* verlist); + void getfacetabovepoint(face* facetsh); + bool 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 assignsegmentmarkers(); + 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(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Constrained Delaunay tetrahedralization functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Segment recovery routines. + void markacutevertices(REAL acuteangle); + enum finddirectionresult finddirection(triface* searchtet, point, long); + enum interresult finddirection2(triface* searchtet, point); + enum interresult finddirection3(triface* searchtet, point); + enum interresult scoutsegment2(face*, triface*, point*); + void getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt); + void getsegmentsplitpoint3(face* sseg, point refpt, REAL* vt); + void delaunizesegments2(); + + // Facets recovery routines. + enum interresult scoutsubface(face* ssub, triface* searchtet, int); + enum interresult scoutcrosstet(face* ssub, triface* searchtet, arraypool*); + void recoversubfacebyflips(face* pssub, triface* crossface, arraypool*); + void formcavity(face*, arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*, arraypool*); + bool delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); + bool fillcavity(arraypool*, arraypool*, arraypool*, arraypool*); + void carvecavity(arraypool*, arraypool*, arraypool*); + void restorecavity(arraypool*, arraypool*, arraypool*); + void splitsubedge(point, face*, arraypool*, arraypool*); + void constrainedfacets2(); + + void formskeleton(clock_t&); + + // 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 removal functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + void initializecavity(list* floorlist, list* ceillist, list* frontlist, + list* ptlist, list* gluelist); + bool delaunizecavvertices(triface*, list*, list*, list*, queue*); + void retrievenewtets(list* newtetlist); + void insertauxsubface(triface* front, triface* idfront); + bool scoutfront(triface* front, triface* idfront); + void gluefronts(triface* front, triface* front1, list* gluetetlist, + list* glueshlist); + bool identifyfronts(list* frontlist,list* misfrontlist,list* gluetetlist, + list* glueshlist); + void detachauxsubfaces(list* newtetlist); + bool carvecavity(list* newtetlist, list* outtetlist, list* gluetetlist, + queue* flipque); + + void replacepolygonsubs(list* oldshlist, list* newshlist); + void orientnewsubs(list* newshlist, face* orientsh, REAL* norm); + bool registerelemflip(enum fliptype ft, point pa1, point pb1, point pc1, + point pa2, point pb2, point pc2); + bool check4fixededge(point pa, point pb); + bool removeedgebyflips(triface* remedge, int*); + bool removefacebyflips(triface* remface, int*); + bool recoveredgebyflips(triface* searchtet, point pb, int*); + bool recoverfacebyflips(triface* front, int*); + bool constrainedcavity(triface* oldtet, list* floorlist, list* ceillist, + list* ptlist, list* frontlist, list* misfrontlist, + list* newtetlist, list* gluetetlist, list* glueshlist, + queue* flipque); + bool findrelocatepoint2(point sp, point np, REAL* n, list*, list*); + bool 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); + void removesteiners2(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh rebuild functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + void transfernodes(); + long reconstructmesh(); + void insertconstrainedpoints(tetgenio *addio); + bool p1interpolatebgm(point pt, triface* bgmtet, long *scount); + void interpolatesizemap(); + void duplicatebgmesh(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh refinement functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + 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 setnewpointsize(point newpt, point e1, point e2); + bool 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 // +// // +/////////////////////////////////////////////////////////////////////////////// + + bool checktet4ill(triface* testtet, bool enqflag); + bool checktet4opt(triface* testtet, bool enqflag); + bool removeedge(badface* remedge, bool optflag); + bool smoothpoint(point smthpt, point, point, list*, bool, REAL*); + bool smoothsliver(badface* remedge, list *starlist); + bool splitsliver(badface* remedge, list *tetlist, list *ceillist); + void tallslivers(bool optflag); + void optimizemesh2(bool optflag); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh output functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + void jettisonnodes(); + void highorder(); + void numberedges(); + void outnodes(tetgenio*); + void outmetrics(tetgenio*); + void outelements(tetgenio*); + void outfaces(tetgenio*); + void outhullfaces(tetgenio*); + void outsubfaces(tetgenio*); + void outedges(tetgenio*); + void outsubsegments(tetgenio*); + void outneighbors(tetgenio*); + void outvoronoi(tetgenio*); + void outsmesh(char*); + void outmesh2medit(char*); + void outmesh2gid(char*); + void outmesh2off(char*); + void outmesh2vtk(char*); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh check functions // +// // +/////////////////////////////////////////////////////////////////////////////// + + int checkmesh(); + int checkshells(); + int checksegments(); + int checkdelaunay(REAL, queue*); + void checkconforming(); + void algorithmicstatistics(); + void qualitystatistics(); + void statistics(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Debug functions // +// // +/////////////////////////////////////////////////////////////////////////////// + /* + void ptet(triface* t); + void psh(face* s); + int pteti(int i, int j, int k, int l); + void pface(int i, int j, int k); + bool pedge(int i, int j); + int psubface(int i, int j, int k); + void psubseg(int i, int j); + int pmark(point p); + void pvert(point p); + int pverti(int i); + REAL test_orient3d(int i, int j, int k, int l); + REAL test_insphere(int i, int j, int k, int l, int m); + REAL test_insphere_s(int i, int j, int k, int l, int m); + void print_tetarray(arraypool* tetarray); + void print_tetlist(list* tetlist); + void print_facearray(arraypool* facearray); + void print_facelist(list* facelist); + void print_subfacearray(arraypool* subfacearray); + void print_subfacelist(list* subfacelist); + void dump_facetof(face* pssub); + void print_fliptetlist(triface *fliptet); + void print_deaditemstack(void* deaditemstack); + int check_deaditemstack(void* deaditemstack, uintptr_t addr); + void print_abtetlist(triface *abtetlist, int len); + int checkpoint2tetmap(); + int checkpoint2submap(); + int checkpoint2segmap(); + */ +/////////////////////////////////////////////////////////////////////////////// +// // +// Class variables // +// // +/////////////////////////////////////////////////////////////////////////////// + + // 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 *tet2segpool, *tet2subpool; + + // 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. + + // Entry to find the binary tree nodes (-u option). + arraypool *btreenode_list; + // The maximum size of a btree node (number after -u option) is + int max_btreenode_size; // <= b->max_btreenode_size. + // The maximum btree depth (for bookkeeping). + int max_btree_depth; + + // Arrays used by Bowyer-Watson algorithm. + arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; + arraypool *caveshlist, *caveshbdlist; + // Stacks used by the boundary recovery algorithm. + arraypool *subsegstack, *subfacstack; + + // Two handles used in constrained facet recovery. + triface firsttopface, firstbotface; + + // An array for registering elementary flips. + arraypool *elemfliplist; + + // An array of fixed edges for facet recovering by flips. + arraypool *fixededgelist; + + // 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, dummypoint; + + // 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. + long meshedges; // Number of output mesh edges. + 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 jettisoninverts; // The number of jettisoned input vertices. + 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. + bool b_steinerflag; + + // Algorithm statistical counters. + long ptloc_count, ptloc_max_count; + long orient3dcount; + long inspherecount, insphere_sos_count; + long flip14count, flip26count, flipn2ncount; + long flip22count; + long inserthullcount; + long maxbowatcavsize, totalbowatcavsize, totaldeadtets; + long across_face_count, across_edge_count, across_max_count; + long maxcavsize, maxregionsize; + long ndelaunayedgecount, cavityexpcount; + long opt_tet_peels, opt_face_flips, opt_edge_flips; + + 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 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. + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class constructor & destructor // +// // +/////////////////////////////////////////////////////////////////////////////// + + 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; + tet2segpool = NULL; + tet2subpool = NULL; + + dummytet = (tetrahedron *) NULL; + dummytetbase = (tetrahedron *) NULL; + dummysh = (shellface *) NULL; + dummyshbase = (shellface *) NULL; + + facetabovepointarray = (point *) NULL; + abovepoint = (point) NULL; + dummypoint = NULL; + btreenode_list = (arraypool *) NULL; + highordertable = (point *) NULL; + subpbcgrouptable = (pbcdata *) NULL; + segpbcgrouptable = (list *) NULL; + idx2segpglist = (int *) NULL; + segpglist = (int *) NULL; + + cavetetlist = NULL; + cavebdrylist = NULL; + caveoldtetlist = NULL; + caveshlist = caveshbdlist = NULL; + subsegstack = subfacstack = NULL; + + elemfliplist = (arraypool *) NULL; + fixededgelist = (arraypool *) NULL; + + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; + longest = 0.0; + hullsize = 0l; + insegments = 0l; + meshedges = 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; + samples = 0l; + randomseed = 1l; + macheps = 0.0; + minfaceang = minfacetdihed = PI; + b_steinerflag = false; + + ptloc_count = ptloc_max_count = 0l; + orient3dcount = 0l; + inspherecount = insphere_sos_count = 0l; + flip14count = flip26count = flipn2ncount = 0l; + flip22count = 0l; + inserthullcount = 0l; + maxbowatcavsize = totalbowatcavsize = totaldeadtets = 0l; + across_face_count = across_edge_count = across_max_count = 0l; + maxcavsize = maxregionsize = 0l; + ndelaunayedgecount = cavityexpcount = 0l; + opt_tet_peels = opt_face_flips = opt_edge_flips = 0l; + + maxcavfaces = maxcavverts = 0; + abovecount = 0l; + bowatvolcount = bowatsubcount = bowatsegcount = 0l; + updvolcount = updsubcount = updsegcount = 0l; + outbowatcircumcount = 0l; + failvolcount = failsubcount = failsegcount = 0l; + r1count = r2count = r3count = 0l; + cdtenforcesegpts = 0l; + rejsegpts = rejsubpts = rejtetpts = 0l; + flip23s = flip32s = flip22s = flip44s = 0l; + } // 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 (tet2segpool != NULL) { + delete tet2segpool; + } + if (tet2subpool != NULL) { + delete tet2subpool; + } + if (dummytetbase != (tetrahedron *) NULL) { + delete [] dummytetbase; + } + if (dummyshbase != (shellface *) NULL) { + delete [] dummyshbase; + } + if (facetabovepointarray != (point *) NULL) { + delete [] facetabovepointarray; + } + if (dummypoint != NULL) { + delete [] dummypoint; + } + if (highordertable != (point *) NULL) { + delete [] highordertable; + } + if (subpbcgrouptable != (pbcdata *) NULL) { + delete [] subpbcgrouptable; + } + if (segpbcgrouptable != (list *) NULL) { + delete segpbcgrouptable; + delete [] idx2segpglist; + delete [] segpglist; + } + + if (cavetetlist != NULL) { + delete cavetetlist; + delete cavebdrylist; + delete caveoldtetlist; + } + if (subsegstack != NULL) { + delete subsegstack; + } + if (subfacstack != NULL) { + delete subfacstack; + } + } // ~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); + +#ifdef TETLIBRARY +void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, + tetgenio *addin = NULL, tetgenio *bgmin = NULL); +#endif // #ifdef TETLIBRARY + +/////////////////////////////////////////////////////////////////////////////// +// // +// terminatetetgen() Terminate TetGen with a given exit code. // +// // +/////////////////////////////////////////////////////////////////////////////// + +inline void terminatetetgen(int x) +{ +#ifdef TETLIBRARY + throw x; +#else + switch (x) { + case 1: // Out of memory. + printf("Error: Out of memory.\n"); + break; + case 2: // Encounter an internal error. + printf(" Please report this bug to sihang@mail.berlios.de. Include\n"); + printf(" the message above, your input data set, and the exact\n"); + printf(" command line you used to run this program, thank you.\n"); + break; + default: + printf("Program stopped.\n"); + } // switch (x) + exit(x); +#endif // #ifdef TETLIBRARY +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 following routines use arbitrary precision floating-point arithmetic. // +// They are provided by J. R. Schewchuk in public domain (http://www.cs.cmu. // +// edu/~quake/robust.html). The source code are in "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); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Inline functions of mesh data structures // +// // +/////////////////////////////////////////////////////////////////////////////// + +// 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) ((uintptr_t) (ptr) & (uintptr_t) 3); + t.tet = (tetrahedron *) ((uintptr_t) (ptr) & ~(uintptr_t) 7); +} + +inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { + return (tetrahedron) ((uintptr_t) t.tet | (uintptr_t) 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; +} + +// Given a face t1, find the face f2 in the adjacent tet. If t2 is not +// a dummytet, then t1 and t2 refer to the same edge. Moreover, t2's +// edge must be in 0th edge ring, e.g., t2.ver is one of {0, 2, 4}. +// No matter what edge version t1 is. + +inline void tetgenmesh::symedge(triface& t1, triface& t2) +{ + decode(t1.tet[t1.loc], t2); + if (t2.tet != dummytet) { + // Search the edge of t1 in t2. + point tapex = apex(t1); + if ((point) (t2.tet[locver2apex[t2.loc][0] + 4]) == tapex) { + t2.ver = 0; + } else if ((point) (t2.tet[locver2apex[t2.loc][2] + 4]) == tapex) { + t2.ver = 2; + } else { + assert((point) (t2.tet[locver2apex[t2.loc][4] + 4]) == tapex); + t2.ver = 4; + } + } +} + +inline void tetgenmesh::symedgeself(triface& t) +{ + tetrahedron ptr; + point tapex; + + ptr = t.tet[t.loc]; + tapex = apex(t); + + decode(ptr, t); + if (t.tet != dummytet) { + // Search the edge of t1 in t2. + if ((point) (t.tet[locver2apex[t.loc][0] + 4]) == tapex) { + t.ver = 0; + } else if ((point) (t.tet[locver2apex[t.loc][2] + 4]) == tapex) { + t.ver = 2; + } else { + assert((point) (t.tet[locver2apex[t.loc][4] + 4]) == tapex); + t.ver = 4; + } + } +} + +// Given a face t1, find the next face t2 in the face ring, t1 and t2 +// are in two different tetrahedra. If the next face is a hull face, +// t2 is dummytet. + +inline void tetgenmesh::tfnext(triface& t1, triface& t2) +{ + int *iptr; + + if ((t1.ver & 1) == 0) { + t2.tet = t1.tet; + iptr = locver2nextf[t1.loc][t1.ver]; + t2.loc = iptr[0]; + t2.ver = iptr[1]; + symedgeself(t2); // t2.tet may be dummytet. + } else { + symedge(t1, t2); + if (t2.tet != dummytet) { + iptr = locver2nextf[t2.loc][t2.ver]; + t2.loc = iptr[0]; + t2.ver = iptr[1]; + } + } +} + +inline void tetgenmesh::tfnextself(triface& t) +{ + int *iptr; + + if ((t.ver & 1) == 0) { + iptr = locver2nextf[t.loc][t.ver]; + t.loc = iptr[0]; + t.ver = iptr[1]; + symedgeself(t); // t.tet may be dummytet. + } else { + symedgeself(t); + if (t.tet != dummytet) { + iptr = locver2nextf[t.loc][t.ver]; + t.loc = iptr[0]; + t.ver = iptr[1]; + } + } +} + +// 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); +} + +// 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; +} + +// Check or set a tetrahedron's marker. + +inline int tetgenmesh::getelemmarker(tetrahedron* ptr) { + return ((int *) (ptr))[elemmarkerindex]; +} + +inline void tetgenmesh::setelemmarker(tetrahedron* ptr, int value) { + ((int *) (ptr))[elemmarkerindex] = value; +} + +// infect(), infected(), uninfect() -- primitives to flag or unflag a +// tetrahedron. The last bit of the element marker is flagged (1) +// or unflagged (0). + +inline void tetgenmesh::infect(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= (int) 1; +} + +inline void tetgenmesh::uninfect(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(int) 1; +} + +// Test a tetrahedron for viral infection. + +inline bool tetgenmesh::infected(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & (int) 1) != 0; +} + +// marktest(), marktested(), unmarktest() -- primitives to flag or unflag a +// tetrahedron. The last second bit of the element marker is marked (1) +// or unmarked (0). +// One needs them in forming Bowyer-Watson cavity, to mark a tetrahedron if +// it has been checked (for Delaunay case) so later check can be avoided. + +inline void tetgenmesh::marktest(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= (int) 2; +} + +inline void tetgenmesh::unmarktest(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(int) 2; +} + +inline bool tetgenmesh::marktested(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & (int) 2) != 0; +} + +// markface(), unmarkface(), facemarked() -- primitives to flag or unflag a +// face of a tetrahedron. From the last 3rd to 6th bits are used for +// face markers, e.g., the last third bit corresponds to loc = 0. +// One use of the face marker is in flip algorithm. Each queued face (check +// for locally Delaunay) is marked. + +inline void tetgenmesh::markface(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= (int) (4<<(t).loc); +} + +inline void tetgenmesh::unmarkface(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(int) (4<<(t).loc); +} + +inline bool tetgenmesh::facemarked(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & (int) (4<<(t).loc)) != 0; +} + +// markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an +// edge of a tetrahedron. From the last 7th to 12th bits are used for +// edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. +// Remark: The last 7th bit is marked by 2^6 = 64. + +inline void tetgenmesh::markedge(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= + (int) (64<> (int) 2; + // return ((int *) (s.sh))[shmarkindex]; +} + +inline void tetgenmesh::setshellmark(face& s, int value) { + ((int *) ((s).sh))[shmarkindex] = (value << (int) 2) + + ((((int *) ((s).sh))[shmarkindex]) & (int) 3); + // ((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; +} + +// sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a +// subface. The last bit of ((int *) ((s).sh))[shmarkindex] is flaged. + +inline void tetgenmesh::sinfect(face& s) { + ((int *) ((s).sh))[shmarkindex] = + (((int *) ((s).sh))[shmarkindex] | (int) 1); + // s.sh[6] = (shellface) ((unsigned long) s.sh[6] | (unsigned long) 4l); +} + +inline void tetgenmesh::suninfect(face& s) { + ((int *) ((s).sh))[shmarkindex] = + (((int *) ((s).sh))[shmarkindex] & ~(int) 1); + // 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 (((int *) ((s).sh))[shmarkindex] & (int) 1) != 0; +} + +// smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag +// a subface. The last 2nd bit of ((int *) ((s).sh))[shmarkindex] is flaged. + +#define smarktest(s) \ + ((int *) ((s).sh))[shmarkindex] = (((int *)((s).sh))[shmarkindex] | (int) 2) + +#define sunmarktest(s) \ + ((int *) ((s).sh))[shmarkindex] = (((int *)((s).sh))[shmarkindex] & ~(int) 2) + +#define smarktested(s) ((((int *) ((s).sh))[shmarkindex] & (int) 2) != 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) { + if ((t).tet[9] != NULL) { + sdecode(((shellface *) (t).tet[9])[(t).loc], s); + } else { + (s).sh = dummysh; + } + //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) { + if ((t).tet[9] == NULL) { + // Allocate space for this tet. + (t).tet[9] = (tetrahedron) tet2subpool->alloc(); + // NULL all fields in this space. + for (int i = 0; i < 4; i++) { + ((shellface *) (t).tet[9])[i] = (shellface) dummysh; + } + } + // Bond t <==> s. + ((shellface *) (t).tet[9])[(t).loc] = sencode(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) { + if ((t).tet[9] != NULL) { + ((shellface *) (t).tet[9])[(t).loc] = (shellface) dummysh; + } + // 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& s) +{ + if ((t).tet[8] != NULL) { + sdecode(((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]], s); + } else { + (s).sh = dummysh; + } + // 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& s) +{ + if ((t).tet[8] == NULL) { + // Allocate space for this tet. + (t).tet[8] = (tetrahedron) tet2segpool->alloc(); + // NULL all fields in this space. + for (int i = 0; i < 6; i++) { + ((shellface *) (t).tet[8])[i] = (shellface) dummysh; + } + } + // Bond the segment. + ((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]] = sencode((s)); + // t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) sencode(seg); +} + +inline void tetgenmesh::tssdissolve1(triface& t) +{ + if ((t).tet[8] != NULL) { + ((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]] + = (shellface) dummysh; + } + // 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. +// The last significant bit of this integer is used by pinfect/puninfect. + +inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) { + return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> (int) 1); +} + +inline void tetgenmesh::setpointtype(point pt, enum verttype value) { + ((int *) (pt))[pointmarkindex + 1] = + ((int) value << 1) + (((int *) (pt))[pointmarkindex + 1] & (int) 1); +} + +// pinfect(), puninfect(), pinfected() -- primitives to flag or unflag +// a point. The last bit of the integer '[pointindex+1]' is flaged. + +inline void tetgenmesh::pinfect(point pt) { + ((int *) (pt))[pointmarkindex + 1] |= (int) 1; +} + +inline void tetgenmesh::puninfect(point pt) { + ((int *) (pt))[pointmarkindex + 1] &= ~(int) 1; +} + +inline bool tetgenmesh::pinfected(point pt) { + return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; +} + +// 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::shellface tetgenmesh::point2seg(point pt) { + return (shellface) ((tetrahedron *) (pt))[point2simindex + 2]; +} + +inline void tetgenmesh::setpoint2seg(point pt, shellface value) { + ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; +} + +inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { + return (point) ((tetrahedron *) (pt))[point2simindex + 3]; +} + +inline void tetgenmesh::setpoint2ppt(point pt, point value) { + ((tetrahedron *) (pt))[point2simindex + 3] = (tetrahedron) value; +} + +inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { + return ((tetrahedron *) (pt))[point2simindex + 4]; +} + +inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { + ((tetrahedron *) (pt))[point2simindex + 4] = 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; +} + +// dot() returns the dot product: v1 dot v2. + +inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) +{ + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} + +// cross() computes the cross product: n = v1 cross v2. + +inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) +{ + n[0] = v1[1] * v2[2] - v2[1] * v1[2]; + n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); + n[2] = v1[0] * v2[1] - v2[0] * v1[1]; +} + +// distance() 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])); +} + +// Linear algebra operators. + +#define NORM2(x, y, z) ((x) * (x) + (y) * (y) + (z) * (z)) + +#define DIST(p1, p2) \ + sqrt(NORM2((p2)[0] - (p1)[0], (p2)[1] - (p1)[1], (p2)[2] - (p1)[2])) + +#define DOT(v1, v2) \ + ((v1)[0] * (v2)[0] + (v1)[1] * (v2)[1] + (v1)[2] * (v2)[2]) + +#define CROSS(v1, v2, n) \ + (n)[0] = (v1)[1] * (v2)[2] - (v2)[1] * (v1)[2];\ + (n)[1] = -((v1)[0] * (v2)[2] - (v2)[0] * (v1)[2]);\ + (n)[2] = (v1)[0] * (v2)[1] - (v2)[0] * (v1)[1] + +#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) + +#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) + +/////////////////////////////////////////////////////////////////////////////// +// // +// Two inline functions used in read/write VTK files. // +// // +/////////////////////////////////////////////////////////////////////////////// + +inline void swapBytes(unsigned char* var, int size) +{ + int i = 0; + int j = size - 1; + char c; + + while (i < j) { + c = var[i]; var[i] = var[j]; var[j] = c; + i++, j--; + } +} + +inline bool testIsBigEndian() +{ + short word = 0x4321; + if((*(char *)& word) != 0x21) + return true; + else + return false; +} + +#endif // #ifndef tetgenH