From 245c8c979130cd8948328579fccab5e50bca3a1e Mon Sep 17 00:00:00 2001 From: Christophe Geuzaine <cgeuzaine@ulg.ac.be> Date: Thu, 6 Dec 2001 13:15:54 +0000 Subject: [PATCH] incorporate the Tetgen tetrahedral mesh generator --- Tetgen/Makefile | 68 + Tetgen/constrain.cpp | 3479 +++++++++++++ Tetgen/defines.h | 175 + Tetgen/linklist.cpp | 1149 +++++ Tetgen/linklist.h | 470 ++ Tetgen/predicate.cpp | 2945 +++++++++++ Tetgen/quality.cpp | 1723 +++++++ Tetgen/tetlib.cpp | 10847 +++++++++++++++++++++++++++++++++++++++++ Tetgen/tetlib.h | 1301 +++++ Tetgen/tetmain.cpp | 11 + Tetgen/trilib.cpp | 5602 +++++++++++++++++++++ Tetgen/trilib.h | 515 ++ 12 files changed, 28285 insertions(+) create mode 100644 Tetgen/Makefile create mode 100644 Tetgen/constrain.cpp create mode 100644 Tetgen/defines.h create mode 100644 Tetgen/linklist.cpp create mode 100644 Tetgen/linklist.h create mode 100644 Tetgen/predicate.cpp create mode 100644 Tetgen/quality.cpp create mode 100644 Tetgen/tetlib.cpp create mode 100644 Tetgen/tetlib.h create mode 100644 Tetgen/tetmain.cpp create mode 100644 Tetgen/trilib.cpp create mode 100644 Tetgen/trilib.h diff --git a/Tetgen/Makefile b/Tetgen/Makefile new file mode 100644 index 0000000000..adff2be3b3 --- /dev/null +++ b/Tetgen/Makefile @@ -0,0 +1,68 @@ +# makefile for Tetgen +# +# Type "make" to compile tetgen. +# +# After compiling, type "tetgen -h" to read instructions +# for using this programs. +# +# Type "make clean" to delete all object(*.o) files. + +# BIN is the directory where you want to put the executable programs. +# SRC is the directory in which the *.cpp source files are. + +BIN = ../bin/ +SRC = ./ + +# CC should be set to the name of your favorite C++ compiler. + +CC = g++ + +# CSWITCHES is a list of all switches passed to the C++ compiler. +# You'd best using the best level of optimization. +# +# By default, Tetgen use double precision floating point numbers. +# If you prefer single precision, use the -DSINGLE switch. +# Double precision uses more memory, but improves the resolution of +# the meshes you can generate with Tetgen. It also reduces the +# likelihood of a floating exception due to overflow. +# +# If yours is not a Unix system, use the -DNO_TIMER switch to eliminate +# the Unix-specific timer code. +# +# If you are modifying Tetgen, I recommend using the -DSELF_CHECK switch +# while you are debugging. Defining the SELF_CHECK symbol causes +# Tetgen to include self-checking code. Tetgen will execute more +# slowly, however, so be sure to remove this switch before compiling a +# production version. +# + +CSWITCHES = -O + +# Objects in lexicographic order. + +OBJS = constrain.o \ + linklist.o \ + predicate.o \ + quality.o \ + tetlib.o \ + trilib.o + +# RM should be set to the name of your favorite rm (file deletion program). + +RM = /bin/rm + +# The action starts here. + +all: tetlib tetgen + +.cpp.o: + $(CC) $(CSWITCHES) -c $< + +tetlib: $(OBJS) + ar r ../lib/Tetgen.a $(OBJS) + +tetgen: tetmain.o $(OBJS) + $(CC) $(CSWITCHES) -o $(BIN)tetgen tetmain.o $(OBJS) -lm + +clean: + $(RM) $(SRC)*.o diff --git a/Tetgen/constrain.cpp b/Tetgen/constrain.cpp new file mode 100644 index 0000000000..fc9c96fed0 --- /dev/null +++ b/Tetgen/constrain.cpp @@ -0,0 +1,3479 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// constrain.cpp Implement the boundary-constranied Delaunay mesh // +// generation routines. // +// // +// Tetgen Version 1.0 beta // +// November, 2001 // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Unlike the two-dimension case that is guaranteed to recover the segment, // +// the three-dimension case has no such guarantee to recover the segment and // +// facet. The Schonart polyhedron[1] is an example of polyhedron which can // +// not be triangulated without adding internal point. So in three-dimension, // +// boundary-constrained mesh generation is a complex problem which can be // +// defined in many ways and several solutions can be proposed. // +// // +// Currently, there exist several methods to solve this problem. Weatherill // +// [2] and Borouchaki[3] retriangulated the elements intersected by a // +// constrained face so as to create a triangulation of the face. In this // +// case, the Steiner points are created on the boundary. George et al. [4] // +// and Owen[5] regenerated the missing edges and faces in boundary by means // +// of local transformations and the possible Steiner points insertion inside // +// the domain. Shewchuk[6] thought of the constrained boundary triangulation // +// problem as a subproblem of mesh improvement in his Delaunay refinement // +// algorithm and proposed a method to recover boundary only need inserting // +// points on segments. // +// // +// The method I used to recover segments and facets is relatively derived // +// from above methods. It has two stages: Segment recover and Facet recover. // +// It can be simply described below, the detail please refer to my thesis: // +// // +// We use the 'edge flip' + 'stitching' method to recover missing segments. // +// For each missing segment, try edge flip operations(flip23, flip22 and // +// flip44) first. Under most cases, after one or a series of flip operations,// +// the missing segment can be recovered. If edge flips failed, inserting a // +// vertex into the mesh at the midpoint of the segment (more accurately, at // +// the midpoint of the place where the segment should be). After the mesh is // +// adjusted to maintain the Delaunay property, the two resulting subsegments // +// may appear in the mesh. If not, the whole procedure is repeated // +// recursively for each missing subsegment until the original segment is // +// represented by a contiguous linear sequence of edges of the mesh. We are // +// assure of eventual success because the Delaunay triangulation always // +// connects a vertex to its nearest neighbour; once the spacing of vertices // +// along a segment is sufficiently small, its entire length will be // +// represented. // +// // +// When all missing subsegments are recovered missing facets are recovered // +// in an analogous manner. For each facet, it is necessary maintain a // +// two-dimensional Delaunay triangulation of its vertices, independent from // +// the tetrahedralization in which we hope its subfaces will eventually // +// appear. For each triangular subface in a facet triangulation, look for // +// a matching face in the tetrahedralization. If there could't find a match // +// subface, we can sure this subface is missing from current mesh. When we // +// find a subface is missing from the mesh, a local re-meshing method is // +// used to recovery all missing subfaces (start from this subface). Please // +// refer to Usermanual chapter 4.4.2 for detail description of this methhod. // +// // +// However, There is no guarantee to recover facets in all condition of my // +// implementation now. Acctually, It's a KNOWN BUG now there are still valid // +// input files for which Tetgen program can not produce a valid mesh. If // +// you come across one of these, please send it to sihang@weboo.com so that // +// I can continue to make the code more robust, thank you. // +// // +// Refernces: // +// // +// [1] E. Schonardt, Uber die Zerlegung von Dreieckspolyedern inTetraeder, // +// Mathematische Annalen, vol 98, pp. 309-312, 1928. // +// [2] N.P. Weatherill and O. Hassan, Efficient three-dimensional Delaunay // +// triangulation with automatic point creation and imposed boundary con- // +// straints, Int. J. Numer. Meth. in Eng., vol 37, pp. 2005-2039, 1994. // +// [3] H. Borouchaki, Triangulation sous contraintes en dimension quelconque,// +// Rapport de Recherche INRIA, RR-2373, 1994. // +// [4] P.L. George, F. Hecht and E. Saltel, Automatic mesh generator with // +// specified boundary, Comp. Meth. in Appl. Mech. and Eng., vol 13, // +// pp. 269-288, 1991. // +// [5] Steven J. Owen, Constrained Triangulation: Application to Hex-Domaint // +// Mesh Generation. Proceedings, 8th International Meshing Roundtable, // +// South Lake Tahoe, CA, U.S.A., pp.31-41, October 1999 // +// [6] Jonathan Richard Shewchuk, Tetrahedral Mesh Generation by Delaunay // +// Refinement. Proceedings of the Fourteenth Annual Symopsium on Comput- // +// ional Geometry (Minneapolis, Minnesota), pages 86-95, Association for // +// Computing Machinery, June, 1998. // +// [7] Jonathan Richard Shewchuk, A Condition Guaranteeing the Existence of // +// Higher-Dimensional Constrained Delaunay Triangulations. Proceedings // +// of the Fourteenth Annual Symopsium on Computional Geometry (Minneapo- // +// lis, Minnesota), pages 76-85, Association for Computing Machinery, // +// June, 1998. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "tetlib.h" + +/////////////////////////////////////////////////////////////////////////////// +// Segment/Facet (Boundary) Constrained Routines. // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// finddirection() Find the first tetrahedron on the path from one point // +// to another. // +// // +// Finds the tetrahedron that intersects a line segment drawn from the // +// origin of 'searchtet' to the point 'endpoint', 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 whether the destination, apex or oppo of the found // +// tetrahedron is collinear with the two points in question. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum mesh3d::finddirectionresult +mesh3d::finddirection(triface *searchtet, point3d tend) +{ + triface checktet; + point3d tstart, tright, tleft, toppo; + int baseorient, rightorient, leftorient, forwardorient; + int rightflag, leftflag, forwardflag; + int basezeroflag, rightzeroflag, leftzeroflag, forwardzeroflag; + + tstart = org(*searchtet); + adjustedgering(*searchtet, CCW); + if (tstart != org(*searchtet)) { + enextself(*searchtet); + } + tright = dest(*searchtet); + if (tright == tend) { + return RIGHTCOLLINEAR; + } + tleft = apex(*searchtet); + if (tleft == tend) { + return LEFTCOLLINEAR; + } + + // Base plane is the plane include face(tstart, tright, tleft), the normal + // is direct to toppo(toppo is above the base plane). + basezeroflag = 0; + // Is 'tend' below the base plane? + baseorient = iorient3d(tstart, tright, tleft, tend); + if (baseorient > 0) { + // Get another side's tetrahdera of base face, so 'tend' is above the + // base plane, need reset tright and tleft. + symself(*searchtet); + assert(searchtet->tet != dummytet); + findversion(searchtet, tstart, tleft, 0); + tright = tleft; + tleft = apex(*searchtet); + } else if (baseorient == 0) { + basezeroflag = 1; + } + + if (verbose > 2) { + printf(" Find direction to point %d, from tet(%d, %d, %d, %d).\n", + pointmark(tend), pointmark(tstart), pointmark(tright), + pointmark(tleft), pointmark(oppo(*searchtet))); + } + + // Repeat turn right and turn left until find a satisfied tetrahedron. + while (true) { + toppo = oppo(*searchtet); + if (toppo == tend) { + return TOPCOLLINEAR; + } + // Must reset 'rightflag' and 'leftflag' at each start time. Because + // we not always turn the same side in three-dimensional case. + rightflag = leftflag = forwardflag = 0; + rightzeroflag = leftzeroflag = 0; forwardzeroflag = 0; + + // Is 'endpoint' to the right? + rightorient = iorient3d(tend, tstart, tright, toppo); + if (rightorient == 0) { + rightzeroflag = 1; + } else { + rightflag = rightorient > 0; + } + // Is 'endpoint' to the left? + leftorient = iorient3d(tstart, tend, tleft, toppo); + if (leftorient == 0) { + leftzeroflag = 1; + } else { + leftflag = leftorient > 0; + } + // Is 'endpoint' forward to the opposite? + forwardorient = iorient3d(tright, toppo, tleft, tend); + if (forwardorient == 0) { + forwardzeroflag = 1; + } else { + forwardflag = forwardorient > 0; + } + + // Check zero flag first. + if (basezeroflag) { + if (rightzeroflag && forwardflag) { + return RIGHTCOLLINEAR; + } else if (leftzeroflag && forwardflag) { + return LEFTCOLLINEAR; + } else if (forwardflag) { + if ((rightflag == 0) && (leftflag == 0)) { + return WITHIN; + } + } + } + + if (rightzeroflag) { + if (leftzeroflag && forwardflag) { + return TOPCOLLINEAR; + } + if (!leftflag && forwardflag) { + // This condition is tend coplanar with the right side(may be WITHIN + // ) of this tet, and we can't turn left side(because the leftflag + // is 0). The only choice is try to turn right. Check if there has + // a boundary on the right. + fnext(*searchtet, checktet); + if (issymexist(&checktet)) { + rightflag = 1; // Here leftflag = 0; + } else { + // Hit a boundary when try turn right, take the right side as + // base plane, then continue... + fnextself(*searchtet); + esymself(*searchtet); // Don't miss the tstart. + enextself(*searchtet); + tleft = tright; + tright = toppo; + basezeroflag = 1; + if (verbose > 2) { + printf(" Take right as base palne. Get tet(%d, %d, %d, %d).\n", + pointmark(tstart), pointmark(tright), + pointmark(tleft), pointmark(oppo(*searchtet))); + } + continue; + } + } + } else if (leftzeroflag) { + if (rightzeroflag && forwardflag) { + return TOPCOLLINEAR; + } + if (!rightflag && forwardflag) { + // This condition is tend coplanar with the left side(may be WITHIN + // ) of this tet, and we can't turn right side(because the right- + // flag is 0). The only choice is try to turn left. Check if there + // has a boundary on the left. + checktet = *searchtet; + enext2fnextself(checktet); + if (issymexist(&checktet)) { + leftflag = 1; // Here rightflag = 0; + } else { + // Hit a boundary when try turn left, take the left side as + // base plane, then continue... + enext2fnextself(*searchtet); + esymself(*searchtet); // Don't miss the tstart. + tright = tleft; + tleft = toppo; + basezeroflag = 1; + if (verbose > 2) { + printf(" Take left as base palne. Get tet(%d, %d, %d, %d).\n", + pointmark(tstart), pointmark(tright), + pointmark(tleft), pointmark(oppo(*searchtet))); + } + continue; + } + } + } else { + // None zero flag(!rightzeroflag && !leftzeroflag) cases. + if (!rightflag && !leftflag) { + // Both rightflag = 0 and leftflag = 0. This mean tend is both below + // the right side and left side. There have two conditions, decide + // by current forwardflag. + if (forwardflag) { + return ACROSS; + } else { + // 'searchtet' faces directly away from `tend'. We could go left + // or right. Ask whether it's a tetrahedron or a boundary on the + // right. + fnext(*searchtet, checktet); + if (issymexist(&checktet)) { + rightflag = 1; // leftflag = 0; + } else { + leftflag = 1; // rightflag = 0; + } + } + } else if (rightflag && leftflag) { + // 'searchtet' faces directly away from `tend'. We could go left + // or right. Ask whether it's a tetrahedron or a boundary on the + // right. + fnext(*searchtet, checktet); + if (issymexist(&checktet)) { + leftflag = 0; // rightflag = 1; + } else { + rightflag = 0; // leftflag = 1; + } + } + } + + if (rightflag) { + // Turn right once. + fnextself(*searchtet); + symself(*searchtet); + assert(searchtet->tet != dummytet); + findversion(searchtet, tstart, tright, 0); + tleft = toppo; + basezeroflag = rightzeroflag; + if (verbose > 2) { + printf(" Turn right side."); + } + } else { + assert(leftflag); + // Turn left once. + enext2fnextself(*searchtet); + symself(*searchtet); + assert(searchtet->tet != dummytet); + findversion(searchtet, tstart, toppo, 0); + tright = toppo; + basezeroflag = leftzeroflag; + if (verbose > 2) { + printf(" Turn left side."); + } + } + if (verbose > 2) { + printf(" Get tet(%d, %d, %d, %d).\n", + pointmark(tstart), pointmark(tright), + pointmark(tleft), pointmark(oppo(*searchtet))); + } +#ifdef SELF_CHECK + baseorient = iorient3d(tstart, tright, tleft, tend); + if (baseorient > 0) { + printf("Internal error in finddirection():\n"); + printf(" 'baseorient' shouldn't below the base plane.\n"); + internalerror(); + } +#endif // defined SELF_CHECK + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// segmentintersection() Find the intersection of an existing segment and // +// a segment that is being inserted. Insert a point // +// at the intersection, splitting an existing shell // +// edge(subsegment). // +// // +// The segment being inserted connects the apex of splittet to endpoint2. // +// splitsubseg is the subsegment being split, the edge being split connects // +// the origin and destination of splittet. // +// // +// On completion, splittet is a handle having the newly inserted // +// intersection point as its origin, and endpoint1 as its destination. // +// // +// Use line and plane intersection algorithm to calculate the intersection // +// point of two segment in 3-space: // +// If the plane is defined as: // +// a*x + b*y + c*z + d = 0 (1) // +// and the line is defined as: // +// x = x1 + (x2 - x1)*t = x1 + e*t // +// y = y1 + (y2 - y1)*t = y1 + f*t (2) // +// z = z1 + (z2 - z1)*t = z1 + g*t // +// Then just substitute (2) into the plane equation (1). You end up with: // +// t = - (a*x1 + b*y1 + c*z1 + d)/(a*e + b*f + c*g) // +// When the denominator is zero, the line is contained in the plane if the // +// numerator is also zero (the point at t=0 satisfies the plane equation), // +// otherwise the line is parallel to the plane. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::segmentintersection(triface *splittet, face *splitshseg, + point3d endpoint2) +{ + point3d endpoint1, torg, tdest; + point3d leftpoint, rightpoint, oppopoint; + point3d newpoint; + enum insertsiteresult success; + enum finddirectionresult collinear; + enum locateresult locval; + REAL a, b, c, d, e, f, g, t; + REAL v12[3], v34[3], norm0[3], norm1[3]; + int i; + + segmentintersectioncount++; + + // Find the other three segment endpoints. + endpoint1 = apex(*splittet); + torg = org(*splittet); + tdest = dest(*splittet); + + // Segment intersection formulae; see the graphic algorithm faq. + Sub3D(tdest, torg, v12); + Sub3D(endpoint2, endpoint1, v34); + Cross3D(v12, v34, norm0); + Cross3D(v34, norm0, norm1); + + a = norm1[0]; + b = norm1[1]; + c = norm1[2]; + d = -(a * endpoint1[0] + b * endpoint1[1] + c * endpoint1[2]); + e = v12[0]; + f = v12[1]; + g = v12[2]; + t = -(a * torg[0] + b * torg[1] + c * torg[2] + d) / (a * e + b * f + c * g); + + // Create the new point. + newpoint = (point3d) points.alloc(); + // Interpolate its coordinate and attributes. + for (i = 0; i < 3 + nextras; i++) { + newpoint[i] = torg[i] + t * v12[i]; + } + setpointmark(newpoint, mark(*splitshseg)); + if (verbose > 1) { + // For debug, need use the pointmark to keep point's index. + setpointmark(newpoint, points.items); + } + if (verbose > 1) { + printf(" Splitting edge (%.12g, %.12g, %.12g) (%.12g, %.12g, %.12g)\n", + torg[0], torg[1], torg[2], tdest[0], tdest[1], tdest[2]); + printf(" at (%.12g, %.12g, %.12g).\n", newpoint[0], newpoint[1], + newpoint[2]); + } + // Insert the intersection point. This should always succeed. + success = insertsite(newpoint, splittet, (face*) NULL, splitshseg); + if (success != SUCCESSFUL) { + printf("Internal error in segmentintersection():"); + printf(" Fail to split a segment.\n"); + internalerror(); + } + if (steinerleft > 0) { + steinerleft--; + } + // Find newpoint in splittet, set it as origin of splittet, + if (!findorg(splittet, newpoint)) { + splittet->ver = 0; + for (splittet->loc = 0; splittet->loc < 4; splittet->loc++) { + if (isaboveplane(splittet, newpoint)) break; + } + assert(splittet->loc < 4); + locval = preciselocate(newpoint, splittet); + assert(locval == ONVERTEX); + } + setpoint2tet(newpoint, encode(*splittet)); + + // Inserting the point may have caused edge flips. We wish to rediscover + // the edge connecting endpoint1 to the new intersection point. + collinear = finddirection(splittet, endpoint1); + rightpoint = dest(*splittet); + leftpoint = apex(*splittet); + oppopoint = oppo(*splittet); + if (leftpoint == endpoint1) { + enext2self(*splittet); + esymself(*splittet); + } else if (oppopoint == endpoint1) { + fnextself(*splittet); + enext2self(*splittet); + esymself(*splittet); + } else if (rightpoint != endpoint1) { + printf("Internal error in segmentintersection():\n"); + printf(" Topological inconsistency after splitting a segment.\n"); + internalerror(); + } + // 'splittet' should have destination endpoint1. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scoutsegment() Scout the first tetrahedron on the path from one // +// endpoint to another, and check for completion(reaching // +// the second endpoint), a collinear point, and the // +// intersection of two segments or the intersection of a // +// segment and a facet. // +// // +// Returns one if the entire segment is successfully inserted, and zero if // +// the job must be finished by conformingedge() or constrainededge(). // +// // +// If the first tetrahedron on the path has the second endpoint as its // +// destination, apex or opposite, a subsegment is inserted and the job is // +// done. // +// // +// If the first tetrahedron on the path has a destination or apex that // +// lies on the segment, a subsegment is inserted connecting the first // +// endpoint to the collinear point, and the search is continued from the // +// collinear point. // +// // +// If the first tetrahedron on the path has a subsegment opposite its origin,// +// then there is a segment that intersects the segment being inserted. Their // +// intersection point is inserted, splitting the subsegment. // +// // +// If the first tetrahedron on the path has a subface opposite its origin, // +// then there is a facet that intersects the segment being inserted. Their // +// intersection point is inserted, splitting the subface. // +// // +// Otherwise, return zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::scoutsegment(triface *searchtet, point3d endpoint2, int newmark) +{ + triface crosstet; + face crossface, crossedge; + point3d leftpoint, rightpoint, oppopoint; + point3d endpoint1; + enum finddirectionresult collinear; + + endpoint1 = org(*searchtet); + if (verbose > 2) { + printf(" Scout segment from %d to %d.\n", + pointmark(endpoint1), pointmark(endpoint2)); + } + collinear = finddirection(searchtet, endpoint2); + rightpoint = dest(*searchtet); + leftpoint = apex(*searchtet); + oppopoint = oppo(*searchtet); + if ((oppopoint == endpoint2) || (leftpoint == endpoint2) || + (rightpoint == endpoint2)) { + if (oppopoint == endpoint2) { + fnextself(*searchtet); + enext2self(*searchtet); + } else if (leftpoint == endpoint2) { + enext2self(*searchtet); + } + // Insert a subsegment, if there isn't already one there. + insertsubsegment(searchtet, newmark); + return 1; + } else if (collinear == LEFTCOLLINEAR) { + // We've collided with a point between the segment's endpoints. + // Make the collinear point be the tetrahedron's origin. + enext2self(*searchtet); + insertsubsegment(searchtet, newmark); + // Insert the remainder of the segment. + return scoutsegment(searchtet, endpoint2, newmark); + } else if (collinear == RIGHTCOLLINEAR) { + // We've collided with a point between the segment's endpoints. + insertsubsegment(searchtet, newmark); + // Make the collinear point be the tetrahedron's origin. + enextself(*searchtet); + // Insert the remainder of the segment. + return scoutsegment(searchtet, endpoint2, newmark); + } else if (collinear == TOPCOLLINEAR) { + // We've collided with a point between the segment's endpoints. + // Make the collinear point be the tetrahedron's origin. + fnextself(*searchtet); + enext2self(*searchtet); + insertsubsegment(searchtet, newmark); + // Insert the remainder of the segment. + return scoutsegment(searchtet, endpoint2, newmark); + } else if (collinear == ACROSS) { + // Check for a crossing subface. + enextfnext(*searchtet, crosstet); + tspivot(crosstet, crossface); + if ((crossface.sh != dummysh) && !isnonsolid(crossface)) { + /*// Insert a point at the intersection. + segmentfacetintersection(&crosstet, &crossface, endpoint2); + *searchtet = crosstet; + insertsubsegment(searchtet, newmark); + // Insert the remainder of the segment. + return scoutsegment(searchtet, endpoint2, newmark); */ + printf("Segment-Facet intersection has not implemented now.\n"); + exit(1); + } else { + // Try to do flip23 on the acrossing face. + if (categorizeface(crosstet) == T23) { + if (verbose > 1) { + printf(" Do face-edge swap (T23).\n"); + } + usefliplist = 1; + flip23(crosstet); + usefliplist = 0; + findorg(searchtet, endpoint1); + assert(org(*searchtet) == endpoint1); + return scoutsegment(searchtet, endpoint2, newmark); + } + } + } else { + assert(collinear == WITHIN); + // Check for a crossing segment. + enextfnext(*searchtet, crosstet); + tsspivot(&crosstet, &crossedge); + if (crossedge.sh != dummysh) { + // Insert a point at the intersection. + segmentintersection(&crosstet, &crossedge, endpoint2); + *searchtet = crosstet; + insertsubsegment(searchtet, newmark); + // Insert the remainder of the segment. + return scoutsegment(searchtet, endpoint2, newmark); + } else { + // Try to do flip22 or flip44 on the acrossing edge. + point3d fliporg, flipdest; + enum facecategory fc; + + fliporg = org(crosstet); + flipdest = dest(crosstet); + fc = categorizeface(crosstet); + if ((fc == T22) || (fc == T44)) { + // Check the flipface does not changed by categorizeface(). + if (((org(crosstet) == fliporg) && (dest(crosstet) == flipdest)) + || ((org(crosstet) == flipdest) && (dest(crosstet) == fliporg))) { + if (verbose > 1) { + printf(" Do face-edge swap (%s).\n", fc == T22 ? "T22" : "T44"); + } + usefliplist = 1; + if (fc == T22) { + flip22(crosstet); + } else { + flip44(crosstet); + } + usefliplist = 0; + findorg(searchtet, endpoint1); + assert(org(*searchtet) == endpoint1); + return scoutsegment(searchtet, endpoint2, newmark); + } + } + } + } + // Can't find such segment, job must be completed by insert points. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// conformingedge() Force a segment into a conforming Delaunay // +// triangulation by inserting a point at its midpoint, // +// and recursively forcing in the two half-segments if // +// necessary. // +// // +// Generates a sequence of edges connecting `endpoint1' to `endpoint2'. // +// `newmark' is the boundary marker of the segment, assigned to each new // +// splitting point and shell edge. // +// // +// Note that conformingedge() does not always maintain the conforming // +// Delaunay property. Once inserted, segments are locked into place; points // +// inserted later (to force other segments in) may render these fixed // +// segments non-Delaunay. The conforming Delaunay property will be restored // +// by enforcequality() by splitting encroached segments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::conformingedge(point3d endpoint1, point3d endpoint2, int newmark) +{ + triface searchtet1, searchtet2; + face brokenshseg, brokenshface; + point3d newpoint; + point3d midpoint1, midpoint2; + enum insertsiteresult success; + enum locateresult locval; + int result1, result2; + int i; + + if (verbose > 2) { + printf(" Forcing segment into triangulation by recursive splitting:"); + printf(" %d, %d\n", pointmark(endpoint1), pointmark(endpoint2)); + } + // Create a new point to insert in the middle of the segment. + newpoint = (point3d) points.alloc(); + // Interpolate coordinates and attributes. + for (i = 0; i < 3 + nextras; i++) { + newpoint[i] = 0.5 * (endpoint1[i] + endpoint2[i]); + } + setpointmark(newpoint, newmark); + if (verbose > 1) { + // For debug, need use the pointmark to keep point's index. + setpointmark(newpoint, points.items); + } + // Find a boundary tetrahedron to search from. + searchtet1.tet = (tetrahedron *) NULL; + // Attempt to insert the new point. + success = insertsite(newpoint, &searchtet1, (face *) NULL, (face *) NULL); + if (success == DUPLICATE) { + if (verbose > 2) { + printf(" Segment intersects existing point (%.12g, %.12g, %.12g).\n", + newpoint[0], newpoint[1], newpoint[2]); + } + // Use the point that's already there. + points.dealloc(newpoint); + newpoint = org(searchtet1); + } else { + if (success == VIOLATINGEDGE) { + if (verbose > 2) { + printf(" Two segments intersect at (%.12g, %.12g, %.12g).\n", + newpoint[0], newpoint[1], newpoint[2]); + } + // By fluke, we've landed right on another segment. Split it. + tsspivot(&searchtet1, &brokenshseg); + success = insertsite(newpoint, &searchtet1, (face*) NULL, &brokenshseg); + if (success != SUCCESSFUL) { + printf("Internal error in conformingedge():"); + printf(" Failure to split a segment.\n"); + internalerror(); + } + } else if (success == VIOLATINGFACE) { + if (verbose > 2) { + printf(" Segments intersect a subface at (%.12g, %.12g, %.12g).\n", + newpoint[0], newpoint[1], newpoint[2]); + } + // By fluke, we've landed right on a subface. Split it. + tspivot(searchtet1, brokenshface); + success = insertsite(newpoint, &searchtet1, &brokenshface, (face*) NULL); + if (success != SUCCESSFUL) { + printf("Internal error in conformingedge():"); + printf(" Failure to split a subface.\n"); + internalerror(); + } + } + // The point has been inserted successfully. + if (steinerleft > 0) { + steinerleft--; + } + // Find newpoint in searchtet1, set it as origin of splittet, + if (!findorg(&searchtet1, newpoint)) { + searchtet1.ver = 0; + for (searchtet1.loc = 0; searchtet1.loc < 4; searchtet1.loc++) { + if (isaboveplane(&searchtet1, newpoint)) break; + } + assert(searchtet1.loc < 4); + locval = preciselocate(newpoint, &searchtet1); + assert(locval == ONVERTEX); + } + setpoint2tet(newpoint, encode(searchtet1)); + } + + searchtet2 = searchtet1; + result1 = scoutsegment(&searchtet1, endpoint1, newmark); + result2 = scoutsegment(&searchtet2, endpoint2, newmark); + if (!result1) { + // The origin of searchtet1 may have changed if a collision with an + // intervening vertex on the segment occurred. + midpoint1 = org(searchtet1); + conformingedge(midpoint1, endpoint1, newmark); + } + if (!result2) { + // The origin of searchtet2 may have changed if a collision with an + // intervening vertex on the segment occurred. + midpoint2 = org(searchtet2); + conformingedge(midpoint2, endpoint2, newmark); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertsegment() Insert a PLC segment into a triangulation. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::insertsegment(point3d endpoint1, point3d endpoint2, int newmark) +{ + triface searchtet1, searchtet2; + tetrahedron encodedtet; + point3d checkpoint; + + if (verbose > 1) { + printf(" Connecting %d to %d.\n", pointmark(endpoint1), + pointmark(endpoint2)); + } + + // Find a tetrahedron whose origin is the segment's first endpoint. + checkpoint = (point3d) NULL; + encodedtet = point2tet(endpoint1); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, searchtet1); + if (findorg(&searchtet1, endpoint1)) { + checkpoint = org(searchtet1); + } + } + if (checkpoint != endpoint1) { + // Find a boundary tetrahedron to search from. + searchtet1.tet = dummytet; + searchtet1.loc = 0; + symself(searchtet1); + // Search for the segment's first endpoint by point location. + if (locate(endpoint1, &searchtet1) != ONVERTEX) { + printf("Internal error in insertsegment(): Unable to locate point\n"); + printf(" (%.12g, %.12g, %.12g) in triangulation.\n", + endpoint1[0], endpoint1[1], endpoint1[2]); + internalerror(); + } + } + // Remember this tetrahedron to improve subsequent point location. + recenttet = searchtet1; + // Scout the beginnings of a path from the first endpoint + // toward the second. + if (scoutsegment(&searchtet1, endpoint2, newmark)) { + // The segment was easily inserted. + if (!fliplist->empty()) { + // Restore Delaunayness if necessary. + dofliplist(); + } + return; + } + // The first endpoint may have changed if a collision with an intervening + // vertex on the segment occurred. + endpoint1 = org(searchtet1); + + // Find a tetrahedron whose origin is the segment's second endpoint. + checkpoint = (point3d) NULL; + encodedtet = point2tet(endpoint2); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, searchtet2); + if (findorg(&searchtet2, endpoint2)) { + checkpoint = org(searchtet2); + } + } + if (checkpoint != endpoint2) { + // Find a boundary tetrahedron to search from. + searchtet2.tet = dummytet; + searchtet2.loc = 0; + symself(searchtet2); + // Search for the segment's second endpoint by point location. + if (locate(endpoint2, &searchtet2) != ONVERTEX) { + printf("Internal error in insertsegment(): Unable to locate point\n"); + printf(" (%.12g, %.12g, %.12g) in triangulation.\n", + endpoint2[0], endpoint2[1], endpoint2[2]); + internalerror(); + } + } + // Remember this tetrahedron to improve subsequent point location. + recenttet = searchtet2; + // Scout the beginnings of a path from the second endpoint + // toward the first. + if (scoutsegment(&searchtet2, endpoint1, newmark)) { + // The segment was easily inserted. + if (!fliplist->empty()) { + // Restore Delaunayness if necessary. + dofliplist(); + } + return; + } + // The second endpoint may have changed if a collision with an intervening + // vertex on the segment occurred. + endpoint2 = org(searchtet2); + + // Insert vertices to force the segment into the triangulation. + conformingedge(endpoint1, endpoint2, newmark); + if (!fliplist->empty()) { + // Restore Delaunayness if necessary. + dofliplist(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getsteinersinseg() Find all steiner points in a given segment. Return // +// in a List. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::getsteinersinseg(point3d endpoint1, point3d endpoint2, + list* steinerpoints) +{ + triface searchtet; + tetrahedron encodedtet; + point3d checkpoint; + point3d leftpoint, rightpoint, oppopoint; + enum finddirectionresult collinear; + + if (verbose > 2) { + printf(" Get steiner points in segment from %d to %d.\n", + pointmark(endpoint1), pointmark(endpoint2)); + } + + // Find a tetrahedron whose origin is the endpoint1. + checkpoint = (point3d) NULL; + encodedtet = point2tet(endpoint1); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, searchtet); + if (findorg(&searchtet, endpoint1)) { + checkpoint = org(searchtet); + } + } + if (checkpoint != endpoint1) { + // Find a boundary tetrahedron to search from. + searchtet.tet = dummytet; + searchtet.loc = 0; + symself(searchtet); + // Search for the segment's first endpoint by point location. + if (locate(endpoint1, &searchtet) != ONVERTEX) { + printf("Internal error in insertsegment(): Unable to locate point\n"); + printf(" (%.12g, %.12g, %.12g) in triangulation.\n", + endpoint1[0], endpoint1[1], endpoint1[2]); + internalerror(); + } + } + // Remember this tetrahedron to improve subsequent point location. + recenttet = searchtet; + + while (true) { + collinear = finddirection(&searchtet, endpoint2); + rightpoint = dest(searchtet); + leftpoint = apex(searchtet); + oppopoint = oppo(searchtet); + if ((oppopoint == endpoint2) || (leftpoint == endpoint2) || + (rightpoint == endpoint2)) { + // We are reach the endpoint2, end of searching. + return; + } else if (collinear == LEFTCOLLINEAR) { + // We've collided with a point between the segment's endpoints. + steinerpoints->append(&leftpoint); + // Make the collinear point be the tetrahedron's origin. + enext2self(searchtet); + } else if (collinear == RIGHTCOLLINEAR) { + // We've collided with a point between the segment's endpoints. + steinerpoints->append(&rightpoint); + // Make the collinear point be the tetrahedron's origin. + enextself(searchtet); + } else if (collinear == TOPCOLLINEAR) { + // We've collided with a point between the segment's endpoints. + steinerpoints->append(&oppopoint); + // Make the collinear point be the tetrahedron's origin. + fnextself(searchtet); + enext2self(searchtet); + } else { // collinear == ACROSS or collinear == WITHIN + printf("Internal error in getsteinersinseg(): Segment is missing.\n"); + internalerror(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getdiagonalapexindex() Get the diagonal point index of the specified // +// triangle edge(that is, get the neighbour tri // +// at this edge, then get this edge's apex in it) // +// in the input triangulateio structure. // +// // +// It required that the input triangulateio structure must be created by // +// triangle with a switch -n, so triangle will generates a list of triangle // +// neighbors in the 'neighborlist' field of triangulateio structure. // +// // +// If the diagonal point exist, return the index in pointlist (>= 0), other- // +// wise, return -1. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d:: +getdiagonalapexindex(triangulateio* out, int triindex, int edgeorient) +{ + int tribase, ntribase; + int ntriindex; + int iorg, idest; + int nidiag; + + tribase = triindex * 3; + ntriindex = out->neighborlist[tribase + minus1mod3[edgeorient]]; + if (ntriindex == -1) { + return -1; + } + ntribase = ntriindex * 3; + + iorg = out->trianglelist[tribase + edgeorient]; + idest = out->trianglelist[tribase + plus1mod3[edgeorient]]; + + nidiag = out->trianglelist[ntribase]; + if ((nidiag != iorg) && (nidiag != idest)) { + return nidiag; + } + nidiag = out->trianglelist[ntribase + 1]; + if ((nidiag != iorg) && (nidiag != idest)) { + return nidiag; + } + nidiag = out->trianglelist[ntribase + 2]; + if ((nidiag != iorg) && (nidiag != idest)) { + return nidiag; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getneighbourtriedge() Get the neighbour triangle's index and edge of // +// the specified triangle edge(that is, get the // +// neighbour tri at this edge, then get this edge's // +// orient) from the input triangulateio structure. // +// // +// It required that the input triangulateio structure must be created by // +// triangle with a switch -n, so triangle will generates a list of triangle // +// neighbors in the 'neighborlist' field of triangulateio structure. // +// // +// If the neighbour tri exist and the edge orient is found, return 1, other- // +// wise, return -1. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d:: +getneighbourtriedge(struct triangulateio* out, int triindex, int edgeorient, + int* ntriindex, int* nedgeorient) +{ + int tribase, ntribase; + int iorg, idest; + int niorg, nidest; + int i; + + tribase = triindex * 3; + *ntriindex = out->neighborlist[tribase + minus1mod3[edgeorient]]; + if (*ntriindex == -1) { + return -1; + } + ntribase = *ntriindex * 3; + + iorg = out->trianglelist[tribase + edgeorient]; + idest = out->trianglelist[tribase + plus1mod3[edgeorient]]; + + *nedgeorient = -1; + for (i = 0; i < 3; i++) { + niorg = out->trianglelist[ntribase + i]; + nidest = out->trianglelist[ntribase + plus1mod3[i]]; + if ((niorg == idest) && (nidest == iorg)) { + *nedgeorient = i; + return 1; + } + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// swapdiagonal() To do an edge flip in the input triangulateio structure. // +// // +// It required that the input triangulateio structure must be created by // +// triangle with a switch -n, so triangle will generates a list of triangle // +// neighbors in the 'neighborlist' field of triangulateio structure. // +// // +// nleftcasing // +// // +// dest norg__ napex dest ______ napex // +// |\ \ | | / /| // +// | \ \<----- nswapedge | / / | // +// rightcasing | \ \ | | / / | // +// | \ \ | nrightcasing | / / | // +// swapedge ----->\ \ | | / / | // +// |_____\ \| |/ /_____| // +// apex org ndest apex ndest // +// // +// leftcasing // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d:: +swapdiagonal(struct triangulateio* out, int triindex, int fixedge, int swapedge) +{ + int tribase, ntribase; + int ntriindex; + int nswapedge; + int org, dest, apex; + int norg, ndest, napex; + int rightcasing, leftcasing; + int nrightcasing, nleftcasing; + + tribase = triindex * 3; + ntriindex = out->neighborlist[tribase + minus1mod3[swapedge]]; + if (ntriindex == -1) { + return 0; // ntriindex does not exist. + } + ntribase = ntriindex * 3; + + org = out->trianglelist[tribase + swapedge]; + dest = out->trianglelist[tribase + plus1mod3[swapedge]]; + apex = out->trianglelist[tribase + minus1mod3[swapedge]]; + // Find edge(sorg, sdest)'s orient in ntriindex. + napex = -1; + for (nswapedge = 0; nswapedge < 3; nswapedge++) { + norg = out->trianglelist[ntribase + nswapedge]; + ndest = out->trianglelist[ntribase + plus1mod3[nswapedge]]; + if ((norg == dest) && (ndest == org)) { + napex = out->trianglelist[ntribase + minus1mod3[nswapedge]]; + break; + } + } + if (napex == -1) { + return 0; // ntriindex does not contain the swap edge. + } + rightcasing = out->neighborlist[tribase + swapedge]; + leftcasing = out->neighborlist[tribase + plus1mod3[swapedge]]; + nrightcasing = out->neighborlist[ntribase + nswapedge]; + nleftcasing = out->neighborlist[ntribase + plus1mod3[nswapedge]]; + + if (fixedge == plus1mod3[swapedge]) { + // Right edge (dest, apex) of swap edge is fixed (not change). + // org <-- napex + out->trianglelist[tribase + swapedge] = napex; + // leftcasing <-- ntriindex + out->neighborlist[tribase + plus1mod3[swapedge]] = ntriindex; + // ntriindex <-- nleftcasing + out->neighborlist[tribase + minus1mod3[swapedge]] = nleftcasing; + // norg <-- apex + out->trianglelist[ntribase + nswapedge] = apex; + // nleftcasing <-- triindex + out->neighborlist[ntribase + plus1mod3[nswapedge]] = triindex; + // triindex <-- leftcasing + out->neighborlist[ntribase + minus1mod3[nswapedge]] = leftcasing; + } else if (fixedge == minus1mod3[swapedge]) { + // Left edge (apex, org) of swap edge is fixed (not change). + // dest <--> napex + out->trianglelist[tribase + plus1mod3[swapedge]] = napex; + // rightcasing <-- ntriindex + out->neighborlist[tribase + swapedge] = ntriindex; + // ntriindex <-- nrightcasing + out->neighborlist[tribase + minus1mod3[swapedge]] = nrightcasing; + // ndest <--> apex + out->trianglelist[ntribase + plus1mod3[nswapedge]] = apex; + // nrightcasing <-- triindex + out->neighborlist[ntribase + nswapedge] = triindex; + // triindex <-- rightcasing + out->neighborlist[ntribase + minus1mod3[nswapedge]] = rightcasing; + } else { + return 0; // Wrong fixedge number. + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// matchface() Matching the subface of a tetrahedron that has the same // +// vertexs as inputs. If such face be found, a subface is // +// inserted and the job is done. // +// // +// If searchtet->tet == dummytet, the subface is defined by 'torg', 'tdest' // +// and 'tapex'. If searchtet->tet != dummytet, the subface is defined by // +// 'searchtet->org()', 'searchtet->dest()' and 'tapex'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum mesh3d::matchfaceresult +mesh3d::matchface(point3d torg, point3d tdest, point3d tapex, + triface* searchtet, int newmark) +{ + triface spintet; + tetrahedron encodedtet; + point3d checkpoint; + point3d leftpoint, rightpoint, oppopoint; + enum finddirectionresult collinear; + int hitbdry; + + if (searchtet->tet == dummytet) { + // Find a tetrahedron whose origin is the face's org. + checkpoint = (point3d) NULL; + encodedtet = point2tet(torg); + if (encodedtet != (tetrahedron) NULL) { + decode(encodedtet, *searchtet); + if (findorg(searchtet, torg)) { + checkpoint = org(*searchtet); + } + } + if (checkpoint != torg) { + // Find a boundary tetrahedron to search from. + searchtet->tet = dummytet; + searchtet->loc = 0; + symself(*searchtet); + // Search for the segment's first endpoint by point location. + if (locate(torg, searchtet) != ONVERTEX) { + printf("Internal error in insertsegment(): Unable to locate point\n"); + printf(" (%.12g, %.12g, %.12g) in triangulation.\n", + torg[0], tdest[1], tapex[2]); + internalerror(); + } + } + // Remember this tetrahedron to improve subsequent point location. + recenttet = *searchtet; + collinear = finddirection(searchtet, tdest); + rightpoint = dest(*searchtet); + leftpoint = apex(*searchtet); + oppopoint = oppo(*searchtet); + if ((oppopoint == tdest) || (leftpoint == tdest) || + (rightpoint == tdest)) { + if (oppopoint == tdest) { + fnextself(*searchtet); + enext2self(*searchtet); + esymself(*searchtet); + } else if (leftpoint == tdest) { + enext2self(*searchtet); + esymself(*searchtet); + } + } else { + return EDGEMISSING; + } + // Here the origin of 'searchtet' must be is torg. + assert(org(*searchtet) == torg); + } + + if (apex(*searchtet) != tapex) { + // Spin around edge torg and tdest, to find a face that contain tapex. + spintet = *searchtet; + hitbdry = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == apex(*searchtet)) { + break; // Rewind, can leave now. + } + if (apex(spintet) == tapex) { + // This is the face we looking for. + *searchtet = spintet; + break; + } + } else { + hitbdry ++; + if(hitbdry >= 2) { + break; + } else { + esym(*searchtet, spintet); + } + } + } + } + if (apex(*searchtet) == tapex) { + // Insert a subface, if there isn't already one there. + insertsubface(searchtet, newmark, 1); + return FACEMATCHING; + } else { + return APEXMISSING; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertfieldpoint() Generate and insert a field(steiner) point into // +// current mesh to form a triangulation for the input // +// triangular faces. // +// // +// This routine is called when gift-wrapping failed. The recover facet job // +// will be done(not neccessary) by insert field point to mesh. // +// // +// An important thing is the new field point must lies 'below' all the faces // +// (giftfaces) (that is visivle from all faces) and lies 'in' the hole. // +// // +// The 'crosstetlist' is a list which keeps all crossing tets, that can help // +// us to determine if the new field point is located in the hole. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::insertfieldpoint(int numberofgiftfaces, triface* giftfaces, + int numberofgiftpoints, point3d* giftpoints, + list* crosstetlist) +{ + link *unfinfacelist; + triface newtet, otherface, *checktetptr; + face checksh; + point3d newpoint; + enum locateresult loc; + int findex; + int i, j; + + if (verbose > 1) { + printf(" Generate field point to complete recover face.\n"); + } + // Create the new point. + newpoint = (point3d) points.alloc(); + // It's a inner point. + setpointmark(newpoint, 0); + if (verbose > 1) { + // For debug, need use the pointmark to keep point's index. + setpointmark(newpoint, points.items); + } + newpoint[0] = newpoint[1] = newpoint[2] = 0.0; + for (i = 0; i < numberofgiftpoints; i++) { + newpoint[0] += giftpoints[i][0]; + newpoint[1] += giftpoints[i][1]; + newpoint[2] += giftpoints[i][2]; + // Interpolate its coordinate and attributes. + for (j = 3; j < 3 + nextras; j++) { + newpoint[j] += giftpoints[i][j]; + } + } + newpoint[0] /= numberofgiftpoints; + newpoint[1] /= numberofgiftpoints; + newpoint[2] /= numberofgiftpoints; + // Interpolate its coordinate and attributes. + for (j = 3; j < 3 + nextras; j++) { + newpoint[j] /= numberofgiftpoints; + } + + // Check if newpoint lies in the hole. + for (i = 0; i < crosstetlist->len(); i++) { + checktetptr = (triface*) (*crosstetlist)[i]; + loc = isintet(org(*checktetptr), dest(*checktetptr), apex(*checktetptr), + oppo(*checktetptr), newpoint); + if (loc != OUTSIDE) { + break; + } + } + if (i >= crosstetlist->len()) { + // This new point is outside the hole. + if (!quiet) { + printf("Internal error in insertfieldpoint(): \n"); + printf(" The barycenter of each corner points locates outside the\n"); + printf(" polyhedra of this corner point and their bounding faces.\n"); + internalerror(); + } + pointdealloc(newpoint); + return 0; + } + // Check if newpoint lies below(is visible from) the all giftface. + for (i = 0; i < numberofgiftfaces; i++) { + if (!isbelowplane(&(giftfaces[i]), newpoint)) { + break; + } + } + if (i < numberofgiftfaces) { + if (!quiet) { + printf("Internal error in insertfieldpoint(): \n"); + printf(" The barycenter of each corner points does not visible from\n"); + printf(" all bounding faces of the polyhedra.\n"); + internalerror(); + } + pointdealloc(newpoint); + return 0; + } + + if (verbose > 1) { + printf(" Create new point (%.12g, %.12g, %.12g) %d.\n", + newpoint[0], newpoint[1], newpoint[2], pointmark(newpoint)); + } + + // Setup a list for keeping all unfinished faces. + unfinfacelist = new link(sizeof(triface)); + // Set user-defined compare function for comparing faces. + unfinfacelist->setcomp((compfunc) &issameface); + + // Create a initial tet use giftfaces[0] and newpoint. + maketetrahedron(&newtet); + setorg(newtet, dest(giftfaces[0])); + setdest(newtet, org(giftfaces[0])); + setapex(newtet, apex(giftfaces[0])); + setoppo(newtet, newpoint); + // Make the connection between giftfaces[i] and newtet. + bond(newtet, giftfaces[0]); + tspivot(giftfaces[0], checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(newtet, checksh); + } + // Add the new tetrahedron's three faces to unfinished faces list. + // Keep 'newpoint' be apex of each faces. + otherface = newtet; + fnextself(otherface); + unfinfacelist->add(&otherface); + otherface = newtet; + enextfnextself(otherface); + unfinfacelist->add(&otherface); + otherface = newtet; + enext2fnextself(otherface); + unfinfacelist->add(&otherface); + if (verbose > 2) { + printf(" Creating newtet "); + dump(&newtet); + } + for (i = 1; i < numberofgiftfaces; i++) { + maketetrahedron(&newtet); + setorg(newtet, dest(giftfaces[i])); + setdest(newtet, org(giftfaces[i])); + setapex(newtet, apex(giftfaces[i])); + setoppo(newtet, newpoint); + // Make the connection between giftfaces[i] and newtet. + bond(newtet, giftfaces[i]); + tspivot(giftfaces[i], checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(newtet, checksh); + } + // Check and bond three inner faces of newtet. + otherface = newtet; + fnextself(otherface); + findex = unfinfacelist->hasitem(&otherface); + if ((findex > 0) && (findex <= unfinfacelist->len())) { + checktetptr = (triface *) unfinfacelist->getnitem(findex); + bond(otherface, *checktetptr); + unfinfacelist->del(findex); // Remove it. + } else { + unfinfacelist->add(&otherface); + } + otherface = newtet; + enextfnextself(otherface); + findex = unfinfacelist->hasitem(&otherface); + if ((findex > 0) && (findex <= unfinfacelist->len())) { + checktetptr = (triface *) unfinfacelist->getnitem(findex); + bond(otherface, *checktetptr); + unfinfacelist->del(findex); // Remove it. + } else { + unfinfacelist->add(&otherface); + } + otherface = newtet; + enext2fnextself(otherface); + findex = unfinfacelist->hasitem(&otherface); + if ((findex > 0) && (findex <= unfinfacelist->len())) { + checktetptr = (triface *) unfinfacelist->getnitem(findex); + bond(otherface, *checktetptr); + unfinfacelist->del(findex); // Remove it. + } else { + unfinfacelist->add(&otherface); + } + if (verbose > 2) { + printf(" Creating newtet "); + dump(&newtet); + } + } + assert(unfinfacelist->len() == 0); + + delete unfinfacelist; + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoverface() Recover a subface that is missing from the volume mesh. // +// // +// This is done by local re-triangulate the missing faces area. Use gift- // +// wrapping algorithm do local triangulation. // +// // +// This function follow these steps: // +// // +// (1) Identify all missing faces from 2D surface mesh, and all vertexs of // +// these missing faces. The missing face will be found one by one start // +// from the input triedge(triindex and edgeorient, this edge must exist // +// in current tetrahedralization). Check other 2 edges are existed. If // +// find oneof edges is missing, this edge's neighbour face is a missing // +// face, take the neighbour triedge and keep it. Then loop untill all // +// missing faces be found. We use a list(missingfacelist) to keep all // +// missingface's index in 2D mesh(out), use a list(equatorpointlist) to // +// keep all the vertexs of missing faces. // +// // +// (2) Identify all the cross edges. These edges penerate all the missing // +// faces and cause they cannot present in current tetrahedralization. // +// The cross edges will be found one by one. A loop is used untill all // +// cross edges are found. We use a list(crossedgelist) to keep all the // +// cross edges. // +// // +// (3) Identify all the cross tetrahedra and classify north and sourth // +// points of endpoints of cross edges. We can identify the cross tets // +// from cross edges. Each cross edge has a set of tetrahedra surrounding // +// it in the mesh. The identify all cross tets procedure proceeds by // +// interrogating the tetrahedra around each cross edge. At the same time,// +// we can identify the endpoint of a cross edge's location (north or // +// sourth). We use a list(crosstetlist) to keep all cross tetrahedra, // +// two list(northpointlist and sourthpointlist) to keep the endpoints of // +// cross edges respectly. // +// // +// (4) Identify and classify all the casing tets(tet which adjoining the // +// cross tets but not a cross tet). Casing tets will be used to generate // +// giftfaces. Pay special attention to protect subsegments for all cross // +// tets. Because they will be deleted before do gift-wrapping. There has // +// a special codes to protect subsegments from cross tets to casing tets // +// and dellocate inner nonsolid subfaces (Here we can't use preservesub- // +// segment() routine directly). If a casing tet doesnot exist, this will // +// happened when a cross tet lies on boundary. We create a fake tet for // +// holding the casing tet's face temporarily. After gift-wrapping, do a // +// remove fake tets job. At last, We can delete all crosstets. So the // +// memory can be re-used for later re-generation. We use two list(north- // +// cassinglist and sourthcassinglist) to keep all the casing tets. // +// // +// (5) Create all the equatorcasing tets. At first, there not exist equator- // +// casing tet. So like above, temporarily create a fake tetrahedron(oppo // +// = null) for holding this equator face. After re-triangulation, they // +// will be removed. First, all casing tets are faceing toward 'tsourth', // +// because we triangulating the 'tnorth' side first. We use a list(equa- // +// tcasinglist) to keep all equator casing tets. // +// // +// (6) Do local re-meshing at both north and sourth side of this facet. The // +// well-known Gift-wrapping algorithm is used at here. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::recoverface(list* pointlist, triangulateio* out, int triindex, + int edgeorient) +{ + list *missingfacelist; + list *crossedgelist, *crosstetlist; + list *equatorpointlist, *northpointlist, *sourthpointlist; + list *equatorcasinglist, *northcasinglist, *sourthcasinglist; + list *backtracelist; + triface *giftfaces; + triface crossedge, crosstet; + triface starttet, spintet; + triface checkface, *checkfaceptr; + face checksh, checkshseg; + point3d torg, tdest, tapex; + point3d tnorth, tsourth, tother; + point3d *giftpoints; + enum matchfaceresult match; + int numberofgiftfaces, numberofgiftpoints; + int *iptr, iorg, idest, iapex; + int hitbdry, orient1, orient2; + int edgecount, successbonded; + int i, j, index, errorflag; + + if (verbose > 1) { + printf(" Recover missing subfaces by local re-triangulation.\n"); + } + + // Step (1): + if (verbose > 2) { + printf(" Identify all missing subfaces.\n"); + } + equatorpointlist = new list("point"); + missingfacelist = new list(sizeof(int) * 2); + + iptr = (int*) missingfacelist->alloc(); + iptr[0] = triindex; + iptr[1] = edgeorient; + for (i = 0; i < missingfacelist->len(); i++) { + iptr = (int*) (*missingfacelist)[i]; + triindex = iptr[0]; + edgeorient = iptr[1]; + index = triindex * 3; + iorg = out->trianglelist[index + edgeorient]; + idest = out->trianglelist[index + plus1mod3[edgeorient]]; + iapex = out->trianglelist[index + minus1mod3[edgeorient]]; + torg = *(point3d*)(*pointlist)[iorg]; + tdest = *(point3d*)(*pointlist)[idest]; + tapex = *(point3d*)(*pointlist)[iapex]; + if (verbose > 2) { + printf(" Subface (%d, %d, %d) is missing.\n", + pointmark(torg), pointmark(tdest), pointmark(tapex)); + } + if (equatorpointlist->hasitem(&torg) == -1) { + equatorpointlist->append(&torg); + } + if (equatorpointlist->hasitem(&tdest) == -1) { + equatorpointlist->append(&tdest); + } + if (equatorpointlist->hasitem(&tapex) == -1) { + equatorpointlist->append(&tapex); + } + // Skip the current edge; Check other edges are existing. + starttet.tet = dummytet; + match = matchface(tdest, tapex, torg, &starttet, 0); + assert(match != FACEMATCHING); + if (match == EDGEMISSING) { + // There must exist another missing face. + iptr = (int*) missingfacelist->alloc(); + // Get neighbour triangle's index and edgeorient. + getneighbourtriedge(out, triindex, plus1mod3[edgeorient], + &iptr[0], &iptr[1]); + assert((iptr[0] != -1) && (iptr[1] != -1)); + } + starttet.tet = dummytet; + match = matchface(tapex, torg, tdest, &starttet, 0); + assert(match != FACEMATCHING); + if (match == EDGEMISSING) { + // There must exist another missing face. + iptr = (int*) missingfacelist->alloc(); + // Get neighbour triangle's index and edgeorient. + getneighbourtriedge(out, triindex, minus1mod3[edgeorient], + &iptr[0], &iptr[1]); + assert((iptr[0] != -1) && (iptr[1] != -1)); + } + } + + // Step (2): + if (verbose > 2) { + printf(" Identify all the cross edges.\n"); + } + crossedgelist = new list(sizeof(triface)); + crossedgelist->setcomp((compfunc) &issameedge); + + // Find one cross edge which cause this subface missing. Here we can + // search from starttet. (starttet hold one of existing edges of the + // last found missing face: torg, tdest, tapex.) + + // First find one of points in 'torg', 'tdest' and 'tapex', which does + // not belong to 'starttet'. + if (isfacehaspoint(&starttet, torg)) { + if (isfacehaspoint(&starttet, tdest)) { + tnorth = tapex; + } else { + tnorth = tdest; + } + } else { + tnorth = torg; + } + // Find which (fnext) direction we should go. + if (!isaboveplane(&starttet, tnorth)) { + esymself(starttet); + } + spintet = starttet; + hitbdry = 0; + orient1 = iorient3d(torg, tdest, tapex, apex(spintet)); + errorflag = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == apex(starttet)) { + errorflag = 1; + break; + } + orient2 = iorient3d(torg, tdest, tapex, apex(spintet)); + if (orient1 == 0) { + if (orient2 != 0) orient1 = orient2; + } else { + if ((orient2 != 0) && (orient1 != orient2)) { + crossedge = spintet; + adjustedgering(crossedge, CCW); + enextfnextself(crossedge); + enextself(crossedge); + break; + } + } + } else { + hitbdry ++; + if (hitbdry >= 2) { + errorflag = 1; + break; + } else { + esym(starttet, spintet); + } + } + } + if (errorflag) { + // No cross edge be found. It may cause by co-circular points of this + // facet(The Delaunay triangulation is not unique). + if (verbose > 1) { + printf(" No cross edge be found, return anyway.\n"); + } + delete equatorpointlist; + delete missingfacelist; + delete crossedgelist; + return 0; + } + // Get all other cross edges. + if (verbose > 2) { + printf(" Queueing cross edge (%d, %d).\n", + pointmark(org(crossedge)), pointmark(dest(crossedge))); + } + crossedgelist->append(&crossedge); + // Walk around this edge, identifying all other incident cross edges. + // Note: Here we should not hit boundry. + for (i = 0; i < crossedgelist->len(); i++) { + spintet = starttet = *(triface*)(*crossedgelist)[i]; + while (fnextself(spintet)) { + if (apex(spintet) == apex(starttet)) { + break; // Rewind, can leave now. + } + // Grab other vertex of this face. + tother = apex(spintet); + // If vertex is not one of equators(and should not coplanar with + // them), there's another bad edge here. Identify it and store it. + if (equatorpointlist->hasitem(&tother) == -1) { + // Find which edge is across the plane in spintet. + orient1 = iorient3d(torg, tdest, tapex, org(spintet)); + orient2 = iorient3d(torg, tdest, tapex, tother); + crossedge = spintet; + if (orient1 != orient2) { + // edge tother and 'org' is the cross edge. + enext2self(crossedge); + esymself(crossedge); + } else { + // egde 'dest' and tother is the cross edge. + enextself(crossedge); + esymself(crossedge); + } + if (crossedgelist->hasitem(&crossedge) == -1) { + if (verbose > 2) { + printf(" Queueing cross edge (%d, %d).\n", + pointmark(org(crossedge)), pointmark(dest(crossedge))); + } + crossedgelist->append(&crossedge); + } + } + } + } + + // Step (3): + if (verbose > 2) { + printf(" Classify all the north and sourth points and\n"); + printf(" identify all the cross(to be dead) tetrahedra.\n"); + } + crosstetlist = new list(sizeof(triface)); + crosstetlist->setcomp((compfunc) &compare2tets); + northpointlist = new list("point"); + sourthpointlist = new list("point"); + + for (i = 0; i < crossedgelist->len(); i++) { + spintet = starttet = *(triface*)(*crossedgelist)[i]; + orient1 = iorient3d(torg, tdest, tapex, org(spintet)); + assert(orient1 != 0); + if (orient1 < 0) { + tnorth = org(spintet); + tsourth = dest(spintet); + } else { // orient1 > 0 + tnorth = dest(spintet); + tsourth = org(spintet); + } + if (northpointlist->hasitem(&tnorth) == -1) { + if (verbose > 2) { + printf(" Queueing north point %d.\n", pointmark(tnorth)); + } + northpointlist->append(&tnorth); + } + if (sourthpointlist->hasitem(&tsourth) == -1) { + if (verbose > 2) { + printf(" Queueing sourth point %d.\n", pointmark(tsourth)); + } + sourthpointlist->append(&tsourth); + } + if (crosstetlist->hasitem(&spintet) == -1) { + if (verbose > 2) { + printf(" Queueing cross tet (%d, %d, %d, %d).\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet)), pointmark(oppo(spintet))); + } + crosstetlist->append(&spintet); + } + while (fnextself(spintet)) { + if (apex(spintet) == apex(starttet)) { + break; // Rewind, can leave now. + } + if (crosstetlist->hasitem(&spintet) == -1) { + if (verbose > 2) { + printf(" Queueing cross tet (%d, %d, %d, %d).\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet)), pointmark(oppo(spintet))); + } + crosstetlist->append(&spintet); + } + } + } + + // Step (4): + if (verbose > 2) { + printf(" Identifying and classifying north and sourth casing faces.\n"); + } + northcasinglist = new list(sizeof(triface)); + sourthcasinglist = new list(sizeof(triface)); + + for (i = 0; i < crosstetlist->len(); i++) { + crosstet = *(triface*) (*crosstetlist)[i]; + if (verbose > 2) { + printf(" Get cross tet (%d, %d, %d, %d).\n", + pointmark(org(crosstet)), pointmark(dest(crosstet)), + pointmark(apex(crosstet)), pointmark(oppo(crosstet))); + } + // Check each face of this crosstet to see if there exist casing face. + // If a face's neighbour tet not in crosstetlist, then it's a casing + // face. At the same time, find the inner subface if exist and delete + // it, don't forget to protect subsegment before delete subface. + for (j = 0; j < 4; j++) { + crosstet.loc = j; + sym(crosstet, checkface); + tspivot(crosstet, checksh); + // Here checkface maybe hold 'dummytet'. + if (crosstetlist->hasitem(&checkface) == -1) { + // It's a casing face of the cavity. + adjustedgering(crosstet, CCW); + torg = org(crosstet); + tdest = dest(crosstet); + tapex = apex(crosstet); + if (checkface.tet == dummytet) { + // This side of crosstet is a boundary face, we need it. + // Temporarily create a fake tetrahedron(oppo = null) for + // holding the face. After re-generation, it will be removed. + maketetrahedron(&checkface); + checkface.loc = 0; // For sure. + setorg(checkface, tdest); + setdest(checkface, torg); + setapex(checkface, tapex); + // setoppo(checkface, NULL); + if (verbose > 2) { + printf(" Creating a fake tet for holding face(%d, %d, %d).\n", + pointmark(tdest), pointmark(torg), pointmark(tapex)); + } + // Temporarily bond them for later protect segments. + bond(checkface, crosstet); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(checkface, checksh); + } + } + adjustedgering(checkface, CCW); + if ((northpointlist->hasitem(&torg) >= 0) || + (northpointlist->hasitem(&tdest) >= 0) || + (northpointlist->hasitem(&tapex) >= 0)) { + if (verbose > 2) { + printf(" Queuing northcasing face (%d, %d, %d).\n", + pointmark(org(checkface)), pointmark(dest(checkface)), + pointmark(apex(checkface))); + } + northcasinglist->append(&checkface); + } else { + if (verbose > 2) { + printf(" Queuing sourthcasing face (%d, %d, %d).\n", + pointmark(org(checkface)), pointmark(dest(checkface)), + pointmark(apex(checkface))); + } + sourthcasinglist->append(&checkface); + } + } else { + // Its a inner face. Check if there exist(to be dead) subface. + if (checksh.sh != dummysh) { + assert(isnonsolid(checksh)); + // Protect subsegment if there need. + findversion(&crosstet, &checksh, 0); // For same enext() direction. + edgecount = 0; + while (edgecount < 3) { + sspivot(checksh, checkshseg); + if (checkshseg.sh != dummysh) { + face tmpchecksh; + spivot(checkshseg, tmpchecksh); + if (tmpchecksh.sh == checksh.sh) { + // We must protect this subsegment, otherwise the pointer + // in checkshseg will be invalid after deallocate checksh. + spintet = crosstet; + hitbdry = 0; + successbonded = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == apex(crosstet)) { + break; + } + tspivot(spintet, tmpchecksh); + if (tmpchecksh.sh != dummysh) { + findversion(&tmpchecksh, &spintet); + ssbond(tmpchecksh, checkshseg); + successbonded = 1; + break; + } + } else { + hitbdry ++; + if (hitbdry >= 2) { + break; + } else { + esym(crosstet, spintet); + } + } + } + if (!successbonded) { + // Can't find a exist subface to hold this subsegment. + // We must insert a new (nonsolid) subface which must + // be inserted at a face which its tet is not in + // crosstetlist. + // Note: + // (1) If we find such face, still need check if it's + // a face of fake tet(oppo() = NULL), if so, we need + // insert the new subface at its real face side, so + // during the gift-wrapping stage, it will be bonded + // automatically and we can safely remove the fake tet. + // (2) If we can't find such face, + spintet = crosstet; + hitbdry = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == apex(crosstet)) { + break; + } + if (crosstetlist->hasitem(&spintet) == -1) { + if (spintet.tet[7] == NULL) { + // It's a fake tet. the subface should insert at + // its real face(that is, loc = 0). + spintet.loc = 0; + if (verbose > 2) { + printf(" Creating a nonsolid subface at fake"); + printf(" tet (%d, %d %d)\n", + pointmark(org(spintet)), + pointmark(dest(spintet)), + pointmark(apex(spintet))); + } + } else { + if (verbose > 2) { + printf(" Creating a nonsolid subface at"); + printf(" casing tet (%d, %d, %d, %d)\n", + pointmark(org(spintet)), + pointmark(dest(spintet)), + pointmark(apex(spintet)), + pointmark(oppo(spintet))); + } + } + if (verbose > 2) { + printf(" for holding subsegment (%d, %d).\n", + pointmark(sorg(checkshseg)), + pointmark(sdest(checkshseg))); + } + insertsubface(&spintet, NONSOLIDFLAG, 1); + successbonded = 1; + break; + } + } else { + hitbdry ++; + if (hitbdry >= 2) { + break; + } else { + esym(crosstet, spintet); + } + } + } + if (!successbonded) { + // We can't find a face suitable for holding subsegment. + // Find a boundary face around this subsegment(spintet + // ) and create a fake tet at this face, then insert + // a new subface at this fake tet. We can sure, this + // fake tet will be added to casing face list later. + triface tmpfaketet; + int count = 1000; + while (fnextself(spintet) && count) count--; + if (count <= 0) { + if (verbose) { + printf("Warnning in recoverface():\n"); + printf(" We can't find a face suitable for holding"); + printf(" subsegment, skip anyway.\n"); + } + } else { + adjustedgering(spintet, CCW); + torg = org(spintet); + tdest = dest(spintet); + tapex = apex(spintet); + maketetrahedron(&tmpfaketet); + tmpfaketet.loc = 0; // For sure. + setorg(tmpfaketet, tdest); + setdest(tmpfaketet, torg); + setapex(tmpfaketet, tapex); + // setoppo(tmpfaketet, NULL); + if (verbose > 2) { + printf(" Creating a fake tet for holding"); + printf(" subface (%d, %d, %d).\n", pointmark(tdest), + pointmark(torg), pointmark(tapex)); + } + bond(tmpfaketet, spintet); + if (verbose > 2) { + printf(" Creating a nonsolid subface (%d, %d, %d)\n", + pointmark(torg), pointmark(tdest), + pointmark(tapex)); + printf(" for holding subsegment (%d, %d).\n", + pointmark(sorg(checkshseg)), + pointmark(sdest(checkshseg))); + } + // Insert a new subface, and it will automatically bond + // the subsegment(set autobond flag be '1'). + insertsubface(&spintet, NONSOLIDFLAG, 1); + successbonded = 1; + } + } + // assert(successbonded); + } + } + } + senextself(checksh); + enextself(crosstet); + edgecount++; + } + // This subface can be dealloced now. Before dealloc, we must + // dissolve it from two side tetrahedra. + tsdissolve(crosstet); + tsdissolve(checkface); + if (verbose > 2) { + printf(" Deleting subface (%d, %d, %d) (nonsolid).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + shellfacedealloc(&subfaces, checksh.sh); + } + } + } + } + assert(northcasinglist->len()); + assert(sourthcasinglist->len()); + + // Step (5): + if (verbose > 2) { + printf(" Creating casing faces for missing faces(at equator).\n"); + } + equatorcasinglist = new list(sizeof(triface)); + + for (i = 0; i < missingfacelist->len(); i++) { + iptr = (int*) (*missingfacelist)[i]; + index = iptr[0] * 3; + iorg = out->trianglelist[index + edgeorient]; + idest = out->trianglelist[index + plus1mod3[edgeorient]]; + iapex = out->trianglelist[index + minus1mod3[edgeorient]]; + torg = *(point3d*)(*pointlist)[iorg]; + tdest = *(point3d*)(*pointlist)[idest]; + tapex = *(point3d*)(*pointlist)[iapex]; + maketetrahedron(&checkface); + orient1 = iorient3d(torg, tdest, tapex, tsourth); + assert(orient1 != 0); + if (orient1 < 0) { + setorg(checkface, torg); + setdest(checkface, tdest); + } else { + setorg(checkface, tdest); + setdest(checkface, torg); + } + setapex(checkface, tapex); + if (verbose > 2) { + printf(" Creating and queuing equatorcasing face (%d, %d, %d).\n", + pointmark(org(checkface)), pointmark(dest(checkface)), + pointmark(apex(checkface))); + } + equatorcasinglist->append(&checkface); + } + assert(equatorcasinglist->len()); + + // Step (6): + // Generate giftfaces, giftpoints and giftpointdegrees at north side. + numberofgiftfaces = equatorcasinglist->len() + northcasinglist->len(); + numberofgiftpoints = equatorpointlist->len() + northpointlist->len(); + giftfaces = new triface[numberofgiftfaces]; + giftpoints = new point3d[numberofgiftpoints]; + // Create a backtrace list to keep all new tets be created during + // gift-wrapping stage, so we can delete them if gift-wrapping failed. + backtracelist = new list(sizeof(triface)); + + // Generate giftfaces. + for (i = 0; i < equatorcasinglist->len(); i++) { + giftfaces[i] = *(triface*)(*equatorcasinglist)[i]; + } + for (j = i; j < numberofgiftfaces; j++) { + giftfaces[j] = *(triface*)(*northcasinglist)[j - i]; + dissolve(giftfaces[j]); + } + // Generate giftpoints. + for (i = 0; i < equatorpointlist->len(); i++) { + giftpoints[i] = *(point3d*)(*equatorpointlist)[i]; + } + for (j = i; j < numberofgiftpoints; j++) { + giftpoints[j] = *(point3d*)(*northpointlist)[j - i]; + } + + errorflag = giftwrapping(numberofgiftfaces, giftfaces, numberofgiftpoints, + giftpoints, NULL, 0, backtracelist); + if (errorflag == 0) { + if (verbose > 2) { + printf(" Badly, gift-wrapping failed, clear all new tets that"); + printf(" created during gift-wrapping.\n"); + } + for (i = 0; i < backtracelist->len(); i++) { + checkfaceptr = (triface*)(*backtracelist)[i]; + if (verbose > 2) { + printf(" Deleting new tet (%d, %d, %d, %d).\n", + pointmark(org(*checkfaceptr)), pointmark(dest(*checkfaceptr)), + pointmark(apex(*checkfaceptr)), pointmark(oppo(*checkfaceptr))); + } + tetrahedrondealloc(checkfaceptr->tet); + } + if (!insertfieldpoint(numberofgiftfaces, giftfaces, numberofgiftpoints, + giftpoints, crosstetlist)) { + printf("Internalerror in recoverface(): Insert field point failed.\n"); + internalerror(); + } + } + + if (verbose > 2) { + printf(" Removing and replacing fake tets at equator with real tets.\n"); + } + for (i = 0; i < equatorcasinglist->len(); i++) { + checkfaceptr = (triface*)(*equatorcasinglist)[i]; + assert(oppo(*checkfaceptr) == (point3d) NULL); + sym(*checkfaceptr, starttet); + dissolve(starttet); + // Remove fake tet. + tetrahedrondealloc(checkfaceptr->tet); + // Replace fake tet with a real tet. + adjustedgering(starttet, CCW); + *checkfaceptr = starttet; + // Set a hit pointer in vertexs of face. + torg = org(starttet); + tdest = dest(starttet); + tapex = apex(starttet); + setpoint2tet(torg, encode(starttet)); + setpoint2tet(tdest, encode(starttet)); + setpoint2tet(tapex, encode(starttet)); + if (verbose > 2) { + printf(" Changing (%d, %d, %d).\n", + pointmark(torg), pointmark(tdest), pointmark(tapex)); + } + } + delete [] giftfaces; + delete [] giftpoints; + backtracelist->clear(); + + // Generate giftfaces, giftpoints and giftpointdegrees at sourth side. + numberofgiftfaces = equatorcasinglist->len() + sourthcasinglist->len(); + numberofgiftpoints = equatorpointlist->len() + sourthpointlist->len(); + giftfaces = new triface[numberofgiftfaces]; + giftpoints = new point3d[numberofgiftpoints]; + + // Generate giftfaces. + for (i = 0; i < equatorcasinglist->len(); i++) { + giftfaces[i] = *(triface*)(*equatorcasinglist)[i]; + } + for (j = i; j < numberofgiftfaces; j++) { + giftfaces[j] = *(triface*)(*sourthcasinglist)[j - i]; + dissolve(giftfaces[j]); + } + // Generate giftpoints. + for (i = 0; i < equatorpointlist->len(); i++) { + giftpoints[i] = *(point3d*)(*equatorpointlist)[i]; + } + for (j = i; j < numberofgiftpoints; j++) { + giftpoints[j] = *(point3d*)(*sourthpointlist)[j - i]; + } + + errorflag = giftwrapping(numberofgiftfaces, giftfaces, numberofgiftpoints, + giftpoints, NULL, 0, backtracelist); + if (errorflag == 0) { + if (verbose > 2) { + printf(" Badly, gift-wrapping failed, clear all new tets that"); + printf(" created during gift-wrapping.\n"); + } + for (i = 0; i < backtracelist->len(); i++) { + checkfaceptr = (triface*)(*backtracelist)[i]; + if (verbose > 2) { + printf(" Deleting new tet (%d, %d, %d, %d).\n", + pointmark(org(*checkfaceptr)), pointmark(dest(*checkfaceptr)), + pointmark(apex(*checkfaceptr)), pointmark(oppo(*checkfaceptr))); + } + tetrahedrondealloc(checkfaceptr->tet); + } + if (!insertfieldpoint(numberofgiftfaces, giftfaces, numberofgiftpoints, + giftpoints, crosstetlist)) { + printf("Internalerror in recoverface(): Generate field point failed.\n"); + internalerror(); + } + } + + if (verbose > 2) { + printf(" Removing and replacing fake tets in north with dummytet.\n"); + } + for (i = 0; i < northcasinglist->len(); i++) { + checkface = *(triface*)(*northcasinglist)[i]; + if (oppo(checkface) == (point3d) NULL) { + if (verbose > 2) { + printf(" Changing (%d, %d, %d).\n", + pointmark(org(checkface)), pointmark(dest(checkface)), + pointmark(apex(checkface))); + } + sym(checkface, starttet); + dissolve(starttet); + tspivot(checkface, checksh); + if (checksh.sh != dummysh) { + stdissolve(checksh); + } + tetrahedrondealloc(checkface.tet); + checkface = starttet; + } + // Set a hit pointer in vertexs of face. + torg = org(checkface); + tdest = dest(checkface); + tapex = apex(checkface); + setpoint2tet(torg, encode(checkface)); + setpoint2tet(tdest, encode(checkface)); + setpoint2tet(tapex, encode(checkface)); + } + + if (verbose > 2) { + printf(" Removing and replacing fake tets in sourth with dummytet.\n"); + } + for (i = 0; i < sourthcasinglist->len(); i++) { + checkface = *(triface*)(*sourthcasinglist)[i]; + if (oppo(checkface) == (point3d) NULL) { + if (verbose > 2) { + printf(" Changing (%d, %d, %d).\n", + pointmark(org(checkface)), pointmark(dest(checkface)), + pointmark(apex(checkface))); + } + sym(checkface, starttet); + dissolve(starttet); + tspivot(checkface, checksh); + if (checksh.sh != dummysh) { + stdissolve(checksh); + } + tetrahedrondealloc(checkface.tet); + checkface = starttet; + } + // Set a hit pointer in vertexs of face. + torg = org(checkface); + tdest = dest(checkface); + tapex = apex(checkface); + setpoint2tet(torg, encode(checkface)); + setpoint2tet(tdest, encode(checkface)); + setpoint2tet(tapex, encode(checkface)); + } + + // We can delete all crosstets in crosstetlist. + for (i = 0; i < crosstetlist->len(); i++) { + crosstet = *(triface*)(*crosstetlist)[i]; + if (verbose > 2) { + printf(" Deleting tet (%d, %d, %d, %d).\n", + pointmark(org(crosstet)), pointmark(dest(crosstet)), + pointmark(apex(crosstet)), pointmark(oppo(crosstet))); + } + tetrahedrondealloc(crosstet.tet); + } + + delete [] giftfaces; + delete [] giftpoints; + delete missingfacelist; + delete crossedgelist; + delete crosstetlist; + delete equatorpointlist; + delete northpointlist; + delete sourthpointlist; + delete equatorcasinglist; + delete northcasinglist; + delete sourthcasinglist; + delete backtracelist; + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertfacet() Force the facet of PLC existing in tetrahedralization. // +// // +// First, we need form a triangulation of the PSLG facet. Obviously, we can // +// not only consider the verteices of a facet given in the .poly or .smesh // +// file. Which vertices of the tetrahedralization need to be considered in a // +// facet triangulation? The answer can be found from Shewchuk's paper [6]: // +// ... // +// It is a fact, albeit somewhat nonintuitive, that if a facet appears in // +// a Delaunay tetrahedralization as a union of faces, then the triangulation // +// of the facet is determined solely by the vertices of the tetrahedralizat- // +// ion that lies in the plane of the facet. If a vertex lies close to a // +// facet, but not in the same plane, it may cause a subfacet to be missing, // +// but it can not affect the shape of the triangulation if all subfacets are // +// present. The detail please see Jonathan's Ph.D. thesis page 92. // +// Hence the facet triangulation need only consider vertices lying in the // +// plane of the facet. Furthermore, because each facet is segment-bounded, // +// and segment are recovered (in the tetrahedralization) before facets, each // +// facet triangulation can saftly ignore vertices that lie outside the facet // +// (even in the same plane). The only addtional vertices to be considered // +// are those that were inserted on segments to force segments and other // +// facets into the mesh. // +// Unfortunately, if a facet triangulation is not unique because of // +// cocircularity degeneracies, then foregoing statement about extraplanar // +// vertices having no effect on the triangulation does not apply. To be // +// specific, suppose a facet triangulation has four or more cocircular // +// vertices, which are triangulate one way, whereas the tetrahedralization // +// contains a set of faces that triangulate the same vertices with a // +// diffrent (but also Delaunay) set of triangles. An aggressive implementat- // +// ion might identify these cases and correct the facet triangulation so // +// that it matches the tetrahedralization (it is not always possible to // +// force the tetrahedralization to match the triangulation). However, // +// inserting a new vertex at the center of the collective circumcircle is // +// always available as a lazy alternative. Here I use the first method. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::insertfacet(list* facetseglist, list* holelist, int boundmark, + int facenumber) +{ + list *pointlist, *conlist; + list *indexlist, *steinerlist; + queue *matchlist; + triface searchtet; + point3d torg, tdest, tapex, tfarapex; + point3d *segendpoints; + enum matchfaceresult match; + struct triangulateio in, out; + REAL norm[3], xaxi[3], yaxi[3]; + REAL v0[3], v1[3], dsin; + REAL *holecoords; + int *indexofsegendpoints, *indexofconpoints; + int iorg, idest, iapex, ifarapex; + int edgeorient, isswapable, maxlooptimes; + int i, j, index; + + // Set a pointlist and conlist for PSLG facet. 'pointlist' keep all the + // pointers of points. 'conlist' keep each segment's endpoint index in + // 'pointlist'. + pointlist = new list("point"); + conlist = new list(sizeof(int) * 2); + + // First form a PSLG from the input 'facetseglist'. Remeber, at this + // time, every segment is existed in the domain and is represented by + // a contiguous linear sequence of edges of the tetrahedralization. We + // must get all the steiner vertexs in the segment not merely get the + // segment's endpoints. + indexlist = new list(sizeof(int) * 2); + for (i = 0; i < facetseglist->len(); i++) { + segendpoints = (point3d*) (*facetseglist)[i]; + indexofsegendpoints = (int*) indexlist->alloc(); + if (segendpoints[1] == (point3d) NULL) { + // It's a isolate point in PSLG. + pointlist->append(&segendpoints[0]); + // We don't need this point's index. + indexofsegendpoints[0] = indexofsegendpoints[1] = -1; + } else { + indexofsegendpoints[0] = pointlist->hasitem(&segendpoints[0]); + if (indexofsegendpoints[0] == -1) { + pointlist->append(&segendpoints[0]); + indexofsegendpoints[0] = pointlist->len() - 1; + } + indexofsegendpoints[1] = pointlist->hasitem(&segendpoints[1]); + if (indexofsegendpoints[1] == -1) { + pointlist->append(&segendpoints[1]); + indexofsegendpoints[1] = pointlist->len() - 1; + } + } + } + // Get all steiner points in segments and make connection infomation. + steinerlist = new list("point", 10, 5); + for (i = 0; i < facetseglist->len(); i++) { + segendpoints = (point3d*) (*facetseglist)[i]; + if (segendpoints[1] != (point3d) NULL) { + getsteinersinseg(segendpoints[0], segendpoints[1], steinerlist); + indexofsegendpoints = (int*) (*indexlist)[i]; + indexofconpoints = (int*) conlist->alloc(); + indexofconpoints[0] = indexofsegendpoints[0]; + for (j = 0; j < steinerlist->len(); j++) { + pointlist->append((*steinerlist)[j]); + indexofconpoints[1] = pointlist->len() - 1; + indexofconpoints = (int*) conlist->alloc(); + indexofconpoints[0] = pointlist->len() - 1; + } + indexofconpoints[1] = indexofsegendpoints[1]; + steinerlist->clear(); + } + } + delete steinerlist; + delete indexlist; + + // Create the 2D mesh that will know what the correct subfaces are. + if (verbose > 1) { + printf(" Generate %d surface mesh: %d points, %d segments", + facenumber, pointlist->len(), conlist->len()); + if (holelist) { + printf(", %d holes.", holelist->len()); + } + printf("\n"); + } + triangulateioinit(&in); + triangulateioinit(&out); + if ((pointlist->len() == 3) && (conlist->len() == 3)) { + // The facet is a triangle, this case we need not do surface mesh and + // set the output mesh directly. + out.numberoftriangles = 1; + out.trianglelist = new int[3]; + out.trianglelist[0] = 0; + out.trianglelist[1] = 1; + out.trianglelist[2] = 2; + out.neighborlist = new int[3]; + out.neighborlist[0] = -1; + out.neighborlist[1] = -1; + out.neighborlist[2] = -1; + } else { + in.numberofpoints = pointlist->len(); + in.pointlist = new REAL[in.numberofpoints * 2]; + in.numberofsegments = conlist->len(); + in.segmentlist = new int[in.numberofsegments * 2]; + index = 0; + for (i = 0; i < in.numberofsegments; i ++) { + indexofconpoints = (int*) (*conlist)[i]; + in.segmentlist[index++] = indexofconpoints[0]; + in.segmentlist[index++] = indexofconpoints[1]; + } + // Determine normal, we need find two not coliner vectors from + // input points. + torg = *(point3d*)(*pointlist)[in.segmentlist[0]]; + tdest = *(point3d*)(*pointlist)[in.segmentlist[1]]; + Sub3D(tdest, torg, v0); + Normalize3D(v0); + index = 2; + dsin = 0; + for (i = 1; fabs(dsin) <= usertolerance; i++) { + torg = *(point3d*)(*pointlist)[in.segmentlist[index++]]; + tdest = *(point3d*)(*pointlist)[in.segmentlist[index++]]; + Sub3D(tdest, torg, v1); + Normalize3D(v1); + Cross3D(v0, v1, norm); + dsin = Mag3D(norm); + if (!(fabs(dsin) <= usertolerance)) Scale3D(norm, 1./dsin); + if (i >= in.numberofsegments) { + printf("Recover Error: Can't find normal for %d facet.\n", facenumber); + recovererror(); + } + } + // Project onto a plane + Assign3D(v1, xaxi); + Cross3D(norm, xaxi, yaxi); + index = 0; + for (i = 0; i < in.numberofpoints; i ++) { + torg = *(point*)(*pointlist)[i]; + in.pointlist[index++] = Dot3D(torg, xaxi); + in.pointlist[index++] = Dot3D(torg, yaxi); + } + if (holelist) { + if (holelist->len() > 0) { + in.numberofholes = holelist->len(); + in.holelist = new REAL[in.numberofholes * 2]; + index = 0; + for (i = 0; i < in.numberofholes; i++) { + holecoords = (REAL *) (*holelist)[i]; + in.holelist[index++] = Dot3D(holecoords, xaxi); + in.holelist[index++] = Dot3D(holecoords, yaxi); + } + } + } + index = 0; + // Now create the 2D mesh. + surfmesh->triangulate(&in, &out, NULL); + if (surfmesh->triangles.items <= 0) { + printf("Recover error: Can't form a surface mesh for facet %d.\n", + facenumber); + recovererror(); + } + if (verbose > 1) { + printf(" Surface mesh result: %d triangles.\n", + surfmesh->triangles.items); + } + surfmesh->trianglerestart(); + } + + // Set up a list to keep all the subface's index. + matchlist = new queue("int"); + for (i = 0; i < out.numberoftriangles; i++) { + matchlist->push(&i); + } + // To recover each of faces by swapping. In this case, though, + // no point insertion is ever needed. + maxlooptimes = out.numberoftriangles * 4; // Set a emergency break times. + if (maxlooptimes <= 0) { + // int type Overflow, set it be the largest int number. + maxlooptimes = 0x7fffffff; + } + while (matchlist->get(&i) && maxlooptimes) { + maxlooptimes--; + index = i * 3; + iorg = out.trianglelist[index++]; + idest = out.trianglelist[index++]; + iapex = out.trianglelist[index++]; + torg = *(point3d*)(*pointlist)[iorg]; + tdest = *(point3d*)(*pointlist)[idest]; + tapex = *(point3d*)(*pointlist)[iapex]; + if (verbose > 1) { + printf(" Matching subface (%d, %d, %d).\n", + pointmark(torg), pointmark(tdest), pointmark(tapex)); + } + edgeorient = 0; + searchtet.tet = dummytet; + match = matchface(torg, tdest, tapex, &searchtet, boundmark); + if (match == EDGEMISSING) { + // edge (torg, tdest) is missing. Check if other edges are missing. + searchtet.tet = dummytet; + match = matchface(tdest, tapex, torg, &searchtet, boundmark); + if (match == EDGEMISSING) { + searchtet.tet = dummytet; + match = matchface(tapex, torg, tdest, &searchtet, boundmark); + if (match == EDGEMISSING) { + // All edges of subface are missing. Push back to list to + // handle it later. + matchlist->push(&i); + continue; + } else { + // edge (tapex, torg) exist, both other 2 edges are missing. + assert(match == APEXMISSING); + edgeorient = 2; + } + } else { + // edge (tdest, tapex) exist. + assert(match == APEXMISSING); + edgeorient = 1; + } + } + if (match == APEXMISSING) { + // Two edges are missing. Check if there exist degenerate case. + // 'searchtet' must be a handle of the exist edge. + ifarapex = getdiagonalapexindex(&out, i, plus1mod3[edgeorient]); + if (ifarapex != -1) { + // Before repair the degenerate case, we should ensure that the + // new triangle is valid (it's three corners appear in counter- + // clockwise direction). Sep. 5, 2001. + // Thanks Prof. Renato Cardoso Mesquita (renato@cpdee.ufmg.br). + isswapable = 1; + if (edgeorient == 0) { + if (orient2d(&(in.pointlist[iorg * 2]), &(in.pointlist[idest * 2]), + &(in.pointlist[ifarapex * 2])) <= 0) { + isswapable = 0; + } + } else if (edgeorient == 1) { + if (orient2d(&(in.pointlist[idest * 2]), &(in.pointlist[iapex * 2]), + &(in.pointlist[ifarapex * 2])) <= 0) { + isswapable = 0; + } + } else { // edgeorient == 2 + if (orient2d(&(in.pointlist[iapex * 2]), &(in.pointlist[iorg * 2]), + &(in.pointlist[ifarapex * 2])) <= 0) { + isswapable = 0; + } + } + if (isswapable) { + tfarapex = *(point3d*)(*pointlist)[ifarapex]; + match = matchface(NULL, NULL, tfarapex, &searchtet, boundmark); + if (match == FACEMATCHING) { + swapdiagonal(&out, i, edgeorient, plus1mod3[edgeorient]); + continue; + } + } + } + ifarapex = getdiagonalapexindex(&out, i, minus1mod3[edgeorient]); + if (ifarapex != -1) { + // Before repair the degenerate case, we should ensure that the + // new triangle is valid (it's three corners appear in counter- + // clockwise direction). Sep. 5, 2001. + // Thanks Prof. Renato Cardoso Mesquita (renato@cpdee.ufmg.br). + isswapable = 1; + if (edgeorient == 0) { + if (orient2d(&(in.pointlist[iorg * 2]), &(in.pointlist[idest * 2]), + &(in.pointlist[ifarapex * 2])) <= 0) { + isswapable = 0; + } + } else if (edgeorient == 1) { + if (orient2d(&(in.pointlist[idest * 2]), &(in.pointlist[iapex * 2]), + &(in.pointlist[ifarapex * 2])) <= 0) { + isswapable = 0; + } + } else { // edgeorient == 2 + if (orient2d(&(in.pointlist[iapex * 2]), &(in.pointlist[iorg * 2]), + &(in.pointlist[ifarapex * 2])) <= 0) { + isswapable = 0; + } + } + if (isswapable) { + tfarapex = *(point3d*)(*pointlist)[ifarapex]; + match = matchface(NULL, NULL, tfarapex, &searchtet, boundmark); + if (match == FACEMATCHING) { + swapdiagonal(&out, i, edgeorient, minus1mod3[edgeorient]); + continue; + } + } + } + // Subface is missing. + matchlist->push(&i); + // Recover the missing face. + recoverface(pointlist, &out, i, edgeorient); + } + } + + if (maxlooptimes == 0) { + printf("Recover error in insertfacet():\n"); + printf(" Failed to recover facet %d.\n", facenumber); + recovererror(); + } + triangulateiodeinit(&in); + triangulateiodeinit(&out); + delete matchlist; + delete pointlist; + delete conlist; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// formskeleton() Create the shell segments and subfaces of a triangula- // +// tion, including PLC edges, facets and facets on the // +// convex hull. // +// // +// The PLC edges and facets are read from a .poly or .smesh file. The return // +// value is the number of facets in the file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::formskeleton(FILE *polyfile) +{ + list **seglist, **holelist; + point3d *segendpoints; + REAL *holecoords; + char inputline[INPUTLINESIZE]; + char *stringptr; + int *boundmarkers; + int facets, polygons, segments, inholes; + int facetmarkers; + int head, end1, end2, end3; + int i, j, k; + + if (poly || smesh) { + // Read number of facets and number of boundary markers from a .poly or + // .smesh file. + stringptr = readline(inputline, polyfile, inpolyfilename); + facets = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + facetmarkers = 0; + } else { + facetmarkers = (int) strtol (stringptr, &stringptr, 0); + } + } else { + facets = 0; + } + + if (facets > 0) { + if (!quiet) { + printf("Inserting segments into Delaunay triangulation.\n"); + } + // Compute a mapping from points to tetrahedra. + makepointmap(); + // For each facet, create a list for keeping the endpoints of + // segments of each facet, and create a list for keeping the + // holes point (coordinates) of each facet. + boundmarkers = new int[facets]; + seglist = new list*[facets]; + holelist = new list*[facets]; + for (i = 0; i < facets; i++) { + boundmarkers[i] = 0; + seglist[i] = NULL; + holelist[i] = NULL; + } + + if (poly) { + // For each facet, read and insert all segments. + for (i = 1; i <= facets; i++) { + seglist[i - 1] = new list(sizeof(point3d) * 2); + // Read number of polygons, number of holes and boundary marker of + // this facets. + // Note: I assume each polygon in facet at least has two endpoints. + stringptr = readline(inputline, polyfile, inpolyfilename); + polygons = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + inholes = 0; + boundmarkers[i - 1] = 0; // no boundary marker for this facet. + } else { + inholes = (int) strtol (stringptr, &stringptr, 0); + if (facetmarkers == 1) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + boundmarkers[i - 1] = 0; // no boundary marker for this facet. + } else { + boundmarkers[i - 1] = (int) strtol (stringptr, &stringptr, 0); + } + } + } + + // For each polygon in this facet, raed and insert all segments. + for (j = 1; j <= polygons; j++) { + // Read number of segments of a polygon. + stringptr = readline(inputline, polyfile, inpolyfilename); + segments = (int) strtol (stringptr, &stringptr, 0); + if (segments <= 1) { + printf("File I/O Error: Wrong vertex number of polygon %d", j); + printf(" in facet %d in %s.\n", i, inpolyfilename); + if (segments == 1) { + printf(" If you want define a isolate point in facet, you can\n"); + printf(" define a degenrate segment(with two same endpoints)\n "); + printf(" in stead of.\n"); + } + exit(1); + } + // Read and insert the segments. + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + // Try load another non-empty line to continue + stringptr = readline(inputline, polyfile, inpolyfilename); + if (*stringptr == '\0') { + printf("File I/O Error: No endpoints for polygon %d", j); + printf(" in facet %d in %s.\n", i, inpolyfilename); + exit(1); + } else { + head = end1 = (int) strtol (stringptr, &stringptr, 0); + } + } else { + head = end1 = (int) strtol (stringptr, &stringptr, 0); + } + for (k = 1; k < segments; k++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + // Try load another non-empty line to continue + stringptr = readline(inputline, polyfile, inpolyfilename); + if (*stringptr == '\0') { + printf("File I/O Error: Missing %d endpoints for", segments - k); + printf(" polygon %d in facet %d in %s.\n", j, i, inpolyfilename); + exit(1); + } else { + end2 = (int) strtol (stringptr, &stringptr, 0); + } + } else { + end2 = (int) strtol (stringptr, &stringptr, 0); + } + if ((end1 < firstnumber) || (end1 >= firstnumber + inpoints)) { + if (!quiet) { + printf("Warning: Invalid endpoint %d of polygon %d", k, j); + printf(" in facet %d in %s.\n", i, inpolyfilename); + } + } else if ((end2 < firstnumber) || (end2 >= firstnumber + inpoints)) { + if (!quiet) { + printf("Warning: Invalid endpoint %d of polygon %d", k + 1, j); + printf(" in facet %d in %s.\n", i, inpolyfilename); + } + } else { + segendpoints = (point3d *) seglist[i - 1]->alloc(); + segendpoints[0] = getpoint(end1); + segendpoints[1] = getpoint(end2); + if (segendpoints[0] == segendpoints[1]) { + if (segments == 2) { + // A degenrate edge, it represent an isolate point in facet. + segendpoints[1] = (point3d) NULL; + } else { + printf("Warning: Polygon %d has two identical endpoints", j); + printf(" in facet %d in %s.\n", i, inpolyfilename); + } + } else { + insertsegment(segendpoints[0], segendpoints[1], boundmarkers[i-1]); + } + } + end1 = end2; + } + if (segments > 2) { + if (((end2 >= firstnumber) && (end2 < firstnumber + inpoints)) + && ((head >= firstnumber) && (head < firstnumber + inpoints))) { + segendpoints = (point3d *) seglist[i - 1]->alloc(); + segendpoints[0] = getpoint(end2); + segendpoints[1] = getpoint(head); + if (segendpoints[0] == segendpoints[1]) { + if (segments == 2) { + // A degenrate edge, it represent an isolate point in the facet. + segendpoints[1] = (point3d) NULL; + } else { + printf("Warning: Polygon %d has two identical endpoints", j); + printf(" in facet %d in %s.\n", i, inpolyfilename); + } + } else { + insertsegment(segendpoints[0], segendpoints[1], boundmarkers[i-1]); + } + } + } + } + + // Read the holes' coordinates. + if (inholes > 0) { + holelist[i - 1] = new list(sizeof(REAL) * 3); + for (j = 1; j <= inholes; j++) { + holecoords = (REAL *) holelist[i - 1]->alloc(); + stringptr = readline(inputline, polyfile, inpolyfilename); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Hole %d in facet %d", j, i); + printf(" has no coordinates in %s.\n", inpolyfilename); + exit(1); + } else { + holecoords[0] = (REAL) strtod (stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Hole %d in facet %d", j, i); + printf(" is missing its y-coordinate in %s.\n", inpolyfilename); + exit(1); + } else { + holecoords[1] = (REAL) strtod (stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Hole %d in facet %d", j, i); + printf(" is missing its z-coordinate in %s.\n", inpolyfilename); + exit(1); + } else { + holecoords[2] = (REAL) strtod (stringptr, &stringptr); + } + } + } + } // end inserting all segments loop. + } else if (smesh) { + // Read all input triangular facets and its boundary markers. + for (i = 1; i <= facets; i++) { + // Read three endpoints of this facet. + stringptr = readline(inputline, polyfile, insmeshfilename); + segments = (int) strtol (stringptr, &stringptr, 0); + if (segments <= 1) { + printf("File I/O Error: Wrong vertex number of facet %d", i); + printf(" in %s.\n", insmeshfilename); + if (segments == 1) { + printf(" If you want define a isolate point in facet, you can\n"); + printf(" define a degenrate segment(with two same endpoints)\n "); + printf(" in stead of.\n"); + } + exit(1); + } + seglist[i - 1] = new list(sizeof(point3d) * 2, segments); + // In smesh files, we obtain facet's boundary mark at last. + boundmarkers[i-1] = 0; + // Read and insert the segments. + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + // Try load another non-empty line to continue + stringptr = readline(inputline, polyfile, insmeshfilename); + if (*stringptr == '\0') { + printf("File I/O Error: No endpoints for facet %d", i); + printf(" in %s.\n", insmeshfilename); + exit(1); + } else { + head = end1 = (int) strtol (stringptr, &stringptr, 0); + } + } else { + head = end1 = (int) strtol (stringptr, &stringptr, 0); + } + for (k = 1; k < segments; k++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + // Try load another non-empty line to continue + stringptr = readline(inputline, polyfile, inpolyfilename); + if (*stringptr == '\0') { + printf("File I/O Error: Missing %d endpoints for", segments - k); + printf(" facet %d in %s.\n", i, insmeshfilename); + exit(1); + } else { + end2 = (int) strtol (stringptr, &stringptr, 0); + } + } else { + end2 = (int) strtol (stringptr, &stringptr, 0); + } + if ((end1 < firstnumber) || (end1 >= firstnumber + inpoints)) { + if (!quiet) { + printf("Warning: Invalid endpoint %d of facet %d", k, i); + printf(" in %s.\n", insmeshfilename); + } + } else if ((end2 < firstnumber) || (end2 >= firstnumber + inpoints)) { + if (!quiet) { + printf("Warning: Invalid endpoint %d of facet %d", k + 1, i); + printf(" in %s.\n", insmeshfilename); + } + } else { + segendpoints = (point3d *) seglist[i - 1]->alloc(); + segendpoints[0] = getpoint(end1); + segendpoints[1] = getpoint(end2); + if (segendpoints[0] == segendpoints[1]) { + if (segments == 2) { + // A degenrate edge, it represent an isolate point in facet. + segendpoints[1] = (point3d) NULL; + } else { + printf("Warning: Facet %d has two identical endpoints", i); + printf(" in %s.\n", insmeshfilename); + } + } else { + insertsegment(segendpoints[0], segendpoints[1], boundmarkers[i-1]); + } + } + end1 = end2; + } + if (segments > 2) { + if (((end2 >= firstnumber) && (end2 < firstnumber + inpoints)) + && ((head >= firstnumber) && (head < firstnumber + inpoints))) { + segendpoints = (point3d *) seglist[i - 1]->alloc(); + segendpoints[0] = getpoint(end2); + segendpoints[1] = getpoint(head); + if (segendpoints[0] == segendpoints[1]) { + if (segments == 2) { + // A degenrate edge, it represent an isolate point in the facet. + segendpoints[1] = (point3d) NULL; + } else { + printf("Warning: Facet %d has two identical endpoints", i); + printf(" in %s.\n", insmeshfilename); + } + } else { + insertsegment(segendpoints[0], segendpoints[1], boundmarkers[i-1]); + } + } + } + // Read facet's boundary marker at last. + if (facetmarkers == 1) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + boundmarkers[i - 1] = 0; // no boundary marker for this facet. + } else { + boundmarkers[i - 1] = (int) strtol (stringptr, &stringptr, 0); + } + } + } + } + + // At this point, we can sure there are no missing segments in the + // tetrahedralization(that is: every segment is existed in the domain + // and is represented by a contiguous linear sequence of edges of the + // tetrahedralization). + if (!quiet) { + printf("Inserting facets into Delaunay triangulation.\n"); + } + // Re-compute a mapping from points to tetrahedra. + makepointmap(); + // Set up a 2D mesh object. Switches are chosen to read a PSLG (p), + // numbers all items starting from zero (rather than one) (z), + // generates a list of triangle neighbors (n), no terminal output + // except errors (Q). suppresses use of exact arithmetic (X), + // suppresses output of segments (P), suppresses node output (N). + // surfmesh = new mesh2d("pznQXPN"); + surfmesh = new mesh2d("pznQPN"); + + for (i = 0; i < facets; i++) { + if (seglist[i]->len() > 2) { + insertfacet(seglist[i], holelist[i], boundmarkers[i], i + 1); + } + delete seglist[i]; + if (holelist[i]) { + delete holelist[i]; + } + } + delete [] boundmarkers; + delete [] seglist; + delete [] holelist; + } + + return facets; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Segment/Facet (Boundary) Constrained Routines. // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Carving out Holes and Concavities Routines. // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::infecthull() +{ + triface tetloop, tsymtet; + tetrahedron **deadtet; + face hullface; + point3d horg, hdest, hapex; + + if (verbose) { + printf(" Marking concavities (external tetrahedra) 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) || (isnonsolid(hullface))) { + // 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 (mark(hullface) == 0) { + setmark(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 tetrahedra to any // +// neighbors not protected by subfaces. Delete all infected // +// tetrahedra. // +// // +// This is the procedure that actually creates holes and concavities. // +// // +// This procedure operates in two phases. The first phase 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. // +// // +// The second phase actually eliminates the infected tetrahedra. It also // +// eliminates orphaned segments and points(not done now). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::plague() +{ + triface testtet; + triface neighbor; + triface spintet, swaptet, livetet; + tetrahedron **virusloop; + tetrahedron **deadtet; + face neighborshell; + face testseg, testface; + point3d testpoint; + point3d norg, ndest, napex; + point3d deadorg, deaddest, deadapex, deadoppo; + int killseg, killorg; + int edgecount, successbonded, hitbdry; + + if (verbose) { + printf(" Marking neighbors of marked tetrahedra.\n"); + } + // 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; + // A tetrahedron is marked as infected by messing with one of its + // vertices(opposite), setting it to an illegal value. Hence, we + // have to temporarily uninfect this tetrahedron so that we can get + // the proper vertex of this tetrahedron. + uninfect(testtet); + if (verbose > 2) { + // Assign the tetrahedron an orientation for convenience in + // checking its points. + testtet.loc = 0; + deadorg = org(testtet); + deaddest = dest(testtet); + deadapex = apex(testtet); + deadoppo = oppo(testtet); + printf(" Checking (%d, %d, %d, %d)\n", pointmark(deadorg), + pointmark(deaddest), pointmark(deadapex), pointmark(deadoppo)); + } + // 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, neighborshell); + // Check if the neighbor is nonexistent or already infected. + if ((neighbor.tet == dummytet) || infected(neighbor)) { + if (neighborshell.sh != dummysh) { + // There is a subface separating the tetrahedron from its neighbor, + // but both tetrahedra are dying, so the subface dies too. + // Before deallocte the subface, check each of the three sides + // of the subface for preserving subsegments if there need. This + // is done by spinning around each edge, checking if it is still + // connected to at least one live tetrahedron. + neighborshell.shver = 0; + // For keep the same enext() direction. + findversion(&testtet, &neighborshell, 0); + for (edgecount = 0; edgecount < 3; edgecount++) { + sspivot(neighborshell, testseg); + if (testseg.sh != dummysh) { + spivot(testseg, testface); + if (testface.sh == neighborshell.sh) { + killseg = 1; + spintet = testtet; + successbonded = hitbdry = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == apex(testtet)) { + break; // Rewind, can leave now. + } + if (spintet.tet == testtet.tet) { + continue; + } + if (!infected(spintet)) { + // A live tetrahedron. The segment survives. + killseg = 0; + tspivot(spintet, testface); + if (testface.sh == dummysh) { + livetet = spintet; + adjustedgering(livetet, CCW); + fnextself(livetet); + tspivot(livetet, testface); + } + if (testface.sh != dummysh) { + findversion(&testface, &spintet); + ssbond(testface, testseg); + successbonded = 1; + break; + } + } + } else { + hitbdry ++; + if(hitbdry >= 2) { + break; + } else { + esym(testtet, spintet); + } + } + } + if (killseg) { + if (verbose > 1) { + printf(" Deleting subsegment (%d, %d)\n", + pointmark(sorg(testseg)), + pointmark(sdest(testseg))); + } + shellfacedealloc(&subsegs, testseg.sh); + } else { + if (!successbonded) { + // Badly, We must insert a nonsolid subface for holding + // this subsegment; otherwise, it will missing after + // removing all infected tetrahedra. + insertsubface(&livetet, NONSOLIDFLAG, 1); + tspivot(livetet, testface); + findversion(&testface, &testtet); + ssbond(testface, testseg); + } + } + } + } + senextself(neighborshell); + enextself(testtet); + } + shellfacedealloc(&subfaces, neighborshell.sh); + if (neighbor.tet != dummytet) { + // Make sure the subface doesn't get deallocated again later + // when the infected neighbor is visited. + tsdissolve(neighbor); + } + } + } else { // The neighbor exists and is not infected. + if ((neighborshell.sh == dummysh) || (isnonsolid(neighborshell))) { + // There is no subface protecting the neighbor, so + // the neighbor becomes infected. + if (verbose > 2) { + deadorg = org(neighbor); + deaddest = dest(neighbor); + deadapex = apex(neighbor); + deadoppo = oppo(neighbor); + printf(" Marking (%d, %d, %d, %d)\n", pointmark(deadorg), + pointmark(deaddest), pointmark(deadapex), pointmark(deadoppo)); + } + 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(neighborshell); + // The subface becomes a boundary. Set markers accordingly. + if (mark(neighborshell) == 0) { + setmark(neighborshell, 1); + } + norg = org(neighbor); + ndest = dest(neighbor); + napex = apex(neighbor); + if (pointmark(norg) == 0) { + setpointmark(norg, 1); + } + if (pointmark(ndest) == 0) { + setpointmark(ndest, 1); + } + if (pointmark(napex) == 0) { + setpointmark(napex, 1); + } + } + } + } + // Remark the tetrahedron as infected, so it doesn't get added to the + // virus pool again. + infect(testtet); + virusloop = (tetrahedron **) viri.traverse(); + } + + if (verbose) { + printf(" Deleting marked tetrahedra.\n"); + } + viri.traversalinit(); + virusloop = (tetrahedron **) viri.traverse(); + while (virusloop != (tetrahedron **) NULL) { + testtet.tet = *virusloop; + + // Check each of the four corners of the tetrahedron for elimination. + // To be finished... + + // 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++; + } + } + // Return the dead tetrahedron to the pool of tetrahedra. + tetrahedrondealloc(testtet.tet); + virusloop = (tetrahedron **) viri.traverse(); + } + // Empty the virus pool. + viri.restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 // +// attribute and/or an volume constraint through a (segment-bounded) region. // +// The tetrahedra are marked to ensure that each tetrahedra is added to the // +// virus pool only once, so the procedure will terminate. // +// // +// The second phase uninfects all infected tetrahedra, returning them to // +// normal. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::regionplague(REAL attribute, REAL volume) +{ + triface testtet; + triface neighbor; + tetrahedron **virusloop; + tetrahedron **regiontet; + face neighborshell; + point3d regionorg, regiondest, regionapex, regionoppo; + + if (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. + viri.traversalinit(); + virusloop = (tetrahedron **) viri.traverse(); + while (virusloop != (tetrahedron **) NULL) { + testtet.tet = *virusloop; + // A tetrahedron is marked as infected by messing with one of its + // vertices(opposite), setting it to an illegal value. Hence, we + // have to temporarily uninfect this tetrahedron so that we can get + // the proper vertex of this tetrahedron. + uninfect(testtet); + if (regionattrib) { + // Set an attribute. + setelemattribute(testtet.tet, eextras, attribute); + } + if (varvolume) { + // Set an volume constraint. + setvolumebound(testtet.tet, volume); + } + if (verbose > 2) { + // Assign the tetrahedron an orientation for convenience in + // checking its points. + testtet.loc = 0; + regionorg = org(testtet); + regiondest = dest(testtet); + regionapex = apex(testtet); + regionoppo = oppo(testtet); + printf(" Checking (%d, %d, %d, %d)\n", pointmark(regionorg), + pointmark(regiondest), pointmark(regionapex), pointmark(regionoppo)); + } + // 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, neighborshell); + // 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) + && ((neighborshell.sh == dummysh) || (isnonsolid(neighborshell)))) { + if (verbose > 2) { + regionorg = org(neighbor); + regiondest = dest(neighbor); + regionapex = apex(neighbor); + regionoppo = oppo(neighbor); + printf(" Marking (%d, %d, %d, %d)\n", pointmark(regionorg), + pointmark(regiondest), pointmark(regionapex), pointmark(regionoppo)); + } + // Infect the neighbor. + infect(neighbor); + // Ensure that the neighbor's neighbors will be infected. + regiontet = (tetrahedron **) viri.alloc(); + *regiontet = neighbor.tet; + } + } + // Remark the tetrahedron as infected, so it doesn't get added to the + // virus pool again. + infect(testtet); + virusloop = (tetrahedron **) viri.traverse(); + } + + // Uninfect all tetrahedra. + if (verbose > 1) { + printf(" Unmarking marked tetrahedra.\n"); + } + viri.traversalinit(); + virusloop = (tetrahedron **) viri.traverse(); + while (virusloop != (tetrahedron **) NULL) { + testtet.tet = *virusloop; + uninfect(testtet); + virusloop = (tetrahedron **) viri.traverse(); + } + // Empty the virus pool. + viri.restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 mesh3d::carveholes(REAL *holelist, int holes, REAL *regionlist, + int regions) +{ + triface searchtet; + tetrahedron *tptr; + triface *holetets; + triface *regiontets; + tetrahedron **holetet; + tetrahedron **regiontet; + enum locateresult intersect; + int i; + + if (!(quiet || (noholes && convex))) { + printf("Removing unwanted tetrahedra.\n"); + if (verbose && (holes > 0)) { + printf(" Marking holes for elimination.\n"); + } + } + + if ((holes > 0) && !noholes) { + // Allocate storage for the tetrahedra in which hole points fall. + holetets = (triface *) new triface[holes]; + } + + if (regions > 0) { + // Allocate storage for the tetrahedra in which region points fall. + regiontets = (triface *) new triface[regions]; + } + + // Now, we have to find all the holes and regions BEFORE we infect hull + // and carve the holes, because locate() won't work when there exist + // infect tetrahedra and the tetrahedronlization is no longer convex. + + if ((holes > 0) && !noholes) { + // Infect each tetrahedron 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 tetrahedron on the outer boundary. + searchtet.tet = dummytet; + searchtet.loc = 0; + symself(searchtet); + // Ensure that the hole is above the boundary face; otherwise, + // locate() will falsely report that the hole falls within the + // starting tetrahedron. + adjustedgering(searchtet, CCW); + if (isaboveplane(&searchtet, &holelist[i])) { + // Find a tetrahedron that contains the hole. + intersect = locate(&holelist[i], &searchtet); + if ((intersect != OUTSIDE) && (!infected(searchtet))) { + // Record the tetrahedron for processing carve hole. + holetets[i / 3] = searchtet; + } + } + } + } + } + + if (regions > 0) { + // Find the starting tetrahedron for each region. + for (i = 0; i < regions; i++) { + regiontets[i].tet = dummytet; + // Ignore region points that aren't within the bounds of the mesh. + if ((regionlist[5 * i] >= xmin) && (regionlist[5 * i] <= xmax) && + (regionlist[5 * i + 1] >= ymin) && (regionlist[5 * i + 1] <= ymax) && + (regionlist[5 * i + 2] >= zmin) && (regionlist[5 * i + 2] <= zmax)) { + // Start searching from some tetrahedron on the outer boundary. + searchtet.tet = dummytet; + searchtet.loc = 0; + symself(searchtet); + // Ensure that the region point above the boundary face; otherwise, + // locate() will falsely report that the region point falls within + // the starting tetrahedron. + adjustedgering(searchtet, CCW); + if (isaboveplane(&searchtet, ®ionlist[5 * i])) { + // Find a tetrahedron that contains the region point. + intersect = locate(®ionlist[5 * i], &searchtet); + if ((intersect != OUTSIDE) && (!infected(searchtet))) { + // Record the tetrahedron for processing after the + // holes have been carved. + regiontets[i] = searchtet; + } + } + } + } + } + + if (((holes > 0) && !noholes) || !convex || (regions > 0)) { + // Initialize a pool of viri to be used for holes, concavities, + // regional attributes, and/or regional volume constraints. + viri.init(sizeof(tetrahedron *), VIRUSPERBLOCK, POINTER, 0); + } + + if (!convex) { + // Mark as infected any unprotected tetrahedra on the boundary. + // This is one way by which concavities are created. + infecthull(); + } + + if ((holes > 0) && !noholes) { + // Infect the hole tetrahedron. This is done by marking the + // tetrahedron as infect and including the tetrahedron in + // the virus pool. + for (i = 0; i < holes; i++) { + infect(holetets[i]); + holetet = (tetrahedron **) viri.alloc(); + *holetet = holetets[i].tet; + } + } + + if (viri.items > 0) { + // Carve the holes and concavities. + plague(); + } + // The virus pool should be empty now. + + if (regions > 0) { + if (!quiet) { + if (regionattrib) { + if (varvolume) { + printf("Spreading regional attributes and volume constraints.\n"); + } else { + printf("Spreading regional attributes.\n"); + } + } else { + printf("Spreading regional volume constraints.\n"); + } + } + if (regionattrib && !refine) { + // Assign every tetrahedron a regional attribute of zero. + tetrahedrons.traversalinit(); + tptr = tetrahedrontraverse(); + while (tptr != (tetrahedron *) NULL) { + setelemattribute(tptr, eextras, 0.0); + tptr = tetrahedrontraverse(); + } + } + for (i = 0; i < regions; 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 **) viri.alloc(); + *regiontet = regiontets[i].tet; + // Apply one region's attribute and/or volume constraint. + regionplague(regionlist[5 * i + 3], regionlist[5 * i + 4]); + // The virus pool should be empty now. + } + } + } + if (regionattrib && !refine) { + // Note the fact that each tetrahedron has an additional attribute. + eextras++; + } + } + + // Free up memory. + if (((holes > 0) && !noholes) || !convex || (regions > 0)) { + viri.deinit(); + } + if ((holes > 0) && !noholes) { + delete [] holetets; + } + if (regions > 0) { + delete [] regiontets; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Carving out Holes and Concavities Routines. // +/////////////////////////////////////////////////////////////////////////////// diff --git a/Tetgen/defines.h b/Tetgen/defines.h new file mode 100644 index 0000000000..289dec79ae --- /dev/null +++ b/Tetgen/defines.h @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// defines.h Header file for all the Tetgen source files needed. // +// Defines switches (Optional) used at compilation time. // +// Includes the most general used head files for all C/C++ // +// program code needed. Defines marcos used throughout all // +// the source files. // +// // +// Tetgen Version 1.0 beta // +// July, 2001 // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef definesH +#define definesH + +// If yours is not a Unix system, define the NO_TIMER compiler switch to +// remove the Unix-specific timing code. + +// #define NO_TIMER + +// Define to disable assertions. Generally the assert() operater only be +// used in developping stage, to catch the casual mistakes in programming. + +// #define NDEBUG + +// To insert lots of self-checks for internal errors, define the SELF_CHECK +// symbol. This will slow down the program significantly. It is best to +// define the symbol using the -DSELF_CHECK compiler switch, but you could +// write "#define SELF_CHECK" below. If you are modifying this code, I +// recommend you turn self-checks on. + +// #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. +// +// Double precision uses more memory, but improves the resolution of the +// meshes you can generate with Triangle. It also reduces the likelihood +// of a floating exception due to overflow. Finally, it is much faster +// than single precision on 64-bit architectures like the DEC Alpha. I +// recommend double precision unless you want to generate a mesh for which +// you do not have enough memory. + +// #define SINGLE + +#ifdef SINGLE + #define REAL float +#else + #define REAL double +#endif // not defined SINGLE + +// numbers that speaks for theirself. + +#define PI 3.141592653589793238462643383279502884197169399375105820974944592308 + +#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732 + +#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333 + +// For efficiency, a variety of data structures are allocated in bulk. The +// following constants determine how many of each structure is allocated +// at once. + +#define TRIPERBLOCK 4092 // Number of triangles allocated at once. +#define SHELLEPERBLOCK 508 // Number of shell edges allocated at once. +#define POINTPERBLOCK 4092 // Number of points allocated at once. +#define VIRUSPERBLOCK 1020 // Number of virus triangles allocated at once. +#define TETPERBLOCK 8188 // Number of tetrahedrons allocated at once. +#define SUBFACEPERBLOCK 1020 // Number of subfaces allocated at once. +#define SUBSEGPERBLOCK 1020 // Number of subsegments allocated at once. +#define POINTPERBLOCK 4092 // Number of points allocated at once. +// Number of encroached segments allocated at once. +#define BADSEGMENTPERBLOCK 252 +// Number of encroached segments allocated at once. +#define BADSHELLFACEPERBLOCK 508 +// Number of skinny triangles allocated at once. +#define BADTRIPERBLOCK 4092 +// Number of skinny tetrahedras allocated at once. +#define BADTETPERBLOCK 8188 + +// The point marker DEADPOINT is an arbitrary number chosen large enough to +// (hopefully) not conflict with user boundary markers. Make sure that it +// is small enough to fit into your machine's integer size. + +#define DEADPOINT -1073741824 + +// The nonsolid flag NONSOLIDFLAG is an arbitrary number chosen large enou- +// gh to (hopefully)not conflict with user boundary markers.Make sure that +// it is small enough to fit into your machine's integer size. + +#define NONSOLIDFLAG -1342177279 + +// Maximum number of characters in a file name (including the null). + +#ifndef FILENAMESIZE + #define FILENAMESIZE 512 +#endif + +// Maximum number of characters in a line read from a file ( including the +// null). + +#ifndef INPUTLINESIZE + #define INPUTLINESIZE 512 +#endif + +// Constant for algorithms based on random sampling. This constant +// have been chosen empirically to optimize its respective algorithms. + +// Used for the point location scheme of Mucke, Saias, and Zhu, to decide +// how large a random sample of triangles to inspect. +#define SAMPLEFACTOR 11 + +// Here is the most general used head files for all C/C++ program code +// needed. + +#include <stdio.h> // standard IO: FILE, NULL (*), EOF, ... +#include <stdlib.h> // standard lib: abort(), system(), getenv(), ... +#include <string.h> // declarations for string manipulation functions. +#include <math.h> // math lib: sin(), sqrt(), pow(), ... +#include <assert.h> +#ifndef NO_TIMER + #include <sys/time.h> +#else + #include <time.h> // definitions/declarations for time routines. +#endif // defined NO_TIMER +#ifdef INTEL_MSC_TURBOC + #include <float.h> // some compilers will not need float.h. +#endif // defined INTEL_MSC_TURBOC + +// If you will compile and run this code on Intel CPUs, Maybe there is some +// choices must be made by user to correctly execute Jonathan Schewck's +// code for arbitrary floating-point precision arithmetic and robust geom- +// etric predicates. For more detail please see: +// +// http://www.cs.cmu.edu/~quake/robust.pc.html. + +// If you use Microsoft C/C++ or TOURB C/C++ or BORLAND C/C++, define the +// symbol INTEL_MSC_TURBOC by using -DINTEL_MSC_TURBOC compiler switch +// or by writing #define INTEL_MSC_TURBOC as follows. + +// #define INTEL_MSC_TURBOC + +// If you use gcc running under Linux, define the symbol INTEL_GCC_LINUX. Be +// sure to undefine the symbol INTEL_MSC_TURBOC. + +// #define INTEL_GCC_LINUX + +// If you use gcc but not running under linux, use the following symbol. Be +// sure to undefine the symbol INTEL_MSC_TURBOC and INTEL_GCC_LINUX. + +// #define INTEL_GCC + +// Routines for Arbitrary Precision Floating-point Arithmetic and Fast +// Robust Geometric Predicates. + +void exactinit(); +REAL orient2d(REAL* pa, REAL* pb, REAL* pc); +REAL incircle(REAL* pa, REAL* pb, REAL* pc, REAL* pd); +REAL orient3d(REAL* pa, REAL* pb, REAL* pc, REAL* pd); +REAL insphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe); + +#endif // #ifndef definesH diff --git a/Tetgen/linklist.cpp b/Tetgen/linklist.cpp new file mode 100644 index 0000000000..24d9aa23cc --- /dev/null +++ b/Tetgen/linklist.cpp @@ -0,0 +1,1149 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// linklist.cpp Implement link, list, queue, stack, memorypool data types // +// which declared in linklist.h. // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// Please see linklist.h for a detail description. // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> // Defined function printf(). +#include <stdlib.h> // Defined function qsort(). +#include <string.h> // Defined function strncmp(), strcmp(). +#include "linklist.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// Predefined linar order functions for most primitive data types. // // +// // +// Return -1 if x < y; Return +1 if x > y; Return Zero if x == y. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int compare2ints(const void* pi1, const void* pi2) +{ + if(*(int*)pi1 < *(int*)pi2) { + return -1; + } else if(*(int*)pi1 > *(int*)pi2) { + return 1; + } else { + return 0; + } +} + +int compare2unsignedlongs(const void* pl1, const void* pl2) +{ + if(*(unsigned long*)pl1 < *(unsigned long*)pl2) { + return -1; + } else if(*(unsigned long*)pl1 > *(unsigned long*)pl2) { + return 1; + } else { + return 0; + } +} + +int compare2chars(const void* pc1, const void* pc2) +{ + if(*(char*)pc1 < *(char*)pc2) { + return -1; + } else if(*(char*)pc1 > *(char*)pc2) { + return 1; + } else { + return 0; + } +} + +int compare2strings(const void* pstr1, const void* pstr2) +{ + return strcmp(*(char**)pstr1, *(char**)pstr2); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// setpredefineddatatype() // +// // +/////////////////////////////////////////////////////////////////////////////// + +void setpredefineddatatype(char* strdatatype, int *itembytes, compfunc *comp) +{ + if (strncmp(strdatatype, "int", 3) == 0) { + *itembytes = sizeof(int); + *comp = compare2ints; + } else if (strncmp(strdatatype, "unsigned long", 13) == 0) { + *itembytes = sizeof(unsigned long); + *comp = compare2unsignedlongs; + } else if (strncmp(strdatatype, "char", 4) == 0) { + *itembytes = sizeof(char); + *comp = compare2chars; + } else if (strncmp(strdatatype, "string", 6) == 0) { + *itembytes = sizeof(char*); + *comp = compare2strings; + } else if (strncmp(strdatatype, "point", 5) == 0) { + // This type is for my tetrahedra program used, actually type is REAL*. + *itembytes = sizeof(unsigned long); + *comp = compare2unsignedlongs; + } else { + printf("Sorry, this data type %s is unknown by list now.\n", strdatatype); + assert(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// class list implementation // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +list::list(char* strdatatype, int _maxitems, int _expandsize) +{ + int bytesofint = sizeof(int); + + assert(strdatatype && (_maxitems > 0) && (_expandsize > 0)); + + comp = (compfunc) NULL; + setpredefineddatatype(strdatatype, &itembytes, &comp); + assert(comp && (itembytes > 0)); + + if(itembytes % bytesofint) { + itemints = itembytes / bytesofint + 1; + } else { + itemints = itembytes / bytesofint; + } + + maxitems = _maxitems; + expandsize = _expandsize; + base = new void*[maxitems * itemints]; + items = 0; +} + +list::list(int _itembytes, int _maxitems, int _expandsize) +{ + assert((_itembytes > 0) && (_maxitems > 0) && (_expandsize > 0)); + + int bytesofint = sizeof(int); + itembytes = _itembytes; + if(itembytes % bytesofint) { + itemints = itembytes / bytesofint + 1; + } else { + itemints = itembytes / bytesofint; + } + comp = NULL; + + maxitems = _maxitems; + expandsize = _expandsize; + base = new void*[maxitems * itemints]; + items = 0; +} + +list::~list() +{ + delete [] base; +} + +list::list(list& src) +{ + assert(src.items > 0); + itembytes = src.itembytes; + itemints = src.itemints; + maxitems = src.maxitems; + expandsize = src.expandsize; + items = src.items; + base = new void*[maxitems * itemints]; + memcpy(base, src.base, items * itemints * sizeof(int)); +} + +void* list::operator[](int index) +{ + assert(index >= 0 && index < items); + return (void*) (base + index * itemints); +} + +void* list::getitem(int index) +{ + assert(index >= 0 && index < items); + return (void*) (base + index * itemints); +} + +void* list::alloc() +{ + if (items == maxitems) { + // No space available, need re-allocate. + void **newbase = new void*[(maxitems + expandsize) * itemints]; + memcpy(newbase, base, maxitems * itemints * sizeof(int)); + delete [] base; + base = newbase; + maxitems += expandsize; + } + items++; + return (base + (items - 1) * itemints); +} + +void list::append(void* newitem) +{ + if (items == maxitems) { + // No space available, need re-allocate. + void **newbase = new void*[(maxitems + expandsize) * itemints]; + memcpy(newbase, base, maxitems * itemints * sizeof(int)); + delete [] base; + base = newbase; + maxitems += expandsize; + } + memcpy((void*) (base + items * itemints), newitem, itembytes); + items++; +} + +// Insert an item before index 'befireindex'. 'beforeindex' should be a +// value from 1 to listlength. If 'beforeindex' == listlength, insert +// operation is equal to append operation. + +void list::insert(int beforeindex, void* insertitem) +{ + assert(beforeindex > 0 && beforeindex <= items); + if (beforeindex == items) { + append(insertitem); + return; + } + if (items == maxitems) { + // No space available, need re-allocate. + void **newbase = new void*[(maxitems + expandsize) * itemints]; + memcpy(newbase, base, maxitems * itemints * sizeof(int)); + delete [] base; + base = newbase; + maxitems += expandsize; + } + // Do block move. + memmove((void*) (base + (beforeindex + 1) * itemints), // dest + (void*) (base + beforeindex * itemints), // src + (items - beforeindex) * itemints * sizeof(int)); // size in bytes + // Insert the insertitem. + memcpy((void*) (base + beforeindex * itemints), insertitem, itembytes); + items++; +} + +// Delete an item from the list. 'deleteindex' should be a value from +// 0 to listlength-1. + +void list::del(int deleteindex) +{ + assert(deleteindex >= 0 && deleteindex < items); + if (deleteindex != (items - 1)) { + // Do block move. + memmove((void*) (base + deleteindex * itemints), // dest + (void*) (base + (deleteindex + 1) * itemints), // src + (items - deleteindex - 1) * itemints * sizeof(int)); + } + items--; +} + +// To remove a specific item from the list when its index is unknown. The +// value returned is the index of the item in the Items array before it +// was removed. After an item is removed, all the items that follow it +// are moved up in index position and the Count(items) is reduced by one. +// If the Items array contains more than one copy of the pointer, only the +// first copy is deleted. + +int list::remove(void *removeitem) +{ + int index = hasitem(removeitem); + if (index != -1) { + del(index); + } + return index; +} + +// Return 0 to listlength - 1 if 'checkitem' existed in list, +// Return -1 if 'checkitem' not exist. + +int list::hasitem(void* checkitem) +{ + int i; + + for (i = 0; i < items; i ++) { + if (comp) { + if ((*comp)((void*)(base + i * itemints), checkitem) == 0) return i; + } else { + if (compare2ints((void*)(base + i * itemints), checkitem) == 0) return i; + } + } + return -1; +} + +// Return 0 to listlength - 1 if 'finditem' existed in list, +// Return -1 if 'finditem' not exist. + +int list::indexof(void* finditem) +{ + return hasitem(finditem); +} + +// Performs a QuickSort on the list based on the comparison function. + +void list::sort() +{ + qsort((void*)base, (size_t)items, (size_t)(itemints * sizeof(int)), comp); +} + +void list::simplify() +{ + compfunc compare; + void *pathitem, *checkitem; + int i, j; + + if(comp) { + compare = (compfunc) comp; + } else { + compare = (compfunc) compare2ints; // Use default compare function. + } + + for (i = 0; i < items; i++) { + pathitem = (*this)[i]; + for (j = i + 1; j < items; j++) { + checkitem = (*this)[j]; + if ((*compare)(pathitem, checkitem) == 0) { + del(j); + j--; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// class list implementation // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// class link implementation // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Constructor, Destructor // +/////////////////////////////////////////////////////////////////////////////// + +link::link(char* strdatatype, int itemcount) +{ + int _itembytes; + + assert(strdatatype && (itemcount > 0)); + + _itembytes = 0; + comp = (compfunc) NULL; + setpredefineddatatype(strdatatype, &_itembytes, &comp); + assert(comp && (_itembytes > 0)); + + poolinit(_itembytes, itemcount); + init(); +} + +link::link(int _itembytes, int itemcount) +{ + assert((_itembytes > 0) && (itemcount > 0)); + + poolinit(_itembytes, itemcount); + init(); + comp = NULL; +} + +link::~link() +{ + while (firstblock != (void**)NULL) { + nowblock = (void **) *firstblock; + delete [] firstblock; + firstblock = nowblock; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Member Functions // +/////////////////////////////////////////////////////////////////////////////// + +void link::poolinit(int bytes, int itemcount) +{ + itembytes = bytes; + itemsperblock = itemcount; + + int bytesofint = sizeof(int); + if(itembytes % bytesofint) + itemints = itembytes / bytesofint + 1; + else + itemints = itembytes / bytesofint; + // In double link, each items need 2 pointer spaces. So actually one + // item's total space is: itemints + 2 (ints). + firstblock = new void*[1 + (itemints + 2) * itemsperblock]; + *firstblock = (void*) NULL; + maxitems = itemsperblock; + poolrestart(); +} + +void link::poolrestart() +{ + // Set the currently active block. + nowblock = firstblock; + // Find the first item in the pool. Increment by the size of (void *). + nextitem = (void*) (nowblock + 1); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + // The stack of deallocated items is empty. + deaditemstack = (void*) NULL; +} + +void* link::alloc() +{ + void *newitem; + void **newblock; + + // 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 = new void*[1 + (itemints + 2) * itemsperblock]; + *nowblock = (void *) newblock; + // The next block pointer is NULL. + *newblock = (void *) NULL; + maxitems += itemsperblock; + } + // Move to the new block. + nowblock = (void **) *nowblock; + // Find the first item in the block. + // Increment by the size of (VOID *). + nextitem = (void *)(nowblock + 1); + // 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. + nextitem = (void *) ((void **) nextitem + itemints + 2); + unallocateditems--; + } + + return newitem; +} + +void link::dealloc(void* dyingitem) +{ + // Push freshly killed item onto stack. + *((void **) dyingitem) = deaditemstack; + deaditemstack = dyingitem; +} + +/////////////////////////////////////////////////////////////////////////////// +// Member Functions // +/////////////////////////////////////////////////////////////////////////////// + +void link::init() +{ + head = (void**) alloc(); + tail = (void**) alloc(); + *head = (void*) tail; + *(head + 1) = NULL; + *tail = NULL; + *(tail + 1) = (void*) head; + nextlinkitem = *head; + curpos = 1; + items = 0; +} + +int link::move(int i) +{ // i > 0 move forward, i < 0 move backward. + void** nownode = (void**)nextlinkitem; + if(i > 0) { + int j = 0; + while((j < i) && *nownode) { + nownode = (void**)*nownode; + j++; + } + if((j > i) || !(*nownode)) return 0; + nextlinkitem = (void*)nownode; + curpos += i; + } else if(i < 0) { + int j = 0; + i = -i; + while((j < i) && *(nownode+1)) { + nownode = (void**)*(nownode+1); + j++; + } + if((j > i) || !(*(nownode+1))) return 0; + nextlinkitem = (void*)nownode; + curpos -= i; + } + return 1; +} + +int link::locate(int i) +{ + if((i == 0) || (i > items)) return 0; + + int headdist = i - 1, taildist = items - i, curdist = i - curpos; + int abscurdist = curdist >= 0 ? curdist : -curdist; + + int mindist; + if(headdist > taildist) { // 1000 + if(taildist > abscurdist) { + mindist = curdist; + } else { + // taildist <= abs(curdist) + mindist = -taildist; + goend(); + } + } else { // 1000 else + // headdist <= taildist + if(headdist > abscurdist) { + mindist = curdist; + } else { + // headdist <= abs(curdist) + mindist = headdist; + rewind(); + } + } // 1000 + + return move(mindist); +} + +void link::add(void* elem) +{ + void **newnode = tail; + memcpy((void*)(newnode + 2), elem, itembytes); + tail = (void**) alloc(); + // To do init jobs after 'new' operater + for(int i = 0; i < itemints + 2; i ++) { + *((int *) (tail + i)) = 0; + } + *tail = NULL; + *newnode = (void*) tail; + *(tail + 1) = (void*) newnode; + items++; +} + +void link::insert(int index, void* elem) +{ + if(!locate(index)) assert(0); + void **nownode = (void**) nextlinkitem; + + // Insert a node before 'nownode'. + void **newnode = (void**) alloc(); + // To do init jobs after 'new' operater + for(int i = 0; i < itemints + 2; i ++) { + *((int *) (newnode + i)) = 0; + } + memcpy((void*)(newnode + 2), elem, itembytes); + + *(void**)(*(nownode + 1)) = (void*)newnode; + *newnode = (void*) nownode; + *(newnode + 1) = *(nownode+1); + *(nownode + 1) = (void*)newnode; + items++; + + nextlinkitem = (void*) newnode; +} + +void link::del(int index) +{ + if(!locate(index)) assert(0); + void **deadnode = (void**)nextlinkitem; + + // now delete the nownode + void **nextnode = (void**)*deadnode; + void **prevnode = (void**)*(deadnode + 1); + *prevnode = (void*)nextnode; + *(nextnode + 1) = (void*)prevnode; + + dealloc((void*) deadnode); + items--; + + nextlinkitem = (void*) nextnode; +} + +void* link::getitem() +{ + if (nextlinkitem == (void*)tail) return NULL; + void **nownode = (void**)nextlinkitem; + nextlinkitem = *nownode; + curpos += 1; + return (void*)(nownode + 2); +} + +void* link::getnitem(int n) +{ + if(!locate(n)) return NULL; + return (void*)((void**)nextlinkitem + 2); +} + +void link::setnitem(int n, void* elem) +{ + assert(locate(n)); + void **nownode = (void**) nextlinkitem; + // now set the nownode's data field with the new data + memcpy((void*)(nownode + 2), elem, itembytes); +} + +int link::hasitem(void* testitem) +{ + void *pathitem; + int count; + + rewind(); + pathitem = getitem(); + count = 0; + while (pathitem) { + count ++; + if (comp) { + if ((*comp)(pathitem, testitem) == 0) return count; + } else { + if (compare2ints(pathitem, testitem) == 0) return count; + } + pathitem = getitem(); + } + return -1; +} + +void link::sort() +{ + compfunc compare; + if(comp) { + compare = (compfunc) comp; + } else { + compare = (compfunc) compare2ints; // Use default compare function. + } + + unsigned char *table; + table = new unsigned char[items * itembytes]; + rewind(); + void *pathitem = getitem(); + int count = 0; + while(pathitem) { + memcpy(&table[count * itembytes], pathitem, itembytes); + pathitem = getitem(); + count ++; + } + if(count != items) assert(0); + + qsort (table, (size_t) items, (size_t) itembytes, compare); + + clear(); + for(int i = 0; i < count; i ++) { + add(&table[i * itembytes]); + } + delete [] table; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// class link implementation // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// class queue implementation // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +int queue::get(void* retitem) +{ + void* tmpitem = link::getnitem(1); + if(tmpitem == NULL) return 0; + memcpy(retitem, tmpitem, itembytes); + link::del(1); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// class queue implementation // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// class stack implementation // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Constructor, Destructor // +/////////////////////////////////////////////////////////////////////////////// + +stack::stack(char* strdatatype, int _itemsperblock) +{ + int _itembytes; + + assert(strdatatype && _itemsperblock > 0); + + _itembytes = 0; + comp = (compfunc) NULL; + setpredefineddatatype(strdatatype, &_itembytes, &comp); + assert(comp && (_itembytes > 0)); + + init(_itembytes, _itemsperblock); +} + +stack::stack(int _itembytes, int _itemsperblock) +{ + assert(_itembytes > 0 && _itemsperblock > 0); + init(_itembytes, _itemsperblock); + comp = NULL; +} + +stack::~stack() +{ + while (firstblock != (void**)NULL) { + nowblock = (void **) *firstblock; + delete [] firstblock; + firstblock = nowblock; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Member Functions // +/////////////////////////////////////////////////////////////////////////////// + +void stack::init(int bytes, int _itemsperblock) +{ + int bytesofint = sizeof(int); + itembytes = bytes; + if(itembytes % bytesofint) + itemints = itembytes / bytesofint + 1; + else + itemints = itembytes / bytesofint; + itemsperblock = _itemsperblock; + firstblock = new void*[2 + itemints*itemsperblock]; + *firstblock = (void*) NULL; + *(firstblock + 1) = (void*) NULL; + restart(); +} + +void stack::restart() +{ + // clear items count + items = 0; + // find the maxitem's count we can save now. + maxitems = itemsperblock; + nowblock = firstblock; + while(*nowblock != (void*) NULL) { + maxitems += itemsperblock; + nowblock = (void**) *nowblock; + } + // Set the currently active block. + nowblock = firstblock; + // Find the first item in the pool. Increment by the size of (void *). + top = (void*)(nowblock + 2); + // There are lots of unused items left in this block. + unuseditems = itemsperblock; +} + +void stack::push(void* newitem) +{ + if( unuseditems == 0 ) { + // nowblock is out of space + if( items < maxitems ) { + // next block is available, just move to it + nowblock = (void**)*nowblock; + } else { + // we need allocte an new block for extend allocating space + void **newblock = new void*[2 + itemints*itemsperblock]; + // set pointer to newblock and point back + *nowblock = (void*) newblock; + *newblock = (void*) NULL; + *(newblock + 1) = (void*) nowblock; + // move to newblock + nowblock = newblock; + // don't forget to extend maxitems + maxitems += itemsperblock; + } + top = (void*)(nowblock + 2); + // There are lots of unused items left in this block. + unuseditems = itemsperblock; + } + // save newitem + memcpy(top, newitem, itembytes); + items ++; + unuseditems --; + // move top to next available item + if(unuseditems) top = (void *) ((void **) top + itemints); +} + +void* stack::topitem() +{ + if(items == 0) return NULL; + void* retitem; + if (unuseditems == 0) { + // this time need attention, because last Push() operator didn't move + // top to next block, so top is still point to the last pushed item + retitem = top; + } else if (unuseditems == itemsperblock) { + // this time need move back a block + void** prevblock = (void**)*(nowblock + 1); + // top is point to the last item of the block + retitem = (void*)(prevblock + 2 + itemints * (itemsperblock - 1)); + } else { + // normal case in a block + retitem = (void*) ((void**) top - itemints); + } + return retitem; +} + +int stack::pop(void* retitem) +{ + if(items == 0) return 0; + if(unuseditems == 0) { + // this time need attention, because last Push() operator didn't move + // top to next block, so top is still point to the last pushed item + } else if(unuseditems == itemsperblock){ + // this time need move back a block + nowblock = (void**)*(nowblock + 1); + // top is point to the last item of the block + top = (void*)(nowblock + 2 + itemints * (itemsperblock - 1)); + unuseditems = 0; + } else { + // normal case in a block + top = (void*) ((void**) top - itemints); + } + // get wanted item + memcpy(retitem, top, itembytes); + items --; + unuseditems ++; + return 1; +} + +int stack::hasitem(void* testitem) +{ + void** pathblock = firstblock; + void* pathitem = (void*) (pathblock + 2); + int pathitemsleft = itemsperblock; + + while(pathitem != top) { + // 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 *). + pathitem = (void *) (pathblock + 2); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; + } + if (comp) { + if ((*comp)(pathitem, testitem) == 0) return 1; + } else { + if (compare2ints(pathitem, testitem) == 0) return 1; + } + // Find the next item in the block. + pathitem = (void*) ((void**) pathitem + itemints); + pathitemsleft--; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// class stack implementation // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// class memorypool implementation // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +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 = 0; + unallocateditems = 0; + pathitemsleft = 0; +} + +memorypool::memorypool(int bytecount, int itemcount, enum wordtype wtype, + int alignment) +{ + assert(bytecount > 0 && itemcount > 0); + init(bytecount, itemcount, wtype, alignment); +} + +memorypool::~memorypool() +{ + while (firstblock != (void **) NULL) { + nowblock = (void **) *(firstblock); + delete [] firstblock; + firstblock = nowblock; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// init() Initialize a pool of memory for allocation of items. // +// // +// This routine initializes the machinery for allocating items. A `pool' is // +// created whose records have size at least `bytecount'. Items will be allo- // +// cated 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 `alig- // +// nment'-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. // +// // +// Don't change this routine unless you understand it. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void memorypool::init(int bytecount, int itemcount, enum wordtype wtype, + int alignment) +{ + int wordsize; + + // Initialize values in the pool. + itemwordtype = wtype; + wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REALTYPE); + // 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 (sizeof(void *) > alignbytes) { + alignbytes = 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**) + (new BYTE[itemsperblock * itembytes + sizeof(void *) + alignbytes]); + // Set the next block pointer to NULL. + *firstblock = (void *) NULL; + restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// restart() Deallocate all items in a 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 memorypool::restart() +{ + unsigned long alignptr; + + items = 0; + maxitems = 0; + + // Set the currently active block. + nowblock = firstblock; + // Find the first item in the pool. Increment by the size of (void *). + alignptr = (unsigned long) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void *) + (alignptr + (unsigned long) alignbytes + - (alignptr % (unsigned long) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + // The stack of deallocated items is empty. + deaditemstack = (void *) NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// deinit() Free to the operating system all memory taken by a pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void memorypool::deinit() +{ + while (firstblock != (void **) NULL) { + nowblock = (void **) *(firstblock); + delete [] firstblock; + firstblock = nowblock; + } + + 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 = 0; + unallocateditems = 0; + pathitemsleft = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// alloc() Allocate space for an item. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* memorypool::alloc() +{ + void *newitem; + void **newblock; + unsigned long alignptr; + + // First check the linked list of dead items. If the list is not + // empty, allocate an item from the list rather than a fresh one. + if (deaditemstack != (void *) NULL) { + newitem = deaditemstack; // Take first item in list. + deaditemstack = * (void **) deaditemstack; + } else { + // Check if there are any free items left in the current block. + if (unallocateditems == 0) { + // Check if another block must be allocated. + if (*(nowblock) == (void *) NULL) { + // Allocate a new block of items, pointed to by the previous block. + newblock = (void**) + (new BYTE[itemsperblock * itembytes + sizeof(void *) + alignbytes]); + *(nowblock) = (void *) newblock; + // The next block pointer is NULL. + *newblock = (void *) NULL; + } + // Move to the new block. + nowblock = (void **) *(nowblock); + // Find the first item in the block. + // Increment by the size of (void *). + alignptr = (unsigned long) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void *) + (alignptr + (unsigned long) alignbytes + - (alignptr % (unsigned long) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + } + // Allocate a new item. + newitem = nextitem; + // Advance `nextitem' pointer to next free item in block. + if (itemwordtype == POINTER) { + nextitem = (void *) ((void **) nextitem + itemwords); + } else { + nextitem = (void *) ((REALTYPE *) 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 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 memorypool::traversalinit() +{ + unsigned long alignptr; + + // Begin the traversal in the first block. + pathblock = firstblock; + // Find the first item in the block. Increment by the size of (void *). + alignptr = (unsigned long) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void *) + (alignptr + (unsigned long) alignbytes + - (alignptr % (unsigned long) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// traverse() Find the next item in the list. // +// // +// This routine is used in conjunction with traversalinit(). Be forewarned // +// that this routine successively returns all items in the list, including // +// deallocated ones on the deaditemqueue. It's up to you to figure out which // +// ones are actually dead. Why? I don't want to allocate extra space just // +// to demarcate dead items. It can usually be done more space-efficiently by // +// a routine that knows something about the structure of the item. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* memorypool::traverse() +{ + void *newitem; + unsigned long alignptr; + + // Stop upon exhausting the list of items. + if (pathitem == nextitem) { + return (void *) NULL; + } + // Check whether any untraversed items remain in the current block. + if (pathitemsleft == 0) { + // Find the next block. + pathblock = (void **) *(pathblock); + // Find the first item in the block. Increment by the size of (void *). + alignptr = (unsigned long) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void *) + (alignptr + (unsigned long) alignbytes + - (alignptr % (unsigned long) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; + } + newitem = pathitem; + // Find the next item in the block. + if (itemwordtype == POINTER) { + pathitem = (void *) ((void **) pathitem + itemwords); + } else { + pathitem = (void *) ((REALTYPE *) pathitem + itemwords); + } + pathitemsleft--; + return newitem; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// class memorypool implementation // +/////////////////////////////////////////////////////////////////////////////// diff --git a/Tetgen/linklist.h b/Tetgen/linklist.h new file mode 100644 index 0000000000..599b5f2115 --- /dev/null +++ b/Tetgen/linklist.h @@ -0,0 +1,470 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// linklist.h Defines some commonly usefed data types, such as link, list // +// queue and stack, etc. // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +// A given programming language will have a number of data types available // +// along with some operations on them. C/C++ include int, float/double, char,// +// etc. We don't normally worry about how these datatypes and operations are // +// implemented, but just know what we can do with them. However, most real // +// programming tasks require more complex and sophisticated data types. If // +// the programming language we used doesn't provide them, we'll have to // +// define them ourself. Once we've defined such data types, we again don't // +// want to worry about how they're implemented, just know what operations // +// are allowed on that data type. There is huge range of reasonable courses, // +// textbooks and online sources on data structures and algorithms[1][2][3] // +// [4][5]. // +// // +// In this file, we defined some basic and most useful data types in our // +// common programming task. They are list, (double) link, queue, stack and // +// memorypool. where memorypool is a special data type for deal with large // +// memory blocks and was frequently used in the Tetgen Mesh program. All the // +// data types defined here can be used with arbitrary data types, include // +// built-in and user defined datatypes. // +// // +// I didn't use the Standard Template Library (STL)[2] of C++ language // +// provided to implement these data types. Although the STL is powerful and // +// elegant. But it need all the data types be known at compilation time, so // +// it can not compile each data types to seperate object files (*.o) until // +// the real date types are constructed. And it duplicate code which will // +// increase the code length and compilation time. // +// // +// The basic idea for implementing arbitrary datatypes is to use the // +// generic pointer type of C++ 'void*'. LEDA[4]'s solution is to consider // +// a data structure whose containers have a slot for storing objects of a // +// type T, which T are not stored directly in the containers of the data // +// structure but on the heap. The data slots of the containers have type // +// 'void*', and contain pointers to the objects on the heap. Type casting is // +// used to bridge the gap between the untyped world of the data slots (all // +// data is void*) and the typed world of the heap. So to implement a single // +// data types, there need 2 seperate classes in LEDA, one represents the // +// abstract data type (data slots) and the other represents the real data // +// type (data on the heap), these two classes can be compiled seperately // +// into object files (*.o) then link them together when in use. // +// // +// My implementation was diffrent with LEDA's. As a fact, I implemented // +// the data slots directly on the heap (dynamic memory). Any data type is // +// internally represented by a set of bytes, the size of data type is // +// determined at running time. Use a pointer (type of void*) to access each // +// data slot. No type casing is needed, it is up to user to determinte which // +// type is. This scheme simplified the implementation but less convient in // +// use than LEDA's. Users have to provide the size of data types to the // +// constructor and there still can not deal with some kinds of data types // +// (like a structure/class contains pointers as its member variables). // +// Anyway, I don't mean to provide more general data types at here, anybody // +// is welcome to make any improvement on it. Thank you. // +// // +// References: // +// // +// [1] A.V. Aho, J.E. Hopcroft, and J.D. Ullman. Data Structures and // +// Algorithms. Addison-Wesley, 1983. // +// [2] Joseph Bergin, Joe Bergin, F. B. Schneider, Data Structure Programm- // +// ing : With the Standard Template Library in C++ (Undergraduate Texts // +// in Computer Science). Springer Verlag, June 1998. // +// [3] Bruno R. Preiss, Data Structures and Algorithms with Object-Oriented // +// Design Patterns in C++. John Wiley & Sons; August 31, 1998. // +// [4] K. Mehlhorn and S. Naher, The LEDA Platform of Combinatorial and // +// Geometric Computing. Cambridge University Press, 1999. // +// [5] Data Structures and Algorithms, Online source, // +// http://www.cee.hw.ac.uk/~alison/ds.html. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef linklistH +#define linklistH + +#ifndef NULL + #define NULL 0 +#endif + +#ifndef BYTE + #define BYTE char +#endif + +#ifdef SINGLE + #define REALTYPE float +#else + #define REALTYPE double +#endif + +// Uncomment the following line to disable assertions. + +// #define NDEBUG + +#include <assert.h> + +/////////////////////////////////////////////////////////////////////////////// +// // +// Define a line-order function type, used in list, link. // +// // +// 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. // +// // +// For many primitive data types (like int, unsigned long, ...) a function // +// compare is predefined and defines the so-called default ordering of the // +// type. The default ordering is the usual "less than or equal" for the // +// numerical types, the lexicographic ordering for strings, and the // +// lexicographic ordering of the Cartesian coordinates for points. For all // +// other types T there is no default ordering, and the user has to define // +// the function compare if a linear order on T is required. // +// // +/////////////////////////////////////////////////////////////////////////////// + +typedef int (*compfunc) (const void *, const void *); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class list // +// // +// Any data type list with dynamic re-allocation. // +// // +// base--> __________ list, which stores an array of pointers(void*), // +// |_ _| is ofen used to maintain lists of objects. list // +// |_ data 0 _| introduces properties and methods to: // +// |__________| > Add or delete the objects in the list. // +// |_ _| > Rearrange the objects in the list. // +// |_ data 1 _| > Locate and access objects in the list. // +// |__________| > Sort the objects in the list. // +// |_ _| // +// |_ data 2 _| Note: The index of list is zero-based. Where 0 is // +// |__________| the index of the first object, 1 is the // +// |_ _| index of second object, and so on. // +// |_ _| // +// |__________| // +// // +/////////////////////////////////////////////////////////////////////////////// + +class list { + + void **base; + int itembytes, itemints; + long items, maxitems; + long expandsize; + compfunc comp; + + public: + + list(char*, int _maxitems = 256, int _expandsize = 128); + list(int _itembytes, int _maxitems = 256, int _expandsize = 128); + list(list&); + ~list(); + + list& operator=(list&); + int getitembytes() { return itembytes; } + int getitemints() { return itemints; } + long getmaxitems() { return maxitems; } + long getexpandsize() { return expandsize; } + void setexpandsize(int size) { assert(size); expandsize = size; } + void setcomp(compfunc compf) { assert(compf); comp = compf; } + + void *operator[](int index); + void *getitem(int index); + void clear() { items = 0; } + int len() { return items; } + void *alloc(); + void append(void*); + void insert(int, void*); + void del(int); + int remove(void*); + int hasitem(void*); + int indexof(void*); + void sort(); + void simplify(); + + friend void mergelist(list*, list*, list&, compfunc compf = NULL); + friend void splitlist(list*, int, list&, list&, compfunc compf = NULL); +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class link // +// // +// Any data type double link list build on a memory pool. // +// // +// This class's storage scheme is same with class memorypool. It implement a // +// double link list on the pool. Item traverse is through pointers stored in // +// each item's 'next' and 'prev' field. So do not need traverseinit() and // +// traverse(). // +// // +// head-> _________ _________ _________ _________<-tail // +// |__next___|--> |__next___|--> |__next___|--> |__NULL___| // +// |__NULL___|<-- |__prev___|<-- |__prev___|<-- |__prev___| // +// | | |_ _| |_ _| | | // +// | | |_ Data1 _| |_ Data2 _| | | // +// |_________| |_________| |_________| |_________| // +// // +// // +// Note: The index of link is one-based. Where 1 is the index of the first // +// object, 2 is the index of second object, and so on. 0 is used to // +// indicate a void link. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class link { + + protected: + + void **firstblock, **nowblock; + void *nextitem; + void *deaditemstack; + int itemsperblock; + int unallocateditems; + long maxitems; + + void **head, **tail; + void *nextlinkitem; + int itembytes, itemints; + long items; + int curpos; + compfunc comp; + + protected: + + void *alloc(); + void dealloc(void*); + void poolinit(int, int); + void poolrestart(); + + public: + + link(char*, int itemcount = 256); + link(int _itembytes, int itemcount = 256); + ~link(); + + int getitembytes() { return itembytes; } + int getitemints() { return itemints; } + void setcomp(compfunc compf) { assert(compf); comp = compf; } + + void init(); + void clear() { poolrestart(); init(); } + int len() { return items; } + int move(int); + int locate(int); + void add(void*); + void insert(int, void*); + void del(int); + void rewind() { nextlinkitem = *head; curpos= 1; } + void goend() { nextlinkitem = *(tail+1); curpos = items; } + void *getitem(); + void *getnitem(int); + void setnitem(int, void*); + int hasitem(void*); + void sort(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class queue // +// // +// Unbounded any data type queue with dynamic re-allocation. // +// // +// ___________ ___________ ___________ // +// Get() <-- |_ _|<--|_ _|<--|_ _| <-- Push() // +// |_ Data0 _| |_ Data1 _| |_ Data2 _| // +// |___________| |___________| |___________| // +// // +// queue head queue tail // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class queue Member Functions // +// // +// > Empty() // +// Tests if queue is empty. // +// > Push() // +// Adds a new top element (Stack and queue operation!). // +// > Bot() // +// Returns the bottom element (queue operation!) but does not remove it. // +// > Get() // +// Returns the bottom element (queue operation!) and removes it. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class queue : public link { + + public: + + queue(char* str, int itemcount = 256) : link(str, itemcount) {}; + queue(int _itembytes, int itemcount = 256) : link(_itembytes, itemcount) {}; + + int empty() { return items == 0; } + void push(void* newitem) { link::add(newitem); }; + void *bot() { return link::getnitem(1); }; + int get(void*); +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class stack // +// // +// Unbounded any data type stack with dynamic re-allocation. // +// // +// firstblock nowblock // +// __________ __________ __________ // +// |__next____|---> |__next____|---> |__NULL____| // +// |__NULL____|<--- |__prev____|<--- |__prev____| // +// Base---> |_ _| |_ _| |_ _| // +// |_ data 0 _| |_ data 3 _| |_ data 6 _| // +// |__________| |__________| |__________| // +// |_ _| |_ _| |_ _| <--- top // +// |_ data 1 _| |_ data 4 _| |_ _| // +// |__________| |__________| |__________| // +// |_ _| |_ _| |_ _| // +// |_ data 2 _| |_ data 5 _| |_ _| // +// |__________| |__________| |__________| // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Class stack Member Functions // +// // +// > Clear() // +// Clears the contents of stack. // +// > Empty() // +// Tests if stack is empty. // +// > Top() // +// Returns the top element (stack operation!) but does not pop it. // +// > Pop() // +// Returns the top element (stack operation!) and pops it. // +// > Push() // +// Adds a new top element (stack and queue operation!). // +// > Len() // +// Returns the number of elements in stack. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class stack { + + void **firstblock, **nowblock; + void *top; + int itembytes, itemints; + int itemsperblock; + int unuseditems; + long items, maxitems; + compfunc comp; + + public: + + stack(char*, int _itemsperblock = 256); + stack(int _itembytes, int _itemsperblock = 256); + ~stack(); + + void setcomp(compfunc compf) { assert(compf); comp = compf; } + + void init(int, int); + void restart(); + int len() { return items; } + int empty() { return items == 0; } + void push(void*); + void *topitem(); + int pop(void*); + int hasitem(void*); +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Labels that signify whether a record consists primarily of pointers or of // +// floating-point words. Used to make decisions about data alignment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum wordtype {POINTER, FLOATINGPOINT}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// class 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// memorypool member functions: // +// // +// > init() // +// Initialize a pool of memory for allocation of items. // +// > restart() // +// Deallocate all items in a pool. // +// > alloc() // +// Allocate space for an item. // +// > dealloc() // +// Deallocate space for an item. // +// > traversalinit() // +// Prepare to traverse the entire list of items. // +// > traverse() // +// Find the next item in the list. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class memorypool { + + public: + + void **firstblock, **nowblock; + void *nextitem; + void *deaditemstack; + void **pathblock; + void *pathitem; + wordtype itemwordtype; + int alignbytes; + int itembytes, itemwords; + int itemsperblock; + long items, maxitems; + int unallocateditems; + int pathitemsleft; + + public: + + memorypool(); + memorypool(int, int, enum wordtype, int); + ~memorypool(); + + void init(int, int, enum wordtype, int); + void restart(); + void deinit(); + void *alloc(); + void dealloc(void*); + void traversalinit(); + void *traverse(); +}; + +#endif // #ifndef linklistH diff --git a/Tetgen/predicate.cpp b/Tetgen/predicate.cpp new file mode 100644 index 0000000000..1fa2a405c1 --- /dev/null +++ b/Tetgen/predicate.cpp @@ -0,0 +1,2945 @@ +/*****************************************************************************/ +/* */ +/* 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 "defines.h" + +/* 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 */ + +/* 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) + +REAL splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ +REAL epsilon; /* = 2^(-p). Used to estimate roundoff errors. */ +/* A set of coefficients used to calculate maximum roundoff errors. */ +REAL resulterrbound; +REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; +REAL o3derrboundA, o3derrboundB, o3derrboundC; +REAL iccerrboundA, iccerrboundB, iccerrboundC; +REAL isperrboundA, isperrboundB, isperrboundC; + +/*****************************************************************************/ +/* */ +/* 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. */ +/* */ +/*****************************************************************************/ + +void exactinit() +{ +/* Here is the code configure Intel CPUs to correctly execute my code. For */ +/* detail please see: */ +/* http://www.cs.cmu.edu/~quake/robust.pc.html */ + +#ifdef INTEL_MSC_TURBOC +#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 /* defined INTEL_MSC_TURBOC */ + +#ifdef INTEL_GCC_LINUX +#include <i386/fpu_control.h> +#ifdef SINGLE +__setfpucw(4210); /* set FPU control word for single precision */ +#else /* not SINGLE */ +__setfpucw(4722); /* set FPU control word for double precision */ +#endif /* not SINGLE */ +#endif /* defined INTEL_GCC_LINUX */ + +#ifdef INTEL_GCC +#include <i386/fpu_control.h> +void set_ctrlword(v) +int v; +{ + asm("fldcw %0" :: "m" (v)); +} +#ifdef SINGLE +set_ctrlword(4210); /* set FPU control word for single precision */ +#else /* not SINGLE */ +set_ctrlword(4722); /* set FPU control word for double precision */ +#endif /* not SINGLE */ +#endif /* defined INTEL_GCC */ + + REAL half; + REAL check, lastcheck; + int every_other; + + 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; +} + +/*****************************************************************************/ +/* */ +/* 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +// orient2d() 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 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); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +// orient3d() 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 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; + + 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); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +// incircle() 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 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); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // +// // +// insphere() 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 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 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/quality.cpp b/Tetgen/quality.cpp new file mode 100644 index 0000000000..879673f8d8 --- /dev/null +++ b/Tetgen/quality.cpp @@ -0,0 +1,1723 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// quality.cpp Implement the Delaunay Mesh Refinement Algorithm of // +// Shewchuk[2] to generate an almost good mesh. // +// // +// Tetgen Version 1.0 beta // +// November, 2001 // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Generating a mesh whose elements have small aspect ratio and their sizes // +// conform to a given control spacing function is one of the most important // +// steps in numerical simulations. The aspect ratio of an element is usually // +// defined as the ratio of the radius of its circumsphere to the radius of // +// its inscribed sphere. An alternative but weaker quality measurement is // +// radius-edge ratio, which is the ratio of the circumradius to the shortest // +// edge length of the tetrahedron. // +// // +// In two-dimensions, many methods guaranteed to generate well-shaped meshes,// +// such as the two-dimensional Delaunay refinement method of J. Ruppert[1]. // +// Surprisingly, in three-dimensions, generating well-shaped meshes is // +// considerablely more difficult, because it is difficult to identify and // +// remove some bad-shaped tetrahedra(like sliver) from the mesh. Even so, // +// generating a three-dimensional mesh with small radius-edge ratio is well // +// understood. Shewchuk[2] extend the Ruppert's method to three-dimensions // +// by proving that all tetrahedron will have radius-edge ratio no more than // +// 2. // +// // +// In this file, I implement Shewchuk's algorithm to generate an almost good // +// tetrahedral mesh with good grading effect. But there is no guarantee that // +// slivers can be eliminated. It can be done by an additional mesh smooth- // +// ing and mesh improvement routines. This algorithm does not guarantee to // +// terminate when there exists small angles in the domain. It may cause // +// infinite loop in boundary protection step. For Tetgen can terminate in // +// all conditions, I modified the algorithm slightly on the split encroached // +// subfaces rule. When a encroached subface is determined, it not always be // +// splited, only there is no small angle at this subface(that is, angle // +// between suface-to-subface and subface-to-subsegment is not a small angle // +// (<=45 degree)). (I'm looking for a pleasant way to fix this problem.) // +// Despite these unpleasant facts, the Delaunay refinement algorithm falls // +// into the class of algorithms that usually outperform their worst-case // +// bounds. One can apply a tighter bound (as low as 1.1)on radius-edge ratio // +// than the theory suggests is possible, or even apply bounds on dihedral // +// angles, and still produce a small, nicely graded mesh. // +// // +// Refernces: // +// // +// [1] Jim Ruppert, A Delaunay Refinement Algorithm for Quality Two- // +// Dimensional Mesh Generation, Journal of Algorithms 18(3):548-585, // +// May 1995. // +// [2] Jonathan Richard Shewchuk, Delaunay Refinement Mesh Generation. // +// PhD thesis, School of Computer Science, Carnegie Mellon University, // +// May 1997. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "tetlib.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// dumplist() Debug functions. Write the items of list to a text file. // +// // +// type = 0 tetrahedron, type = 1 subface, type = 2 subsegments. // +// If sort > 0, sort list before output. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::dumplist(char* dumpfile, list* checklist, int type, int sort) +{ + FILE *outfile; + triface *checktet; + face *checksh; + point3d torg, tdest, tapex, toppo; + int i; + + outfile = fopen(dumpfile, "w"); + if (outfile == (FILE *) NULL) { + printf("Error: Cannot create file %s.\n", dumpfile); + return; + } + if (!quiet) { + printf("Writing %s.\n", dumpfile); + } + if (verbose < 1) { + numbernodes(1); + } + if (sort > 0) { + checklist->sort(); + } + fprintf(outfile, "# Dumping %d items.\n", checklist->len()); + for (i = 0; i < checklist->len(); i++) { + if (type == 0) { + checktet = (triface*)(*checklist)[i]; + fprintf(outfile, "%4d x%lx ", i, (unsigned long)(checktet->tet)); + if (isdead(checktet)) { + fprintf(outfile, "(dead)\n"); + } else { + org (*checktet, torg); + dest(*checktet, tdest); + apex(*checktet, tapex); + oppo(*checktet, toppo); + fprintf(outfile, "(%d, %d, %d, %d)\n", pointmark(torg), + pointmark(tdest), pointmark(tapex), pointmark(toppo)); + } + } else if (type == 1) { + checksh = (face*)(*checklist)[i]; + fprintf(outfile, "%4d x%lx ", i, (unsigned long)(checksh->sh)); + if (isdead(checksh)) { + fprintf(outfile, "(dead)\n"); + } else { + sorg (*checksh, torg); + sdest(*checksh, tdest); + sapex(*checksh, tapex); + fprintf(outfile, "(%d, %d, %d)\n", + pointmark(torg), pointmark(tdest), pointmark(tapex)); + } + } else if (type == 2) { + checksh = (face*)(*checklist)[i]; + fprintf(outfile, "%4d x%lx ", i, (unsigned long)(checksh->sh)); + if (isdead(checksh)) { + fprintf(outfile, "(dead)\n"); + } else { + sorg (*checksh, torg); + sdest(*checksh, tdest); + fprintf(outfile, "(%d, %d)\n", pointmark(torg), pointmark(tdest)); + } + } + } + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// Mesh quality testing routines // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkquality() To test edges of the face is encroached if they are // +// subsegments, otherwise to test if it is encroached if // +// it is a subface, last to test if the two tets abuting // +// this face are good quality. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::checkquality(triface* testface) +{ + if (shsegflaws) { + face testseg; + int i; + for (i = 0; i < 3; i++) { + enextself(*testface); + tsspivot(testface, &testseg); + if (testseg.sh != dummysh) { + uncheckedshseglist->append(&testseg); + } + } + } + if (shflaws) { + face testsh; + tspivot(*testface, testsh); + if (testsh.sh != dummysh) { + uncheckedshlist->append(&testsh); + } + } + if (tetflaws) { + triface neighbortet; + uncheckedtetlist->append(testface); + sym(*testface, neighbortet); + if (neighbortet.tet != dummytet) { + uncheckedtetlist->append(&neighbortet); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkedge4encroach() Check a segment to see if it is encroached; add // +// it to the list if it is. // +// // +// An encroached segment is an unflippable edge that has a point in its // +// diametral sphere (that is, it faces an angle greater than 90 degrees). // +// This definition is due to Ruppert[1]. While in three dimension, A // +// subsegment is encroached if a vertex other than its endpoints lies inside // +// or on its diametral sphere (that is, it faces an angle greater than or // +// equal 90 degrees). This definition is due to Shewchuk[2]. This definition // +// of encroachment is slightly stronger than Ruppert's, to ensure that all // +// unencroached subsegments are strongly Delaunay. // +// // +// Returns a nonzero value if the edge is encroached. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// Check whether the apex('eapex') is inside the diametral sphere of the +// subsegment(from 'eorg' to 'edest'). Pythagoras' Theorem is used to +// check whether the angle at the vertex is greater than 90 degrees. + +inline bool isedgeencroached(point3d eapex, point3d eorg, point3d edest) +{ + return (eapex[0] * (eorg[0] + edest[0]) + + eapex[1] * (eorg[1] + edest[1]) + + eapex[2] * (eorg[2] + edest[2]) >= + eapex[0] * eapex[0] + eorg[0] * edest[0] + + eapex[1] * eapex[1] + eorg[1] * edest[1] + + eapex[2] * eapex[2] + eorg[2] * edest[2]); +} + +int mesh3d::checkedge4encroach(face *testedge, point3d testpoint) +{ + badface3d *badedge; + triface neighbortet, spintet; + triface tmptet; + face tmpseg; + point3d eorg, edest, eapex; + point3d torg, tdest, tapex; + int encroachflag, smallangleflag; + int hitbdry; + + encroachflag = 0; + eorg = sorg(*testedge); + edest = sdest(*testedge); + + if (testpoint == (point3d) NULL) { + // Spin around subsegment 'testedge', find all faces which contained + // it. For each face, check whether its apex is inside the diametral + // sphere of the 'testedge'. + sstpivot(testedge, &neighbortet); + spintet = neighbortet; + tapex = apex(neighbortet); + hitbdry = 0; + while (true) { + if (fnextself(spintet)) { + eapex = apex(spintet); + if (eapex == tapex) { + break; // Rewind, can leave now. + } + if (isedgeencroached(eapex, eorg, edest)) { + encroachflag = 1; + break; + } + } else { + hitbdry ++; + if (hitbdry >= 2) { + break; + } else { + esym(neighbortet, spintet); + } + } + } + if (!encroachflag) { + eapex = apex(neighbortet); + if (isedgeencroached(eapex, eorg, edest)) { + encroachflag = 1; + } + } + } else { + // Check does 'testedge' be encroached by 'testpoint'. + if ((testpoint != eorg) && (testpoint != edest)) { + if (isedgeencroached(testpoint, eorg, edest)) { + encroachflag = 1; + sstpivot(testedge, &neighbortet); + } + } + } + + if (encroachflag && (!sinfected(*testedge)) && + (!nobisect || ((nobisect == 1) && !isridge(&neighbortet)))) { + // 'testedge' is being encroached. It will be splitted by inserting its + // midpoint. + // If 'testedge' is belong to one or more triangular faces which its + // (or their) three edges are all subsegments. Don't insert midpoint. + // Because it will create small angles which can't be removed by edge + // flips. + spintet = neighbortet; + tapex = apex(neighbortet); + hitbdry = smallangleflag = 0; + while (true) { + if (fnextself(spintet)) { + eapex = apex(spintet); + if (eapex == tapex) { + break; // Rewind, can leave now. + } + tmptet = spintet; + enextself(tmptet); + tsspivot(&tmptet, &tmpseg); + if (tmpseg.sh != dummysh) { + enextself(tmptet); + tsspivot(&tmptet, &tmpseg); + if (tmpseg.sh != dummysh) { + smallangleflag = 1; + break; + } + } + } else { + hitbdry ++; + if (hitbdry >= 2) { + break; + } else { + esym(neighbortet, spintet); + } + } + } + if (!smallangleflag) { + tmptet = neighbortet; + enextself(tmptet); + tsspivot(&tmptet, &tmpseg); + if (tmpseg.sh != dummysh) { + enextself(tmptet); + tsspivot(&tmptet, &tmpseg); + if (tmpseg.sh != dummysh) { + smallangleflag = 1; + } + } + } + + if (!smallangleflag) { + if (verbose > 2) { + printf(" Queueing encroached segment from %d to %d.\n", + pointmark(eorg), pointmark(edest)); + } + // Add the shell edge to the list of encroached segments. + badedge = (badface3d *) badsegments.alloc(); + badedge->shface = *testedge; + badedge->faceorg = eorg; + badedge->facedest = edest; + } else { + if (!quiet && verbose) { + printf("Warning: Not split encroached subsegment:\n"); + printf(" from (%.12g, %.12g, %.12g)\n",eorg[0], eorg[1], eorg[2]); + printf(" to (%.12g, %.12g, %.12g)\n", edest[0], edest[1], edest[2]); + } + // Set a flag in 'testedge' to avoid multiple tests later. + // Here temporarily use the tenth pointer ('testedge->sh[10]'), infect + // its shver field. + sinfect(*testedge); + } + } + + // The return value indicate 'testedge' is being encroached. + return encroachflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkface4encroach() Check a subface to see if it is encroached; add // +// it to the list if it is. // +// // +// An encroached subface is an unflippable face that has a point lies in or // +// on its equatorial sphere. This definition is due to Shewchuk[2]. // +// // +// A priority queue used for keep Encroached faces. Why? The question 's // +// answer could be found at Shewchuk's paper[2]: // +// ... // +// One further amendment to the algorithm is necessary to obtain the best // +// possible bound on the circumradius-to-shortest edge ratios of the tetrah- // +// edra. When several encroached subfacets exist, they should not be split // +// in arbitrary order. If a vertex p encroaches upon a subfacet f of a facet // +// F, but the projection of p to F dest not lie in f, then splitting f is // +// not the best choice. One can show(Lemma 1) that there is some subfacet g // +// of F that is encroached upon by p and contain the projection point of p // +// to F. (The lemma assume that there are no encroached subsegments in the // +// mesh, as they have priority.) A better bound is achieved if the algorithm // +// splits g first and delays the splitting of f indefinitely. // +// ... // +// // +// Returns a nonzero value if the edge is encroached. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::checkface4encroach(face* testface, point3d testpoint) +{ + triface testtet, tmptet, tmptmptet; + face testsh, neighborsh; + point3d forg, fdest, fapex, foppo; + enum locateresult loc; + REAL cent[3], proj[3]; + REAL dx, dy, dz; + REAL radius2, dist2; + int encroachflag; + + // If its a nonsolid face, need not check. + if (isnonsolid(*testface)) return 0; + + // Find circumcenter of the face. + forg = sorg(*testface); + fdest = sdest(*testface); + fapex = sapex(*testface); + if ((testpoint != (point3d) NULL) && + ((testpoint == forg) || (testpoint == fdest) || (testpoint == fapex))) { + return 0; + } + circumcenter(forg, fdest, fapex, cent); + + // Get the square radius of equatorial sphere. + dx = forg[0] - cent[0]; + dy = forg[1] - cent[1]; + dz = forg[2] - cent[2]; + radius2 = dx * dx + dy * dy + dz * dz; + + encroachflag = 0; + if (testpoint == (point3d) NULL) { + // Check two opposite vertex to find encroaching point. + testsh = *testface; + stpivot(testsh, testtet); + if (testtet.tet != dummytet) { + foppo = oppo(testtet); + dx = foppo[0] - cent[0]; + dy = foppo[1] - cent[1]; + dz = foppo[2] - cent[2]; + dist2 = dx * dx + dy * dy + dz * dz; + if (dist2 <= radius2) { + encroachflag = 1; + } + } + if (!encroachflag) { + sesymself(testsh); + stpivot(testsh, testtet); + if (testtet.tet != dummytet) { + foppo = oppo(testtet); + dx = foppo[0] - cent[0]; + dy = foppo[1] - cent[1]; + dz = foppo[2] - cent[2]; + dist2 = dx * dx + dy * dy + dz * dz; + if (dist2 <= radius2) { + encroachflag = 1; + } + } + } + } else { + foppo = testpoint; + dx = foppo[0] - cent[0]; + dy = foppo[1] - cent[1]; + dz = foppo[2] - cent[2]; + dist2 = dx * dx + dy * dy + dz * dz; + if (dist2 <= radius2) { + encroachflag = 1; + testsh = *testface; + stpivot(testsh, testtet); + if (testtet.tet == dummytet) { + sesymself(testsh); + stpivot(testsh, testtet); + assert(testtet.tet != dummytet); + } + } + } + + if (encroachflag) { + // Before add it to bad face queue, we need check its project point + // of this face to determine which queue(0 or 1) it should keep. + proj[0] = foppo[0]; + proj[1] = foppo[1]; + proj[2] = foppo[2]; + projontoface(forg, fdest, fapex, proj); + loc = iscoplanarintri(proj, &testtet); + if (loc != OUTSIDE) { + enqueuebadface(testface, cent, 1); + } else { + enqueuebadface(testface, cent, 0); + } + } + + return encroachflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// testtetrahedron() 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::testtetrahedron(triface* testtet) +{ + point3d torg, tdest, tapex, toppo; + REAL dxod, dyod, dzod, dxda, dyda, dzda, dxao, dyao, dzao; + REAL dxop, dyop, dzop, dxdp, dydp, dzdp, dxap, dyap, dzap; + REAL dxod2, dyod2, dzod2, dxda2, dyda2, dzda2, dxao2, dyao2, dzao2; + REAL dxop2, dyop2, dzop2, dxdp2, dydp2, dzdp2, dxap2, dyap2, dzap2; + REAL dxoc, dyoc, dzoc, dxoc2, dyoc2, dzoc2; + REAL edgelen[6], cent[3], smedgelen, radius, ratio2, volume; + int i; + + torg = org(*testtet); + tdest = dest(*testtet); + tapex = apex(*testtet); + toppo = oppo(*testtet); + + dxod = torg[0] - tdest[0]; + dyod = torg[1] - tdest[1]; + dzod = torg[2] - tdest[2]; + dxda = tdest[0] - tapex[0]; + dyda = tdest[1] - tapex[1]; + dzda = tdest[2] - tapex[2]; + dxao = tapex[0] - torg[0]; + dyao = tapex[1] - torg[1]; + dzao = tapex[2] - torg[2]; + + dxop = torg[0] - toppo[0]; + dyop = torg[1] - toppo[1]; + dzop = torg[2] - toppo[2]; + dxdp = tdest[0] - toppo[0]; + dydp = tdest[1] - toppo[1]; + dzdp = tdest[2] - toppo[2]; + dxap = tapex[0] - toppo[0]; + dyap = tapex[1] - toppo[1]; + dzap = tapex[2] - toppo[2]; + + dxod2 = dxod * dxod; + dyod2 = dyod * dyod; + dzod2 = dzod * dzod; + dxda2 = dxda * dxda; + dyda2 = dyda * dyda; + dzda2 = dzda * dzda; + dxao2 = dxao * dxao; + dyao2 = dyao * dyao; + dzao2 = dzao * dzao; + + dxop2 = dxop * dxop; + dyop2 = dyop * dyop; + dzop2 = dzop * dzop; + dxdp2 = dxdp * dxdp; + dydp2 = dydp * dydp; + dzdp2 = dzdp * dzdp; + dxap2 = dxap * dxap; + dyap2 = dyap * dyap; + dzap2 = dzap * dzap; + + // Find the lengths of the tetrahedron's siz edges. + edgelen[0] = dxod2 + dyod2 + dzod2; + edgelen[1] = dxda2 + dyda2 + dzda2; + edgelen[2] = dxao2 + dyao2 + dzao2; + edgelen[3] = dxop2 + dyop2 + dzop2; + edgelen[4] = dxdp2 + dydp2 + dzdp2; + edgelen[5] = dxap2 + dyap2 + dzap2; + + smedgelen = edgelen[0]; + for (i = 1; i < 6; i++) { + if (smedgelen > edgelen[i]) smedgelen = edgelen[i]; + } + if (smedgelen <= usertolerance) { + printf("Precssion error in testtetrahedron(): \n"); + printf(" The shortest edge length %.12g is smaller than tolerance.\n", + smedgelen); + printf(" This probably means that I am trying to refine mesh to a\n"); + printf(" smaller size than can be accommodated by the finite \n"); + printf(" precision of floating point arithmetic. \n"); + precisionerror(); + } + + circumcenter(torg, tdest, tapex, toppo, cent); + + dxoc = torg[0] - cent[0]; + dyoc = torg[1] - cent[1]; + dzoc = torg[2] - cent[2]; + dxoc2 = dxoc * dxoc; + dyoc2 = dyoc * dyoc; + dzoc2 = dzoc * dzoc; + + radius = dxoc2 + dyoc2 + dzoc2; + ratio2 = radius / smedgelen; + + // Check whether the ratio is smaller than permitted. + if (ratio2 > goodratio) { + // It's a bad-shaped tet, before we decide to split it, we need check + // if this bad-shaped tet is les on boundary with a small (dihedral) + // angle. If so, we should not split it, or it maybe cause endless + // loop until run out of your machine's precission. + // Not done yet. + // Add this tet to the list of bad tetrahedra. + enqueuebadtet(testtet, ratio2, torg, tdest, tapex, toppo, cent); + return; + } + if (varvolume || fixedvolume) { + // Check whether the volume is larger than permitted. + volume = tetvolume(torg, tdest, tapex, toppo); + if (volume < 0) volume = -volume; + if (fixedvolume && (volume > maxvolume)) { + // Add this tetrahedron to the list of bad tetrahedra. + enqueuebadtet(testtet, 0, torg, tdest, tapex, toppo, cent); + } else if (varvolume) { + // Nonpositive volume constraints are treated as unconstrained. + if ((volume > volumebound(testtet->tet)) && + (volumebound(testtet->tet) > 0.0)) { + // Add this tetrahedron to the list of bad tetrahedron. + enqueuebadtet(testtet, 0, torg, tdest, tapex, toppo, cent); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// enqueuebadface() Add a bad subface to the end of a queue. // +// // +// The queue is actually a set of 2 queues. 'rank' should be 0 or 1. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::enqueuebadface(face *insface, point3d inscent, int rank) +{ + badface3d *newface; + + if (verbose > 2) { + printf(" Queueing bad face: (%d, %d, %d) rank %i.\n", + pointmark(sorg(*insface)), pointmark(sdest(*insface)), + pointmark(sapex(*insface)), rank); + } + // Allocate space for the bad face. + newface = (badface3d *) badfaces.alloc(); + newface->shface = *insface; + if (inscent != NULL) { + // We need not re-calculate circumcenter when split face. + newface->badfacetet.tet = dummytet; + newface->cent[0] = inscent[0]; + newface->cent[1] = inscent[1]; + newface->cent[2] = inscent[2]; + } else { + // We need re-calculate circumcenter when split face. + newface->badfacetet.tet = (tetrahedron *) NULL; + } + sapex(*insface, newface->faceapex); + sorg(*insface, newface->faceorg); + sdest(*insface, newface->facedest); + newface->nextface = (badface3d *) NULL; + // Add the face to the end of a queue. + *facequetail[rank] = newface; + // Maintain a pointer to the NULL pointer at the end of the queue. + facequetail[rank] = &newface->nextface; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dequeuebadface() Remove a face from the front of the queue. // +// // +/////////////////////////////////////////////////////////////////////////////// + +badface3d* mesh3d::dequeuebadface() +{ + badface3d *result; + int queuenumber; + + // Look for a nonempty queue. + for (queuenumber = 1; queuenumber >= 0; queuenumber--) { + result = facequefront[queuenumber]; + if (result != (badface3d *) NULL) { + // Remove the face from the queue. + facequefront[queuenumber] = result->nextface; + // Maintain a pointer to the NULL pointer at the end of the queue. + if (facequefront[queuenumber] == (badface3d *) NULL) { + facequetail[queuenumber] = &facequefront[queuenumber]; + } + return result; + } + } + return (badface3d *) NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// enqueuebadtet() Add a bad tetrahedron to the end of a queue. // +// // +// The queue is actually a set of 64 queues. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::enqueuebadtet(triface *instet, REAL ratio, point3d insorg, + point3d insdest, point3d insapex, point3d insoppo, + point3d inscent) +{ + badtet *newtet; + int queuenumber; + + // Allocate space for the bad tetrahedron. + newtet = (badtet *) badtets.alloc(); + newtet->btet = *instet; + newtet->key = ratio; + newtet->cent[0] = inscent[0]; + newtet->cent[1] = inscent[1]; + newtet->cent[2] = inscent[2]; + newtet->tetorg = insorg; + newtet->tetdest = insdest; + newtet->tetapex = insapex; + newtet->tetoppo = insoppo; + newtet->nexttet = (badtet *) NULL; + // Determine the appropriate queue to put the bad tetrahedron into. + if (ratio > goodratio) { // square of 1.414 + queuenumber = (int) ((ratio - goodratio) / 0.5); + if (queuenumber > 63) { + queuenumber = 63; + } else if (queuenumber < 0) { + // The integer overflow( caused by a very large ratio.) + queuenumber = 63; + } + } else { + // It's not a bad ratio; put the tetrahedron in the lowest-priority + // queue. + queuenumber = 0; + } + // Add the tetrahedron to the end of a queue. + *tetquetail[queuenumber] = newtet; + // Maintain a pointer to the NULL pointer at the end of the queue. + tetquetail[queuenumber] = &newtet->nexttet; + + if (verbose > 2) { + printf(" Queueing bad tet: (%d, %d, %d, %d), ratio %g, qnum %d.\n", + pointmark(insorg), pointmark(insdest), pointmark(insapex), + pointmark(insoppo), sqrt(ratio), queuenumber); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dequeuebadtet() Remove a tetrahedron from the front of the queue. // +// // +/////////////////////////////////////////////////////////////////////////////// + +badtet* mesh3d::dequeuebadtet() +{ + badtet *result; + int queuenumber; + + // Look for a nonempty queue. + for (queuenumber = 63; queuenumber >= 0; queuenumber--) { + result = tetquefront[queuenumber]; + if (result != (badtet *) NULL) { + // Remove the tetrahedron from the queue. + tetquefront[queuenumber] = result->nexttet; + // Maintain a pointer to the NULL pointer at the end of the queue. + if (tetquefront[queuenumber] == (badtet *) NULL) { + tetquetail[queuenumber] = &tetquefront[queuenumber]; + } + return result; + } + } + return (badtet *) NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tallyencsegs() Traverse the entire list of shell edges, check each edge // +// to see if it is encroached. If so, add it to the list. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::tallyencsegs() +{ + face edgeloop; + + if (verbose) { + printf(" Making a list of encroached segments.\n"); + } + subsegs.traversalinit(); + edgeloop.shver = 0; + edgeloop.sh = shellfacetraverse(&subsegs); + while (edgeloop.sh != (shellface *) NULL) { + // If the segment is encroached, add it to the list. + checkedge4encroach(&edgeloop); + edgeloop.sh = shellfacetraverse(&subsegs); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tallyencfaces() Traverse the entire list of shell faces, check each // +// face to see if it is encroached. If so, add it to // +// the list. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::tallyencfaces() +{ + face faceloop; + + if (verbose) { + printf(" Making a list of encroached subfaces.\n"); + } + subfaces.traversalinit(); + faceloop.shver = 0; + faceloop.sh = shellfacetraverse(&subfaces); + while (faceloop.sh != (shellface *) NULL) { + // If the subface is encroached, add it to the list. + checkface4encroach(&faceloop); + faceloop.sh = shellfacetraverse(&subfaces); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tallytets() Traverse the entire list of tetrahedra, check each tet to // +// see if it is bad quality. If so, add it to the list. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::tallytets() +{ + triface tetloop; + + if (verbose) { + printf(" Making a list of bad tetrahedra.\n"); + } + tetrahedrons.traversalinit(); + tetloop.loc = 0; + tetloop.ver = 0; + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + testtetrahedron(&tetloop); + tetloop.tet = tetrahedrontraverse(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Mesh quality testing routines // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Mesh quality maintenance routines // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// repairencsegs() Find and repair all the encroached segments. // +// // +// Encroached segments are repaired by splitting them by inserting a point // +// at or near their centers. // +// // +// When a segment is split, the two resulting subsegments are always tested // +// to see if they are encroached upon, regardless of the value of 'flaws'. // // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::repairencsegs() +{ + badface3d *encloop; + triface enctet; + triface *checktet, *skiptet; + face *checksh, *skipsh; + point3d eorg, edest; + point3d newpoint; + REAL segmentlength, nearestpoweroftwo; + REAL split; + int acuteorg, acutedest; + int flipcount; + int i; + + while ((badsegments.items > 0) && (steinerleft != 0)) { + badsegments.traversalinit(); + encloop = badsegmenttraverse(); + while ((encloop != (badface3d *) NULL) && (steinerleft != 0)) { + // Check this edge is not splitted. + eorg = sorg(encloop->shface); + edest = sdest(encloop->shface); + if (((eorg != (point3d) NULL) && (edest != (point3d) NULL)) && + ((eorg == encloop->faceorg) && (edest == encloop->facedest))) { + // To decide where to split a segment, we need to know if the + // segment shares an endpoint with an adjacent segment. + // The concern is that, if we simply split every encroached + // segment in its center, two adjacent segments with a small + // angle between them might lead to an infinite loop; each + // point added to split one segment will encroach upon the + // other segment, which must then be split with a point that + // will encroach upon the first segment, and so on forever. + // To avoid this, imagine a set of concentric circles, whose + // radii are powers of two, about each segment endpoint. + // These concentric circles determine where the segment is + // split. (If both endpoints are shared with adjacent + // segments, split the segment in the middle, and apply the + // concentric shells for later splittings.) + + // Is the origin shared with another segment? + acuteorg = isexistincidentseg(&(encloop->shface)); + // Is the destination shared with another segment? + sesymself(encloop->shface); + acutedest = isexistincidentseg(&(encloop->shface)); + sesymself(encloop->shface); + + // Use the concentric circles if exactly one endpoint is shared + // with another adjacent segment. + if (acuteorg ^ acutedest) { + segmentlength = sqrt((edest[0] - eorg[0]) * (edest[0] - eorg[0]) + + (edest[1] - eorg[1]) * (edest[1] - eorg[1]) + + (edest[2] - eorg[2]) * (edest[2] - eorg[2])); + // Find the power of two nearest the segment's length. + nearestpoweroftwo = 1.0; + while (segmentlength > SQUAREROOTTWO * nearestpoweroftwo) { + nearestpoweroftwo *= 2.0; + } + while (segmentlength < (0.5 * SQUAREROOTTWO) * nearestpoweroftwo) { + nearestpoweroftwo *= 0.5; + } + // Where do we split the segment? + split = 0.5 * nearestpoweroftwo / segmentlength; + if (acutedest) { + split = 1.0 - split; + } + } else { + // If we're not worried about adjacent segments, split + // this segment in the middle. + split = 0.5; + } + + // Create the new point. + newpoint = (point3d) points.alloc(); + // Interpolate its coordinate and attributes. + for (i = 0; i < 3 + nextras; i++) { + newpoint[i] = (1.0 - split) * eorg[i] + split * edest[i]; + } + // The new point must lies on subsegment. + setpointmark(newpoint, mark(encloop->shface)); + if (verbose > 1) { + // Set pointmark of newpoint for Debuging outputs. + setpointmark(newpoint, points.items); + } + // Check whether the new point lies on an endpoint. + if ((compare2points(&newpoint, &eorg) == 0) || + (compare2points(&newpoint, &edest) == 0)) { + printf("Error: Ran out of precision at (%.12g, %.12g, %.12g).\n", + newpoint[0], newpoint[1], newpoint[2]); + printf(" I attempted to split a segment to a smaller size than\n"); + printf(" can be accommodated by the finite precision of floating\n"); + printf(" point arithmetic.\n"); + precisionerror(); + } + if (verbose > 1) { + printf(" Insert circumcenter: (%.12g, %.12g, %.12g) %d\n", + newpoint[0], newpoint[1], newpoint[2], pointmark(newpoint)); + printf(" to split subsegment (%d, %d).\n", + pointmark(eorg), pointmark(edest)); + } + if (shsegflaws) { + uncheckedshseglist->clear(); + } + if (shflaws) { + uncheckedshlist->clear(); + } + if (tetflaws) { + uncheckedtetlist->clear(); + } + // Find a tetra contain this subsegment. + sstpivot(&(encloop->shface), &enctet); + findversion(&enctet, &(encloop->shface)); + // Insert the splitting point. This should always succssed. + flipcount = insertonedge(newpoint, &enctet, &(encloop->shface)); + if (verbose > 1) { + printf(" Successfully split subsegment with %d flips.\n", flipcount); + } + if (steinerleft > 0) { + steinerleft--; + } + // Check all encroached segments, subfaces and bad tetrahedra caused + // by insert this point if there need. + if (shsegflaws && (uncheckedshseglist->len() > 0)) { + uncheckedshseglist->sort(); + skipsh = checksh = (face*)(*uncheckedshseglist)[0]; + if (!isdead(checksh)) { + checkedge4encroach(checksh); + } + for (i = 1; i < uncheckedshseglist->len(); i++) { + checksh = (face*)(*uncheckedshseglist)[i]; + if (checksh->sh != skipsh->sh) { + if (!isdead(checksh)) { + checkedge4encroach(checksh); + } + skipsh = checksh; + } + } + } + if (shflaws && (uncheckedshlist->len() > 0)) { + uncheckedshlist->sort(); + skipsh = checksh = (face*)(*uncheckedshlist)[0]; + if (!isdead(checksh)) { + checkface4encroach(checksh); + } + for (i = 1; i < uncheckedshlist->len(); i++) { + checksh = (face*)(*uncheckedshlist)[i]; + if (checksh->sh != skipsh->sh) { + if (!isdead(checksh)) { + checkface4encroach(checksh); + } + skipsh = checksh; + } + } + } + if (tetflaws && (uncheckedtetlist->len() > 0)) { + uncheckedtetlist->sort(); + skiptet = checktet = (triface*)(*uncheckedtetlist)[0]; + if (!isdead(checktet)) { + testtetrahedron(checktet); + } + for (i = 1; i < uncheckedtetlist->len(); i++) { + checktet = (triface*)(*uncheckedtetlist)[i]; + if (checktet->tet != skiptet->tet) { + if (!isdead(checktet)) { + testtetrahedron(checktet); + } + skiptet = checktet; + } + } + } + // Check the two new subsegments to see if they're encroached. + checkedge4encroach(&(encloop->shface)); + senextself(encloop->shface); + spivotself(encloop->shface); + encloop->shface.shver = 0; + checkedge4encroach(&(encloop->shface)); + } + badsegmentdealloc(encloop); + encloop = badsegmenttraverse(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// repairencfaces() Find and repair all the encroached subfaces. // +// // +// Encroached subfaces are repaired by splitting them by inserting a point // +// at or near their circumcenters. However, if the new point would encroach // +// upon any any subsegment, it is not inserted; instead, all the subsegments // +// it would encroach upon are split. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::repairencfaces() +{ + list *neartetlist, *nearseglist; + badface3d* badface; + triface badfacetet; + triface testtet, neighbortet; + triface *checktet, *skiptet; + face testseg; + face *checksh, *skipsh; + point3d borg, bdest, bapex; + point3d newpoint; + enum locateresult intersect; + enum insertsiteresult success; + int encroachedseg; + int errorflag, flipcount; + int i, j; + + neartetlist = nearseglist = NULL; + + while (badfaces.items > 0) { + // Fix one encroached subfaces by inserting a point at its circumcenter. + badface = dequeuebadface(); + assert(badface); + borg = sorg(badface->shface); + bdest = sdest(badface->shface); + bapex = sapex(badface->shface); + // Make sure that this subface is still the same subface when it was + // tested and determined to be encroached. Subsequent transformations + // may have made it a different subface. + if (((borg != (point3d) NULL) && (bdest != (point3d) NULL) && + (bapex != (point3d) NULL)) && ((borg == badface->faceorg) && + (bdest == badface->facedest) && (bapex == badface->faceapex))) { + errorflag = 0; + // Create a new point at the subface's circumcenter. + newpoint = (point3d) points.alloc(); + // Check if we need calculate circumcenter. + if (badface->badfacetet.tet == (tetrahedron *) NULL) { + circumcenter(borg, bdest, bapex, badface->cent); + } + newpoint[0] = badface->cent[0]; + newpoint[1] = badface->cent[1]; + newpoint[2] = badface->cent[2]; + if (issamepoint(newpoint, borg) || issamepoint(newpoint, bdest) + || issamepoint(newpoint, bapex)) { + if (!quiet) { + printf("Warning: Newpoint (%.12g, %.12g, %.12g) falls on", + newpoint[0], newpoint[1], newpoint[2]); + printf(" existing vertex.\n"); + } + errorflag = 1; + pointdealloc(newpoint); + } else { + setpointmark(newpoint, mark(badface->shface)); + if (verbose > 1) { + // Set pointmark of newpoint for Debuging outputs. + setpointmark(newpoint, points.items); + } + // Find a tetrahedron that contain this face. + stpivot(badface->shface, badfacetet); + if (badfacetet.tet == dummytet) { + sesymself(badface->shface); + stpivot(badface->shface, badfacetet); + assert(badfacetet.tet != dummytet); + } + // Find where the newpoint locates(ONFACE or ONEDGE), keep the + // result in 'badfacetet'. + intersect = iscoplanarintri(newpoint, &badfacetet); + if (intersect == OUTSIDE) { + adjustedgering(badfacetet, CCW); + intersect = preciselocate(newpoint, &badfacetet); + } + + if ((intersect == ONFACE) || (intersect == ONEDGE)) { + // If 'newpoint' is located ONFACE or ONEDGE, check if its + // location encroaches on any nearby subsegments. It's a two + // step process: First, gather all nearby subsegments; Second, + // check each subsegment to discover its encroachness. + encroachedseg = 0; + assert(badfacetet.tet != dummytet); + if (!neartetlist) { + neartetlist = new list(sizeof(triface)); + neartetlist->setcomp((compfunc) &compare2tets); + nearseglist = new list(sizeof(face)); + nearseglist->setcomp((compfunc) &compare2shfaces); + } else { + neartetlist->clear(); + nearseglist->clear(); + } + // Step 1, gather all nearby subsegments, store them in list + // 'nearseglist'. + // To address this, Boywer-Watson's scheme (Cavity) is adopted. + // All tetrahedra whose circumspheres contain 'newpoint' are + // found. Check the edges for each tetrahedron, if there exist + // a subsegment abutting this edge, add it to 'nearseglist'. + testtet = badfacetet; + // Check three edges of 'testtet''s current face. + for (i = 0; i < 3; i++) { + tsspivot(&testtet, &testseg); + if (testseg.sh != dummysh) { + nearseglist->append(&testseg); + } + enextself(testtet); + } + neartetlist->append(&testtet); + sym(testtet, neighbortet); + if (neighbortet.tet != dummytet) { + if (iinsphere(&neighbortet, newpoint) == 1) { + neartetlist->append(&neighbortet); + } + } + for (i = 0; i < neartetlist->len(); i++) { + // Get a tetra, which its circumsphere contain 'newpoint'. + testtet = *(triface *)(* neartetlist)[i]; + adjustedgering(testtet, CCW); + // Check other three edges of this tetra. At the same time, + // check other three neighbors of this tetra to see if their + // circumsphere contain 'newpoint'. + for (j = 0; j < 3; j++) { + fnext(testtet, neighbortet); + enextself(neighbortet); + tsspivot(&neighbortet, &testseg); + if (testseg.sh != dummysh) { + if (nearseglist->hasitem(&testseg) == -1) { + nearseglist->append(&testseg); + } + } + symself(neighbortet); + if (neighbortet.tet != dummytet) { + if (iinsphere(&neighbortet, newpoint) == 1) { + if (neartetlist->hasitem(&neighbortet) == -1) { + neartetlist->append(&neighbortet); + } + } + } + enextself(testtet); + } + } + // Step 2, Check each segment to discover if they will be + // encroached by 'newpoint'. + for (i = 0; i < nearseglist->len(); i++) { + testseg = *(face *)(* nearseglist)[i]; + if (checkedge4encroach(&testseg, newpoint)) { + encroachedseg++; + } + } + + if (encroachedseg == 0) { + if (verbose > 1) { + printf(" Insert circumcenter: (%.12g, %.12g, %.12g) %d\n", + newpoint[0], newpoint[1], newpoint[2], + pointmark(newpoint)); + printf(" to split subface (%d, %d, %d).\n", + pointmark(borg), pointmark(bdest), pointmark(bapex)); + } + uncheckedshseglist->clear(); + if (shflaws) { + uncheckedshlist->clear(); + } + if (tetflaws) { + uncheckedtetlist->clear(); + } + if (intersect == ONFACE) { + tspivot(badfacetet, badface->shface); + assert(badface->shface.sh != dummysh); + flipcount = insertonface(newpoint, &badfacetet, + &(badface->shface)); + } else { // must intersect == ONEDGE + flipcount = insertonedge(newpoint, &badfacetet, (face*) NULL); + } + if (verbose > 1) { + printf(" Successfully split subface with %d flips.\n", + flipcount); + } + success = SUCCESSFUL; + } else { + success = VIOLATINGEDGE; + } + } else if (intersect == OUTSIDE) { + success = FAILED; + } else if (intersect == ONVERTEX) { + success = DUPLICATE; + } else { // intersect == INTETRAHEDRON + printf("Precision error in repairencface(): INTETRAHEDRON.\n"); + printf(" Failed to locate a subface to contain newpoint.\n"); + precisionerror(); + } + + if (success == SUCCESSFUL) { + if (steinerleft > 0) { + steinerleft--; + } + // We need check if new insert point cause other subfaces be + // encroached and cause bad quality tetrahedra. + if (shflaws && (uncheckedshlist->len() > 0)) { + uncheckedshlist->sort(); + skipsh = checksh = (face*)(*uncheckedshlist)[0]; + if (!isdead(checksh)) { + checkface4encroach(checksh); + } + for (i = 1; i < uncheckedshlist->len(); i++) { + checksh = (face*)(*uncheckedshlist)[i]; + if (checksh->sh != skipsh->sh) { + if (!isdead(checksh)) { + checkface4encroach(checksh); + } + skipsh = checksh; + } + } + } + if (tetflaws && (uncheckedtetlist->len() > 0)) { + uncheckedtetlist->sort(); + skiptet = checktet = (triface*)(*uncheckedtetlist)[0]; + if (!isdead(checktet)) { + testtetrahedron(checktet); + } + for (i = 1; i < uncheckedtetlist->len(); i++) { + checktet = (triface*)(*uncheckedtetlist)[i]; + if (checktet->tet != skiptet->tet) { + if (!isdead(checktet)) { + testtetrahedron(checktet); + } + skiptet = checktet; + } + } + } + } else if (success == VIOLATINGEDGE) { + // Failed to insert the new point, but some segment was + // marked as being encroached. + pointdealloc(newpoint); + } else if (success == VIOLATINGFACE) { + printf("Internalerror in splitface(): Unexpected condition"); + printf(" success == VIOLATINGFACE.\n"); + internalerror(); + } else if (success == FAILED) { + if (!quiet && verbose) { + // Failed to insert the new point because it fails outside the + // mesh. It's a except condition caused by the existing of + // small angles in input model. + printf("Warning: New point (%.12g, %.12g, %.12g) falls", + newpoint[0], newpoint[1], newpoint[2]); + printf(" outside the mesh.\n"); + } + pointdealloc(newpoint); + } else { // success == DUPLICATE + if (!quiet) { + // Failed to insert the new point because a vertex is already + // there. + printf("Warning: New point (%.12g, %.12g, %.12g) falls on", + newpoint[0], newpoint[1], newpoint[2]); + printf(" existing vertex.\n"); + } + pointdealloc(newpoint); + errorflag = 1; + } + } + if (errorflag) { + if (verbose) { + printf(" The new point is at the circumcenter of subface.\n"); + printf(" (%.12g, %.12g, %.12g) (%.12g, %.12g, %.12g)", + borg[0], borg[1], borg[2], bdest[0], bdest[1], bdest[2]); + printf(" (%.12g, %.12g, %.12g)\n", bapex[0], bapex[1], bapex[2]); + } + printf(" This probably means that I am trying to refine subfaces\n"); + printf(" to a smaller size than can be accommodated by the finite\n"); + printf(" precision of floating point arithmetic.\n"); + precisionerror(); + } + } + // Return the badface to the pool. + badfaces.dealloc((void *) badface); + // Fix any encroached segments that may have resulted. + if (badsegments.items > 0) { + repairencsegs(); + } + } + + if (neartetlist) { + delete neartetlist; + delete nearseglist; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// splittet() Inserts a point at the circumsphere of a bad quality // +// tetrahedron, thus eliminating the tetrahedron. // +// // +// If the new point would encroach upon any subsegment or subfacet, then it // +// is not inserted; instead, all the subsegments it would encroach upon are // +// split. If the bad quality tetrahedron is not eliminated as a result, then // +// all the subfacets its circumcenter would encroach upon are split. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::splittet(badtet* intet) +{ + triface searchtet; + triface testtet, neighbortet; + triface *checktet, *skiptet; + face testsh, testseg; + face *checksh, *skipsh; + point3d borg, bdest, bapex, boppo; + point3d newpoint; + enum locateresult intersect; + enum insertsiteresult success; + int encroachedseg, encroachedface; + int errorflag, flipcount; + int i, j; + + borg = org(intet->btet); + bdest = dest(intet->btet); + bapex = apex(intet->btet); + boppo = oppo(intet->btet); + // Make sure that this tet is still the same tet it was when it was tested + // and determined to be encroached. Subsequent transformations may have + // made it a different tet. + if (((borg != (point3d) NULL) && (bdest != (point3d) NULL) && + (bapex != (point3d) NULL) && (boppo != (point3d) NULL)) && + ((borg == intet->tetorg) && (bdest == intet->tetdest) && + (bapex == intet->tetapex) && (boppo == intet->tetoppo))) { + errorflag = 0; + // Create a new point at the tetrahedron's circumsphere. + newpoint = (point3d) points.alloc(); + newpoint[0] = intet->cent[0]; + newpoint[1] = intet->cent[1]; + newpoint[2] = intet->cent[2]; + if (issamepoint(newpoint, borg) || issamepoint(newpoint, bdest) + || issamepoint(newpoint, bapex) || issamepoint(newpoint, boppo)) { + if (!quiet) { + printf("Warning: Newpoint (%.12g, %.12g, %.12g) falls on", + newpoint[0], newpoint[1], newpoint[2]); + printf(" existing vertex.\n"); + } + pointdealloc(newpoint); + errorflag = 1; + } else { + // The new point must be in mesh, set a marker of zero. + setpointmark(newpoint, 0); + if (verbose > 1) { + // Set pointmark of newpoint for Debuging outputs. + setpointmark(newpoint, points.items); + } + // Ensure that the circumcenter must above 'intet->btet', so point + // location will work. + searchtet = intet->btet; + searchtet.ver = 0; + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + if (isaboveplane(&(searchtet), newpoint)) break; + } + assert(searchtet.loc < 4); + // Find where the newpoint locates, keep the result in 'searchtet'. + intersect = preciselocate(newpoint, &searchtet); + + if ((intersect == INTETRAHEDRON) || (intersect == ONFACE) + || (intersect == ONEDGE)) { + // If 'newpoint' is located inside current mesh, insert the point + // to split tetrahedron. Before insertion, check if its location + // encroaches on any nearby subsegments or subfaces. Use Boywer- + // Watson insertion scheme for encroach checking. + encroachedseg = encroachedface = 0; + cavetetlist->clear(); + caveshlist->clear(); + cavebdfacelist->clear(); + + // Collect all tetra to form the cavity. At the same time, collect + // the boundary faces of this cavity and all encountered subfaces. + testtet = searchtet; + cavetetlist->append(&testtet); + tspivot(testtet, testsh); + if ((testsh.sh != dummysh) && !isnonsolid(testsh)) { + caveshlist->append(&testsh); + adjustedgering(testtet, CCW); + cavebdfacelist->append(&testtet); + } else { + sym(testtet, neighbortet); + assert(neighbortet.tet != dummytet); + if (iinsphere(&neighbortet, newpoint) == 1) { + cavetetlist->append(&neighbortet); + } else { + adjustedgering(testtet, CCW); + cavebdfacelist->append(&testtet); + } + } + for (i = 0; i < cavetetlist->len(); i++) { + // Get a tetra, which its circumsphere contain 'newpoint'. + testtet = *(triface *)(* cavetetlist)[i]; + adjustedgering(testtet, CCW); + // Check other three neighbors of this tet. + for (j = 0; j < 3; j++) { + fnext(testtet, neighbortet); + tspivot(neighbortet, testsh); + if ((testsh.sh != dummysh) && !isnonsolid(testsh)) { + caveshlist->append(&testsh); + adjustedgering(neighbortet, CCW); + cavebdfacelist->append(&neighbortet); + } else { + symself(neighbortet); + assert(neighbortet.tet != dummytet); + if (iinsphere(&neighbortet, newpoint) == 1) { + if (cavetetlist->hasitem(&neighbortet) == -1) { + cavetetlist->append(&neighbortet); + } + } else { + symself(neighbortet); + adjustedgering(neighbortet, CCW); + cavebdfacelist->append(&neighbortet); + } + } + enextself(testtet); + } + } + // Check for any encroached subsegments first. + for (i = 0; i < caveshlist->len(); i++) { + testsh = *(face *)(* caveshlist)[i]; + for (j = 0; j < 3; j++) { + sspivot(testsh, testseg); + if (testseg.sh != dummysh) { + if (checkedge4encroach(&testseg, newpoint)) { + encroachedseg++; + } + } + senextself(testsh); + } + } + if (encroachedseg) { + success = VIOLATINGEDGE; + } else { + // If no subsegment be encroached, then check subfaces. + for (i = 0; i < caveshlist->len(); i++) { + testsh = *(face *)(* caveshlist)[i]; + if (checkface4encroach(&testsh, newpoint)) { + encroachedface++; + } + } + if (encroachedface) { + success = VIOLATINGFACE; + } + } + if ((!encroachedseg) && (!encroachedface)) { + if (cavetetlist->hasitem(&(intet->btet)) != -1) { + if (verbose > 1) { + printf(" Insert circumcenter: (%.12g, %.12g, %.12g) %d\n", + newpoint[0], newpoint[1], newpoint[2], + pointmark(newpoint)); + printf(" to split tetra (%d, %d, %d, %d).\n", + pointmark(borg), pointmark(bdest), pointmark(bapex), + pointmark(boppo)); + } + uncheckedshseglist->clear(); + uncheckedshlist->clear(); + uncheckedtetlist->clear(); + if (intersect == INTETRAHEDRON) { + flipcount = insertininterior(newpoint, &searchtet); + } else if (intersect == ONFACE) { + tspivot(searchtet, testsh); + if (testsh.sh != dummysh) { + // Note: 'testsh' must be an nonsolid subface. + assert(isnonsolid(testsh)); + flipcount = insertonface(newpoint, &searchtet, &testsh); + } else { + flipcount = insertonface(newpoint, &searchtet, (face*) NULL); + } + } else { // must intersect == ONEDGE + // Note: 'newpoint' must locate on a interior edge. + flipcount = insertonedge(newpoint, &searchtet, (face*) NULL); + } + if (verbose > 1) { + printf(" Successfully split tetra with %d flips.\n", flipcount); + } + success = SUCCESSFUL; + } else { + // Do not insert 'newpoint', because it can't eliminate bad tet. + if (!quiet && verbose) { + printf("Warning: A bad-quality tet survived.\n"); + } + success = VIOLATINGEDGE; + } + } + } else if (intersect == ONVERTEX) { + success = DUPLICATE; + } else { // intersect == OUTSIDE + success = FAILED; + } + + if (success == SUCCESSFUL) { + if (steinerleft > 0) { + steinerleft--; + } + // Check and queue the bad quality tetrahedra caused by inserting + // 'newpoint'. + if (uncheckedtetlist->len() > 0) { + uncheckedtetlist->sort(); + skiptet = checktet = (triface*)(*uncheckedtetlist)[0]; + if (!isdead(checktet)) { + testtetrahedron(checktet); + } + for (i = 1; i < uncheckedtetlist->len(); i++) { + checktet = (triface*)(*uncheckedtetlist)[i]; + if (checktet->tet != skiptet->tet) { + if (!isdead(checktet)) { + testtetrahedron(checktet); + } + skiptet = checktet; + } + } + } + } else if ((success == VIOLATINGEDGE) || (success == VIOLATINGFACE)) { + // Failed to insert the new point, but some segment and subfaces + // was marked as being encroached. + pointdealloc(newpoint); + } else if (success == FAILED) { + if (!quiet && verbose) { + // Failed to insert the new point because it fails outside the + // mesh. It's an except condition caused by the existing of + // small angles in input model. + printf("Warning: New point (%.12g, %.12g, %.12g) falls outside", + newpoint[0], newpoint[1], newpoint[2]); + printf(" the mesh.\n"); + } + pointdealloc(newpoint); + } else { // success == DUPLICATE + if (!quiet) { + // Failed to insert the new point because a vertex is already there. + printf("Warning: New point (%.12g, %.12g, %.12g) falls on", + newpoint[0], newpoint[1], newpoint[2]); + printf(" existing vertex.\n"); + } + pointdealloc(newpoint); + errorflag = 1; + } + } + if (errorflag) { + if (verbose) { + printf(" The new point is at the circumsphere of tetrahedron:\n"); + printf(" (%.12g, %.12g, %.12g) (%.12g, %.12g, %.12g)\n", + borg[0], borg[1], borg[2], bdest[0], bdest[1], bdest[2]); + printf(" (%.12g, %.12g, %.12g) (%.12g, %.12g, %.12g)\n", + bapex[0], bapex[1], bapex[2], boppo[0], boppo[1], boppo[2]); + } + printf(" This probably means that I am trying to refine tetrahedra\n"); + printf(" to a smaller size than can be accommodated by the finite\n"); + printf(" precision of floating point arithmetic.\n"); + precisionerror(); + } + } + // Return the badtet to the pool. + badtets.dealloc((void *) intet); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// enforcequality() Remove all the encroached edges and bad triangles // +// from the triangulation. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::enforcequality() +{ + int i; + + if (!quiet) { + printf("Adding Steiner points to enforce quality.\n"); + } + + // Initialize the pool of encroached segments. + badsegments.init(sizeof(badface3d), BADSHELLFACEPERBLOCK, POINTER, 0); + // Initialize the queue of subsegments which might be encroached. + uncheckedshseglist = new list(sizeof(face)); + uncheckedshseglist->setcomp((compfunc) &compare2shfaces); + if (verbose) { + printf(" Looking for encroached segments.\n"); + } + // Test all segments to see if they're encroached. + tallyencsegs(); + // Note that steinerleft == -1 if an unlimited number + // of Steiner points is allowed. + while ((badsegments.items > 0) && (steinerleft != 0)) { + if (verbose > 1) { + printf(" Splitting %d encroached segments.\n", badsegments.items); + } + // Fix the segments without noting newly encroached segments or + // subfaces. + repairencsegs(); + // Now, find all the segments that became encroached while adding + // points to split encroached segments. + tallyencsegs(); + } + + // Initialize the pool of encroached subfaces. + badfaces.init(sizeof(badface3d), BADSHELLFACEPERBLOCK, POINTER, 0); + // Initialize the queue of subfaces which might be encroached. + uncheckedshlist = new list(sizeof(face)); + uncheckedshlist->setcomp((compfunc) &compare2shfaces); + // Initialize the queues of bad subfaces. + for (i = 0; i < 2; i++) { + facequefront[i] = (badface3d *) NULL; + facequetail[i] = &facequefront[i]; + } + if (verbose) { + printf(" Looking for encroached subfaces.\n"); + } + // Test all subfaces to see if they're encroached. + tallyencfaces(); + shsegflaws = 1; + // Note that steinerleft == -1 if an unlimited number + // of Steiner points is allowed. + while ((badfaces.items > 0) && (steinerleft != 0)) { + if (verbose > 1) { + printf(" Splitting %d encroached subfaces.\n", badfaces.items); + } + // Fix the subfaces without noting newly encroached subfaces. + repairencfaces(); + // Now, find all the subfaces that became encroached while adding + // points to split encroached subfaces. + tallyencfaces(); + } + // At this point, if we haven't run out of Steiner points, the + // triangulation should be (conforming) Delaunay. + + // Next, we worry about enforcing tetrahedra quality. + if ((minratio > 0.0) || varvolume || fixedvolume) { + // Initialize the pool of bad tetrahedra. + badtets.init(sizeof(badtet), BADTETPERBLOCK, POINTER, 0); + // Initialize the queue of tetrahedra which might have bad quality. + uncheckedtetlist = new list(sizeof(triface)); + uncheckedtetlist->setcomp((compfunc) &compare2tets); + // Initialize lists for Boywer-Watson point insertion scheme. + cavetetlist = new list(sizeof(triface)); + cavetetlist->setcomp((compfunc) &compare2tets); + caveshlist = new list(sizeof(face)); + caveshlist->setcomp((compfunc) &compare2shfaces); + cavebdfacelist = new list(sizeof(triface)); + cavebdfacelist->setcomp((compfunc) &compare2tets); + // Initialize the queues of bad tetrahedra. + for (i = 0; i < 64; i++) { + tetquefront[i] = (badtet *) NULL; + tetquetail[i] = &tetquefront[i]; + } + // Test all tetrahedra to see if they're bad. + tallytets(); + if (verbose) { + printf(" Splitting bad tetrahedra.\n"); + } + shflaws = shsegflaws = tetflaws = 1; + while ((badtets.items > 0) && (steinerleft != 0)) { + // Fix one bad tetrahedron by inserting a point at its circumsphere. + splittet(dequeuebadtet()); + // Fix any encroached segments and subfaces that may have resulted. + // Record any new bad tetrahedra or encroached segments, subfaces + // that result. + if (badsegments.items > 0) { + repairencsegs(); + } + if (badfaces.items > 0) { + repairencfaces(); + } + } + delete cavetetlist; + delete caveshlist; + delete cavebdfacelist; + delete uncheckedtetlist; + } + delete uncheckedshseglist; + delete uncheckedshlist; + + // At this point, if we haven't run out of Steiner points, the + // triangulation should be (conforming) Delaunay and have no + // low-quality tetrahedra except slivers. + + // Might we have run out of Steiner points too soon? + if (!quiet && ((badsegments.items > 0) || (badfaces.items > 0)) + && (steinerleft == 0)) { + printf("\nWarning: I ran out of Steiner points, but the mesh has\n"); + printf(" %ld encroached segments and %ld encroached subfaces,\n", + badsegments.items, badfaces.items); + printf(" therefore might not be truly Delaunay.\n"); + printf(" If the Delaunay property is important to you, try increasing\n"); + printf(" the number of Steiner points (controlled by the -S switch)\n"); + printf(" slightly and try again.\n\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Mesh quality maintenance routines // +/////////////////////////////////////////////////////////////////////////////// diff --git a/Tetgen/tetlib.cpp b/Tetgen/tetlib.cpp new file mode 100644 index 0000000000..19e74aabde --- /dev/null +++ b/Tetgen/tetlib.cpp @@ -0,0 +1,10847 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// tetlib.cpp The implementation file for class mesh3d. Include the mesh // +// data structure and the kernel member functions. // +// // +// Tetgen Version 1.0 beta // +// November, 2001 // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "tetlib.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// Initialize all the lookup tables used by mesh manipulation primitives. // // +// // +// The internal representation of the tetrahedron-based and triangle-edge // +// data structures takes advantage of the predefined numbering scheme for // +// vertices and faces of a tetrahedron, also takes advantage of that the // +// structure of two edge rings of each face is identical. Therefore, it can // +// be implemented by several global lookup tables as following. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// For enext() primitive, use 'ver' as index. +int mesh3d::ve[6] = { 2, 5, 4, 1, 0, 3 }; + +// For org(), dest() and apex() primitives, use 'ver' as index. +int mesh3d::vo[6] = { 0, 1, 1, 2, 2, 0 }; +int mesh3d::vd[6] = { 1, 0, 2, 1, 0, 2 }; +int mesh3d::va[6] = { 2, 2, 0, 0, 1, 1 }; + +// For org(), dest() and apex() primitives, use 'loc' as first index and +// 'ver' as second index. +int mesh3d::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 mesh3d::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 mesh3d::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 index. +int mesh3d::loc2oppo[4] = { 3, 2, 0, 1 }; + +// For fnext() primitives, use 'loc' as first index and 'ver' as second +// index, return a new 'loc' and new 'ver' in an int array. +// Note: Only valid for face version = 0, 2, 4. +int mesh3d::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} } }; + +int mesh3d::plus1mod3[3] = {1, 2, 0}; +int mesh3d::minus1mod3[3] = {2, 0, 1}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh data type linar-order functions used in list and link. Refer to // +// file linklist.h for the description about linar-order functions. // +// // +// compare2points() Compare two points by their x-coordinate, using the // +// y-coordinate as a secondary key, and the z-coordinate // +// if their need. // +// compare2tets() Compare two handles of tetrahedra by their addresses. // +// compare2shfaces() Compare two handles of subfaces/subsegments by their // +// addresses. // +// // +// Return 0 if they are same. Return 1 or -1 if they are not same. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int compare2points(point3d *point1, point3d *point2) +{ + if ((*point1)[0] < (*point2)[0]) { + return -1; + } else if ((*point1)[0] > (*point2)[0]) { + return +1; + } else { + if ((*point1)[1] < (*point2)[1]) { + return -1; + } else if ((*point1)[1] > (*point2)[1]) { + return +1; + } else { + if ((*point1)[2] < (*point2)[2]) { + return -1; + } else if ((*point1)[2] > (*point2)[2]) { + return +1; + } else { + return 0; + } + } + } +} + +int compare2tets(triface* tface1, triface* tface2) +{ + if (tface1->tet == tface2->tet) { + return 0; // These two tets are same. + } else if (tface1->tet < tface2->tet) { + return -1; + } else { + return 1; + } +} + +int compare2shfaces(face* sface1, face* sface2) +{ + if (sface1->sh == sface2->sh) { + return 0; // These two subfaces/segments are same. + } else if (sface1->sh < sface2->sh) { + return -1; + } else { + return 1; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Advanced Mesh Manipulaton Primitives // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// getnextface() Get next face of 'tface1' in the same face ring. // +// // +// The right-hand rule is used to determine where the next face is. // +// // +// The next face is returned in 'tface2' or 'tface1'(if tface2 == NULL). // // +// If next face exists, return true. Otherwise, (it's a outer boundary face),// +// return false, and the return handle remain unchanged. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool mesh3d::getnextface(triface* tface1, triface* tface2) +{ + // First we need decide where the next face in the face ring locate: + // in this tet('tface1') or in it's neigbhour? This can do quickly by + // checking which edge ring of the 'tface1' version belong to. + if (EdgeRing(tface1->ver) == CW) { + // Indicate the next face is in the neigbhour tet. + if (!issymexist(tface1)) { + // Encounter Outer Space when spin face around edge. return. + return false; + } + + point3d torg, tdest; + + org(*tface1, torg); + dest(*tface1, tdest); + if (tface2) { + sym(*tface1, *tface2); + findversion(tface2, torg, tdest, 0); // 0 mean don't reverse direction. + } else { + symself(*tface1); + findversion(tface1, torg, tdest, 0); + } + } else { + // Indicate the next face is still in this tet('tface1'). + if (tface2) { + *tface2 = *tface1; + } + } + // At here, we know the next face must in retface.tet. + // assert(EdgeRing(tface2->ver) == CCW); + int tloc, tver; + + if (tface2) { + tloc = tface2->loc; tver = tface2->ver; + tface2->loc = locver2nextf[tloc][tver][0]; + tface2->ver = locver2nextf[tloc][tver][1]; + } else { + tloc = tface1->loc; tver = tface1->ver; + tface1->loc = locver2nextf[tloc][tver][0]; + tface1->ver = locver2nextf[tloc][tver][1]; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tsspivot() Finds a subsegment abutting on this tetrahderon's edge. // +// // +// To find if there has a subsegment adjoining to this tetrahedron's edge, // +// first we need find a subface around this edge to see if there exist a // +// subsegment adjoining to it. Why? Because There are no pointers directly // +// connecting tetrahedron and adjoining subsegments. Connections between // +// tetrahedron and subsegments are entirely mediate througth subfaces. // +// Because a subsegment may be shared by any number of subfaces and // +// tetrahedra, each subsegment has a pointer to only one adjoining subfaces // +// (choose arbitrarly). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::tsspivot(triface* tface, face* tseg) +{ + triface spintet; + face tmpface; + point3d tapex; + int hitbdry; + + spintet = *tface; + apex(*tface, tapex); + hitbdry = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == tapex) { + break; // Rewind, can leave now. + } + tspivot(spintet, tmpface); + if (tmpface.sh != dummysh) { + findversion(&tmpface, tface); + sspivot(tmpface, *tseg); + return; + } + } else { + hitbdry ++; + if(hitbdry >= 2) { + break; + } else { + esym(*tface, spintet); + } + } + } + // Check myself last. + tspivot(*tface, tmpface); + if (tmpface.sh != dummysh) { + findversion(&tmpface, tface); + sspivot(tmpface, *tseg); + return; + } + // Not find a subsegment. + tseg->sh = dummysh; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// sstpivot() Finds a tetrahedron abutting a subsegment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::sstpivot(face* tseg, triface* tface) +{ + face parentface; + + // Get the subface which contains the segment. + sdecode(tseg->sh[0], parentface); + assert(parentface.sh != dummysh); + // Get the tetrahera which the subface attches to. + stpivot(parentface, *tface); + if (tface->tet == dummytet) { + sesymself(parentface); + stpivot(parentface, *tface); + assert(tface->tet != dummytet); + } + findversion(tface, tseg, 0); +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Advanced Mesh Manipulaton Primitives // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Auxiliary Mesh Manipulaton Primitives // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// findversion() Find and set version to indicate the directed edge in // +// the input handle('tface' or 'sface'), which its origin // +// is 'dorg' and destination is 'ddest'. // +// // +// If 'symflag' = 1, inverse the edge direction(esymself) before return. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d:: +findversion(triface* tface, point3d dorg, point3d ddest, int symflag) +{ + point3d torg, tdest; + + tface->ver = 0; + while (tface->ver < 6) { + org(*tface, torg); + dest(*tface, tdest); + if ((torg == dorg) && (tdest == ddest)) { + break; + } else if ((torg == ddest) && (tdest == dorg)) { + tface->ver++; + break; + } + tface->ver += 2; + } + assert(tface->ver < 6); + if (symflag) { + esymself(*tface); + } +} + +void mesh3d::findversion(triface* tface, triface* dface, int symflag) +{ + findversion(tface, org(*dface), dest(*dface), symflag); +} + +void mesh3d::findversion(triface* tface, face* sface, int symflag) +{ + findversion(tface, sorg(*sface), sdest(*sface), symflag); +} + +void mesh3d:: +findversion(face* sface, point3d dorg, point3d ddest, int symflag) +{ + point3d shorg, shdest; + + sface->shver = 0; + while (sface->shver < 6) { + sorg(*sface, shorg); + sdest(*sface, shdest); + if ((shorg == dorg) && (shdest == ddest)) { + break; + } else if ((shorg == ddest) && (shdest == dorg)) { + sface->shver++; + break; + } + sface->shver += 2; + } + assert(sface->shver < 6); + if (symflag) { + sesymself(*sface); + } +} + +void mesh3d::findversion(face* sface, triface* dface, int symflag) +{ + findversion(sface, org(*dface), dest(*dface), symflag); +} + +void mesh3d::findversion(face* sface, face* sface1, int symflag) +{ + findversion(sface, sorg(*sface1), sdest(*sface1), symflag); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// findorg() Find and set the version to indicate the desired point. // +// // +// If 'dorg' is a one of vertices of 'tface', set the origin of 'tface' be // +// 'dorg' and return true, else return false. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool mesh3d::findorg(triface* tface, point3d dorg) +{ + if (org(*tface) == dorg) { + return true; + } + if (dest(*tface) == dorg) { + enextself(*tface); + } else { + if (apex(*tface) == dorg) { + enext2self(*tface); + } else { + if (oppo(*tface) == dorg) { + // Keep in the same tet after fnext(). + adjustedgering(*tface, CCW); + fnextself(*tface); + enext2self(*tface); + } else { + return false; + } + } + } + return true; +} + +bool mesh3d::findorg(face* sface, point3d dorg) +{ + point3d pointptr; + + sorg(*sface, pointptr); + if (pointptr == dorg) { + return true; + } + sdest(*sface, pointptr); + if (pointptr == dorg) { + senextself(*sface); + } else { + sapex(*sface, pointptr); + if (pointptr == dorg) { + senext2self(*sface); + } else { + return false; + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// isridge() Returns true if the edge of tetrahedron(specified by loc and // +// ver) is a ridge in the mesh. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool mesh3d::isridge(triface* tface) +{ + triface testtet; + point3d tapex; + + // Spin current edge(loc and ver) to see if there will encounter + // 'Outer boundary' (dummytet). + if (!fnext(*tface, testtet)) { + return true; + } + + apex(*tface, tapex); + do { + if(!fnextself(testtet)) { + return true; + } + } while (apex(testtet) != tapex); + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// isexistincidentseg() Test if there exist any segment shares with input // +// segment's origin. return true if there exists at // +// least one incident segment, otherwise return // +// false. // +// // +// This primitive is used to help decide where to split a segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool mesh3d::isexistincidentseg(face* tseg) +{ + triface neighbortet, spintet, testtet; + face testseg; + point3d tapex; + int findsegflag, reverseflag, hitbdry; + + // Get a tet that contain this segment with the same origin and + // destination of this segment. + sstpivot(tseg, &neighbortet); + // Spin around the tet, skip myself(tseg). + apex(neighbortet, tapex); + spintet = neighbortet; + hitbdry = reverseflag = findsegflag = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == tapex) { + break; // Rewind, can leave now. + } + // Get the correct edge. Note the 'reverseflag'. + if (!reverseflag) { + enext2(spintet, testtet); + } else { + enext(spintet, testtet); + } + tsspivot(&testtet, &testseg); + if (testseg.sh != dummysh) { + findsegflag = 1; + break; + } + } else { + hitbdry ++; + if(hitbdry >= 2) { + break; + } else { + reverseflag = 1; + esym(neighbortet, spintet); + } + } + } + if (!findsegflag) { + enext2(neighbortet, testtet); + tsspivot(&testtet, &testseg); + if (testseg.sh != dummysh) { + findsegflag = 1; + } + } + return findsegflag == 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// issameface(), issameedge() Compare the vertices of two faces/edges to // +// see if they are the same face/edge. The // +// inputs are handles of two tetrahedra. // +// Return 0 (same) or 1 (diffrent). // +// // +// These two primitives mainly used as list or link's linear order functions.// +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::issameface(triface* tface1, triface* tface2) +{ + point3d torg1, tdest1, tapex1; + point3d torg2, tdest2, tapex2; + int oldver, tmpver; + + org(*tface1, torg1); + dest(*tface1, tdest1); + apex(*tface1, tapex1); + + oldver = tface2->ver; + tmpver = 0; + while (tmpver < 6) { + tface2->ver = tmpver; + org(*tface2, torg2); + dest(*tface2, tdest2); + apex(*tface2, tapex2); + if (((torg1 == torg2) && (tdest1 == tdest2) && (tapex1 == tapex2)) + || ((torg1 == tdest2) && (tdest1 == torg2) && (tapex1 == tapex2))) { + break; + } + tmpver += 2; + } + + tface2->ver = oldver; + if (tmpver < 6) { + return 0; // These two faces are same. + } else { + return 1; + } +} + +int mesh3d::issameedge(triface* tface1, triface* tface2) +{ + point3d torg1, tdest1; + point3d torg2, tdest2; + + org(*tface1, torg1); + dest(*tface1, tdest1); + org(*tface2, torg2); + dest(*tface2, tdest2); + + if (((torg1 == torg2) && (tdest1 == tdest2)) + || ((torg1 == tdest2) && (tdest1 == torg2))) { + return 0; // These two edges are same. + } else { + return 1; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dump() For debuging, Print out the details of a tetrahedron/shellfacet // +// handle to standard output. // +// // +// It can be called directly from the debugger, and presents information // +// about a tetrahedron handle in digestible form. It's also used when the // +// highest level of verbosity (`-VVV') is specified. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::dump(triface* tface) +{ + triface tmpface, prtface; + point3d tmppt; + face tmpsh; + int facecount; + + printf("Tetra x%lx with loc(%i) and ver(%i):\n", + (unsigned long)(tface->tet), tface->loc, tface->ver); + + tmpface = *tface; + facecount = 0; + while(facecount < 4) { + tmpface.loc = facecount; + sym(tmpface, prtface); + if(prtface.tet == dummytet) { + printf(" [%i] Outer space.\n", facecount); + } else { + printf(" [%i] x%lx loc(%i).\n", facecount, + (unsigned long)(prtface.tet), prtface.loc); + } + facecount ++; + } + + org(*tface, tmppt); + if(tmppt == (point3d) NULL) { + printf(" Org [%i] NULL\n", locver2org[tface->loc][tface->ver]); + } else { + printf(" Org [%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2org[tface->loc][tface->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + dest(*tface, tmppt); + if(tmppt == (point3d) NULL) { + printf(" Dest[%i] NULL\n", locver2dest[tface->loc][tface->ver]); + } else { + printf(" Dest[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2dest[tface->loc][tface->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + apex(*tface, tmppt); + if(tmppt == (point3d) NULL) { + printf(" Apex[%i] NULL\n", locver2apex[tface->loc][tface->ver]); + } else { + printf(" Apex[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + locver2apex[tface->loc][tface->ver], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + oppo(*tface, tmppt); + if(tmppt == (point3d) NULL) { + printf(" Oppo[%i] NULL\n", loc2oppo[tface->loc]); + } else { + printf(" Oppo[%i] x%lx (%.12g,%.12g,%.12g) %d\n", + loc2oppo[tface->loc], (unsigned long)(tmppt), + tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt)); + } + + if (useshelles) { + tmpface = *tface; + facecount = 0; + while(facecount < 4) { + tmpface.loc = facecount; + tspivot(tmpface, tmpsh); + if(tmpsh.sh != dummysh) { + printf(" [%i] x%lx ver(%i).\n", facecount, + (unsigned long)(tmpsh.sh), tmpsh.shver); + } + facecount ++; + } + } +} + +void mesh3d::dump(face* sface) +{ + face printsh; + triface printtet; + point3d tapex, printpoint; + + sapex(*sface, tapex); + if (tapex != NULL) { + printf("subface x%lx with version %d and mark %d:\n", + (unsigned long)(sface->sh), sface->shver, mark(*sface)); + } else { + printf("Subsegment x%lx with version %d and mark %d:\n", + (unsigned long)(sface->sh), sface->shver, mark(*sface)); + } + + if (tapex != NULL) { + // Current we don't use the first three pointers in subface, + // so their values are always NULL. Skipped. + /*sdecode(sface->sh[0], printsh); + if (printsh.sh == dummysh) { + printf(" [0] = No shell\n"); + } else { + printf(" [0] = x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + } + sdecode(sface->sh[1], printsh); + if (printsh.sh == dummysh) { + printf(" [1] = No shell\n"); + } else { + printf(" [1] = x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + } + sdecode(sface->sh[2], printsh); + if (printsh.sh == dummysh) { + printf(" [2] = No shell\n"); + } else { + printf(" [2] = x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + }*/ + } else { + sdecode(sface->sh[0], printsh); + if (printsh.sh == dummysh) { + printf(" [0] = No shell\n"); + } else { + printf(" [0] = x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + } + sdecode(sface->sh[1], printsh); + if (printsh.sh != dummysh) { + printf(" [1] = x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + } + sdecode(sface->sh[2], printsh); + if (printsh.sh != dummysh) { + printf(" [2] = x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + } + } + + sorg(*sface, printpoint); + if (printpoint == (point3d) NULL) + printf(" Org [%d] = NULL\n", vo[sface->shver]); + else + printf(" Org [%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + vo[sface->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + sdest(*sface, printpoint); + if (printpoint == (point3d) NULL) + printf(" Dest[%d] = NULL\n", vd[sface->shver]); + else + printf(" Dest[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + vd[sface->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + + if (tapex != NULL) { + sapex(*sface, printpoint); + if (printpoint == (point3d) NULL) + printf(" Apex[%d] = NULL\n", va[sface->shver]); + else + printf(" Apex[%d] = x%lx (%.12g,%.12g,%.12g) %d\n", + va[sface->shver], (unsigned long)(printpoint), printpoint[0], + printpoint[1], printpoint[2], pointmark(printpoint)); + + decode(sface->sh[6], printtet); + if (printtet.tet == dummytet) { + printf(" [6] = Outer space\n"); + } else { + printf(" [6] = x%lx %d\n", + (unsigned long)(printtet.tet), printtet.loc); + } + decode(sface->sh[7], printtet); + if (printtet.tet == dummytet) { + printf(" [7] = Outer space\n"); + } else { + printf(" [7] = x%lx %d\n", + (unsigned long)(printtet.tet), printtet.loc); + } + + sdecode(sface->sh[8], printsh); + if (printsh.sh == dummysh) { + printf(" [8] = No subsegment\n"); + } else { + printf(" [8] = x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + } + sdecode(sface->sh[9], printsh); + if (printsh.sh == dummysh) { + printf(" [9] = No subsegment\n"); + } else { + printf(" [9] = x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + } + sdecode(sface->sh[10], printsh); + if (printsh.sh == dummysh) { + printf(" [10]= No subsegment\n"); + } else { + printf(" [10]= x%lx %d\n", + (unsigned long)(printsh.sh), printsh.shver); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Auxiliary Mesh Manipulaton Primitives // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Memory Management Routines // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 mesh3d::dummyinit(int ttetwords, int tshwords) +{ + unsigned long alignptr; + + // 'tetwords' and 'shwords' are used by the mesh manipulation primitives + // to extract orientations of tetrahedra and subfaces from pointers. + tetwords = ttetwords; // Initialize `tetwords' once and for all. + shwords = tshwords; // Initialize `shwords' once and for all. + + // Set up 'dummytet', the 'tetrahedron' that occupies "outer space". + dummytetbase = (tetrahedron *) new BYTE[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 tetrahedrons 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 (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 BYTE[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; + // Set the boundary marker to zero. + * (int *) (dummysh + 11) = 0; + // Initialize the four adjoining subfaces of 'dummytet' to be the + // omnipresent subface. + dummytet[8 ] = (tetrahedron) dummysh; + dummytet[9 ] = (tetrahedron) dummysh; + dummytet[10] = (tetrahedron) dummysh; + dummytet[11] = (tetrahedron) dummysh; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// initializepointpool() Calculate the size of the point data structure // +// and initialize its memory pool. // +// // +// This routine also computes the 'pointmarkindex' and `point2tetindex' // +// indices used to find values within each point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::initializepointpool() +{ + int pointsize; + + // 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 = ((3 + nextras) * sizeof(REAL) + sizeof(int) - 1) + / sizeof(int); + pointsize = (pointmarkindex + 1) * sizeof(int); + if (poly || smesh) { + // The index within each point at which a tetrahedron pointer is found. + // Ensure the pointer is aligned to a sizeof(tetrahedron)-byte address. + point2tetindex = (pointsize + sizeof(tetrahedron) - 1) + / sizeof(tetrahedron); + pointsize = (point2tetindex + 1) * sizeof(tetrahedron); + } + // Initialize the pool of points. + points.init(pointsize, POINTPERBLOCK, + (sizeof(REAL) >= sizeof(tetrahedron)) ? FLOATINGPOINT : POINTER, 4); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// initializetetshpools() Calculate the sizes of the tetrahedron and // +// subface data structures and initialize their // +// memory pools. // +// // +// This routine also computes the 'highorderindex', 'elemattribindex', and // +// 'volumeboundindex' indices used to find values within each tetrahedron. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::initializetetshpools() +{ + int tetsize; + + // There are four pointers to other tetrahedra, four pointers to + // corners, and possibly four pointers to subfaces. + highorderindex = 8 + (useshelles * 4); + // The number of bytes occupied by a tetrahedron. + tetsize = highorderindex * sizeof(tetrahedron); + // The index within each tetrahedron at which its attributes are found, + // where the index is measured in REALs. + elemattribindex = (tetsize + sizeof(REAL) - 1) / sizeof(REAL); + // The index within each tetrahedron at which the maximum volume + // constraint is found, where the index is measured in REALs. Note that + // if the 'regionattrib' flag is set, an additional attribute will be + // added. + volumeboundindex = elemattribindex + eextras + regionattrib; + // If tetrahedron attributes or a volume bound are needed, increase the + // number of bytes occupied by a tetrahedron. + if (varvolume) { + tetsize = (volumeboundindex + 1) * sizeof(REAL); + } else if (eextras + regionattrib > 0) { + tetsize = volumeboundindex * sizeof(REAL); + } + // If a Voronoi diagram or tetrahedron neighbor graph is requested, make + // sure there's room to store an integer index in each tetrahedron. This + // integer index can occupy the same space as the subfaces or attributes + // or volume constraint or extra nodes. + if ((voronoi || neighbors) && + (tetsize < 8 * sizeof(tetrahedron) + sizeof(int))) { + tetsize = 8 * sizeof(tetrahedron) + sizeof(int); + } + // Having determined the memory size of a tetrahedron, initialize the pool. + tetrahedrons.init(tetsize, TETPERBLOCK, POINTER, 8); + + if (useshelles) { + // Initialize the pool of subfaces. Note: Here we need eight-byte + // aligned for each subface record (to keep six version). + subfaces.init(11 * sizeof(shellface) + sizeof(int), SUBFACEPERBLOCK, + POINTER, 8); + // Initialize the pool of subsegments. The subsegment's record is same + // with subface. + subsegs.init(11 * sizeof(shellface) + sizeof(int), SUBSEGPERBLOCK, + POINTER, 8); + // Initialize the "outer space" tetrahedron and omnipresent subface. + dummyinit(tetrahedrons.itemwords, subfaces.itemwords); + } else { + // Initialize the "outer space" tetrahedron. + dummyinit(tetrahedrons.itemwords, 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrondealloc() Deallocate space for a tetrahedron, marking it // +// dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::tetrahedrondealloc(tetrahedron *dyingtetrahedron) +{ + // Set tetrahedron's vertices to NULL. This makes it possible to detect + // dead tetrahedra when traversing the list of all tetrahedra. + dyingtetrahedron[4] = (tetrahedron) NULL; + dyingtetrahedron[5] = (tetrahedron) NULL; + dyingtetrahedron[6] = (tetrahedron) NULL; + dyingtetrahedron[7] = (tetrahedron) NULL; + tetrahedrons.dealloc((void *) dyingtetrahedron); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetrahedron* mesh3d::tetrahedrontraverse() +{ + tetrahedron *newtetrahedron; + + do { + newtetrahedron = (tetrahedron *) tetrahedrons.traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; + } + } while (newtetrahedron[4] == (tetrahedron) NULL); // Skip dead ones. + return newtetrahedron; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shellfacedealloc() Deallocate space for a shellface, marking it dead. // +// Used both for dealloc a subface and subsegment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::shellfacedealloc(memorypool *pool, shellface *dyingshellface) +{ + // Set shellface's vertices to NULL. This makes it possible to detect dead + // shellfaces when traversing the list of all shellfaces. + dyingshellface[3] = (shellface) NULL; + dyingshellface[4] = (shellface) NULL; + dyingshellface[5] = (shellface) NULL; + pool->dealloc((void *) dyingshellface); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shellfaceraverse() Traverse the subfaces, skipping dead ones. Used for // +// both subfaces and subsegments pool traverse. // +// // +/////////////////////////////////////////////////////////////////////////////// + +shellface* mesh3d::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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// badsegmentdealloc() Deallocate space for a bad segment, marking it // +// dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::badsegmentdealloc(badface3d *dyingshseg) +{ + // Set segment's faceorg to NULL. This makes it possible to detect dead + // segments when traversing the list of all segments. + dyingshseg->faceorg = (point3d) NULL; + badsegments.dealloc((void *) dyingshseg); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// badsegmenttraverse() Traverse the bad segments, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +badface3d* mesh3d::badsegmenttraverse() +{ + badface3d *newsh; + + do { + newsh = (badface3d *) badsegments.traverse(); + if (newsh == (badface3d *) NULL) { + return (badface3d *) NULL; + } + } while (newsh->faceorg == (point3d) NULL); // Skip dead ones. + return newsh; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// pointdealloc() Deallocate space for a point, marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::pointdealloc(point3d dyingpoint) +{ + // Mark the point as dead. This makes it possible to detect dead points + // when traversing the list of all points. + setpointmark(dyingpoint, DEADPOINT); + points.dealloc((void *) dyingpoint); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// pointtraverse() Traverse the points, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +point3d mesh3d::pointtraverse() +{ + point3d newpoint; + + do { + newpoint = (point3d) points.traverse(); + if (newpoint == (point3d) NULL) { + return (point3d) NULL; + } + } while (pointmark(newpoint) == DEADPOINT); // Skip dead ones. + return newpoint; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getpoint() Get a specific point, by number, from the list. // +// // +// The first point is number 'firstnumber'. // +// // +// Note that this takes O(n) time ( with a small constant, if POINTPERBLOCK // +// is large ). I don't care to take the trouble to make it work in constant // +// time. // +// // +/////////////////////////////////////////////////////////////////////////////// + +point3d mesh3d::getpoint(int number) +{ + void **getblock; + point3d foundpoint; + unsigned long alignptr; + int current; + + getblock = points.firstblock; + current = firstnumber; + // Find the right block. + while (current + points.itemsperblock <= number) { + getblock = (void **) *getblock; + current += points.itemsperblock; + } + // Now find the right point. + alignptr = (unsigned long) (getblock + 1); + foundpoint = (point3d) (alignptr + (unsigned long) points.alignbytes + - (alignptr % (unsigned long) points.alignbytes)); + while (current < number) { + foundpoint += points.itemwords; + current++; + } + return foundpoint; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// maketetrahedron() Create a new tetrahedron. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::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 vertex points. + 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 (useshelles) { + newtet->tet[8 ] = (tetrahedron) dummysh; + newtet->tet[9 ] = (tetrahedron) dummysh; + newtet->tet[10] = (tetrahedron) dummysh; + newtet->tet[11] = (tetrahedron) dummysh; + } + for (int i = 0; i < eextras; i++) { + setelemattribute(newtet->tet, i, 0.0); + } + if (varvolume) { + setvolumebound(newtet->tet, -1.0); + } + // Initialize the location and version to be Zero. + newtet->loc = 0; + newtet->ver = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makeshellface() Create a new shellface with version zero. Used both // +// for subfaces and seusegments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::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 vertex points. + 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; + // Set the boundary marker to zero. + * (int *) (newface->sh + 11) = 0; + // Initialize the version to be Zero. + newface->shver = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Memory Management Routines // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Geometric Primitives // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +inline bool mesh3d::iszero(const REAL value) +{ + if (noroundoff) { + return value == 0.0; + } else { + return fabs(value) <= usertolerance; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// iorient3d() 3D Orientation test. // +// // +// Return +1 if the point pd lies "below" the plane passing through pa, pb, // +// and pc; Return -1 if pd lies "above" the plane. Return 0 if the points // +// are coplanar. // +// // +// "below" and "above" is defined use Right-Hand Rule, so that pa, pb, and // +// pc appear in counterclockwise order when making a fist, the thumb points // +// to the "above" direction of the plane. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::iorient3d(point3d pa, point3d pb, point3d pc, point3d pd) +{ + orient3dcount++; + + if (!noexact) { + // Using adaptive precision arithmetic and tolerance. + REAL sign = orient3d(pa, pb, pc, pd); + if (iszero(sign)) sign = 0.0; + if (sign > 0) return (1); + else if (sign < 0) return (-1); + else return (0); + } else { + // Using as nearly exact arithmetic as required. +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MINMAX4(da, db, dc, dd, dMin, dMax) { \ + REAL dMax1, dMax2, dMin1, dMin2; \ + if (da > db) {dMax1 = da; dMin1 = db;} \ + else {dMax1 = db; dMin1 = da;} \ + if (dc > dd) {dMax2 = dc; dMin2 = dd;} \ + else {dMax2 = dd; dMin2 = dc;} \ + if (dMax1 > dMax2) dMax = dMax1; \ + else dMax = dMax2; \ + if (dMin1 < dMin2) dMin = dMin1; \ + else dMin = dMin2; \ + } + REAL dXa = pa[0], dYa = pa[1], dZa = pa[2]; + REAL dXb = pb[0], dYb = pb[1], dZb = pb[2]; + REAL dXc = pc[0], dYc = pc[1], dZc = pc[2]; + REAL dXd = pd[0], dYd = pd[1], dZd = pd[2]; + + REAL dMaxX, dMinX, dMaxY, dMinY, dMaxZ, dMinZ; + + REAL dDX2 = dXb - dXa; + REAL dDX3 = dXc - dXa; + REAL dDX4 = dXd - dXa; + MINMAX4(dXa, dXb, dXc, dXd, dMinX, dMaxX); + + REAL dDY2 = dYb - dYa; + REAL dDY3 = dYc - dYa; + REAL dDY4 = dYd - dYa; + MINMAX4(dYa, dYb, dYc, dYd, dMinY, dMaxY); + + REAL dDZ2 = dZb - dZa; + REAL dDZ3 = dZc - dZa; + REAL dDZ4 = dZd - dZa; + MINMAX4(dZa, dZb, dZc, dZd, dMinZ, dMaxZ); + REAL dMax = MAX( MAX(dMaxX-dMinX, dMaxY-dMinY), dMaxZ-dMinZ); + + // dDet is proportional to the cell volume + REAL dDet = (dDX2*(dDY3*dDZ4 - dDY4*dDZ3) + + dDX3*(dDY4*dDZ2 - dDY2*dDZ4) + + dDX4*(dDY2*dDZ3 - dDY3*dDZ2)); + + // Compute an upper bound on the error bound. + REAL dError = usertolerance * dMax*dMax*dMax; + + // Note: This is an left-hand direction, we need translate + // it to right-hand direction we used. + if (dDet > dError) + return (-1); // return (1); + else if (dDet < -dError) + return (1); // return (-1); + return(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// iinsphere() Insphere test. // +// // +// Determines whether point 'pe' is inside or outside the circumsphere of // +// the tetrahedron formed by point pa, pb, pc, pd. // +// // +// 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. // +// // +// Note: We can skip to test pa, pb, pc, and pd 's orientation, because we // +// always known the orientation of pa, pb, pc and pd in advance. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::iinsphere(point3d pa, point3d pb, point3d pc, point3d pd, + point3d pe) +{ +#ifdef SELF_CHECK + assert(iorient3d(pa, pb, pc, pd) > 0); +#endif // defined SELF_CHECK + + inspherecount++; + + if (!noexact) { + // Using adaptive precision arithmetic and tolerance. + REAL sign = insphere(pa, pb, pc, pd, pe); + if (iszero(sign)) sign = 0.0; + if (sign > 0.) return (1); + else if (sign < 0.) return (-1); + else return (0); + } else { + // Using as nearly exact arithmetic as required. +#define dDet4By4(Mat4) \ + ( Mat4[0][0] * ( Mat4[1][1]*Mat4[2][2]*Mat4[3][3] \ + + Mat4[1][2]*Mat4[2][3]*Mat4[3][1] \ + + Mat4[1][3]*Mat4[2][1]*Mat4[3][2] \ + - Mat4[3][1]*Mat4[2][2]*Mat4[1][3] \ + - Mat4[3][2]*Mat4[2][3]*Mat4[1][1] \ + - Mat4[3][3]*Mat4[2][1]*Mat4[1][2] ) \ + - Mat4[0][1] * ( Mat4[1][0]*Mat4[2][2]*Mat4[3][3] \ + + Mat4[1][2]*Mat4[2][3]*Mat4[3][0] \ + + Mat4[1][3]*Mat4[2][0]*Mat4[3][2] \ + - Mat4[3][0]*Mat4[2][2]*Mat4[1][3] \ + - Mat4[3][2]*Mat4[2][3]*Mat4[1][0] \ + - Mat4[3][3]*Mat4[2][0]*Mat4[1][2] ) \ + + Mat4[0][2] * ( Mat4[1][0]*Mat4[2][1]*Mat4[3][3] \ + + Mat4[1][1]*Mat4[2][3]*Mat4[3][0] \ + + Mat4[1][3]*Mat4[2][0]*Mat4[3][1] \ + - Mat4[3][0]*Mat4[2][1]*Mat4[1][3] \ + - Mat4[3][1]*Mat4[2][3]*Mat4[1][0] \ + - Mat4[3][3]*Mat4[2][0]*Mat4[1][1] ) \ + - Mat4[0][3] * ( Mat4[1][0]*Mat4[2][1]*Mat4[3][2] \ + + Mat4[1][1]*Mat4[2][2]*Mat4[3][0] \ + + Mat4[1][2]*Mat4[2][0]*Mat4[3][1] \ + - Mat4[3][0]*Mat4[2][1]*Mat4[1][2] \ + - Mat4[3][1]*Mat4[2][2]*Mat4[1][0] \ + - Mat4[3][2]*Mat4[2][0]*Mat4[1][1] ) \ + ) + REAL dXa = pa[0], dYa = pa[1], dZa = pa[2]; + REAL dXb = pb[0], dYb = pb[1], dZb = pb[2]; + REAL dXc = pc[0], dYc = pc[1], dZc = pc[2]; + REAL dXd = pd[0], dYd = pd[1], dZd = pd[2]; + REAL dXe = pe[0], dYe = pe[1], dZe = pe[2]; + + REAL a2dInSphMat[4][4], dWa; + dWa = dXa*dXa + dYa*dYa + dZa*dZa; + + a2dInSphMat[0][0] = dXb - dXa; + a2dInSphMat[0][1] = dYb - dYa; + a2dInSphMat[0][2] = dZb - dZa; + a2dInSphMat[0][3] = dXb*dXb + dYb*dYb + dZb*dZb - dWa; + + a2dInSphMat[1][0] = dXc - dXa; + a2dInSphMat[1][1] = dYc - dYa; + a2dInSphMat[1][2] = dZc - dZa; + a2dInSphMat[1][3] = dXc*dXc + dYc*dYc + dZc*dZc - dWa; + + a2dInSphMat[2][0] = dXd - dXa; + a2dInSphMat[2][1] = dYd - dYa; + a2dInSphMat[2][2] = dZd - dZa; + a2dInSphMat[2][3] = dXd*dXd + dYd*dYd + dZd*dZd - dWa; + + a2dInSphMat[3][0] = dXe - dXa; + a2dInSphMat[3][1] = dYe - dYa; + a2dInSphMat[3][2] = dZe - dZa; + a2dInSphMat[3][3] = dXe*dXe + dYe*dYe + dZe*dZe - dWa; + + // Set up a scale that is the average of the distance from A to each + // of the other verts. Use some trickery to take advantage of + // already knowing what the differences in coordinates are. + REAL dAveLen = 0.25 * (Mag3D(a2dInSphMat[0]) + + Mag3D(a2dInSphMat[1]) + + Mag3D(a2dInSphMat[2]) + + Mag3D(a2dInSphMat[3])); + REAL dScale = pow(dAveLen, 5); + // REAL dDet = dDet4By4(a2dInSphMat) / dScale * + // iorient3d(pa, pb, pc, pd); + REAL dDet = dDet4By4(a2dInSphMat) / dScale; + if (dDet > userubtolerance) { + return (1); // return (-1); + } else if (dDet < -userubtolerance) { + return (-1); // return ( 1); + } else { + return (0); + } + } +} + +int mesh3d::iinsphere(triface* tface, point3d testpoint) +{ + point3d torg, tdest, tapex, toppo; + + org(*tface, torg); + dest(*tface, tdest); + apex(*tface, tapex); + oppo(*tface, toppo); + // We need form a positive orientation of this points before test. + if (EdgeRing(tface->ver) == CCW) { + return iinsphere(tdest, torg, tapex, toppo, testpoint); + } else { + return iinsphere(torg, tdest, tapex, toppo, testpoint); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Some convient functions for Geometric Primitives. // +// // +// isbelowplane Return ture iff point is 'below' the plane. // +// isaboveplane Return true iff point is 'above' the plane. // +// iscoplane Return true iff points are coplane. // +// iscoline Return true iff three points are colinear. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool mesh3d::isbelowplane(triface* tface, point3d testpoint) +{ + point3d torg, tdest, tapex; + + org(*tface, torg); + dest(*tface, tdest); + apex(*tface, tapex); + return iorient3d(torg, tdest, tapex, testpoint) > 0; +} + +bool mesh3d::isaboveplane(triface* tface, point3d testpoint) +{ + point3d torg, tdest, tapex; + + org(*tface, torg); + dest(*tface, tdest); + apex(*tface, tapex); + return iorient3d(torg, tdest, tapex, testpoint) < 0; +} + +bool mesh3d::iscoplane(triface* tface, point3d testpoint) +{ + point3d torg, tdest, tapex; + + org(*tface, torg); + dest(*tface, tdest); + apex(*tface, tapex); + return iorient3d(torg, tdest, tapex, testpoint) == 0; +} + +bool mesh3d::isbelowplane(point3d pa, point3d pb, point3d pc, point3d pd) +{ + return iorient3d(pa, pb, pc, pd) > 0; +} + +bool mesh3d::isaboveplane(point3d pa, point3d pb, point3d pc, point3d pd) +{ + return iorient3d(pa, pb, pc, pd) < 0; +} + +bool mesh3d::iscoplane(point3d pa, point3d pb, point3d pc, point3d pd) +{ + return iorient3d(pa, pb, pc, pd) == 0; +} + +bool mesh3d::iscoline(point3d pa, point3d pb, point3d pc) +{ + REAL v0[3], v1[3], v2[3]; + + Sub3D(pa, pc, v0); + Sub3D(pb, pc, v1); + Cross3D(v0, v1, v2); + return (iszero(v2[0])) && (iszero(v2[1])) && (iszero(v2[2])); +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Geometric Primitives // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Geometric Functions // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// projontoface() Get the orthogonal projection of point 'p' onto the // +// plane passing through points a, b and c. Return by 'p'. // +// // +// Given a facet F(contains point a, b and c) and a point p, the orthogonal // +// projection p' of p onto F is the point that is coplanar with points a, b // +// and c, and satisfies the requirement that the line pp' is orthogonal to F.// +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::projontoface(point3d a, point3d b, point3d c, point3d p) +{ + REAL ba[3], ca[3], pa[3], n[3]; + REAL dist; + + // Get unit normal of face abc. + Sub3D(b, a, ba); + Sub3D(c, a, ca); + Cross3D(ba, ca, n); + Normalize3D(n); + + Sub3D(p, a, pa); + dist = Dot3D(pa, n); + + p[0] -= dist * n[0]; + p[1] -= dist * n[1]; + p[2] -= dist * n[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// circumcenter() Get the equatorial shpere center of a triangular face // +// defines by vertices a, b and c. Return by 'res'. // +// // +// Equatorial shpere of a triangular face is the smallest sphere that passes // +// through the three vertices of the face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::circumcenter(point3d a, point3d b, point3d c, point3d res) +{ + REAL adRow1[3], adRow2[3], adRow3[3]; + REAL dRHS1, dRHS2, dRHS3; + REAL dTemp; + + Sub3D(a, b, adRow1); + Sub3D(a, c, adRow2); + Cross3D(adRow1, adRow2, adRow3); + +#ifdef SELF_CHECK + if (noexact) { + noexact = 0; + // Check three dividers to catch precission problems before going on. + REAL adOrg[3] = {0, 0, 0}; + if (iorient3d(adRow1, adRow2, adRow3, adOrg) == 0) { + printf("Precssion error in circumcenter():\n"); + printf(" Try to find circumcenter from three almost colinear points:\n"); + printf(" point 1: (%.12g, %.12g, %.12g).\n", a[0], a[1], a[2]); + printf(" point 2: (%.12g, %.12g, %.12g).\n", b[0], b[1], b[2]); + printf(" point 3: (%.12g, %.12g, %.12g).\n", c[0], c[1], c[2]); + printf(" This probably means I am trying to refine mesh by splitting\n"); + printf(" a nearly degenerate subface(which created by the approximate\n"); + printf(" finite precision of floating point arithmetic).\n"); + noexact = 1; + precisionerror(); + } + noexact = 1; + } +#endif // defined SELF_CHECK + + dRHS1 = 0.5 * (Dot3D(a, a) - Dot3D(b, b)); + dRHS2 = 0.5 * (Dot3D(a, a) - Dot3D(c, c)); + dRHS3 = Dot3D(a, adRow3); + + // Gussian elimination. Solve 3 by 3 linear system. + + // Pivot first column + if (fabs(adRow1[0]) >= fabs(adRow2[0]) && + fabs(adRow1[0]) >= fabs(adRow3[0])) { + ; + } else if (fabs(adRow2[0]) >= fabs(adRow3[0])) { + dTemp = dRHS1; + dRHS1 = dRHS2; + dRHS2 = dTemp; + Swap3D(adRow1, adRow2); + } else { + dTemp = dRHS1; + dRHS1 = dRHS3; + dRHS3 = dTemp; + Swap3D(adRow1, adRow3); + } + + // Eliminate first column + dRHS2 -= dRHS1 * adRow2[0]/adRow1[0]; + dRHS3 -= dRHS1 * adRow3[0]/adRow1[0]; + adRow2[1] = adRow2[1] - adRow1[1] * (adRow2[0]/adRow1[0]); + adRow2[2] = adRow2[2] - adRow1[2] * (adRow2[0]/adRow1[0]); + adRow3[1] = adRow3[1] - adRow1[1] * (adRow3[0]/adRow1[0]); + adRow3[2] = adRow3[2] - adRow1[2] * (adRow3[0]/adRow1[0]); + + // Pivot second column + if (fabs(adRow2[1]) >= fabs(adRow3[1])) { + ; + } else { + dTemp = dRHS2; + dRHS2 = dRHS3; + dRHS3 = dTemp; + Swap3D(adRow2, adRow3); + } + + // Eliminate second column + dRHS3 -= dRHS2 * adRow3[1]/adRow2[1]; + adRow3[2] = adRow3[2] - adRow2[2] * (adRow3[1]/adRow2[1]); + + // Solve for dRHS3 and back-substitute + res[2] = dRHS3 /= adRow3[2]; + dRHS2 -= adRow2[2] * dRHS3; + dRHS1 -= adRow1[2] * dRHS3; + + // Solve for dRHS2 and back-substitute + res[1] = dRHS2 /= adRow2[1]; + dRHS1 -= adRow1[1] * dRHS2; + + // Solve for dRHS1 + res[0] = dRHS1 /= adRow1[0]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// circumcenter() Get the circumcenter of four points a, b, c and d. // +// Return by 'res'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::circumcenter(point3d a, point3d b, point3d c, point3d d, + point3d res) +{ + REAL adRow1[3], adRow2[3], adRow3[3]; + REAL dRHS1, dRHS2, dRHS3; + REAL dTemp; + + Sub3D(a, b, adRow1); + Sub3D(a, c, adRow2); + Sub3D(a, d, adRow3); + +#ifdef SELF_CHECK + if (noexact) { + noexact = 0; + if (iorient3d(a, b, c, d) == 0) { + printf("Precssion error in circumcenter():\n"); + printf(" Try to find circumcenter from four almost coplanar points:\n"); + printf(" point 1: (%.12g, %.12g, %.12g).\n", a[0], a[1], a[2]); + printf(" point 2: (%.12g, %.12g, %.12g).\n", b[0], b[1], b[2]); + printf(" point 3: (%.12g, %.12g, %.12g).\n", c[0], c[1], c[2]); + printf(" point 4: (%.12g, %.12g, %.12g).\n", d[0], d[1], d[2]); + printf(" This probably means I am trying to refine mesh by splitting\n"); + printf(" a nearly degenerate tetrahedron(which created by the \n"); + printf(" approximate finite precision of floating point arithmetic).\n"); + noexact = 1; + precisionerror(); + } + noexact = 1; + } +#endif // defined SELF_CHECK + + dRHS1 = 0.5 * (Dot3D(a, a) - Dot3D(b, b)); + dRHS2 = 0.5 * (Dot3D(a, a) - Dot3D(c, c)); + dRHS3 = 0.5 * (Dot3D(a, a) - Dot3D(d, d)); + + // Gussian elimination. Solve 3 by 3 linear system. + + // Pivot first column + if (fabs(adRow1[0]) >= fabs(adRow2[0]) && + fabs(adRow1[0]) >= fabs(adRow3[0])) { + ; + } else if (fabs(adRow2[0]) >= fabs(adRow3[0])) { + dTemp = dRHS1; + dRHS1 = dRHS2; + dRHS2 = dTemp; + Swap3D(adRow1, adRow2); + } else { + dTemp = dRHS1; + dRHS1 = dRHS3; + dRHS3 = dTemp; + Swap3D(adRow1, adRow3); + } + + // Eliminate first column + dRHS2 -= dRHS1 * adRow2[0]/adRow1[0]; + dRHS3 -= dRHS1 * adRow3[0]/adRow1[0]; + adRow2[1] = adRow2[1] - adRow1[1] * (adRow2[0]/adRow1[0]); + adRow2[2] = adRow2[2] - adRow1[2] * (adRow2[0]/adRow1[0]); + adRow3[1] = adRow3[1] - adRow1[1] * (adRow3[0]/adRow1[0]); + adRow3[2] = adRow3[2] - adRow1[2] * (adRow3[0]/adRow1[0]); + + // Pivot second column + if (fabs(adRow2[1]) >= fabs(adRow3[1])) { + ; + } else { + dTemp = dRHS2; + dRHS2 = dRHS3; + dRHS3 = dTemp; + Swap3D(adRow2, adRow3); + } + + // Eliminate second column + dRHS3 -= dRHS2 * adRow3[1]/adRow2[1]; + adRow3[2] = adRow3[2] - adRow2[2] * (adRow3[1]/adRow2[1]); + + // Solve for dRHS3 and back-substitute + res[2] = dRHS3 /= adRow3[2]; + dRHS2 -= adRow2[2] * dRHS3; + dRHS1 -= adRow1[2] * dRHS3; + + // Solve for dRHS2 and back-substitute + res[1] = dRHS2 /= adRow2[1]; + dRHS1 -= adRow1[1] * dRHS2; + + // Solve for dRHS1 + res[0] = dRHS1 /= adRow1[0]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetvolume() Get the volume of tetrahedron formed by vertices a, b, c // +// and d. // +// // +// Note: The return value may be negative. True volume is fabs(retval). // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL mesh3d::tetvolume(point3d a, point3d b, point3d c, point3d d) +{ + REAL adVec1[3], adVec2[3], adVec3[3]; + REAL adCross[3]; + + Sub3D(b, a, adVec1); + Sub3D(c, a, adVec2); + Sub3D(d, a, adVec3); + + Cross3D(adVec2, adVec3, adCross); + return Dot3D(adCross, adVec1) / 6.; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// facedihedral() Get the diahedral angle of two faces, given by their // +// vertices. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL mesh3d::facedihedral(point3d forg1, point3d fdest1, point3d fapex1, + point3d forg2, point3d fdest2, point3d fapex2) +{ + REAL adNorm0[3], adNorm1[3]; + REAL dScale, dDot; + + Normal3D(forg1, fdest1, fapex1, adNorm0); + Normalize3D(adNorm0); + Normal3D(forg2, fdest2, fapex2, adNorm1); + Normalize3D(adNorm1); + + dScale = 180. / acos(-1.); + dDot = -Dot3D(adNorm0, adNorm1); + if (dDot > 1.) dDot = 1.; + else if (dDot < -1.) dDot = -1.; + return acos(dDot) * dScale; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetalldihedral() Get all(six) dihedral angles in tetrahedron form by // +// vertices a, b, c and d. Return by array adDihed[6]. // +// // +// The order in which the dihedrals are assigned matters for computation of // +// solid angles. The way they're currently set up, combining them as (0,1,2),// +// (0,3,4), (1,3,5), (2,4,5) gives (in order) solid angles at vertices a, b, // +// c and d. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::tetalldihedral(point3d a, point3d b, point3d c, point3d d, + REAL adDihed[6]) +{ + REAL adNorm0[3], adNorm1[3], adNorm2[3], adNorm3[3]; + REAL dScale, dDot; + + Normal3D(c, b, d, adNorm0); + Normalize3D(adNorm0); + Normal3D(a, c, d, adNorm1); + Normalize3D(adNorm1); + Normal3D(b, a, d, adNorm2); + Normalize3D(adNorm2); + Normal3D(a, b, c, adNorm3); + Normalize3D(adNorm3); + + dScale = 180. / acos(-1.); + dDot = -Dot3D(adNorm0, adNorm1); + if (dDot > 1.) dDot = 1.; + else if (dDot < -1.) dDot = -1.; + adDihed[5] = acos(dDot) * dScale; // Edge CD + + dDot = -Dot3D(adNorm0, adNorm2); + if (dDot > 1.) dDot = 1.; + else if (dDot < -1.) dDot = -1.; + adDihed[4] = acos(dDot) * dScale; // Edge BD + + dDot = -Dot3D(adNorm0, adNorm3); + if (dDot > 1.) dDot = 1.; + else if (dDot < -1.) dDot = -1.; + adDihed[3] = acos(dDot) * dScale; // Edge BC + + dDot = -Dot3D(adNorm1, adNorm2); + if (dDot > 1.) dDot = 1.; + else if (dDot < -1.) dDot = -1.; + adDihed[2] = acos(dDot) * dScale; // Edge AD + + dDot = -Dot3D(adNorm1, adNorm3); + if (dDot > 1.) dDot = 1.; + else if (dDot < -1.) dDot = -1.; + adDihed[1] = acos(dDot) * dScale; // Edge AC + + dDot = -Dot3D(adNorm2, adNorm3); + if (dDot > 1.) dDot = 1.; + else if (dDot < -1.) dDot = -1.; + adDihed[0] = acos(dDot) * dScale; // Edge AB +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Geometric Functions // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Point Location Routines // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// A crucial step in implementing many mesh generation algorithms is the // +// point location problem. For example, in incremental insertion algorithms, // +// for each vertex to be inserted, we need to find the tetrahedron in which // +// this vertex lies, so that it may be inserted. In many circumstances, the // +// dominant cost is the time required for point location. // +// // +// Fortunately the point location problem is well studied in computational // +// geometry literature and and several theoretically optimal algorithms have // +// been proposed, such as the "Bucketing" algorithm of Asano et al.[1], the // +// "Alternating Digital Tree(ADT)" algorithm of Bonet and Peraire[2] and the // +// Fast Randomized Point Location algorithm of Mucke, Isaac and Zhu[3], etc. // +// Where [3] is more suitable and faster algorithm for our 3D Delaunay mesh // +// generator. // +// // +// Our mesh data structure is very suitable for implementing the algorithm // +// in [3], and with using the Adaptive Exact Geometric Predicates package of // +// Shewchuk to improve the robustness of implementation(use -X swith). // +// // +// Please see tetlib.h and predicate.h to learn more about the mesh data // +// structure and exact floating-point arithmetic. // +// // +// Refernces: // +// // +// [1] T. Asano, M. Edahiro, H. Imai, M. Iri, and K. Murota. Practical use // +// of bucketing techniques in computational geometry. In G. T. Toussaint,// +// editor, Computational Geometry, pages 153-195. North-Holland, // +// Amsterdam, Netherlands, 1985. // +// [2] J. Bonet and J. Peraire. An alternating digital tree (ADT) algorithm // +// for 3D geometric searching and intersection problems. International // +// Journal for Numerical Methods in Engineering, 31:1-17, 1991. // +// [3] Ernst P. Mucke, Isaac Saias, and Binhai Zhu, Fast Randomized Point // +// Location Without Preprocessing in Two- and Three-dimensional Delaunay // +// Triangulations, Proceedings of the Twelfth Annual Symposium on // +// Computational Geometry, ACM, May 1996. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// randomnation() Generate a random number between 0 and 'choices' - 1. // +// // +// This is a simple linear congruential random number generator. Hence,it is // +// a bad random number generator, but good enough for most randomized geome- // +// tric algorithms. // +// // +/////////////////////////////////////////////////////////////////////////////// + +unsigned long mesh3d::randomnation(unsigned int choices) +{ + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed / (714025l / choices + 1); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makepointmap() Construct a mapping from points to tetrahedra to // +// improve the speed of point location for subsegment and // +// subfacet insertion. // +// // +// Traverses all the tetrahedra, and provides each corner of each // +// tetrahedron with a pointer to that tetrahedera. Of course, pointers will // +// be overwritten by other pointers because (almost) each point is a corner // +// of several tetrahedra, but in the end every point will point to some // +// tetrahedron that contains it. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::makepointmap() +{ + triface tetloop; + point3d pointptr; + + if (verbose) { + printf(" Constructing mapping from points to tetrahedra.\n"); + } + tetrahedrons.traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + // Check all four points of the tetrahedron. + org(tetloop, pointptr); + setpoint2tet(pointptr, encode(tetloop)); + dest(tetloop, pointptr); + setpoint2tet(pointptr, encode(tetloop)); + apex(tetloop, pointptr); + setpoint2tet(pointptr, encode(tetloop)); + oppo(tetloop, pointptr); + setpoint2tet(pointptr, encode(tetloop)); + // Get another tet. + tetloop.tet = tetrahedrontraverse(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// isintet() Determine whether a vertex lies inside, on or outside a // +// tetrahedron. // +// // +// For 'testpoint' to be inside the tetrahedron formed by pa, pb, pc and pd, // +// the orientation has to be right with respect to every face of tetrahedron.// +// // +// Return OUTSIDE if the point lies outside the tet; Return ONVERTEX if it // +// coincides with a vertex in the tet; Return ONEDGE if it lies on an edge; // +// Return ONFACE if it lies on a face; and Return INTETRAHEDRON if it lies // +// strictly inside. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum mesh3d::locateresult +mesh3d::isintet(point3d pa, point3d pb, point3d pc, point3d pd, + point3d testpoint) +{ + int isign, icheck, iretval; + + // Get orientation of pa, pb, pc and pd. + isign = iorient3d(pa, pb, pc, pd); + assert(isign != 0); + + iretval = 0; + icheck = iorient3d(pb, pd, pc, testpoint) * isign; + if (icheck == -1) return OUTSIDE; + iretval += icheck; + + icheck = iorient3d(pc, pd, pa, testpoint) * isign; + if (icheck == -1) return OUTSIDE; + iretval += icheck; + + icheck = iorient3d(pa, pd, pb, testpoint) * isign; + if (icheck == -1) return OUTSIDE; + iretval += icheck; + + icheck = iorient3d(pa, pb, pc, testpoint) * isign; + if (icheck == -1) return OUTSIDE; + iretval += icheck; + + if (iretval == 1) { + return ONVERTEX; + } else if (iretval == 2) { + return ONEDGE; + } else if (iretval == 3) { + return ONFACE; + } else { + return INTETRAHEDRON; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// distance() Returns the minimum "distance" of triface to point p. // +// // +/////////////////////////////////////////////////////////////////////////////// + +static REAL square (REAL x) +{ + return (x * x); +} + +REAL mesh3d::distance(triface* tface, point3d p) +{ +#define Min(X,Y) (((X) < (Y)) ? (X) : (Y)) + REAL d, da, db, dc; + point3d tp; + + org(*tface, tp); + da = square(p[0] - tp[0]) + square(p[1] - tp[1]) + square(p[2] - tp[2]); + dest(*tface, tp); + db = square(p[0] - tp[0]) + square(p[1] - tp[1]) + square(p[2] - tp[2]); + apex(*tface, tp); + dc = square(p[0] - tp[0]) + square(p[1] - tp[1]) + square(p[2] - tp[2]); + d = Min (da, db); + return (Min (d, dc)); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// iscoplanarintri() Determine whether a vertex lies inside, on or // +// outside a triangle in 3D. It assume the vert and the // +// triangle are coplanar. Approximate test. Nonrobust. // +// // +// Return OUTSIDE if the vertex lies outside the triangle. // +// // +// Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' is // +// a handle whose origin is the existing vertex. // +// // +// Returns ONEDGE if the point lies on a mesh edge. 'searchtet' is a handle // +// whose face version is the edge on which the point lies. // +// // +// Returns ONFACE if the point lies strictly within a facet. 1searchtet' is // +// a handle whose location is the face on which the point lies. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum mesh3d::locateresult +mesh3d::iscoplanarintri(point3d searchpoint, triface* searchtet) +{ + point3d torg, tdest, tapex; + const REAL dTol = 5.e-9; + const REAL dFloor = 1.e-6; + REAL dSum, dDiff; // Tempory variables used in fuzzycomp(). + int iCompRes; + +#define fuzzycomp(dA, dB, iRes) \ + dSum = fabs(dA) + fabs(dB); \ + dDiff = dA - dB; \ + dSum = (dSum > dFloor) ? dSum : dFloor; \ + if (dDiff > dTol * dSum) iRes = 1; \ + else if (dDiff < - dTol * dSum) iRes = -1; \ + else iRes = 0; + +#define orient2dfast(pa, pb, pc) \ + ((pa[0] - pc[0]) * (pb[1] - pc[1]) - (pa[1] - pc[1]) * (pb[0] - pc[0])) + +// #define iszero3(v) (iszero(v[0]) && iszero(v[1]) && iszero(v[2])) +#define iszero3(v) \ + (sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) <= userubtolerance) + + org(*searchtet, torg); // torg = searchtet->org(); + // First find out the on vertex case, this must do first. + fuzzycomp(searchpoint[0], torg[0], iCompRes); + if (iCompRes == 0) { + fuzzycomp(searchpoint[1], torg[1], iCompRes); + if (iCompRes == 0) { + fuzzycomp(searchpoint[2], torg[2], iCompRes); + if (iCompRes == 0) { + return ONVERTEX; + } + } + } + + dest(*searchtet, tdest); // tdest = searchtet->dest(); + fuzzycomp(searchpoint[0], tdest[0], iCompRes); + if (iCompRes == 0) { + fuzzycomp(searchpoint[1], tdest[1], iCompRes); + if (iCompRes == 0) { + fuzzycomp(searchpoint[2], tdest[2], iCompRes); + if (iCompRes == 0) { + enextself(*searchtet); + return ONVERTEX; + } + } + } + + apex(*searchtet, tapex); // tapex = searchtet->apex(); + fuzzycomp(searchpoint[0], tapex[0], iCompRes); + if (iCompRes == 0) { + fuzzycomp(searchpoint[1], tapex[1], iCompRes); + if (iCompRes == 0) { + fuzzycomp(searchpoint[2], tapex[2], iCompRes); + if (iCompRes == 0) { + enext2self(*searchtet); + return ONVERTEX; + } + } + } + + // Next find out the on edge case. + REAL v0[3], v1[3], v2[3]; + int iCompRes1, iCompRes2; + + Sub3D(torg, searchpoint, v0); + Sub3D(tdest, searchpoint, v1); + Cross3D(v0, v1, v2); + if (iszero3(v2)) { + fuzzycomp(torg[0], searchpoint[0], iCompRes1); + fuzzycomp(searchpoint[0], tdest[0], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + fuzzycomp(torg[1], searchpoint[1], iCompRes1); + fuzzycomp(searchpoint[1], tdest[1], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + fuzzycomp(torg[2], searchpoint[2], iCompRes1); + fuzzycomp(searchpoint[2], tdest[2], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + return ONEDGE; + } + } + } + } + Sub3D(tapex, searchpoint, v0); + Cross3D(v0, v1, v2); + if (iszero3(v2)) { + fuzzycomp(tdest[0], searchpoint[0], iCompRes1); + fuzzycomp(searchpoint[0], tapex[0], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + fuzzycomp(tdest[1], searchpoint[1], iCompRes1); + fuzzycomp(searchpoint[1], tapex[1], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + fuzzycomp(tdest[2], searchpoint[2], iCompRes1); + fuzzycomp(searchpoint[2], tapex[2], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + enextself(*searchtet); + return ONEDGE; + } + } + } + } + Sub3D(torg, searchpoint, v1); + Cross3D(v0, v1, v2); + if (iszero3(v2)) { + fuzzycomp(tapex[0], searchpoint[0], iCompRes1); + fuzzycomp(searchpoint[0], torg[0], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + fuzzycomp(tapex[1], searchpoint[1], iCompRes1); + fuzzycomp(searchpoint[1], torg[1], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + fuzzycomp(tapex[2], searchpoint[2], iCompRes1); + fuzzycomp(searchpoint[2], torg[2], iCompRes2); + if ((iCompRes1 != 1) == (iCompRes2 != 1)) { + enext2self(*searchtet); + return ONEDGE; + } + } + } + } + + // Finally, to find if this point lies on face. + REAL adNorm[3], adBasisX[3], adBasisY[3]; + REAL VProjA[2], VProjB[2], VProjC[2], VProj[2]; + REAL sign0, sign1, sign2; + + // Find a normal and two vectors in the plane. + Sub3D(tdest, torg, v0); + Sub3D(tapex, torg, v1); + Cross3D(v0, v1, adNorm); + Normalize3D(adNorm); + Assign3D(v0, adBasisX); + Normalize3D(adBasisX); + Cross3D(adNorm, adBasisX, adBasisY); + + // Project onto a plane (adBasisX, adBasisY). + VProjA[0] = Dot3D(torg, adBasisX); + VProjA[1] = Dot3D(torg, adBasisY); + VProjB[0] = Dot3D(tdest, adBasisX); + VProjB[1] = Dot3D(tdest, adBasisY); + VProjC[0] = Dot3D(tapex, adBasisX); + VProjC[1] = Dot3D(tapex, adBasisY); + VProj [0] = Dot3D(searchpoint, adBasisX); + VProj [1] = Dot3D(searchpoint, adBasisY); + + sign0 = orient2dfast(VProjA, VProjB, VProj); + sign1 = orient2dfast(VProjB, VProjC, VProj); + sign2 = orient2dfast(VProjC, VProjA, VProj); + + if (((sign0 > 0.) == (sign1 > 0.)) && ((sign1 > 0.) == (sign2 > 0.))) { + return ONFACE; + } else { + return OUTSIDE; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// preciselocate() Find a tetrahedron, a face or an edge containing a // +// given point. // +// // +// Begins its search from 'searchtet'. It is important that 'searchtet' be a // +// handle with the property that 'searchpoint' is strictly lies 'above' of // +// the facet denoted by 'searchtet', or is colplanar with that facet and // +// does not intersect that facet. (In particular, 'searchpoint' should not // +// be the vertex of that facet.) // +// // +// Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' is // +// a handle whose origin is the existing vertex. // +// // +// Returns ONEDGE if the point lies on a mesh edge. 'searchtet' is a handle // +// whose face version is the edge on which the point lies. // +// // +// Returns ONFACE if the point lies strictly within a facet. 'searchtet' is // +// a handle whose location is the face on which the point lies. // +// // +// Returns INTETRAHEDRON if the point lies strictly within a tetrahededron. // +// 'searchtet' is a handle on the tetrahedron that contains the point. // +// // +// Returns OUTSIDE if the point lies outside the mesh. 'searchtet' is a // +// handle whose location is the face the point is to 'above' of. This might // +// occur when the circumcenter of a tetrahedron falls just slightly outside // +// the mesh due to floating-point roundoff error. It also occurs when // +// seeking a hole or region point that a foolish user has placed outside the // +// mesh. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. However, // +// it can still be used to find the circumcenter of a triangle, a tetrahedron// +// as long as the search is begun from the tetrahedron in question. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum mesh3d::locateresult +mesh3d::preciselocate(point3d searchpoint, triface* searchtet) +{ + triface checkface, backtracetet; + point3d torg, tdest, toppo; + enum locateresult retval; + int ahead, turns; + + assert(searchtet->tet != dummytet); + if (verbose > 2) { + printf(" Searching for point %d.\n", pointmark(searchpoint)); + } + + while (true) { + // Check if we walk off the boundary of the triangulation. + if (searchtet->tet == dummytet) { + *searchtet = backtracetet; + if (verbose > 2) { + printf(" End Searching: Outside current mesh.\n"); + } + return OUTSIDE; + } + if (verbose > 2) { + printf(" From face (%d, %d, %d, %d)\n", + pointmark(org(*searchtet)), pointmark(dest(*searchtet)), + pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); + } + searchtet->ver = 0; + // 'toppo' is the shared vertex at all orientation tests. + oppo(*searchtet, toppo); + // Check three side faces of 'searchtet' in order. + // To see if we need walk through this face. + for (turns = 0; turns < 3; turns++) { + fnext(*searchtet, checkface); + org(checkface, torg); + dest(checkface, tdest); + ahead = iorient3d(torg, tdest, toppo, searchpoint); + if (ahead == 0) { + // Check if `searchpoint' is locate on face, on edge or on vertex. + retval = iscoplanarintri(searchpoint, &checkface); + if (retval != OUTSIDE) { + *searchtet = checkface; + if (verbose > 2) { + printf(" End Searching: "); + if (retval == ONVERTEX) { + printf("On Vertex."); + } else if (retval == ONEDGE) { + printf("On Edge."); + } else if (retval == ONFACE) { + printf("On Face."); + } + printf("\n"); + } + return retval; + } + } else if (ahead < 0) { + // We need walk through this face and continue to search. + backtracetet = checkface; + sym(checkface, *searchtet); + break; + } + enextself(*searchtet); + } + if (turns >= 3) { + // Found! Inside tetrahedron. + if (verbose > 2) { + printf(" End Searching: Inside tetraheda.\n"); + } + return INTETRAHEDRON; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// locate() Find a tetrahedron, a face or an edge containing a given // +// point. // +// // +// Searching begins from one of: the input 'searchtet', a recently encount- // +// ered tetrahedron 'recenttet', or from a tetrahedra chosen from a random // +// sample. The choice is made by determining which tetrahedron's vertexs is // +// closest to the point we are searcing for. Normally, 'searchtet' should be // +// a handle on the convex hull of the tetrahedrization. // +// // +// On completion, 'searchtet' is a tetrahedron that contains 'searchpoint'. // +// // +// Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' is // +// a handle whose origin is the existing vertex. // +// // +// Returns ONEDGE if the point lies on a mesh edge. 'searchtet' is a handle // +// whose face version is the edge on which the point lies. // +// // +// Returns ONFACE if the point lies strictly within a facet. 1searchtet' is // +// a handle whose location is the face on which the point lies. // +// // +// Returns INTETRAHEDRON if the point lies strictly within a tetrahededron. // +// 'searchtet' is a handle on the tetrahedron that contains the point. // +// // +// Returns OUTSIDE if the point lies outside the mesh. 'searchtet' is a // +// handle whose location is the face the point is to 'above' of. This might // +// occur when the circumcenter of a tetrahedron falls just slightly outside // +// the mesh due to floating-point roundoff error. It also occurs when // +// seeking a hole or region point that a foolish user has placed outside the // +// mesh. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// not generally work after the holes and concavities have been carved. // +// // +// Details on the random sampling method can be found in [3]. // +// // +// The simple method to implement this algorithm use tet-tri data structure // +// is listed here: ( Suppose T is the current tetrahedrization, and p is the // +// point we are searching.) // +// // +// (1). Choose a random facet a (a belong to T), such that p belong to a+. // +// (2). If T union a = NULL, then p lies outside the current triangulation,// +// and a is visible from p. Otherwise, there is a unique tetrahedron // +// t(t include a+, t belong to T), If p belong to t, then p is locat- // +// ed inside(the tetrahedron t(a+) of)T. In both cases we are done.If // +// p not belong to t(a+), then there exists some facet b of the tetr- // +// ahedron t(a+), with the same orientation of a, and p belong to b+. // +// Repeat step (2) with a = b. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum mesh3d::locateresult +mesh3d::locate(point3d searchpoint, triface *searchtet) +{ + triface checkface; + tetrahedron *firsttet, *tetptr; + point3d torg, tdest, tapex; + void **sampleblock; + long sampleblocks, samplesperblock, samplenum; + long tetblocks, i, j; + unsigned long alignptr; + REAL searchdist, dist; + int ahead; + + // Record the distance from the suggested starting tetrahedron to the + // point we seek. + if(searchtet->tet == dummytet) { + // This is an 'Outer Space' handle, get a hull tetrahedron. + searchtet->loc = 0; + symself(*searchtet); + } + searchdist = distance(searchtet, searchpoint); + + // Select "good" candidate using k random samples, taking the closest one. + // (The trick here is that we use just normal FP distance() function.) + + // If a recently encountered tetrahedron has been recorded and has not + // been deallocated, test it as a good starting point. + if (!isdead(&recenttet)) { + // adjustedgering(recenttet, CCW); + dist = distance(&recenttet, searchpoint); + if (dist < searchdist) { + *searchtet = recenttet; + searchdist = dist; + } + } + + // The number of random samples taken is proportional to the cube 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 < tetrahedrons.items) { + samples++; + } + // Find how much blocks in current tet pool. + tetblocks = (tetrahedrons.maxitems + TETPERBLOCK - 1) / TETPERBLOCK; + // Find the average samles per block. Each block at least have 1 sample. + samplesperblock = 1 + (samples / tetblocks); + sampleblocks = samples / samplesperblock; + sampleblock = tetrahedrons.firstblock; + for (i = 0; i < sampleblocks; i++) { + alignptr = (unsigned long) (sampleblock + 1); + firsttet = (tetrahedron *) + (alignptr + (unsigned long) tetrahedrons.alignbytes + - (alignptr % (unsigned long) tetrahedrons.alignbytes)); + for (j = 0; j < samplesperblock; j++) { + if (i == tetblocks - 1) { + // This is the last block. + samplenum = randomnation((int) + (tetrahedrons.maxitems - (i * TETPERBLOCK))); + } else { + samplenum = randomnation(TETPERBLOCK); + } + tetptr = (tetrahedron *) + (firsttet + (samplenum * tetrahedrons.itemwords)); + if (tetptr[4] != (tetrahedron) NULL) { + checkface.tet = tetptr; + dist = distance(&checkface, searchpoint); + if (dist < searchdist) { + *searchtet = checkface; + searchdist = dist; + } + } + } + sampleblock = (void **) *sampleblock; + } + if (verbose > 2) { + printf(" Randomly sampling %d times.\n", sampleblocks * samplesperblock); + } + + // Orient 'searchtet' to fit the preconditions of calling preciselocate(). + adjustedgering(*searchtet, CCW); + org(*searchtet, torg); + dest(*searchtet, tdest); + apex(*searchtet, tapex); + ahead = iorient3d(torg, tdest, tapex, searchpoint); + if (ahead > 0) { + // 'searchpoint' is below the face, get the other side of the face. + // Note: Must check first if searchtet is located on 'Outer Boundary'. + if(!issymexist(searchtet)) { + return OUTSIDE; + } + symself(*searchtet); + } else if (ahead == 0) { + // Check if `searchpoint' is locate on face, on edge or on vertex. + enum locateresult retval = iscoplanarintri(searchpoint, searchtet); + if (retval != OUTSIDE) { + if (verbose > 2) { + printf(" End Searching: "); + if (retval == ONVERTEX) { + printf("On Vertex."); + } else if (retval == ONEDGE) { + printf("On Edge."); + } else if (retval == ONFACE) { + printf("On Face."); + } + printf("\n"); + } + return retval; + } + } + return preciselocate(searchpoint, searchtet); +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Point Location Routines // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Mesh Transformation Routines // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Three-dimensional flip operators and algorithm. // +// // +// The "local transformation" (LTRANS for short) procedure for three- // +// dimensional triangulations is analogous to the edge flipping procedure // +// used first by Lawson[1] to construct two-dimensional triangulations. The // +// LTRANS methods was instroduced (first) by Barry Joe[2]. He proved that // +// the LTRANS can be used to construct a Delaunay triangulation when apply // +// in an appropriate order to a special triangulation with his incermental // +// flip algorithm[2]. // +// // +// The LTRANS procedure is based on the possible configurations of five // +// distinct non-coplanar three-dimensional points, a, b, c, d, e. See Barry // +// Joe 's paper[2] fig 1 and fig 2. There show five possible configurations // +// of the five points, named configuration 1 ,..., configuration 5. Let T be // +// a triangulation of the five points. the LTRANS procedure is that if the // +// five points are in congiguration 1 or 3, then replace the triangulation // +// T by other possible triangulation of the five points.The LTRANS procedure // +// can be consider to be a face swap. Joe also show that the LOTRANS applied // +// to the T are of the one of four types, called type 1 to 4(2-3 swap, 3-2 // +// swap, 2-2 swap, 4-4 swap, See paper[2] fig 3). // +// // +// Let T(i, 0) be the preliminary triangulation of the first i vertices // +// obtained by joining the ith vertex to the visible bondary faces of T(i-1, // +// D). For k >= 1, let T(i, k) be the triangualtion obtained by applying the // +// LOTRANS procedure to a non-locally-optimal transformable(paper[1], defin- // +// ition 1 and 2) interior face of T(i, k-1). The following therom is proved // +// by Barry Joe(paper[2], Therom 3): // +// If T(i-1, D) is a Delaunay triangulation, then there exists a finite // +// m(m >= 0) such that T(i, m) = T(i, D) is a Delaunay triangulation. // +// // +// Another useful notion in programming is face type, which described in // +// [3]. Let abc be an interior face in a triangulation T of a point set V. // +// Then abc is incident on two tetrahedron abcd and abce. We assign a face // +// type to abc of the form 'Trs' and 'Nrs' where 'T' stands for transform- // +// able and 'N' stands for nontransformable, and the possible types are: // +// T23, T32, T22, T44, N44, N40, N30, N20. Except for the case r=s=4, 'r' is // +// number of tetrahedroa in the triangulation of a, b, c, d, e containing // +// abcd and abce, and either 's' is zero if the configuration has only one // +// possible triangulation or 's' is the number of tetrahedron in the other // +// triangulation of a, b, c, d, e. the T44 and N44 types involv a sixth ver- // +// tex and a pair of simultaneous local transformations(involve 4 tetrahedron// +// before and after) as explained in paper[3]. // +// // +// Refernces: // +// // +// [1] Lawson, C.L. (1977), Software for C1 surface interpolation, in J.R. // +// Rice, ed,. Mathematic Software III, Acadmeic Press, New York, 161-194.// +// [2] Barry Joe. Construction of three-dimensional Delaunay triangulations // +// using local transformations. Computer Aided Geometric Design, 8(1991),// +// pp. 123-142. // +// [3] Barry Joe. Construction of Three-Dimensional Improved-Quality Triang- // +// ulati on Using Local Transformations". SIAM Journal on Scientific // +// Computing, 16(6):1292�C1307, November 1995. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// categorizeface() Decide which type of triangular face this is, anyway. // +// // +// The input is a handle of triangular face of a tetrahedron. Return an // +// enumerate type of facecategory. Normally, the input handle will not be // +// changed. If return 'eT32' and 'eN32', function will reset the face // +// version to the edge which is tansformable('eT32') or non-transformable // +// ('eN32'). If return 'eT22', 'eT44', or 'eN44', function will reset the // +// input face version to be the diagonal edge. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum mesh3d::facecategory mesh3d::categorizeface(triface& horiz) +{ + triface symhoriz, abdcasing, bcdcasing, cadcasing; + triface bad, abe, symbad, symabe; + face tmpsh0, tmpsh1, tmpseg; + point3d pa, pb, pc, pd, pe; + point3d abdoppo, bcdoppo, cadoppo; + int sharecount; + + sym(horiz, symhoriz); + if (symhoriz.tet == dummytet) { + return LOCKED; // Can't swap a boundary face. + } + if (checksegments) { + tspivot(horiz, tmpsh0); + if (tmpsh0.sh != dummysh) { + if (!isnonsolid(tmpsh0)) { + return LOCKED; // Can't swap a subface. + } + } + } + // Now we assume 'horiz' is tet<a, b, c, d>, + adjustedgering(horiz, CCW); + // Set 'symhoriz' be tet<b, a, c, e>. + findversion(&symhoriz, &horiz); + + org(horiz, pa); + dest(horiz, pb); + apex(horiz, pc); + oppo(horiz, pd); + oppo(symhoriz, pe); + + // Find the number of tetrahedra that be a neighbor of both of the + // first two. + sharecount = 2; + abdoppo = bcdoppo = cadoppo = (point3d) NULL; + + // Set 'abdcasing', 'bcdcasing', 'cadcasing'. + fnext(horiz, abdcasing); // at edge<a, b>. + if (issymexist(&abdcasing)) { + symself(abdcasing); + oppo(abdcasing, abdoppo); + if (abdoppo == pe) sharecount++; + } + enextself(horiz); + fnext(horiz, bcdcasing); // at edge<b, c>. + if (issymexist(&bcdcasing)) { + symself(bcdcasing); + oppo(bcdcasing, bcdoppo); + if (bcdoppo == pe) sharecount++; + } + enextself(horiz); + fnext(horiz, cadcasing); // at edge<c, a> + if (issymexist(&cadcasing)) { + symself(cadcasing); + oppo(cadcasing, cadoppo); + if (cadoppo == pe) sharecount++; + } + enextself(horiz); // Rewind. + + if (sharecount == 4) { // Four tet case. + return N40; + } + + if (sharecount == 3) { // Three tet case. + if (abdoppo == pe) { + // Check if edge<a, b> cross face<c, d, e>. + if (!isbelowplane(pc, pd, pe, pa)) + return N30; // Either eN30 or eOther. + if (!isaboveplane(pc, pd, pe, pb)) + return N30; // Either eN30 or eOther. + // return eT32; // Return edge<a, b>. + } else if (bcdoppo == pe) { + // Check if edge<b, c> cross the face<a, d, e>. + if (!isbelowplane(pa, pd, pe, pb)) + return N30; // Either eN30 or eOther. + if (!isaboveplane(pa, pd, pe, pc)) + return N30; // Either eN30 or eOther. + enextself(horiz); + // return eT32; // Return edge<b, c>. + } else { + assert(cadoppo == pe); + // Check if edge<c, a> cross the face<b, d, e>. + if (!isbelowplane(pb, pd, pe, pc)) + return N30; // Either eN30 or eOther. + if (!isaboveplane(pb, pd, pe, pa)) + return N30; // Either eN30 or eOther. + enext2self(horiz); + // return T32; // Return edge<c, a>. + } + if (checksegments) { + tsspivot(&horiz, &tmpseg); + if (tmpseg.sh != dummysh) { + // Unfortunately, it's a subsegment, not swappable. + return LOCKED; + } + } + return T32; + } // End of three tet case. + + // Only leave two tets case: 'horiz' and 'symhoriz'. + assert(sharecount == 2); + // These returns take out all cases which allow the saving of an + // orientation primitive evaluation. This is very useful, as + // this is a critical code path. + // Check if e above face<b, c, d>. + int iOrientA = iorient3d(pb, pc, pd, pe); + if (iOrientA == -1) { + enextself(horiz); + return N32; // Return edge<b, c>. + } + // Check if e above face<c, a, d>. + int iOrientB = iorient3d(pc, pa, pd, pe); + if (iOrientB == -1) { + enext2self(horiz); + return N32; // Return edge<c, a>. + } + if (iOrientA + iOrientB == 0) return N20; + // Check if e above face<a, b, d>. + int iOrientC = iorient3d(pa, pb, pd, pe); + if (iOrientC == -1) { + return N32; // Return edge<a, b>. + } + + switch (iOrientA + iOrientB + iOrientC) { + case 0: + // Impossible, but included for completeness. + case 1: + // Two orientations are zero (three points co-linear). Hopelessly + // unswappable. Bail out. + return N20; + case 2: + // Four points are coplanar; (T22, T44, N44) One orientation must + // be 0; verts are re-labeled to make it iOrientC. This implies + // that edge<a, b> is the coplanar edge. + assert(!(iOrientA && iOrientB && iOrientC)); + if (iOrientA == 0) { + // edge<b, c> is the diagonal. + enextself(horiz); + enext2self(symhoriz); + org(horiz, pa); + dest(horiz, pb); + apex(horiz, pc); + } else if (iOrientB == 0) { + // edge<c, a> is the diagonal. + enext2self(horiz); + enextself(symhoriz); + org(horiz, pa); + dest(horiz, pb); + apex(horiz, pc); + } + // Now we can sure edge<a, b> is the diagonal. Verify that the + // re-labeling was done correctly. + // assert(iorient3d(pa, pb, pd, pe) == 0); + if (checksegments) { + tsspivot(&horiz, &tmpseg); + if (tmpseg.sh != dummysh) { + // Unfortunately, it's a subsegment, not swappable. + return LOCKED; + } + } + + // The configuration of these two tets is classified based on the + // properties of the coplanar faces: + // 1 If both are BFaces with the same boundary condition, swappable + // two tets to two. + // 2 If both are BFaces with different bdry cond, not swappable. + // 3 If one is a BFace and the other not, not swappable. + // 4 If neither is a BFace, both opposite cells are tets, and the + // tets have the same fourth vert, swappable four to four. + // 5 If neither is a BFace, both opposite cells are tets, and the + // tets do not have the same fourth vert, not swappable, although + // some non-local transformations might make it so. + // 6 If neither is a BFace and one or both opposite cells is not a + // tet, not swappable. + fnext(horiz, bad); + fnext(symhoriz, abe); + sym(bad, symbad); + sym(abe, symabe); + + if ((symbad.tet == dummytet) && (symabe.tet == dummytet)) { + // Both faces are on the boundary. + if (checksegments) { + tspivot(bad, tmpsh0); + tspivot(abe, tmpsh1); + if ((tmpsh0.sh == dummysh) && (tmpsh1.sh == dummysh)) { + return T22; // case 1. + } else if ((tmpsh0.sh != dummysh) && (tmpsh1.sh != dummysh)) { + if (mark(tmpsh0) == mark(tmpsh1)) { + return T22; // case 1. + } else { + return LOCKED; // case 2. + } + } else { + if (tmpsh0.sh != dummysh) { + if (isnonsolid(tmpsh0)) { + return T22; // case 1. + } + } else { // tmpsh1.sh != dummysh + if (isnonsolid(tmpsh1)) { + return T22; // case 1. + } + } + return LOCKED; // case 2. + } + } else { + return T22; // case 1. + } + } else if ((symbad.tet != dummytet) && (symabe.tet != dummytet)) { + // Both faces are inner facets. + point3d badoppo, abeoppo; + oppo(symbad, badoppo); + oppo(symabe, abeoppo); + if (badoppo == abeoppo) { + if (checksegments) { + tspivot(bad, tmpsh0); + tspivot(abe, tmpsh1); + if ((tmpsh0.sh == dummysh) && (tmpsh1.sh == dummysh)) { + return T44; // case 1. + } else if ((tmpsh0.sh != dummysh) && (tmpsh1.sh != dummysh)) { + if (mark(tmpsh0) == mark(tmpsh1)) { + return T44; // case 1. + } else { + return LOCKED; // case 2. + } + } else { + if (tmpsh0.sh == dummysh) { + if (isnonsolid(tmpsh1)) { + return T44; // case 1. + } + } else { + if (isnonsolid(tmpsh0)) { + return T44; // case 1. + } + } + return LOCKED; // case 2. + } + } else { + return T44; // case 4. + } + } else { + return N44; // case 5. + } + } else { + // Exactly one face on the boundary or internal faces with cells + // other than tets + return LOCKED; // cases 3, 6. + } + case 3: + // Configuration is convex and therefore swappable two tets to three. + return T23; + default: + // No other cases should be possible + assert(0); + return LOCKED; + } // End of switch. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// querydoswap() Determines whether swapping is needed for current seting // +// swaptype. // +// // +// There are many measures can be used, like Delaunay criterion, local max- // +// min solid angle criterion and max-min dihedral angle, etc. Current only // +// use the Delaunay criterion. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool mesh3d::querydoswap(triface& queryface) +{ + triface symface; + point3d symoppo; + + sym(queryface, symface); + assert(symface.tet != dummytet); + oppo(symface, symoppo); + int sign = iinsphere(&queryface, symoppo); + if (sign > 0) { + return (true); + } else if (sign < 0) { + return (false); + } else { + cospherecount ++; + return (false); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// preservesubsegments() Preserve subsegments that abutting a flipping // +// subface(must be nonsolid). // +// // +// This routine used in all local transformation routines to prevent missing // +// subsegments when flip away a nonsolid subface. The inputs are a handle // +// of flipping face 'abc'(must be a nonsolid subface) and a handle of tetra // +// that abutting this face 'abcd'. This routine will check each side of face // +// 'abc', to see if their exist a subsegment abutting at this side. If find, // +// still need determine whether there exist another subfaces hold this // +// subsegment. If no such subface be found. We must insert a subface for // +// holding this subsegment, otherwise, this subsegment will missing after do // +// flip. At each case, the face 'abc' will dealloc at end. Before 'abc' is // +// deallocated, we must dissolve it from its two adjacent tets, otherwise, // +// one of tet may still think it is connecting a subface. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::preservesubsegments(face& abc, triface& abcd) +{ + triface tmpabcd, spintet; + face checkseg, checksh; + point3d tapex; + int edgecount, successbonded, hitbdry; + + tmpabcd = abcd; + adjustedgering(tmpabcd, CCW); // For fnext() should be exist. + findversion(&abc, &tmpabcd, false); // For keep same enext() direction. + + edgecount = 0; + while (edgecount < 3) { + sspivot(abc, checkseg); + if (checkseg.sh != dummysh) { + // Find a subsegment adjoining at a nonsolid subface('abc'). + spivot(checkseg, checksh); + if (checksh.sh != abc.sh) { + // There must exist another subface that hold this segment. We can + // safely deallocte this subface. + } else { + // We must find another subface to hold this segment, if no such + // subface be found. Then we should insert a nonsolid subface. + spintet = tmpabcd; + apex(tmpabcd, tapex); + successbonded = hitbdry = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == tapex) { + break; // Rewind, can leave now. + } + tspivot(spintet, checksh); + if (checksh.sh != dummysh) { + findversion(&checksh, &spintet); + ssbond(checksh, checkseg); + successbonded = 1; + break; + } + } else { + hitbdry ++; + if(hitbdry >= 2) { + break; + } else { + esym(tmpabcd, spintet); + } + } + } + if (!successbonded) { + // Badly, We must insert a subface for holding this subsegment; + // otherwise, this subsegment will missing after do flip. + triface tmptet; + fnext(tmpabcd, tmptet); + insertsubface(&tmptet, NONSOLIDFLAG, 1); + face newsh; + tspivot(tmptet, newsh); + findversion(&newsh, &tmptet); + ssbond(newsh, checkseg); + } + } + } + senextself(abc); + enextself(tmpabcd); + edgecount ++; + } + + // Before dealloc subface, must dissolve it from its two side. + tsdissolve(abcd); + sym(abcd, tmpabcd); + if (tmpabcd.tet != dummytet) { + tsdissolve(tmpabcd); + } + // This subface can be dealloced now. + shellfacedealloc(&subfaces, abc.sh); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip23() Swap two tets for three. // +// // +// See Barry Joe's paper [2] as listed at above. See figure 1 in this paper. // +// We change configuration 1A to 1B. The input 'flipface' is face<abc>. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::flip23(triface& flipface) +{ + triface abcd, bace; // Old configuration. + triface oldabd, oldbcd, oldcad; + triface abdcasing, bcdcasing, cadcasing; + face abdsh, bcdsh, cadsh; + triface oldbae, oldcbe, oldace; + triface baecasing, cbecasing, acecasing; + face baesh, cbesh, acesh; + face abcsh; // Flipping subface. + triface edab, edbc, edca; // New configuration. + point3d pa, pb, pc, pd, pe; + REAL attrib, volume; + int flipcount, i; + + flip_t23s++; + + if (verbose > 2) { + printf(" Do T23 on face (%d, %d, %d, %d).\n", + pointmark(org(flipface)), pointmark(dest(flipface)), + pointmark(apex(flipface)), pointmark(oppo(flipface))); + } + + abcd = flipface; + abcd.ver = 0; // adjust at edge<ab>. + sym(abcd, bace); + findversion(&bace, &abcd); // adjust at edge<ba>. + // Keep all old faces and their casings before doflip. + oldabd = oldbcd = oldcad = abcd; + fnextself(oldabd); + enextfnextself(oldbcd); + enext2fnextself(oldcad); + oldbae = oldcbe = oldace = bace; + fnextself(oldbae); + enext2fnextself(oldcbe); + enextfnextself(oldace); + sym(oldabd, abdcasing); + sym(oldbcd, bcdcasing); + sym(oldcad, cadcasing); + sym(oldbae, baecasing); + sym(oldcbe, cbecasing); + sym(oldace, acecasing); + if (checksegments) { + // Check the flip face<a, b, c> 's three edges to see if there exist + // subsegment. This step must do first. + tspivot(abcd, abcsh); + if (abcsh.sh != dummysh) { + assert(isnonsolid(abcsh)); + preservesubsegments(abcsh, abcd); + } + // Now, we can find all subfaces abutting faces in old configuration. + tspivot(oldabd, abdsh); + tspivot(oldbcd, bcdsh); + tspivot(oldcad, cadsh); + tspivot(oldbae, baesh); + tspivot(oldcbe, cbesh); + tspivot(oldace, acesh); + } + org (abcd, pa); + dest(abcd, pb); + apex(abcd, pc); + oppo(abcd, pd); + oppo(bace, pe); + + // Set new configuration. + edab.tet = abcd.tet; + // default: 'edab' loc = 0, ver = 0. + setorg (edab, pe); + setdest(edab, pd); + setapex(edab, pa); + setoppo(edab, pb); + + edbc.tet = bace.tet; + // defult: 'edbc' loc = 0, ver = 0. + setorg (edbc, pe); + setdest(edbc, pd); + setapex(edbc, pb); + setoppo(edbc, pc); + + maketetrahedron(&edca); + // default: 'edca' loc = 0, ver = 0. + 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 < eextras; i++) { + attrib = elemattribute(abcd.tet, i); + setelemattribute(edca.tet, i, attrib); + } + // Set the volume constraint of the new tetrahedron('edca'). + if (varvolume) { + volume = volumebound(abcd.tet); + setvolumebound(edca.tet, volume); + } + + // There may be shell facets that need to be bonded to new configuarton. + if (checksegments) { + // Clear old flags in 'edab'('abcd') and 'edbc'('bace'). + for (i = 0; i < 4; i ++) { + edab.loc = i; + tsdissolve(edab); + edbc.loc = i; + tsdissolve(edbc); + } + if (abdsh.sh != dummysh) { + edab.loc = 2; // face<a, b, d>. + tsbond(edab, abdsh); + } + if (baesh.sh != dummysh) { + edab.loc = 3; // face<b, a, e>. + tsbond(edab, baesh); + } + if (bcdsh.sh != dummysh) { + edbc.loc = 2; // face<b, c, d>. + tsbond(edbc, bcdsh); + } + if (cbesh.sh != dummysh) { + edbc.loc = 3; // face<c, b, e>. + tsbond(edbc, cbesh); + } + if (cadsh.sh != dummysh) { + edca.loc = 2; // face<c, a, d>. + tsbond(edca, cadsh); + } + if (acesh.sh != dummysh) { + edca.loc = 3; // face<a, c, e>. + tsbond(edca, acesh); + } + } + + // Clear old bonds in 'edab'('abcd') and 'edbc'('bace'). + for (i = 0; i < 4; i ++) { + edab.loc = i; + dissolve(edab); + edbc.loc = i; + dissolve(edbc); + } + // Bond the three tetrahedra. + 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 each casing faces. + 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); + + edab.loc = 0; + edbc.loc = 0; + edca.loc = 0; +#ifdef SELF_CHECK + if (!isaboveplane(&edab, pb)) { + printf("Internal error in flip23():\n"); + printf(" Clockwise tetrahedron after flip (edab).\n"); + internalerror(); + } + if (!isaboveplane(&edbc, pc)) { + printf("Internal error in flip23():\n"); + printf(" Clockwise tetrahedron after flip (edbc).\n"); + internalerror(); + } + if (!isaboveplane(&edca, pa)) { + printf("Internal error in flip23():\n"); + printf(" Clockwise tetrahedron after flip (edca).\n"); + internalerror(); + } +#endif // defined SELF_CHECK + if (verbose > 2) { + printf(" Updating edab "); + dump(&edab); + printf(" Updating edbc "); + dump(&edbc); + printf(" Creating edca "); + dump(&edca); + } + + if(usefliplist) { + flipcount = 0; + edab.loc = 2; // face<a, b, d>. + enqueuefliplist(edab); + edab.loc = 3; // face<b, a, e>. + enqueuefliplist(edab); + edbc.loc = 2; // face<b, c, d>. + enqueuefliplist(edbc); + edbc.loc = 3; // face<c, b, e>. + enqueuefliplist(edbc); + edca.loc = 2; // face<c, a, d>. + enqueuefliplist(edca); + edca.loc = 3; // face<a, c, e>. + enqueuefliplist(edca); + } else { + flipcount = 1; + edab.loc = 2; // face<a, b, d>. + flipcount += flip(edab); + edab.loc = 3; // face<b, a, e>. + flipcount += flip(edab); + edbc.loc = 2; // face<b, c, d>. + flipcount += flip(edbc); + edbc.loc = 3; // face<c, b, e>. + flipcount += flip(edbc); + edca.loc = 2; // face<c, a, d>. + flipcount += flip(edca); + edca.loc = 3; // face<a, c, e>. + flipcount += flip(edca); + } + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip32() Swap three tets for two. // +// // +// See Barry Joe's paper [2] as listed at above. See figure 1 in this paper. // +// We change configuration 1B to 1A. The input 'flipface' is edge<de>. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::flip32(triface& flipface) +{ + triface edab, edbc, edca; // Old configuration. + triface oldabd, oldbcd, oldcad; + triface abdcasing, bcdcasing, cadcasing; + face abdsh, bcdsh, cadsh; + triface oldbae, oldcbe, oldace; + triface baecasing, cbecasing, acecasing; + face baesh, cbesh, acesh; + triface abcd, bace; // New configuration. + point3d pa, pb, pc, pd, pe; + int flipcount, i; + + flip_t32s++; + + if (verbose > 2) { + printf(" Do T32 on face (%d, %d, %d, %d).\n", + pointmark(org(flipface)), pointmark(dest(flipface)), + pointmark(apex(flipface)), pointmark(oppo(flipface))); + } + + // 'flipface' must be tet<e, d, a, b> or tet<d, e, a, -b>. + edab = flipface; + // Adjust face version first, so 'edab' be tet<e, d, a, b>. + adjustedgering(edab, CCW); + // Set 'edbc' and 'edca'. + fnext(edab, edbc); + symself(edbc); + findversion(&edbc, &edab, 0); + fnext(edbc, edca); + symself(edca); + findversion(&edca, &edab, 0); + // Keep all old faces and their casings before doflip. + oldabd = oldbae = edab; + enextfnextself(oldabd); + enext2fnextself(oldbae); + oldbcd = oldcbe = edbc; + enextfnextself(oldbcd); + enext2fnextself(oldcbe); + oldcad = oldace = edca; + enextfnextself(oldcad); + enext2fnextself(oldace); + sym(oldabd, abdcasing); + sym(oldbcd, bcdcasing); + sym(oldcad, cadcasing); + sym(oldbae, baecasing); + sym(oldcbe, cbecasing); + sym(oldace, acecasing); + if (checksegments) { + // Check faces around flip edge<e, d> to see if there exist subsegment + // attaching it. This step must do first. The three face are ed(abc). + face tmpface; + tspivot(edab, tmpface); + if (tmpface.sh != dummysh) { + assert(isnonsolid(tmpface)); + preservesubsegments(tmpface, edab); + } + tspivot(edbc, tmpface); + if (tmpface.sh != dummysh) { + assert(isnonsolid(tmpface)); + preservesubsegments(tmpface, edbc); + } + tspivot(edca, tmpface); + if (tmpface.sh != dummysh) { + assert(isnonsolid(tmpface)); + preservesubsegments(tmpface, edca); + } + // Find all subfaces abutting faces in old configuration. + tspivot(oldabd, abdsh); + tspivot(oldbcd, bcdsh); + tspivot(oldcad, cadsh); + tspivot(oldbae, baesh); + tspivot(oldcbe, cbesh); + tspivot(oldace, acesh); + } + apex(edab, pa); + oppo(edab, pb); + oppo(edbc, pc); + dest(edab, pd); + org (edab, pe); + + // Set new configuration. + abcd.tet = edab.tet; + // default: 'abcd' loc = 0, ver = 0. + setorg (abcd, pa); + setdest(abcd, pb); + setapex(abcd, pc); + setoppo(abcd, pd); + + bace.tet = edbc.tet; + // default: 'bace' loc = 0, ver = 0. + setorg (bace, pb); + setdest(bace, pa); + setapex(bace, pc); + setoppo(bace, pe); + + // In flip32 case, we needn't reset element attributes and volume + // constraint, because no new tetrahedron be created. + + // There may be shell facets that need to be bonded to the new + // configuration. + if (checksegments) { + // Clear old flags in 'abcd'('edab') and 'bace'('edbc'). + for (i = 0; i < 4; i ++) { + abcd.loc = i; + tsdissolve(abcd); + bace.loc = i; + tsdissolve(bace); + } + if (abdsh.sh != dummysh) { + abcd.loc = 1; // face<a, b, d>. + tsbond(abcd, abdsh); + } + if (baesh.sh != dummysh) { + bace.loc = 1; // face<b, a, e>. + tsbond(bace, baesh); + } + if (bcdsh.sh != dummysh) { + abcd.loc = 2; // face<b, c, d>. + tsbond(abcd, bcdsh); + } + if (cbesh.sh != dummysh) { + bace.loc = 3; // face<c, b, e>. + tsbond(bace, cbesh); + } + if (cadsh.sh != dummysh) { + abcd.loc = 3; // face<c, a, d>. + tsbond(abcd, cadsh); + } + if (acesh.sh != dummysh) { + bace.loc = 2; // face<a, c, e>. + tsbond(bace, acesh); + } + } + + for (i = 0; i < 4; i ++) { + abcd.loc = i; + dissolve(abcd); + bace.loc = i; + dissolve(bace); + } + // Bond the new configuration. + abcd.loc = 0; + bace.loc = 0; + bond(abcd, bace); + // Bond each casing faces. + 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); + + abcd.loc = 0; + bace.loc = 0; +#ifdef SELF_CHECK + if (!isaboveplane(&abcd, pd)) { + printf("Internal error in flip32():\n"); + printf(" Clockwise tetrahedron after flip (abcd).\n"); + internalerror(); + } + if (!isaboveplane(&bace, pe)) { + printf("Internal error in flip32():\n"); + printf(" Clockwise tetrahedron after flip (bace).\n"); + internalerror(); + } +#endif // defined SELF_CHECK + if (verbose > 2) { + printf(" Updating abcd "); + dump(&abcd); + printf(" Updating bace "); + dump(&bace); + printf(" Deleting edca "); + dump(&edca); + } + // Dealloc 'edca'. + tetrahedrondealloc(edca.tet); + + if (usefliplist) { + flipcount = 0; + abcd.loc = 1; // face<a, b, d>. + enqueuefliplist(abcd); + bace.loc = 1; // face<b, a, e>. + enqueuefliplist(bace); + abcd.loc = 2; // face<b, c, d>. + enqueuefliplist(abcd); + bace.loc = 3; // face<c, b, e>. + enqueuefliplist(bace); + abcd.loc = 3; // face<c, a, d>. + enqueuefliplist(abcd); + bace.loc = 2; // face<a, c, e>. + enqueuefliplist(bace); + } else { + flipcount = 1; + abcd.loc = 1; // face<a, b, d>. + flipcount += flip(abcd); + bace.loc = 1; // face<b, a, e>. + flipcount += flip(bace); + abcd.loc = 2; // face<b, c, d>. + flipcount += flip(abcd); + bace.loc = 3; // face<c, b, e>. + flipcount += flip(bace); + abcd.loc = 3; // face<c, a, d>. + flipcount += flip(abcd); + bace.loc = 2; // face<a, c, e>. + flipcount += flip(bace); + } + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip22() Swap two tets for two, in the case where two faces are // +// coplanar. // +// // +// See Barry Joe's paper [2] as listed at above. See figure 1 in this paper. // +// We change configuration 3A to 3B. The input 'flipface' lock at edge<ab>. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::flip22(triface& flipface) +{ + triface abcd, bace; // Old configuration. + triface oldbcd, oldcad, oldcbe, oldace; + triface bcdcasing, cadcasing, cbecasing, acecasing; + face bcdsh, cadsh, cbesh, acesh; + triface oldabd, oldbae; // Flipping faces. + face abdsh, baesh, abcsh; // Flipping subfaces. + face abdrightseg, abdleftseg; + face baerightseg, baeleftseg; + triface ceda, cdeb; // New configuration. + face debsh, edash; + triface ghosttet; + point3d pa, pb, pc, pd, pe; + int flipcount, i; + + flip_t22s++; + + if (verbose > 2) { + printf(" Do T22 on face (%d, %d, %d, %d).\n", + pointmark(org(flipface)), pointmark(dest(flipface)), + pointmark(apex(flipface)), pointmark(oppo(flipface))); + } + + // Set a 'ghosttet' handle the Outer space. + ghosttet.tet = dummytet; + + abcd = flipface; + adjustedgering(abcd, CCW); + sym(abcd, bace); + findversion(&bace, &abcd); + oldbcd = oldcad = abcd; + enextfnextself(oldbcd); + enext2fnextself(oldcad); + oldcbe = oldace = bace; + enext2fnextself(oldcbe); + enextfnextself(oldace); + sym(oldbcd, bcdcasing); + sym(oldcad, cadcasing); + sym(oldcbe, cbecasing); + sym(oldace, acecasing); + if (checksegments) { + // Check the flip face<a, b, c> 's three edges to see if there exist + // subsegment. This step must do first. + tspivot(abcd, abcsh); + if (abcsh.sh != dummysh) { + assert(isnonsolid(abcsh)); + preservesubsegments(abcsh, abcd); + } + // Now, we can find all subfaces abutting faces in old configuration. + tspivot(oldbcd, bcdsh); + tspivot(oldcad, cadsh); + tspivot(oldcbe, cbesh); + tspivot(oldace, acesh); + // Keep flip edge<ab> and its two(coplanar) flip faces. + fnext(abcd, oldabd); + fnext(bace, oldbae); + tspivot(oldabd, abdsh); + tspivot(oldbae, baesh); + if ((abdsh.sh != dummysh) || (baesh.sh != dummysh)) { + // Check if there missing a half subface. + if (abdsh.sh == dummysh) { + assert(baesh.sh != dummysh); + insertsubface(&oldabd, NONSOLIDFLAG, 1); + tspivot(oldabd, abdsh); + } else if (baesh.sh == dummysh) { + assert(abdsh.sh != dummysh); + insertsubface(&oldbae, NONSOLIDFLAG, 1); + tspivot(oldbae, baesh); + } + // Save segments which adhering to 'abdsh'. + findversion(&abdsh, &abcd, 0); // lock at edge<ab>. + senextself(abdsh); // arrive edge<bd>. + sspivot(abdsh, abdrightseg); + senextself(abdsh); // arrive edge<da> + sspivot(abdsh, abdleftseg); + // Save segments which adhering to 'baesh'. + findversion(&baesh, &bace, 0); // lock at edge<ba>. + senextself(baesh); // arrive edge<ae>. + sspivot(baesh, baerightseg); + senextself(baesh); // arrive edge<eb>. + sspivot(baesh, baeleftseg); + } + } + org (abcd, pa); + dest(abcd, pb); + apex(abcd, pc); + oppo(abcd, pd); + oppo(bace, pe); + + // Set new configuration. + ceda.tet = abcd.tet; + // default: 'ceda' loc = 0, ver = 0. + setorg (ceda, pc); + setdest(ceda, pe); + setapex(ceda, pd); + setoppo(ceda, pa); + + cdeb.tet = bace.tet; + // default: 'cdeb' loc = 0, ver = 0. + setorg (cdeb, pc); + setdest(cdeb, pd); + setapex(cdeb, pe); + setoppo(cdeb, pb); + + // In flip22 case, we needn't reset element attributes and volume + // constraint, because no new tetrahedron be created. + + // There may be shell facets that need to be bonded to the new + // configuration. + if (checksegments) { + // Clear old flags in 'ceda'('abcd') and 'cdeb'('bace'). + for (i = 0; i < 4; i ++) { + ceda.loc = i; + tsdissolve(ceda); + cdeb.loc = i; + tsdissolve(cdeb); + } + if (bcdsh.sh != dummysh) { + cdeb.loc = 1; // face<b, c, d>. + tsbond(cdeb, bcdsh); + } + if (cbesh.sh != dummysh) { + cdeb.loc = 3; // face<c, b, e>. + tsbond(cdeb, cbesh); + } + if (cadsh.sh != dummysh) { + ceda.loc = 3; // face<c, a, d>. + tsbond(ceda, cadsh); + } + if (acesh.sh != dummysh) { + ceda.loc = 1; // face<a, c, e>. + tsbond(ceda, acesh); + } + if (abdsh.sh != dummysh) { + debsh.sh = abdsh.sh; + // default: 'debsh' ver = 0. + setsorg (debsh, pd); + setsdest(debsh, pe); + setsapex(debsh, pb); + + edash.sh = baesh.sh; + // default: 'edash' ver = 0. + setsorg (edash, pe); + setsdest(edash, pd); + setsapex(edash, pa); + + ssdissolve(debsh); + ssdissolve(edash); + senextself(debsh); + ssbond(debsh, baeleftseg); + senextself(debsh); + ssbond(debsh, abdrightseg); + senextself(edash); + ssbond(edash, abdleftseg); + senextself(edash); + ssbond(edash, baerightseg); + + // Bond with cdeb(loc = 2). + cdeb.loc = 2; + tsbond(cdeb, debsh); + // Bond other side of cdeb('Outer space'). + sesymself(debsh); + tsbond(ghosttet, debsh); + // Bond with ceda. + ceda.loc = 2; + tsbond(ceda, edash); + // Bond other side of ceda('Outer space'). + sesymself(edash); + tsbond(ghosttet, edash); + } + } + + // Clear old flags in 'ceda'('abcd') and 'cdeb'('bace'). + for (i = 0; i < 4; i ++) { + ceda.loc = i; + dissolve(ceda); + cdeb.loc = i; + dissolve(cdeb); + } + // Bond the new configuration. + ceda.loc = 0; + cdeb.loc = 0; + bond(ceda, cdeb); + // Bond each casing facets. + cdeb.loc = 1; + bond(cdeb, bcdcasing); + ceda.loc = 3; + bond(ceda, cadcasing); + cdeb.loc = 3; + bond(cdeb, cbecasing); + ceda.loc = 1; + bond(ceda, acecasing); + // Bond 'Outer space', diffrent in flip44. + cdeb.loc = 2; + bond(cdeb, ghosttet); + ceda.loc = 2; + bond(ceda, ghosttet); + + ceda.loc = 0; + cdeb.loc = 0; +#ifdef SELF_CHECK + if (!isaboveplane(&ceda, pa)) { + printf("Internal error in flip22():\n"); + printf(" Clockwise tetrahedron after flip (ceda).\n"); + internalerror(); + } + if (!isaboveplane(&cdeb, pb)) { + printf("Internal error in flip22():\n"); + printf(" Clockwise tetrahedron after flip (cdeb).\n"); + internalerror(); + } +#endif // defined SELF_CHECK + if (verbose > 2) { + printf(" Updating ceda "); + dump(&ceda); + printf(" Updating cdeb "); + dump(&cdeb); + } + + if (usefliplist) { + flipcount = 0; + ceda.loc = 3; // face<c, a, d>. + enqueuefliplist(ceda); + ceda.loc = 1; // face<a, c, e>. + enqueuefliplist(ceda); + cdeb.loc = 1; // face<b, c, d>. + enqueuefliplist(cdeb); + cdeb.loc = 3; // face<c, b, e>. + enqueuefliplist(cdeb); + } else { + flipcount = 1; + ceda.loc = 3; // face<c, a, d>. + flipcount += flip(ceda); + ceda.loc = 1; // face<a, c, e>. + flipcount += flip(ceda); + cdeb.loc = 1; // face<b, c, d>. + flipcount += flip(cdeb); + cdeb.loc = 3; // face<c, b, e>. + flipcount += flip(cdeb); + } + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip44() Swap four tets for four, in the case where two faces are // +// coplanar. // +// // +// See Barry Joe's paper [2] as listed at above. See figure 1 in this paper. // +// We change configuration 3A to 3B. The input 'flipface' lock at edge<ab>. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::flip44(triface& flipface) +{ + triface abcd, bace, bafd, abfe; // Old configuration. + triface oldbcd, oldcad, oldcbe, oldace; + triface oldfbd, oldafd, oldbfe, oldfae; + triface bcdcasing, cadcasing, cbecasing, acecasing; + triface fbdcasing, afdcasing, bfecasing, faecasing; + face bcdsh, cadsh, cbesh, acesh; + face fbdsh, afdsh, bfesh, faesh; + triface oldabd, oldbae; // Flipping faces. + face abdsh, baesh, abcsh, abfsh; // Flipping subfaces. + face abdrightseg, abdleftseg; + face baerightseg, baeleftseg; + triface ceda, cdeb, fdea, fedb; // New configuration. + face debsh, edash; + point3d pa, pb, pc, pd, pe, pf; + int flipcount, i; + + flip_t44s++; + + if (verbose > 2) { + printf(" Do T44 on face (%d, %d, %d, %d).\n", + pointmark(org(flipface)), pointmark(dest(flipface)), + pointmark(apex(flipface)), pointmark(oppo(flipface))); + } + + abcd = flipface; + adjustedgering(abcd, CCW); + sym(abcd, bace); + findversion(&bace, &abcd); + + // Find the other side two tets of 'abcd' and 'bace'. + bafd = abcd; + fnextself(bafd); + fnextself(bafd); + esymself(bafd); + abfe = bace; + fnextself(abfe); + fnextself(abfe); + esymself(abfe); + + oldbcd = oldcad = abcd; + enextfnextself(oldbcd); + enext2fnextself(oldcad); + oldcbe = oldace = bace; + enext2fnextself(oldcbe); + enextfnextself(oldace); + + oldfbd = oldafd = bafd; + enext2fnextself(oldfbd); + enextfnextself(oldafd); + oldbfe = oldfae = abfe; + enextfnextself(oldbfe); + enext2fnextself(oldfae); + + sym(oldbcd, bcdcasing); + sym(oldcad, cadcasing); + sym(oldcbe, cbecasing); + sym(oldace, acecasing); + + sym(oldfbd, fbdcasing); + sym(oldafd, afdcasing); + sym(oldbfe, bfecasing); + sym(oldfae, faecasing); + + if (checksegments) { + // Check the flip face<a, b, c> 's three edges to see if there exist + // subsegment. This step must do first. + tspivot(abcd, abcsh); + if (abcsh.sh != dummysh) { + assert(isnonsolid(abcsh)); + preservesubsegments(abcsh, abcd); + } + // Now, we can find all subfaces abutting faces in old configuration. + tspivot(oldbcd, bcdsh); + tspivot(oldcad, cadsh); + tspivot(oldcbe, cbesh); + tspivot(oldace, acesh); + + // Check the flip face<a, b, f> 's three edges to see if there exist + // subsegment. This step must do first. + tspivot(bafd, abfsh); + if (abfsh.sh != dummysh) { + assert(isnonsolid(abfsh)); + preservesubsegments(abfsh, bafd); + } + // Now, we can find all subfaces abutting faces in old configuration. + tspivot(oldfbd, fbdsh); + tspivot(oldafd, afdsh); + tspivot(oldbfe, bfesh); + tspivot(oldfae, faesh); + + // Keep flip edge and (coplanar) flip faces. This codes are diffrent + // with flip22. 'abdsh' and 'baesh' may be 'dummysh'. + fnext(abcd, oldabd); + fnext(bace, oldbae); + tspivot(oldabd, abdsh); + tspivot(oldbae, baesh); + if ((abdsh.sh != dummysh) || (baesh.sh != dummysh)) { + // Check if there missing a half subface. + if (abdsh.sh == dummysh) { + assert(baesh.sh != dummysh); + insertsubface(&oldabd, NONSOLIDFLAG, 1); + tspivot(oldabd, abdsh); + } else if (baesh.sh == dummysh) { + assert(abdsh.sh != dummysh); + insertsubface(&oldbae, NONSOLIDFLAG, 1); + tspivot(oldbae, baesh); + } + // Save segments which adhering to 'abdsh'. + findversion(&abdsh, &abcd, 0); // lock at edge<ab>. + senextself(abdsh); + sspivot(abdsh, abdrightseg); + senextself(abdsh); + sspivot(abdsh, abdleftseg); + // Save segments which adhering to 'baesh'. + findversion(&baesh, &bace, 0); // lock at edge<ba>. + senextself(baesh); + sspivot(baesh, baerightseg); + senextself(baesh); + sspivot(baesh, baeleftseg); + } + } + org (abcd, pa); + dest(abcd, pb); + apex(abcd, pc); + oppo(abcd, pd); + oppo(bace, pe); + apex(bafd, pf); + + // Set new configuration. + ceda.tet = abcd.tet; + // default: 'ceda' loc = 0, ver = 0. + setorg (ceda, pc); + setdest(ceda, pe); + setapex(ceda, pd); + setoppo(ceda, pa); + + cdeb.tet = bace.tet; + // default: 'cdeb' loc = 0, ver = 0. + setorg (cdeb, pc); + setdest(cdeb, pd); + setapex(cdeb, pe); + setoppo(cdeb, pb); + + fdea.tet = bafd.tet; + // default: 'fdea' loc = 0, ver = 0. + setorg (fdea, pf); + setdest(fdea, pd); + setapex(fdea, pe); + setoppo(fdea, pa); + + fedb.tet = abfe.tet; + // default: 'fedb' loc = 0, ver = 0. + setorg (fedb, pf); + setdest(fedb, pe); + setapex(fedb, pd); + setoppo(fedb, pb); + + // In flip44 case, we needn't reset element attributes and volume + // constraint, because no new tetrahedron be created. + + // There may be shell facets that need to be bonded to the new + // configuration. + if (checksegments) { + // Clear old flags in 'ceda'('abcd') and 'cdeb'('bace'). + for (i = 0; i < 4; i ++) { + ceda.loc = i; + tsdissolve(ceda); + cdeb.loc = i; + tsdissolve(cdeb); + } + if (bcdsh.sh != dummysh) { + cdeb.loc = 1; // face<b, c, d>. + tsbond(cdeb, bcdsh); + } + if (cbesh.sh != dummysh) { + cdeb.loc = 3; // face<c, b, e>. + tsbond(cdeb, cbesh); + } + if (cadsh.sh != dummysh) { + ceda.loc = 3; // face<c, a, d>. + tsbond(ceda, cadsh); + } + if (acesh.sh != dummysh) { + ceda.loc = 1; // face<a, c, e>. + tsbond(ceda, acesh); + } + + // Clear old flags in 'fdea'('bafd') and 'fedb'('abfe'). + for (i = 0; i < 4; i ++) { + fdea.loc = i; + tsdissolve(fdea); + fedb.loc = i; + tsdissolve(fedb); + } + if (fbdsh.sh != dummysh) { + fedb.loc = 3; // face<f, b, d>. + tsbond(fedb, fbdsh); + } + if (bfesh.sh != dummysh) { + fedb.loc = 1; // face<b, f, e>. + tsbond(fedb, bfesh); + } + if (afdsh.sh != dummysh) { + fdea.loc = 1; // face<a, f, d>. + tsbond(fdea, afdsh); + } + if (faesh.sh != dummysh) { + fdea.loc = 3; // face<f, a, e>. + tsbond(fdea, faesh); + } + if (abdsh.sh != dummysh) { + debsh.sh = abdsh.sh; + // default: 'debsh' ver = 0. + setsorg (debsh, pd); + setsdest(debsh, pe); + setsapex(debsh, pb); + + edash.sh = baesh.sh; + // default: 'edash' ver = 0. + setsorg (edash, pe); + setsdest(edash, pd); + setsapex(edash, pa); + + ssdissolve(debsh); + ssdissolve(edash); + senextself(debsh); + ssbond(debsh, baeleftseg); + senextself(debsh); + ssbond(debsh, abdrightseg); + senextself(edash); + ssbond(edash, abdleftseg); + senextself(edash); + ssbond(edash, baerightseg); + + // Sandwich between 2 tets. + cdeb.loc = 2; + tsbond(cdeb, debsh); + sesymself(debsh); // Don't forget to change edgering. + fedb.loc = 2; + tsbond(fedb, debsh); + // Sandwich between 2 tets. + ceda.loc = 2; + tsbond(ceda, edash); + sesymself(edash); // Don't forget to change edgering. + fdea.loc = 2; + tsbond(fdea, edash); + } + } + + for (i = 0; i < 4; i ++) { + ceda.loc = i; + dissolve(ceda); + cdeb.loc = i; + dissolve(cdeb); + } + // Bond the new configuration. + ceda.loc = 0; + cdeb.loc = 0; + bond(ceda, cdeb); + // Bond each casing facets. + cdeb.loc = 1; + bond(cdeb, bcdcasing); + ceda.loc = 3; + bond(ceda, cadcasing); + cdeb.loc = 3; + bond(cdeb, cbecasing); + ceda.loc = 1; + bond(ceda, acecasing); + + for (i = 0; i < 4; i ++) { + fdea.loc = i; + dissolve(fdea); + fedb.loc = i; + dissolve(fedb); + } + // Bond the two new tetrahedron at other side. + fdea.loc = 0; + fedb.loc = 0; + bond(fdea, fedb); + // Bond each casing facets. + fedb.loc = 3; + bond(fedb, fbdcasing); + fdea.loc = 1; + bond(fdea, afdcasing); + fedb.loc = 1; + bond(fedb, bfecasing); + fdea.loc = 3; + bond(fdea, faecasing); + + // Bond two side tets. + ceda.loc = 2; + fdea.loc = 2; + bond(ceda, fdea); + cdeb.loc = 2; + fedb.loc = 2; + bond(cdeb, fedb); + + ceda.loc = 0; + cdeb.loc = 0; + fdea.loc = 0; + fedb.loc = 0; +#ifdef SELF_CHECK + if (!isaboveplane(&ceda, pa)) { + printf("Internal error in flip44():\n"); + printf(" Clockwise tetrahedron after flip (ceda).\n"); + internalerror(); + } + if (!isaboveplane(&cdeb, pb)) { + printf("Internal error in flip44():\n"); + printf(" Clockwise tetrahedron after flip (cdeb).\n"); + internalerror(); + } + if (!isaboveplane(&fdea, pa)) { + printf("Internal error in flip44():\n"); + printf(" Clockwise tetrahedron after flip (fdea).\n"); + internalerror(); + } + if (!isaboveplane(&fedb, pb)) { + printf("Internal error in flip44():\n"); + printf(" Clockwise tetrahedron after flip (fedb).\n"); + internalerror(); + } +#endif // defined SELF_CHECK + if (verbose > 2) { + printf(" Updating ceda "); + dump(&ceda); + printf(" Updating cdeb "); + dump(&cdeb); + printf(" Updating fdea "); + dump(&fdea); + printf(" Updating fedb "); + dump(&fedb); + } + + if (usefliplist) { + flipcount = 0; + ceda.loc = 3; // face<c, a, d>. + enqueuefliplist(ceda); + ceda.loc = 1; // face<a, c, e>. + enqueuefliplist(ceda); + cdeb.loc = 1; // face<b, c, d>. + enqueuefliplist(cdeb); + cdeb.loc = 3; // face<c, b, e>. + enqueuefliplist(cdeb); + fdea.loc = 1; // face<a, f, d>. + enqueuefliplist(fdea); + fdea.loc = 3; // face<f, a, e>. + enqueuefliplist(fdea); + fedb.loc = 3; // face<f, b, d>. + enqueuefliplist(fedb); + fedb.loc = 1; // face<b, f, e>. + enqueuefliplist(fedb); + } else { + flipcount = 1; + ceda.loc = 3; // face<c, a, d>. + flipcount += flip(ceda); + ceda.loc = 1; // face<a, c, e>. + flipcount += flip(ceda); + cdeb.loc = 1; // face<b, c, d>. + flipcount += flip(cdeb); + cdeb.loc = 3; // face<c, b, e>. + flipcount += flip(cdeb); + fdea.loc = 1; // face<a, f, d>. + flipcount += flip(fdea); + fdea.loc = 3; // face<f, a, e>. + flipcount += flip(fdea); + fedb.loc = 3; // face<f, b, d>. + flipcount += flip(fedb); + fedb.loc = 1; // face<b, f, e>. + flipcount += flip(fedb); + } + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip() Swap away face if this is legal and improves the mesh quality // +// measure. // +// // +// Swapping typically propagates through the mesh, and the return value is // +// the total number of swaps done during this invocation. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::flip(triface& flipface) +{ + // If the face has been removed, don't bother + if (isdead(&flipface)) return (0); + + enum facecategory fc = categorizeface(flipface); + // Determine the preferable configuration and swap if necessary. + switch (fc) { + // These cases are handled by edge swapping. + case N44: + case N32: + // if (edgeswapallow) { + // if (querydoswap(flipface)) { + // return edgeswap(flipface, 0); + // } + // } + break; + // These cases are definitely unswappable + case N40: + case N30: + case N20: + case LOCKED: + break; + case T44: + if (querydoswap(flipface)) { + return flip44(flipface); + } + break; + case T22: + if (querydoswap(flipface)) { + return flip22(flipface); + } + break; + case T23: + if (querydoswap(flipface)) { + return flip23(flipface); + } + break; + case T32: + if (querydoswap(flipface)) { + return flip32(flipface); + } + break; + // Catch-all for bad cases + default: + // Shouldn't be here: face wasn't categorized. + break; + } + checkquality(&flipface); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// enqueuefliplist() Add a face which may be non-Delaunay to 'fliplist', // +// so we can batch process all (to be flipped) faces // +// together rather than flip a face at one time. // +// // +// This routine couple with dequeuefliplist() and dofliplist() are used for // +// the implementation of Randomized Incremental Delaunay Algorithm. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::enqueuefliplist(triface& flipface) +{ + badface3d queface; + + queface.badfacetet = flipface; + org (flipface, queface.faceorg); + dest(flipface, queface.facedest); + apex(flipface, queface.faceapex); + if (verbose > 2) { + printf(" Queueing flip face: (%d, %d, %d).\n", + pointmark(queface.faceorg), pointmark(queface.facedest), + pointmark(queface.faceapex)); + } + fliplist->push(&queface); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dequeuefliplist() Get a exist face from 'fliplist', check its Delaunay // +// -hood and perform corresponding flip operator if it // +// is a non-Delaunay face. // +// // +// This routine couple with enqueuefliplist() and dofliplist() are used for // +// the implementation of Randomized Incremental Delaunay Algorithm. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool mesh3d::dequeuefliplist(triface& flipface) +{ + badface3d queface; + point3d forg, fdest, fapex; + + while (fliplist->get(&queface)) { + if (!isdead(&(queface.badfacetet))) { + org (queface.badfacetet, forg); + dest(queface.badfacetet, fdest); + apex(queface.badfacetet, fapex); + if ((forg == queface.faceorg) + && (fdest == queface.facedest) + && (fapex == queface.faceapex)) { + flipface = queface.badfacetet; + if (verbose > 2) { + printf(" Getting flip face: (%d, %d, %d).\n", + pointmark(queface.faceorg), pointmark(queface.facedest), + pointmark(queface.faceapex)); + } + return true; + } + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Mesh Transformation Routines // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Insert/Delete point routines // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertsite() Insert a vertex into Delaunay tetrahedrization, perform- // +// ing flips as necessary to maintain the Delaunay property. // +// // +// The point 'insertpoint' is located. If 'searchtet.tet' is not NULL, // +// the search for the containing tetrahedron begins from 'searchtet'. If // +// 'searchtet.tet' is NULL, a full point location procedure is called. If // +// 'inseertpoint' is found inside a tetrahedron, the tetrahedra is split // +// into four by call routine insertininterior(); if 'insertpoint' lies on // +// an edge, the edge is split in two, thereby splitting the adjacent // +// tetrahedra. It will be done by call routine insertonedge(); if // +// 'insertpoint' lies on an face, the face is split into three, thereby // +// splitting the adjacent tetrahedra. It will be done by call routine // +// insertonface(). Face or edge flips are used to restore the Delaunay // +// property. If 'insertpoint' lies on an existing vertex, no action is // +// taken, and value DUPLICATEPOINT is returned. On return, 'searchtet' is // +// set to a handle contain the existing vertex. // +// // +// Normally, the parameter 'splitface' and 'splitedge' are set to NULL, // +// implying that no subface and subsegment should be split. In this case, if // +// 'insertpoint' is found to lie on a subface or a subsegment, no action is // +// taken, and the value VIOLATINGFACE or VIOLATINGEDGE is returned. On // +// return, 'searchtet' is set to a handle whose primary face or edge is the // +// violated subface or subsegment. // +// // +// If the calling routine wishes to split a subface or subsegment by // +// inserting a point in it, the parameter 'splitface' or 'splitedge' should // +// be that subface or subsegment. In this case, 'searchtet' MUST be the // +// tetrahedron handle reached by pivoting from that subface or subsegment; // +// no point location is done. // +// // +// If a point being inserted, the return value will be SUCCESSFUL. If a // +// point is found to locate outside the mesh and can't be inserted, the // +// return value will be FAILED otherwise. In either case, 'searchtet' is set // +// to a handle whose contains the newly inserted vertex. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum mesh3d::insertsiteresult +mesh3d::insertsite(point3d insertpoint, triface* searchtet, face* splitface, + face* splitedge) +{ + triface horiz; + face brokenshell; + badface3d *encroached; + enum locateresult intersect; + int flipcount; + + if (verbose > 1) { + printf(" Insert point to mesh: (%.12g, %.12g, %.12g) %d.\n", + insertpoint[0], insertpoint[1], insertpoint[2], + pointmark(insertpoint)); + } + + if ((splitface == NULL) && (splitedge == NULL)) { + // Find the location of the point to be inserted. Check if a good + // starting tetrahedron has already been provided by the caller. + if (isdead(searchtet)) { + // Find a boundary tetrahedron. + horiz.tet = dummytet; + horiz.loc = 0; + symself(horiz); + // Search for a tetrahedron containing 'insertpoint'. + intersect = locate(insertpoint, &horiz); + } else { + // Start searching from the tetrahedron provided by the caller. + horiz = *searchtet; + intersect = preciselocate(insertpoint, &horiz); + } + } else { + // The calling routine provides the edge or face in which the point + // is inserted. + horiz = *searchtet; + if ((splitface != NULL) && (splitedge == NULL)) { + intersect = ONFACE; + } else if ((splitface == NULL) && (splitedge != NULL)) { + intersect = ONEDGE; + } else { + printf("Internal error in insertsite():"); + printf(" splitface and splitedge couldn't use together.\n"); + internalerror(); + } + } + + // Keep search stat. + *searchtet = horiz; + recenttet = horiz; + + switch (intersect) { + case ONVERTEX: + // There's already a vertex there. Return in 'searchtet' a tetrahedron + // whose origin is the existing vertex. + if (verbose > 1) { + printf(" Not insert for duplicating point.\n"); + } + return DUPLICATE; + + case ONEDGE: + // The vertex falls on an edge or boundary. + if (checksegments && (splitedge == NULL)) { + // Check whether the vertex falls on a shell edge. + tsspivot(&horiz, &brokenshell); + if (brokenshell.sh != dummysh) { + // The vertex falls on a shell edge. + if (shsegflaws) { + if (nobisect == 0) { + // Add the shell edge to the list of encroached segments. + encroached = (badface3d*) badsegments.alloc(); + encroached->shface = brokenshell; + sorg (brokenshell, encroached->faceorg); + sdest(brokenshell, encroached->facedest); + } else if ((nobisect == 1) && (intersect == ONEDGE)) { + // This segment may be split only if it is an internal + // boundary. + if (!isridge(&horiz)) { + // Add the shell edge to the list of encroached segments. + encroached = (badface3d*) badsegments.alloc(); + encroached->shface = brokenshell; + sorg (brokenshell, encroached->faceorg); + sdest(brokenshell, encroached->facedest); + } + } + } + // Return a handle whose primary edge contains the point, which + // has not been inserted. + if (verbose > 1) { + printf(" Not insert for landing right on other subsegment.\n"); + } + return VIOLATINGEDGE; + } + } + flipcount = insertonedge(insertpoint, searchtet, splitedge); + if (verbose > 1) { + printf(" Successfully insert on edge with %d flips.\n", flipcount); + } + return SUCCESSFUL; + + case ONFACE: + // The vertex falls on a facet or boundary. + if (checksegments && (splitface == NULL)) { + // Check whether the vertex falls on a shell facet. + tspivot(horiz, brokenshell); + if (brokenshell.sh != dummysh) { + // The vertex falls on a shell facet. + if (shflaws) { + if (nobisect == 0) { + // Add the shell facet to the list of encroached subfaces. + enqueuebadface(&brokenshell, (point3d)NULL, 1); + } else if ((nobisect == 1) && (intersect == ONEDGE)) { + // This subface may be split only if it is an internal + // boundary. + if (issymexist(&horiz)) { + // Add the shell facet to the list of encroached subface. + enqueuebadface(&brokenshell, (point3d)NULL, 1); + } + } + } + // Return a handle whose primary face contains the point, + // which has not been inserted. + if (verbose > 1) { + printf(" Not insert for landing right on other subface.\n"); + } + return VIOLATINGFACE; + } + } + flipcount = insertonface(insertpoint, searchtet, splitface); + if (verbose > 1) { + printf(" Successfully insert on face with %d flips.\n", flipcount); + } + return SUCCESSFUL; + + case INTETRAHEDRON: + // This vertex falls inside a tetrahedron. + flipcount = insertininterior(insertpoint, searchtet); + if (verbose > 1) { + printf(" Successfully insert in tetrahedron with %d flips.\n", + flipcount); + } + return SUCCESSFUL; + + case OUTSIDE: + if (verbose > 1) { + printf(" Not insert for locating outside the mesh.\n"); + } + return FAILED; + } // End of switch(intersect) + + // Should never have a chance to reach here. + return FAILED; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertininterior() Insert the point in a tetrahedron, splitting it // +// into four. Face or edge flips are used to restore // +// the Delaunay property. // +// // +// 'insertpoint'(v) lies above wrt 'horiz'(abcd). // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::insertininterior(point3d insertpoint, triface* horiz) +{ + triface oldabd, oldbcd, oldcad; // Old configuration. + triface abdcasing, bcdcasing, cadcasing; + face abdsh, bcdsh, cadsh; + triface abcv, badv, cbdv, acdv; // New configuration. + point3d pa, pb, pc, pd; + REAL attrib, volume; + int flipcount, i; + + abcv = *horiz; + abcv.ver = 0; + // Set the vertices of changed and new tetrahedron. + org (abcv, pa); + dest(abcv, pb); + apex(abcv, pc); + oppo(abcv, pd); + + oldabd = oldbcd = oldcad = abcv; + fnextself(oldabd); + enextfnextself(oldbcd); + enext2fnextself(oldcad); + sym(oldabd, abdcasing); + sym(oldbcd, bcdcasing); + sym(oldcad, cadcasing); + maketetrahedron(&badv); + maketetrahedron(&cbdv); + maketetrahedron(&acdv); + + // Set 'badv' vertexs. + setorg (badv, pb); + setdest(badv, pa); + setapex(badv, pd); + setoppo(badv, insertpoint); + // Set 'cbdv' vertexs. + setorg (cbdv, pc); + setdest(cbdv, pb); + setapex(cbdv, pd); + setoppo(cbdv, insertpoint); + // Set 'acdv' vertexs. + setorg (acdv, pa); + setdest(acdv, pc); + setapex(acdv, pd); + setoppo(acdv, insertpoint); + // Set 'abcv' vertexs + setoppo(abcv, insertpoint); + + // Set the element attributes of the new tetrahedron. + for (i = 0; i < eextras; 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 tetrahedron. + if (varvolume) { + volume = volumebound(abcv.tet); + setvolumebound(badv.tet, volume); + setvolumebound(cbdv.tet, volume); + setvolumebound(acdv.tet, volume); + } + + // There may be shell facets that need to be bonded to + // the new tetrahedron. + if (checksegments) { + 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); + } + } + + // Bond the new triangles to the surrounding tetrahedron. + bond(badv, abdcasing); + bond(cbdv, bcdcasing); + bond(acdv, cadcasing); + + badv.loc = 3; // face<d, v, b>. + cbdv.loc = 2; + bond(badv, cbdv); + cbdv.loc = 3; // face<d, v, c>. + acdv.loc = 2; + bond(cbdv, acdv); + acdv.loc = 3; // face<d, v, a>. + badv.loc = 2; + bond(acdv, badv); + badv.loc = 1; // face<b, v, a>. + bond(badv, oldabd); + cbdv.loc = 1; // face<c, v, b>. + bond(cbdv, oldbcd); + acdv.loc = 1; // face<a, v, c>. + bond(acdv, oldcad); + + badv.loc = 0; + cbdv.loc = 0; + acdv.loc = 0; +#ifdef SELF_CHECK + if (!isaboveplane(&abcv, insertpoint)) { + printf("Internal error in insertininterior():\n"); + printf(" Clockwise tetrahedron prior to point insertion(abcv).\n"); + internalerror(); + } + if (!isaboveplane(&badv, insertpoint)) { + printf("Internal error in insertininterior():\n"); + printf(" Clockwise tetrahedron after point insertion (badv).\n"); + internalerror(); + } + if (!isaboveplane(&cbdv, insertpoint)) { + printf("Internal error in insertininterior():\n"); + printf(" Clockwise tetrahedron after point insertion (cbdv).\n"); + internalerror(); + } + if (!isaboveplane(&acdv, insertpoint)) { + printf("Internal error in insertininterior():\n"); + printf(" Clockwise tetrahedron after point insertion (acdv).\n"); + internalerror(); + } +#endif // defined SELF_CHECK + if (verbose > 2) { + printf(" Updating abcv "); + dump(&abcv); + printf(" Creating badv "); + dump(&badv); + printf(" Creating cbdv "); + dump(&cbdv); + printf(" Creating acdv "); + dump(&acdv); + } + + flipcount = 0; + + if (usefliplist) { + enqueuefliplist(abcv); + enqueuefliplist(badv); + enqueuefliplist(cbdv); + enqueuefliplist(acdv); + } else { + flipcount += flip(abcv); + flipcount += flip(badv); + flipcount += flip(cbdv); + flipcount += flip(acdv); + } + + if (isdead(horiz)) { + // We need return a live tet. + if (!isdead(&badv)) { + *horiz = badv; + } else if (!isdead(&cbdv)) { + *horiz = cbdv; + } else { + // assert(!acdv.isdead()); + if (!isdead(&acdv)) { + *horiz = acdv; + } else { + if (verbose) { + printf("Warnning in insertininterior():\n"); + printf(" After %d flips, we can't return a live tet.\n", flipcount); + } + } + } + } + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertonface() Insert a point on a face of tetrahedron, splitting one // +// tetrahedron into three (if the face lies on outer // +// boundary), or two tetrahedras into six. Face or edge // +// flips are used to restore the Delaunay property. // +// // +// 'horiz.location()' indicates the face where 'insertpoint' lies on. If the // +// 'splitface' != NULL, this mean insert a point on boundary face. 'horiz' // +// should be a handle of this boundary face adjoining to. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::insertonface(point3d insertpoint, triface* horiz, face* splitface) +{ + triface oldbcd, oldcad, oldace, oldcbe; // Old configuration. + triface bcdcasing, cadcasing, acecasing, cbecasing; + face bcdsh, cadsh, acesh, cbesh; + triface badv, cbdv, acdv, abev, bcev, caev; // New configuration. + point3d pa, pb, pc, pd, pe; + REAL attrib, volume; + bool mirrorflag; + int flipcount, i; + + badv = *horiz; + badv.ver = 0; + // Now assume 'badv' is tet<a, b, c, d>. + org (badv, pa); + dest(badv, pb); + apex(badv, pc); + oppo(badv, pd); + // Is there a second tetrahderon? + mirrorflag = issymexist(&badv); + if (mirrorflag) { + // This is a interior face. + sym(badv, abev); + findversion(&abev, &badv); + // Now 'abev' is tet <b, a, c, e>. + oppo(abev, pe); + } + + oldbcd = oldcad = badv; + enextfnextself(oldbcd); + enext2fnextself(oldcad); + fnextself(badv); + esymself(badv); // Now 'badv' is tet<b, a, d, c>. + sym(oldbcd, bcdcasing); + sym(oldcad, cadcasing); + maketetrahedron(&cbdv); + maketetrahedron(&acdv); + // Is there a second tetrahderon? + if (mirrorflag) { + // This is a interior face. + oldace = oldcbe = abev; + enextfnextself(oldace); + enext2fnextself(oldcbe); + fnextself(abev); + esymself(abev); // Now abev is tet<a, b, e, c>. + sym(oldace, acecasing); + sym(oldcbe, cbecasing); + maketetrahedron(&caev); + maketetrahedron(&bcev); + } else { + // Splitting the boundary face increases the number of boundary faces. + hullsize += 2; + } + + // Set the vertices of changed and new tetrahedron. + // Set 'cbdv'. + setorg (cbdv, pc); + setdest(cbdv, pb); + setapex(cbdv, pd); + setoppo(cbdv, insertpoint); + // Set 'acdv'. + setorg (acdv, pa); + setdest(acdv, pc); + setapex(acdv, pd); + setoppo(acdv, insertpoint); + // Set 'badv'. + setoppo(badv, insertpoint); + + // Set the element attributes of new tetrahedron. + for (i = 0; i < eextras; i++) { + attrib = elemattribute(badv.tet, i); + setelemattribute(cbdv.tet, i, attrib); + setelemattribute(acdv.tet, i, attrib); + } + if (varvolume) { + // Set the area constraint of new tetrahedron. + volume = volumebound(badv.tet); + setvolumebound(cbdv.tet, volume); + setvolumebound(acdv.tet, volume); + } + + if (mirrorflag) { + // Set 'caev'. + setorg (caev, pc); + setdest(caev, pa); + setapex(caev, pe); + setoppo(caev, insertpoint); + // Set 'bcev'. + setorg(bcev, pb); + setdest(bcev, pc); + setapex(bcev, pe); + setoppo(bcev, insertpoint); + // Set 'abev'. + setoppo(abev, insertpoint); + + // Set the element attributes of new tetrahedron. + for (i = 0; i < eextras; i++) { + attrib = elemattribute(abev.tet, i); + setelemattribute(bcev.tet, i, attrib); + setelemattribute(caev.tet, i, attrib); + } + if (varvolume) { + // Set the area constraint of new tetrahedron. + volume = volumebound(abev.tet); + setvolumebound(bcev.tet, volume); + setvolumebound(caev.tet, volume); + } + } + + // There may be shell facets that need to be bonded to the + // new tetrahedron. + if (checksegments) { + tspivot(oldbcd, bcdsh); + if (bcdsh.sh != dummysh) { + tsdissolve(oldbcd); + tsbond(cbdv, bcdsh); + } + tspivot(oldcad, cadsh); + if (cadsh.sh != dummysh) { + tsdissolve(oldcad); + tsbond(acdv, cadsh); + } + if (mirrorflag) { + tspivot(oldace, acesh); + if (acesh.sh != dummysh) { + tsdissolve(oldace); + tsbond(caev, acesh); + } + tspivot(oldcbe, cbesh); + if (cbesh.sh != dummysh) { + tsdissolve(oldcbe); + tsbond(bcev, cbesh); + } + } + } + + // Bond the new tetrahedron to the surrounding tetrahedron. + bond(cbdv, bcdcasing); // Default 'cbdv' loc = 0. + bond(acdv, cadcasing); // Default 'acdv' loc = 0. + cbdv.loc = 2; + bond(cbdv, oldbcd); + cbdv.loc = 3; + acdv.loc = 2; + bond(cbdv, acdv); + acdv.loc = 3; + bond(acdv, oldcad); + + if (mirrorflag) { + bond(caev, acecasing); // Default 'bcev' loc = 0. + bond(bcev, cbecasing); // Default 'caev' loc = 0. + caev.loc = 2; + bond(caev, oldace); + caev.loc = 3; + bcev.loc = 2; + bond(caev, bcev); + bcev.loc = 3; + bond(bcev, oldcbe); + + // Bond two new coplanar facets. + cbdv.loc = 1; + bcev.loc = 1; + bond(cbdv, bcev); + acdv.loc = 1; + caev.loc = 1; + bond(acdv, caev); + } + + cbdv.loc = 0; + acdv.loc = 0; + if (mirrorflag) { + bcev.loc = 0; + caev.loc = 0; + } + + if (splitface != (face *) NULL) { + // We need insert two new subface into current mesh. + triface righttet, lefttet; + face oldrightshface, oldleftshface; + face rightshseg, leftshseg; + face newrightshface, newleftshface; + + // Init 'splitface' be face<a, b, c> + findversion(splitface, pa, pb, 0); + senext(*splitface, oldrightshface); // face<b, c, a> + senext2(*splitface, oldleftshface); // face<c, a, b> + + // Set 'splitface' be face<a, b, v> + setsapex(*splitface, insertpoint); + // Insert two new shell facets at cbdv(right), and acdv(left). + // Set 'righttet' be tet<c, b, v, -d>. + fnext(cbdv, righttet); + // Insert a new subface abutting 'righttet'. + insertsubface(&righttet, mark(*splitface), 0); + // Set 'lefttet' tet<a, c, v, -d>. + fnext(acdv, lefttet); + // Insert a new subface abutting 'leftete'. + insertsubface(&lefttet, mark(*splitface), 0); + + // Set 'newrightshface' be face<b, c, v> + tspivot(righttet, newrightshface); + findversion(&newrightshface, pb, pc, 0); + // Set 'newleftshface' be face<c, a, v> + tspivot(lefttet, newleftshface); + findversion(&newleftshface, pc, pa, 0); + + // Bond subsegments to two new shell facets if there have one. Do not + // forget to dissolve orgin bond first. + sspivot(oldrightshface, rightshseg); + if (rightshseg.sh != dummysh) { + ssdissolve(oldrightshface); + ssbond(newrightshface, rightshseg); + } + sspivot(oldleftshface, leftshseg); + if (leftshseg.sh != dummysh) { + ssdissolve(oldleftshface); + ssbond(newleftshface, leftshseg); + } + + if (shflaws) { + // Now, the original 'splitface' was splitted to 'splitface', + // 'newrightshface' and 'newleftshface'. + // In quality mesh generation step, we need check if these three + // subfaces being encroached. (Because they are Delaunay faces + // and needn't do flip). + uncheckedshlist->append(splitface); + uncheckedshlist->append(&newrightshface); + uncheckedshlist->append(&newleftshface); + } + } // if (splitface != (face *) NULL) + +#ifdef SELF_CHECK + if (!isaboveplane(&badv, insertpoint)) { + printf("Internal error in insertonface():\n"); + printf(" Clockwise tetra prior to face point insertion (badv).\n"); + internalerror(); + } + if (!isaboveplane(&cbdv, insertpoint)) { + printf("Internal error in insertonface():\n"); + printf(" Clockwise tetra after face point insertion (cbdv).\n"); + internalerror(); + } + if (!isaboveplane(&acdv, insertpoint)) { + printf("Internal error in insertonface():\n"); + printf(" Clockwise tetra after face point insertion (acdv).\n"); + internalerror(); + } + if (mirrorflag) { + if (!isaboveplane(&abev, insertpoint)) { + printf("Internal error in insertonface():\n"); + printf(" Clockwise tetra prior to face point insertion (abev).\n"); + internalerror(); + } + if (!isaboveplane(&bcev, insertpoint)) { + printf("Internal error in insertonface():\n"); + printf(" Clockwise tetra after face point insertion (bcev).\n"); + internalerror(); + } + if (!isaboveplane(&caev, insertpoint)) { + printf("Internal error in insertonface():\n"); + printf(" Clockwise tetra after face point insertion (caev).\n"); + internalerror(); + } + } +#endif // defined SELF_CHECK + if (verbose > 2) { + printf(" Updating badv "); + dump(&badv); + printf(" Creating cbdv "); + dump(&cbdv); + printf(" Creating acdv "); + dump(&acdv); + if (mirrorflag) { + printf(" Updating abev "); + dump(&abev); + printf(" Creating bcev "); + dump(&bcev); + printf(" Creating caev "); + dump(&caev); + } + } + + flipcount = 0; + + if (usefliplist) { + enqueuefliplist(badv); + enqueuefliplist(cbdv); + enqueuefliplist(acdv); + if (mirrorflag) { + enqueuefliplist(abev); + enqueuefliplist(bcev); + enqueuefliplist(caev); + } + } else { + flipcount += flip(badv); + flipcount += flip(cbdv); + flipcount += flip(acdv); + if (mirrorflag) { + flipcount += flip(abev); + flipcount += flip(bcev); + flipcount += flip(caev); + } + } + + if (isdead(horiz)) { + // We need return a live tet. + if (splitface != (face*) NULL) { + stpivot(*splitface, *horiz); + if (horiz->tet == dummytet) { + assert(mirrorflag == 0); + sesymself(*splitface); + stpivot(*splitface, *horiz); + } + } else if (!isdead(&cbdv)) { + *horiz = cbdv; + } else if (!isdead(&acdv)) { + *horiz = acdv; + } else { + assert(mirrorflag); + if (!isdead(&abev)) { + *horiz = abev; + } else if (!isdead(&bcev)) { + *horiz = bcev; + } else { + // assert(!caev.isdead()); + if (!isdead(&caev)) { + *horiz = caev; + } else { + if (verbose) { + printf("Warnning in insertonface(): \n"); + printf(" After %d flips, we can't return a live tet.\n", + flipcount); + } + } + } + } + } + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertonedge() Insert a point on an edge, splitting all tetrahedra // +// which around this edge into two. Face or edge flips // +// are used to restore the Delaunay property. // +// // +// 'horiz.org()' and 'horiz.dest()' indicates the edge where 'insertpoint' // +// lies on. If the 'splitedge' != NULL, this mean insert a point on boundary // +// edge. 'horiz' should be a handle of this boundary edge adjoining to. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::insertonedge(point3d insertpoint, triface* horiz, face* splitedge) +{ + triface bdrytet, spintet, tmptet; + triface *bots, *oldtops, *topcasings, *newtops; + face topshell; + triface tmpbond0, tmpbond1; + point3d pa, pb, nj, nj_1; + point3d tapex; + int wrapfacecount, wrapcount, hitbdry; + int flipcount, i, k; + + // Adjust 'horiz''s face version. Let it belong to EdgeRing = 0. + adjustedgering(*horiz, CCW); + // Now, we can sure 'horiz' be tet<b, a, n1, n2>. + + // First find how much tetrahedron wrap around edge<ba>. As a side effect, + // we will find if edge<ba> lies on boundary, if so we keep the handle + // of first encountered boundary tetrahedron in 'bdrytet'. + spintet = *horiz; + apex(*horiz, tapex); + wrapfacecount = 1; + hitbdry = 0; + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == tapex) { + break; + } + wrapfacecount ++; + } else { + hitbdry ++; + if (hitbdry >= 2) { + break; + } else { + esym(spintet, bdrytet); + esym(*horiz, spintet); + } + } + } + + // Determine how much wrap tetrahedra from 'wrapfacecount'. + if (hitbdry == 0) { + wrapcount = wrapfacecount; + } else { + wrapcount = wrapfacecount - 1; + // Splitting the boundary face increases the number of boundary faces. + hullsize += 2; + } + assert(wrapcount >= 1); + + // Current state: + // Let i = wrapfacecount be the total found faces. Let the wrap faces' + // apex sequence be: n[1], n[2], ..., n[i]. + // IF hitbdry = 0, THEN edge<ba> is inner edge. 'horiz' will be tet + // <b, a, n[1], n[2]>; ELSE the edge <ba> is on boundary. 'bdrytet' + // will be tet<a, b, n[j], n[j-1]>; (where 2 <= j <= i). + // Note: the 'horiz' and 'bdrytet' has inversed fnext direction. + // + // For using the same process to cope with the diffrence between 'horiz' + // and 'bdrytet', we adjust 'horiz' be tet<a, b, n[2], n[1]>. So they + // have the same fnext direction. + if (hitbdry == 0) { + fnext(*horiz, spintet); + esymself(spintet); + } else { + spintet = bdrytet; + } + + // Make lists of dividing(bot, oldtop) tetrahedra, Create new(newtop) + // tetrahedra for each original tetrahedron. + bots = new triface[wrapcount]; + oldtops = new triface[wrapcount]; + topcasings = new triface[wrapcount]; + newtops = new triface[wrapcount]; + + // Walk around the edge, gathering tetrahedra and setting up new tetra- + // hedra. If 'hitbdry' != 0, start from 'bdrytet', otherwise, start + // from 'horiz'. For convinence, we inverse the number sequence of the + // apex of each wrapfaces in Owen's paper, and let the vertex sequence + // be : 1, 2, ..., j-1, j. (where j = wrapcount.) + for (i = 0; i < wrapcount; i ++) { + fnextself(spintet); + // Set 'tmptet' be tet<b, a, n[j], n[j-1]>. + esym(spintet, tmptet); + // Set 'bots[i]' be tet<n[j], a, n[j-1], b>. + enextself(tmptet); + fnext(tmptet, bots[i]); + esymself(bots[i]); + // Set 'oldtops[i]' be tet<n[j], b, n[j-1], -a>. + enextself(tmptet); + fnext(tmptet, oldtops[i]); + // Set 'topcasing'. + sym(oldtops[i], topcasings[i]); + maketetrahedron(&(newtops[i])); + } + + org (spintet, pa); + dest(spintet, pb); + // Set the vertices of changed and new tetrahedra. + for (i = 0; i < wrapcount; i ++) { + // 'bots[i]' is tet<n[j], a, n[j-1], b>. + org (bots[i], nj); // + apex(bots[i], nj_1); + // Set 'newtops[i]' be tet<b, n[j], n[j-1], v>. + setorg(newtops[i], pb); + setdest(newtops[i], nj); + setapex(newtops[i], nj_1); + setoppo(newtops[i], insertpoint); + // Set 'bots' be tet<n[j], a, n[j-1], v>. + setoppo(bots[i], insertpoint); + // Set the element attributes of a new tetrahedron. + for (k = 0; k < eextras; k++) { + setelemattribute(newtops[i].tet, k, + elemattribute(bots[i].tet, k)); + } + if (varvolume) { + // Set the area constraint of a new tetrahedron. + setvolumebound(newtops[i].tet, volumebound(bots[i].tet)); + } + } + + // There may be shell facets that need to be bonded to each new + // tetrahedron's topcasing side. + if (checksegments) { + // Check topcasing shell at each newtops side. + for (i = 0; i < wrapcount; i ++) { + tspivot(oldtops[i], topshell); + if (topshell.sh != dummysh) { + tsdissolve(oldtops[i]); + tsbond(newtops[i], topshell); + } + } + } + + // Bond newtops to topcasings and bots. + for (i = 0; i < wrapcount; i ++) { + // default: 'newtops[i]' loc = 0. + bond(newtops[i], topcasings[i]); + newtops[i].loc = 2; + bond(newtops[i], oldtops[i]); + } + // Bond between each newtops. Becareful the boundary case. + tmpbond0 = newtops[0]; + for (i = 1; i <= wrapcount; i ++) { + if(i == wrapcount) { + if(hitbdry == 0) { + tmpbond1 = newtops[0]; + } else { + break; // Insert on boundary edge case. + } + } else { + tmpbond1 = newtops[i]; + } + tmpbond0.loc = 1; + tmpbond1.loc = 3; + bond(tmpbond0, tmpbond1); + tmpbond0 = tmpbond1; + } + + for (i = 0; i < wrapcount; i ++) { + newtops[i].loc = 0; + } + + // There may be shell facets that need to be bonded to each new + // tetrahedron's sandwich side. + if (checksegments || (splitedge != (face*) NULL)) { + triface tmpbot, tmpnewtop; + face botsandwichsh, modibotleftsh, modibotrightsh; + face botleftshseg, botrightshseg; + face newtopsandwichsh; + + for (i = 0; i < wrapcount; i ++) { + // Set 'tmpbot' be tet<a, n[j], v, n[j-1]>. + fnext(bots[i], tmpbot); + esymself(tmpbot); + tspivot(tmpbot, botsandwichsh); + if (botsandwichsh.sh != dummysh) { + findversion(&botsandwichsh, &tmpbot); + // Set 'botsandwichsh' be face<n[j], a, v> + setsapex(botsandwichsh, insertpoint); + // Set 'modibotleftsh' be face<v, n[j], a> + senext2(botsandwichsh, modibotleftsh); + // Set 'tmpnewtop' be tet<n[j], b, v, n[j-1]>. + fnext(newtops[i], tmpnewtop); + esymself(tmpnewtop); + // Insert a new shell facet at 'tmpnewtop'. + insertsubface(&tmpnewtop, mark(botsandwichsh), 0); + // Set 'newtopsandwichsh' be face<b, n[j], v>. + tspivot(tmpnewtop, newtopsandwichsh); + findversion(&newtopsandwichsh, &tmpnewtop); + // Check subsegment that need bond to 'botleftshseg'. + sspivot(modibotleftsh, botleftshseg); + if (botleftshseg.sh != dummysh) { + ssdissolve(modibotleftsh); + ssbond(newtopsandwichsh, botleftshseg); + } + // Here we can skip checking the right side's segment, because + // if there exist a segment, it will be split later. + if (shflaws) { + // Now, the original 'botsandwichsh' was splitted to + // 'botsandwichsh' and 'newtopsandwichsh'. + // In quality mesh generation step, we need check if these two + // subfaces being encroached. (Because they are Delaunay faces + // and needn't do flip). + uncheckedshlist->append(&botsandwichsh); + uncheckedshlist->append(&newtopsandwichsh); + } + } + } + if (hitbdry != 0) { + // Additional shell facet at bots[0] other side need split. + // We recall: 'bots[0]' is tet<n[j], a, n[j-1], v>. + // 'newtops[0]' is tet<b, n[j], n[j-1], v>. + // Set 'tmpbot' be tet<n[j-1], pa, v, n[j]>. + tmpbot = bots[0]; + enextfnextself(tmpbot); + esymself(tmpbot); + tspivot(tmpbot, botsandwichsh); + if (botsandwichsh.sh != dummysh) { + // Set 'botsandwichsh' be face<a, n[j-1], v>. + findversion(&botsandwichsh, &tmpbot); + setsapex(botsandwichsh, insertpoint); + // Set 'modibotrightsh' be face<n[j-1], v, a>. + senext(botsandwichsh, modibotrightsh); + // Set 'tmpnewtop' be tet<b, n[j-1], v, n[j]>. + tmpnewtop = newtops[0]; + enext2fnextself(tmpnewtop); + esymself(tmpnewtop); + // Insert a new shell facet at 'tmpnewtop'. + insertsubface(&tmpnewtop, mark(botsandwichsh), 0); + // Set 'newtopsandwichsh' be face<n[j-1], b, v>. + tspivot(tmpnewtop, newtopsandwichsh); + findversion(&newtopsandwichsh, &tmpnewtop); + // Check subsegment that need bond to 'botrightshseg'. + sspivot(modibotrightsh, botrightshseg); + if (botrightshseg.sh != dummysh) { + ssdissolve(modibotrightsh); + ssbond(newtopsandwichsh, botrightshseg); + } + // Here we can skip checking the left side's segment, because + // if there exist a segment, it will be split later. + if (shflaws) { + uncheckedshlist->append(&botsandwichsh); + uncheckedshlist->append(&newtopsandwichsh); + } + } + } + + if (splitedge != (face*) NULL) { + face newseg; + face tmpbond0; + // We need insert a new subsegments into current mesh. + // Set 'splitedge' be edge<ab>. + findversion(splitedge, pa, pb, 0); + // Set 'splitedge' be edge<av>. + setsdest(*splitedge, insertpoint); + // Insert a new subsegment <vb> at 'tmpnewtop'. + fnext(newtops[0], tmpnewtop); + enext2self(tmpnewtop); + insertsubsegment(&tmpnewtop, mark(*splitedge)); + tsspivot(&tmpnewtop, &newseg); + findversion(&newseg, insertpoint, pb, 0); + // Bond these two edges. So it can be quickly found each other after + // splitting this subsegment. + senext(*splitedge, tmpbond0); + senext2self(newseg); + sbond(tmpbond0, newseg); + } + } + +#ifdef SELF_CHECK + for (i = 0; i < wrapcount; i ++) { + if (!isaboveplane(&(bots[i]), insertpoint)) { + printf("Internal error in insertonedge():\n"); + printf(" Clockwise tetra prior to point insertion (bots[%i]).\n", i); + internalerror(); + } + if (!isaboveplane(&(newtops[i]), insertpoint)) { + printf("Internal error in insertonedge():\n"); + printf(" Clockwise tetra after point insertion (bots[%i]).\n", i); + internalerror(); + } + } +#endif // defined SELF_CHECK + if (verbose > 2) { + for (i = 0; i < wrapcount; i ++) { + printf(" Updating bots[%i] ", i); + dump(&(bots[i])); + printf(" Creating newtops[%i] ", i); + dump(&(newtops[i])); + } + } + + flipcount = 0; + + if (usefliplist) { + for (i = 0; i < wrapcount; i ++) { + enqueuefliplist(bots[i]); + enqueuefliplist(newtops[i]); + } + } else { + for (i = 0; i < wrapcount; i ++) { + flipcount += flip(bots[i]); + flipcount += flip(newtops[i]); + } + } + + if (isdead(horiz)) { + // We need return a live tet. + if (splitedge != (face*) NULL) { + sstpivot(splitedge, horiz); // *horiz = splitedge->sstpivot(); + } else { + for (i = 0; i < wrapcount; i ++) { + if (!isdead(&(bots[i]))) { + *horiz = bots[i]; + break; + } + if (!isdead(&(newtops[i]))) { + *horiz = newtops[i]; + break; + } + } + // assert(i < wrapcount); + if (!(i < wrapcount)) { + if (verbose) { + printf("Warnning in insertonedge(): \n"); + printf(" After %d flips, we can't return a live tet.\n", flipcount); + } + } + } + } + + delete [] bots; + delete [] oldtops; + delete [] topcasings; + delete [] newtops; + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertsubface() Create a new shell facet and insert it between two // +// tetrahedra. // +// // +// The new shell facet is created at the face described by the handle 'tet'. // +// Its vertices are properly initialized. The marker 'shellemark' is applied // +// to the shell facet and, if appropriate, its vertices. // +// // +// The 'autobondsegs' flag is used to indicate whether we need do a segment // +// searching for the newly inserted subface. Generally, in mesh refinement // +// stage, we always need searching segments for newly inserted subface, so // +// the (triface).tsspivot() primitive will work correctly and efficiently. // +// But when do a local transformation on a face or edge, the segments around // +// the subface always can be found before do flipping. So there need not do // +// segments searching (set autobondsegs = 0). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::insertsubface(triface* tet, int shellemark, int autobondsegs) +{ + triface abcd, bace; + face newshell, bseg; + point3d pa, pb, pc; + int i; + + abcd = *tet; + adjustedgering(abcd, CCW); + // Mark points if possible. + org (abcd, pa); + dest(abcd, pb); + apex(abcd, pc); + if (shellemark != NONSOLIDFLAG) { + if (pointmark(pa) == 0) { + setpointmark(pa, shellemark); + } + if (pointmark(pb) == 0) { + setpointmark(pb, shellemark); + } + if (pointmark(pc) == 0) { + setpointmark(pc, shellemark); + } + } + + // Check if there's already exist a shell facet here. + tspivot(abcd, newshell); + if (newshell.sh == dummysh) { + // Make new shell facet and initialize its vertices. + makeshellface(&subfaces, &newshell); + setsorg (newshell, pb); + setsdest(newshell, pa); + setsapex(newshell, pc); + // Bond new shell facet to the two tetrahedra it is sandwiched + // between. Note that the facing tetrahedron 'oppotet' might be + // equal to 'dummytet' (outer space), but the new shell facet is + // bonded to it all the same. + tsbond(abcd, newshell); + sym(abcd, bace); + sesymself(newshell); + tsbond(bace, newshell); + if (autobondsegs) { + // Check if there exist subsegments at three edge of this new + // subface. If so, set corresponding pointers in 'newshell' to + // link these subsegments. + // Now 'abcd' and 'newshell' both locked at edge 'ab'. + for (i = 0; i < 3; i ++) { + enextself(abcd); + senextself(newshell); + tsspivot(&abcd, &bseg); // bseg = abcd.sspivot(); + if (bseg.sh != dummysh) { + ssbond(newshell, bseg); + } + } + } + setmark(newshell, shellemark); + if (verbose > 2) { + printf(" Inserting new: "); + dump(&newshell); + } + } else { + // The exist shellface may be nonsolid(<0) or not have a marker(0), now + // we can make it solid or set new shell's mark. + if (shellemark != NONSOLIDFLAG) { + if (isnonsolid(newshell) || (mark(newshell) == 0)) { + setmark(newshell, shellemark); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertsubsegment() Create a new subsegment and connect it to surround- // +// ing subface. // +// // +// The new subsegment is created at the face described by the handle 'tet'. // +// Its vertices are properly initialized. The marker 'shellemark' is applied // +// to the shell facet and, if appropriate, its vertices. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::insertsubsegment(triface* tet, int shellemark) +{ + triface abcd, spintet; + face newseg, parentshell; + point3d pa, pb; + point3d tapex; + int hitbdry, subfacecount; + + abcd = *tet; + adjustedgering(abcd, CCW); + org(abcd, pa); + dest(abcd, pb); + // Mark points if possible. + if (shellemark != NONSOLIDFLAG) { + if (pointmark(pa) == 0) { + setpointmark(pa, shellemark); + } + if (pointmark(pb) == 0) { + setpointmark(pb, shellemark); + } + } + // Check if there's already exist a subsegment at this face's side. + tsspivot(&abcd, &newseg); // newseg = abcd.sspivot(); + if (newseg.sh == dummysh) { + // Make new subsegment and initialize its vertices. + makeshellface(&subsegs, &newseg); + setsorg(newseg, pb); + setsdest(newseg, pa); + setmark(newseg, shellemark); + // To insert this subsegment, spin around this edge, to find all surro- + // unding subface and set connection between them. If there isn't + // exist such subface, we need create a nonsolid subface, and insert + // it to mesh for insert this subsegment. + spintet = abcd; + apex(abcd, tapex); + subfacecount = 0; + hitbdry = 0; + tspivot(spintet, parentshell); + if (parentshell.sh != dummysh) { + findversion(&parentshell, &spintet); + ssbond(parentshell, newseg); + subfacecount++; + } + while (true) { + if (fnextself(spintet)) { + if (apex(spintet) == tapex) { + break; // Rewind, can leave now. + } + tspivot(spintet, parentshell); + if (parentshell.sh != dummysh) { + findversion(&parentshell, &spintet); + ssbond(parentshell, newseg); + subfacecount++; + } + } else { + hitbdry ++; + if(hitbdry >= 2) { + break; + } else { + esym(abcd, spintet); + } + } + } + if (subfacecount == 0) { + // Insert a new shell facet for holding the new subsegment. + if (verbose > 2) { + printf(" Inserting a nonsolid subface for holding subsegment.\n"); + } + insertsubface(&abcd, NONSOLIDFLAG, 1); + tspivot(abcd, parentshell); + findversion(&parentshell, &abcd); + ssbond(parentshell, newseg); + } + if (verbose > 2) { + printf(" Inserting new: "); + dump(&newseg); + } + } else { + if (mark(newseg) == 0) { + setmark(newseg, shellemark); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// deletesite() Delete a vertex from a Delaunay triangulation, ensuring // +// that the triangulation remains Delaunay. // +// // +// The origin of `deltet' is deleted. Only interior points that do not lie // +// on segments (shell edges) or boundaries may be deleted. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::deletesite(triface *deltet) +{ + list *incitetlist, *incishlist; + list *equatorpointlist, *equatoredgelist; + list *northpointlist, *sourthpointlist; + list *equatorcasinglist; + list *northcasinglist, *sourthcasinglist; + list *northbacktracelist, *sourthbacktracelist; + triface *giftfaces; + triface *incitet, testtet, neighbortet; + face *incish, testsh, testseg; + point3d *giftpoints; + point3d delorg, deldest, delapex; + point3d torg, tdest, tapex; + point3d tnorth; + triangulateio in, out; + REAL norm[3], xaxi[3], yaxi[3]; + REAL v0[3], v1[3], dsin; + REAL *northattribs, *sourthattribs; + REAL northvolume, sourthvolume; + int *equatoredge, shellmark, add2north; + int bdrycount, bdry2count; + int orgori, destori, apexori; + int numberofgiftfaces, numberofgiftpoints; + int success; + int i, j, index; + + testtet = *deltet; + delorg = org(testtet); + adjustedgering(testtet, CCW); + findorg(&testtet, delorg); + + if (verbose > 1) { + printf(" Delete point from mesh: (%.12g, %.12g, %.12g) %d\n", + delorg[0], delorg[1], delorg[2], pointmark(delorg)); + } + + // Get all tetrahedra and subfaces which incidents to the delete point. + incitetlist = new list(sizeof(triface)); + incishlist = new list(sizeof(face)); + incitetlist->setcomp((compfunc) &compare2tets); + incishlist->setcomp((compfunc) &compare2shfaces); + bdrycount = bdry2count = 0; + + if (verbose > 2) { + printf(" Queuing incident tet (%d, %d, %d, %d).\n", + pointmark(org(testtet)), pointmark(dest(testtet)), + pointmark(apex(testtet)), pointmark(oppo(testtet))); + } + incitetlist->append(&testtet); + symself(testtet); + if (testtet.tet != dummytet) { + adjustedgering(testtet, CCW); + findorg(&testtet, delorg); + if (verbose > 2) { + printf(" Queuing incident tet (%d, %d, %d, %d).\n", + pointmark(org(testtet)), pointmark(dest(testtet)), + pointmark(apex(testtet)), pointmark(oppo(testtet))); + } + incitetlist->append(&testtet); + tspivot(testtet, testsh); + if (testsh.sh != dummysh) { + if (!isnonsolid(testsh)) { + bdry2count++; + findorg(&testsh, delorg); + if (verbose > 2) { + printf(" Queuing incident face (%d, %d, %d) (inner).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + incishlist->append(&testsh); + } + } + } else { + tspivot(*deltet, testsh); + assert(testsh.sh != dummysh); + assert(!isnonsolid(testsh)); + bdrycount++; + findorg(&testsh, delorg); + if (verbose > 2) { + printf(" Queuing incident boundary face (%d, %d, %d).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + incishlist->append(&testsh); + } + // Keep adding tets and subfaces incident on delpoint until they're + // all there. + for (i = 0; i < incitetlist->len(); i++) { + incitet = (triface *)(* incitetlist)[i]; + for (j = 0; j < 2; j++) { + testtet = *incitet; + if (j == 1) { + enext2self(testtet); + } + fnextself(testtet); + if (issymexist(&testtet)) { + symself(testtet); + if (incitetlist->hasitem(&testtet) == -1) { + // This tet need add to queue. + adjustedgering(testtet, CCW); + findorg(&testtet, delorg); + if (verbose > 2) { + printf(" Queuing incident tet (%d, %d, %d, %d).\n", + pointmark(org(testtet)), pointmark(dest(testtet)), + pointmark(apex(testtet)), pointmark(oppo(testtet))); + } + incitetlist->append(&testtet); + tspivot(testtet, testsh); + if (testsh.sh != dummysh) { + if (!isnonsolid(testsh)) { + bdry2count++; + findorg(&testsh, delorg); + if (verbose > 2) { + printf(" Queuing incident face (%d, %d, %d) (inner).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + incishlist->append(&testsh); + } + } + } else { + // This tet has been added to queue, we still need check if there + // exist inner shell face at two side of this tet. + tspivot(testtet, testsh); + if (testsh.sh != dummysh) { + if (!isnonsolid(testsh)) { + if (incishlist->hasitem(&testsh) == -1) { + bdry2count++; + findorg(&testsh, delorg); + if (verbose > 2) { + printf(" Queuing incident face (%d, %d, %d) (inner).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + incishlist->append(&testsh); + } + } + } + } + } else { + tspivot(testtet, testsh); + assert(testsh.sh != dummysh); + assert(!isnonsolid(testsh)); + bdrycount++; + findorg(&testsh, delorg); + if (verbose > 2) { + printf(" Queuing incident boundary face (%d, %d, %d).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + incishlist->append(&testsh); + } + } + } + + if (((bdry2count > 0) && (bdrycount > 0)) || + ((bdry2count == 1) || (bdrycount == 1))) { + if (!quiet) { + printf("Warnning: "); + if ((bdry2count > 0) && (bdrycount > 0)) { + printf("Try to delete a point which seems locating on the"); + printf(" intersection of two incident facets.\n"); + } else { + printf("Try to delete a point which is an apex on boundary.\n"); + } + } + delete incitetlist; + delete incishlist; + return 0; + } + + if ((bdry2count > 0) || (bdrycount > 0)) { + if (verbose > 2) { + printf(" Generating a triangulation for coplanar subfaces.\n"); + } + equatorpointlist = new list("point"); + equatoredgelist = new list(sizeof(int) * 2); + + testsh = *(face *)(* incishlist)[0]; + assert(delorg == sorg(testsh)); + deldest = sdest(testsh); + delapex = sapex(testsh); + equatorpointlist->append(&deldest); + equatorpointlist->append(&delapex); + equatoredge = (int*) equatoredgelist->alloc(); + equatoredge[0] = 0; + equatoredge[1] = 1; + for (i = 1; i < incishlist->len(); i++) { + incish = (face *)(* incishlist)[i]; + assert(delorg == sorg(*incish)); + tdest = sdest(*incish); + tapex = sapex(*incish); +#ifdef SELF_CHECK + if ((!iscoplane(delorg, deldest, delapex, tdest)) || + (!iscoplane(delorg, deldest, delapex, tapex))) { + printf("Internal Error in deletesite(): Try to delete a point"); + printf(" which is an apex on the boundary.\n"); + internalerror(); + } +#endif // defined SELF_CHECK + equatoredge = (int*) equatoredgelist->alloc(); + equatoredge[0] = equatorpointlist->hasitem(&tdest); + if (equatoredge[0] == -1) { + equatorpointlist->append(&tdest); + equatoredge[0] = equatorpointlist->len() - 1; + } + equatoredge[1] = equatorpointlist->hasitem(&tapex); + if (equatoredge[1] == -1) { + equatorpointlist->append(&tapex); + equatoredge[1] = equatorpointlist->len() - 1; + } + } + assert(equatorpointlist->len() > 0); + assert(equatoredgelist->len() > 0); + if (verbose > 2) { + printf(" There are %d points and %d segments at equator.\n", + equatorpointlist->len(), equatoredgelist->len()); + } + + triangulateioinit(&in); + triangulateioinit(&out); + in.numberofpoints = equatorpointlist->len(); + in.pointlist = new REAL[in.numberofpoints * 2]; + in.numberofsegments = equatoredgelist->len(); + in.segmentlist = new int[in.numberofsegments * 2]; + index = 0; + for (i = 0; i < in.numberofsegments; i ++) { + equatoredge = (int*) (*equatoredgelist)[i]; + in.segmentlist[index++] = equatoredge[0]; + in.segmentlist[index++] = equatoredge[1]; + } + // Determine normal, verify closed polygon. + tdest = *(point3d*)(*equatorpointlist)[in.segmentlist[0]]; + tapex = *(point3d*)(*equatorpointlist)[in.segmentlist[1]]; + Sub3D(tapex, tdest, v0); + Normalize3D(v0); + index = 2; + dsin = 0; + for (i = 1; iszero(dsin); i++) { + tdest = *(point3d*)(*equatorpointlist)[in.segmentlist[index++]]; + tapex = *(point3d*)(*equatorpointlist)[in.segmentlist[index++]]; + Sub3D(tapex, tdest, v1); + Normalize3D(v1); + Cross3D(v0, v1, norm); + dsin = Mag3D(norm); + if (!iszero(dsin)) Scale3D(norm, 1./dsin); + if (i >= in.numberofsegments) { + printf("Internal Error in deletesite(): Can't find a normal.\n"); + internalerror(); + } + } + // Project onto a plane + Assign3D(v1, xaxi); + Cross3D(norm, xaxi, yaxi); + index = 0; + for (i = 0; i < in.numberofpoints; i ++) { + tdest = *(point*)(*equatorpointlist)[i]; + in.pointlist[index++] = Dot3D(tdest, xaxi); + in.pointlist[index++] = Dot3D(tdest, yaxi); + } + // Now create the 2D mesh. + surfmesh->triangulate(&in, &out, NULL); + if (surfmesh->triangles.items <= 0) { + printf("Internal Error in deletesite(): Can't form a surface"); + printf(" triangulation.\n"); + internalerror(); + } + if (verbose > 2) { + printf(" Triangulation result: %d triangles.\n", + surfmesh->triangles.items); + } + surfmesh->trianglerestart(); + } + + if (verbose > 2) { + printf(" Identify the casing faces at north"); + } + northpointlist = new list("point"); + northcasinglist = new list(sizeof(triface)); + if (bdry2count > 0) { + if (verbose > 2) { + printf(" and sourth.\n"); + } + sourthpointlist = new list("point"); + sourthcasinglist = new list(sizeof(triface)); + for (i = 0; i < incitetlist->len(); i++) { + testtet = *(triface *)(* incitetlist)[i]; + enextfnextself(testtet); + tspivot(testtet, testsh); + torg = org(testtet); + tdest = dest(testtet); + tapex = apex(testtet); + orgori = iorient3d(delorg, deldest, delapex, torg); + destori = iorient3d(delorg, deldest, delapex, tdest); + apexori = iorient3d(delorg, deldest, delapex, tapex); + add2north = 1; + if (orgori != 0) { + if (orgori < 0) { + if (northpointlist->hasitem(&torg) == -1) { + northpointlist->append(&torg); + } + } else { + if (sourthpointlist->hasitem(&torg) == -1) { + sourthpointlist->append(&torg); + } + add2north = 0; + } + } + if (destori != 0) { + if (destori < 0) { + if (northpointlist->hasitem(&tdest) == -1) { + northpointlist->append(&tdest); + } + } else { + if (sourthpointlist->hasitem(&tdest) == -1) { + sourthpointlist->append(&tdest); + } + add2north = 0; + } + } + if (apexori != 0) { + if (apexori < 0) { + if (northpointlist->hasitem(&tapex) == -1) { + northpointlist->append(&tapex); + } + } else { + if (sourthpointlist->hasitem(&tapex) == -1) { + sourthpointlist->append(&tapex); + } + add2north = 0; + } + } + symself(testtet); + if (testtet.tet == dummytet) { + assert (testsh.sh != dummysh); + maketetrahedron(&testtet); + setorg(testtet, torg); + setdest(testtet, tdest); + setapex(testtet, tapex); + sesymself(testsh); + tsbond(testtet, testsh); + if (verbose > 2) { + printf(" Creating a fake tet for holding face(%d, %d, %d).\n", + pointmark(torg), pointmark(tdest), pointmark(tapex)); + } + } + adjustedgering(testtet, CCW); + if (add2north) { + if (verbose > 2) { + printf(" Queuing northcasing face (%d, %d, %d).\n", + pointmark(org(testtet)), pointmark(dest(testtet)), + pointmark(apex(testtet))); + } + northcasinglist->append(&testtet); + } else { + if (verbose > 2) { + printf(" Queuing sourthcasing face (%d, %d, %d).\n", + pointmark(org(testtet)), pointmark(dest(testtet)), + pointmark(apex(testtet))); + } + sourthcasinglist->append(&testtet); + } + } + assert(sourthcasinglist->len() > 0); + if (verbose > 2) { + printf(" There are %d points at sourth.\n", sourthpointlist->len()); + } + } else { + if (verbose > 2) { + printf(".\n"); + } + for (i = 0; i < incitetlist->len(); i++) { + testtet = *(triface *)(* incitetlist)[i]; + enextfnextself(testtet); + tspivot(testtet, testsh); + torg = org(testtet); + tdest = dest(testtet); + tapex = apex(testtet); + if (bdrycount > 0) { + if (equatorpointlist->hasitem(&torg) == -1) { + if (northpointlist->hasitem(&torg) == -1) { + northpointlist->append(&torg); + } + } + if (equatorpointlist->hasitem(&tdest) == -1) { + if (northpointlist->hasitem(&tdest) == -1) { + northpointlist->append(&tdest); + } + } + if (equatorpointlist->hasitem(&tapex) == -1) { + if (northpointlist->hasitem(&tapex) == -1) { + northpointlist->append(&tapex); + } + } + } else { + if (northpointlist->hasitem(&torg) == -1) { + northpointlist->append(&torg); + } + if (northpointlist->hasitem(&tdest) == -1) { + northpointlist->append(&tdest); + } + if (northpointlist->hasitem(&tapex) == -1) { + northpointlist->append(&tapex); + } + } + symself(testtet); + if (testtet.tet == dummytet) { + assert (testsh.sh != dummysh); + maketetrahedron(&testtet); + setorg(testtet, torg); + setdest(testtet, tdest); + setapex(testtet, tapex); + sesymself(testsh); + tsbond(testtet, testsh); + if (verbose > 2) { + printf(" Creating a fake tet for holding face(%d, %d, %d).\n", + pointmark(torg), pointmark(tdest), pointmark(tapex)); + } + } + adjustedgering(testtet, CCW); + if (verbose > 2) { + printf(" Queuing northcasing face (%d, %d, %d).\n", + pointmark(org(testtet)), pointmark(dest(testtet)), + pointmark(apex(testtet))); + } + northcasinglist->append(&testtet); + } + } + assert(northcasinglist->len() > 0); + if (verbose > 2) { + printf(" There are %d points at north.\n", northpointlist->len()); + } + + if (verbose > 2) { + printf(" Protecting subsegments process.\n"); + } + for (i = 0; i < northcasinglist->len(); i++) { + testtet = *(triface*)(*northcasinglist)[i]; + tspivot(testtet, testsh); + if (testsh.sh == dummysh) { + for (j = 0; j < 3; j++) { + tsspivot(&testtet, &testseg); + if (testseg.sh != dummysh) { + if (verbose > 2) { + printf(" Segment (%d, %d) needs protect.\n", + pointmark(sorg(testseg)), pointmark(sdest(testseg))); + } + insertsubface(&testtet, NONSOLIDFLAG, 1); + break; + } + enextself(testtet); + } + } else { + // Bond all segments to this subface to ensure + // the subsegment-subface bond. + for (j = 0; j < 3; j++) { + sspivot(testsh, testseg); + if (testseg.sh != dummysh) { + ssbond(testsh, testseg); + } + senextself(testsh); + } + } + } + if (bdry2count > 0) { + for (i = 0; i < sourthcasinglist->len(); i++) { + testtet = *(triface*)(*sourthcasinglist)[i]; + tspivot(testtet, testsh); + if (testsh.sh == dummysh) { + for (j = 0; j < 3; j++) { + tsspivot(&testtet, &testseg); + if (testseg.sh != dummysh) { + if (verbose > 2) { + printf(" Segment (%d, %d) needs protect.\n", + pointmark(sorg(testseg)), pointmark(sdest(testseg))); + } + insertsubface(&testtet, NONSOLIDFLAG, 1); + break; + } + enextself(testtet); + } + } else { + // Bond all segments of to this subface to ensure + // the subsegment-subface bond. + for (j = 0; j < 3; j++) { + sspivot(testsh, testseg); + if (testseg.sh != dummysh) { + ssbond(testsh, testseg); + } + senextself(testsh); + } + } + } + } + + // Keep shellmark for subfaces. + if ((bdrycount > 0) || (bdry2count > 0)) { + incish = (face*)(*incishlist)[0]; + shellmark = mark(*incish); +#ifdef SELF_CHECK + for (i = 1; i < incishlist->len(); i++) { + incish = (face*)(*incishlist)[i]; + if (shellmark != mark(*incish)) { + printf("Internal error in deletesite(): \n"); + printf(" Try to delete a point which seems on a subsegment.\n"); + internalerror(); + } + } +#endif // defined SELF_CHECK + } + + // Keep attribs and varvolume for new tets. + if (eextras || varvolume) { + if (eextras > 0) { + northattribs = new REAL[eextras]; + if (bdry2count > 0) { + sourthattribs = new REAL[eextras]; + } + } + if (bdry2count > 0) { + // Find a incident tet which belong to sourth side. + incish = (face*)(*incishlist)[0]; + // This is sourth side. (Above we use the first shell face as horiz). + stpivot(*incish, testtet); +#ifdef SELF_CHECK + assert(isbelowplane(delorg, deldest, delapex, oppo(testtet))); +#endif // defined SELF_CHECK + for (i = 0; i < eextras; i++) { + sourthattribs[i] = elemattribute(testtet.tet, i); + } + if (varvolume) { + sourthvolume = volumebound(testtet.tet); + } + // Set north side. + symself(testtet); + assert(testtet.tet != dummytet); + for (i = 0; i < eextras; i++) { + northattribs[i] = elemattribute(testtet.tet, i); + } + if (varvolume) { + northvolume = volumebound(testtet.tet); + } + } else { + incitet = (triface*)(*incitetlist)[0]; + // There only north side. + for (i = 0; i < eextras; i++) { + northattribs[i] = elemattribute(incitet->tet, i); + } + if (varvolume) { + northvolume = volumebound(incitet->tet); + } + } + } else { + northattribs = sourthattribs = NULL; + northvolume = sourthvolume = 0; + } + + if ((bdrycount > 0) || (bdry2count > 0)) { + if (verbose > 2) { + printf(" Creating casing faces for holding subfaces on equator.\n"); + } + equatorcasinglist = new list(sizeof(triface)); + tnorth = *(point3d*)(*northpointlist)[0]; + for (i = 0; i < out.numberoftriangles; i++) { + index = i * 3; + orgori = out.trianglelist[index++]; + destori = out.trianglelist[index++]; + apexori = out.trianglelist[index++]; + torg = *(point3d*)(*equatorpointlist)[orgori]; + tdest = *(point3d*)(*equatorpointlist)[destori]; + tapex = *(point3d*)(*equatorpointlist)[apexori]; + maketetrahedron(&testtet); + makeshellface(&subfaces, &testsh); + setmark(testsh, shellmark); + add2north = iorient3d(torg, tdest, tapex, tnorth); + assert(add2north != 0); + if (add2north < 0) { + setorg(testtet, tdest); + setdest(testtet, torg); + setsorg(testsh, torg); + setsdest(testsh, tdest); + } else { + setorg(testtet, torg); + setdest(testtet, tdest); + setsorg(testsh, tdest); + setsdest(testsh, torg); + } + setapex(testtet, tapex); + setsapex(testsh, tapex); + tsbond(testtet, testsh); + if (verbose > 2) { + printf(" Creating and queuing equator casing face (%d, %d, %d).\n", + pointmark(org(testtet)), pointmark(dest(testtet)), + pointmark(apex(testtet))); + printf(" Creating and bonding equator subface (%d, %d, %d).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + equatorcasinglist->append(&testtet); + } + assert(equatorcasinglist->len() > 0); + } + + if ((bdrycount > 0) || (bdry2count > 0)) { + numberofgiftfaces = equatorcasinglist->len() + northcasinglist->len(); + numberofgiftpoints = equatorpointlist->len() + northpointlist->len(); + } else { + numberofgiftfaces = northcasinglist->len(); + numberofgiftpoints = northpointlist->len(); + } + giftfaces = new triface[numberofgiftfaces]; + giftpoints = new point3d[numberofgiftpoints]; + // Create a backtrace list to keep all new tets be created during + // gift-wrapping stage, so we can delete them if gift-wrapping failed. + northbacktracelist = new list(sizeof(triface)); + + if ((bdrycount > 0) || (bdry2count > 0)) { + // Generate giftfaces. + for (i = 0; i < equatorcasinglist->len(); i++) { + giftfaces[i] = *(triface*)(*equatorcasinglist)[i]; + } + for (j = i; j < numberofgiftfaces; j++) { + giftfaces[j] = *(triface*)(*northcasinglist)[j - i]; + } + // Generate giftpoints. + for (i = 0; i < equatorpointlist->len(); i++) { + giftpoints[i] = *(point3d*)(*equatorpointlist)[i]; + } + for (j = i; j < numberofgiftpoints; j++) { + giftpoints[j] = *(point3d*)(*northpointlist)[j - i]; + } + } else { + // Generate giftfaces. + for (i = 0; i < numberofgiftfaces; i++) { + giftfaces[i] = *(triface*)(*northcasinglist)[i]; + } + // Generate giftpoints. + for (i = 0; i < numberofgiftpoints; i++) { + giftpoints[i] = *(point3d*)(*northpointlist)[i]; + } + } + + success = giftwrapping(numberofgiftfaces, giftfaces, numberofgiftpoints, + giftpoints, northattribs, northvolume, + northbacktracelist); + + if ((bdry2count > 0) && success) { + if (verbose > 2) { + printf(" Removing and replacing fake tets at equator with real tets.\n"); + } + for (i = 0; i < equatorcasinglist->len(); i++) { + incitet = (triface*)(*equatorcasinglist)[i]; + assert(oppo(*incitet) == (point3d) NULL); + sym(*incitet, testtet); + tspivot(*incitet, testsh); + dissolve(testtet); + stdissolve(testsh); + // Remove fake tet. + tetrahedrondealloc(incitet->tet); + // Replace fake tet with a real tet. + adjustedgering(testtet, CCW); + *incitet = testtet; + if (verbose > 2) { + printf(" Changing (%d, %d, %d).\n", pointmark(org(testtet)), + pointmark(dest(testtet)), pointmark(apex(testtet))); + } + } + delete [] giftfaces; + delete [] giftpoints; + + numberofgiftfaces = equatorcasinglist->len() + sourthcasinglist->len(); + numberofgiftpoints = equatorpointlist->len() + sourthpointlist->len(); + giftfaces = new triface[numberofgiftfaces]; + giftpoints = new point3d[numberofgiftpoints]; + // Create a backtrace list to keep all new tets be created during + // gift-wrapping stage, so we can delete them if gift-wrapping failed. + sourthbacktracelist = new list(sizeof(triface)); + + // Generate giftfaces. + for (i = 0; i < equatorcasinglist->len(); i++) { + giftfaces[i] = *(triface*)(*equatorcasinglist)[i]; + } + for (j = i; j < numberofgiftfaces; j++) { + giftfaces[j] = *(triface*)(*sourthcasinglist)[j - i]; + } + // Generate giftpoints. + for (i = 0; i < equatorpointlist->len(); i++) { + giftpoints[i] = *(point3d*)(*equatorpointlist)[i]; + } + for (j = i; j < numberofgiftpoints; j++) { + giftpoints[j] = *(point3d*)(*sourthpointlist)[j - i]; + } + + success = giftwrapping(numberofgiftfaces, giftfaces, numberofgiftpoints, + giftpoints, sourthattribs, sourthvolume, + sourthbacktracelist); + if (!success) { + if (verbose > 2) { + printf(" Badly, gift-wrapping failed, clear all new tets that"); + printf(" created at sourthcasing.\n"); + } + // Removing new subfaces at equators first. Because after new tets + // being removed, subfaces will no holder. + for (i = 0; i < equatorcasinglist->len(); i++) { + testtet = *(triface*)(*equatorcasinglist)[i]; + tspivot(testtet, testsh); + assert(testsh.sh != dummysh); + shellfacedealloc(&subfaces, testsh.sh); + } + // Clear equatorcasinglist to avoid deallocate subfaces twice. + equatorcasinglist->clear(); + // Dealloc all new tets in which created during gift-wrapping. + for (i = 0; i < sourthbacktracelist->len(); i++) { + incitet = (triface*)(*sourthbacktracelist)[i]; + if (verbose > 2) { + printf(" Deleting new tet (%d, %d, %d, %d).\n", + pointmark(org(*incitet)), pointmark(dest(*incitet)), + pointmark(apex(*incitet)), pointmark(oppo(*incitet))); + } + tetrahedrondealloc(incitet->tet); + } + } + } + + if (!success) { + if (verbose > 2) { + printf(" Badly, gift-wrapping failed, clear all new tets that"); + printf(" created during gift-wrapping.\n"); + } + if ((bdrycount > 0) || (bdry2count > 0)) { + // Removing new subfaces at equators. + for (i = 0; i < equatorcasinglist->len(); i++) { + testtet = *(triface*)(*equatorcasinglist)[i]; + tspivot(testtet, testsh); + assert(testsh.sh != dummysh); + shellfacedealloc(&subfaces, testsh.sh); + // It's a equator casing fake tet, delete it. + tetrahedrondealloc(testtet.tet); + } + equatorcasinglist->clear(); + } + for (i = 0; i < northbacktracelist->len(); i++) { + incitet = (triface*)(*northbacktracelist)[i]; + if (verbose > 2) { + printf(" Deleting new tet (%d, %d, %d, %d).\n", + pointmark(org(*incitet)), pointmark(dest(*incitet)), + pointmark(apex(*incitet)), pointmark(oppo(*incitet))); + } + tetrahedrondealloc(incitet->tet); + } + } + + if (success) { + if (verbose > 2) { + printf(" Deleting all incident tets and incident faces.\n"); + } + for (i = 0; i < incitetlist->len(); i++) { + incitet = (triface*)(*incitetlist)[i]; + if (verbose > 2) { + printf(" Deleting tet (%d, %d, %d, %d).\n", + pointmark(org(*incitet)), pointmark(dest(*incitet)), + pointmark(apex(*incitet)), pointmark(oppo(*incitet))); + } + tspivot(*incitet, testsh); + if (testsh.sh != dummysh) { + if (verbose > 2) { + printf(" Deleting subface (%d, %d, %d).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + sym(*incitet, neighbortet); + tsdissolve(neighbortet); + shellfacedealloc(&subfaces, testsh.sh); + } + fnext(*incitet, testtet); + tspivot(testtet, testsh); + if (testsh.sh != dummysh) { + if (verbose > 2) { + printf(" Deleting subface (%d, %d, %d).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + sym(testtet, neighbortet); + tsdissolve(neighbortet); + shellfacedealloc(&subfaces, testsh.sh); + } + enext2(*incitet, testtet); + fnextself(testtet); + tspivot(testtet, testsh); + if (testsh.sh != dummysh) { + if (verbose > 2) { + printf(" Deleting subface (%d, %d, %d).\n", + pointmark(sorg(testsh)), pointmark(sdest(testsh)), + pointmark(sapex(testsh))); + } + sym(testtet, neighbortet); + tsdissolve(neighbortet); + shellfacedealloc(&subfaces, testsh.sh); + } + tetrahedrondealloc(incitet->tet); + } + + // Now we can delete the point. + pointdealloc(delorg); + if (bdrycount > 0) { + // Deleting the boundary point decreases the number of boundary faces. + hullsize -= 2; + } + } else { + // Gift-wrapping failed, we need restore original configuration. + // This time, all tets in 'incitetlist' are still keep the pointers + // to its casing face, use this pointers to restore original config- + // uration. + if (verbose > 2) { + printf(" Fail to delete point, restoring original configuration.\n"); + } + for (i = 0; i < incitetlist->len(); i++) { + incitet = (triface*)(*incitetlist)[i]; + enextfnextself(*incitet); + // Get and bond its casing face if its not a 'dummytet'. + sym(*incitet, testtet); + if (testtet.tet != dummytet) { + bond(*incitet, testtet); + } + // Get and bond the subface if its not a 'dummysh'. + tspivot(*incitet, testsh); + if (testsh.sh != dummysh) { + tsbond(*incitet, testsh);; + } + } + } + + // Whatever success or not success, we must do the following step. + if (verbose > 2) { + printf(" Removing and replacing fake tets in north with dummytet.\n"); + } + for (i = 0; i < northcasinglist->len(); i++) { + testtet = *(triface*)(*northcasinglist)[i]; + if (oppo(testtet) == (point3d) NULL) { + if (verbose > 2) { + printf(" Changing (%d, %d, %d).\n", pointmark(org(testtet)), + pointmark(dest(testtet)), pointmark(apex(testtet))); + } + if (success) { + // Here do dissolve only success == 1. + sym(testtet, neighbortet); + dissolve(neighbortet); + } + tspivot(testtet, testsh); + if (testsh.sh != dummysh) { + stdissolve(testsh); + } + tetrahedrondealloc(testtet.tet); + testtet = neighbortet; + if (shflaws && success) { + uncheckedshlist->append(&testsh); + } + } + // Ensure adjoining tets quality at casing face. + if (tetflaws && success) { + uncheckedtetlist->append(&testtet); + sym(testtet, neighbortet); + if (neighbortet.tet != dummytet) { + uncheckedtetlist->append(&neighbortet); + } + } + } + + if (bdry2count > 0) { + if (verbose > 2) { + printf(" Removing and replacing fake tets in sourth with dummytet.\n"); + } + for (i = 0; i < sourthcasinglist->len(); i++) { + testtet = *(triface*)(*sourthcasinglist)[i]; + if (oppo(testtet) == (point3d) NULL) { + // This is a fake tet, delete it and set it be 'dummytet'. + if (verbose > 2) { + printf(" Changing (%d, %d, %d).\n", + pointmark(org(testtet)), pointmark(dest(testtet)), + pointmark(apex(testtet))); + } + if (success) { + // Here do dissolve only success == 1. + sym(testtet, neighbortet); + dissolve(neighbortet); + } + tspivot(testtet, testsh); + if (testsh.sh != dummysh) { + stdissolve(testsh); + } + tetrahedrondealloc(testtet.tet); + testtet = neighbortet; + if (shflaws && success) { + uncheckedshlist->append(&testsh); + } + } + // Ensure adjoining tets quality at casing face. + if (tetflaws && success) { + uncheckedtetlist->append(&testtet); + sym(testtet, neighbortet); + if (neighbortet.tet != dummytet) { + uncheckedtetlist->append(&neighbortet); + } + } + } + } + + if ((bdrycount > 0) || (bdry2count > 0)) { + if (verbose > 2) { + printf(" Bonding subsegments to equator subfaces.\n"); + } + for (i = 0; i < equatorcasinglist->len(); i++) { + testtet = *(triface*)(*equatorcasinglist)[i]; + tspivot(testtet, testsh); + // For keep the same enext() direction. + findversion(&testsh, org(testtet), dest(testtet), 0); + for (j = 0; j < 3; j++) { + tsspivot(&testtet, &testseg); + if (testseg.sh != dummysh) { + ssbond(testsh, testseg); + } + enextself(testtet); + senextself(testsh); + } + // Check quality if need. + if (shflaws && success) { + uncheckedshlist->append(&testsh); + } + if (bdry2count == 0) { + // Remove and replacing fake tet at equator with 'dummytet'. + sym(testtet, neighbortet); + dissolve(neighbortet); + tspivot(testtet, testsh); + if (testsh.sh != dummysh) { + stdissolve(testsh); + } + tetrahedrondealloc(testtet.tet); + testtet = neighbortet; + } + if (tetflaws && success) { + uncheckedtetlist->append(&testtet); + if (bdry2count > 0) { + symself(testtet); + uncheckedtetlist->append(&testtet); + } + } + } + } + + if (!success && docheck) { + checkmesh(); + checkshells(); + } + + delete [] giftfaces; + delete [] giftpoints; + delete incitetlist; + delete incishlist; + delete northpointlist; + delete northcasinglist; + delete northbacktracelist; + if ((bdrycount > 0) || (bdry2count > 0)) { + delete equatorpointlist; + delete equatoredgelist; + delete equatorcasinglist; + if (bdry2count > 0) { + delete sourthpointlist; + delete sourthcasinglist; + delete sourthbacktracelist; + } + } + if (eextras) { + delete [] northattribs; + if (bdry2count > 0) { + delete [] sourthattribs; + } + } + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Insert/Delete point routines // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Giftwrapping Algorithm Implementation // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Using gift-wrapping algorithm to construct a constrained Delaunay // +// triangulation for input triangular faces bounded polyhedras. // +// // +// Gift-wrapping (also called pivoting or incremental search) algorithm // +// can be used to solve Convex Hull(CH) problem, construct Delaunay Triangu- // +// lation(DT) and Constrained Delaunay Triangulation(CDT) in d-dimension. // +// // +// To construct three-dimensional CDT, gift-wrapping begins by finding a // +// single constrained Delaunay tetrahedron, upon which the remaining // +// Delaunay tetrahedras crystallize one by one. Say that a constraining // +// triangluar face of a Delaunay tetrahedron is UNFINISHED if the algorithm // +// has not yet identified the other Delaunay tetrahedron that shares the // +// face. To finish a face is to find the other tetrahedron, one must test // +// the visibility of each vertex from that face, If the face lies in the // +// boundary of the input, the face becomes finished when the algorithm // +// recognizes that there is no other adjoining tetrahedron. // +// // +// Generally, the running time for constructing a CDT using gift-wrapping // +// is O(nv*nf*ns), where nv is the number of input vertexs, nf is the number // +// of constraining triangluar face and ns is the number of final tetrahedra. // +// There have many variant implementations of this algorithm to improve the // +// poor time complexity of general gift-wrapping. Although in practice, gift // +// -wrapping is not very suitable for constructing CDT. But if the vertexs' // +// and constraining faces number are tipically small, the performance of the // +// algorithm used is not critical, our improved implementation of gift- // +// wrapping will suffice. // +// // +// The main idea in achieving an optimal result of gift-wrapping algorithm // +// is that of eliminating redundant computations. Observe for each // +// UNFINISHED face, we must find a best visible vertex that can form a // +// constrained Delaunay tetrahedron with this face. For each visible vertex, // +// we need check all other constraining faces to see if they are obstructed // +// the visibility of vertex from the face. If we have known some of these // +// constraining faces can not obstructed the visibility of vertex in advance,// +// we could have same time not to compute them. // +// // +// For this purpose, I use a Weighted-Vertex approach, which is widely // +// used in variety of graph algorithms, to help us determine whether a face // +// can be safely skipped. At first each vertex's weight is the number of // +// edges connected at this vertex. So all vertex's weight must great or // +// equal than 3. During the procedure of gift-wrapping, the weight of vertex // +// will decrease 1 when an UNFINISHED face(contains this vertex) becaomes // +// finished, and increase 1 when a new UNFINISHED face(contains this vertex) // +// be generated. Then, if a face contains a vertex with Zero-weight, it // +// means the face has already been finished, so it has no chance to obstruct // +// other vertex(Remember the meaning of CDT), we can saftly skip this face // +// in later computing. The weight of vertex also can help us in chossing a // +// best face to start and chossing a best vertex when there exist more than // +// one visible vertexs. Please see the functions getgiftface() and // +// getgiftpoint() for a detail description. // +// // +// At last, there need do a lot of triangle-triangle intersection tests in // +// finding a best vertex to finish a face. I use the codes of Tomas Moller // +// directly to do the tests, see below. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Triangle/triangle intersection test routine, by Tomas Moller. // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Triangle/triangle intersection test routine, // +// by Tomas Moller, 1997. // +// See article "A Fast Triangle-Triangle Intersection Test", // +// Journal of Graphics Tools, 2(2), 1997 // +// // +// int tri_tri_intersect(REAL V0[3], REAL V1[3], REAL V2[3], // +// REAL U0[3], REAL U1[3], REAL U2[3]) // +// // +// parameters: vertices of triangle 1: V0,V1,V2 // +// vertices of triangle 2: U0,U1,U2 // +// result : returns 1 if the triangles intersect, otherwise 0 // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// fuzzycomp_interval() Compare two floating-point values dx and dy. // +// // +// Return +1 if dx > dy; Return -1 if dx < dy; Return 0 if dx == dy. // +// // +// Si hang added this function for compare intervals. July, 2001. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int fuzzycomp_interval(REAL dA, REAL dB) +{ + REAL dSum, dDiff; + REAL dTol = 5.e-9; + REAL dFloor = 1.e-6; + + dSum = fabs(dA) + fabs(dB); + dDiff = dA - dB; + dSum = (dSum > dFloor) ? dSum : dFloor; + if (dDiff > dTol * dSum) return 1; + else if (dDiff < - dTol * dSum) return -1; + else return 0; +} + +// if USE_EPSILON_TEST is true then we do a check: +// if |dv|<EPSILON then dv=0.0; +// else no check is done (which is less robust) + +#define USE_EPSILON_TEST TRUE +#define EPSILON 1e-7 + +// some macros +#define CROSS(dest,v1,v2) \ + dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ + dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ + dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; + +#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) + +#define SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + +// sort so that a<=b +#define SORT(a,b) \ + if(a>b) { \ + REAL c; \ + c=a; a=b; b=c; \ + } + +#define ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1) \ + isect0=VV0+(VV1-VV0)*D0/(D0-D1); \ + isect1=VV0+(VV2-VV0)*D0/(D0-D2); + +#define COMPUTE_INTERVALS(VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,isect0,isect1) \ + if(D0D1>0.0f) \ + { \ + /* here we know that D0D2<=0.0 */ \ +/* that is D0, D1 are on the same side, D2 on the other or on the plane */ \ + ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \ + } \ + else if(D0D2>0.0f) \ + { \ + /* here we know that d0d1<=0.0 */ \ + ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \ + } \ + else if(D1*D2>0.0f || D0!=0.0f) \ + { \ + /* here we know that d0d1<=0.0 or that D0!=0.0 */ \ + ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1); \ + } \ + else if(D1!=0.0f) \ + { \ + ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \ + } \ + else if(D2!=0.0f) \ + { \ + ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \ + } \ + else \ + { \ + /* triangles are coplanar */ \ + return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2); \ + } + +// this edge to edge test is based on Franlin Antonio's gem: +// "Faster Line Segment Intersection", in Graphics Gems III, +// pp. 199-202 +#define EDGE_EDGE_TEST(V0,U0,U1) \ + Bx=U0[i0]-U1[i0]; \ + By=U0[i1]-U1[i1]; \ + Cx=V0[i0]-U0[i0]; \ + Cy=V0[i1]-U0[i1]; \ + f=Ay*Bx-Ax*By; \ + d=By*Cx-Bx*Cy; \ + if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) \ + { \ + e=Ax*Cy-Ay*Cx; \ + if(f>0) \ + { \ + if(e>=0 && e<=f) return 1; \ + } \ + else \ + { \ + if(e<=0 && e>=f) return 1; \ + } \ + } + +#define EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2) \ +{ \ + REAL Ax,Ay,Bx,By,Cx,Cy,e,d,f; \ + Ax=V1[i0]-V0[i0]; \ + Ay=V1[i1]-V0[i1]; \ + /* test edge U0,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U0,U1); \ + /* test edge U1,U2 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U1,U2); \ + /* test edge U2,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U2,U0); \ +} + +#define POINT_IN_TRI(V0,U0,U1,U2) \ +{ \ + REAL a,b,c,d0,d1,d2; \ + /* is T1 completly inside T2? */ \ + /* check if V0 is inside tri(U0,U1,U2) */ \ + a=U1[i1]-U0[i1]; \ + b=-(U1[i0]-U0[i0]); \ + c=-a*U0[i0]-b*U0[i1]; \ + d0=a*V0[i0]+b*V0[i1]+c; \ + \ + a=U2[i1]-U1[i1]; \ + b=-(U2[i0]-U1[i0]); \ + c=-a*U1[i0]-b*U1[i1]; \ + d1=a*V0[i0]+b*V0[i1]+c; \ + \ + a=U0[i1]-U2[i1]; \ + b=-(U0[i0]-U2[i0]); \ + c=-a*U2[i0]-b*U2[i1]; \ + d2=a*V0[i0]+b*V0[i1]+c; \ + if(d0*d1>0.0) \ + { \ + if(d0*d2>0.0) return 1; \ + } \ +} + +int coplanar_tri_tri(const REAL N[3], + const REAL V0[3],const REAL V1[3],const REAL V2[3], + const REAL U0[3],const REAL U1[3],const REAL U2[3]) +{ + REAL A[3]; + short i0,i1; + // first project onto an axis-aligned plane, that maximizes the area + // of the triangles, compute indices: i0,i1. + A[0]=fabs(N[0]); + A[1]=fabs(N[1]); + A[2]=fabs(N[2]); + if(A[0]>A[1]) + { + if(A[0]>A[2]) + { + i0=1; // A[0] is greatest + i1=2; + } + else + { + i0=0; // A[2] is greatest + i1=1; + } + } + else // A[0]<=A[1] + { + if(A[2]>A[1]) + { + i0=0; // A[2] is greatest + i1=1; + } + else + { + i0=0; // A[1] is greatest + i1=2; + } + } + + // test all edges of triangle 1 against the edges of triangle 2 + EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2); + EDGE_AGAINST_TRI_EDGES(V1,V2,U0,U1,U2); + EDGE_AGAINST_TRI_EDGES(V2,V0,U0,U1,U2); + + // finally, test if tri1 is totally contained in tri2 or vice versa + POINT_IN_TRI(V0,U0,U1,U2); + POINT_IN_TRI(U0,V0,V1,V2); + + return 0; +} + + +int tri_tri_intersect(const REAL V0[3], const REAL V1[3], const REAL V2[3], + const REAL U0[3], const REAL U1[3], const REAL U2[3]) +{ + REAL E1[3],E2[3]; + REAL N1[3],N2[3],d1,d2; + REAL du0,du1,du2,dv0,dv1,dv2; + REAL D[3]; + REAL isect1[2], isect2[2]; + REAL du0du1,du0du2,dv0dv1,dv0dv2; + short index; + REAL vp0,vp1,vp2; + REAL up0,up1,up2; + REAL b,c,max; + + // compute plane equation of triangle(V0,V1,V2) + SUB(E1,V1,V0); + SUB(E2,V2,V0); + CROSS(N1,E1,E2); + d1=-DOT(N1,V0); + // plane equation 1: N1.X+d1=0 + + // put U0,U1,U2 into plane equation 1 to compute signed distances to + // the plane + du0=DOT(N1,U0)+d1; + du1=DOT(N1,U1)+d1; + du2=DOT(N1,U2)+d1; + + // coplanarity robustness check +#if USE_EPSILON_TEST==TRUE + if(fabs(du0)<EPSILON) du0=0.0; + if(fabs(du1)<EPSILON) du1=0.0; + if(fabs(du2)<EPSILON) du2=0.0; +#endif + du0du1=du0*du1; + du0du2=du0*du2; + + if(du0du1>0.0f && du0du2>0.0f) // same sign on all of them + not equal 0 ? + return 0; // no intersection occurs + + // compute plane of triangle (U0,U1,U2) + SUB(E1,U1,U0); + SUB(E2,U2,U0); + CROSS(N2,E1,E2); + d2=-DOT(N2,U0); + // plane equation 2: N2.X+d2=0 + + // put V0,V1,V2 into plane equation 2 + dv0=DOT(N2,V0)+d2; + dv1=DOT(N2,V1)+d2; + dv2=DOT(N2,V2)+d2; + +#if USE_EPSILON_TEST==TRUE + if(fabs(dv0)<EPSILON) dv0=0.0; + if(fabs(dv1)<EPSILON) dv1=0.0; + if(fabs(dv2)<EPSILON) dv2=0.0; +#endif + + dv0dv1=dv0*dv1; + dv0dv2=dv0*dv2; + + if(dv0dv1>0.0f && dv0dv2>0.0f) // same sign on all of them + not equal 0 ? + return 0; // no intersection occurs + + // compute direction of intersection line + CROSS(D,N1,N2); + + // compute and index to the largest component of D + max=fabs(D[0]); + index=0; + b=fabs(D[1]); + c=fabs(D[2]); + if(b>max) max=b,index=1; + if(c>max) max=c,index=2; + + // this is the simplified projection onto L + vp0=V0[index]; + vp1=V1[index]; + vp2=V2[index]; + + up0=U0[index]; + up1=U1[index]; + up2=U2[index]; + + // compute interval for triangle 1 + COMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,isect1[0],isect1[1]); + + // compute interval for triangle 2 + COMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,isect2[0],isect2[1]); + + SORT(isect1[0],isect1[1]); + SORT(isect2[0],isect2[1]); + + // Origin code, + // if(isect1[1]<isect2[0] || isect2[1]<isect1[0]) return 0; + // New code, changed by Si hang + if ((fuzzycomp_interval(isect1[1], isect2[0]) == -1) || + (fuzzycomp_interval(isect2[1], isect1[0]) == -1)) return 0; + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// Triangle/triangle intersection test routine, by Tomas Moller. // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// struct giftface // +// // +// A struct used to store a face for giftwrapping algorithm to construct // +// constrained Delaunay tetrahedralization. Where 'casing' is a handle of // +// unfinished face, and 'iorg', 'idest' and 'iapex' are vertex index in // +// pointweightlist. Use these values to get weights value for each vertices // +// of this giftface. // +// // +/////////////////////////////////////////////////////////////////////////////// + +typedef struct giftfacetype { + triface casing; + int iorg, idest, iapex; +} giftface; + +/////////////////////////////////////////////////////////////////////////////// +// // +// dumpgifts() Debug routine. Write current gift faces and gift points // +// infomation to file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::dumpgifts(char *gfile, list *giftfacelist, int numberofgiftpoints, + point3d *giftpoints, int *pointweightlist) +{ + FILE *outfile; + giftface *gfaceptr; + int i; + + outfile = fopen(gfile, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", gfile); + return; + } + if (!quiet) { + printf("Writing %s.\n", gfile); + } + if (verbose < 1) { + numbernodes(1); + } + fprintf(outfile, "# Dumpping %d giftfaces.\n", giftfacelist->len()); + for (i = 0; i < giftfacelist->len(); i++) { + gfaceptr = (giftface*)(*giftfacelist)[i]; + fprintf(outfile, "%4d %4d %4d %4d index %3d %3d %3d.\n", i, + pointmark(org(gfaceptr->casing)), + pointmark(dest(gfaceptr->casing)), + pointmark(apex(gfaceptr->casing)), + gfaceptr->iorg, gfaceptr->idest, gfaceptr->iapex); + } + fprintf(outfile, "# Dumpping %d giftpoints.\n", numberofgiftpoints); + for (i = 0; i < numberofgiftpoints; i++) { + fprintf(outfile, "%4d %4d %10.8g %10.8g %10.8g weight %4d.\n", i, + pointmark(giftpoints[i]), giftpoints[i][0], giftpoints[i][1], + giftpoints[i][2], pointweightlist[i]); + } + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// is_face_tet_inter_0() Check if input face is intersecting with input // +// tetrahedron(given by its four vertexs). There // +// no shared points between face and tetrahedron. // +// // +// Take each faces of tetrahedron and do triangle-triangle intersection test // +// with the input face. They are intersected if one of face pairs are inter- // +// secting. If all face pairs are not intersecting, we still need to check // +// if the face is fully in the tetrahedron. // +// // +// Return 1 if they are intersecting each other. Otherwise, return zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::is_face_tet_inter_0(triface *checkface, point3d torg, point3d tdest, + point3d tapex, point3d toppo) +{ + point3d forg, fdest, fapex; + int intersect; + int i; + + org (*checkface, forg); + dest(*checkface, fdest); + apex(*checkface, fapex); + + intersect = 0; + for (i = 0; i < 4; i++) { + if (i == 0) { + // 'checkface' must not intersect with base face. Remember, they are + // both the input constraining faces. + // intersect = tri_tri_intersect(forg, fdest, fapex, torg, tdest, tapex); + } else if (i == 1) { + intersect = tri_tri_intersect(forg, fdest, fapex, torg, tdest, toppo); + } else if (i == 2) { + intersect = tri_tri_intersect(forg, fdest, fapex, tdest, tapex, toppo); + } else if (i == 3) { + intersect = tri_tri_intersect(forg, fdest, fapex, tapex, torg, toppo); + } + if (intersect) { + return 1; + } + } + // Check the face is located in the tetrahedron. That is to check each + // vertex of face is located in tetrahedron. Because no face pair is + // intersecting(above step guarantee this) and not exists share point. + // We only need check one vertex, choose aribitarily. + enum locateresult loc = isintet(torg, tdest, tapex, toppo, forg); + // There only two cases (in or out) can be happened. + if ((intersect != OUTSIDE) && (intersect != INTETRAHEDRON)) { + printf("Internal error in is_face_tet_inter_0(): \n"); + printf(" Invalid isintet() return value: %i. \n", intersect); + printf(" (0 = coplanar, 1 = on vertex, 2 = on edge, 3 = on face.)\n"); + internalerror(); + } + intersect = (loc == OUTSIDE) ? 0 : 1; + return intersect; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// is_face_tet_inter_1() Check if input face is intersecting with input // +// tetrahedron(given by its four vertexs). There 1 // +// share points between face and tetrahedron. // +// // +// Here we can reuse is_face_tet_inter_0(). But before use it we must detach // +// the share point between face and tetrahedron. // +// // +// Return 1 if they are intersecting each other. Otherwise, return zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::is_face_tet_inter_1(triface *checkface, point3d torg, point3d tdest, + point3d tapex, point3d toppo) +{ + point3d forg, fdest, fapex; + REAL tmpforg[3], center[3]; + int oldversion, intersect; + int i; + + // We will change the checkface's version. So we must restore it after + // checking, otherwise, the index fields in each giftface will invalid. + oldversion = checkface->ver; + // First set the share vertex be origin of checkface. + for (i = 0; i < 3; i++) { + org(*checkface, forg); + if ((forg == torg) || (forg == tdest) || + (forg == tapex) || (forg == toppo)) { + dest(*checkface, fdest); + apex(*checkface, fapex); + break; + } + enextself(*checkface); + } + assert(i < 3); + + center[0] = 0.5 * (fdest[0] + fapex[0]); + center[1] = 0.5 * (fdest[1] + fapex[1]); + center[2] = 0.5 * (fdest[2] + fapex[2]); + + tmpforg[0] = 0.5 * (forg[0] + center[0]); + tmpforg[1] = 0.5 * (forg[1] + center[1]); + tmpforg[2] = 0.5 * (forg[2] + center[2]); + + setorg(*checkface, tmpforg); + intersect = is_face_tet_inter_0(checkface, torg, tdest, tapex, toppo); + setorg(*checkface, forg); + checkface->ver = oldversion; + + return intersect; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// is_face_tet_inter_2() Check if input face is intersecting with input // +// tetrahedron(given by its four vertexs). There 2 // +// share points between face and tetrahedron. // +// // +// The algorithm: // +// // +// Set the face be f, two share point be org, dest. The other point in face // +// be apex, The other two points in tetrahedron be v0, v1. // +// Take f as reference plane. Check spatial relation between f and v0, v1. // +// IF (v0 and v1 are locating at the same side to f) THEN // +// return No intersecyion; // +// ELSE IF (v0 and v1 are locating at diffrent side to f) THEN // +// Take a face f' in tetrahedron which contain v0 but not contain v1 as // +// reference plane. Check spatial relation between f' and apex, v1. // +// IF (apex and v1 are loacting at the same side of f') THEN // +// return Intersection; // +// ELSE // +// return No intersection; // +// END // +// ELSE IF (v0 or v1 is coplanar with f) THEN // +// IF (v0 is coplanar with f) THEN // +// Take apex face f' in tetrahedron which contain v1 but not conatin v0 // +// as reference plane. Check spatial relation between f' and apex, v0. // +// IF (apex and v0 are loacting at the same side of f') THEN // +// return Intersection; // +// ELSE // +// return No intersection; // +// END // +// ELSE // +// (v1 is coplanar with f) // +// Take apex face f' in tetrahedron which contain v0 but not conatin v1 // +// as reference plane. Check spatial relation between f' and apex, v1. // +// IF (apex and v1 are loacting at the same side of f') THEN // +// return Intersection; // +// ELSE // +// return No intersection; // +// END // +// END // +// ELSE // +// (both v0 and v1 are coplanar with f) // +// Impossible case. Throw; // +// END // +// // +// Return 1 if they are intersecting each other. Otherwise, return zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::is_face_tet_inter_2(triface *checkface, point3d torg, point3d tdest, + point3d tapex, point3d toppo) +{ + point3d forg, fdest, fapex, v0, v1; + int iori1, iori2, iori3, iori4; + int oldversion, intersect; + int i; + + // We will change the checkface's version. So we must restore it after + // checking, otherwise, the index fields in each giftface will invalid. + oldversion = checkface->ver; + // Get forg, fdest, fapex. + for (i = 0; i < 3; i++) { + org(*checkface, forg); + dest(*checkface, fdest); + if (((forg == torg) || (forg == tdest) || + (forg == tapex) || (forg == toppo)) && + ((fdest == torg) || (fdest == tdest) || + (fdest == tapex) || (fdest == toppo))) { + apex(*checkface, fapex); + break; + } + enextself(*checkface); + } + assert(i < 3); + + // Get v0, v1. + if ((torg == forg) || (torg == fdest)) { + if ((tdest == forg) || (tdest == fdest)) { + v0 = tapex; v1 = toppo; + } else if ((tapex == forg) || (tapex == fdest)) { + v0 = tdest; v1 = toppo; + } else { + assert((toppo == forg) || (toppo == fdest)); + v0 = tdest; v1 = tapex; + } + } else { + v0 = torg; + if ((tdest == forg) || (tdest == fdest)) { + if ((tapex == forg) || (tapex == fdest)) { + v1 = toppo; + } else { + assert((toppo == forg) || (toppo == fdest)); + v1 = tapex; + } + } else { + assert((tapex == forg) || (tapex == fdest)); + assert((toppo == forg) || (toppo == fdest)); + v1 = tdest; + } + } + assert((v0 != forg) && (v0 != fdest)); + assert((v1 != forg) && (v1 != fdest)); + + iori1 = iorient3d(forg, fdest, fapex, v0); + iori2 = iorient3d(forg, fdest, fapex, v1); + if (iori1 * iori2 != 0) { + // No coplanar case. + if (iori1 == iori2) { + intersect = 0; // No intersection. + } else { + iori3 = iorient3d(forg, fdest, v0, fapex); + iori4 = iorient3d(forg, fdest, v0, v1); + assert(iori3 * iori4 != 0); + if (iori3 != iori4) { + intersect = 0; // No intersection. + } else { + intersect = 1; // Intersection. + } + } + } else { + // v0, or v1 is coplanar with face. + if (iori1 == 0) { + assert(iori2 != 0); + // v0 is coplanar with face, choose v1. + iori3 = iorient3d(forg, fdest, v1, v0); + iori4 = iorient3d(forg, fdest, v1, fapex); + } else { + assert(iori1 != 0); + // v1 is coplanar with face, choose v0. + iori3 = iorient3d(forg, fdest, v0, v1); + iori4 = iorient3d(forg, fdest, v0, fapex); + } + assert(iori3 * iori4 != 0); + if (iori3 != iori4) { + intersect = 0; // No intersection. + } else { + intersect = 1; // Intersection. + } + } + + checkface->ver = oldversion; + return intersect; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getgiftface() Search for a face that most suitable for finishing. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::getgiftface(list *giftfacelist, int numberofgiftpoints, + int *pointweightlist, list *smallweightlist) +{ + giftface *gfaceptr; + int smallestweight, smallestindex; + int gpointindex, giftfaceweight; + int i, j, index; + + // First find the smallest weight and index in giftpoint array. + smallestweight = 1000; + smallestindex = -1; + for (i = 0; i < numberofgiftpoints; i++) { + // We only need consider the unfinished giftpoint. + if (pointweightlist[i] == 0) continue; + if (pointweightlist[i] < smallestweight) { + smallestweight = pointweightlist[i]; + smallestindex = i; + } + } + assert(smallestindex != -1); + + if (smallestweight == 3) { + for (i = 0; i < giftfacelist->len(); i++) { + gfaceptr = (giftface*) (*giftfacelist)[i]; + if ((pointweightlist[gfaceptr->iorg] == 3) + || (pointweightlist[gfaceptr->idest] == 3) + || (pointweightlist[gfaceptr->iapex] == 3)) { + break; + } + } + assert(i < giftfacelist->len()); + return i; + } else { + // For there might exist more than one points have the same smallest + // weight, I use a list to keep all these points. + smallweightlist = new list("int"); + smallweightlist->append(&smallestindex); + for (i = smallestindex + 1; i < numberofgiftpoints; i++) { + if (pointweightlist[i] == smallestweight) { + smallweightlist->append(&i); + } + } + // Find the most suit giftface. Get each point index from + // 'smallweightlist', then find in giftface array. If a giftface + // contain this point, then sum its weight of three point. Get a + // giftface which have the smallest sum of weight. + smallestweight = 1000; + smallestindex = -1; + for (i = 0; i < smallweightlist->len(); i++) { + gpointindex = *(int*)(*smallweightlist)[i]; + for (j = 0; j < giftfacelist->len(); j++) { + gfaceptr = (giftface*) (*giftfacelist)[j]; + if ((gfaceptr->iorg == gpointindex) || + (gfaceptr->idest == gpointindex) || + (gfaceptr->iapex == gpointindex)) { + giftfaceweight = pointweightlist[gfaceptr->iorg] + + pointweightlist[gfaceptr->idest] + + pointweightlist[gfaceptr->iapex]; + if (smallestweight > giftfaceweight) { + smallestweight = giftfaceweight; + smallestindex = j; + } + } + } + } + assert(smallestindex != -1); + return smallestindex; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getgiftpoint() Search for a point that finished input face. // +// // +// To get a visible giftpoint for input giftface, we need: // +// (1) First find all points which visible from input face. // +// (2) Choose a best (gift)point among these points. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::getgiftpoint(triface *gface, list *giftfacelist, + int numberofgiftpoints, point3d *giftpoints, + int *pointweightlist, list *candpointindexlist, + list *bestpointindexlist) +{ + giftface *gfaceptr; + point3d forg, fdest, fapex; + point3d testpoint1, testpoint2; + int intersect, sharecount; + int inspheretest, findflag; + int gpointindex1, gpointindex2; + int largerweight; + int i, j, index; + + // Get the vertexs of input (gift)face. + dest(*gface, forg); + org(*gface, fdest); + apex(*gface, fapex); + if (verbose > 2) { + printf(" Finding a point for finishing face (%d, %d, %d).\n", + pointmark(forg), pointmark(fdest), pointmark(fapex)); + } + + candpointindexlist->clear(); + + // Step (1): + for (i = 0; i < numberofgiftpoints; i++) { + if (pointweightlist[i] == 0) { + // All faces which contain this point were finished. + continue; + } + testpoint1 = giftpoints[i]; + if ((testpoint1 == forg) || (testpoint1 == fdest) || + (testpoint1 == fapex)) { + continue; // Skip the vertex of face. + } + if (!isaboveplane(forg, fdest, fapex, testpoint1)) { + // This point is below or coplanar or coincident with this face. + continue; + } + if (verbose > 2) { + printf(" Checking point %d.\n", pointmark(testpoint1)); + } + // Now, we found that this point is vivible form face, we still need + // to check if there exist any constraining face that will cause + // this point unvisible form face. + intersect = 0; + for (j = 0; j < giftfacelist->len(); j++) { + gfaceptr = (giftface*) (*giftfacelist)[j]; + if ((pointweightlist[gfaceptr->iorg] == 0) || + (pointweightlist[gfaceptr->idest] == 0) || + (pointweightlist[gfaceptr->iapex] == 0)) { + // This face contain a zero-weight point, skip it. + continue; + } + if (verbose > 2) { + printf(" Checking face (%d, %d, %d).\n", + pointmark(org(gfaceptr->casing)), + pointmark(dest(gfaceptr->casing)), + pointmark(apex(gfaceptr->casing))); + } + // Check if this face's vertexs are share with the new tet. + sharecount = 0; + if (isfacehaspoint(&gfaceptr->casing, forg)) sharecount++; + if (isfacehaspoint(&gfaceptr->casing, fdest)) sharecount++; + if (isfacehaspoint(&gfaceptr->casing, fapex)) sharecount++; + if (isfacehaspoint(&gfaceptr->casing, testpoint1)) sharecount++; + // assert(sharecount < 4); + if (sharecount == 0) { + intersect = is_face_tet_inter_0(&gfaceptr->casing, forg, fdest, fapex, + testpoint1); + } else if (sharecount == 1) { + intersect = is_face_tet_inter_1(&gfaceptr->casing, forg, fdest, fapex, + testpoint1); + } else if (sharecount == 2) { + intersect = is_face_tet_inter_2(&gfaceptr->casing, forg, fdest, fapex, + testpoint1); + } else { // if (sharecount == 3) + intersect = 0; + } + if (intersect) { + // If exist intersection face. break find face loop. + if (verbose > 2) { + printf(" Skipped.\n"); + } + break; + } + } + if (!intersect) { + if (verbose > 2) { + printf(" Visible from face.\n"); + } + candpointindexlist->append(&i); + } + } + if (candpointindexlist->len() == 0) { + return -1; // Cannot find a point that can finish input face. + } + + // Setp (2): + if (verbose > 2) { + printf(" Find %d candidating points.\n", candpointindexlist->len()); + } + + // Find a best point index from 'candpointindexlist'. A best point + // should be satisfied following rules: + // (1) It must satisfy the empty circumsphere criteria with the input + // face. If there exist more than one such point, choose the point + // which has the largest weight. + // (2) The degenerate case is there exist cosphere points. If so, we + // should choose a point which has the largest weight (This will + // helpfully avoid form a tetrahedron that cause later giftwrapping + // failed). + // (3) If no satisfied point be found. This mean we can't form a constr- + // ained Delaunay tetrahedralization from this faces and points(In + // my case, it should not happen.) For complete the job, we choose + // the point which has the maximized minimum-dihedral angle (not + // done yet). + + bestpointindexlist->clear(); + + for (i = 0; i < candpointindexlist->len(); i++) { + findflag = 1; + gpointindex1 = *(int*)(*candpointindexlist)[i]; + testpoint1 = giftpoints[gpointindex1]; + for (j = 0; j < candpointindexlist->len(); j++) { + if (j == i) continue; // Skip the same point. + gpointindex2 = *(int*)(*candpointindexlist)[j]; + testpoint2 = giftpoints[gpointindex2]; + inspheretest = iinsphere(fdest, forg, fapex, testpoint1, testpoint2); + if (inspheretest > 0) { + findflag = 0; + break; + } else if (inspheretest == 0) { + if (pointweightlist[gpointindex1] < pointweightlist[gpointindex2]) { + findflag = 0; + break; + } + } + } + if (findflag) { + bestpointindexlist->append(&gpointindex1); + } + } + + if (verbose > 2) { + printf(" Find %d best points.\n", bestpointindexlist->len()); + } + if (bestpointindexlist->len() == 1) { + gpointindex1 = *(int*)(*bestpointindexlist)[0]; + } else if (bestpointindexlist->len() > 1) { + for (i = 0; i < bestpointindexlist->len(); i++) { + gpointindex2 = *(int*)(*bestpointindexlist)[i]; + if (verbose > 2) { + printf(" Checking point %d, weight = %d.\n", + pointmark(giftpoints[gpointindex2]), + pointweightlist[gpointindex2]); + } + if (i == 0) { + largerweight = pointweightlist[gpointindex2]; + gpointindex1 = gpointindex2; + } else { + if (largerweight < pointweightlist[gpointindex2]) { + largerweight = pointweightlist[gpointindex2]; + gpointindex1 = gpointindex2; + } + } + } + } else { + // No constrained Delaunay tetrahedralization can be formed. + printf("Internal error in getgiftpoint(): \n"); + printf(" Cannot find a best point from %i points", + candpointindexlist->len()); + internalerror(); + } + + return gpointindex1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// giftwrapping() Generate a constrained Delaunay tetrahedralization from // +// input faces and points use Giftwrapping algorithm. // +// // +// The Giftwrapping algorithm: // +// The gift-wrapping algorithm maintains a dictionary of unfinished faces, // +// which initially contains the d+1 faces of the first Delaunay d-simplex. // +// Repeat the following step: remove an arbitrary unfinished face f from the // +// dictionary and search for a vertex that finishes f. This vertex must be // +// above f, and have the property that the new d-simplex that results has an // +// empty circumsphere. Na��vely, this search takes O(n) time. If no vertex // +// is above f, then f lies on the boundary of the convex hull. Otherwise, s // +// becomes part of the growing triangulation. Check each (d-1)-face of s, // +// except f against the dictionary. If a face is already present in the dic- // +// tionary, then the face is now finished, so remove it from the dictionary. // +// Otherwise, the face is new, so insert it into the dictionary. // +// // +// 'backtracelist' is a list to keep all new tets be created during gift- // +// wrapping stage, so we can delete them if gift-wrapping failed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::giftwrapping(int numberofgiftfaces, triface *giftfaces, + int numberofgiftpoints, point3d *giftpoints, + REAL *tetattribs, REAL volumevar, list *backtracelist) +{ + list *giftfacelist; + list *smallweightlist; + list *candpointindexlist, *bestpointindexlist; + giftface gface, *gfaceptr; + triface newtetra, checkface; + face checksh; + point3d gpoint, checkorg, checkdest; + int *pointweightlist; + int gfaceindex, gpointindex; + int success, facefinished; + int i, j; + + if (verbose > 1) { + printf(" Gift-wrapping begin with %d faces, %d points.\n", + numberofgiftfaces, numberofgiftpoints); + } + giftfacelist = new list(sizeof(giftface)); + pointweightlist = new int[numberofgiftpoints]; + // Init giftfacelist. At init all giftfaces are unfinished. + for (i = 0; i < numberofgiftfaces; i++) { + gfaceptr = (giftface*) giftfacelist->alloc(); + gfaceptr->casing = giftfaces[i]; + gfaceptr->iorg = gfaceptr->idest = gfaceptr->iapex = 0; + } + // Generate weights for every giftpoint. At the same time, store all + // vertexs's index in giftpoints array of each giftface. This will + // avoid to search index every time when a giftface is finfished. + for (i = 0; i < numberofgiftpoints; i++) { + pointweightlist[i] = 0; + for (j = 0; j < giftfacelist->len(); j++) { + gfaceptr = (giftface*) (*giftfacelist)[j]; + if (org(gfaceptr->casing) == giftpoints[i]) { + pointweightlist[i]++; + gfaceptr->iorg = i; + } else if (dest(gfaceptr->casing) == giftpoints[i]) { + pointweightlist[i]++; + gfaceptr->idest = i; + } else if (apex(gfaceptr->casing) == giftpoints[i]) { + pointweightlist[i]++; + gfaceptr->iapex = i; + } + } + assert(pointweightlist[i] > 0); + } + if (verbose > 3) { + dumpgifts("gifts.txt", giftfacelist, numberofgiftpoints, + giftpoints, pointweightlist); + } + + success = 1; + // For there might exist more than one points have the same smallest + // weight, I use a list to keep all these points. + smallweightlist = new list("int"); + // A list to keep all indexs of points which visible from the input + // giftface(*gface) in giftpoints array. + candpointindexlist = new list("int"); + // A list to keep the best point(s) for finishing a giftface. + bestpointindexlist = new list("int"); + // These three lists are created here to avoid create and delete every + // time when finishing each giftface. + + // Do giftwrapping from 'giftfacelist' and 'giftpoints'. + while (giftfacelist->len()) { + // Get a most suit giftface to start. + gfaceindex = getgiftface(giftfacelist, numberofgiftpoints, pointweightlist, + smallweightlist); + gface = *(giftface*)(*giftfacelist)[gfaceindex]; + if (verbose > 2) { + printf(" Choosing face (%d, %d, %d).\n", pointmark(org(gface.casing)), + pointmark(dest(gface.casing)), pointmark(apex(gface.casing))); + } + // Decrease corresponding point's weight. + pointweightlist[gface.iorg]--; + pointweightlist[gface.idest]--; + pointweightlist[gface.iapex]--; + // Remove this giftface from unfinished list. + giftfacelist->del(gfaceindex); + // Get a giftpoint that can be formed a constrained Delaunay tetrahedron + // with this giftface. + gpointindex = getgiftpoint(&gface.casing, giftfacelist, numberofgiftpoints, + giftpoints, pointweightlist, candpointindexlist, + bestpointindexlist); + if (gpointindex == -1) { + success = 0; + break; // Giftwrapping failed. + } + gpoint = giftpoints[gpointindex]; + if (verbose > 2) { + printf(" Choosing point %d.\n", pointmark(gpoint)); + } + // Form a new tetrahedron from gface.casing and gpoint. + maketetrahedron(&newtetra); + backtracelist->append(&newtetra); + setorg (newtetra, dest(gface.casing)); + setdest(newtetra, org(gface.casing)); + setapex(newtetra, apex(gface.casing)); + setoppo(newtetra, gpoint); + // Bond the new tetra with gift face. + bond(gface.casing, newtetra); + tspivot(gface.casing, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(newtetra, checksh); + } + if (tetattribs != NULL) { + // Set the element attributes of the new tetrahedron. + for (i = 0; i < eextras; i++) { + setelemattribute(newtetra.tet, i, tetattribs[i]); + } + } + if (varvolume) { + // Set the area constraint of a new tetrahedron. + setvolumebound(newtetra.tet, volumevar); + } + // Check three new faces of newtetra to see if they are finished by + // other gift faces. If find such gift face, its being finished, + // remove it from giftfaces(set face finished flag be 1). If not + // find such gift face, add this face to giftfaces. + adjustedgering(newtetra, CCW); + for (i = 0; i < 3; i++) { + fnext(newtetra, checkface); + checkorg = org(checkface); + checkdest = dest(checkface); + // Find in giftfacelist to see if any face be finished. If a face + // has both three points(checkorg, checkdest and gpoint). It must + // be finished. + facefinished = 0; + for (j = 0; j < giftfacelist->len(); j++) { + gfaceptr = (giftface*)(*giftfacelist)[j]; + if (isfacehaspoint(&(gfaceptr->casing), checkorg) && + isfacehaspoint(&(gfaceptr->casing), checkdest) && + isfacehaspoint(&(gfaceptr->casing), gpoint)) { + // Find such face, now it is finished. + if (verbose > 2) { + printf(" Finishing face (%d, %d, %d).\n", + pointmark(org(gfaceptr->casing)), + pointmark(dest(gfaceptr->casing)), + pointmark(apex(gfaceptr->casing))); + } + // Decrease corresponding point's weight. + pointweightlist[gfaceptr->iorg]--; + pointweightlist[gfaceptr->idest]--; + pointweightlist[gfaceptr->iapex]--; + // Bond these two tets. + bond(gfaceptr->casing, checkface); + tspivot(gfaceptr->casing, checksh); + if (checksh.sh != dummysh) { + sesymself(checksh); + tsbond(checkface, checksh); + } + // Remove this giftface from unfinished list. + giftfacelist->del(j); + facefinished = 1; + break; + } + } + if (!facefinished) { + // This is an unfinished face, add it to giftfacelist. + gfaceptr = (giftface*) giftfacelist->alloc(); + adjustedgering(checkface, CCW); + gfaceptr->casing = checkface; + if (verbose > 2) { + printf(" Adding an unfinished face (%d, %d, %d).\n", + pointmark(org(gfaceptr->casing)), + pointmark(dest(gfaceptr->casing)), + pointmark(apex(gfaceptr->casing))); + } + // Decide the index of point from gface. Note: here gface's enext() + // sequence is: (idest, iorg)->(iorg, iapex)->(iapex, idest) that + // corresponding to checkface's enext() sequence. But after do + // checkface.adjustedgering(0) it becomes inversed in each term: + // that is: (iorg, idest)->(iapex, iorg)->(idest, iapex). + if (i == 0) { + gfaceptr->iorg = gface.iorg; + gfaceptr->idest = gface.idest; + } else if (i == 1) { + gfaceptr->iorg = gface.iapex; + gfaceptr->idest = gface.iorg; + } else { // i == 2 + gfaceptr->iorg = gface.idest; + gfaceptr->idest = gface.iapex; + } + gfaceptr->iapex = gpointindex; + // Increase each vertex's weight. + pointweightlist[gfaceptr->iorg]++; + pointweightlist[gfaceptr->idest]++; + pointweightlist[gfaceptr->iapex]++; + } + enextself(newtetra); + } + if (verbose > 2) { + printf(" Create new: "); + dump(&newtetra); + } + } + + if (verbose > 1) { + printf(" Gift-wrapping end: %s.\n", success ? "Success" : "Failed"); + } + delete [] pointweightlist; + delete giftfacelist; + delete smallweightlist; + delete bestpointindexlist; + delete candpointindexlist; + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Giftwrapping Algorithm Implementation // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Randomized Incremental Flip Delaunay Tetrahedrization // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Incremental insertion algorithm is the simplest and the most commonly // +// used algorithm for constructing Delaunay triangulations. It extends in // +// a straightforward way to three (or more) dimensions. In three-dimension // +// there have several algorithms based on the incermental scheme, such as // +// the algorithms proposed by Boywer/Watson[1], Joe[2] and Edelsbrunner and // +// Shah[3], etc. // +// // +// Joe[2] provided an Incremental Flip algorithm to construct Delaunay // +// tetrahedralization with time complexity of O(n ^ 2) in the worst case, n // +// is the size of input point set. Edelsbrunner and Shah[3] extended Joe's // +// algorithm to a Randomized Incremental Flip algorithm with time complexity // +// of O(n ^ (d+1)/2) in the worst case and O(n log n + n ^ ceiling(d/2)) in // +// the expected case. Empirical results with an existing implementation // +// (Detri[4]) shows the Edelsbrunner and Shah[3]'s algorithm behaved well // +// for both randomly and regular distributed point sets. // +// // +// Tetgen uses the Randomized Incremental Flip algorithm of Edelsbrunner // +// and Shah[3] to construct Delaunay tetrahedralizations of 3D point sets. // +// The implementation is robust and fast. It used the fast randomized point // +// location algorithm of Mucke, Issac and Zhu's to perform point location. // +// and optionally used the adaptive exact geometric predicates package of // +// Shewchuk to perform exact orientation and insphere tests, Mucke's share- // +// ware Detri[4] also implemented the same algorithm, but lack of speed. My // +// implementation of this algorithm is faster than Detri[4]. // +// // +// Please see predicate.cpp, ptloc.cpp and flips.cpp for more infomation. // +// // +// Refernces: // +// // +// [1] Adrian Bowyer. Computing Dirichlet Tessellations. Computer Journal // +// 24(2):162-166, 1981. David F. Watson. Computing the three-dimens- // +// ional Delaunay Tessellation with Application to Voronoi Polytopes. // +// Computer Journal 24(2):167-172, 1981. // +// [2] Barry Joe. Construction of Three-Dimensional Triangulations using // +// Local Transformations. Computer Aided Geometric Design 8:123-142, // +// 1991. // +// [3] Herbert Edelsbrunner and Nimish Shah. Incremental topological // +// flipping works for regular triangulations. Algorithmica, 15:223-241, // +// 1996. // +// [4] Detri, The Code Constructs the 3D Delaunay Triangulation of a Given // +// Point Set Using a Variant of the Randomized Incremental-Flip // +// Algorithm, // +// http://www.geom.umn.edu/software/cglist/GeomDir/Detri_2.6.a.tar.gz // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// construct_initial_tetra() Create an initial triangulation by building // +// tetrahedron <a, b, c, o>; where the four // +// points are not coplanar, and no three points // +// are colinear. // +// // +// To construct the first tetrahedron, we should detect and avoid degenerate // +// cases. They are two duplicated points, three colinear points and four // +// coplanar points. At two duplicated points case, we just ignored one of // +// them; at colinear and coplanar points cases, we must skip current point // +// and get next point, keep testing until a good point be found, all skipped // +// points will consider later, store them in 'skippointlist'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::construct_initial_tetra(queue* skippointlist) +{ + triface newtet, ghosttet; + point3d pointloop; + point3d pa, pb, pc, pd; + int pointnumber; + int i, j; + + if (verbose > 1) { + printf(" Construct an initial tetrahedron.\n"); + } + // Set a 'ghosttet' handle the Outer space. + ghosttet.tet = dummytet; + pointnumber = points.items; + points.traversalinit(); + pa = pointtraverse(); + i = 0; + while (i < pointnumber) { + pointloop = pointtraverse(); + i++; + if(compare2points(&pa, &pointloop) == 0) { + if (!quiet) { + printf("Warning: A duplicate point at (%.12g, %.12g, %.12g)", + pointloop[0], pointloop[1], pointloop[2]); + printf(" appeared and was ignored.\n"); + } + // This will cause change from input points set. + // pointdealloc(pointloop); + continue; + } + break; + } + if (i >= pointnumber) { + printf("\nAll points are duplicate, no tetrahedralization be"); + printf(" constructed.\n"); + exit(1); + } + pb = pointloop; + while (i < pointnumber) { + pointloop = pointtraverse(); + i++; + if (iscoline(pa, pb, pointloop)) { + if (verbose > 2) { + printf(" A colinear point at (%.12g, %.12g, %.12g) %d", + pointloop[0], pointloop[1], pointloop[2]); + printf(" appeared and queued to process later.\n"); + } + skippointlist->push(&pointloop); + continue; + } + break; + } + if (i >= pointnumber) { + printf("\nAll points are colinear, no tetrahedralization be"); + printf(" constructed.\n"); + exit(1); + } + pc = pointloop; + while (i < pointnumber) { + pointloop = pointtraverse(); + i++; + if (iscoplane(pa, pb, pc, pointloop)) { + if (verbose > 2) { + printf(" A coplanar point at (%.12g, %.12g, %.12g)", + pointloop[0], pointloop[1], pointloop[2]); + printf(" appeared and queued to process later.\n"); + } + skippointlist->push(&pointloop); + continue; + } + break; + } + if (i >= pointnumber) { + printf("All points are coplanar, no tetrahedrization be constructed.\n"); + exit(1); + } + pd = pointloop; + + if (!isaboveplane(pa, pb, pc, pd)) { + pointloop = pa; pa = pb; pb = pointloop; + } + maketetrahedron(&newtet); + setorg(newtet, pa); + setdest(newtet, pb); + setapex(newtet, pc); + setoppo(newtet, pd); + bond(newtet, ghosttet); + if (verbose > 2) { + printf(" Creating tetra "); + dump(&newtet); + } + // At init, all faces of this tet are hull faces. + hullsize = 4; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// collect_visibles() Collects all CH facets visible from insertpoint and // +// stores them in fliplist. At the same time,construct // +// tetrahedra from insertpoint to all visible facets. // +// // +// The 'insertpoint' locate outside CH. And 'horiz' is a init hull facet ha- // +// ndle that visible from 'insertpoint'. ( We assume that an oracle provided // +// the initial facet which is on CH ) 'fliplist' is a queue for return all // +// visible facets from 'insertpoint', which will check doflip later. // +// // +// 'ghosttet' is a handle that hold the 'Outer Space' of current mesh. Use // +// this handle to bond new generated tetrahedron on current boundary, so // +// later point location routines will work correctlly. // +// // +// 'unfinfacelist' is a link to keep all unfinished faces for 'insertpoint'. // +// It is created in routine rand_incr_flip_delaunay() to avoid create it and // +// delete it every time when call this routine. At the beginning and end of // +// this routine, 'unfinfacelist' should be empty. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::collect_visibles(point3d insertpoint, triface *horiz, + triface *ghosttet, link *unfinfacelist) +{ + triface newtet, otherface, horizface; + point3d torg, tdest, tapex; + int findex; + + if (verbose > 1) { + printf(" Collect visible facets for inserting point: \n"); + } + + enqueuefliplist(*horiz); + adjustedgering(*horiz, CCW); + org (*horiz, torg); + dest(*horiz, tdest); + apex(*horiz, tapex); + // Mount the initial tetrahedron to mesh. That is a tetrahedron consists + // of facet horiz and insertpoint. + maketetrahedron(&newtet); // default newtet.ver = 0. + setorg (newtet, tdest); + setdest(newtet, torg); + setapex(newtet, tapex); + setoppo(newtet, insertpoint); + // Make the connection of two tets. + bond(newtet, *horiz); + // Add the new tetrahedron's three faces to unfinished faces list. + // Keep 'insertpoint' be apex of each faces. + fnext(newtet, otherface); + unfinfacelist->add(&otherface); + enextfnext(newtet, otherface); + unfinfacelist->add(&otherface); + enext2fnext(newtet, otherface); + bond(otherface, *ghosttet); + unfinfacelist->add(&otherface); + if (verbose > 2) { + printf(" Creating newtet "); + dump(&newtet); + } + // Hull face number decreased caused by face bond() operation. + hullsize --; + + // Loop untill unfinfacelist is empty. + while (unfinfacelist->len() > 0) { + horizface = * (triface *) unfinfacelist->getnitem(1); + unfinfacelist->del(1); // Remove it. + otherface = horizface; + adjustedgering(otherface, CCW); + // Spin otherface around the edge of otherface. Stop when encounter + // Outer space. + while (fnextself(otherface)) ; + adjustedgering(horizface, CW); + apex(otherface, tapex); + if (isaboveplane(&horizface, tapex)) { + // otherface is visible form insertpoint. + enqueuefliplist(otherface); + org (otherface, torg); + dest(otherface, tdest); + // Mount a tetrahedron to mesh. This tetrahedron is consists of + // otherface's vertexs and insertpoint. + maketetrahedron(&newtet); // default newtet.ver = 0. + setorg (newtet, torg); + setdest(newtet, tdest); + setapex(newtet, tapex); + setoppo(newtet, insertpoint); + // Make the connection of three tets. Note: The other two faces of + // new tet default bond to 'dummytet', here need check if they are + // finished. + bond(newtet, otherface); + // Hull face number decrease caused by bond(). + hullsize --; + fnext(newtet, otherface); + bond(otherface, horizface); + // Check other two face if they already exist in list Unfinshed.If so + // They are finished now and can be removed from list, then bond. + enextfnext(newtet, otherface); + findex = unfinfacelist->hasitem(&otherface); + if ((findex > 0) && (findex <= unfinfacelist->len())) { + horizface = * (triface *) unfinfacelist->getnitem(findex); + unfinfacelist->del(findex); // Remove it. + bond(otherface, horizface); + } else { + bond(otherface, *ghosttet); + unfinfacelist->add(&otherface); + } + enext2fnext(newtet, otherface); + findex = unfinfacelist->hasitem(&otherface); + if ((findex > 0) && (findex <= unfinfacelist->len())) { + horizface = * (triface *) unfinfacelist->getnitem(findex); + unfinfacelist->del(findex); // Remove it. + bond(otherface, horizface); + } else { + bond(otherface, *ghosttet); + unfinfacelist->add(&otherface); + } + if (verbose > 2) { + printf(" Createing newtet "); + dump(&newtet); + } + } else { + // This is a hull face. + hullsize ++; + } + } // End of while. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dofliplist() Flips non-Delaunay triangular facets in given 'fliplist' // +// until all triangular facets are locally Delaunay. // +// // +// ASSUMPTION: Current tetrahedrization is non-Delaunay after inserting // +// point v, AND: all possibly non-Delaunay link-facets after are on 'flip- // +// list. Upon success (which should always happen, by now) dofliplist() // +// returns the number of necessary flips. As a side effect,'fliplist' will // +// be cleared, and current tetrahedrization updated accordingly. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int mesh3d::dofliplist() +{ + triface flipface; + enum facecategory fc; + int flipcount; + + flipcount = flip_t23s + flip_t32s + flip_t22s + flip_t44s; + // Start flip. + if (verbose > 1) { + printf(" Start do flip list: %d faces in list.", fliplist->len()); + if (verbose > 1) printf("\n"); + } + while (dequeuefliplist(flipface)) { + fc = categorizeface(flipface); + // Determine the preferable configuration and swap if necessary. + switch (fc) { + // These cases are handled by edge swapping. + case N44: + case N32: + break; + // These cases are definitely unswappable + case N40: + case N30: + case N20: + case LOCKED: + break; + case T44: + if (querydoswap(flipface)) { + flip44(flipface); + } + break; + case T22: + if (querydoswap(flipface)) { + flip22(flipface); + } + break; + case T23: + if (querydoswap(flipface)) { + flip23(flipface); + } + break; + case T32: + if (querydoswap(flipface)) { + flip32(flipface); + } + break; + // Catch-all for bad cases + default: + break; + } + } + flipcount = flip_t23s + flip_t32s + flip_t22s + flip_t44s - flipcount; + if (verbose > 1) { + printf(" Total %d flips.\n", flipcount); + } + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// rand_incr_flip_delaunay() Construction delaunay tetrahedrization for a // +// given three-dimensional point set use Random- // +// ize Incremental Flip algorithm. // +// // +// The basic idea of the incremental flip algorithm is the folloeing. S be a // +// set of points in IR{3}, Let 4 <= i <= n and assume that the Delaunay tri- // +// angulation of the first i-1 points in S is already constructed; call it // +// D(i-1). Add the i-th point pi (belong to S) to the triangulation,and res- // +// tore Delaunayhood by flipping; this result in D(i). Repeat this procedure // +// until i = n. If implemented correctly, this strategy always leads to the // +// Ddelaunay triangulation of S. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long mesh3d::rand_incr_flip_delaunay() +{ + queue *skippointlist; + link *unfinfacelist; + triface starttet, ghosttet; + point3d pointloop; + enum insertsiteresult insres; + long max_flips, flipcount; + int max_flips_overflow; + + // max_flips is the upper bound on total number of flips. + // see Herbert's Triangulation notes, p95. + max_flips = points.items * (points.items + 3) + 1; + if (max_flips <= 0) { + // The long number Overflow! + max_flips_overflow = 1; + } else { + max_flips_overflow = 0; + } + + usefliplist = 1; + if (fliplist) { + fliplist->clear(); + } else { + fliplist = new queue(sizeof(badface3d)); + } + + // Set a queue for keeping all skipped points in routine + // construct_initial_tetra(); + skippointlist = new queue("point"); + + // Set a 'ghosttet' handle the Outer space. + // This variable only used in routine collect_visible(). + ghosttet.tet = dummytet; + // Setup a list for keeping all unfinished faces. + // This list only used in routine collect_visible(). It is created here + // to avoid create it and delete it every time when call this routine. + unfinfacelist = new link(sizeof(triface)); + // Set user-defined compare function for comparing faces. + unfinfacelist->setcomp((compfunc) &issameface); + + flip_t23s = flip_t32s = flip_t22s = flip_t44s = 0; + cospherecount = 0; + + // Build a initial tetrahedron. + construct_initial_tetra(skippointlist); + + // Delaunay tetrahedrization construction. + // Continue get points after above routine, need not do traverseinit(). + pointloop = pointtraverse(); + while (pointloop != (point3d) NULL) { + starttet.tet = (tetrahedron *) NULL; + insres = insertsite(pointloop, &starttet, NULL, NULL); + if (insres == FAILED) { + // Point is outside current mesh. + collect_visibles(pointloop, &starttet, &ghosttet, unfinfacelist); + assert(unfinfacelist->len() == 0); + } else if (insres == DUPLICATE) { + if (!quiet) { + printf("Warning: A duplicate point at (%.12g, %.12g, %.12g)", + pointloop[0], pointloop[1], pointloop[2]); + printf(" appeared and was ignored.\n"); + } + } + if (!fliplist->empty()) { + flipcount = dofliplist(); + if (!max_flips_overflow) { + max_flips -= flipcount; + } + } + pointloop = pointtraverse(); + } + if (!max_flips_overflow && (max_flips <= 0)) { + printf("Error: Randomize Incremental Flip algorithm crashed!\n"); + internalerror(); + } + + if (skippointlist->len() > 0) { + while (skippointlist->get(&pointloop)) { + starttet.tet = (tetrahedron *) NULL; + insres = insertsite(pointloop, &starttet, NULL, NULL); + if (insres == FAILED) { + // Point is outside current mesh. + collect_visibles(pointloop, &starttet, &ghosttet, unfinfacelist); + assert(unfinfacelist->len() == 0); + } else if (insres == DUPLICATE) { + if (!quiet) { + printf("Warning: A duplicate point at (%.12g, %.12g, %.12g)", + pointloop[0], pointloop[1], pointloop[2]); + printf(" appeared and was ignored.\n"); + } + } + if (!fliplist->empty()) { + flipcount = dofliplist(); + if (!max_flips_overflow) { + max_flips -= flipcount; + } + } + } + if (!max_flips_overflow && (max_flips <= 0)) { + printf("Error: Randomize Incremental Flip algorithm crashed!\n"); + internalerror(); + } + } + + if (verbose) { + printf(" Total flips: %d Where T23: %d T32: %d T22: %d T44: %d\n", + flip_t23s + flip_t32s + flip_t22s + flip_t44s, + flip_t23s, flip_t32s, flip_t22s, flip_t44s); + printf(" Cosphere count: %d\n", cospherecount); + } + assert(fliplist->empty()); + usefliplist = 0; + delete skippointlist; + delete unfinfacelist; + return hullsize; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// delaunay() Form a Delaunay triangulation. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long mesh3d::delaunay() +{ + eextras = 0; + initializetetshpools(); + + if (!quiet) { + if (noexact) { + printf("Using approximate floating-point arthmetic "); + } else { + printf("Using adaptive exact floating-point arthmetic "); + } + if (noroundoff) { + printf("without tolerance.\n"); + } else { + printf("with tolerance %g.\n", usertolerance); + } + printf("Constructing Delaunay triangulation "); + printf("by Randomized Incremental-Flip algorithm.\n"); + } + + return rand_incr_flip_delaunay(); +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// Randomized Incremental Flip Delaunay Tetrahedrization // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// File I/O Routines // +// BEGIN // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// readline() Read a nonempty line from a file. // +// // +// A line is considered "nonempty" if it contains something that looks like // +// a number. // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* mesh3d::readline(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) { + printf("File I/O Error: Unexpected end of file in %s.\n", infilename); + exit(1); + } + // 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// findfield() Find the next field of a string. // +// // +// Jumps past the current field by searching for whitespace, then jumps past // +// the whitespace to find the next field. // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* mesh3d::findfield(char *string) +{ + char *result; + + result = string; + // Skip the current field. Stop upon reaching whitespace. + while ((*result != '\0') && (*result != '#') + && (*result != ' ') && (*result != '\t')) { + 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; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// readnodes() Read the points from a file, which may be a .node or .poly // +// file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::readnodes(FILE **polyfile) +{ + FILE *infile; + point3d pointloop; + char inputline[INPUTLINESIZE]; + char *stringptr; + char *infilename; + REAL x, y, z; + int firstnode; + int nodemarkers; + int currentmarker; + int i, j; + + if (poly || smesh) { + // Read the points from a .poly or .smesh file. + if (poly) { + if (!quiet) { + printf("Opening %s.\n", inpolyfilename); + } + *polyfile = fopen(inpolyfilename, "r"); + if (*polyfile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s.\n", inpolyfilename); + exit(1); + } + stringptr = readline(inputline, *polyfile, inpolyfilename); + } else { + if (!quiet) { + printf("Opening %s.\n", insmeshfilename); + } + *polyfile = fopen(insmeshfilename, "r"); + if (*polyfile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s.\n", insmeshfilename); + exit(1); + } + stringptr = readline(inputline, *polyfile, insmeshfilename); + } + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + inpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + mesh_dim = 3; + } else { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nextras = 0; + } else { + nextras = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nodemarkers = 0; + } else { + nodemarkers = (int) strtol (stringptr, &stringptr, 0); + } + if (inpoints > 0) { + readnodefile = 0; + infilename = inpolyfilename; + infile = *polyfile; + } else { + // If the .poly file claims there are zero points, that means that + // the points should be read from a separate .node file. + readnodefile = 1; + infilename = innodefilename; + } + } else { + readnodefile = 1; + infilename = innodefilename; + *polyfile = (FILE *) NULL; + } + + if (readnodefile) { + // Read the points from a .node file. + if (!quiet) { + printf("Opening %s.\n", innodefilename); + } + infile = fopen(innodefilename, "r"); + if (infile == (FILE *) NULL) { + printf("File I/O Error: Cannot access file %s.\n", innodefilename); + exit(1); + } + // Read number of points, number of dimensions, number of point + // attributes, and number of boundary markers. + stringptr = readline(inputline, infile, innodefilename); + inpoints = (int) strtol (stringptr, &stringptr, 0); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + mesh_dim = 3; + } else { + mesh_dim = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nextras = 0; + } else { + nextras = (int) strtol (stringptr, &stringptr, 0); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + nodemarkers = 0; + } else { + nodemarkers = (int) strtol (stringptr, &stringptr, 0); + } + } + + if (inpoints < 4) { + printf("Error: Input must have at least four input points.\n"); + exit(1); + } + if (mesh_dim != 3) { + printf("Error: This program only works with three-dimensional meshes.\n"); + exit(1); + } + + initializepointpool(); + + // Read the points. + for (i = 0; i < inpoints; i++) { + pointloop = (point3d) points.alloc(); + stringptr = readline(inputline, infile, infilename); + if (i == 0) { + firstnode = (int) strtol (stringptr, &stringptr, 0); + if ((firstnode == 0) || (firstnode == 1)) { + firstnumber = firstnode; + } + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Point %d has no x coordinate.\n", + firstnumber + i); + exit(1); + } + x = (REAL) strtod(stringptr, &stringptr); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Point %d has no y coordinate.\n", + firstnumber + i); + exit(1); + } + y = (REAL) strtod(stringptr, &stringptr); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Point %d has no z coordinate.\n", + firstnumber + i); + exit(1); + } + z = (REAL) strtod(stringptr, &stringptr); + pointloop[0] = x; + pointloop[1] = y; + pointloop[2] = z; + // Read the point attributes. + for (j = 3; j < 3 + nextras; j++) { + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + pointloop[j] = 0.0; + } else { + pointloop[j] = (REAL) strtod(stringptr, &stringptr); + } + } + if (nodemarkers) { + // Read a point marker. + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + setpointmark(pointloop, 0); + } else { + currentmarker = (int) strtol (stringptr, &stringptr, 0); + setpointmark(pointloop, currentmarker); + } + } else { + // If no markers are specified in the file, they default to zero. + setpointmark(pointloop, 0); + } + // Determine the smallest and largest x and y 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 < ymin) ? z : zmin; + zmax = (z > zmax) ? z : zmax; + } + } + if (readnodefile) { + fclose(infile); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// readholes() Read the holes, and possibly regional attributes and volume // +// constraints, from a .poly file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::readholes(FILE *polyfile, REAL **hlist, int *holes, REAL **rlist, + int *regions) +{ + REAL *holelist; + REAL *regionlist; + char inputline[INPUTLINESIZE]; + char *stringptr; + int index; + int i; + + // Read the holes. + if (poly || smesh) { + stringptr = readline(inputline, polyfile, inpolyfilename); + *holes = (int) strtol (stringptr, &stringptr, 0); + } else { + // There need not hole section in smesh file format. + *holes = 0; + } + if (*holes > 0) { + holelist = (REAL *) new REAL[3 * *holes]; + *hlist = holelist; + for (i = 0; i < 3 * *holes; i += 3) { + stringptr = readline(inputline, polyfile, inpolyfilename); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Hole %d has no x coordinate.\n", + firstnumber + (i / 3)); + exit(1); + } else { + holelist[i] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Hole %d has no y coordinate.\n", + firstnumber + (i / 3)); + exit(1); + } else { + holelist[i + 1] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Hole %d has no z coordinate.\n", + firstnumber + (i / 3)); + exit(1); + } else { + holelist[i + 2] = (REAL) strtod(stringptr, &stringptr); + } + } + } else { + *hlist = (REAL *) NULL; + } + + if ((regionattrib || varvolume) && !refine) { + // Read the volume constraints. + stringptr = readline(inputline, polyfile, inpolyfilename); + *regions = (int) strtol (stringptr, &stringptr, 0); + if (*regions > 0) { + regionlist = (REAL *) new REAL[5 * *regions]; + *rlist = regionlist; + index = 0; + for (i = 0; i < *regions; i++) { + stringptr = readline(inputline, polyfile, inpolyfilename); + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Region %d has no x coordinate.\n", + firstnumber + i); + exit(1); + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Region %d has no y coordinate.\n", + firstnumber + i); + exit(1); + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Region %d has no z coordinate.\n", + firstnumber + i); + exit(1); + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + printf("File I/O Error: Region %d has no region attribute or", + firstnumber + i); + printf(" volume constraint.\n"); + exit(1); + } else { + regionlist[index++] = (REAL) strtod(stringptr, &stringptr); + } + stringptr = findfield(stringptr); + if (*stringptr == '\0') { + regionlist[index] = regionlist[index - 1]; + } else { + regionlist[index] = (REAL) strtod(stringptr, &stringptr); + } + index++; + } + } + } else { + // Set `*regions' to zero to avoid an accidental free() later. + *regions = 0; + *rlist = (REAL *) NULL; + } + + fclose(polyfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outnodes() Number the points and write them to a .node file. // +// // +// To save memory, the point numbers are written over the shell markers // +// after the points are written to a file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::outnodes() +{ + FILE *outfile; + point3d pointloop; + int pointnumber; + int i; + + if (!quiet) { + printf("Writing %s.\n", outnodefilename); + } + outfile = fopen(outnodefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outnodefilename); + exit(1); + } + // Number of points, number of dimensions, number of point attributes, + // and number of boundary markers (zero or one). + fprintf(outfile, "%ld %d %d %d\n", points.items, mesh_dim, nextras, + 1 - nobound); + + points.traversalinit(); + pointloop = pointtraverse(); + pointnumber = firstnumber; + while (pointloop != (point3d) 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[i + 3]); + } + if (nobound) { + fprintf(outfile, "\n"); + } else { + // Write the boundary marker. + fprintf(outfile, " %d\n", pointmark(pointloop)); + } + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } + + fprintf(outfile, "# Generated by %s\n", commandline); + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// numbernodes() Number the points. // +// // +// Each point is assigned a marker equal to its number. // +// // +// Used when outnodes() is not called because no .node file is written. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::numbernodes(int myfirstnumber) +{ + point3d pointloop; + int pointnumber; + + points.traversalinit(); + pointloop = pointtraverse(); + if (!myfirstnumber) { + pointnumber = firstnumber; + } else { + pointnumber = myfirstnumber; + } + while (pointloop != (point3d) NULL) { + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outelems() Write the tetrahedra to an .ele file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::outelems() +{ + FILE *outfile; + tetrahedron* tptr; + point3d p1, p2, p3, p4; + int elementnumber; + int i; + + if (!quiet) { + printf("Writing %s.\n", outelefilename); + } + outfile = fopen(outelefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outelefilename); + exit(1); + } + // Number of tetras, points per tetra, attributes per tetra. + fprintf(outfile, "%ld %d %d\n", tetrahedrons.items, 4, eextras); + + tetrahedrons.traversalinit(); + tptr = tetrahedrontraverse(); + elementnumber = firstnumber; + while (tptr != (tetrahedron *) NULL) { + p1 = (point3d) tptr[4]; + p2 = (point3d) tptr[5]; + p3 = (point3d) tptr[6]; + p4 = (point3d) tptr[7]; + // Triangle number, indices for three points. + fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, + pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); + for (i = 0; i < eextras; i++) { + fprintf(outfile, " %.17g", elemattribute(tptr, i)); + } + fprintf(outfile, "\n"); + tptr = tetrahedrontraverse(); + elementnumber++; + } + + fprintf(outfile, "# Generated by %s\n", commandline); + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outelems2gid() Generate mesh files for viewing by Gid. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::outelems2gid() +{ + FILE *outfile; + char gidfilename[FILENAMESIZE]; + tetrahedron* tetptr; + point3d pointloop, p1, p2, p3, p4;; + int pointnumber, elementnumber; + int i; + + strcpy(gidfilename, outelefilename); + strcat(gidfilename, ".gid"); + + if (!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(); + pointloop = pointtraverse(); + pointnumber = 1; // Gid mesh reader must need first number be '1'. + while (pointloop != (point3d) 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[i + 3]); + } + if (nobound) { + fprintf(outfile, "\n"); + } else { + // Write the boundary marker. + fprintf(outfile, " %d\n", pointmark(pointloop)); + } + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } + + fprintf(outfile, "end coordinates\n"); + fprintf(outfile, "elements\n"); + + tetrahedrons.traversalinit(); + tetptr = tetrahedrontraverse(); + elementnumber = 1; + while (tetptr != (tetrahedron *) NULL) { + p1 = (point3d) tetptr[4]; + p2 = (point3d) tetptr[5]; + p3 = (point3d) tetptr[6]; + p4 = (point3d) tetptr[7]; + // tetrahedron number, indices for four points. + fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, + pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); + + for (i = 0; i < eextras; i++) { + fprintf(outfile, " %.17g", elemattribute(tetptr, i)); + } + fprintf(outfile, "\n"); + + tetptr = tetrahedrontraverse(); + elementnumber++; + } + + fprintf(outfile, "end elements\n"); + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outelems2fa() Generate mesh files for viewing by FA. // +// // +// There need three files: .prj, .cor and .elm for FA. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::outelems2fa() +{ + FILE *outfile; + char fafilename[FILENAMESIZE]; + tetrahedron* tetptr; + point3d pointloop, p1, p2, p3, p4;; + int pointnumber, elementnumber; + int i; + + strcpy(fafilename, outelefilename); + strcat(fafilename, ".prj"); + + if (!quiet) { + printf("Writing %s.\n", fafilename); + } + outfile = fopen(fafilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", fafilename); + return; + } + fprintf(outfile, "%s\n", outelefilename); + fprintf(outfile, "0\n0\n0\n"); + fclose(outfile); + + strcpy(fafilename, outelefilename); + strcat(fafilename, ".cor"); + + if (!quiet) { + printf("Writing %s.\n", fafilename); + } + outfile = fopen(fafilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", fafilename); + return; + } + fprintf(outfile, "%d 3\n", points.items); + + points.traversalinit(); + pointloop = pointtraverse(); + pointnumber = 1; // FA mesh reader must need first number be '1'. + while (pointloop != (point3d) NULL) { + // Point number, x , y and z coordinates. + fprintf(outfile, "%.17g %.17g %.17g\n", pointloop[0], + pointloop[1], pointloop[2]); + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } + fclose(outfile); + + strcpy(fafilename, outelefilename); + strcat(fafilename, ".elm"); + + if (!quiet) { + printf("Writing %s.\n", fafilename); + } + outfile = fopen(fafilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", fafilename); + return; + } + fprintf(outfile, "%d 5\n", tetrahedrons.items); + + tetrahedrons.traversalinit(); + tetptr = tetrahedrontraverse(); + elementnumber = 1; + while (tetptr != (tetrahedron *) NULL) { + p1 = (point3d) tetptr[4]; + p2 = (point3d) tetptr[5]; + p3 = (point3d) tetptr[6]; + p4 = (point3d) tetptr[7]; + // tetrahedron number, indices for four points. + fprintf(outfile, "%5d %5d %5d %5d", + pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); + if (eextras > 0) { + fprintf(outfile, " %.17g", elemattribute(tetptr, 0) + 1); + } else { + fprintf(outfile, " 1"); + } + fprintf(outfile, "\n"); + + tetptr = tetrahedrontraverse(); + elementnumber++; + } + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outfaces() Write the faces to a .face file. // +// // +// Also use this routine to output hull faces(when hull = 1). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::outfaces(int hull) +{ + FILE *outfile; + triface tface, tsymface; + face checkmark; + point3d torg, tdest, tapex; + int facenumber; + + if (!quiet) { + printf("Writing %s.\n", facefilename); + } + outfile = fopen(facefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", facefilename); + exit(1); + } + // Number of faces, number of boundary markers (zero or one). + if (hull) { + fprintf(outfile, "%ld %d\n", hullsize, 1 - nobound); + } else { + fprintf(outfile, "%ld %d\n", faces, 1 - nobound); + } + + tetrahedrons.traversalinit(); + tface.tet = tetrahedrontraverse(); + facenumber = firstnumber; + // To loop over the set of faces, loop over all tetrahedrons, 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 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. + // If the 'hull' flag is set, only operate on faces which neighbour is + // 'dummytet'. This will only output hull faces. + 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)) { + org (tface, torg); + dest(tface, tdest); + apex(tface, tapex); + if (hull) { + if (tsymface.tet == dummytet) { + // Only output hull faces. + fprintf(outfile, "%5d %4d %4d %4d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex)); + facenumber++; + } + } else { + if (nobound) { + // Face number, indices of three vertexs. + fprintf(outfile, "%5d %4d %4d %4d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex)); + } else { + // Face number, indices of three vertexs, and a boundary marker. + // If there's no shell face, the boundary marker is zero. + if (useshelles) { + tspivot(tface, checkmark); + if ((checkmark.sh == dummysh) || isnonsolid(checkmark)) { + fprintf(outfile, "%5d %4d %4d %4d %4d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex), 0); + } else { + fprintf(outfile, "%5d %4d %4d %4d %4d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex), + mark(checkmark)); + } + } else { + fprintf(outfile, "%5d %4d %4d %4d %4d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex), + tsymface.tet == dummytet); + } + } + facenumber++; + } + } + } + tface.tet = tetrahedrontraverse(); + } + + fprintf(outfile, "# Generated by %s\n", commandline); + fclose(outfile); +} + +void mesh3d::outfaces2gid(int hull) +{ + FILE *outfile; + char gidfilename[FILENAMESIZE]; + triface tface, tsymface; + face checkmark; + point3d pointloop, torg, tdest, tapex; + int pointnumber, facenumber, i; + + strcpy(gidfilename, facefilename); + strcat(gidfilename, ".gid"); + + if (!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(); + pointloop = pointtraverse(); + pointnumber = 1; // Gid mesh reader must need first number be '1'. + while (pointloop != (point3d) 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[i + 3]); + } + if (nobound) { + fprintf(outfile, "\n"); + } else { + // Write the boundary marker. + fprintf(outfile, " %d\n", pointmark(pointloop)); + } + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } + + fprintf(outfile, "end coordinates\n"); + fprintf(outfile, "elements\n"); + + tetrahedrons.traversalinit(); + tface.tet = tetrahedrontraverse(); + facenumber = 1; + // 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. + // If the 'hull' flag is set, only operate on faces which neighbour is + // 'dummytet'. This will only output hull faces. + 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)) { + org (tface, torg); + dest(tface, tdest); + apex(tface, tapex); + if (hull) { + if (tsymface.tet == dummytet) { + // Only output hull faces. + fprintf(outfile, "%5d %d %d %d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex)); + facenumber++; + } + } else { + if (nobound) { + // Face number, indices of three vertexs. + fprintf(outfile, "%5d %d %d %d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex)); + } else { + // Face number, indices of three vertexs, and a boundary marker. + // If there's no shell face, the boundary marker is zero. + if (useshelles) { + tspivot(tface, checkmark); + if (checkmark.sh == dummysh) { + fprintf(outfile, "%5d %d %d %d %d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex), 0); + } else { + fprintf(outfile, "%5d %d %d %d %d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex), + mark(checkmark)); + } + } else { + fprintf(outfile, "%5d %d %d %d %d\n", facenumber, + pointmark(torg), pointmark(tdest), pointmark(tapex), + tsymface.tet == dummytet); + } + } + facenumber++; + } + } + } + tface.tet = tetrahedrontraverse(); + } + + fprintf(outfile, "end elements\n"); + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outedges() Write the edges (segments) to a .edge file. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::outedges() +{ + FILE *outfile; + face segloop; + point3d torg, tdest; + int edgenumber; + + if (!quiet) { + printf("Writing %s.\n", edgefilename); + } + outfile = fopen(edgefilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", edgefilename); + exit(1); + } + // Number of edges. + fprintf(outfile, "%ld\n", subsegs.items); + + subsegs.traversalinit(); + segloop.sh = shellfacetraverse(&subsegs); + edgenumber = firstnumber; + while (segloop.sh != (shellface*) NULL) { + torg = sorg(segloop); + tdest = sdest(segloop); + fprintf(outfile, "%5d %4d %4d\n", edgenumber, + pointmark(torg), pointmark(tdest)); + edgenumber++; + segloop.sh = shellfacetraverse(&subsegs); + } + + fprintf(outfile, "# Generated by %s\n", commandline); + fclose(outfile); +} + +void mesh3d::outedges2gid() +{ + FILE *outfile; + char gidfilename[FILENAMESIZE]; + face segloop; + point3d pointloop, torg, tdest; + int pointnumber, edgenumber, i; + + strcpy(gidfilename, edgefilename); + strcat(gidfilename, ".gid"); + + if (!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 Linear Nnode 2\n"); + fprintf(outfile, "Coordinates\n"); + + points.traversalinit(); + pointloop = pointtraverse(); + pointnumber = 1; // Gid mesh reader must need first number be '1'. + while (pointloop != (point3d) 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[i + 3]); + } + if (nobound) { + fprintf(outfile, "\n"); + } else { + // Write the boundary marker. + fprintf(outfile, " %d\n", pointmark(pointloop)); + } + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } + + fprintf(outfile, "end coordinates\n"); + fprintf(outfile, "Elements\n"); + + subsegs.traversalinit(); + segloop.sh = shellfacetraverse(&subsegs); + edgenumber = firstnumber; + while (segloop.sh != (shellface*) NULL) { + torg = sorg(segloop); + tdest = sdest(segloop); + fprintf(outfile, "%5d %4d %4d\n", edgenumber, + pointmark(torg), pointmark(tdest)); + edgenumber++; + segloop.sh = shellfacetraverse(&subsegs); + } + + fprintf(outfile, "end elements\n"); + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outneighbors() Write neighbor elems to file *.neigh // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::outneighbors() +{ + FILE *outfile; + tetrahedron *tptr; + triface tetloop, tetsym; + int elementnumber; + int neighbor1, neighbor2, neighbor3, neighbor4; + + if (!quiet) { + printf("Writing %s.\n", neighborfilename); + } + outfile = fopen(neighborfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", neighborfilename); + exit(1); + } + // Number of tetrahedra, four faces per tetrahedron. + fprintf(outfile, "%ld %d\n", tetrahedrons.items, 4); + + tetrahedrons.traversalinit(); + tptr = tetrahedrontraverse(); + elementnumber = firstnumber; + while (tptr != (tetrahedron *) NULL) { + * (int *) (tptr + 8) = elementnumber; + tptr = tetrahedrontraverse(); + elementnumber++; + } + * (int *) (dummytet + 8) = -1; + + tetrahedrons.traversalinit(); + tetloop.tet = tetrahedrontraverse(); + elementnumber = firstnumber; + while (tetloop.tet != (tetrahedron *) NULL) { + tetloop.loc = 0; + sym(tetloop, tetsym); + neighbor1 = * (int *) (tetsym.tet + 8); + tetloop.loc = 1; + sym(tetloop, tetsym); + neighbor2 = * (int *) (tetsym.tet + 8); + tetloop.loc = 2; + sym(tetloop, tetsym); + neighbor3 = * (int *) (tetsym.tet + 8); + tetloop.loc = 3; + sym(tetloop, tetsym); + neighbor4 = * (int *) (tetsym.tet + 8); + // Tetrahedra number, neighboring tetrahedron numbers. + fprintf(outfile, "%4d %4d %4d %4d %4d\n", elementnumber, + neighbor1, neighbor2, neighbor3, neighbor4); + tptr = tetrahedrontraverse(); + elementnumber++; + } + + fprintf(outfile, "# Generated by %s\n", commandline); + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// outoff() Write the triangulation to an .off file. // +// // +// OFF stands for the Object File Format, a format used by the Geometry // +// Center's Geomview package. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::outoff() +{ + FILE *outfile; + triface tface, tsymface; + point3d pointloop; + point3d torg, tdest, tapex; + + if (!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); + exit(1); + } + // 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(); + pointloop = pointtraverse(); + while (pointloop != (point3d) NULL) { + fprintf(outfile, " %.17g %.17g %.17g\n", pointloop[0], + pointloop[1], pointloop[2]); + pointloop = pointtraverse(); + } + + 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. + // If the 'hull' flag is set, only operate on faces which neighbour is + // 'dummytet'. This will only output hull faces. + 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)) { + org (tface, torg); + dest(tface, tdest); + apex(tface, tapex); + // Face number, indices of three vertexs. + fprintf(outfile, "3 %4d %4d %4d\n", pointmark(torg) - 1, + pointmark(tdest) - 1, pointmark(tapex) - 1); + } + } + tface.tet = tetrahedrontraverse(); + } + + fprintf(outfile, "# Generated by %s\n", commandline); + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dumpallbadelems() Output all bad elements. // +// // +// Bad elements include Sliver, Cap, Wedge, Spindle, Needle. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::dumpallbadelems(char* badelemfilename) +{ + FILE *outfile; + tetrahedron* tetptr; + point3d pointloop, p1, p2, p3, p4; + REAL alldihed[6], allsolid[4]; + REAL smallsolid, largesolid; + REAL smalldihed, largedihed; + REAL smallestdiangle, biggestdiangle; + int pointnumber, elementnumber; + int smalldihedcount, largedihedcount; + int smallsolidcount, largesolidcount; + int elemtype; + int i; + + if (!quiet) { + printf("Writing %s.\n", badelemfilename); + } + outfile = fopen(badelemfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", badelemfilename); + return; + } + smallsolid = 3.; + largesolid = 300.; + smalldihed = 20.; + largedihed = 160.; + + fprintf(outfile, "mesh dimension = 3 elemtype tetrahedron nnode = 4\n"); + fprintf(outfile, "coordinates\n"); + + points.traversalinit(); + pointloop = pointtraverse(); + pointnumber = 1; // Gid mesh reader must need first number be '1'. + while (pointloop != (point3d) 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[i + 3]); + } + if (nobound) { + fprintf(outfile, "\n"); + } else { + // Write the boundary marker. + fprintf(outfile, " %d\n", pointmark(pointloop)); + } + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } + + fprintf(outfile, "end coordinates\n"); + fprintf(outfile, "elements\n"); + + tetrahedrons.traversalinit(); + tetptr = tetrahedrontraverse(); + elementnumber = 1; + while (tetptr != (tetrahedron *) NULL) { + p1 = (point3d) tetptr[4]; + p2 = (point3d) tetptr[5]; + p3 = (point3d) tetptr[6]; + p4 = (point3d) tetptr[7]; + + tetalldihedral(p1, p2, p3, p4, alldihed); + smalldihedcount = largedihedcount = 0; + smallestdiangle = 180; + biggestdiangle = 0; + for (i = 0; i < 6; i++) { + if (alldihed[i] < smallestdiangle) { + smallestdiangle = alldihed[i]; + } else if (alldihed[i] > biggestdiangle) { + biggestdiangle = alldihed[i]; + } + if (alldihed[i] < smalldihed) { + smalldihedcount++; + } else if (alldihed[i] > largedihed) { + largedihedcount++; + } + } + allsolid[0] = alldihed[0] + alldihed[1] + alldihed[2] - 180; + allsolid[1] = alldihed[0] + alldihed[3] + alldihed[4] - 180; + allsolid[2] = alldihed[1] + alldihed[3] + alldihed[5] - 180; + allsolid[3] = alldihed[2] + alldihed[4] + alldihed[5] - 180; + smallsolidcount = largesolidcount = 0; + for (i = 0; i < 4; i++) { + if (allsolid[i] < smallsolid) { + smallsolidcount++; + } else if (allsolid[i] > largesolid) { + largesolidcount++; + } + } + if (largesolidcount >= 1) { + elemtype = 1; // capcount++; + } else if ((largedihedcount > 0) && (smalldihedcount > 0)) { + elemtype = 2; // slivercount++; + } else if (largedihedcount > 0) { + elemtype = 3; // spindlecount++; + } else if (smalldihedcount > 0) { + elemtype = 4; // wedgecount++; + } else if (smallsolidcount > 0) { + elemtype = 5; // needlecount++; + } else { + elemtype = 0; // roundcount++; + } + + if (elemtype) { + // tetrahedron number, indices for four points. + fprintf(outfile, "%5d %5d %5d %5d %5d %10.6g %10.6g", elementnumber, + pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4), + smallestdiangle, biggestdiangle); + if (elemtype == 1) { + fprintf(outfile, " Cap"); + } else if (elemtype == 2) { + fprintf(outfile, " Sliver"); + } else if (elemtype == 3) { + fprintf(outfile, " Spindle"); + } else if (elemtype == 4) { + fprintf(outfile, " Wedge"); + } else if (elemtype == 5) { + fprintf(outfile, " Needle"); + } + fprintf(outfile, "\n"); + } + + tetptr = tetrahedrontraverse(); + elementnumber++; + } + + fprintf(outfile, "end elements\n"); + fclose(outfile); +} + +/////////////////////////////////////////////////////////////////////////////// +// END // +// File I/O Routines // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// internalerror() Ask the user to send me the defective product. Exit. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::internalerror() +{ + printf(" Please report this bug to sihang@weboo.com. Include the\n"); + printf(" message above, your input data set, and the exact command\n"); + printf(" line you used to run this program, thank you.\n"); + exit(1); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// precisionerror() Print an error message for precision problems. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::precisionerror() +{ + if (noexact) { + printf(" This problem maybe caused by the approximate floating-point\n"); + printf(" arthmetic. Try use -X switch in commandline and try again.\n"); + } else { + printf(" Try increasing the volume criterion and/or increasing the\n"); + printf(" minimum allowable radius-edge ratio so that tiny tetrahedra\n"); + printf(" are not created.\n"); + } +#ifdef SINGLE + printf(" Alternatively, try recompiling me with double precision\n"); + printf(" arithmetic (by removing \"#define SINGLE\" from the\n"); + printf(" source file or \"-DSINGLE\" from the makefile).\n"); +#endif // SINGLE + exit(1); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recovererror() Print an error message for recover problems. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::recovererror() +{ + printf(" You met a recover problem in your model. Please check your\n"); + printf(" input PLC facet data, make sure that all coplanar segments,\n"); + printf(" polygons and isolated points are list in this facet.\n"); + printf(" If there still has problem, please report this bug to \n"); + printf(" sihang@weboo.com. Include the message above, your input data\n"); + printf(" set, and the exact command line you used to run this program,\n"); + printf(" thank you.\n"); + exit(1); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkmesh() Test the mesh for topological consistency. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::checkmesh() +{ + triface tetraloop; + triface oppotet, oppooppotet; + point3d tetorg, tetdest, tetapex, tetoppo; + point3d oppoorg, oppodest, oppoapex; + int horrors; + int saveexact; + + // Temporarily turn on exact arithmetic if it's off. + saveexact = noexact; + noexact = 0; + if (!quiet) { + printf(" Checking consistency of mesh...\n"); + } + if (verbose < 1) { + numbernodes(1); + } + 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++) { + org (tetraloop, tetorg); + dest(tetraloop, tetdest); + apex(tetraloop, tetapex); + oppo(tetraloop, tetoppo); + if (tetraloop.loc == 0) { // Only test for inversion once. + if (orient3d(tetorg, tetdest, tetapex, tetoppo) >= 0.0) { + printf(" !! !! Inverted "); + dump(&tetraloop); + horrors++; + } + } + // Find the neighboring tetrahedron on this face. + sym(tetraloop, oppotet); + if (oppotet.tet != dummytet) { + // Check that the tetrahedron's neighbor knows it's a neighbor. + sym(oppotet, oppooppotet); + if ((tetraloop.tet != oppooppotet.tet) + || (tetraloop.loc != oppooppotet.loc)) { + printf(" !! !! Asymmetric tetra-tetra bond:\n"); + if (tetraloop.tet == oppooppotet.tet) { + printf(" (Right tetrahedron, wrong orientation)\n"); + } + printf(" First "); + dump(&tetraloop); + printf(" Second (nonreciprocating) "); + dump(&oppotet); + horrors++; + } + // Check that both tetrahedra agree on the identities + // of their shared vertices. + if (findorg(&oppotet, tetorg)) { + dest(oppotet, oppodest); + apex(oppotet, oppoapex); + } else { + oppodest = (point3d) NULL; + } + if ((tetdest != oppoapex) || (tetapex != oppodest)) { + printf(" !! !! Mismatched face coordinates between two tetras:\n"); + printf(" First mismatched "); + dump(&tetraloop); + printf(" Second mismatched "); + dump(&oppotet); + horrors++; + } + } + } + tetraloop.tet = tetrahedrontraverse(); + } + if (horrors == 0) { + if (!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); + } + // Restore the status of exact arithmetic. + noexact = saveexact; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkdelaunay() Ensure that the mesh is (constrained or conforming) // +// Delaunay. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::checkdelaunay() +{ + triface tetraloop; + triface oppotet; + face opposhelle; + point3d tetorg, tetdest, tetapex, tetoppo; + point3d oppooppo; + int shouldbedelaunay; + int horrors; + int saveexact; + + // Temporarily turn on exact arithmetic if it's off. + saveexact = noexact; + noexact = 0; + if (!quiet) { + printf(" Checking Delaunay property of mesh...\n"); + } + if (verbose < 1) { + numbernodes(1); + } + horrors = 0; + // Run through the list of triangles, checking each one. + tetrahedrons.traversalinit(); + tetraloop.tet = tetrahedrontraverse(); + while (tetraloop.tet != (tetrahedron *) NULL) { + // Check all three edges of the triangle. + for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) { + org(tetraloop, tetorg); + dest(tetraloop, tetdest); + apex(tetraloop, tetapex); + oppo(tetraloop, tetoppo); + sym(tetraloop, oppotet); + oppo(oppotet, oppooppo); + // Only test that the face is locally Delaunay if there is an + // adjoining tetrahedron whose pointer is larger (to ensure that + // each pair isn't tested twice). + shouldbedelaunay = (oppotet.tet != dummytet) + && (tetoppo != (point3d) NULL) + && (oppooppo != (point3d) NULL) + && (tetraloop.tet < oppotet.tet); + if (checksegments && shouldbedelaunay) { + // If a shell edge separates the triangles, then the edge is + // constrained, so no local Delaunay test should be done. + tspivot(tetraloop, opposhelle); + if (opposhelle.sh != dummysh){ + if (!isnonsolid(opposhelle)) { + shouldbedelaunay = 0; + } + } + } + if (shouldbedelaunay) { + if (iinsphere(tetdest, tetorg, tetapex, tetoppo, oppooppo) > 0) { + printf(" !! !! Non-Delaunay pair of triangles:\n"); + printf(" First non-Delaunay "); + dump(&tetraloop); + printf(" Second non-Delaunay "); + dump(&oppotet); + horrors++; + } + } + } + tetraloop.tet = tetrahedrontraverse(); + } + if (horrors == 0) { + if (!quiet) { + printf( + " By virtue of my perceptive intelligence, I declare the mesh Delaunay.\n"); + } + } else if (horrors == 1) { + printf( + " !! !! !! !! Precisely one terrifying transgression identified.\n"); + } else { + printf(" !! !! !! !! %d obscenities viewed with horror.\n", horrors); + } + // Restore the status of exact arithmetic. + noexact = saveexact; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkshells() Test the shells of mesh for topological consistency. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::checkshells() +{ + triface oppotet, oppooppotet; + triface testtet, spintet; + face shloop, testsh; + face segloop, testseg; + point3d tapex, spapex; + int hitbdry, edgecount; + int horrors; + + if (!quiet) { + printf(" Checking subfaces-tetrahedra connection...\n"); + } + if (verbose < 1) { + numbernodes(1); + } + horrors = 0; + subfaces.traversalinit(); + shloop.sh = shellfacetraverse(&subfaces); + while (shloop.sh != (shellface *) NULL) { + shloop.shver = 0; + stpivot(shloop, oppotet); + if (oppotet.tet != dummytet) { + tspivot(oppotet, testsh); + if (testsh.sh != shloop.sh) { + printf(" !! !! Wrong tetra-subface connection.\n"); + printf(" Tetra: "); + dump(&oppotet); + printf(" Subface: "); + dump(&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: "); + dump(&oppooppotet); + printf(" Subface: "); + dump(&shloop); + horrors++; + } + if (oppotet.tet != dummytet) { + sym(oppotet, testtet); + if (testtet.tet != oppooppotet.tet) { + printf(" !! !! Wrong tetra-subface-tetra connection.\n"); + printf(" Tetra 1: "); + dump(&oppotet); + printf(" Subface: "); + dump(&shloop); + printf(" Tetra 2: "); + dump(&oppooppotet); + horrors++; + } + } + } + shloop.sh = shellfacetraverse(&subfaces); + } + if (horrors == 0) { + if (!quiet) { + printf(" Subfaces-tetrahedra connected correctly.\n"); + } + } else if (horrors == 1) { + printf( + " !! !! !! !! Precisely one terrifying subface-tetrahedron identified.\n"); + } else { + printf(" !! !! !! !! %d subface-tetrahedron viewed with horror.\n", + horrors); + return; + } + + if (!quiet) { + printf(" Checking Subfaces-subsegments connection...\n"); + } + horrors = 0; + horrors = 0; + subfaces.traversalinit(); + shloop.sh = shellfacetraverse(&subfaces); + while (shloop.sh != (shellface *) NULL) { + shloop.shver = 0; + for (edgecount = 0; edgecount < 3; edgecount++) { + senextself(shloop); + sspivot(shloop, testseg); + if (testseg.sh != dummysh) { + if (!((sorg(shloop) == sorg(testseg)) + && (sdest(shloop) == sdest(testseg))) + && !((sorg(shloop) == sdest(testseg)) + && (sdest(shloop) == sorg(testseg)))) { + printf(" !! !! Wrong subface-subsegment connection.\n"); + printf(" Subface: "); + dump(&shloop); + printf(" Subsegment: "); + dump(&testseg); + horrors++; + } + } + } + shloop.sh = shellfacetraverse(&subfaces); + } + if (horrors == 0) { + if (!quiet) { + printf(" Subfaces-subsegments connected correctly.\n"); + } + } else if (horrors == 1) { + printf( + " !! !! !! !! Precisely one terrifying subface-subsegments identified.\n"); + } else { + printf(" !! !! !! !! %d subface-subsegments viewed with horror.\n", + horrors); + return; + } + + if (!quiet) { + printf(" Checking Subsegments connection...\n"); + } + horrors = 0; + subsegs.traversalinit(); + segloop.sh = shellfacetraverse(&subsegs); + while (segloop.sh != (shellface *) NULL) { + segloop.shver = 0; + sapex(segloop, spapex); + if (spapex != (point3d) NULL) { + printf(" !! !! Detect a subface in subsegs pool(apex() != NULL).\n"); + printf(" Wrong : "); + dump(&segloop); + horrors++; + segloop.sh = shellfacetraverse(&subsegs); + continue; + } + spivot(segloop, testsh); + if (testsh.sh == dummysh) { + printf(" !! !! A subsegment missing its parent subface.\n "); + printf(" Wrong : "); + dump(&segloop); + horrors++; + segloop.sh = shellfacetraverse(&subsegs); + continue; + } + sspivot(testsh, testseg); + if (testseg.sh != segloop.sh) { + printf(" !! !! Wrong subface-subsegment connection.\n "); + printf(" Wrong : "); + dump(&testsh); + printf(" Wrong : "); + dump(&segloop); + horrors++; + segloop.sh = shellfacetraverse(&subsegs); + continue; + } + stpivot(testsh, testtet); + if (testtet.tet == dummytet) { + sesymself(testsh); + stpivot(testsh, testtet); + if (testtet.tet == dummytet) { + printf(" !! !! Parent subface not bonded to a valid tetrahedron.\n "); + printf(" Wrong : "); + dump(&testsh); + horrors++; + segloop.sh = shellfacetraverse(&subsegs); + continue; + } + } + findversion(&testtet, &testseg); + spintet = testtet; + apex(testtet, tapex); + hitbdry = 0; + while (true) { + if (fnextself(spintet)) { + apex(spintet, spapex); + if (spapex == tapex) { + break; // Rewind, can leave now. + } + tspivot(spintet, testsh); + if (testsh.sh != dummysh) { + findversion(&testsh, &spintet); + sspivot(testsh, testseg); + if (testseg.sh == dummysh) { + printf(" !! !! Subface miss bond a subsegment.\n"); + printf(" Miss bond : "); + dump(&testsh); + printf(" Miss : "); + dump(&segloop); + horrors++; + } else if (testseg.sh != segloop.sh) { + printf(" !! !! Wrong subface-subsegment bond.\n"); + printf(" Wrong : "); + dump(&testsh); + printf(" Wrong : "); + dump(&segloop); + horrors++; + } + } + } else { + hitbdry ++; + if(hitbdry >= 2) { + break; + } else { + esym(testtet, spintet); + } + } + } + segloop.sh = shellfacetraverse(&subsegs); + } + if (horrors == 0) { + if (!quiet) { + printf(" Subsegments connected correctly.\n"); + } + } else if (horrors == 1) { + printf( + " !! !! !! !! Precisely one terrifying subsegment identified.\n"); + } else { + printf(" !! !! !! !! %d subsegments viewed with horror.\n", horrors); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// quality_statistics() Print statistics about the quality of the mesh. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::quality_statistics() +{ + triface tetloop; + point3d p[4]; + REAL ratiotable[16]; + REAL dx[6], dy[6], dz[6]; + REAL edgelength[6]; + REAL alldihed[6], allsolid[4]; + REAL tetvol; + REAL tetlongest2; + REAL shortest, longest; + REAL smallestvolume, biggestvolume; + REAL tetminaltitude2; + REAL minaltitude; + REAL tetaspect2; + REAL worstaspect; + REAL smallestdiangle, biggestdiangle; + REAL smallsolid, largesolid; + REAL smalldihed, largedihed; + unsigned long roundcount, slivercount, capcount; + unsigned long needlecount, wedgecount, spindlecount; + int dihedangletable[18]; + int aspecttable[16]; + int aspectindex; + int tendegree; + int smallsolidcount, largesolidcount; + int smalldihedcount, largedihedcount; + int i, ii, j, k; + + printf("Mesh quality statistics:\n\n"); + + ratiotable[0] = 1.5; ratiotable[1] = 2.0; + ratiotable[2] = 2.5; ratiotable[3] = 3.0; + ratiotable[4] = 4.0; ratiotable[5] = 6.0; + ratiotable[6] = 10.0; ratiotable[7] = 15.0; + ratiotable[8] = 25.0; ratiotable[9] = 50.0; + ratiotable[10] = 100.0; ratiotable[11] = 300.0; + ratiotable[12] = 1000.0; ratiotable[13] = 10000.0; + ratiotable[14] = 100000.0; ratiotable[15] = 0.0; + for (i = 0; i < 16; i++) { + aspecttable[i] = 0; + } + for (i = 0; i < 18; i++) { + dihedangletable[i] = 0; + } + + worstaspect = 0.0; + minaltitude = xmax - xmin + ymax - ymin + zmax - zmin; + minaltitude = minaltitude * minaltitude; + shortest = minaltitude; + longest = 0.0; + smallestvolume = minaltitude; + biggestvolume = 0.0; + worstaspect = 0.0; + smallestdiangle = 180.0; + biggestdiangle = 0.0; + smallsolid = 3.; + largesolid = 300.; + smalldihed = 18.; + largedihed = 162.; + roundcount = slivercount = 0l; + capcount = spindlecount = wedgecount = needlecount = 0; + + tetrahedrons.traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + org (tetloop, p[0]); + dest(tetloop, p[1]); + apex(tetloop, p[2]); + oppo(tetloop, p[3]); + tetlongest2 = 0.0; + + for (i = 0; i < 3; i++) { + j = plus1mod3[i]; + k = minus1mod3[i]; + dx[i] = p[j][0] - p[k][0]; + dy[i] = p[j][1] - p[k][1]; + dz[i] = p[j][2] - p[k][2]; + edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i] + dz[i] * dz[i]; + if (edgelength[i] > tetlongest2) { + tetlongest2 = edgelength[i]; + } + if (edgelength[i] > longest) { + longest = edgelength[i]; + } + if (edgelength[i] < shortest) { + shortest = edgelength[i]; + } + } + + for (i = 3; i < 6; i++) { + j = i - 3; + k = 3; + dx[i] = p[j][0] - p[k][0]; + dy[i] = p[j][1] - p[k][1]; + dz[i] = p[j][2] - p[k][2]; + edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i] + dz[i] * dz[i]; + if (edgelength[i] > tetlongest2) { + tetlongest2 = edgelength[i]; + } + if (edgelength[i] > longest) { + longest = edgelength[i]; + } + if (edgelength[i] < shortest) { + shortest = edgelength[i]; + } + } + + tetvol = tetvolume(p[0], p[1], p[2], p[3]); + if (tetvol < 0) tetvol = -tetvol; + if (tetvol < smallestvolume) { + smallestvolume = tetvol; + } + if (tetvol > biggestvolume) { + biggestvolume = tetvol; + } + + tetalldihedral(p[0], p[1], p[2], p[3], alldihed); + smalldihedcount = largedihedcount = 0; + for (i = 0; i < 6; i++) { + if (alldihed[i] < smallestdiangle) { + smallestdiangle = alldihed[i]; + } else if (alldihed[i] > biggestdiangle) { + biggestdiangle = alldihed[i]; + } + if (alldihed[i] < smalldihed) { + smalldihedcount++; + } else if (alldihed[i] > largedihed) { + largedihedcount++; + } + tendegree = (int) (alldihed[i] / 10.); + dihedangletable[tendegree]++; + } + allsolid[0] = alldihed[0] + alldihed[1] + alldihed[2] - 180; + allsolid[1] = alldihed[0] + alldihed[3] + alldihed[4] - 180; + allsolid[2] = alldihed[1] + alldihed[3] + alldihed[5] - 180; + allsolid[3] = alldihed[2] + alldihed[4] + alldihed[5] - 180; + smallsolidcount = largesolidcount = 0; + for (i = 0; i < 4; i++) { + if (allsolid[i] < smallsolid) { + smallsolidcount++; + } else if (allsolid[i] > largesolid) { + largesolidcount++; + } + } + if (largesolidcount >= 1) { + capcount++; + } else if ((largedihedcount > 0) && (smalldihedcount > 0)) { + slivercount++; + } else if (largedihedcount > 0) { + spindlecount++; + } else if (smalldihedcount > 0) { + wedgecount++; + } else if (smallsolidcount > 0) { + needlecount++; + } else { + roundcount++; + } + + tetloop.tet = tetrahedrontraverse(); + } + + shortest = sqrt(shortest); + longest = sqrt(longest); + minaltitude = sqrt(minaltitude); + worstaspect = sqrt(worstaspect); + + 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("\n"); + printf(" Smallest dihedral: %14.5g | Largest dihedral: %14.5g\n\n", + smallestdiangle, biggestdiangle); + printf(" Dihedral Angle histogram:\n"); + for (i = 0; i < 9; i++) { + printf(" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n", + i * 10, i * 10 + 10, dihedangletable[i], + i * 10 + 90, i * 10 + 100, dihedangletable[i + 9]); + } + printf("\n"); + + printf(" Shape histogram:\n\n"); + printf(" Sliver: %d\n", slivercount); + printf(" Needle: %d\n", needlecount); + printf(" Spindle: %d\n", spindlecount); + printf(" Wedge: %d\n", wedgecount); + printf(" Cap: %d\n\n", capcount); + printf(" There are %d bad elements among %d elements.\n", + slivercount + needlecount + spindlecount + wedgecount + capcount, + tetrahedrons.items); + + printf("\n"); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// statistics() Print all sorts of cool facts. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::statistics() +{ + printf("\nStatistics:\n\n"); + printf(" Input points: %d\n", inpoints); + if (refine) { + printf(" Input tetrahedra: %d\n", inelements); + } + if (poly) { + printf(" Input facets: %d\n", infacets); + if (!refine) { + printf(" Input holes: %d\n", holes); + } + } + + printf("\n Mesh points: %ld\n", points.items); + printf(" Mesh tetrahedra: %ld\n", tetrahedrons.items); + printf(" Mesh faces: %ld\n", faces); + if (poly || refine) { + printf(" Mesh boundary faces: %ld\n", hullsize); + printf(" Mesh subfaces: %ld (include nonsolids)\n", subfaces.items); + printf(" Mesh subsegments: %ld\n\n", subsegs.items); + } else { + printf(" Mesh convex hull faces: %ld\n\n", hullsize); + } + if (verbose) { + if (quality) { + quality_statistics(); + } + printf("Memory allocation statistics:\n\n"); + printf(" Maximum number of points: %ld\n", points.maxitems); + printf(" Maximum number of tetrahedra: %ld\n", tetrahedrons.maxitems); + if (subfaces.maxitems > 0) { + printf(" Maximum number of subfaces: %ld\n", subfaces.maxitems); + } + if (subsegs.maxitems > 0) { + printf(" Maximum number of segments: %ld\n", subsegs.maxitems); + } + if (viri.maxitems > 0) { + printf(" Maximum number of viri: %ld\n", viri.maxitems); + } + if (badfaces.maxitems > 0) { + printf(" Maximum number of encroached subfaces: %ld\n", + badfaces.maxitems); + } + if (badsegments.maxitems > 0) { + printf(" Maximum number of encroached segments: %ld\n", + badsegments.maxitems); + } + if (badtets.maxitems > 0) { + printf(" Maximum number of bad tetrahedra: %ld\n", badtets.maxitems); + } + printf(" Approximate heap memory use (bytes): %ld\n\n", + points.maxitems * points.itembytes + + tetrahedrons.maxitems * tetrahedrons.itembytes + + subfaces.maxitems * subfaces.itembytes + + subsegs.maxitems * subsegs.itembytes + + viri.maxitems * viri.itembytes + + badfaces.maxitems * badfaces.itembytes + + badsegments.maxitems * badsegments.itembytes + + badtets.maxitems * badtets.itembytes); + + printf("Algorithmic statistics:\n\n"); + printf(" Number of insphere tests: %ld\n", inspherecount); + printf(" Number of orientation tests: %ld\n", orient3dcount); + if (segmentintersectioncount > 0) { + printf(" Number of segment intersections: %ld\n", + segmentintersectioncount); + } + printf("\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// syntax() Print list of command line switches. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::syntax() +{ + printf("tetgen [-pq__a__AnfcgBPNEIOXzS__T__CQVh] input_file\n"); + printf(" -p Triangulates a Piecewise Linear Complex (.poly file).\n"); + printf(" -q Quality mesh generation. Use Shewchuk's Delaunay Refinement\n"); + printf(" Algorithm. A minimum radius-edge ratio may be specified.\n"); + printf(" -a Applies a maximum tetrahedron volume constraint.\n"); + printf(" -A Applies attributes to identify elements in certain regions.\n"); + printf(" -f Generates a list of tetrahedron faces.\n"); + printf(" -e Generates a list of edges (segments).\n"); + printf(" -c Generates a list of convex hull faces.\n"); + printf(" -n Generates a list of tetrahedron neighbors.\n"); + printf(" -g Generates file for viewing mesh by Geomview(*.off) or Gid.\n"); + printf(" -B Suppresses output of boundary information.\n"); + printf(" -P Suppresses output of .poly file.\n"); + printf(" -N Suppresses output of .node file.\n"); + printf(" -E Suppresses output of .ele file.\n"); + printf(" -I Suppresses mesh iteration numbers.\n"); + printf(" -O Ignores holes in .poly file.\n"); + printf(" -X Use exact arithmetic to perform geometric predicates.\n"); + printf(" -z Numbers all items starting from zero (rather than one).\n"); + printf(" -S Specifies maximum number of added Steiner points.\n"); + printf(" -T Specifies the tolerance for round-to-zero.\n"); + printf(" -C Check consistency of final mesh.\n"); + printf(" -Q Quiet: No terminal output except errors.\n"); + printf(" -V Verbose: Detailed information on what I'm doing.\n"); + printf(" -h Help: Detailed instructions for Tetgen.\n"); + exit(0); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// meshinit() Initialize some variables. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::meshinit() +{ + recenttet.tet = (tetrahedron*) NULL; // No tetra has been visited yet. + samples = 1; // Point location should take at least one sample. + checksegments = 0; // There are no segments in the triangulation yet. + randomseed = 1; + usefliplist = 0; + shflaws = shsegflaws = tetflaws = 0; + fliplist = NULL; + dummytetbase = dummyshbase = NULL; + surfmesh = NULL; + + // Init statistic variables. + flip_t23s = flip_t32s = flip_t22s = flip_t44s = 0; + cospherecount = segmentintersectioncount = 0l; + inspherecount = orient3dcount = 0l; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// meshdeinit() Free all remaining allocated memory. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::meshdeinit() +{ + tetrahedrons.deinit(); + if (dummytetbase) { + delete [] dummytetbase; + } + if (useshelles) { + subfaces.deinit(); + subsegs.deinit(); + if (dummyshbase) { + delete [] dummyshbase; + } + } + points.deinit(); + if (poly && faces) { + badsegments.deinit(); + } + if (quality) { + badfaces.deinit(); + if ((minratio > 0.0) || varvolume || fixedvolume) { + badtets.deinit(); + } + } + if (fliplist) { + delete fliplist; + } + if (surfmesh) { + delete surfmesh; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// parsecommandline() Read the command line, identify switches, and set // +// up options and file names. // +// // +// The effects of this routine are felt entirely through global variables. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::parsecommandline(int argc, char **argv) +{ + int startindex; + int increment; + int meshnumber; + int i, j, k; + char workstring[FILENAMESIZE]; + + poly = smesh = 0; + refine = quality = varvolume = fixedvolume = regionattrib = convex = 0; + firstnumber = 1; + facesout = edgesout = voronoi = neighbors = geomview = 0; + nobound = nopolywritten = nonodewritten = noelewritten = noiterationnum = 0; + noholes = nobisect = 0; + noexact = 1; // default vaule, not use exact arithmetic. + splitseg = 1; // default value in 3D case. + docheck = 0; + steiner = -1; + minratio = 2.0; // Default radius-edge ratio. + maxvolume = -1.0; + noroundoff = 0; + usertolerance = 1e-12; // default tolerance. + userubtolerance = 1e-10; + badelemreport = 0; + quiet = verbose = 0; + innodefilename[0] = '\0'; + commandline[0] = '\0'; + + startindex = 1; + strcpy(commandline, argv[0]); + strcat(commandline, " "); + for (i = startindex; i < argc; i++) { + if (argv[i][0] == '-') { + for (j = startindex; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'p') { + poly = 1; + } + if (argv[i][j] == 'r') { + refine = 1; + } + if (argv[i][j] == 'q') { + quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + minratio = (REAL) strtod(workstring, (char **) NULL); + if (minratio <= 0.0) { + printf("Command line Error: After -q switch, the minimum"); + printf(" radius-edge ratio must greater than Zero.\n"); + exit(1); + } + } else { + minratio = 2; // Default radius-edge ratio. + } + } + if (argv[i][j] == 'a') { + quality = 1; + 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] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + maxvolume = (REAL) strtod(workstring, (char **) NULL); + if (maxvolume <= 0.0) { + printf("Command line Error: After -a switch, the maximum"); + printf(" volume constraints must greater than Zero.\n"); + exit(1); + } + } else { + varvolume = 1; + } + } + if (argv[i][j] == 'A') { + regionattrib = 1; + } + if (argv[i][j] == 'c') { + convex = 1; + } + if (argv[i][j] == 's') { + splitseg = 0; + } + if (argv[i][j] == 'z') { + firstnumber = 0; + } + if (argv[i][j] == 'f') { + facesout = 1; + } + if (argv[i][j] == 'e') { + edgesout = 1; + } + if (argv[i][j] == 'v') { + voronoi = 1; + } + if (argv[i][j] == 'n') { + neighbors = 1; + } + if (argv[i][j] == 'g') { + geomview = 1; + } + if (argv[i][j] == 'B') { + nobound = 1; + } + if (argv[i][j] == 'P') { + nopolywritten = 1; + } + if (argv[i][j] == 'N') { + nonodewritten = 1; + } + if (argv[i][j] == 'E') { + noelewritten = 1; + } + if (argv[i][j] == 'I') { + noiterationnum = 1; + } + if (argv[i][j] == 'O') { + noholes = 1; + } + if (argv[i][j] == 'X') { + noexact = 0; // Use exact arithmetic. + } + if (argv[i][j] == 'Y') { + nobisect++; + } + if (argv[i][j] == 'S') { + steiner = 0; + while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + j++; + steiner = steiner * 10 + (int) (argv[i][j] - '0'); + } + } + 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'; + usertolerance = (REAL) strtod(workstring, (char **) NULL); + if (usertolerance <= 0.0) { + printf("Command line Error: After -T switch, tolerance"); + printf(" must greater than Zero.\n"); + exit(1); + } + userubtolerance = usertolerance * 1e+2; + } + } + if (argv[i][j] == 'F') { + noroundoff = 1; + } + if (argv[i][j] == 'b') { + badelemreport = 1; + } + if (argv[i][j] == 'C') { + docheck = 1; + } + if (argv[i][j] == 'Q') { + quiet = 1; + } + if (argv[i][j] == 'V') { + verbose++; + } + if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || + (argv[i][j] == '?')) { + syntax(); + } + } + } else { + strncpy(innodefilename, argv[i], FILENAMESIZE - 1); + innodefilename[FILENAMESIZE - 1] = '\0'; + } + strcat(commandline, argv[i]); + strcat(commandline, " "); + } + commandline[FILENAMESIZE - 1] = '\0'; + if (innodefilename[0] == '\0') { + syntax(); + } + if (!strcmp(&innodefilename[strlen(innodefilename) - 5], ".node")) { + innodefilename[strlen(innodefilename) - 5] = '\0'; + } + if (!strcmp(&innodefilename[strlen(innodefilename) - 5], ".poly")) { + innodefilename[strlen(innodefilename) - 5] = '\0'; + poly = 1; + smesh = 0; + } + if (!strcmp(&innodefilename[strlen(innodefilename) - 6], ".smesh")) { + innodefilename[strlen(innodefilename) - 6] = '\0'; + smesh = 1; + poly = 0; + } + if (!strcmp(&innodefilename[strlen(innodefilename) - 4], ".ele")) { + innodefilename[strlen(innodefilename) - 4] = '\0'; + refine = 1; + } + if (!strcmp(&innodefilename[strlen(innodefilename) - 7], ".volume")) { + innodefilename[strlen(innodefilename) - 5] = '\0'; + refine = 1; + quality = 1; + varvolume = 1; + } + steinerleft = steiner; + useshelles = poly || smesh || refine || quality || convex; + goodratio = minratio; + goodratio *= goodratio; + if (refine && noiterationnum) { + printf("Command line Error: You cannot use the -I switch when"); + printf(" refining a triangulation.\n"); + exit(1); + } + // Be careful not to allocate space for element volume constraints that + // will never be assigned any value (other than the default -1.0). + if (!refine && !poly && !smesh) { + 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 || (!poly && !smesh)) { + regionattrib = 0; + } + + strcpy(inpolyfilename, innodefilename); + strcpy(insmeshfilename, innodefilename); + strcpy(inelefilename, innodefilename); + strcpy(volumefilename, innodefilename); + increment = 0; + strcpy(workstring, innodefilename); + 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(outnodefilename, innodefilename); + strcpy(outelefilename, innodefilename); + strcpy(facefilename, innodefilename); + strcpy(edgefilename, innodefilename); + strcpy(neighborfilename, innodefilename); + strcpy(offfilename, innodefilename); + strcat(outnodefilename, ".node"); + strcat(outelefilename, ".ele"); + strcat(facefilename, ".face"); + strcat(edgefilename, ".edge"); + strcat(neighborfilename, ".neigh"); + strcat(offfilename, ".off"); + } else if (increment == 0) { + strcpy(outnodefilename, innodefilename); + strcpy(outpolyfilename, innodefilename); + strcpy(outelefilename, innodefilename); + strcpy(facefilename, innodefilename); + strcpy(edgefilename, innodefilename); + strcpy(neighborfilename, innodefilename); + strcpy(offfilename, innodefilename); + strcat(outnodefilename, ".1.node"); + strcat(outpolyfilename, ".1.poly"); + strcat(outelefilename, ".1.ele"); + strcat(facefilename, ".1.face"); + strcat(edgefilename, ".1.edge"); + strcat(neighborfilename, ".1.neigh"); + strcat(offfilename, ".1.off"); + } else { + workstring[increment] = '%'; + workstring[increment + 1] = 'd'; + workstring[increment + 2] = '\0'; + sprintf(outnodefilename, workstring, meshnumber + 1); + strcpy(outpolyfilename, outnodefilename); + strcpy(outelefilename, outnodefilename); + strcpy(facefilename, outnodefilename); + strcpy(edgefilename, outnodefilename); + strcpy(neighborfilename, outnodefilename); + strcpy(offfilename, outnodefilename); + strcat(outnodefilename, ".node"); + strcat(outpolyfilename, ".poly"); + strcat(outelefilename, ".ele"); + strcat(facefilename, ".face"); + strcat(edgefilename, ".edge"); + strcat(neighborfilename, ".neigh"); + strcat(offfilename, ".off"); + } + strcat(innodefilename, ".node"); + strcat(inpolyfilename, ".poly"); + strcat(insmeshfilename, ".smesh"); + strcat(inelefilename, ".ele"); + strcat(volumefilename, ".volume"); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// triangulate() Gosh, do everything. // +// // +// 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 points from a file and either // +// - triangulate them // +// - Insert the PLC segments and subfaces (-p). // +// - Read the holes (-p), regional attributes (-pA), and regional area // +// constraints (-pa). Carve the holes and concavities, and spread the // +// regional attributes and area constraints. // +// - Enforce the constraints on minimum angle (-q) and maximum area (-a). // +// Also enforce the conforming Delaunay property (-q and -a). // +// - Compute the number of edges in the resulting mesh. // +// - Write the output files and print the statistics. // +// - Check the consistency and Delaunay property of the mesh (-C). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void mesh3d::triangulate(int argc, char **argv) +{ + REAL *holearray; // Array of holes. + REAL *regionarray; // Array of regional attributes and area constraints. + FILE *polyfile; + // Variables for timing the performance of Tetrahedra. +#ifndef NO_TIMER + // The types are defined in sys/time.h. + struct timeval tv0, tv1, tv2, tv3, tv4, tv5, tv6; + struct timezone tz; +#else // NO_TIMER + // The types are defined in time.h. + clock_t tv0, tv1, tv2, tv3, tv4, tv5, tv6; +#endif // NO_TIMER + +#ifndef NO_TIMER + gettimeofday(&tv0, &tz); +#else // NO_TIMER + tv0 = clock(); +#endif // NO_TIMER + + exactinit(); // Initialize exact arithmetic constants. + parsecommandline(argc, argv); + readnodes(&polyfile); + + if (verbose > 1) { + // Number the points first when "-VV" or "-VVV" switch be set, so that + // the debug functions can output point indices for you. + numbernodes(1); + } + + if (!quiet) { +#ifndef NO_TIMER + gettimeofday(&tv1, &tz); +#else // NO_TIMER + tv1 = clock(); +#endif // NO_TIMER + } + + if (refine) { + // Read and reconstruct a mesh. + printf("Sorry, the -r switch has not been implemented now.\n"); + exit(1); + } else { + hullsize = delaunay(); // Triangulate the points. + } + + if (!quiet) { + if (refine) { + printf("Mesh reconstruction"); + } else { + printf("Delaunay"); + } +#ifndef NO_TIMER + gettimeofday(&tv2, &tz); + printf(" milliseconds: %ld\n", 1000l * (tv2.tv_sec - tv1.tv_sec) + + (tv2.tv_usec - tv1.tv_usec) / 1000l); +#else // NO_TIMER + tv2 = clock(); + printf(" milliseconds: %g\n", 1000l * (tv2 - tv1) / (REAL) CLK_TCK); +#endif // NO_TIMER + } + + if (useshelles) { + checksegments = 1; // Segments will be introduced next. + if (!refine) { + // Insert PLC segments/facets and/or convex hull segments/facets. + infacets = formskeleton(polyfile); + if (docheck) { + checkshells(); + } + } + } + + if (!quiet) { +#ifndef NO_TIMER + gettimeofday(&tv3, &tz); + if (useshelles && !refine) { + printf("Segment and facet milliseconds: %ld\n", + 1000l * (tv3.tv_sec - tv2.tv_sec) + + (tv3.tv_usec - tv2.tv_usec) / 1000l); + } +#else // NO_TIMER + tv3 = clock(); + if (useshelles && !refine) { + printf("Segment and facet milliseconds: %g\n", + 1000l * (tv3 - tv2) / (REAL) CLK_TCK); + } +#endif // NO_TIMER + } + + if (poly || smesh) { + readholes(polyfile, &holearray, &holes, ®ionarray, ®ions); + if (!refine) { + // Carve out holes and concavities. + carveholes(holearray, holes, regionarray, regions); + } + } else { + // Without a PLC, there can be no holes or regional attributes + // or area constraints. The following are set to zero to avoid + // an accidental free later. + holes = 0; + regions = 0; + } + + if (!quiet) { +#ifndef NO_TIMER + gettimeofday(&tv4, &tz); + if (poly && !refine) { + printf("Hole milliseconds: %ld\n", 1000l * (tv4.tv_sec - tv3.tv_sec) + + (tv4.tv_usec - tv3.tv_usec) / 1000l); + } +#else // NO_TIMER + tv4 = clock(); + if (poly && !refine) { + printf("Hole milliseconds: %g\n", 1000l * (tv4 - tv3) / (REAL) CLK_TCK); + } +#endif // NO_TIMER + } + + if (quality) { + enforcequality(); // Enforce angle and area constraints. + } + + if (!quiet) { +#ifndef NO_TIMER + gettimeofday(&tv5, &tz); + if (quality) { + printf("Quality milliseconds: %ld\n", + 1000l * (tv5.tv_sec - tv4.tv_sec) + + (tv5.tv_usec - tv4.tv_usec) / 1000l); + } +#else // NO_TIMER + tv5 = clock(); + if (quality) { + printf("Quality milliseconds: %g\n", + 1000l * (tv5 - tv4) / (REAL) CLK_TCK); + } +#endif // NO_TIMER + } + + // Compute the number of edges. + faces = (4l * tetrahedrons.items + hullsize) / 2l; + + if (!quiet) { + printf("\n"); + } + + // If not using iteration numbers, don't write a .node file if one was + // read, because the original one would be overwritten! + if (nonodewritten || (noiterationnum && readnodefile)) { + if (!quiet) { + printf("NOT writing a .node file.\n"); + } + numbernodes(); // We must remember to number the points. + } else { + outnodes(); // Numbers the points too. + } + + if (noelewritten) { + if (!quiet) { + printf("NOT writing an .ele file.\n"); + } + } else { + outelems(); + } + + if (regions > 0) { + delete [] regionarray; + } + if (holes > 0) { + delete [] holearray; + } + if (geomview) { + outelems2gid(); // for gid mesh reader. + outfaces2gid(1); // only output hull faces. + outoff(); // off file of geomview. + // outelems2fa(); // Fa files. + } + if (facesout || convex) { + outfaces(convex); + } + if (edgesout) { + outedges(); + } + if (neighbors) { + outneighbors(); + } + if (badelemreport) { + dumpallbadelems("badelems.gid"); + } + + if (!quiet) { +#ifndef NO_TIMER + gettimeofday(&tv6, &tz); + printf("\nOutput milliseconds: %ld\n", + 1000l * (tv6.tv_sec - tv5.tv_sec) + + (tv6.tv_usec - tv5.tv_usec) / 1000l); + printf("Total running milliseconds: %ld\n", + 1000l * (tv6.tv_sec - tv0.tv_sec) + + (tv6.tv_usec - tv0.tv_usec) / 1000l); +#else // NO_TIMER + tv6 = clock(); + printf("\nOutput milliseconds: %g\n", + 1000l * (tv6 - tv5) / (REAL) CLK_TCK); + printf("Total running milliseconds: %g\n", + 1000l * (tv6 - tv0) / (REAL) CLK_TCK); +#endif // NO_TIMER + statistics(); + } + + // If the "-C" swith be set. + if (docheck) { + checkmesh(); + if (checksegments) { + checkshells(); + } + checkdelaunay(); + } +} diff --git a/Tetgen/tetlib.h b/Tetgen/tetlib.h new file mode 100644 index 0000000000..3c860cb03e --- /dev/null +++ b/Tetgen/tetlib.h @@ -0,0 +1,1301 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// tetlib.h Head file for the mesh data structures and mesh3d class // +// declaration. // +// // +// Tetgen Version 1.0 beta // +// Oct. 2001 // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Tetgen is based on the Delaunay method and incorporates face-swapping // +// techniques. It generates meshes composed of tetrahedral elements. Tetgen // +// generates exact Delaunay tetrahedralization for three-dimensional point // +// sets, generates boundary constrained Delaunay tetrahedralization and // +// quality (conforming) Delaunay tetrahedralizations for three-dimensional // +// Piecewise Linear Complex(PLC). // +// // +// The Tetgen mesh generator is based on: // +// // +// [*] Integrating two relative mesh data structures: The tetrahedron-based // +// mesh data structure and triangle-edge mesh data structure. // +// // +// [*] Using the randomized incremental flip algorithm to construct Delaunay // +// tetrahedralization for 3D point sets. // +// // +// [*] Using simple face/edge swapping method and local re-meshing method to // +// construct boundary-constrained Delaunay tetrahedralization. // +// // +// [*] Using the Delaunay refinement algorithm and the radius-edge ratio // +// quality measure to incrementally insert (steiner) points into the // +// mesh to eliminate bad quality tetrahedra and generate an almost good // +// mesh with good grading. // +// // +// [*] Other algorithms involve fast randomized point location algorithm to // +// perform fast point location and using gift-wrapping algorithm to // +// construct a constrained Delaunay triangulation for triangular faces // +// bounded polyhedras. // +// // +// [*] Embedding the 2D mesh generator Triangle to generate planar surface // +// mesh, Optionally using the adaptive exact arithmetic package to // +// improve the robustness of the implementation. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef tetlibH +#define tetlibH + +#include "defines.h" +#include "linklist.h" +#include "trilib.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh data structures // +// // +// The efficiency of a tetrahedral mesh generator is rests on its // +// algorithms and data structures. It was important to begin with a robust // +// data structure that was flexible and encompassing enough to serve as the // +// basis for a wide variety of applications. In Tetgen, the data structure // +// was so designed make it suitable for both Delaunay and Advancing-Front // +// methods. // +// // +// Tetgen integrates two relative mesh data structures: The tetrahedron- // +// based data structure of Shewchuk[1] and the triangle-edge data structure // +// of Mucke[2]. Where the tetrahedron-based data structure is convenient for // +// storing tetrahedral mesh and is deeply discussed in [1]. The triangle- // +// edge data structure is convenient for face classification and mesh // +// manipulation, it was mainly described in [2]. // +// // +// Please refer to Dobkin and Laszlo[3], Guibas and Stolfi[4], and Owen[5] // +// for more general discussions about mesh data structures. // +// // +// Refernces: // +// // +// [1] Jonathan Richard Shewchuk, Delaunay Refinement Mesh Generation. Ph.D. // +// thesis, School of Computer Science, Carnegie Mellon University, Pitt- // +// sburgh, Pennsylvania. May 1997. Available as Technical Rreport CMU-CS // +// -97-137. // +// [2] Ernst P. Mucke, Shapes and Implementations in Three-Dimensional Geom- // +// etry. Ph.D. thesis, Technical Report UIUCDCS-R-93-1836. Department of // +// Computer Science, University of Illinois at Urbana-Champaign, Urbana, // +// Illinois, 1993. // +// [3] David P. Dobkin and Michael J. Laszlo. Primitives for the Manipulati- // +// on of Three-Dimensional Subdivisions. Algorithmica 4:3-32, 1989. // +// [4] Leonidas J. Guibas and Jorge Stolfi, Primitives for the Manipulation // +// of General Subdivisions and the Computation of Voronoi Diagrams, ACM // +// Transactions on Graphics 4(2):74-123, April 1985. // +// [5] Steven J. Owen, Non-Simplicial Unstructured Mesh Generation, Ph.D. // +// Dissertation, Carnegie Mellon University, 1999. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// Basic data structure: tetrahedron // +// // +// Each tetrahedron contain four pointers to its vertices, and four // +// pointers to the adjoining tetrahedra. Plus four pointers to subfaces // +// (define below, these pointers are usually 'dummysh'). It may or may not // +// also contain user-defined attributes and/or a floating-point volume // +// constraint. // +// Because the size and structure of a 'tetrahedron' is not decided until // +// runtime, It is not simply defined to be a structure or a class. // +// // +/////////////////////////////////////////////////////////////////////////////// + +typedef REAL **tetrahedron; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Basic data structure: shellface // +// // +// The shell (triangular) face data structure. Both subface and subsegment // +// are represented by shellface(see[1]). // +// Each subface contains three pointers to its vertices, three pointers to // +// adjoining subfaces, and two pointers to adjoining tetrahedron, plus one // +// boundary marker. Three pointer to adjoining subfaces are only used for // +// coplanar neighbours in a common facet, or used for applications of gener- // +// ating surface meshes. Each subface also has three pointers to adjoining // +// subsegments. To save space, there are no pointers directly connecting // +// tetrahedra and adjoining subsegments; connections between tetrahedra and // +// subsegments are entirely mediated through subfaces. // +// Because a subsegment may be shared by any number of subfaces and // +// tetrahedra, each subsegment has a pointer to only one adjoining subface // +// (chosen arbitrarily); the others must be found through the connectivity // +// of the mesh. // +// // +/////////////////////////////////////////////////////////////////////////////// + +typedef REAL **shellface; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Basic data structure: point3d // +// // +// The point data structure. Each point is actually an array of REALs. // +// The number of REALs is unknown until runtime. An integer boundary marker, // +// and sometimes a pointer to a tetrahedron, is appended after the REALs. // +// // +/////////////////////////////////////////////////////////////////////////////// + +typedef REAL *point3d; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Handle // +// // +// The oriented triangular face 'triface' and oriented shell face 'face' // +// data structures defined below do not themselves store any part of the // +// mesh. The mesh itself is made of 'tetrahedron's, 'shellface's, and // +// 'point3d's. // +// // +// Oriented triangular faces and oriented shell faces will usually be // +// referred to as "handles". A handle is essentially a pointer into the // +// mesh; it allows you to "hold" one particular part of the mesh. Handles // +// are used to specify the regions in which one is traversing and modifying // +// the mesh. // +// // +// A 'triface' is a handle that holds a tetrahedron. It holds a specific // +// side of the tetrahedron. An 'face' is a handle that holds a shell face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// The handle data structure: triface // +// // +// triface is an oriented triangluar face of tetrahedron in 3D. The // +// orientation is determined by face vertices. A triface includes a pointer // +// to a tetrahedron, a face location and a face version. where face location // +// is an integer number varies from 0 to 3. It was used to indicate a face // +// of tetrahedron. Face version is an integer number varies from 0 to 5. It // +// was used to represent an oriented edge of face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class triface { + + public: + + tetrahedron* tet; + int loc; // Range from 0 to 3. + int ver; // Range from 0 to 5. + + // Constructors; + triface() : tet(0), loc(0), ver(0) {} + triface(const triface& tface) { + tet = tface.tet; loc = tface.loc; ver = tface.ver; + } + + // Operators; + triface& operator=(const triface& tface) { + tet = tface.tet; loc = tface.loc; ver = tface.ver; + return *this; + } + bool operator==(triface& tface) { + return (tet == tface.tet) && (loc == tface.loc) && (ver == tface.ver); + } + bool operator!=(triface& tface) { + return (tet != tface.tet) || (loc != tface.loc) || (ver != tface.ver); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// The handle data structure: face // +// // +// face is an oriented subface in 3D. The orientation is determined by its // +// vertices. A face includes a pointer to a shell face, and a face version. // +// where face version is an integer number varies from 0 to 5. It was used // +// to represent an oriented edge of face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class face { + + public: + + shellface *sh; + int shver; // Ranges from 0 to 5. + + // Constructors; + face() : sh(0), shver(0) {} + face(const face& sface) { + sh = sface.sh; shver = sface.shver; + } + + // Operators; + face& operator=(const face& sface) { + sh = sface.sh; shver = sface.shver; + return *this; + } + bool operator==(face& sface) { + return (sh == sface.sh) && (shver == sface.shver); + } + bool operator!=(face& sface) { + return (sh != sface.sh) || (shver != sface.shver); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh data type linar-order functions used in list and link data types. // +// See linklist.h for detail description about linar-order functions. // +// // +// compare2points() Compare two point3ds by their x-coordinate, using the // +// y-coordinate as a secondary key, and the z-coordinate // +// if their need. // +// compare2tets() Compare two handles of tetrahedra by thire address. // +// compare2shfaces() Compare two handles of subfaces/subsegments by thire // +// address. // +// // +// Return 0 if they are the same. Return 1 or -1 if they are not the same. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int compare2points(point3d*, point3d*); +int compare2tets(triface*, triface*); +int compare2shfaces(face*, face*); + +/////////////////////////////////////////////////////////////////////////////// +// // +// struct badface3d // +// // +// A queue used to store bad triangular faces. Each face's vertices are // +// stored so that one can check whether a face is still the same. // +// // +/////////////////////////////////////////////////////////////////////////////// + +typedef struct badface3dtype { + triface badfacetet; // A tetrahedron hold the bad face. + face shface; // A bad face. + REAL cent[3]; // The circumcenters' coordinates. + point3d faceorg, facedest, faceapex; // The three vertices. + struct badface3dtype *nextface; // Pointer to next bad face. +} badface3d; + +/////////////////////////////////////////////////////////////////////////////// +// // +// struct badtet // +// // +// A queue used to store bad tetrahedra. Each tetrahedron's vertices are // +// stored so that one can check whether a tetrahedron is still the same. // +// // +/////////////////////////////////////////////////////////////////////////////// + +typedef struct badtettype { + triface btet; // A bad tet. + REAL key; // radius-edge ratio^2. + REAL cent[3]; // The circumcenters' coordinates. + point3d tetorg, tetdest, tetapex, tetoppo; // The four vertices. + struct badtettype *nexttet; // Pointer to next bad tet. +} badtet; + +/////////////////////////////////////////////////////////////////////////////// +// // +// class mesh3d The quality Tetrahedral mesh Generator and Delaunay // +// triangulator implementation class. // +// // +// Tetgen is based on the Delaunay method and incorporates face-swapping // +// techniques. It generates meshed composed of tetrahedral elements. Tetgen // +// generates exact Delaunay tetrahedralization for three-dimensional point // +// sets, generates boundary constrained Delaunay tetrahedralization and // +// quality (conforming) Delaunay tetrahedralizations for three-dimensional // +// Piecewise Linear Complex(PLC). // +// // +/////////////////////////////////////////////////////////////////////////////// + +class mesh3d { + + public: + + // Labels that signify the result of point location. The result of a + // search indicates that the point falls in the interior of a tetra- + // hedra, on a face, on an edge, on a vertex, or outside the mesh. + enum locateresult {INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, OUTSIDE}; + + // Labels that signify the result of site insertion. The result indica- + // tes that the point was inserted with complete success, was inserted + // but encroaches on a segment or a subface, was not inserted because + // it lies on a segment or a subface, or was not inserted because + // another point occupies the same location. + enum insertsiteresult {SUCCESSFUL, FAILED, VIOLATINGEDGE, VIOLATINGFACE, + DUPLICATE}; + + // Labels that signify the result of direction finding. The result + // indicates that a segment connecting the two query points falls + // within the tetrahedron's face, along the left edge of the face + // along the right edge of the face, along the top edge of the face + // or cross the opposite face of the query point. + enum finddirectionresult {LEFTCOLLINEAR, RIGHTCOLLINEAR, TOPCOLLINEAR, + WITHIN, ACROSS}; + + // Labels that signify the result of subface matching. The result of a + // match indicates that the match face is existing in mesh, one edge + // of the match face is missing, or two edge of the match face is mis- + // sing. + enum matchfaceresult {FACEMATCHING, EDGEMISSING, APEXMISSING}; + + // Labels that signify the result of tetrahedral face category. The + // result of face category are used for purposes of face and edge + // swapping. 'T' indicates that the face is transformable, 'N' + // indicates non-transformable. For 'T' faces, the digits are the + // number of tets before and after the transformation. For 'N' faces, + // the digits are the number of tets that would be present for a + // transformable case; a zero as the second digit indicates that the + // case is irrevocably unswappable. + enum facecategory {T23, T32, T22, T44, N32, N44, N40, N30, N20, LOCKED}; + + // Labels that signify two edge ring of a triangle, one(CCW) traversing + // edges in counterclockwise direction and one(CW) travesing edges in + // clockwise direction. + enum {CCW = 0, CW = 1}; + + public: + + // Variables used to allocate and access memory. + memorypool tetrahedrons; + memorypool subfaces; + memorypool subsegs; + memorypool points; + memorypool viri; + memorypool badsegments; + memorypool badfaces; + memorypool badtets; + + // Variables for global used. + REAL xmin, xmax, ymin, ymax, zmin, zmax; + int inpoints, inelements, infacets, holes, regions; + int mesh_dim, nextras, eextras; + long faces, hullsize; + int tetwords, shwords; + int pointmarkindex, point2tetindex; + int highorderindex, elemattribindex, volumeboundindex; + int checksegments; + int readnodefile; + long samples; + unsigned long randomseed; + + // Vraiables for switches. + int poly, smesh; + int refine, quality, varvolume, fixedvolume, regionattrib, convex; + int firstnumber; + int facesout, edgesout, voronoi, neighbors, geomview; + int nopolywritten, nonodewritten, noelewritten, noiterationnum; + int nobound, noholes, nobisect, noexact, noroundoff; + int splitseg, steiner, steinerleft; + int docheck; + int quiet, verbose; + int useshelles, usefliplist; + int shflaws, shsegflaws, tetflaws; + int badelemreport; + REAL minratio, goodratio; + REAL maxvolume; + REAL usertolerance, userubtolerance; + + // A queue used to keep all uncategorized tetrahedra's face, which + // maybe non local optimal. + queue *fliplist; + + // Pointer to the two-dimensional mesh generator. Used in surface + // recover process. + mesh2d *surfmesh; + + // Pointer to a recently visited tetrahedron. Improves point location + // if proximate points are inserted sequentially. + triface recenttet; + + // 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 shell face. Referenced by any tetrahedron, + // or subface that isn't really connected to a shell face at that + // location. + shellface *dummysh; + shellface *dummyshbase; // Keep base address so we can free() it later. + + // Variables used to quality mesh generation. + badface3d *facequefront[2]; + badface3d **facequetail[2]; + badtet *tetquefront[64]; + badtet **tetquetail[64]; + list *uncheckedshseglist; + list *uncheckedshlist; + list *uncheckedtetlist; + // Three lists used for Boywer-Watson point insertion scheme. + list *cavetetlist; + list *caveshlist; + list *cavebdfacelist; + + long inspherecount; // Number of insphere tests performed. + long orient3dcount; // Number of orient3d tests performed. + long cospherecount; // Number of cosphere tested. + long segmentintersectioncount; // Number segment intersection performed. + // Number of flips performed. + long flip_t23s, flip_t32s, flip_t22s, flip_t44s; + + // Vraiables for filenames. + char innodefilename[FILENAMESIZE]; + char inelefilename[FILENAMESIZE]; + char inpolyfilename[FILENAMESIZE]; + char insmeshfilename[FILENAMESIZE]; + char volumefilename[FILENAMESIZE]; + char outnodefilename[FILENAMESIZE]; + char outelefilename[FILENAMESIZE]; + char outpolyfilename[FILENAMESIZE]; + char facefilename[FILENAMESIZE]; + char edgefilename[FILENAMESIZE]; + char neighborfilename[FILENAMESIZE]; + char offfilename[FILENAMESIZE]; + char commandline[FILENAMESIZE]; + + public: + + //////////////////////////////////////////////////////////////////////// + // // + // Mesh manipulation primitives. // + // // + // Each tetrahedron contains four pointers to other tetrahedra, // + // with face locations. Each pointer points not to the first pointer // + // of a tetrahedron, but to one of the first four pointers of a // + // tetrahedron. It is necessary to extract both the tetrahedron // + // itself and the face location. To save memory, We keep both pieces // + // (a tet pointer, a face loction) of information in one pointer, and // + // do not allocate space for face version. To make this possible, I // + // assume that all tetrahedra are aligned to eight-byte boundaries. // + // The three least significant bits (two for face locations, one for // + // viral infection) are masked out to produce the real pointer. The // + // 'decode' primitive below decodes a pointer, extracting a face // + // location, and a pointer to the a tetrahedron. The 'encode' // + // primitive compresses a pointer to a tetrahedron and a face // + // location into a single pointer. // + // // + //////////////////////////////////////////////////////////////////////// + + // Fast lookup tables for mesh manipulation primitives. These tables are + // just like global variables that used by all objects of the class. + + // For enext() primitive, use 'ver' as index. + static int ve[6]; + + // For org(), dest() and apex() primitives, use 'ver' as index. + static int vo[6], vd[6], va[6]; + + // For org(), dest() and apex() primitives, use 'loc' as first index + // and 'ver' as second index. + static int locver2org[4][6]; + static int locver2dest[4][6]; + static int locver2apex[4][6]; + // For oppo() primitives, use 'loc' as index. + static int loc2oppo[4]; + + // For fnext() primitives, use 'loc' as first index and 'ver' as second + // index, return a new 'loc' and new 'ver' in an int array. + // Note: Only valid for face version = 0, 2, 4. + static int locver2nextf[4][6][2]; + + static int plus1mod3[3]; + static int minus1mod3[3]; + + // 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) + + //////////////////////////////////////////////////////////////////////// + // Primitives for tetrahedron // + //////////////////////////////////////////////////////////////////////// + + // decode() converts a pointer to a triface. The location is + // extracted from the two least significant bits of the pointer. + static void decode(tetrahedron ptr, triface& tface) { + tface.loc = (int) ((unsigned long) (ptr) & (unsigned long) 3l); + tface.tet = (tetrahedron *) + ((unsigned long) (ptr) & ~(unsigned long) 7l); + } + + // encode() compresses a triface into a single pointer. It relies on + // the assumption that all tetrahedra are aligned to four-byte + // boundaries, so the two least significant bits of (triface).tet are + // zero. + static tetrahedron encode(triface& tface) { + return (tetrahedron) + ((unsigned long) tface.tet | (unsigned long) tface.loc); + } + + // sym() finds the abutting tetrahedron on the same face. + static void sym(triface& tface1, triface& tface2) { + tetrahedron ptr = tface1.tet[tface1.loc]; + decode(ptr, tface2); + } + + static void symself(triface& tface) { + tetrahedron ptr = tface.tet[tface.loc]; + decode(ptr, tface); + } + + // The bond() and dissolve() primitives are just like the splice() + // primitive of Mucke[2]'s. + + // Bond two tetrahedra together at their faces. + static void bond(triface& tface1, triface& tface2) { + tface1.tet[tface1.loc] = encode(tface2); + tface2.tet[tface2.loc] = encode(tface1); + } + + // Dissolve a bond (from one side). Note that the other tetrahedron + // will still think it's connected to this tetrahedron. Usually, + // however, the other tetrahedron is being deleted entirely, or bonded + // to another tetrahedron, so it doesn't matter. + // Note: 'dummytet' isn't a static member variable of class, that's + // the reason why dissolve() can not be declared with 'static'. + void dissolve(triface& tface) { + tface.tet[tface.loc] = (tetrahedron) dummytet; + } + + // These primitives determine or set the origin, destination, apex or + // opposition of a tetrahedron with respect to 'loc' and 'ver'. + + static void org(triface& tface, point3d& pointptr) { + pointptr = (point3d) tface.tet[locver2org[tface.loc][tface.ver] + 4]; + } + + static point3d org(triface& tface) { + return (point3d) tface.tet[locver2org[tface.loc][tface.ver] + 4]; + } + + static void dest(triface& tface, point3d& pointptr) { + pointptr = (point3d) tface.tet[locver2dest[tface.loc][tface.ver] + 4]; + } + + static point3d dest(triface& tface) { + return (point3d) tface.tet[locver2dest[tface.loc][tface.ver] + 4]; + } + + static void apex(triface& tface, point3d& pointptr) { + pointptr = (point3d) tface.tet[locver2apex[tface.loc][tface.ver] + 4]; + } + + static point3d apex(triface& tface) { + return (point3d) tface.tet[locver2apex[tface.loc][tface.ver] + 4]; + } + + static void oppo(triface& tface, point3d& pointptr) { + pointptr = (point3d) tface.tet[loc2oppo[tface.loc] + 4]; + } + + static point3d oppo(triface& tface) { + return (point3d) tface.tet[loc2oppo[tface.loc] + 4]; + } + + static void setorg(triface& tface, point3d pointptr) { + tface.tet[locver2org[tface.loc][tface.ver] + 4] = (tetrahedron) pointptr; + } + + static void setdest(triface& tface, point3d pointptr) { + tface.tet[locver2dest[tface.loc][tface.ver] + 4] = (tetrahedron) pointptr; + } + + static void setapex(triface& tface, point3d pointptr) { + tface.tet[locver2apex[tface.loc][tface.ver] + 4] = (tetrahedron) pointptr; + } + + static void setoppo(triface& tface, point3d pointptr) { + tface.tet[loc2oppo[tface.loc] + 4] = (tetrahedron) pointptr; + } + + static void setvertices2null(triface& tface) { + tface.tet[4] = (tetrahedron) NULL; + tface.tet[5] = (tetrahedron) NULL; + tface.tet[6] = (tetrahedron) NULL; + tface.tet[7] = (tetrahedron) NULL; + } + + // These primitives were drived from Mucke[2]'s triangle-based 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 + // direction of the same undirected edge of a triangular face. + // e0.sym() = e1 and vice versa. + static void esym(triface& tface1, triface& tface2) { + tface2.tet = tface1.tet; + tface2.loc = tface1.loc; + tface2.ver = tface1.ver + (EdgeRing(tface1.ver) ? -1 : 1); + } + + static void esymself(triface& tface) { + tface.ver += (EdgeRing(tface.ver) ? -1 : 1); + } + + // If e0 and e1 are both in the same edge ring of a triangular face, + // e1 = e0.enext(). Then e1 is the successor of e0. + static void enext(triface& tface1, triface& tface2) { + tface2.tet = tface1.tet; + tface2.loc = tface1.loc; + tface2.ver = ve[tface1.ver]; + } + + static void enextself(triface& tface) { + tface.ver = ve[tface.ver]; + } + + // enext2() is equal to e2 = e0.enext().enext() + static void enext2(triface& tface1, triface& tface2) { + tface2.tet = tface1.tet; + tface2.loc = tface1.loc; + tface2.ver = ve[ve[tface1.ver]]; + } + + static void enext2self(triface& tface) { + tface.ver = ve[ve[tface.ver]]; + } + + // If f0 and f1 are both in the same triangle ring of a triangular face, + // f1 = f0.fnext(), Then f1 is the successor of f0. Return true if f1 + // exist. Return false if f1 not exist(f0 is a boundary face). + #define fnext(triface1, triface2) \ + getnextface(&(triface1), &(triface2)) + + #define fnextself(triface) \ + getnextface(&(triface), NULL) + + // enextfnext() and enext2fnext() are combination primitives of enext() + // and fnext(). + #define enextfnext(triface1, triface2) \ + enext(triface1, triface2); \ + fnextself(triface2) + + #define enextfnextself(triface) \ + enextself(triface); \ + fnextself(triface) + + #define enext2fnext(triface1, triface2) \ + enext2(triface1, triface2); \ + fnextself(triface2) + + #define enext2fnextself(triface) \ + enext2self(triface); \ + fnextself(triface) + + // Primitives to infect or cure a tetrahedron with the virus. These rely + // on the assumption that all tetrahedron are aligned to eight-byte + // boundaries. + static void infect(triface& tface) { + tface.tet[0] = (tetrahedron) + ((unsigned long) tface.tet[0] | (unsigned long) 4l); + } + + static void uninfect(triface& tface) { + tface.tet[0] = (tetrahedron) + ((unsigned long) tface.tet[0] & ~ (unsigned long) 4l); + } + + // Test a tetrahedron for viral infection. + static bool infected(triface& tface) { + return (((unsigned long) tface.tet[0] & (unsigned long) 4l) != 0); + } + + // Check or set a tetrahedron's attributes. + REAL elemattribute(tetrahedron* ptr, int attnum) { + return ((REAL *) (ptr))[elemattribindex + attnum]; + } + + void setelemattribute(tetrahedron* ptr, int attnum, REAL value) { + ((REAL *) (ptr))[elemattribindex + attnum] = value; + } + + // Check or set a tetrahedron's maximum volume bound. + REAL volumebound(tetrahedron* ptr) { + return ((REAL *) (ptr))[volumeboundindex]; + } + + void setvolumebound(tetrahedron* ptr, REAL value) { + ((REAL *) (ptr))[volumeboundindex] = value; + } + + //////////////////////////////////////////////////////////////////////// + // Primitives for shellface // + //////////////////////////////////////////////////////////////////////// + + // sdecode() converts a pointer to an oriented shell face. The face + // version is extracted from the least three significant bit of the + // pointer. The three least significant bits are masked out to + // produce the real pointer. + static void sdecode(shellface sptr, face& sface) { + sface.shver = (int) ((unsigned long) (sptr) & (unsigned long) 7l); + sface.sh = (shellface *) + ((unsigned long) (sptr) & ~ (unsigned long) 7l); + } + + // sencode() compresses an oriented shell face into a single pointer. + // It relies on the assumption that all shell faces are aligned to + // eight-byte boundaries, so the least three significant bit of + // (face).sh is zero. + static shellface sencode(face& sface) { + return (shellface) + ((unsigned long) sface.sh | (unsigned long) sface.shver); + } + + // spivot() finds the other shell face (from the same face) that shares + // the same edge. + static void spivot(face& sface1, face& sface2) { + shellface sptr = sface1.sh[Orient(sface1.shver)]; + sdecode(sptr, sface2); + } + + static void spivotself(face& sface) { + shellface sptr = sface.sh[Orient(sface.shver)]; + sdecode(sptr, sface); + } + + // Bond two shell faces together. + static void sbond(face& sface1, face& sface2) { + sface1.sh[Orient(sface1.shver)] = sencode(sface2); + sface2.sh[Orient(sface2.shver)] = sencode(sface1); + } + + // Dissolve a shell face bond (from one side). Note that the other + // shell face will still think it's connected to this shell face. + // Note: 'dummysh' isn't a static member variable of class, that's + // the reason why sdissolve() can not be declared with 'static'. + void sdissolve(face& sface) { + sface.sh[Orient(sface.shver)] = (shellface) dummysh; + } + + // These primitives determine or set the origin, destination, or apex + // of a shell face with respect to current face version. + + static void sorg(face& sface, point3d& pointptr) { + pointptr = (point3d) sface.sh[3 + vo[sface.shver]]; + } + + static point3d sorg(face& sface) { + return (point3d) sface.sh[3 + vo[sface.shver]]; + } + + static void sdest(face& sface, point3d& pointptr) { + pointptr = (point3d) sface.sh[3 + vd[sface.shver]]; + } + + static point3d sdest(face& sface) { + return (point3d) sface.sh[3 + vd[sface.shver]]; + } + + static void sapex(face& sface, point3d& pointptr) { + pointptr = (point3d) sface.sh[3 + va[sface.shver]]; + } + + static point3d sapex(face& sface) { + return (point3d) sface.sh[3 + va[sface.shver]]; + } + + static void setsorg(face& sface, point3d pointptr) { + sface.sh[3 + vo[sface.shver]] = (shellface) pointptr; + } + + static void setsdest(face& sface, point3d pointptr) { + sface.sh[3 + vd[sface.shver]] = (shellface) pointptr; + } + + static void setsapex(face& sface, point3d pointptr) { + sface.sh[3 + va[sface.shver]] = (shellface) pointptr; + } + + // These primitives were drived from Mucke[2]'s triangle-based data + // structure to change face-edge relation in a tetrahedron (sesym, + // senext and senext2). + + static void sesym(face& sface1, face& sface2) { + sface2.sh = sface1.sh; + sface2.shver = sface1.shver + (EdgeRing(sface1.shver) ? -1 : 1); + } + + static void sesymself(face& sface) { + sface.shver += (EdgeRing(sface.shver) ? -1 : 1); + } + + static void senext(face& sface1, face& sface2) { + sface2.sh = sface1.sh; + sface2.shver = ve[sface1.shver]; + } + + static void senextself(face& sface) { sface.shver = ve[sface.shver]; } + + static void senext2(face& sface1, face& sface2) { + sface2.sh = sface1.sh; + sface2.shver = ve[ve[sface1.shver]]; + } + + static void senext2self(face& sface) { + sface.shver = ve[ve[sface.shver]]; + } + + // These primitives read or set a shell marker. Shell markers are used + // to hold user boundary information. + + static int mark(face& sface) { return (* (int *) (sface.sh + 11)); } + + static void setmark(face& sface, int value) { + * (int *) (sface.sh + 11) = value; + } + + // Primitives to infect or cure a shell face with the virus. These rely + // on the assumption that all shell face are aligned to eight-byte + // boundaries. + static void sinfect(face& sface) { + sface.sh[10] = (shellface) + ((unsigned long) sface.sh[10] | (unsigned long) 4l); + } + + static void suninfect(face& sface) { + sface.sh[10] = (shellface) + ((unsigned long) sface.sh[10] & ~ (unsigned long) 4l); + } + + // Test a shell face for viral infection. + static bool sinfected(face& sface) { + return (((unsigned long) sface.sh[10] & (unsigned long) 4l) != 0); + } + + //////////////////////////////////////////////////////////////////////// + // Primitives for interacting tetrahedra and subfaces // + //////////////////////////////////////////////////////////////////////// + + // tspivot() finds a subface abutting on this tetrahdera. + static void tspivot(triface& tface, face& sface) { + shellface sptr = (shellface) tface.tet[8 + tface.loc]; + sdecode(sptr, sface); + } + + // stpivot() finds a tetrahedron abutting a subface. + static void stpivot(face& sface, triface& tface) { + tetrahedron ptr = (tetrahedron) sface.sh[6 + EdgeRing(sface.shver)]; + decode(ptr, tface); + } + + // tsbond() bond a tetrahedron to a subface. + static void tsbond(triface& tface, face& sface) { + tface.tet[8 + tface.loc] = (tetrahedron) sencode(sface); + sface.sh[6 + EdgeRing(sface.shver)] = (shellface) encode(tface); + } + + // tsdissolve() dissolve a bond (from the tetrahedron side). + void tsdissolve(triface& tface) { + tface.tet[8 + tface.loc] = (tetrahedron) dummysh; + } + + // stdissolve() dissolve a bond (from the subface side). + void stdissolve(face& sface) { + sface.sh[6 + EdgeRing(sface.shver)] = (shellface) dummytet; + } + + //////////////////////////////////////////////////////////////////////// + // Primitives for interacting subfaces and subsegments // + //////////////////////////////////////////////////////////////////////// + + // sspivot() finds a subsegment abutting a subface. + static void sspivot(face& sface, face& edge) { + shellface sptr = (shellface) sface.sh[8 + Orient(sface.shver)]; + sdecode(sptr, edge); + } + + // ssbond() bond a subface to a subsegment. + static void ssbond(face& sface, face& edge) { + sface.sh[8 + Orient(sface.shver)] = sencode(edge); + edge.sh[0] = sencode(sface); + } + + // ssdisolve() dissolve a bond (from the subface side) + void ssdissolve(face& sface) { + sface.sh[8 + Orient(sface.shver)] = (shellface) dummysh; + } + + //////////////////////////////////////////////////////////////////////// + // Primitives for point3d // + //////////////////////////////////////////////////////////////////////// + + int pointmark(point3d pt) { return ((int *) (pt))[pointmarkindex]; } + + void setpointmark(point3d pt, int value) { + ((int *) (pt))[pointmarkindex] = value; + } + + tetrahedron point2tet(point3d pt) { + return ((tetrahedron *) (pt))[point2tetindex]; + } + + void setpoint2tet(point3d pt, tetrahedron value) { + ((tetrahedron *) (pt))[point2tetindex] = value; + } + + //////////////////////////////////////////////////////////////////////// + // Advanced primitives // + //////////////////////////////////////////////////////////////////////// + + // Implement the fnext(), fnextself() primitives of tetrahedron. + bool getnextface(triface*, triface*); + + // Finds a subsegment abutting on this tetrahdera. + void tsspivot(triface*, face*); + + // Finds a tetrahedron abutting a subsegment. + void sstpivot(face*, triface*); + + //////////////////////////////////////////////////////////////////////// + // Useful auxiliary primitives // + //////////////////////////////////////////////////////////////////////// + + // Find and set version to indicate the directed edge. + static void findversion(triface*, point3d, point3d, int symflag = 1); + static void findversion(triface*, triface*, int symflag = 1); + static void findversion(triface*, face*, int symflag = 1); + static void findversion(face*, point3d, point3d, int symflag = 1); + static void findversion(face*, triface*, int symflag = 1); + static void findversion(face*, face*, int symflag = 1); + + // Find and set the version to indicate the desired point. + bool findorg(triface*, point3d); + bool findorg(face*, point3d); + + // Adjusts edge version(ver) to corresponding edge ring if necessary. + // Here direction is only valid for 0(CCW) or 1(CW). + static void adjustedgering(triface& tface, int direction) { + if (EdgeRing(tface.ver) != direction) { + esymself(tface); + } + } + + // Returns true if adjoining tetrahedron exist. + // Note: 'dummytet' is not a static member variable. + bool issymexist(triface* tface) { + tetrahedron *ptr = (tetrahedron *) + ((unsigned long)(tface->tet[tface->loc]) & ~(unsigned long)3l); + return ptr != dummytet; + } + + // Returns true if this tetrahedron is dealloced. + static bool isdead(triface* tface) { + if (tface->tet == (tetrahedron*) NULL) { + return true; + } else { + return tface->tet[4] == (tetrahedron) NULL; + } + } + + // Returns true if this shellface is dealloced. + static bool isdead(face* sface) { + if (sface->sh == (shellface*) NULL) { + return true; + } else { + return sface->sh[3] == (shellface) NULL; + } + } + + // Test if the subface is a non-solid subface. Every non-solid subface + // is marked with a special flag NONSOLIDFLAG. + static bool isnonsolid(face& sface) { + return mark(sface) == NONSOLIDFLAG; + } + + // Return true if the point is one of vertices of input triangular face. + static bool isfacehaspoint(triface* tface, point3d testpoint) { + return ((org(*tface) == testpoint) + || (dest(*tface) == testpoint) + || (apex(*tface) == testpoint)); + } + + // Compare the coordinates of two points to see if they are coincident. + // Return true if they are coincident, otherwise return false; + bool issamepoint(point3d p1, point3d p2) { + return (fabs(p1[0] - p2[0]) <= usertolerance) + && (fabs(p1[1] - p2[1]) <= usertolerance) + && (fabs(p1[2] - p2[2]) <= usertolerance); + } + + // Returns true if the edge of tetrahedron(specified by loc and ver) + // is a ridge in the mesh. + bool isridge(triface*); + + // Test if there exist any segment shars with input segment's origin. + // Return true if exist such segment, otherwise return false. + bool isexistincidentseg(face*); + + // Compare the vertices of two faces/edges to see if they are the same + // face/edge. The inputs are handles of two tetrahedra. Only return + // 0 (same) or 1 (diffrent). The two primitives mainly used as list + // or link's linear order functions. + static int issameface(triface*, triface*); + static int issameedge(triface*, triface*); + + // For debuging, print out the details of a tetrahedron/shellfacet + // handle to standard output. + void dump(triface*); + void dump(face*); + + //////////////////////////////////////////////////////////////////////// + // End of mesh manipulation primitives // + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // Commonly used vector operations // + //////////////////////////////////////////////////////////////////////// + + // Assign3D(), Return: vres = va. + static void Assign3D(REAL* va, REAL* vres) { + vres[0] = va[0]; vres[1] = va[1]; vres[2] = va[2]; + } + + // Sub3D(), Return: vres = va - vb. + static void Sub3D(REAL* va, REAL* vb, REAL* vres) { + vres[0] = va[0] - vb[0]; + vres[1] = va[1] - vb[1]; + vres[2] = va[2] - vb[2]; + } + + // Scale3D(), Return: vres = vres * scale. + static void Scale3D(REAL* vres, REAL scale) { + vres[0] *= scale; vres[1] *= scale; vres[2] *= scale; + } + + // Dot3D(), Return: va dot vb. + static REAL Dot3D(REAL* va, REAL* vb) { + return (va[0] * vb[0] + va[1] * vb[1] + va[2] * vb[2]); + } + + // Cross3D(), Return: vres = va cross vb. + static void Cross3D(REAL* va, REAL* vb, REAL* vres) { + vres[0] = va[1] * vb[2] - va[2] * vb[1]; + vres[1] = va[2] * vb[0] - va[0] * vb[2]; + vres[2] = va[0] * vb[1] - va[1] * vb[0]; + } + + // Mag3D(), Return: Mod of va. + static REAL Mag3D(REAL* va) { + return sqrt(va[0] * va[0] + va[1] * va[1] + va[2] * va[2]); + } + + // Dist3D(), Return the Euler distance of va and vb. + static REAL Dist3D(REAL* va, REAL* vb) { + return sqrt((va[0] - vb[0]) * (va[0] - vb[0]) + + (va[1] - vb[1]) * (va[1] - vb[1]) + + (va[2] - vb[2]) * (va[2] - vb[2])); + } + + // Normal3D(), Compute the vector normal for a set of three points. + static void Normal3D(REAL* va, REAL* vb, REAL* vc, REAL* vnormal) { + REAL tempb[3], tempc[3]; + Sub3D(vb, va, tempb); + Sub3D(vc, va, tempc); + Cross3D(tempb, tempc, vnormal); + } + + // Normalize3D(), Return: Unit vector of va. + static void Normalize3D(REAL* va) { + REAL invmag = 1. / Mag3D(va); + Scale3D(va, invmag); + } + + static void Swap3D(REAL* va, REAL* vb) { + REAL temp; + temp = vb[0]; vb[0] = va[0]; va[0] = temp; + temp = vb[1]; vb[1] = va[1]; va[1] = temp; + temp = vb[2]; vb[2] = va[2]; va[2] = temp; + } + + //////////////////////////////////////////////////////////////////////// + // End of commonly used vector operations // + //////////////////////////////////////////////////////////////////////// + + // Memory management routines + void dummyinit(int, int); + void initializepointpool(); + void initializetetshpools(); + void tetrahedrondealloc(tetrahedron*); + tetrahedron *tetrahedrontraverse(); + void shellfacedealloc(memorypool*, shellface*); + shellface *shellfacetraverse(memorypool*); + void badsegmentdealloc(badface3d*); + badface3d *badsegmenttraverse(); + void pointdealloc(point3d); + point3d pointtraverse(); + point3d getpoint(int number); + // tetrahedron, subface and subsegment construct routines + void maketetrahedron(triface*); + void makeshellface(memorypool*, face*); + + // Geometric primitives. + inline bool iszero(const REAL); + int iorient3d(point3d, point3d, point3d, point3d); + int iinsphere(point3d, point3d, point3d, point3d, point3d); + int iinsphere(triface*, point3d); + bool isbelowplane(point3d, point3d, point3d, point3d); + bool isbelowplane(triface*, point3d); + bool isaboveplane(point3d, point3d, point3d, point3d); + bool isaboveplane(triface*, point3d); + bool iscoplane(point3d, point3d, point3d, point3d); + bool iscoplane(triface*, point3d); + bool iscoline(point3d, point3d, point3d); + + // Geometric functions. + void projontoface(point3d, point3d, point3d, point3d); + void circumcenter(point3d, point3d, point3d, point3d); + void circumcenter(point3d, point3d, point3d, point3d, point3d); + REAL tetvolume(point3d, point3d, point3d, point3d); + REAL facedihedral(point3d, point3d, point3d, point3d, point3d, point3d); + void tetalldihedral(point3d, point3d, point3d, point3d, REAL[6]); + + // Point location routines. + // (Fast Randomized Point Location Algorithm of Mucke, Issac and Zhu.) + unsigned long randomnation(unsigned int); + void makepointmap(); + REAL distance(triface*, point3d); + enum locateresult isintet(point3d, point3d, point3d, point3d, point3d); + enum locateresult iscoplanarintri(point3d, triface*); + enum locateresult preciselocate(point3d, triface*); + enum locateresult locate(point3d, triface*); + + // Mesh transformation routines. + enum facecategory categorizeface(triface&); + bool querydoswap(triface&); + // bool querydoswap(triface&, enum facecategory); + void preservesubsegments(face&, triface&); + void enqueuefliplist(triface&); + bool dequeuefliplist(triface&); + int flip23(triface&); + int flip32(triface&); + int flip22(triface&); + int flip44(triface&); + int flip(triface&); + + // Insert/Delete point routines. + enum insertsiteresult insertsite(point3d, triface*, face*, face*); + int insertonedge(point3d, triface*, face*); + int insertonface(point3d, triface*, face*); + int insertininterior(point3d, triface*); + void insertsubface(triface*, int, int); + void insertsubsegment(triface*, int); + int deletesite(triface*); + + // Giftwrapping Algorithm + int is_face_tet_inter_0(triface*, point3d, point3d, point3d, point3d); + int is_face_tet_inter_1(triface*, point3d, point3d, point3d, point3d); + int is_face_tet_inter_2(triface*, point3d, point3d, point3d, point3d); + int getgiftface(list*, int, int*, list*); + int getgiftpoint(triface*, list*, int, point3d*, int*, list*, list*); + int giftwrapping(int, triface*, int, point3d*, REAL*, REAL, list*); + + // Delaunay tetrahedrization construct routines. + // (Randomized Incremental Flip Algorithm.) + void construct_initial_tetra(queue*); + void collect_visibles(point3d, triface*, triface*, link*); + int dofliplist(); + long rand_incr_flip_delaunay(); + long delaunay(); + + // Segment/facet (boundary) constrained routines. + enum finddirectionresult finddirection(triface*, point3d); + void segmentintersection(triface*, face*, point3d); + int scoutsegment(triface*, point3d, int); + void conformingedge(point3d, point3d, int); + void insertsegment(point3d, point3d, int); + void getsteinersinseg(point3d, point3d, list*); + int getdiagonalapexindex(struct triangulateio*, int, int); + int getneighbourtriedge(struct triangulateio*, int, int, int*, int*); + int swapdiagonal(struct triangulateio*, int, int, int); + enum matchfaceresult matchface(point3d, point3d, point3d, triface*, int); + int insertfieldpoint(int, triface*, int, point3d*, list*); + int recoverface(list*, triangulateio*, int, int); + void insertfacet(list*, list*, int, int); + int formskeleton(FILE*); + + // Carving out holes and concavities routines. + void infecthull(); + void plague(); + void regionplague(REAL, REAL); + void carveholes(REAL*, int, REAL*, int); + + // Mesh quality testing routines. + void checkquality(triface*); + int checkedge4encroach(face*, point3d testpoint = NULL); + int checkface4encroach(face*, point3d testpoint = NULL); + void testtetrahedron(triface*); + void enqueuebadface(face*, point3d, int); + badface3d *dequeuebadface(); + void enqueuebadtet(triface*, REAL, point3d, point3d, point3d, point3d, + point3d); + badtet *dequeuebadtet(); + // Mesh quality maintenance routines. + void tallyencsegs(); + void tallyencfaces(); + void tallytets(); + void repairencsegs(); + void repairencfaces(); + void splittet(badtet*); + void enforcequality(); + + // File I/O routines. + char *readline(char*, FILE*, char*); + char *findfield(char*); + void readnodes(FILE**); + void readholes(FILE*, REAL**, int*, REAL**, int*); + void numbernodes(int myfirstnumber = 0); + void outnodes(); + void outelems(); + void outfaces(int hull = 0); + void outedges(); + void outneighbors(); + void outoff(); + void outelems2gid(); + void outfaces2gid(int hull = 0); + void outedges2gid(); + void outelems2fa(); + + // Debug routines. + void dumpgifts(char*, list*, int, point3d*, int*); + void dumplist(char*, list*, int, int); + void dumpallbadelems(char*); + + void internalerror(); + void precisionerror(); + void recovererror(); + + void checkmesh(); + void checkdelaunay(); + void checkshells(); + void statistics(); + void quality_statistics(); + void syntax(); + + void meshinit(); + void meshdeinit(); + void parsecommandline(int, char**); + + public: + + mesh3d() { meshinit(); } + ~mesh3d() { meshdeinit(); } + + // General mesh construction. + void triangulate(int, char**); +}; + +#endif // #ifndef tetlibH diff --git a/Tetgen/tetmain.cpp b/Tetgen/tetmain.cpp new file mode 100644 index 0000000000..ded9d1649a --- /dev/null +++ b/Tetgen/tetmain.cpp @@ -0,0 +1,11 @@ + +#include "tetlib.h" + +int main(int argc, char* argv[]) +{ + mesh3d mymesh; + + mymesh.triangulate(argc, argv); + + return 0; +} diff --git a/Tetgen/trilib.cpp b/Tetgen/trilib.cpp new file mode 100644 index 0000000000..b4cab2f132 --- /dev/null +++ b/Tetgen/trilib.cpp @@ -0,0 +1,5602 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// trilib.cpp Implementation file for two-dimensional mesh generator. // +// // +// Tetgen Version 1.0 beta // +// July, 2001 // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +// This file with it's couple file trilib.h are modified version of the // +// triangle program of Jonathan Richard Shewchuk, which is public available // +// from the following Web page with its license(see trilib.h): // +// // +// http://www.cs.cmu.edu/~quake/triangle.html // +// // +// Please see trilib.h for description of how to use this modified codes // +// of triangle. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/*****************************************************************************/ +/* */ +/* Traingle program license: */ +/* */ +/* Triangle */ +/* A Two-Dimensional Quality Mesh Generator */ +/* and Delaunay Triangulator. */ +/* Version 1.3 */ +/* */ +/* Copyright 1996 */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This program may be freely redistributed under the condition that the */ +/* copyright notices (including this entire header and the copyright */ +/* notice printed when the `-h' switch is selected) are not removed, and */ +/* no compensation is received. Private, research, and institutional */ +/* use is free. You may distribute modified versions of this code UNDER */ +/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */ +/* SAME FILE 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 as */ +/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */ +/* WITH THE AUTHOR. (If you are not directly supplying this code to a */ +/* customer, and you are instead telling them how they can obtain it for */ +/* free, then you are not required to make any arrangement with me.) */ +/* */ +/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ +/* whatsoever. This code is provided "as-is". Use at your own risk. */ +/* */ +/*****************************************************************************/ + +#include "trilib.h" + +/********* struct triangulateio routines begin here *********/ +/** **/ +/** **/ + +void triangulateioinit(struct triangulateio* io) +{ + io->pointlist = NULL; + io->pointattributelist = NULL; + io->pointmarkerlist = NULL; + io->numberofpoints = 0; + io->numberofpointattributes = 0; + + io->trianglelist = NULL; + io->triangleattributelist = NULL; + io->trianglearealist = NULL; + io->neighborlist = NULL; + io->numberoftriangles = 0; + io->numberofcorners = 0; + io->numberoftriangleattributes = 0; + + io->segmentlist = NULL; + io->segmentmarkerlist = NULL; + io->numberofsegments = 0; + + io->holelist = NULL; + io->numberofholes = 0; + + io->regionlist = NULL; + io->numberofregions = 0; + + io->edgelist = NULL; + io->edgemarkerlist = NULL; + io->normlist = NULL; + io->numberofedges = 0; +} + +void triangulateiodeinit(struct triangulateio* io) +{ + if (io->numberofpoints && io->pointlist) { + delete [] io->pointlist; + io->pointlist = NULL; + io->numberofpoints = 0; + } + if (io->numberofpointattributes && io->pointattributelist) { + delete [] io->pointattributelist; + io->pointattributelist = NULL; + io->numberofpointattributes = 0; + } + if (io->pointmarkerlist) { + delete [] io->pointmarkerlist; + io->pointmarkerlist = NULL; + } + + if (io->numberoftriangles && io->trianglelist) { + delete [] io->trianglelist; + io->trianglelist = NULL; + io->numberoftriangles = 0; + io->numberofcorners = 0; + } + if (io->numberoftriangleattributes && io->triangleattributelist) { + delete [] io->triangleattributelist; + io->triangleattributelist = NULL; + io->numberoftriangleattributes = 0; + } + if (io->trianglearealist) { + delete [] io->trianglearealist; + io->trianglearealist = NULL; + } + if (io->neighborlist) { + delete [] io->neighborlist; + io->neighborlist = NULL; + } + + if (io->numberofsegments && io->segmentlist) { + delete [] io->segmentlist; + io->segmentlist = NULL; + io->numberofsegments = 0; + } + if (io->segmentmarkerlist) { + delete [] io->segmentmarkerlist; + io->segmentmarkerlist = NULL; + } + + if (io->numberofholes && io->holelist) { + delete [] io->holelist; + io->holelist = NULL; + io->numberofholes = 0; + } + + if (io->numberofregions && io->regionlist) { + delete [] io->regionlist; + io->regionlist = NULL; + io->numberofregions = 0; + } + + if (io->numberofedges && io->edgelist) { + delete [] io->edgelist; + io->edgelist = NULL; + io->numberofedges = 0; + } + if (io->edgemarkerlist) { + delete [] io->edgemarkerlist; + io->edgemarkerlist = NULL; + } + if (io->normlist) { + delete [] io->normlist; + io->normlist = NULL; + } +} + +void triangulateioreport(struct triangulateio *io, int markers, + int reporttriangles, int reportneighbors, int reportsegments, + int reportedges, int reportnorms) +{ + int i, j; + + if (io->numberofpoints && io->pointlist) { + for (i = 0; i < io->numberofpoints; i++) { + printf("Point %4d:", i + 1); + for (j = 0; j < 2; j++) { + printf(" %.6g", io->pointlist[i * 2 + j]); + } + if (io->numberofpointattributes > 0) { + printf(" attributes"); + } + for (j = 0; j < io->numberofpointattributes; j++) { + printf(" %.6g", + io->pointattributelist[i * io->numberofpointattributes + j]); + } + if (markers) { + printf(" marker %d\n", io->pointmarkerlist[i]); + } else { + printf("\n"); + } + } + printf("\n"); + } + + if (reporttriangles || reportneighbors) { + for (i = 0; i < io->numberoftriangles; i++) { + if (reporttriangles) { + printf("Triangle %4d points:", i + 1); + for (j = 0; j < io->numberofcorners; j++) { + printf(" %4d", io->trianglelist[i * io->numberofcorners + j] + 1); + } + if (io->numberoftriangleattributes > 0) { + printf(" attributes"); + } + for (j = 0; j < io->numberoftriangleattributes; j++) { + printf(" %.6g", io->triangleattributelist[i * + io->numberoftriangleattributes + j]); + } + printf("\n"); + } + if (reportneighbors) { + printf("Triangle %4d neighbors:", i + 1); + for (j = 0; j < 3; j++) { + if (io->neighborlist[i * 3 + j] < 0) { + printf(" %4d", io->neighborlist[i * 3 + j]); + } else { + printf(" %4d", io->neighborlist[i * 3 + j] + 1); + } + } + printf("\n"); + } + } + printf("\n"); + } + + if (reportsegments) { + for (i = 0; i < io->numberofsegments; i++) { + printf("Segment %4d points:", i + 1); + for (j = 0; j < 2; j++) { + printf(" %4d", io->segmentlist[i * 2 + j] + 1); + } + if (markers) { + printf(" marker %d\n", io->segmentmarkerlist[i]); + } else { + printf("\n"); + } + } + printf("\n"); + } + + if (reportedges) { + for (i = 0; i < io->numberofedges; i++) { + printf("Edge %4d points:", i + 1); + for (j = 0; j < 2; j++) { + printf(" %4d", io->edgelist[i * 2 + j] + 1); + } + if (reportnorms && (io->edgelist[i * 2 + 1] == -1)) { + for (j = 0; j < 2; j++) { + printf(" %.6g", io->normlist[i * 2 + j]); + } + } + if (markers) { + printf(" marker %d\n", io->edgemarkerlist[i]); + } else { + printf("\n"); + } + } + printf("\n"); + } +} + +/** **/ +/** **/ +/********* struct triangulateio routines end here *********/ + +/********* Mesh manipulation primitives begin here *********/ +/** **/ +/** **/ + +/* Fast lookup arrays to speed some of the mesh manipulation primitives. */ + +int mesh2d::plus1mod3[3] = {1, 2, 0}; +int mesh2d::minus1mod3[3] = {2, 0, 1}; + +/********* Primitives for triangles *********/ +/* */ +/* */ + +/* decode() converts a pointer to an oriented triangle. The orientation is */ +/* extracted from the two least significant bits of the pointer. */ + +#define decode(ptr, triedge) \ + (triedge).orient = (int) ((unsigned long) (ptr) & (unsigned long) 3l); \ + (triedge).tri = (triangle *) \ + ((unsigned long) (ptr) ^ (unsigned long) (triedge).orient) + +/* encode() compresses an oriented triangle into a single pointer. It */ +/* relies on the assumption that all triangles are aligned to four-byte */ +/* boundaries, so the two least significant bits of (triedge).tri are zero.*/ + +#define encode(triedge) \ + (triangle) ((unsigned long) (triedge).tri | (unsigned long) (triedge).orient) + +/* The following edge manipulation primitives are all described by Guibas */ +/* and Stolfi. However, they use an edge-based data structure, whereas I */ +/* am using a triangle-based data structure. */ + +/* sym() finds the abutting triangle, on the same edge. Note that the */ +/* edge direction is necessarily reversed, because triangle/edge handles */ +/* are always directed counterclockwise around the triangle. */ + +#define sym(triedge1, triedge2) \ + ptr = (triedge1).tri[(triedge1).orient]; \ + decode(ptr, triedge2); + +#define symself(triedge) \ + ptr = (triedge).tri[(triedge).orient]; \ + decode(ptr, triedge); + +/* lnext() finds the next edge (counterclockwise) of a triangle. */ + +#define lnext(triedge1, triedge2) \ + (triedge2).tri = (triedge1).tri; \ + (triedge2).orient = plus1mod3[(triedge1).orient] + +#define lnextself(triedge) \ + (triedge).orient = plus1mod3[(triedge).orient] + +/* lprev() finds the previous edge (clockwise) of a triangle. */ + +#define lprev(triedge1, triedge2) \ + (triedge2).tri = (triedge1).tri; \ + (triedge2).orient = minus1mod3[(triedge1).orient] + +#define lprevself(triedge) \ + (triedge).orient = minus1mod3[(triedge).orient] + +/* onext() spins counterclockwise around a point; that is, it finds the next */ +/* edge with the same origin in the counterclockwise direction. This edge */ +/* will be part of a different triangle. */ + +#define onext(triedge1, triedge2) \ + lprev(triedge1, triedge2); \ + symself(triedge2); + +#define onextself(triedge) \ + lprevself(triedge); \ + symself(triedge); + +/* oprev() spins clockwise around a point; that is, it finds the next edge */ +/* with the same origin in the clockwise direction. This edge will be */ +/* part of a different triangle. */ + +#define oprev(triedge1, triedge2) \ + sym(triedge1, triedge2); \ + lnextself(triedge2); + +#define oprevself(triedge) \ + symself(triedge); \ + lnextself(triedge); + +/* dnext() spins counterclockwise around a point; that is, it finds the next */ +/* edge with the same destination in the counterclockwise direction. This */ +/* edge will be part of a different triangle. */ + +#define dnext(triedge1, triedge2) \ + sym(triedge1, triedge2); \ + lprevself(triedge2); + +#define dnextself(triedge) \ + symself(triedge); \ + lprevself(triedge); + +/* dprev() spins clockwise around a point; that is, it finds the next edge */ +/* with the same destination in the clockwise direction. This edge will */ +/* be part of a different triangle. */ + +#define dprev(triedge1, triedge2) \ + lnext(triedge1, triedge2); \ + symself(triedge2); + +#define dprevself(triedge) \ + lnextself(triedge); \ + symself(triedge); + +/* rnext() moves one edge counterclockwise about the adjacent triangle. */ +/* (It's best understood by reading Guibas and Stolfi. It involves */ +/* changing triangles twice.) */ + +#define rnext(triedge1, triedge2) \ + sym(triedge1, triedge2); \ + lnextself(triedge2); \ + symself(triedge2); + +#define rnextself(triedge) \ + symself(triedge); \ + lnextself(triedge); \ + symself(triedge); + +/* rnext() moves one edge clockwise about the adjacent triangle. */ +/* (It's best understood by reading Guibas and Stolfi. It involves */ +/* changing triangles twice.) */ + +#define rprev(triedge1, triedge2) \ + sym(triedge1, triedge2); \ + lprevself(triedge2); \ + symself(triedge2); + +#define rprevself(triedge) \ + symself(triedge); \ + lprevself(triedge); \ + symself(triedge); + +/* These primitives determine or set the origin, destination, or apex of a */ +/* triangle. */ + +#define org(triedge, pointptr) \ + pointptr = (point) (triedge).tri[plus1mod3[(triedge).orient] + 3] + +#define dest(triedge, pointptr) \ + pointptr = (point) (triedge).tri[minus1mod3[(triedge).orient] + 3] + +#define apex(triedge, pointptr) \ + pointptr = (point) (triedge).tri[(triedge).orient + 3] + +#define setorg(triedge, pointptr) \ + (triedge).tri[plus1mod3[(triedge).orient] + 3] = (triangle) pointptr + +#define setdest(triedge, pointptr) \ + (triedge).tri[minus1mod3[(triedge).orient] + 3] = (triangle) pointptr + +#define setapex(triedge, pointptr) \ + (triedge).tri[(triedge).orient + 3] = (triangle) pointptr + +#define setvertices2null(triedge) \ + (triedge).tri[3] = (triangle) NULL; \ + (triedge).tri[4] = (triangle) NULL; \ + (triedge).tri[5] = (triangle) NULL; + +/* Bond two triangles together. */ + +#define bond(triedge1, triedge2) \ + (triedge1).tri[(triedge1).orient] = encode(triedge2); \ + (triedge2).tri[(triedge2).orient] = encode(triedge1) + +/* Dissolve a bond (from one side). Note that the other triangle will still */ +/* think it's connected to this triangle. Usually, however, the other */ +/* triangle is being deleted entirely, or bonded to another triangle, so */ +/* it doesn't matter. */ + +#define dissolve(triedge) \ + (triedge).tri[(triedge).orient] = (triangle) dummytri + +/* Copy a triangle/edge handle. */ + +#define triedgecopy(triedge1, triedge2) \ + (triedge2).tri = (triedge1).tri; \ + (triedge2).orient = (triedge1).orient + +/* Test for equality of triangle/edge handles. */ + +#define triedgeequal(triedge1, triedge2) \ + (((triedge1).tri == (triedge2).tri) && \ + ((triedge1).orient == (triedge2).orient)) + +/* Primitives to infect or cure a triangle with the virus. These rely on */ +/* the assumption that all shell edges are aligned to four-byte boundaries.*/ + +#define infect(triedge) \ + (triedge).tri[6] = (triangle) \ + ((unsigned long) (triedge).tri[6] | (unsigned long) 2l) + +#define uninfect(triedge) \ + (triedge).tri[6] = (triangle) \ + ((unsigned long) (triedge).tri[6] & ~ (unsigned long) 2l) + +/* Test a triangle for viral infection. */ + +#define infected(triedge) \ + (((unsigned long) (triedge).tri[6] & (unsigned long) 2l) != 0) + +/* Check or set a triangle's attributes. */ + +#define elemattribute(triedge, attnum) \ + ((REAL *) (triedge).tri)[elemattribindex + (attnum)] + +#define setelemattribute(triedge, attnum, value) \ + ((REAL *) (triedge).tri)[elemattribindex + (attnum)] = value + +/* Check or set a triangle's maximum area bound. */ + +#define areabound(triedge) ((REAL *) (triedge).tri)[areaboundindex] + +#define setareabound(triedge, value) \ + ((REAL *) (triedge).tri)[areaboundindex] = value + +/********* Primitives for shell edges *********/ +/* */ +/* */ + +/* sdecode() converts a pointer to an oriented shell edge. The orientation */ +/* is extracted from the least significant bit of the pointer. The two */ +/* least significant bits (one for orientation, one for viral infection) */ +/* are masked out to produce the real pointer. */ + +#define sdecode(sptr, edge) \ + (edge).shorient = (int) ((unsigned long) (sptr) & (unsigned long) 1l); \ + (edge).sh = (shelle *) \ + ((unsigned long) (sptr) & ~ (unsigned long) 3l) + +/* sencode() compresses an oriented shell edge into a single pointer. It */ +/* relies on the assumption that all shell edges are aligned to two-byte */ +/* boundaries, so the least significant bit of (edge).sh is zero. */ + +#define sencode(edge) \ + (shelle) ((unsigned long) (edge).sh | (unsigned long) (edge).shorient) + +/* ssym() toggles the orientation of a shell edge. */ + +#define ssym(edge1, edge2) \ + (edge2).sh = (edge1).sh; \ + (edge2).shorient = 1 - (edge1).shorient + +#define ssymself(edge) \ + (edge).shorient = 1 - (edge).shorient + +/* spivot() finds the other shell edge (from the same segment) that shares */ +/* the same origin. */ + +#define spivot(edge1, edge2) \ + sptr = (edge1).sh[(edge1).shorient]; \ + sdecode(sptr, edge2) + +#define spivotself(edge) \ + sptr = (edge).sh[(edge).shorient]; \ + sdecode(sptr, edge) + +/* snext() finds the next shell edge (from the same segment) in sequence; */ +/* one whose origin is the input shell edge's destination. */ + +#define snext(edge1, edge2) \ + sptr = (edge1).sh[1 - (edge1).shorient]; \ + sdecode(sptr, edge2) + +#define snextself(edge) \ + sptr = (edge).sh[1 - (edge).shorient]; \ + sdecode(sptr, edge) + +/* These primitives determine or set the origin or destination of a shell */ +/* edge. */ + +#define sorg(edge, pointptr) \ + pointptr = (point) (edge).sh[2 + (edge).shorient] + +#define sdest(edge, pointptr) \ + pointptr = (point) (edge).sh[3 - (edge).shorient] + +#define setsorg(edge, pointptr) \ + (edge).sh[2 + (edge).shorient] = (shelle) pointptr + +#define setsdest(edge, pointptr) \ + (edge).sh[3 - (edge).shorient] = (shelle) pointptr + +/* These primitives read or set a shell marker. Shell markers are used to */ +/* hold user boundary information. */ + +#define mark(edge) (* (int *) ((edge).sh + 6)) + +#define setmark(edge, value) \ + * (int *) ((edge).sh + 6) = value + +/* Bond two shell edges together. */ + +#define sbond(edge1, edge2) \ + (edge1).sh[(edge1).shorient] = sencode(edge2); \ + (edge2).sh[(edge2).shorient] = sencode(edge1) + +/* Dissolve a shell edge bond (from one side). Note that the other shell */ +/* edge will still think it's connected to this shell edge. */ + +#define sdissolve(edge) \ + (edge).sh[(edge).shorient] = (shelle) dummysh + +/* Copy a shell edge. */ + +#define shellecopy(edge1, edge2) \ + (edge2).sh = (edge1).sh; \ + (edge2).shorient = (edge1).shorient + +/* Test for equality of shell edges. */ + +#define shelleequal(edge1, edge2) \ + (((edge1).sh == (edge2).sh) && \ + ((edge1).shorient == (edge2).shorient)) + +/********* Primitives for interacting triangles and shell edges *********/ +/* */ +/* */ + +/* tspivot() finds a shell edge abutting a triangle. */ + +#define tspivot(triedge, edge) \ + sptr = (shelle) (triedge).tri[6 + (triedge).orient]; \ + sdecode(sptr, edge) + +/* stpivot() finds a triangle abutting a shell edge. It requires that the */ +/* variable `ptr' of type `triangle' be defined. */ + +#define stpivot(edge, triedge) \ + ptr = (triangle) (edge).sh[4 + (edge).shorient]; \ + decode(ptr, triedge) + +/* Bond a triangle to a shell edge. */ + +#define tsbond(triedge, edge) \ + (triedge).tri[6 + (triedge).orient] = (triangle) sencode(edge); \ + (edge).sh[4 + (edge).shorient] = (shelle) encode(triedge) + +/* Dissolve a bond (from the triangle side). */ + +#define tsdissolve(triedge) \ + (triedge).tri[6 + (triedge).orient] = (triangle) dummysh + +/* Dissolve a bond (from the shell edge side). */ + +#define stdissolve(edge) \ + (edge).sh[4 + (edge).shorient] = (shelle) dummytri + +/********* Primitives for points *********/ +/* */ +/* */ + +#define pointmark(pt) ((int *) (pt))[pointmarkindex] + +#define setpointmark(pt, value) \ + ((int *) (pt))[pointmarkindex] = value + +#define point2tri(pt) ((triangle *) (pt))[point2triindex] + +#define setpoint2tri(pt, value) \ + ((triangle *) (pt))[point2triindex] = value + +/** **/ +/** **/ +/********* Mesh manipulation primitives end here *********/ + +/********* User interaction routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* syntax() Print list of command line switches. */ +/* */ +/*****************************************************************************/ + +void mesh2d::syntax() +{ + printf("triangle [-paAcevngBPNEIOXzo_YS__iFlsCQVh] input_file\n"); + exit(0); +} + +/*****************************************************************************/ +/* */ +/* info() Print out complete instructions. */ +/* */ +/*****************************************************************************/ + +void mesh2d::info() +{ + printf("Triangle\n"); + printf( +"A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.\n"); + printf("Version 1.3\n\n"); + printf( +"Copyright 1996 Jonathan Richard Shewchuk (bugs/comments to jrs@cs.cmu.edu)\n" +); + printf("School of Computer Science / Carnegie Mellon University\n"); + printf("5000 Forbes Avenue / Pittsburgh, Pennsylvania 15213-3891\n"); + printf( +"Created as part of the Archimedes project (tools for parallel FEM).\n"); + printf( +"Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.\n"); + printf("There is no warranty whatsoever. Use at your own risk.\n"); +} + +/*****************************************************************************/ +/* */ +/* internalerror() Ask the user to send me the defective product. Exit. */ +/* */ +/*****************************************************************************/ + +void mesh2d::internalerror() +{ + printf(" Please report this bug to sihang@weboo.com\n"); + printf(" Include the message above, your input data set, and the exact\n"); + printf(" command line you used to run Triangle.\n"); + exit(1); +} + +/*****************************************************************************/ +/* */ +/* parsecommandline() Read the command line, identify switches, and set */ +/* up options and file names. */ +/* */ +/* The effects of this routine are felt entirely through global variables. */ +/* */ +/*****************************************************************************/ + +void mesh2d::parsecommandline(int argc, char **argv, int libflag) +{ + int startindex; + char startchar; + int increment; + int meshnumber; + int i, j, k; + char workstring[FILENAMESIZE]; + + poly = refine = quality = vararea = fixedarea = regionattrib = convex = 0; + firstnumber = 1; + edgesout = voronoi = neighbors = geomview = 0; + nobound = nopolywritten = nonodewritten = noelewritten = noiterationnum = 0; + noholes = noexact = 0; + incremental = sweepline = 0; + dwyer = 1; + splitseg = 0; + docheck = 0; + nobisect = 0; + steiner = -1; + order = 1; + minangle = 0.0; + maxarea = -1.0; + quiet = verbose = 0; + + if (libflag) { + startindex = 0; + } else { + startindex = 1; + } + + for (i = startindex; i < argc; i++) { + if (libflag) { + startchar = '-'; + } else { + startchar = argv[i][0]; + } + if (startchar == '-') { + for (j = startindex; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'p') { + poly = 1; + } + if (argv[i][j] == 'r') { + refine = 1; + } + if (argv[i][j] == 'q') { + quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + minangle = (REAL) strtod(workstring, (char **) NULL); + } else { + minangle = 20.0; + } + } + if (argv[i][j] == 'a') { + quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + fixedarea = 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'; + maxarea = (REAL) strtod(workstring, (char **) NULL); + if (maxarea <= 0.0) { + printf("Error: Maximum area must be greater than zero.\n"); + exit(1); + } + } else { + vararea = 1; + } + } + if (argv[i][j] == 'A') { + regionattrib = 1; + } + if (argv[i][j] == 'c') { + convex = 1; + } + if (argv[i][j] == 'z') { + firstnumber = 0; + } + if (argv[i][j] == 'e') { + edgesout = 1; + } + if (argv[i][j] == 'v') { + voronoi = 1; + } + if (argv[i][j] == 'n') { + neighbors = 1; + } + if (argv[i][j] == 'g') { + geomview = 1; + } + if (argv[i][j] == 'B') { + nobound = 1; + } + if (argv[i][j] == 'P') { + nopolywritten = 1; + } + if (argv[i][j] == 'N') { + nonodewritten = 1; + } + if (argv[i][j] == 'E') { + noelewritten = 1; + } + if (argv[i][j] == 'I') { + noiterationnum = 1; + } + if (argv[i][j] == 'O') { + noholes = 1; + } + if (argv[i][j] == 'X') { + noexact = 1; + } + if (argv[i][j] == 'o') { + if (argv[i][j + 1] == '2') { + j++; + order = 2; + } + } + if (argv[i][j] == 'Y') { + nobisect++; + } + if (argv[i][j] == 'S') { + steiner = 0; + while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + j++; + steiner = steiner * 10 + (int) (argv[i][j] - '0'); + } + } + if (argv[i][j] == 'i') { + incremental = 1; + } + if (argv[i][j] == 'F') { + sweepline = 1; + } + if (argv[i][j] == 'l') { + dwyer = 0; + } + if (argv[i][j] == 's') { + splitseg = 1; + } + if (argv[i][j] == 'C') { + docheck = 1; + } + if (argv[i][j] == 'Q') { + quiet = 1; + } + if (argv[i][j] == 'V') { + verbose++; + } + if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || + (argv[i][j] == '?')) { + info(); + } + } + } + } + + steinerleft = steiner; + useshelles = poly || refine || quality || convex; + // Be careful not to add an extra attribute to each element unless the + // input supports it (PSLG in, but not refining a preexisting mesh). + if (refine || !poly) { + regionattrib = 0; + } +} + +/** **/ +/** **/ +/********* User interaction routines begin here *********/ + +/********* Debugging routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* printtriangle() Print out the details of a triangle/edge handle. */ +/* */ +/* I originally wrote this procedure to simplify debugging; it can be */ +/* called directly from the debugger, and presents information about a */ +/* triangle/edge handle in digestible form. It's also used when the */ +/* highest level of verbosity (`-VVV') is specified. */ +/* */ +/*****************************************************************************/ + +void mesh2d::printtriangle(struct triedge *t) +{ + struct triedge printtri; + struct edge printsh; + point printpoint; + + printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri, + t->orient); + decode(t->tri[0], printtri); + if (printtri.tri == dummytri) { + printf(" [0] = Outer space\n"); + } else { + printf(" [0] = x%lx %d\n", (unsigned long) printtri.tri, + printtri.orient); + } + decode(t->tri[1], printtri); + if (printtri.tri == dummytri) { + printf(" [1] = Outer space\n"); + } else { + printf(" [1] = x%lx %d\n", (unsigned long) printtri.tri, + printtri.orient); + } + decode(t->tri[2], printtri); + if (printtri.tri == dummytri) { + printf(" [2] = Outer space\n"); + } else { + printf(" [2] = x%lx %d\n", (unsigned long) printtri.tri, + printtri.orient); + } + org(*t, printpoint); + if (printpoint == (point) NULL) + printf(" Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3); + else + printf(" Origin[%d] = x%lx (%.12g, %.12g)\n", + (t->orient + 1) % 3 + 3, (unsigned long) printpoint, + printpoint[0], printpoint[1]); + dest(*t, printpoint); + if (printpoint == (point) NULL) + printf(" Dest [%d] = NULL\n", (t->orient + 2) % 3 + 3); + else + printf(" Dest [%d] = x%lx (%.12g, %.12g)\n", + (t->orient + 2) % 3 + 3, (unsigned long) printpoint, + printpoint[0], printpoint[1]); + apex(*t, printpoint); + if (printpoint == (point) NULL) + printf(" Apex [%d] = NULL\n", t->orient + 3); + else + printf(" Apex [%d] = x%lx (%.12g, %.12g)\n", + t->orient + 3, (unsigned long) printpoint, + printpoint[0], printpoint[1]); + if (useshelles) { + sdecode(t->tri[6], printsh); + if (printsh.sh != dummysh) { + printf(" [6] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + sdecode(t->tri[7], printsh); + if (printsh.sh != dummysh) { + printf(" [7] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + sdecode(t->tri[8], printsh); + if (printsh.sh != dummysh) { + printf(" [8] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + } + if (vararea) { + printf(" Area constraint: %.4g\n", areabound(*t)); + } +} + +/*****************************************************************************/ +/* */ +/* printshelle() Print out the details of a shell edge handle. */ +/* */ +/* I originally wrote this procedure to simplify debugging; it can be */ +/* called directly from the debugger, and presents information about a */ +/* shell edge handle in digestible form. It's also used when the highest */ +/* level of verbosity (`-VVV') is specified. */ +/* */ +/*****************************************************************************/ + +void mesh2d::printshelle(struct edge *s) +{ + struct edge printsh; + struct triedge printtri; + point printpoint; + + printf("shell edge x%lx with orientation %d and mark %d:\n", + (unsigned long) s->sh, s->shorient, mark(*s)); + sdecode(s->sh[0], printsh); + if (printsh.sh == dummysh) { + printf(" [0] = No shell\n"); + } else { + printf(" [0] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + sdecode(s->sh[1], printsh); + if (printsh.sh == dummysh) { + printf(" [1] = No shell\n"); + } else { + printf(" [1] = x%lx %d\n", (unsigned long) printsh.sh, + printsh.shorient); + } + sorg(*s, printpoint); + if (printpoint == (point) NULL) + printf(" Origin[%d] = NULL\n", 2 + s->shorient); + else + printf(" Origin[%d] = x%lx (%.12g, %.12g)\n", + 2 + s->shorient, (unsigned long) printpoint, + printpoint[0], printpoint[1]); + sdest(*s, printpoint); + if (printpoint == (point) NULL) + printf(" Dest [%d] = NULL\n", 3 - s->shorient); + else + printf(" Dest [%d] = x%lx (%.12g, %.12g)\n", + 3 - s->shorient, (unsigned long) printpoint, + printpoint[0], printpoint[1]); + decode(s->sh[4], printtri); + if (printtri.tri == dummytri) { + printf(" [4] = Outer space\n"); + } else { + printf(" [4] = x%lx %d\n", (unsigned long) printtri.tri, + printtri.orient); + } + decode(s->sh[5], printtri); + if (printtri.tri == dummytri) { + printf(" [5] = Outer space\n"); + } else { + printf(" [5] = x%lx %d\n", (unsigned long) printtri.tri, + printtri.orient); + } +} + +/** **/ +/** **/ +/********* Debugging routines end here *********/ + +/********* Memory management routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* dummyinit() Initialize the triangle that fills "outer space" and the */ +/* omnipresent shell edge. */ +/* */ +/* The triangle that fills "outer space", called `dummytri', is pointed to */ +/* by every triangle and shell edge on a boundary (be it outer or inner) of */ +/* the triangulation. Also, `dummytri' points to one of the triangles on */ +/* the convex hull (until the holes and concavities are carved), making it */ +/* possible to find a starting triangle for point location. */ +/* */ +/* The omnipresent shell edge, `dummysh', is pointed to by every triangle */ +/* or shell edge that doesn't have a full complement of real shell edges */ +/* to point to. */ +/* */ +/*****************************************************************************/ + +void mesh2d::dummyinit(int trianglewords, int shellewords) +{ + unsigned long alignptr; + + /* `triwords' and `shwords' are used by the mesh manipulation primitives */ + /* to extract orientations of triangles and shell edges from pointers. */ + triwords = trianglewords; /* Initialize `triwords' once and for all. */ + shwords = shellewords; /* Initialize `shwords' once and for all. */ + + /* Set up `dummytri', the `triangle' that occupies "outer space". */ + dummytribase = (triangle *) new BYTE[triwords * sizeof(triangle) + + triangles.alignbytes]; + if (dummytribase == (triangle *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */ + alignptr = (unsigned long) dummytribase; + dummytri = (triangle *) + (alignptr + (unsigned long) triangles.alignbytes + - (alignptr % (unsigned long) triangles.alignbytes)); + /* Initialize the three adjoining triangles 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. */ + dummytri[0] = (triangle) dummytri; + dummytri[1] = (triangle) dummytri; + dummytri[2] = (triangle) dummytri; + /* Three NULL vertex points. */ + dummytri[3] = (triangle) NULL; + dummytri[4] = (triangle) NULL; + dummytri[5] = (triangle) NULL; + + if (useshelles) { + /* Set up `dummysh', the omnipresent "shell edge" pointed to by any */ + /* triangle side or shell edge end that isn't attached to a real shell */ + /* edge. */ + dummyshbase = (shelle *) new BYTE[shwords * sizeof(shelle) + + shelles.alignbytes]; + if (dummyshbase == (shelle *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + /* Align `dummysh' on a `shelles.alignbytes'-byte boundary. */ + alignptr = (unsigned long) dummyshbase; + dummysh = (shelle *) + (alignptr + (unsigned long) shelles.alignbytes + - (alignptr % (unsigned long) shelles.alignbytes)); + /* Initialize the two adjoining shell edges to be the omnipresent shell */ + /* edge. 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] = (shelle) dummysh; + dummysh[1] = (shelle) dummysh; + /* Two NULL vertex points. */ + dummysh[2] = (shelle) NULL; + dummysh[3] = (shelle) NULL; + /* Initialize the two adjoining triangles to be "outer space". */ + dummysh[4] = (shelle) dummytri; + dummysh[5] = (shelle) dummytri; + /* Set the boundary marker to zero. */ + * (int *) (dummysh + 6) = 0; + + /* Initialize the three adjoining shell edges of `dummytri' to be */ + /* the omnipresent shell edge. */ + dummytri[6] = (triangle) dummysh; + dummytri[7] = (triangle) dummysh; + dummytri[8] = (triangle) dummysh; + } +} + +/*****************************************************************************/ +/* */ +/* initializepointpool() Calculate the size of the point data structure */ +/* and initialize its memory pool. */ +/* */ +/* This routine also computes the `pointmarkindex' and `point2triindex' */ +/* indices used to find values within each point. */ +/* */ +/*****************************************************************************/ + +void mesh2d::initializepointpool() +{ + int pointsize; + + /* 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 = ((mesh_dim + nextras) * sizeof(REAL) + sizeof(int) - 1) + / sizeof(int); + pointsize = (pointmarkindex + 1) * sizeof(int); + if (poly) { + /* The index within each point at which a triangle pointer is found. */ + /* Ensure the pointer is aligned to a sizeof(triangle)-byte address. */ + point2triindex = (pointsize + sizeof(triangle) - 1) / sizeof(triangle); + pointsize = (point2triindex + 1) * sizeof(triangle); + } + /* Initialize the pool of points. */ + points.init(pointsize, POINTPERBLOCK, + (sizeof(REAL) >= sizeof(triangle)) ? FLOATINGPOINT : POINTER, 0); +} + +/*****************************************************************************/ +/* */ +/* initializetrisegpools() Calculate the sizes of the triangle and shell */ +/* edge data structures and initialize their */ +/* memory pools. */ +/* */ +/* This routine also computes the `highorderindex', `elemattribindex', and */ +/* `areaboundindex' indices used to find values within each triangle. */ +/* */ +/*****************************************************************************/ + +void mesh2d::initializetrisegpools() +{ + int trisize; + + /* The index within each triangle at which the extra nodes (above three) */ + /* associated with high order elements are found. There are three */ + /* pointers to other triangles, three pointers to corners, and possibly */ + /* three pointers to shell edges before the extra nodes. */ + highorderindex = 6 + (useshelles * 3); + /* The number of bytes occupied by a triangle. */ + trisize = ((order + 1) * (order + 2) / 2 + (highorderindex - 3)) * + sizeof(triangle); + /* The index within each triangle at which its attributes are found, */ + /* where the index is measured in REALs. */ + elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL); + /* The index within each triangle at which the maximum area constraint */ + /* is found, where the index is measured in REALs. Note that if the */ + /* `regionattrib' flag is set, an additional attribute will be added. */ + areaboundindex = elemattribindex + eextras + regionattrib; + /* If triangle attributes or an area bound are needed, increase the number */ + /* of bytes occupied by a triangle. */ + if (vararea) { + trisize = (areaboundindex + 1) * sizeof(REAL); + } else if (eextras + regionattrib > 0) { + trisize = areaboundindex * sizeof(REAL); + } + /* If a Voronoi diagram or triangle neighbor graph is requested, make */ + /* sure there's room to store an integer index in each triangle. This */ + /* integer index can occupy the same space as the shell edges or */ + /* attributes or area constraint or extra nodes. */ + if ((voronoi || neighbors) && + (trisize < 6 * sizeof(triangle) + sizeof(int))) { + trisize = 6 * sizeof(triangle) + sizeof(int); + } + /* Having determined the memory size of a triangle, initialize the pool. */ + triangles.init(trisize, TRIPERBLOCK, POINTER, 4); + + if (useshelles) { + /* Initialize the pool of shell edges. */ + shelles.init(6 * sizeof(triangle) + sizeof(int), SHELLEPERBLOCK, + POINTER, 4); + + /* Initialize the "outer space" triangle and omnipresent shell edge. */ + dummyinit(triangles.itemwords, shelles.itemwords); + } else { + /* Initialize the "outer space" triangle. */ + dummyinit(triangles.itemwords, 0); + } +} + +/*****************************************************************************/ +/* */ +/* triangledealloc() Deallocate space for a triangle, marking it dead. */ +/* */ +/*****************************************************************************/ + +void mesh2d::triangledealloc(triangle *dyingtriangle) +{ + /* Set triangle's vertices to NULL. This makes it possible to */ + /* detect dead triangles when traversing the list of all triangles. */ + dyingtriangle[3] = (triangle) NULL; + dyingtriangle[4] = (triangle) NULL; + dyingtriangle[5] = (triangle) NULL; + triangles.dealloc(dyingtriangle); +} + +/*****************************************************************************/ +/* */ +/* triangletraverse() Traverse the triangles, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +triangle* mesh2d::triangletraverse() +{ + triangle *newtriangle; + + do { + newtriangle = (triangle *) triangles.traverse(); + if (newtriangle == (triangle *) NULL) { + return (triangle *) NULL; + } + } while (newtriangle[3] == (triangle) NULL); /* Skip dead ones. */ + return newtriangle; +} + +/*****************************************************************************/ +/* */ +/* shelledealloc() Deallocate space for a shell edge, marking it dead. */ +/* */ +/*****************************************************************************/ + +void mesh2d::shelledealloc(shelle *dyingshelle) +{ + /* Set shell edge's vertices to NULL. This makes it possible to */ + /* detect dead shells when traversing the list of all shells. */ + dyingshelle[2] = (shelle) NULL; + dyingshelle[3] = (shelle) NULL; + shelles.dealloc(dyingshelle); +} + +/*****************************************************************************/ +/* */ +/* shelletraverse() Traverse the shell edges, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +shelle* mesh2d::shelletraverse() +{ + shelle *newshelle; + + do { + newshelle = (shelle *) shelles.traverse(); + if (newshelle == (shelle *) NULL) { + return (shelle *) NULL; + } + } while (newshelle[2] == (shelle) NULL); /* Skip dead ones. */ + return newshelle; +} + +/*****************************************************************************/ +/* */ +/* pointdealloc() Deallocate space for a point, marking it dead. */ +/* */ +/*****************************************************************************/ + +void mesh2d::pointdealloc(point dyingpoint) +{ + /* Mark the point as dead. This makes it possible to detect dead points */ + /* when traversing the list of all points. */ + setpointmark(dyingpoint, DEADPOINT); + points.dealloc(dyingpoint); +} + +/*****************************************************************************/ +/* */ +/* pointtraverse() Traverse the points, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +point mesh2d::pointtraverse() +{ + point newpoint; + + do { + newpoint = (point) points.traverse(); + if (newpoint == (point) NULL) { + return (point) NULL; + } + } while (pointmark(newpoint) == DEADPOINT); /* Skip dead ones. */ + return newpoint; +} + +/*****************************************************************************/ +/* */ +/* getpoint() Get a specific point, by number, from the list. */ +/* */ +/* The first point is number 'firstnumber'. */ +/* */ +/* Note that this takes O(n) time (with a small constant, if POINTPERBLOCK */ +/* is large). I don't care to take the trouble to make it work in constant */ +/* time. */ +/* */ +/*****************************************************************************/ + +point mesh2d::getpoint(int number) +{ + void **getblock; + point foundpoint; + unsigned long alignptr; + int current; + + getblock = points.firstblock; + current = firstnumber; + /* Find the right block. */ + while (current + points.itemsperblock <= number) { + getblock = (void **) *getblock; + current += points.itemsperblock; + } + /* Now find the right point. */ + alignptr = (unsigned long) (getblock + 1); + foundpoint = (point) (alignptr + (unsigned long) points.alignbytes + - (alignptr % (unsigned long) points.alignbytes)); + while (current < number) { + foundpoint += points.itemwords; + current++; + } + return foundpoint; +} + +/*****************************************************************************/ +/* */ +/* triangledeinit() Free all remaining allocated memory. */ +/* */ +/*****************************************************************************/ + +void mesh2d::triangledeinit() +{ + triangles.deinit(); + if (dummytribase) delete [] dummytribase; + if (useshelles) { + shelles.deinit(); + if (dummyshbase) delete [] dummyshbase; + } + points.deinit(); +} + +/** **/ +/** **/ +/********* Memory management routines end here *********/ + +/********* Constructors begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* maketriangle() Create a new triangle with orientation zero. */ +/* */ +/*****************************************************************************/ + +void mesh2d::maketriangle(struct triedge *newtriedge) +{ + int i; + + newtriedge->tri = (triangle *) triangles.alloc(); + /* Initialize the three adjoining triangles to be "outer space". */ + newtriedge->tri[0] = (triangle) dummytri; + newtriedge->tri[1] = (triangle) dummytri; + newtriedge->tri[2] = (triangle) dummytri; + /* Three NULL vertex points. */ + newtriedge->tri[3] = (triangle) NULL; + newtriedge->tri[4] = (triangle) NULL; + newtriedge->tri[5] = (triangle) NULL; + /* Initialize the three adjoining shell edges to be the omnipresent */ + /* shell edge. */ + if (useshelles) { + newtriedge->tri[6] = (triangle) dummysh; + newtriedge->tri[7] = (triangle) dummysh; + newtriedge->tri[8] = (triangle) dummysh; + } + for (i = 0; i < eextras; i++) { + setelemattribute(*newtriedge, i, 0.0); + } + if (vararea) { + setareabound(*newtriedge, -1.0); + } + + newtriedge->orient = 0; +} + +/*****************************************************************************/ +/* */ +/* makeshelle() Create a new shell edge with orientation zero. */ +/* */ +/*****************************************************************************/ + +void mesh2d::makeshelle(struct edge *newedge) +{ + newedge->sh = (shelle *) shelles.alloc(); + /* Initialize the two adjoining shell edges to be the omnipresent */ + /* shell edge. */ + newedge->sh[0] = (shelle) dummysh; + newedge->sh[1] = (shelle) dummysh; + /* Two NULL vertex points. */ + newedge->sh[2] = (shelle) NULL; + newedge->sh[3] = (shelle) NULL; + /* Initialize the two adjoining triangles to be "outer space". */ + newedge->sh[4] = (shelle) dummytri; + newedge->sh[5] = (shelle) dummytri; + /* Set the boundary marker to zero. */ + setmark(*newedge, 0); + + newedge->shorient = 0; +} + +/** **/ +/** **/ +/********* Constructors end here *********/ + +/********* Determinant evaluation routines begin here *********/ +/** **/ +/** **/ + +/* This routines were move to "predicate.cpp" file */ + +/** **/ +/** **/ +/********* Determinant evaluation routines end here *********/ + +/*****************************************************************************/ +/* */ +/* triangleinit() Initialize some variables. */ +/* */ +/*****************************************************************************/ + +void mesh2d::triangleinit() +{ + points.maxitems = triangles.maxitems = shelles.maxitems = viri.maxitems = 0l; + points.itembytes = triangles.itembytes = shelles.itembytes = + viri.itembytes = 0; + recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */ + samples = 1; /* Point location should take at least one sample. */ + checksegments = 0; /* There are no segments in the triangulation yet. */ + circumcentercount = 0; + randomseed = 1; + incirclecount = counterclockcount = 0; + dummytribase = NULL; + dummyshbase = NULL; + restartsymbol = 0; +} + +/*****************************************************************************/ +/* */ +/* trianglerestart() Clear all remaining allocated memory, not free them. */ +/* */ +/*****************************************************************************/ + +void mesh2d::trianglerestart() +{ + triangles.restart(); + if (useshelles) { + shelles.restart(); + } + points.restart(); + + recenttri.tri = (triangle *) NULL; // No triangle has been visited yet. + samples = 1; // Point location should take at least one sample. + checksegments = 0; // There are no segments in the triangulation yet. + randomseed = 1; + circumcentercount = 0; + restartsymbol = 1; +} + +/*****************************************************************************/ +/* */ +/* counterclockwise() 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. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. 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, this function is usually quite fast, but will run */ +/* more slowly when the input points are collinear or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +#define dDIST2D(a, b) sqrt((a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1])) + +REAL mesh2d::counterclockwise(point pa, point pb, point pc) +{ + counterclockcount++; + if (noexact) { + REAL xa = pa[0], ya = pa[1]; + REAL xb = pb[0], yb = pb[1]; + REAL xc = pc[0], yc = pc[1]; + + // Currently no exact arithmetic + REAL dDet = ((xb - xa) * (yc - ya) - (xc - xa) * (yb - ya)); + // Scale the determinant by the mean of the magnitudes of vectors: + REAL dScale = (dDIST2D (pa, pb) + + dDIST2D (pb, pc) + + dDIST2D (pc, pa)) / 3; + REAL dScaleDet = dDet / (dScale * dScale); + if (fabs(dScaleDet) <= 1.e-10) { + dDet = 0.; + } + return dDet; + } else { + REAL dDet = orient2d(pa, pb, pc); + return dDet; + } +} + +/*****************************************************************************/ +/* */ +/* incircle() 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. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. 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, this function is usually quite fast, but will run */ +/* more slowly when the input points are cocircular or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL mesh2d::iincircle(point pa, point pb, point pc, point pd) +{ + incirclecount++; + return incircle(pa, pb, pc, pd); +} + +/*****************************************************************************/ +/* */ +/* randomnation() Generate a random number between 0 and `choices' - 1. */ +/* */ +/* This is a simple linear congruential random number generator. Hence, it */ +/* is a bad random number generator, but good enough for most randomized */ +/* geometric algorithms. */ +/* */ +/*****************************************************************************/ + +unsigned long mesh2d::randomnation(unsigned int choices) +{ + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed / (714025l / choices + 1); +} + +/********* Point location routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* makepointmap() Construct a mapping from points to triangles to improve */ +/* the speed of point location for segment insertion. */ +/* */ +/* Traverses all the triangles, and provides each corner of each triangle */ +/* with a pointer to that triangle. Of course, pointers will be */ +/* overwritten by other pointers because (almost) each point is a corner */ +/* of several triangles, but in the end every point will point to some */ +/* triangle that contains it. */ +/* */ +/*****************************************************************************/ + +void mesh2d::makepointmap() +{ + struct triedge triangleloop; + point triorg; + + if (verbose) { + printf(" Constructing mapping from points to triangles.\n"); + } + triangles.traversalinit(); + triangleloop.tri = triangletraverse(); + while (triangleloop.tri != (triangle *) NULL) { + /* Check all three points of the triangle. */ + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + org(triangleloop, triorg); + setpoint2tri(triorg, encode(triangleloop)); + } + triangleloop.tri = triangletraverse(); + } +} + +/*****************************************************************************/ +/* */ +/* preciselocate() Find a triangle or edge containing a given point. */ +/* */ +/* Begins its search from `searchtri'. It is important that `searchtri' */ +/* be a handle with the property that `searchpoint' is strictly to the left */ +/* of the edge denoted by `searchtri', or is collinear with that edge and */ +/* does not intersect that edge. (In particular, `searchpoint' should not */ +/* be the origin or destination of that edge.) */ +/* */ +/* These conditions are imposed because preciselocate() is normally used in */ +/* one of two situations: */ +/* */ +/* (1) To try to find the location to insert a new point. Normally, we */ +/* know an edge that the point is strictly to the left of. In the */ +/* incremental Delaunay algorithm, that edge is a bounding box edge. */ +/* In Ruppert's Delaunay refinement algorithm for quality meshing, */ +/* that edge is the shortest edge of the triangle whose circumcenter */ +/* is being inserted. */ +/* */ +/* (2) To try to find an existing point. In this case, any edge on the */ +/* convex hull is a good starting edge. The possibility that the */ +/* vertex one seeks is an endpoint of the starting edge must be */ +/* screened out before preciselocate() is called. */ +/* */ +/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ +/* */ +/* This implementation differs from that given by Guibas and Stolfi. It */ +/* walks from triangle to triangle, crossing an edge only if `searchpoint' */ +/* is on the other side of the line containing that edge. After entering */ +/* a triangle, there are two edges by which one can leave that triangle. */ +/* If both edges are valid (`searchpoint' is on the other side of both */ +/* edges), one of the two is chosen by drawing a line perpendicular to */ +/* the entry edge (whose endpoints are `forg' and `fdest') passing through */ +/* `fapex'. Depending on which side of this perpendicular `searchpoint' */ +/* falls on, an exit edge is chosen. */ +/* */ +/* This implementation is empirically faster than the Guibas and Stolfi */ +/* point location routine (which I originally used), which tends to spiral */ +/* in toward its target. */ +/* */ +/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ +/* is a handle whose origin is the existing vertex. */ +/* */ +/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ +/* handle whose primary edge is the edge on which the point lies. */ +/* */ +/* Returns INTRIANGLE if the point lies strictly within a triangle. */ +/* `searchtri' is a handle on the triangle that contains the point. */ +/* */ +/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ +/* handle whose primary edge the point is to the right of. This might */ +/* occur when the circumcenter of a triangle falls just slightly outside */ +/* the mesh due to floating-point roundoff error. It also occurs when */ +/* seeking a hole or region point that a foolish user has placed outside */ +/* the mesh. */ +/* */ +/* WARNING: This routine is designed for convex triangulations, and will */ +/* not generally work after the holes and concavities have been carved. */ +/* However, it can still be used to find the circumcenter of a triangle, as */ +/* long as the search is begun from the triangle in question. */ +/* */ +/*****************************************************************************/ + +enum mesh2d::locateresult +mesh2d::preciselocate(point searchpoint, struct triedge *searchtri) +{ + struct triedge backtracktri; + point forg, fdest, fapex; + point swappoint; + REAL orgorient, destorient; + int moveleft; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose > 2) { + printf(" Searching for point (%.12g, %.12g).\n", + searchpoint[0], searchpoint[1]); + } + /* Where are we? */ + org(*searchtri, forg); + dest(*searchtri, fdest); + apex(*searchtri, fapex); + while (1) { + if (verbose > 2) { + printf(" At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]); + } + /* Check whether the apex is the point we seek. */ + if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) { + lprevself(*searchtri); + return ONVERTEX; + } + /* Does the point lie on the other side of the line defined by the */ + /* triangle edge opposite the triangle's destination? */ + destorient = counterclockwise(forg, fapex, searchpoint); + /* Does the point lie on the other side of the line defined by the */ + /* triangle edge opposite the triangle's origin? */ + orgorient = counterclockwise(fapex, fdest, searchpoint); + if (destorient > 0.0) { + if (orgorient > 0.0) { + /* Move left if the inner product of (fapex - searchpoint) and */ + /* (fdest - forg) is positive. This is equivalent to drawing */ + /* a line perpendicular to the line (forg, fdest) passing */ + /* through `fapex', and determining which side of this line */ + /* `searchpoint' falls on. */ + moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) + + (fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0; + } else { + moveleft = 1; + } + } else { + if (orgorient > 0.0) { + moveleft = 0; + } else { + /* The point we seek must be on the boundary of or inside this */ + /* triangle. */ + if (destorient == 0.0) { + lprevself(*searchtri); + return ONEDGE; + } + if (orgorient == 0.0) { + lnextself(*searchtri); + return ONEDGE; + } + return INTRIANGLE; + } + } + + /* Move to another triangle. Leave a trace `backtracktri' in case */ + /* floating-point roundoff or some such bogey causes us to walk */ + /* off a boundary of the triangulation. We can just bounce off */ + /* the boundary as if it were an elastic band. */ + if (moveleft) { + lprev(*searchtri, backtracktri); + fdest = fapex; + } else { + lnext(*searchtri, backtracktri); + forg = fapex; + } + sym(backtracktri, *searchtri); + + /* Check for walking off the edge. */ + if (searchtri->tri == dummytri) { + /* Turn around. */ + triedgecopy(backtracktri, *searchtri); + swappoint = forg; + forg = fdest; + fdest = swappoint; + apex(*searchtri, fapex); + /* Check if the point really is beyond the triangulation boundary. */ + destorient = counterclockwise(forg, fapex, searchpoint); + orgorient = counterclockwise(fapex, fdest, searchpoint); + if ((orgorient < 0.0) && (destorient < 0.0)) { + return OUTSIDE; + } + } else { + apex(*searchtri, fapex); + } + } +} + +/*****************************************************************************/ +/* */ +/* locate() Find a triangle or edge containing a given point. */ +/* */ +/* Searching begins from one of: the input `searchtri', a recently */ +/* encountered triangle `recenttri', or from a triangle chosen from a */ +/* random sample. The choice is made by determining which triangle's */ +/* origin is closest to the point we are searcing for. Normally, */ +/* `searchtri' should be a handle on the convex hull of the triangulation. */ +/* */ +/* Details on the random sampling method can be found in the Mucke, Saias, */ +/* and Zhu paper cited in the header of this code. */ +/* */ +/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ +/* */ +/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ +/* is a handle whose origin is the existing vertex. */ +/* */ +/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ +/* handle whose primary edge is the edge on which the point lies. */ +/* */ +/* Returns INTRIANGLE if the point lies strictly within a triangle. */ +/* `searchtri' is a handle on the triangle that contains the point. */ +/* */ +/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ +/* handle whose primary edge the point is to the right of. This might */ +/* occur when the circumcenter of a triangle falls just slightly outside */ +/* the mesh due to floating-point roundoff error. It also occurs when */ +/* seeking a hole or region point that a foolish user has placed outside */ +/* the mesh. */ +/* */ +/* WARNING: This routine is designed for convex triangulations, and will */ +/* not generally work after the holes and concavities have been carved. */ +/* */ +/*****************************************************************************/ + +enum mesh2d::locateresult +mesh2d::locate(point searchpoint, struct triedge *searchtri) +{ + void **sampleblock; + triangle *firsttri; + struct triedge sampletri; + point torg, tdest; + unsigned long alignptr; + REAL searchdist, dist; + REAL ahead; + long sampleblocks, samplesperblock, samplenum; + long triblocks; + long i, j; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose > 2) { + printf(" Randomly sampling for a triangle near point (%.12g, %.12g).\n", + searchpoint[0], searchpoint[1]); + } + /* Record the distance from the suggested starting triangle to the */ + /* point we seek. */ + org(*searchtri, torg); + searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (verbose > 2) { + printf(" Boundary triangle has origin (%.12g, %.12g).\n", + torg[0], torg[1]); + } + + /* If a recently encountered triangle has been recorded and has not been */ + /* deallocated, test it as a good starting point. */ + if (recenttri.tri != (triangle *) NULL) { + if (recenttri.tri[3] != (triangle) NULL) { + org(recenttri, torg); + if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { + triedgecopy(recenttri, *searchtri); + return ONVERTEX; + } + dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (dist < searchdist) { + triedgecopy(recenttri, *searchtri); + searchdist = dist; + if (verbose > 2) { + printf(" Choosing recent triangle with origin (%.12g, %.12g).\n", + torg[0], torg[1]); + } + } + } + } + + /* The number of random samples taken is proportional to the cube root of */ + /* the number of triangles in the mesh. The next bit of code assumes */ + /* that the number of triangles increases monotonically. */ + while (SAMPLEFACTOR * samples * samples * samples < triangles.items) { + samples++; + } + triblocks = (triangles.maxitems + TRIPERBLOCK - 1) / TRIPERBLOCK; + samplesperblock = 1 + (samples / triblocks); + sampleblocks = samples / samplesperblock; + sampleblock = triangles.firstblock; + sampletri.orient = 0; + for (i = 0; i < sampleblocks; i++) { + alignptr = (unsigned long) (sampleblock + 1); + firsttri = (triangle *) (alignptr + (unsigned long) triangles.alignbytes + - (alignptr % (unsigned long) triangles.alignbytes)); + for (j = 0; j < samplesperblock; j++) { + if (i == triblocks - 1) { + samplenum = randomnation((int) + (triangles.maxitems - (i * TRIPERBLOCK))); + } else { + samplenum = randomnation(TRIPERBLOCK); + } + sampletri.tri = (triangle *) + (firsttri + (samplenum * triangles.itemwords)); + if (sampletri.tri[3] != (triangle) NULL) { + org(sampletri, torg); + dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (dist < searchdist) { + triedgecopy(sampletri, *searchtri); + searchdist = dist; + if (verbose > 2) { + printf(" Choosing triangle with origin (%.12g, %.12g).\n", + torg[0], torg[1]); + } + } + } + } + sampleblock = (void **) *sampleblock; + } + /* Where are we? */ + org(*searchtri, torg); + dest(*searchtri, tdest); + /* Check the starting triangle's vertices. */ + if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { + return ONVERTEX; + } + if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) { + lnextself(*searchtri); + return ONVERTEX; + } + /* Orient `searchtri' to fit the preconditions of calling preciselocate(). */ + ahead = counterclockwise(torg, tdest, searchpoint); + if (ahead < 0.0) { + /* Turn around so that `searchpoint' is to the left of the */ + /* edge specified by `searchtri'. */ + symself(*searchtri); + } else if (ahead == 0.0) { + /* Check if `searchpoint' is between `torg' and `tdest'. */ + if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) + && ((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) { + return ONEDGE; + } + } + return preciselocate(searchpoint, searchtri); +} + +/** **/ +/** **/ +/********* Point location routines end here *********/ + +/********* Mesh transformation routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* insertshelle() Create a new shell edge and insert it between two */ +/* triangles. */ +/* */ +/* The new shell edge is inserted at the edge described by the handle */ +/* `tri'. Its vertices are properly initialized. The marker `shellemark' */ +/* is applied to the shell edge and, if appropriate, its vertices. */ +/* */ +/*****************************************************************************/ + +void mesh2d::insertshelle(struct triedge *tri, int shellemark) +{ + struct triedge oppotri; + struct edge newshelle; + point triorg, tridest; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + /* Mark points if possible. */ + org(*tri, triorg); + dest(*tri, tridest); + if (pointmark(triorg) == 0) { + setpointmark(triorg, shellemark); + } + if (pointmark(tridest) == 0) { + setpointmark(tridest, shellemark); + } + /* Check if there's already a shell edge here. */ + tspivot(*tri, newshelle); + if (newshelle.sh == dummysh) { + /* Make new shell edge and initialize its vertices. */ + makeshelle(&newshelle); + setsorg(newshelle, tridest); + setsdest(newshelle, triorg); + /* Bond new shell edge to the two triangles it is sandwiched between. */ + /* Note that the facing triangle `oppotri' might be equal to */ + /* `dummytri' (outer space), but the new shell edge is bonded to it */ + /* all the same. */ + tsbond(*tri, newshelle); + sym(*tri, oppotri); + ssymself(newshelle); + tsbond(oppotri, newshelle); + setmark(newshelle, shellemark); + if (verbose > 2) { + printf(" Inserting new "); + printshelle(&newshelle); + } + } else { + if (mark(newshelle) == 0) { + setmark(newshelle, shellemark); + } + } +} + +/*****************************************************************************/ +/* */ +/* Terminology */ +/* */ +/* A "local transformation" replaces a small set of triangles with another */ +/* set of triangles. This may or may not involve inserting or deleting a */ +/* point. */ +/* */ +/* The term "casing" is used to describe the set of triangles that are */ +/* attached to the triangles being transformed, but are not transformed */ +/* themselves. Think of the casing as a fixed hollow structure inside */ +/* which all the action happens. A "casing" is only defined relative to */ +/* a single transformation; each occurrence of a transformation will */ +/* involve a different casing. */ +/* */ +/* A "shell" is similar to a "casing". The term "shell" describes the set */ +/* of shell edges (if any) that are attached to the triangles being */ +/* transformed. However, I sometimes use "shell" to refer to a single */ +/* shell edge, so don't get confused. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* flip() Transform two triangles to two different triangles by flipping */ +/* an edge within a quadrilateral. */ +/* */ +/* Imagine the original triangles, abc and bad, oriented so that the */ +/* shared edge ab lies in a horizontal plane, with the point b on the left */ +/* and the point a on the right. The point c lies below the edge, and the */ +/* point d lies above the edge. The `flipedge' handle holds the edge ab */ +/* of triangle abc, and is directed left, from vertex a to vertex b. */ +/* */ +/* The triangles abc and bad are deleted and replaced by the triangles cdb */ +/* and dca. The triangles that represent abc and bad are NOT deallocated; */ +/* they are reused for dca and cdb, respectively. Hence, any handles that */ +/* may have held the original triangles are still valid, although not */ +/* directed as they were before. */ +/* */ +/* Upon completion of this routine, the `flipedge' handle holds the edge */ +/* dc of triangle dca, and is directed down, from vertex d to vertex c. */ +/* (Hence, the two triangles have rotated counterclockwise.) */ +/* */ +/* WARNING: This transformation is geometrically valid only if the */ +/* quadrilateral adbc is convex. Furthermore, this transformation is */ +/* valid only if there is not a shell edge between the triangles abc and */ +/* bad. This routine does not check either of these preconditions, and */ +/* it is the responsibility of the calling routine to ensure that they are */ +/* met. If they are not, the streets shall be filled with wailing and */ +/* gnashing of teeth. */ +/* */ +/*****************************************************************************/ + +void mesh2d::flip(struct triedge *flipedge) +{ + struct triedge botleft, botright; + struct triedge topleft, topright; + struct triedge top; + struct triedge botlcasing, botrcasing; + struct triedge toplcasing, toprcasing; + struct edge botlshelle, botrshelle; + struct edge toplshelle, toprshelle; + point leftpoint, rightpoint, botpoint; + point farpoint; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + /* Identify the vertices of the quadrilateral. */ + org(*flipedge, rightpoint); + dest(*flipedge, leftpoint); + apex(*flipedge, botpoint); + sym(*flipedge, top); +#ifdef SELF_CHECK + if (top.tri == dummytri) { + printf("Internal error in flip(): Attempt to flip on boundary.\n"); + lnextself(*flipedge); + return; + } + if (checksegments) { + tspivot(*flipedge, toplshelle); + if (toplshelle.sh != dummysh) { + printf("Internal error in flip(): Attempt to flip a segment.\n"); + lnextself(*flipedge); + return; + } + } +#endif /* SELF_CHECK */ + apex(top, farpoint); + + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(*flipedge, botleft); + sym(botleft, botlcasing); + lprev(*flipedge, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn counterclockwise. */ + bond(topleft, botlcasing); + bond(botleft, botrcasing); + bond(botright, toprcasing); + bond(topright, toplcasing); + + if (checksegments) { + /* Check for shell edges and rebond them to the quadrilateral. */ + tspivot(topleft, toplshelle); + tspivot(botleft, botlshelle); + tspivot(botright, botrshelle); + tspivot(topright, toprshelle); + if (toplshelle.sh == dummysh) { + tsdissolve(topright); + } else { + tsbond(topright, toplshelle); + } + if (botlshelle.sh == dummysh) { + tsdissolve(topleft); + } else { + tsbond(topleft, botlshelle); + } + if (botrshelle.sh == dummysh) { + tsdissolve(botleft); + } else { + tsbond(botleft, botrshelle); + } + if (toprshelle.sh == dummysh) { + tsdissolve(botright); + } else { + tsbond(botright, toprshelle); + } + } + + /* New point assignments for the rotated quadrilateral. */ + setorg(*flipedge, farpoint); + setdest(*flipedge, botpoint); + setapex(*flipedge, rightpoint); + setorg(top, botpoint); + setdest(top, farpoint); + setapex(top, leftpoint); + if (verbose > 2) { + printf(" Edge flip results in left "); + lnextself(topleft); + printtriangle(&topleft); + printf(" and right "); + printtriangle(flipedge); + } +} + +/*****************************************************************************/ +/* */ +/* insertsite() Insert a vertex into a Delaunay triangulation, */ +/* performing flips as necessary to maintain the Delaunay */ +/* property. */ +/* */ +/* The point `insertpoint' is located. If `searchtri.tri' is not NULL, */ +/* the search for the containing triangle begins from `searchtri'. If */ +/* `searchtri.tri' is NULL, a full point location procedure is called. */ +/* If `insertpoint' is found inside a triangle, the triangle is split into */ +/* three; if `insertpoint' lies on an edge, the edge is split in two, */ +/* thereby splitting the two adjacent triangles into four. Edge flips are */ +/* used to restore the Delaunay property. If `insertpoint' lies on an */ +/* existing vertex, no action is taken, and the value DUPLICATEPOINT is */ +/* returned. On return, `searchtri' is set to a handle whose origin is the */ +/* existing vertex. */ +/* */ +/* Normally, the parameter `splitedge' is set to NULL, implying that no */ +/* segment should be split. In this case, if `insertpoint' is found to */ +/* lie on a segment, no action is taken, and the value VIOLATINGPOINT is */ +/* returned. On return, `searchtri' is set to a handle whose primary edge */ +/* is the violated segment. */ +/* */ +/* If the calling routine wishes to split a segment by inserting a point in */ +/* it, the parameter `splitedge' should be that segment. In this case, */ +/* `searchtri' MUST be the triangle handle reached by pivoting from that */ +/* segment; no point location is done. */ +/* */ +/* `segmentflaws' and `triflaws' are flags that indicate whether or not */ +/* there should be checks for the creation of encroached segments or bad */ +/* quality faces. If a newly inserted point encroaches upon segments, */ +/* these segments are added to the list of segments to be split if */ +/* `segmentflaws' is set. If bad triangles are created, these are added */ +/* to the queue if `triflaws' is set. */ +/* */ +/* If a duplicate point or violated segment does not prevent the point */ +/* from being inserted, the return value will be ENCROACHINGPOINT if the */ +/* point encroaches upon a segment (and checking is enabled), or */ +/* SUCCESSFULPOINT otherwise. In either case, `searchtri' is set to a */ +/* handle whose origin is the newly inserted vertex. */ +/* */ +/* insertsite() does not use flip() for reasons of speed; some */ +/* information can be reused from edge flip to edge flip, like the */ +/* locations of shell edges. */ +/* */ +/*****************************************************************************/ + +enum mesh2d::insertsiteresult +mesh2d::insertsite(point insertpoint, struct triedge *searchtri, + struct edge *splitedge, int segmentflaws, int triflaws) +{ + struct triedge horiz; + struct triedge top; + struct triedge botleft, botright; + struct triedge topleft, topright; + struct triedge newbotleft, newbotright; + struct triedge newtopright; + struct triedge botlcasing, botrcasing; + struct triedge toplcasing, toprcasing; + struct triedge testtri; + struct edge botlshelle, botrshelle; + struct edge toplshelle, toprshelle; + struct edge brokenshelle; + struct edge checkshelle; + struct edge rightedge; + struct edge newedge; + struct edge *encroached; + point first; + point leftpoint, rightpoint, botpoint, toppoint, farpoint; + REAL attrib; + REAL area; + enum insertsiteresult success; + enum locateresult intersect; + int doflip; + int mirrorflag; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by spivot() and tspivot(). */ + + if (verbose > 1) { + printf(" Inserting (%.12g, %.12g).\n", insertpoint[0], insertpoint[1]); + } + if (splitedge == (struct edge *) NULL) { + /* Find the location of the point to be inserted. Check if a good */ + /* starting triangle has already been provided by the caller. */ + if (searchtri->tri == (triangle *) NULL) { + /* Find a boundary triangle. */ + horiz.tri = dummytri; + horiz.orient = 0; + symself(horiz); + /* Search for a triangle containing `insertpoint'. */ + intersect = locate(insertpoint, &horiz); + } else { + /* Start searching from the triangle provided by the caller. */ + triedgecopy(*searchtri, horiz); + intersect = preciselocate(insertpoint, &horiz); + } + } else { + /* The calling routine provides the edge in which the point is inserted. */ + triedgecopy(*searchtri, horiz); + intersect = ONEDGE; + } + if (intersect == ONVERTEX) { + /* There's already a vertex there. Return in `searchtri' a triangle */ + /* whose origin is the existing vertex. */ + triedgecopy(horiz, *searchtri); + triedgecopy(horiz, recenttri); + return DUPLICATEPOINT; + } + if ((intersect == ONEDGE) || (intersect == OUTSIDE)) { + /* The vertex falls on an edge or boundary. */ + if (checksegments && (splitedge == (struct edge *) NULL)) { + /* Check whether the vertex falls on a shell edge. */ + tspivot(horiz, brokenshelle); + if (brokenshelle.sh != dummysh) { + /* The vertex falls on a shell edge. */ + /* Return a handle whose primary edge contains the point, */ + /* which has not been inserted. */ + triedgecopy(horiz, *searchtri); + triedgecopy(horiz, recenttri); + return VIOLATINGPOINT; + } + } + /* Insert the point on an edge, dividing one triangle into two (if */ + /* the edge lies on a boundary) or two triangles into four. */ + lprev(horiz, botright); + sym(botright, botrcasing); + sym(horiz, topright); + /* Is there a second triangle? (Or does this edge lie on a boundary?) */ + mirrorflag = topright.tri != dummytri; + if (mirrorflag) { + lnextself(topright); + sym(topright, toprcasing); + maketriangle(&newtopright); + } else { + /* Splitting the boundary edge increases the number of boundary edges. */ + hullsize++; + } + maketriangle(&newbotright); + + /* Set the vertices of changed and new triangles. */ + org(horiz, rightpoint); + dest(horiz, leftpoint); + apex(horiz, botpoint); + setorg(newbotright, botpoint); + setdest(newbotright, rightpoint); + setapex(newbotright, insertpoint); + setorg(horiz, insertpoint); + for (i = 0; i < eextras; i++) { + /* Set the element attributes of a new triangle. */ + setelemattribute(newbotright, i, elemattribute(botright, i)); + } + if (vararea) { + /* Set the area constraint of a new triangle. */ + setareabound(newbotright, areabound(botright)); + } + if (mirrorflag) { + dest(topright, toppoint); + setorg(newtopright, rightpoint); + setdest(newtopright, toppoint); + setapex(newtopright, insertpoint); + setorg(topright, insertpoint); + for (i = 0; i < eextras; i++) { + /* Set the element attributes of another new triangle. */ + setelemattribute(newtopright, i, elemattribute(topright, i)); + } + if (vararea) { + /* Set the area constraint of another new triangle. */ + setareabound(newtopright, areabound(topright)); + } + } + + /* There may be shell edges that need to be bonded */ + /* to the new triangle(s). */ + if (checksegments) { + tspivot(botright, botrshelle); + if (botrshelle.sh != dummysh) { + tsdissolve(botright); + tsbond(newbotright, botrshelle); + } + if (mirrorflag) { + tspivot(topright, toprshelle); + if (toprshelle.sh != dummysh) { + tsdissolve(topright); + tsbond(newtopright, toprshelle); + } + } + } + + /* Bond the new triangle(s) to the surrounding triangles. */ + bond(newbotright, botrcasing); + lprevself(newbotright); + bond(newbotright, botright); + lprevself(newbotright); + if (mirrorflag) { + bond(newtopright, toprcasing); + lnextself(newtopright); + bond(newtopright, topright); + lnextself(newtopright); + bond(newtopright, newbotright); + } + + if (splitedge != (struct edge *) NULL) { + /* Split the shell edge into two. */ + setsdest(*splitedge, insertpoint); + ssymself(*splitedge); + spivot(*splitedge, rightedge); + insertshelle(&newbotright, mark(*splitedge)); + tspivot(newbotright, newedge); + sbond(*splitedge, newedge); + ssymself(newedge); + sbond(newedge, rightedge); + ssymself(*splitedge); + } + +#ifdef SELF_CHECK + if (counterclockwise(rightpoint, leftpoint, botpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to edge point insertion (bottom).\n"); + } + if (mirrorflag) { + if (counterclockwise(leftpoint, rightpoint, toppoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to edge point insertion (top).\n"); + } + if (counterclockwise(rightpoint, toppoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge point insertion (top right).\n" + ); + } + if (counterclockwise(toppoint, leftpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge point insertion (top left).\n" + ); + } + } + if (counterclockwise(leftpoint, botpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge point insertion (bottom left).\n" + ); + } + if (counterclockwise(botpoint, rightpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf( + " Clockwise triangle after edge point insertion (bottom right).\n"); + } +#endif /* SELF_CHECK */ + if (verbose > 2) { + printf(" Updating bottom left "); + printtriangle(&botright); + if (mirrorflag) { + printf(" Updating top left "); + printtriangle(&topright); + printf(" Creating top right "); + printtriangle(&newtopright); + } + printf(" Creating bottom right "); + printtriangle(&newbotright); + } + + /* Position `horiz' on the first edge to check for */ + /* the Delaunay property. */ + lnextself(horiz); + } else { + /* Insert the point in a triangle, splitting it into three. */ + lnext(horiz, botleft); + lprev(horiz, botright); + sym(botleft, botlcasing); + sym(botright, botrcasing); + maketriangle(&newbotleft); + maketriangle(&newbotright); + + /* Set the vertices of changed and new triangles. */ + org(horiz, rightpoint); + dest(horiz, leftpoint); + apex(horiz, botpoint); + setorg(newbotleft, leftpoint); + setdest(newbotleft, botpoint); + setapex(newbotleft, insertpoint); + setorg(newbotright, botpoint); + setdest(newbotright, rightpoint); + setapex(newbotright, insertpoint); + setapex(horiz, insertpoint); + for (i = 0; i < eextras; i++) { + /* Set the element attributes of the new triangles. */ + attrib = elemattribute(horiz, i); + setelemattribute(newbotleft, i, attrib); + setelemattribute(newbotright, i, attrib); + } + if (vararea) { + /* Set the area constraint of the new triangles. */ + area = areabound(horiz); + setareabound(newbotleft, area); + setareabound(newbotright, area); + } + + /* There may be shell edges that need to be bonded */ + /* to the new triangles. */ + if (checksegments) { + tspivot(botleft, botlshelle); + if (botlshelle.sh != dummysh) { + tsdissolve(botleft); + tsbond(newbotleft, botlshelle); + } + tspivot(botright, botrshelle); + if (botrshelle.sh != dummysh) { + tsdissolve(botright); + tsbond(newbotright, botrshelle); + } + } + + /* Bond the new triangles to the surrounding triangles. */ + bond(newbotleft, botlcasing); + bond(newbotright, botrcasing); + lnextself(newbotleft); + lprevself(newbotright); + bond(newbotleft, newbotright); + lnextself(newbotleft); + bond(botleft, newbotleft); + lprevself(newbotright); + bond(botright, newbotright); + +#ifdef SELF_CHECK + if (counterclockwise(rightpoint, leftpoint, botpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to point insertion.\n"); + } + if (counterclockwise(rightpoint, leftpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after point insertion (top).\n"); + } + if (counterclockwise(leftpoint, botpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after point insertion (left).\n"); + } + if (counterclockwise(botpoint, rightpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after point insertion (right).\n"); + } +#endif /* SELF_CHECK */ + if (verbose > 2) { + printf(" Updating top "); + printtriangle(&horiz); + printf(" Creating left "); + printtriangle(&newbotleft); + printf(" Creating right "); + printtriangle(&newbotright); + } + } + + /* The insertion is successful by default, unless an encroached */ + /* edge is found. */ + success = SUCCESSFULPOINT; + /* Circle around the newly inserted vertex, checking each edge opposite */ + /* it for the Delaunay property. Non-Delaunay edges are flipped. */ + /* `horiz' is always the edge being checked. `first' marks where to */ + /* stop circling. */ + org(horiz, first); + rightpoint = first; + dest(horiz, leftpoint); + /* Circle until finished. */ + while (1) { + /* By default, the edge will be flipped. */ + doflip = 1; + if (checksegments) { + /* Check for a segment, which cannot be flipped. */ + tspivot(horiz, checkshelle); + if (checkshelle.sh != dummysh) { + /* The edge is a segment and cannot be flipped. */ + doflip = 0; + } + } + if (doflip) { + /* Check if the edge is a boundary edge. */ + sym(horiz, top); + if (top.tri == dummytri) { + /* The edge is a boundary edge and cannot be flipped. */ + doflip = 0; + } else { + /* Find the point on the other side of the edge. */ + apex(top, farpoint); + /* In the incremental Delaunay triangulation algorithm, any of */ + /* `leftpoint', `rightpoint', and `farpoint' could be vertices */ + /* of the triangular bounding box. These vertices must be */ + /* treated as if they are infinitely distant, even though their */ + /* "coordinates" are not. */ + if ((leftpoint == infpoint1) || (leftpoint == infpoint2) + || (leftpoint == infpoint3)) { + /* `leftpoint' is infinitely distant. Check the convexity of */ + /* the boundary of the triangulation. 'farpoint' might be */ + /* infinite as well, but trust me, this same condition */ + /* should be applied. */ + doflip = counterclockwise(insertpoint, rightpoint, farpoint) > 0.0; + } else if ((rightpoint == infpoint1) || (rightpoint == infpoint2) + || (rightpoint == infpoint3)) { + /* `rightpoint' is infinitely distant. Check the convexity of */ + /* the boundary of the triangulation. 'farpoint' might be */ + /* infinite as well, but trust me, this same condition */ + /* should be applied. */ + doflip = counterclockwise(farpoint, leftpoint, insertpoint) > 0.0; + } else if ((farpoint == infpoint1) || (farpoint == infpoint2) + || (farpoint == infpoint3)) { + /* `farpoint' is infinitely distant and cannot be inside */ + /* the circumcircle of the triangle `horiz'. */ + doflip = 0; + } else { + /* Test whether the edge is locally Delaunay. */ + doflip = iincircle(leftpoint, insertpoint, rightpoint, farpoint) + > 0.0; + } + if (doflip) { + /* We made it! Flip the edge `horiz' by rotating its containing */ + /* quadrilateral (the two triangles adjacent to `horiz'). */ + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(horiz, botleft); + sym(botleft, botlcasing); + lprev(horiz, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn counterclockwise. */ + bond(topleft, botlcasing); + bond(botleft, botrcasing); + bond(botright, toprcasing); + bond(topright, toplcasing); + if (checksegments) { + /* Check for shell edges and rebond them to the quadrilateral. */ + tspivot(topleft, toplshelle); + tspivot(botleft, botlshelle); + tspivot(botright, botrshelle); + tspivot(topright, toprshelle); + if (toplshelle.sh == dummysh) { + tsdissolve(topright); + } else { + tsbond(topright, toplshelle); + } + if (botlshelle.sh == dummysh) { + tsdissolve(topleft); + } else { + tsbond(topleft, botlshelle); + } + if (botrshelle.sh == dummysh) { + tsdissolve(botleft); + } else { + tsbond(botleft, botrshelle); + } + if (toprshelle.sh == dummysh) { + tsdissolve(botright); + } else { + tsbond(botright, toprshelle); + } + } + /* New point assignments for the rotated quadrilateral. */ + setorg(horiz, farpoint); + setdest(horiz, insertpoint); + setapex(horiz, rightpoint); + setorg(top, insertpoint); + setdest(top, farpoint); + setapex(top, leftpoint); + for (i = 0; i < eextras; i++) { + /* Take the average of the two triangles' attributes. */ + attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i)); + setelemattribute(top, i, attrib); + setelemattribute(horiz, i, attrib); + } + if (vararea) { + if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) { + area = -1.0; + } else { + /* Take the average of the two triangles' area constraints. */ + /* This prevents small area constraints from migrating a */ + /* long, long way from their original location due to flips. */ + area = 0.5 * (areabound(top) + areabound(horiz)); + } + setareabound(top, area); + setareabound(horiz, area); + } +#ifdef SELF_CHECK + if (insertpoint != (point) NULL) { + if (counterclockwise(leftpoint, insertpoint, rightpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to edge flip (bottom).\n"); + } + /* The following test has been removed because constrainededge() */ + /* sometimes generates inverted triangles that insertsite() */ + /* removes. */ +/* + if (counterclockwise(rightpoint, farpoint, leftpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle prior to edge flip (top).\n"); + } +*/ + if (counterclockwise(farpoint, leftpoint, insertpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge flip (left).\n"); + } + if (counterclockwise(insertpoint, rightpoint, farpoint) < 0.0) { + printf("Internal error in insertsite():\n"); + printf(" Clockwise triangle after edge flip (right).\n"); + } + } +#endif /* SELF_CHECK */ + if (verbose > 2) { + printf(" Edge flip results in left "); + lnextself(topleft); + printtriangle(&topleft); + printf(" and right "); + printtriangle(&horiz); + } + /* On the next iterations, consider the two edges that were */ + /* exposed (this is, are now visible to the newly inserted */ + /* point) by the edge flip. */ + lprevself(horiz); + leftpoint = farpoint; + } + } + } + if (!doflip) { + /* The handle `horiz' is accepted as locally Delaunay. */ + /* Look for the next edge around the newly inserted point. */ + lnextself(horiz); + sym(horiz, testtri); + /* Check for finishing a complete revolution about the new point, or */ + /* falling off the edge of the triangulation. The latter will */ + /* happen when a point is inserted at a boundary. */ + if ((leftpoint == first) || (testtri.tri == dummytri)) { + /* We're done. Return a triangle whose origin is the new point. */ + lnext(horiz, *searchtri); + lnext(horiz, recenttri); + return success; + } + /* Finish finding the next edge around the newly inserted point. */ + lnext(testtri, horiz); + rightpoint = leftpoint; + dest(horiz, leftpoint); + } + } +} + +/** **/ +/** **/ +/********* Mesh transformation routines end here *********/ + +/********* Divide-and-conquer Delaunay triangulation begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* The divide-and-conquer bounding box */ +/* */ +/* I originally implemented the divide-and-conquer and incremental Delaunay */ +/* triangulations using the edge-based data structure presented by Guibas */ +/* and Stolfi. Switching to a triangle-based data structure doubled the */ +/* speed. However, I had to think of a few extra tricks to maintain the */ +/* elegance of the original algorithms. */ +/* */ +/* The "bounding box" used by my variant of the divide-and-conquer */ +/* algorithm uses one triangle for each edge of the convex hull of the */ +/* triangulation. These bounding triangles all share a common apical */ +/* vertex, which is represented by NULL and which represents nothing. */ +/* The bounding triangles are linked in a circular fan about this NULL */ +/* vertex, and the edges on the convex hull of the triangulation appear */ +/* opposite the NULL vertex. You might find it easiest to imagine that */ +/* the NULL vertex is a point in 3D space behind the center of the */ +/* triangulation, and that the bounding triangles form a sort of cone. */ +/* */ +/* This bounding box makes it easy to represent degenerate cases. For */ +/* instance, the triangulation of two vertices is a single edge. This edge */ +/* is represented by two bounding box triangles, one on each "side" of the */ +/* edge. These triangles are also linked together in a fan about the NULL */ +/* vertex. */ +/* */ +/* The bounding box also makes it easy to traverse the convex hull, as the */ +/* divide-and-conquer algorithm needs to do. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* pointsort() Sort an array of points by x-coordinate, using the */ +/* y-coordinate as a secondary key. */ +/* */ +/* Uses quicksort. Randomized O(n log n) time. No, I did not make any of */ +/* the usual quicksort mistakes. */ +/* */ +/*****************************************************************************/ + +void mesh2d::pointsort(point *sortarray, int arraysize) +{ + int left, right; + int pivot; + REAL pivotx, pivoty; + point temp; + + if (arraysize == 2) { + /* Recursive base case. */ + if ((sortarray[0][0] > sortarray[1][0]) || + ((sortarray[0][0] == sortarray[1][0]) && + (sortarray[0][1] > sortarray[1][1]))) { + temp = sortarray[1]; + sortarray[1] = sortarray[0]; + sortarray[0] = temp; + } + return; + } + /* Choose a random pivot to split the array. */ + pivot = (int) randomnation(arraysize); + pivotx = sortarray[pivot][0]; + pivoty = sortarray[pivot][1]; + /* Split the array. */ + left = -1; + right = arraysize; + while (left < right) { + /* Search for a point whose x-coordinate is too large for the left. */ + do { + left++; + } while ((left <= right) && ((sortarray[left][0] < pivotx) || + ((sortarray[left][0] == pivotx) && + (sortarray[left][1] < pivoty)))); + /* Search for a point whose x-coordinate is too small for the right. */ + do { + right--; + } while ((left <= right) && ((sortarray[right][0] > pivotx) || + ((sortarray[right][0] == pivotx) && + (sortarray[right][1] > pivoty)))); + if (left < right) { + /* Swap the left and right points. */ + temp = sortarray[left]; + sortarray[left] = sortarray[right]; + sortarray[right] = temp; + } + } + if (left > 1) { + /* Recursively sort the left subset. */ + pointsort(sortarray, left); + } + if (right < arraysize - 2) { + /* Recursively sort the right subset. */ + pointsort(&sortarray[right + 1], arraysize - right - 1); + } +} + +/*****************************************************************************/ +/* */ +/* pointmedian() An order statistic algorithm, almost. Shuffles an array */ +/* of points so that the first `median' points occur */ +/* lexicographically before the remaining points. */ +/* */ +/* Uses the x-coordinate as the primary key if axis == 0; the y-coordinate */ +/* if axis == 1. Very similar to the pointsort() procedure, but runs in */ +/* randomized linear time. */ +/* */ +/*****************************************************************************/ + +void mesh2d::pointmedian(point *sortarray, int arraysize, int median, int axis) +{ + int left, right; + int pivot; + REAL pivot1, pivot2; + point temp; + + if (arraysize == 2) { + /* Recursive base case. */ + if ((sortarray[0][axis] > sortarray[1][axis]) || + ((sortarray[0][axis] == sortarray[1][axis]) && + (sortarray[0][1 - axis] > sortarray[1][1 - axis]))) { + temp = sortarray[1]; + sortarray[1] = sortarray[0]; + sortarray[0] = temp; + } + return; + } + /* Choose a random pivot to split the array. */ + pivot = (int) randomnation(arraysize); + pivot1 = sortarray[pivot][axis]; + pivot2 = sortarray[pivot][1 - axis]; + /* Split the array. */ + left = -1; + right = arraysize; + while (left < right) { + /* Search for a point whose x-coordinate is too large for the left. */ + do { + left++; + } while ((left <= right) && ((sortarray[left][axis] < pivot1) || + ((sortarray[left][axis] == pivot1) && + (sortarray[left][1 - axis] < pivot2)))); + /* Search for a point whose x-coordinate is too small for the right. */ + do { + right--; + } while ((left <= right) && ((sortarray[right][axis] > pivot1) || + ((sortarray[right][axis] == pivot1) && + (sortarray[right][1 - axis] > pivot2)))); + if (left < right) { + /* Swap the left and right points. */ + temp = sortarray[left]; + sortarray[left] = sortarray[right]; + sortarray[right] = temp; + } + } + /* Unlike in pointsort(), at most one of the following */ + /* conditionals is true. */ + if (left > median) { + /* Recursively shuffle the left subset. */ + pointmedian(sortarray, left, median, axis); + } + if (right < median - 1) { + /* Recursively shuffle the right subset. */ + pointmedian(&sortarray[right + 1], arraysize - right - 1, + median - right - 1, axis); + } +} + +/*****************************************************************************/ +/* */ +/* alternateaxes() Sorts the points as appropriate for the divide-and- */ +/* conquer algorithm with alternating cuts. */ +/* */ +/* Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1. */ +/* For the base case, subsets containing only two or three points are */ +/* always sorted by x-coordinate. */ +/* */ +/*****************************************************************************/ + +void mesh2d::alternateaxes(point *sortarray, int arraysize, int axis) +{ + int divider; + + divider = arraysize >> 1; + if (arraysize <= 3) { + /* Recursive base case: subsets of two or three points will be */ + /* handled specially, and should always be sorted by x-coordinate. */ + axis = 0; + } + /* Partition with a horizontal or vertical cut. */ + pointmedian(sortarray, arraysize, divider, axis); + /* Recursively partition the subsets with a cross cut. */ + if (arraysize - divider >= 2) { + if (divider >= 2) { + alternateaxes(sortarray, divider, 1 - axis); + } + alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis); + } +} + +/*****************************************************************************/ +/* */ +/* mergehulls() Merge two adjacent Delaunay triangulations into a */ +/* single Delaunay triangulation. */ +/* */ +/* This is similar to the algorithm given by Guibas and Stolfi, but uses */ +/* a triangle-based, rather than edge-based, data structure. */ +/* */ +/* The algorithm walks up the gap between the two triangulations, knitting */ +/* them together. As they are merged, some of their bounding triangles */ +/* are converted into real triangles of the triangulation. The procedure */ +/* pulls each hull's bounding triangles apart, then knits them together */ +/* like the teeth of two gears. The Delaunay property determines, at each */ +/* step, whether the next "tooth" is a bounding triangle of the left hull */ +/* or the right. When a bounding triangle becomes real, its apex is */ +/* changed from NULL to a real point. */ +/* */ +/* Only two new triangles need to be allocated. These become new bounding */ +/* triangles at the top and bottom of the seam. They are used to connect */ +/* the remaining bounding triangles (those that have not been converted */ +/* into real triangles) into a single fan. */ +/* */ +/* On entry, `farleft' and `innerleft' are bounding triangles of the left */ +/* triangulation. The origin of `farleft' is the leftmost vertex, and */ +/* the destination of `innerleft' is the rightmost vertex of the */ +/* triangulation. Similarly, `innerright' and `farright' are bounding */ +/* triangles of the right triangulation. The origin of `innerright' and */ +/* destination of `farright' are the leftmost and rightmost vertices. */ +/* */ +/* On completion, the origin of `farleft' is the leftmost vertex of the */ +/* merged triangulation, and the destination of `farright' is the rightmost */ +/* vertex. */ +/* */ +/*****************************************************************************/ + +void mesh2d::mergehulls(struct triedge *farleft, struct triedge *innerleft, + struct triedge *innerright, struct triedge *farright, + int axis) +{ + struct triedge leftcand, rightcand; + struct triedge baseedge; + struct triedge nextedge; + struct triedge sidecasing, topcasing, outercasing; + struct triedge checkedge; + point innerleftdest; + point innerrightorg; + point innerleftapex, innerrightapex; + point farleftpt, farrightpt; + point farleftapex, farrightapex; + point lowerleft, lowerright; + point upperleft, upperright; + point nextapex; + point checkvertex; + int changemade; + int badedge; + int leftfinished, rightfinished; + triangle ptr; /* Temporary variable used by sym(). */ + + dest(*innerleft, innerleftdest); + apex(*innerleft, innerleftapex); + org(*innerright, innerrightorg); + apex(*innerright, innerrightapex); + /* Special treatment for horizontal cuts. */ + if (dwyer && (axis == 1)) { + org(*farleft, farleftpt); + apex(*farleft, farleftapex); + dest(*farright, farrightpt); + apex(*farright, farrightapex); + /* The pointers to the extremal points are shifted to point to the */ + /* topmost and bottommost point of each hull, rather than the */ + /* leftmost and rightmost points. */ + while (farleftapex[1] < farleftpt[1]) { + lnextself(*farleft); + symself(*farleft); + farleftpt = farleftapex; + apex(*farleft, farleftapex); + } + sym(*innerleft, checkedge); + apex(checkedge, checkvertex); + while (checkvertex[1] > innerleftdest[1]) { + lnext(checkedge, *innerleft); + innerleftapex = innerleftdest; + innerleftdest = checkvertex; + sym(*innerleft, checkedge); + apex(checkedge, checkvertex); + } + while (innerrightapex[1] < innerrightorg[1]) { + lnextself(*innerright); + symself(*innerright); + innerrightorg = innerrightapex; + apex(*innerright, innerrightapex); + } + sym(*farright, checkedge); + apex(checkedge, checkvertex); + while (checkvertex[1] > farrightpt[1]) { + lnext(checkedge, *farright); + farrightapex = farrightpt; + farrightpt = checkvertex; + sym(*farright, checkedge); + apex(checkedge, checkvertex); + } + } + /* Find a line tangent to and below both hulls. */ + do { + changemade = 0; + /* Make innerleftdest the "bottommost" point of the left hull. */ + if (counterclockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0) { + lprevself(*innerleft); + symself(*innerleft); + innerleftdest = innerleftapex; + apex(*innerleft, innerleftapex); + changemade = 1; + } + /* Make innerrightorg the "bottommost" point of the right hull. */ + if (counterclockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0) { + lnextself(*innerright); + symself(*innerright); + innerrightorg = innerrightapex; + apex(*innerright, innerrightapex); + changemade = 1; + } + } while (changemade); + /* Find the two candidates to be the next "gear tooth". */ + sym(*innerleft, leftcand); + sym(*innerright, rightcand); + /* Create the bottom new bounding triangle. */ + maketriangle(&baseedge); + /* Connect it to the bounding boxes of the left and right triangulations. */ + bond(baseedge, *innerleft); + lnextself(baseedge); + bond(baseedge, *innerright); + lnextself(baseedge); + setorg(baseedge, innerrightorg); + setdest(baseedge, innerleftdest); + /* Apex is intentionally left NULL. */ + if (verbose > 2) { + printf(" Creating base bounding "); + printtriangle(&baseedge); + } + /* Fix the extreme triangles if necessary. */ + org(*farleft, farleftpt); + if (innerleftdest == farleftpt) { + lnext(baseedge, *farleft); + } + dest(*farright, farrightpt); + if (innerrightorg == farrightpt) { + lprev(baseedge, *farright); + } + /* The vertices of the current knitting edge. */ + lowerleft = innerleftdest; + lowerright = innerrightorg; + /* The candidate vertices for knitting. */ + apex(leftcand, upperleft); + apex(rightcand, upperright); + /* Walk up the gap between the two triangulations, knitting them together. */ + while (1) { + /* Have we reached the top? (This isn't quite the right question, */ + /* because even though the left triangulation might seem finished now, */ + /* moving up on the right triangulation might reveal a new point of */ + /* the left triangulation. And vice-versa.) */ + leftfinished = counterclockwise(upperleft, lowerleft, lowerright) <= 0.0; + rightfinished = counterclockwise(upperright, lowerleft, lowerright) <= 0.0; + if (leftfinished && rightfinished) { + /* Create the top new bounding triangle. */ + maketriangle(&nextedge); + setorg(nextedge, lowerleft); + setdest(nextedge, lowerright); + /* Apex is intentionally left NULL. */ + /* Connect it to the bounding boxes of the two triangulations. */ + bond(nextedge, baseedge); + lnextself(nextedge); + bond(nextedge, rightcand); + lnextself(nextedge); + bond(nextedge, leftcand); + if (verbose > 2) { + printf(" Creating top bounding "); + printtriangle(&baseedge); + } + /* Special treatment for horizontal cuts. */ + if (dwyer && (axis == 1)) { + org(*farleft, farleftpt); + apex(*farleft, farleftapex); + dest(*farright, farrightpt); + apex(*farright, farrightapex); + sym(*farleft, checkedge); + apex(checkedge, checkvertex); + /* The pointers to the extremal points are restored to the leftmost */ + /* and rightmost points (rather than topmost and bottommost). */ + while (checkvertex[0] < farleftpt[0]) { + lprev(checkedge, *farleft); + farleftapex = farleftpt; + farleftpt = checkvertex; + sym(*farleft, checkedge); + apex(checkedge, checkvertex); + } + while (farrightapex[0] > farrightpt[0]) { + lprevself(*farright); + symself(*farright); + farrightpt = farrightapex; + apex(*farright, farrightapex); + } + } + return; + } + /* Consider eliminating edges from the left triangulation. */ + if (!leftfinished) { + /* What vertex would be exposed if an edge were deleted? */ + lprev(leftcand, nextedge); + symself(nextedge); + apex(nextedge, nextapex); + /* If nextapex is NULL, then no vertex would be exposed; the */ + /* triangulation would have been eaten right through. */ + if (nextapex != (point) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = iincircle(lowerleft, lowerright, upperleft, nextapex) > 0.0; + while (badedge) { + /* Eliminate the edge with an edge flip. As a result, the */ + /* left triangulation will have one more boundary triangle. */ + lnextself(nextedge); + sym(nextedge, topcasing); + lnextself(nextedge); + sym(nextedge, sidecasing); + bond(nextedge, topcasing); + bond(leftcand, sidecasing); + lnextself(leftcand); + sym(leftcand, outercasing); + lprevself(nextedge); + bond(nextedge, outercasing); + /* Correct the vertices to reflect the edge flip. */ + setorg(leftcand, lowerleft); + setdest(leftcand, NULL); + setapex(leftcand, nextapex); + setorg(nextedge, NULL); + setdest(nextedge, upperleft); + setapex(nextedge, nextapex); + /* Consider the newly exposed vertex. */ + upperleft = nextapex; + /* What vertex would be exposed if another edge were deleted? */ + triedgecopy(sidecasing, nextedge); + apex(nextedge, nextapex); + if (nextapex != (point) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = iincircle(lowerleft, lowerright, upperleft, nextapex) + > 0.0; + } else { + /* Avoid eating right through the triangulation. */ + badedge = 0; + } + } + } + } + /* Consider eliminating edges from the right triangulation. */ + if (!rightfinished) { + /* What vertex would be exposed if an edge were deleted? */ + lnext(rightcand, nextedge); + symself(nextedge); + apex(nextedge, nextapex); + /* If nextapex is NULL, then no vertex would be exposed; the */ + /* triangulation would have been eaten right through. */ + if (nextapex != (point) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = iincircle(lowerleft, lowerright, upperright, nextapex) > 0.0; + while (badedge) { + /* Eliminate the edge with an edge flip. As a result, the */ + /* right triangulation will have one more boundary triangle. */ + lprevself(nextedge); + sym(nextedge, topcasing); + lprevself(nextedge); + sym(nextedge, sidecasing); + bond(nextedge, topcasing); + bond(rightcand, sidecasing); + lprevself(rightcand); + sym(rightcand, outercasing); + lnextself(nextedge); + bond(nextedge, outercasing); + /* Correct the vertices to reflect the edge flip. */ + setorg(rightcand, NULL); + setdest(rightcand, lowerright); + setapex(rightcand, nextapex); + setorg(nextedge, upperright); + setdest(nextedge, NULL); + setapex(nextedge, nextapex); + /* Consider the newly exposed vertex. */ + upperright = nextapex; + /* What vertex would be exposed if another edge were deleted? */ + triedgecopy(sidecasing, nextedge); + apex(nextedge, nextapex); + if (nextapex != (point) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = iincircle(lowerleft, lowerright, upperright, nextapex) + > 0.0; + } else { + /* Avoid eating right through the triangulation. */ + badedge = 0; + } + } + } + } + if (leftfinished || (!rightfinished && + (iincircle(upperleft, lowerleft, lowerright, upperright) > 0.0))) { + /* Knit the triangulations, adding an edge from `lowerleft' */ + /* to `upperright'. */ + bond(baseedge, rightcand); + lprev(rightcand, baseedge); + setdest(baseedge, lowerleft); + lowerright = upperright; + sym(baseedge, rightcand); + apex(rightcand, upperright); + } else { + /* Knit the triangulations, adding an edge from `upperleft' */ + /* to `lowerright'. */ + bond(baseedge, leftcand); + lnext(leftcand, baseedge); + setorg(baseedge, lowerright); + lowerleft = upperleft; + sym(baseedge, leftcand); + apex(leftcand, upperleft); + } + if (verbose > 2) { + printf(" Connecting "); + printtriangle(&baseedge); + } + } +} + +/*****************************************************************************/ +/* */ +/* divconqrecurse() Recursively form a Delaunay triangulation by the */ +/* divide-and-conquer method. */ +/* */ +/* Recursively breaks down the problem into smaller pieces, which are */ +/* knitted together by mergehulls(). The base cases (problems of two or */ +/* three points) are handled specially here. */ +/* */ +/* On completion, `farleft' and `farright' are bounding triangles such that */ +/* the origin of `farleft' is the leftmost vertex (breaking ties by */ +/* choosing the highest leftmost vertex), and the destination of */ +/* `farright' is the rightmost vertex (breaking ties by choosing the */ +/* lowest rightmost vertex). */ +/* */ +/*****************************************************************************/ + +void mesh2d::divconqrecurse(point *sortarray, int vertices, int axis, + struct triedge *farleft, struct triedge *farright) +{ + struct triedge midtri, tri1, tri2, tri3; + struct triedge innerleft, innerright; + REAL area; + int divider; + + if (verbose > 2) { + printf(" Triangulating %d points.\n", vertices); + } + if (vertices == 2) { + /* The triangulation of two vertices is an edge. An edge is */ + /* represented by two bounding triangles. */ + maketriangle(farleft); + setorg(*farleft, sortarray[0]); + setdest(*farleft, sortarray[1]); + /* The apex is intentionally left NULL. */ + maketriangle(farright); + setorg(*farright, sortarray[1]); + setdest(*farright, sortarray[0]); + /* The apex is intentionally left NULL. */ + bond(*farleft, *farright); + lprevself(*farleft); + lnextself(*farright); + bond(*farleft, *farright); + lprevself(*farleft); + lnextself(*farright); + bond(*farleft, *farright); + if (verbose > 2) { + printf(" Creating "); + printtriangle(farleft); + printf(" Creating "); + printtriangle(farright); + } + /* Ensure that the origin of `farleft' is sortarray[0]. */ + lprev(*farright, *farleft); + return; + } else if (vertices == 3) { + /* The triangulation of three vertices is either a triangle (with */ + /* three bounding triangles) or two edges (with four bounding */ + /* triangles). In either case, four triangles are created. */ + maketriangle(&midtri); + maketriangle(&tri1); + maketriangle(&tri2); + maketriangle(&tri3); + area = counterclockwise(sortarray[0], sortarray[1], sortarray[2]); + if (area == 0.0) { + /* Three collinear points; the triangulation is two edges. */ + setorg(midtri, sortarray[0]); + setdest(midtri, sortarray[1]); + setorg(tri1, sortarray[1]); + setdest(tri1, sortarray[0]); + setorg(tri2, sortarray[2]); + setdest(tri2, sortarray[1]); + setorg(tri3, sortarray[1]); + setdest(tri3, sortarray[2]); + /* All apices are intentionally left NULL. */ + bond(midtri, tri1); + bond(tri2, tri3); + lnextself(midtri); + lprevself(tri1); + lnextself(tri2); + lprevself(tri3); + bond(midtri, tri3); + bond(tri1, tri2); + lnextself(midtri); + lprevself(tri1); + lnextself(tri2); + lprevself(tri3); + bond(midtri, tri1); + bond(tri2, tri3); + /* Ensure that the origin of `farleft' is sortarray[0]. */ + triedgecopy(tri1, *farleft); + /* Ensure that the destination of `farright' is sortarray[2]. */ + triedgecopy(tri2, *farright); + } else { + /* The three points are not collinear; the triangulation is one */ + /* triangle, namely `midtri'. */ + setorg(midtri, sortarray[0]); + setdest(tri1, sortarray[0]); + setorg(tri3, sortarray[0]); + /* Apices of tri1, tri2, and tri3 are left NULL. */ + if (area > 0.0) { + /* The vertices are in counterclockwise order. */ + setdest(midtri, sortarray[1]); + setorg(tri1, sortarray[1]); + setdest(tri2, sortarray[1]); + setapex(midtri, sortarray[2]); + setorg(tri2, sortarray[2]); + setdest(tri3, sortarray[2]); + } else { + /* The vertices are in clockwise order. */ + setdest(midtri, sortarray[2]); + setorg(tri1, sortarray[2]); + setdest(tri2, sortarray[2]); + setapex(midtri, sortarray[1]); + setorg(tri2, sortarray[1]); + setdest(tri3, sortarray[1]); + } + /* The topology does not depend on how the vertices are ordered. */ + bond(midtri, tri1); + lnextself(midtri); + bond(midtri, tri2); + lnextself(midtri); + bond(midtri, tri3); + lprevself(tri1); + lnextself(tri2); + bond(tri1, tri2); + lprevself(tri1); + lprevself(tri3); + bond(tri1, tri3); + lnextself(tri2); + lprevself(tri3); + bond(tri2, tri3); + /* Ensure that the origin of `farleft' is sortarray[0]. */ + triedgecopy(tri1, *farleft); + /* Ensure that the destination of `farright' is sortarray[2]. */ + if (area > 0.0) { + triedgecopy(tri2, *farright); + } else { + lnext(*farleft, *farright); + } + } + if (verbose > 2) { + printf(" Creating "); + printtriangle(&midtri); + printf(" Creating "); + printtriangle(&tri1); + printf(" Creating "); + printtriangle(&tri2); + printf(" Creating "); + printtriangle(&tri3); + } + return; + } else { + /* Split the vertices in half. */ + divider = vertices >> 1; + /* Recursively triangulate each half. */ + divconqrecurse(sortarray, divider, 1 - axis, farleft, &innerleft); + divconqrecurse(&sortarray[divider], vertices - divider, 1 - axis, + &innerright, farright); + if (verbose > 1) { + printf(" Joining triangulations with %d and %d vertices.\n", divider, + vertices - divider); + } + /* Merge the two triangulations into one. */ + mergehulls(farleft, &innerleft, &innerright, farright, axis); + } +} + +long mesh2d::removeghosts(struct triedge *startghost) +{ + struct triedge searchedge; + struct triedge dissolveedge; + struct triedge deadtri; + point markorg; + long hullsize; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose) { + printf(" Removing ghost triangles.\n"); + } + /* Find an edge on the convex hull to start point location from. */ + lprev(*startghost, searchedge); + symself(searchedge); + dummytri[0] = encode(searchedge); + /* Remove the bounding box and count the convex hull edges. */ + triedgecopy(*startghost, dissolveedge); + hullsize = 0; + do { + hullsize++; + lnext(dissolveedge, deadtri); + lprevself(dissolveedge); + symself(dissolveedge); + /* If no PSLG is involved, set the boundary markers of all the points */ + /* on the convex hull. If a PSLG is used, this step is done later. */ + if (!poly) { + /* Watch out for the case where all the input points are collinear. */ + if (dissolveedge.tri != dummytri) { + org(dissolveedge, markorg); + if (pointmark(markorg) == 0) { + setpointmark(markorg, 1); + } + } + } + /* Remove a bounding triangle from a convex hull triangle. */ + dissolve(dissolveedge); + /* Find the next bounding triangle. */ + sym(deadtri, dissolveedge); + /* Delete the bounding triangle. */ + triangledealloc(deadtri.tri); + } while (!triedgeequal(dissolveedge, *startghost)); + return hullsize; +} + +/*****************************************************************************/ +/* */ +/* divconqdelaunay() Form a Delaunay triangulation by the divide-and- */ +/* conquer method. */ +/* */ +/* Sorts the points, calls a recursive procedure to triangulate them, and */ +/* removes the bounding box, setting boundary markers as appropriate. */ +/* */ +/*****************************************************************************/ + +long mesh2d::divconqdelaunay() +{ + point *sortarray; + struct triedge hullleft, hullright; + int divider; + int i, j; + + /* Allocate an array of pointers to points for sorting. */ + sortarray = (point *) new point[inpoints]; + if (sortarray == (point *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + points.traversalinit(); + for (i = 0; i < inpoints; i++) { + sortarray[i] = pointtraverse(); + } + if (verbose) { + printf(" Sorting points.\n"); + } + /* Sort the points. */ + pointsort(sortarray, inpoints); + /* Discard duplicate points, which can really mess up the algorithm. */ + i = 0; + for (j = 1; j < inpoints; j++) { + if ((sortarray[i][0] == sortarray[j][0]) + && (sortarray[i][1] == sortarray[j][1])) { + if (!quiet) { + printf( +"Warning: A duplicate point at (%.12g, %.12g) appeared and was ignored.\n", + sortarray[j][0], sortarray[j][1]); + } +/* Commented out - would eliminate point from output .node file, but causes + a failure if some segment has this point as an endpoint. + setpointmark(sortarray[j], DEADPOINT); +*/ + } else { + i++; + sortarray[i] = sortarray[j]; + } + } + i++; + if (dwyer) { + /* Re-sort the array of points to accommodate alternating cuts. */ + divider = i >> 1; + if (i - divider >= 2) { + if (divider >= 2) { + alternateaxes(sortarray, divider, 1); + } + alternateaxes(&sortarray[divider], i - divider, 1); + } + } + if (verbose) { + printf(" Forming triangulation.\n"); + } + /* Form the Delaunay triangulation. */ + divconqrecurse(sortarray, i, 0, &hullleft, &hullright); + delete [] sortarray; + + return removeghosts(&hullleft); +} + +/** **/ +/** **/ +/********* Divide-and-conquer Delaunay triangulation ends here *********/ + +/********* General mesh construction routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* delaunay() Form a Delaunay triangulation. */ +/* */ +/*****************************************************************************/ + +long mesh2d::delaunay() +{ + eextras = 0; + if (!restartsymbol) { + initializetrisegpools(); + } + if (!quiet) { + printf("Constructing Delaunay triangulation "); + if (incremental) { + printf("by incremental method.\n"); + } else if (sweepline) { + printf("by sweepline method.\n"); + } else { + printf("by divide-and-conquer method.\n"); + } + } + if (incremental) { + // return incrementaldelaunay(); + printf("Sorry, the incremental delaunay algorithm have not"); + printf(" been transformed.\n"); + exit(1); + } else if (sweepline) { + // return sweeplinedelaunay(); + printf("Sorry, the sweepline delaunay algorithm have not"); + printf(" been transformed.\n"); + exit(1); + } else { + return divconqdelaunay(); + } + return 0; +} + +/** **/ +/** **/ +/********* General mesh construction routines end here *********/ + +/********* Segment (shell edge) insertion begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* finddirection() Find the first triangle on the path from one point */ +/* to another. */ +/* */ +/* Finds the triangle that intersects a line segment drawn from the */ +/* origin of `searchtri' to the point `endpoint', and returns the result */ +/* in `searchtri'. The origin of `searchtri' does not change, even though */ +/* the triangle 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 whether the destination or apex of the found */ +/* triangle is collinear with the two points in question. */ +/* */ +/*****************************************************************************/ + +enum mesh2d::finddirectionresult +mesh2d::finddirection(struct triedge *searchtri, point endpoint) +{ + struct triedge checktri; + point startpoint; + point leftpoint, rightpoint; + REAL leftccw, rightccw; + int leftflag, rightflag; + triangle ptr; /* Temporary variable used by onext() and oprev(). */ + + org(*searchtri, startpoint); + dest(*searchtri, rightpoint); + apex(*searchtri, leftpoint); + /* Is `endpoint' to the left? */ + leftccw = counterclockwise(endpoint, startpoint, leftpoint); + leftflag = leftccw > 0.0; + /* Is `endpoint' to the right? */ + rightccw = counterclockwise(startpoint, endpoint, rightpoint); + rightflag = rightccw > 0.0; + if (leftflag && rightflag) { + /* `searchtri' faces directly away from `endpoint'. We could go */ + /* left or right. Ask whether it's a triangle or a boundary */ + /* on the left. */ + onext(*searchtri, checktri); + if (checktri.tri == dummytri) { + leftflag = 0; + } else { + rightflag = 0; + } + } + while (leftflag) { + /* Turn left until satisfied. */ + onextself(*searchtri); + if (searchtri->tri == dummytri) { + printf("Internal error in finddirection(): Unable to find a\n"); + printf(" triangle leading from (%.12g, %.12g) to", startpoint[0], + startpoint[1]); + printf(" (%.12g, %.12g).\n", endpoint[0], endpoint[1]); + internalerror(); + } + apex(*searchtri, leftpoint); + rightccw = leftccw; + leftccw = counterclockwise(endpoint, startpoint, leftpoint); + leftflag = leftccw > 0.0; + } + while (rightflag) { + /* Turn right until satisfied. */ + oprevself(*searchtri); + if (searchtri->tri == dummytri) { + printf("Internal error in finddirection(): Unable to find a\n"); + printf(" triangle leading from (%.12g, %.12g) to", startpoint[0], + startpoint[1]); + printf(" (%.12g, %.12g).\n", endpoint[0], endpoint[1]); + internalerror(); + } + dest(*searchtri, rightpoint); + leftccw = rightccw; + rightccw = counterclockwise(startpoint, endpoint, rightpoint); + rightflag = rightccw > 0.0; + } + if (leftccw == 0.0) { + return LEFTCOLLINEAR; + } else if (rightccw == 0.0) { + return RIGHTCOLLINEAR; + } else { + return WITHIN; + } +} + +/*****************************************************************************/ +/* */ +/* segmentintersection() Find the intersection of an existing segment */ +/* and a segment that is being inserted. Insert */ +/* a point at the intersection, splitting an */ +/* existing shell edge. */ +/* */ +/* The segment being inserted connects the apex of splittri to endpoint2. */ +/* splitshelle is the shell edge being split, and MUST be opposite */ +/* splittri. Hence, the edge being split connects the origin and */ +/* destination of splittri. */ +/* */ +/* On completion, splittri is a handle having the newly inserted */ +/* intersection point as its origin, and endpoint1 as its destination. */ +/* */ +/*****************************************************************************/ + +void mesh2d:: +segmentintersection(struct triedge *splittri, struct edge *splitshelle, + point endpoint2) +{ + point endpoint1; + point torg, tdest; + point leftpoint, rightpoint; + point newpoint; + enum insertsiteresult success; + enum finddirectionresult collinear; + REAL ex, ey; + REAL tx, ty; + REAL etx, ety; + REAL split, denom; + int i; + triangle ptr; /* Temporary variable used by onext(). */ + + /* Find the other three segment endpoints. */ + apex(*splittri, endpoint1); + org(*splittri, torg); + dest(*splittri, tdest); + /* Segment intersection formulae; see the Antonio reference. */ + tx = tdest[0] - torg[0]; + ty = tdest[1] - torg[1]; + ex = endpoint2[0] - endpoint1[0]; + ey = endpoint2[1] - endpoint1[1]; + etx = torg[0] - endpoint2[0]; + ety = torg[1] - endpoint2[1]; + denom = ty * ex - tx * ey; + if (denom == 0.0) { + printf("Internal error in segmentintersection():"); + printf(" Attempt to find intersection of parallel segments.\n"); + internalerror(); + } + split = (ey * etx - ex * ety) / denom; + /* Create the new point. */ + newpoint = (point) points.alloc(); + /* Interpolate its coordinate and attributes. */ + for (i = 0; i < 2 + nextras; i++) { + newpoint[i] = torg[i] + split * (tdest[i] - torg[i]); + } + setpointmark(newpoint, mark(*splitshelle)); + if (verbose > 1) { + printf( + " Splitting edge (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n", + torg[0], torg[1], tdest[0], tdest[1], newpoint[0], newpoint[1]); + } + /* Insert the intersection point. This should always succeed. */ + success = insertsite(newpoint, splittri, splitshelle, 0, 0); + if (success != SUCCESSFULPOINT) { + printf("Internal error in segmentintersection():\n"); + printf(" Failure to split a segment.\n"); + internalerror(); + } + if (steinerleft > 0) { + steinerleft--; + } + /* Inserting the point may have caused edge flips. We wish to rediscover */ + /* the edge connecting endpoint1 to the new intersection point. */ + collinear = finddirection(splittri, endpoint1); + dest(*splittri, rightpoint); + apex(*splittri, leftpoint); + if ((leftpoint[0] == endpoint1[0]) && (leftpoint[1] == endpoint1[1])) { + onextself(*splittri); + } else if ((rightpoint[0] != endpoint1[0]) || + (rightpoint[1] != endpoint1[1])) { + printf("Internal error in segmentintersection():\n"); + printf(" Topological inconsistency after splitting a segment.\n"); + internalerror(); + } + /* `splittri' should have destination endpoint1. */ +} + +/*****************************************************************************/ +/* */ +/* scoutsegment() Scout the first triangle on the path from one endpoint */ +/* to another, and check for completion (reaching the */ +/* second endpoint), a collinear point, and the */ +/* intersection of two segments. */ +/* */ +/* Returns one if the entire segment is successfully inserted, and zero if */ +/* the job must be finished by conformingedge() or constrainededge(). */ +/* */ +/* If the first triangle on the path has the second endpoint as its */ +/* destination or apex, a shell edge is inserted and the job is done. */ +/* */ +/* If the first triangle on the path has a destination or apex that lies on */ +/* the segment, a shell edge is inserted connecting the first endpoint to */ +/* the collinear point, and the search is continued from the collinear */ +/* point. */ +/* */ +/* If the first triangle on the path has a shell edge opposite its origin, */ +/* then there is a segment that intersects the segment being inserted. */ +/* Their intersection point is inserted, splitting the shell edge. */ +/* */ +/* Otherwise, return zero. */ +/* */ +/*****************************************************************************/ + +int mesh2d:: +scoutsegment(struct triedge *searchtri, point endpoint2, int newmark) +{ + struct triedge crosstri; + struct edge crossedge; + point leftpoint, rightpoint; + point endpoint1; + enum finddirectionresult collinear; + shelle sptr; /* Temporary variable used by tspivot(). */ + + collinear = finddirection(searchtri, endpoint2); + dest(*searchtri, rightpoint); + apex(*searchtri, leftpoint); + if (((leftpoint[0] == endpoint2[0]) && (leftpoint[1] == endpoint2[1])) || + ((rightpoint[0] == endpoint2[0]) && (rightpoint[1] == endpoint2[1]))) { + /* The segment is already an edge in the mesh. */ + if ((leftpoint[0] == endpoint2[0]) && (leftpoint[1] == endpoint2[1])) { + lprevself(*searchtri); + } + /* Insert a shell edge, if there isn't already one there. */ + insertshelle(searchtri, newmark); + return 1; + } else if (collinear == LEFTCOLLINEAR) { + /* We've collided with a point between the segment's endpoints. */ + /* Make the collinear point be the triangle's origin. */ + lprevself(*searchtri); + insertshelle(searchtri, newmark); + /* Insert the remainder of the segment. */ + return scoutsegment(searchtri, endpoint2, newmark); + } else if (collinear == RIGHTCOLLINEAR) { + /* We've collided with a point between the segment's endpoints. */ + insertshelle(searchtri, newmark); + /* Make the collinear point be the triangle's origin. */ + lnextself(*searchtri); + /* Insert the remainder of the segment. */ + return scoutsegment(searchtri, endpoint2, newmark); + } else { + lnext(*searchtri, crosstri); + tspivot(crosstri, crossedge); + /* Check for a crossing segment. */ + if (crossedge.sh == dummysh) { + return 0; + } else { + org(*searchtri, endpoint1); + /* Insert a point at the intersection. */ + segmentintersection(&crosstri, &crossedge, endpoint2); + triedgecopy(crosstri, *searchtri); + insertshelle(searchtri, newmark); + /* Insert the remainder of the segment. */ + return scoutsegment(searchtri, endpoint2, newmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* conformingedge() Force a segment into a conforming Delaunay */ +/* triangulation by inserting a point at its midpoint, */ +/* and recursively forcing in the two half-segments if */ +/* necessary. */ +/* */ +/* Generates a sequence of edges connecting `endpoint1' to `endpoint2'. */ +/* `newmark' is the boundary marker of the segment, assigned to each new */ +/* splitting point and shell edge. */ +/* */ +/* Note that conformingedge() does not always maintain the conforming */ +/* Delaunay property. Once inserted, segments are locked into place; */ +/* points inserted later (to force other segments in) may render these */ +/* fixed segments non-Delaunay. The conforming Delaunay property will be */ +/* restored by enforcequality() by splitting encroached segments. */ +/* */ +/*****************************************************************************/ + +void mesh2d::conformingedge(point endpoint1, point endpoint2, int newmark) +{ + struct triedge searchtri1, searchtri2; + struct edge brokenshelle; + point newpoint; + point midpoint1, midpoint2; + enum insertsiteresult success; + int result1, result2; + int i; + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (verbose > 2) { + printf("Forcing segment into triangulation by recursive splitting:\n"); + printf(" (%.12g, %.12g) (%.12g, %.12g)\n", endpoint1[0], endpoint1[1], + endpoint2[0], endpoint2[1]); + } + /* Create a new point to insert in the middle of the segment. */ + newpoint = (point) points.alloc(); + /* Interpolate coordinates and attributes. */ + for (i = 0; i < 2 + nextras; i++) { + newpoint[i] = 0.5 * (endpoint1[i] + endpoint2[i]); + } + setpointmark(newpoint, newmark); + /* Find a boundary triangle to search from. */ + searchtri1.tri = (triangle *) NULL; + /* Attempt to insert the new point. */ + success = insertsite(newpoint, &searchtri1, (struct edge *) NULL, 0, 0); + if (success == DUPLICATEPOINT) { + if (verbose > 2) { + printf(" Segment intersects existing point (%.12g, %.12g).\n", + newpoint[0], newpoint[1]); + } + /* Use the point that's already there. */ + pointdealloc(newpoint); + org(searchtri1, newpoint); + } else { + if (success == VIOLATINGPOINT) { + if (verbose > 2) { + printf(" Two segments intersect at (%.12g, %.12g).\n", + newpoint[0], newpoint[1]); + } + /* By fluke, we've landed right on another segment. Split it. */ + tspivot(searchtri1, brokenshelle); + success = insertsite(newpoint, &searchtri1, &brokenshelle, 0, 0); + if (success != SUCCESSFULPOINT) { + printf("Internal error in conformingedge():\n"); + printf(" Failure to split a segment.\n"); + internalerror(); + } + } + /* The point has been inserted successfully. */ + if (steinerleft > 0) { + steinerleft--; + } + } + triedgecopy(searchtri1, searchtri2); + result1 = scoutsegment(&searchtri1, endpoint1, newmark); + result2 = scoutsegment(&searchtri2, endpoint2, newmark); + if (!result1) { + /* The origin of searchtri1 may have changed if a collision with an */ + /* intervening vertex on the segment occurred. */ + org(searchtri1, midpoint1); + conformingedge(midpoint1, endpoint1, newmark); + } + if (!result2) { + /* The origin of searchtri2 may have changed if a collision with an */ + /* intervening vertex on the segment occurred. */ + org(searchtri2, midpoint2); + conformingedge(midpoint2, endpoint2, newmark); + } +} + +/*****************************************************************************/ +/* */ +/* delaunayfixup() Enforce the Delaunay condition at an edge, fanning out */ +/* recursively from an existing point. Pay special */ +/* attention to stacking inverted triangles. */ +/* */ +/* This is a support routine for inserting segments into a constrained */ +/* Delaunay triangulation. */ +/* */ +/* The origin of fixuptri is treated as if it has just been inserted, and */ +/* the local Delaunay condition needs to be enforced. It is only enforced */ +/* in one sector, however, that being the angular range defined by */ +/* fixuptri. */ +/* */ +/* This routine also needs to make decisions regarding the "stacking" of */ +/* triangles. (Read the description of constrainededge() below before */ +/* reading on here, so you understand the algorithm.) If the position of */ +/* the new point (the origin of fixuptri) indicates that the vertex before */ +/* it on the polygon is a reflex vertex, then "stack" the triangle by */ +/* doing nothing. (fixuptri is an inverted triangle, which is how stacked */ +/* triangles are identified.) */ +/* */ +/* Otherwise, check whether the vertex before that was a reflex vertex. */ +/* If so, perform an edge flip, thereby eliminating an inverted triangle */ +/* (popping it off the stack). The edge flip may result in the creation */ +/* of a new inverted triangle, depending on whether or not the new vertex */ +/* is visible to the vertex three edges behind on the polygon. */ +/* */ +/* If neither of the two vertices behind the new vertex are reflex */ +/* vertices, fixuptri and fartri, the triangle opposite it, are not */ +/* inverted; hence, ensure that the edge between them is locally Delaunay. */ +/* */ +/* `leftside' indicates whether or not fixuptri is to the left of the */ +/* segment being inserted. (Imagine that the segment is pointing up from */ +/* endpoint1 to endpoint2.) */ +/* */ +/*****************************************************************************/ + +void mesh2d::delaunayfixup(struct triedge *fixuptri, int leftside) +{ + struct triedge neartri; + struct triedge fartri; + struct edge faredge; + point nearpoint, leftpoint, rightpoint, farpoint; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + lnext(*fixuptri, neartri); + sym(neartri, fartri); + /* Check if the edge opposite the origin of fixuptri can be flipped. */ + if (fartri.tri == dummytri) { + return; + } + tspivot(neartri, faredge); + if (faredge.sh != dummysh) { + return; + } + /* Find all the relevant vertices. */ + apex(neartri, nearpoint); + org(neartri, leftpoint); + dest(neartri, rightpoint); + apex(fartri, farpoint); + /* Check whether the previous polygon vertex is a reflex vertex. */ + if (leftside) { + if (counterclockwise(nearpoint, leftpoint, farpoint) <= 0.0) { + /* leftpoint is a reflex vertex too. Nothing can */ + /* be done until a convex section is found. */ + return; + } + } else { + if (counterclockwise(farpoint, rightpoint, nearpoint) <= 0.0) { + /* rightpoint is a reflex vertex too. Nothing can */ + /* be done until a convex section is found. */ + return; + } + } + if (counterclockwise(rightpoint, leftpoint, farpoint) > 0.0) { + /* fartri is not an inverted triangle, and farpoint is not a reflex */ + /* vertex. As there are no reflex vertices, fixuptri isn't an */ + /* inverted triangle, either. Hence, test the edge between the */ + /* triangles to ensure it is locally Delaunay. */ + if (iincircle(leftpoint, farpoint, rightpoint, nearpoint) <= 0.0) { + return; + } + /* Not locally Delaunay; go on to an edge flip. */ + } /* else fartri is inverted; remove it from the stack by flipping. */ + flip(&neartri); + lprevself(*fixuptri); /* Restore the origin of fixuptri after the flip. */ + /* Recursively process the two triangles that result from the flip. */ + delaunayfixup(fixuptri, leftside); + delaunayfixup(&fartri, leftside); +} + +/*****************************************************************************/ +/* */ +/* constrainededge() Force a segment into a constrained Delaunay */ +/* triangulation by deleting the triangles it */ +/* intersects, and triangulating the polygons that */ +/* form on each side of it. */ +/* */ +/* Generates a single edge connecting `endpoint1' to `endpoint2'. The */ +/* triangle `starttri' has `endpoint1' as its origin. `newmark' is the */ +/* boundary marker of the segment. */ +/* */ +/* To insert a segment, every triangle whose interior intersects the */ +/* segment is deleted. The union of these deleted triangles is a polygon */ +/* (which is not necessarily monotone, but is close enough), which is */ +/* divided into two polygons by the new segment. This routine's task is */ +/* to generate the Delaunay triangulation of these two polygons. */ +/* */ +/* You might think of this routine's behavior as a two-step process. The */ +/* first step is to walk from endpoint1 to endpoint2, flipping each edge */ +/* encountered. This step creates a fan of edges connected to endpoint1, */ +/* including the desired edge to endpoint2. The second step enforces the */ +/* Delaunay condition on each side of the segment in an incremental manner: */ +/* proceeding along the polygon from endpoint1 to endpoint2 (this is done */ +/* independently on each side of the segment), each vertex is "enforced" */ +/* as if it had just been inserted, but affecting only the previous */ +/* vertices. The result is the same as if the vertices had been inserted */ +/* in the order they appear on the polygon, so the result is Delaunay. */ +/* */ +/* In truth, constrainededge() interleaves these two steps. The procedure */ +/* walks from endpoint1 to endpoint2, and each time an edge is encountered */ +/* and flipped, the newly exposed vertex (at the far end of the flipped */ +/* edge) is "enforced" upon the previously flipped edges, usually affecting */ +/* only one side of the polygon (depending upon which side of the segment */ +/* the vertex falls on). */ +/* */ +/* The algorithm is complicated by the need to handle polygons that are not */ +/* convex. Although the polygon is not necessarily monotone, it can be */ +/* triangulated in a manner similar to the stack-based algorithms for */ +/* monotone polygons. For each reflex vertex (local concavity) of the */ +/* polygon, there will be an inverted triangle formed by one of the edge */ +/* flips. (An inverted triangle is one with negative area - that is, its */ +/* vertices are arranged in clockwise order - and is best thought of as a */ +/* wrinkle in the fabric of the mesh.) Each inverted triangle can be */ +/* thought of as a reflex vertex pushed on the stack, waiting to be fixed */ +/* later. */ +/* */ +/* A reflex vertex is popped from the stack when a vertex is inserted that */ +/* is visible to the reflex vertex. (However, if the vertex behind the */ +/* reflex vertex is not visible to the reflex vertex, a new inverted */ +/* triangle will take its place on the stack.) These details are handled */ +/* by the delaunayfixup() routine above. */ +/* */ +/*****************************************************************************/ + +void mesh2d:: +constrainededge(struct triedge *starttri, point endpoint2, int newmark) +{ + struct triedge fixuptri, fixuptri2; + struct edge fixupedge; + point endpoint1; + point farpoint; + REAL area; + int collision; + int done; + triangle ptr; /* Temporary variable used by sym() and oprev(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + org(*starttri, endpoint1); + lnext(*starttri, fixuptri); + flip(&fixuptri); + /* `collision' indicates whether we have found a point directly */ + /* between endpoint1 and endpoint2. */ + collision = 0; + done = 0; + do { + org(fixuptri, farpoint); + /* `farpoint' is the extreme point of the polygon we are "digging" */ + /* to get from endpoint1 to endpoint2. */ + if ((farpoint[0] == endpoint2[0]) && (farpoint[1] == endpoint2[1])) { + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around endpoint2. */ + delaunayfixup(&fixuptri, 0); + delaunayfixup(&fixuptri2, 1); + done = 1; + } else { + /* Check whether farpoint is to the left or right of the segment */ + /* being inserted, to decide which edge of fixuptri to dig */ + /* through next. */ + area = counterclockwise(endpoint1, endpoint2, farpoint); + if (area == 0.0) { + /* We've collided with a point between endpoint1 and endpoint2. */ + collision = 1; + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around farpoint. */ + delaunayfixup(&fixuptri, 0); + delaunayfixup(&fixuptri2, 1); + done = 1; + } else { + if (area > 0.0) { /* farpoint is to the left of the segment. */ + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around farpoint, on the */ + /* left side of the segment only. */ + delaunayfixup(&fixuptri2, 1); + /* Flip the edge that crosses the segment. After the edge is */ + /* flipped, one of its endpoints is the fan vertex, and the */ + /* destination of fixuptri is the fan vertex. */ + lprevself(fixuptri); + } else { /* farpoint is to the right of the segment. */ + delaunayfixup(&fixuptri, 0); + /* Flip the edge that crosses the segment. After the edge is */ + /* flipped, one of its endpoints is the fan vertex, and the */ + /* destination of fixuptri is the fan vertex. */ + oprevself(fixuptri); + } + /* Check for two intersecting segments. */ + tspivot(fixuptri, fixupedge); + if (fixupedge.sh == dummysh) { + flip(&fixuptri); /* May create an inverted triangle on the left. */ + } else { + /* We've collided with a segment between endpoint1 and endpoint2. */ + collision = 1; + /* Insert a point at the intersection. */ + segmentintersection(&fixuptri, &fixupedge, endpoint2); + done = 1; + } + } + } + } while (!done); + /* Insert a shell edge to make the segment permanent. */ + insertshelle(&fixuptri, newmark); + /* 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 (!scoutsegment(&fixuptri, endpoint2, newmark)) { + constrainededge(&fixuptri, endpoint2, newmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* insertsegment() Insert a PSLG segment into a triangulation. */ +/* */ +/*****************************************************************************/ + +void mesh2d::insertsegment(point endpoint1, point endpoint2, int newmark) +{ + struct triedge searchtri1, searchtri2; + triangle encodedtri; + point checkpoint; + triangle ptr; /* Temporary variable used by sym(). */ + + if (verbose > 1) { + printf(" Connecting (%.12g, %.12g) to (%.12g, %.12g).\n", + endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]); + } + + /* Find a triangle whose origin is the segment's first endpoint. */ + checkpoint = (point) NULL; + encodedtri = point2tri(endpoint1); + if (encodedtri != (triangle) NULL) { + decode(encodedtri, searchtri1); + org(searchtri1, checkpoint); + } + if (checkpoint != endpoint1) { + /* Find a boundary triangle to search from. */ + searchtri1.tri = dummytri; + searchtri1.orient = 0; + symself(searchtri1); + /* Search for the segment's first endpoint by point location. */ + if (locate(endpoint1, &searchtri1) != ONVERTEX) { + printf( + "Internal error in insertsegment(): Unable to locate PSLG point\n"); + printf(" (%.12g, %.12g) in triangulation.\n", + endpoint1[0], endpoint1[1]); + internalerror(); + } + } + /* Remember this triangle to improve subsequent point location. */ + triedgecopy(searchtri1, recenttri); + /* Scout the beginnings of a path from the first endpoint */ + /* toward the second. */ + if (scoutsegment(&searchtri1, endpoint2, newmark)) { + /* The segment was easily inserted. */ + return; + } + /* The first endpoint may have changed if a collision with an intervening */ + /* vertex on the segment occurred. */ + org(searchtri1, endpoint1); + + /* Find a triangle whose origin is the segment's second endpoint. */ + checkpoint = (point) NULL; + encodedtri = point2tri(endpoint2); + if (encodedtri != (triangle) NULL) { + decode(encodedtri, searchtri2); + org(searchtri2, checkpoint); + } + if (checkpoint != endpoint2) { + /* Find a boundary triangle to search from. */ + searchtri2.tri = dummytri; + searchtri2.orient = 0; + symself(searchtri2); + /* Search for the segment's second endpoint by point location. */ + if (locate(endpoint2, &searchtri2) != ONVERTEX) { + printf( + "Internal error in insertsegment(): Unable to locate PSLG point\n"); + printf(" (%.12g, %.12g) in triangulation.\n", + endpoint2[0], endpoint2[1]); + internalerror(); + } + } + /* Remember this triangle to improve subsequent point location. */ + triedgecopy(searchtri2, recenttri); + /* Scout the beginnings of a path from the second endpoint */ + /* toward the first. */ + if (scoutsegment(&searchtri2, endpoint1, newmark)) { + /* The segment was easily inserted. */ + return; + } + /* The second endpoint may have changed if a collision with an intervening */ + /* vertex on the segment occurred. */ + org(searchtri2, endpoint2); + + if (splitseg) { + /* Insert vertices to force the segment into the triangulation. */ + conformingedge(endpoint1, endpoint2, newmark); + } else { + /* Insert the segment directly into the triangulation. */ + constrainededge(&searchtri1, endpoint2, newmark); + } +} + +/*****************************************************************************/ +/* */ +/* markhull() Cover the convex hull of a triangulation with shell edges. */ +/* */ +/*****************************************************************************/ + +void mesh2d::markhull() +{ + struct triedge hulltri; + struct triedge nexttri; + struct triedge starttri; + triangle ptr; /* Temporary variable used by sym() and oprev(). */ + + /* Find a triangle handle on the hull. */ + hulltri.tri = dummytri; + hulltri.orient = 0; + symself(hulltri); + /* Remember where we started so we know when to stop. */ + triedgecopy(hulltri, starttri); + /* Go once counterclockwise around the convex hull. */ + do { + /* Create a shell edge if there isn't already one here. */ + insertshelle(&hulltri, 1); + /* To find the next hull edge, go clockwise around the next vertex. */ + lnextself(hulltri); + oprev(hulltri, nexttri); + while (nexttri.tri != dummytri) { + triedgecopy(nexttri, hulltri); + oprev(hulltri, nexttri); + } + } while (!triedgeequal(hulltri, starttri)); +} + +/*****************************************************************************/ +/* */ +/* formskeleton() Create the shell edges of a triangulation, including */ +/* PSLG edges and edges on the convex hull. */ +/* */ +/* The PSLG edges are read from a .poly file. The return value is the */ +/* number of segments in the file. */ +/* */ +/*****************************************************************************/ + +int mesh2d:: +formskeleton(int *segmentlist, int *segmentmarkerlist, int numberofsegments) +{ + char polyfilename[6]; + int index; + point endpoint1, endpoint2; + int segments; + int segmentmarkers; + int end1, end2; + int boundmarker; + int i; + + if (poly) { + if (!quiet) { + printf("Inserting segments into Delaunay triangulation.\n"); + } + strcpy(polyfilename, "input"); + segments = numberofsegments; + segmentmarkers = segmentmarkerlist != (int *) NULL; + index = 0; + /* If segments are to be inserted, compute a mapping */ + /* from points to triangles. */ + if (segments > 0) { + if (verbose) { + printf(" Inserting PSLG segments.\n"); + } + makepointmap(); + } + + boundmarker = 0; + /* Read and insert the segments. */ + for (i = 1; i <= segments; i++) { + end1 = segmentlist[index++]; + end2 = segmentlist[index++]; + if (segmentmarkers) { + boundmarker = segmentmarkerlist[i - 1]; + } + if ((end1 < firstnumber) || (end1 >= firstnumber + inpoints)) { + if (!quiet) { + printf("Warning: Invalid first endpoint of segment %d in %s.\n", i, + polyfilename); + } + } else if ((end2 < firstnumber) || (end2 >= firstnumber + inpoints)) { + if (!quiet) { + printf("Warning: Invalid second endpoint of segment %d in %s.\n", i, + polyfilename); + } + } else { + endpoint1 = getpoint(end1); + endpoint2 = getpoint(end2); + if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) { + if (!quiet) { + printf("Warning: Endpoints of segment %d are coincident in %s.\n", + i, polyfilename); + } + } else { + insertsegment(endpoint1, endpoint2, boundmarker); + } + } + } + } else { + segments = 0; + } + if (convex || !poly) { + /* Enclose the convex hull with shell edges. */ + if (verbose) { + printf(" Enclosing convex hull with segments.\n"); + } + markhull(); + } + return segments; +} + +/** **/ +/** **/ +/********* Segment (shell edge) insertion ends here *********/ + +/********* Carving out holes and concavities begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* infecthull() Virally infect all of the triangles of the convex hull */ +/* that are not protected by shell edges. Where there are */ +/* shell edges, set boundary markers as appropriate. */ +/* */ +/*****************************************************************************/ + +void mesh2d::infecthull() +{ + struct triedge hulltri; + struct triedge nexttri; + struct triedge starttri; + struct edge hulledge; + triangle **deadtri; + point horg, hdest; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (verbose) { + printf(" Marking concavities (external triangles) for elimination.\n"); + } + /* Find a triangle handle on the hull. */ + hulltri.tri = dummytri; + hulltri.orient = 0; + symself(hulltri); + /* Remember where we started so we know when to stop. */ + triedgecopy(hulltri, starttri); + /* Go once counterclockwise around the convex hull. */ + do { + /* Ignore triangles that are already infected. */ + if (!infected(hulltri)) { + /* Is the triangle protected by a shell edge? */ + tspivot(hulltri, hulledge); + if (hulledge.sh == dummysh) { + /* The triangle is not protected; infect it. */ + infect(hulltri); + deadtri = (triangle **) viri.alloc(); + *deadtri = hulltri.tri; + } else { + /* The triangle is protected; set boundary markers if appropriate. */ + if (mark(hulledge) == 0) { + setmark(hulledge, 1); + org(hulltri, horg); + dest(hulltri, hdest); + if (pointmark(horg) == 0) { + setpointmark(horg, 1); + } + if (pointmark(hdest) == 0) { + setpointmark(hdest, 1); + } + } + } + } + /* To find the next hull edge, go clockwise around the next vertex. */ + lnextself(hulltri); + oprev(hulltri, nexttri); + while (nexttri.tri != dummytri) { + triedgecopy(nexttri, hulltri); + oprev(hulltri, nexttri); + } + } while (!triedgeequal(hulltri, starttri)); +} + +/*****************************************************************************/ +/* */ +/* plague() Spread the virus from all infected triangles to any neighbors */ +/* not protected by shell edges. Delete all infected triangles. */ +/* */ +/* This is the procedure that actually creates holes and concavities. */ +/* */ +/* This procedure operates in two phases. The first phase identifies all */ +/* the triangles that will die, and marks them as infected. They are */ +/* marked to ensure that each triangle is added to the virus pool only */ +/* once, so the procedure will terminate. */ +/* */ +/* The second phase actually eliminates the infected triangles. It also */ +/* eliminates orphaned points. */ +/* */ +/*****************************************************************************/ + +void mesh2d::plague() +{ + struct triedge testtri; + struct triedge neighbor; + triangle **virusloop; + triangle **deadtri; + struct edge neighborshelle; + point testpoint; + point norg, ndest; + point deadorg, deaddest, deadapex; + int killorg; + triangle ptr; /* Temporary variable used by sym() and onext(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (verbose) { + printf(" Marking neighbors of marked triangles.\n"); + } + /* Loop through all the infected triangles, spreading the virus to */ + /* their neighbors, then to their neighbors' neighbors. */ + viri.traversalinit(); + virusloop = (triangle **) viri.traverse(); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + /* A triangle is marked as infected by messing with one of its shell */ + /* edges, setting it to an illegal value. Hence, we have to */ + /* temporarily uninfect this triangle so that we can examine its */ + /* adjacent shell edges. */ + uninfect(testtri); + if (verbose > 2) { + /* Assign the triangle an orientation for convenience in */ + /* checking its points. */ + testtri.orient = 0; + org(testtri, deadorg); + dest(testtri, deaddest); + apex(testtri, deadapex); + printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + deadorg[0], deadorg[1], deaddest[0], deaddest[1], + deadapex[0], deadapex[1]); + } + /* Check each of the triangle's three neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + /* Find the neighbor. */ + sym(testtri, neighbor); + /* Check for a shell between the triangle and its neighbor. */ + tspivot(testtri, neighborshelle); + /* Check if the neighbor is nonexistent or already infected. */ + if ((neighbor.tri == dummytri) || infected(neighbor)) { + if (neighborshelle.sh != dummysh) { + /* There is a shell edge separating the triangle from its */ + /* neighbor, but both triangles are dying, so the shell */ + /* edge dies too. */ + shelledealloc(neighborshelle.sh); + if (neighbor.tri != dummytri) { + /* Make sure the shell edge doesn't get deallocated again */ + /* later when the infected neighbor is visited. */ + uninfect(neighbor); + tsdissolve(neighbor); + infect(neighbor); + } + } + } else { /* The neighbor exists and is not infected. */ + if (neighborshelle.sh == dummysh) { + /* There is no shell edge protecting the neighbor, so */ + /* the neighbor becomes infected. */ + if (verbose > 2) { + org(neighbor, deadorg); + dest(neighbor, deaddest); + apex(neighbor, deadapex); + printf( + " Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + deadorg[0], deadorg[1], deaddest[0], deaddest[1], + deadapex[0], deadapex[1]); + } + infect(neighbor); + /* Ensure that the neighbor's neighbors will be infected. */ + deadtri = (triangle **) viri.alloc(); + *deadtri = neighbor.tri; + } else { /* The neighbor is protected by a shell edge. */ + /* Remove this triangle from the shell edge. */ + stdissolve(neighborshelle); + /* The shell edge becomes a boundary. Set markers accordingly. */ + if (mark(neighborshelle) == 0) { + setmark(neighborshelle, 1); + } + org(neighbor, norg); + dest(neighbor, ndest); + if (pointmark(norg) == 0) { + setpointmark(norg, 1); + } + if (pointmark(ndest) == 0) { + setpointmark(ndest, 1); + } + } + } + } + /* Remark the triangle as infected, so it doesn't get added to the */ + /* virus pool again. */ + infect(testtri); + virusloop = (triangle **) viri.traverse(); + } + + if (verbose) { + printf(" Deleting marked triangles.\n"); + } + viri.traversalinit(); + virusloop = (triangle **) viri.traverse(); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + + /* Check each of the three corners of the triangle for elimination. */ + /* This is done by walking around each point, checking if it is */ + /* still connected to at least one live triangle. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + org(testtri, testpoint); + /* Check if the point has already been tested. */ + if (testpoint != (point) NULL) { + killorg = 1; + /* Mark the corner of the triangle as having been tested. */ + setorg(testtri, NULL); + /* Walk counterclockwise about the point. */ + onext(testtri, neighbor); + /* Stop upon reaching a boundary or the starting triangle. */ + while ((neighbor.tri != dummytri) + && (!triedgeequal(neighbor, testtri))) { + if (infected(neighbor)) { + /* Mark the corner of this triangle as having been tested. */ + setorg(neighbor, NULL); + } else { + /* A live triangle. The point survives. */ + killorg = 0; + } + /* Walk counterclockwise about the point. */ + onextself(neighbor); + } + /* If we reached a boundary, we must walk clockwise as well. */ + if (neighbor.tri == dummytri) { + /* Walk clockwise about the point. */ + oprev(testtri, neighbor); + /* Stop upon reaching a boundary. */ + while (neighbor.tri != dummytri) { + if (infected(neighbor)) { + /* Mark the corner of this triangle as having been tested. */ + setorg(neighbor, NULL); + } else { + /* A live triangle. The point survives. */ + killorg = 0; + } + /* Walk clockwise about the point. */ + oprevself(neighbor); + } + } + if (killorg) { + if (verbose > 1) { + printf(" Deleting point (%.12g, %.12g)\n", + testpoint[0], testpoint[1]); + } + pointdealloc(testpoint); + } + } + } + + /* Record changes in the number of boundary edges, and disconnect */ + /* dead triangles from their neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + sym(testtri, neighbor); + if (neighbor.tri == dummytri) { + /* There is no neighboring triangle on this edge, so this edge */ + /* is a boundary edge. This triangle is being deleted, so this */ + /* boundary edge is deleted. */ + hullsize--; + } else { + /* Disconnect the triangle from its neighbor. */ + dissolve(neighbor); + /* There is a neighboring triangle on this edge, so this edge */ + /* becomes a boundary edge when this triangle is deleted. */ + hullsize++; + } + } + /* Return the dead triangle to the pool of triangles. */ + triangledealloc(testtri.tri); + virusloop = (triangle **) viri.traverse(); + } + /* Empty the virus pool. */ + viri.restart(); +} + +/*****************************************************************************/ +/* */ +/* regionplague() Spread regional attributes and/or area constraints */ +/* (from a .poly file) throughout the mesh. */ +/* */ +/* This procedure operates in two phases. The first phase spreads an */ +/* attribute and/or an area constraint through a (segment-bounded) region. */ +/* The triangles are marked to ensure that each triangle is added to the */ +/* virus pool only once, so the procedure will terminate. */ +/* */ +/* The second phase uninfects all infected triangles, returning them to */ +/* normal. */ +/* */ +/*****************************************************************************/ + +void mesh2d::regionplague(REAL attribute, REAL area) +{ + struct triedge testtri; + struct triedge neighbor; + triangle **virusloop; + triangle **regiontri; + struct edge neighborshelle; + point regionorg, regiondest, regionapex; + triangle ptr; /* Temporary variable used by sym() and onext(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (verbose > 1) { + printf(" Marking neighbors of marked triangles.\n"); + } + /* Loop through all the infected triangles, spreading the attribute */ + /* and/or area constraint to their neighbors, then to their neighbors' */ + /* neighbors. */ + viri.traversalinit(); + virusloop = (triangle **) viri.traverse(); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + /* A triangle is marked as infected by messing with one of its shell */ + /* edges, setting it to an illegal value. Hence, we have to */ + /* temporarily uninfect this triangle so that we can examine its */ + /* adjacent shell edges. */ + uninfect(testtri); + if (regionattrib) { + /* Set an attribute. */ + setelemattribute(testtri, eextras, attribute); + } + if (vararea) { + /* Set an area constraint. */ + setareabound(testtri, area); + } + if (verbose > 2) { + /* Assign the triangle an orientation for convenience in */ + /* checking its points. */ + testtri.orient = 0; + org(testtri, regionorg); + dest(testtri, regiondest); + apex(testtri, regionapex); + printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + regionorg[0], regionorg[1], regiondest[0], regiondest[1], + regionapex[0], regionapex[1]); + } + /* Check each of the triangle's three neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + /* Find the neighbor. */ + sym(testtri, neighbor); + /* Check for a shell between the triangle and its neighbor. */ + tspivot(testtri, neighborshelle); + /* Make sure the neighbor exists, is not already infected, and */ + /* isn't protected by a shell edge. */ + if ((neighbor.tri != dummytri) && !infected(neighbor) + && (neighborshelle.sh == dummysh)) { + if (verbose > 2) { + org(neighbor, regionorg); + dest(neighbor, regiondest); + apex(neighbor, regionapex); + printf(" Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + regionorg[0], regionorg[1], regiondest[0], regiondest[1], + regionapex[0], regionapex[1]); + } + /* Infect the neighbor. */ + infect(neighbor); + /* Ensure that the neighbor's neighbors will be infected. */ + regiontri = (triangle **) viri.alloc(); + *regiontri = neighbor.tri; + } + } + /* Remark the triangle as infected, so it doesn't get added to the */ + /* virus pool again. */ + infect(testtri); + virusloop = (triangle **) viri.traverse(); + } + + /* Uninfect all triangles. */ + if (verbose > 1) { + printf(" Unmarking marked triangles.\n"); + } + viri.traversalinit(); + virusloop = (triangle **) viri.traverse(); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + uninfect(testtri); + virusloop = (triangle **) viri.traverse(); + } + /* Empty the virus pool. */ + viri.restart(); +} + +/*****************************************************************************/ +/* */ +/* carveholes() 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 mesh2d:: +carveholes(REAL *holelist, int holes, REAL *regionlist, int regions) +{ + struct triedge searchtri; + struct triedge triangleloop; + struct triedge *regiontris; + triangle **holetri; + triangle **regiontri; + point searchorg, searchdest; + enum locateresult intersect; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!(quiet || (noholes && convex))) { + printf("Removing unwanted triangles.\n"); + if (verbose && (holes > 0)) { + printf(" Marking holes for elimination.\n"); + } + } + + if (regions > 0) { + /* Allocate storage for the triangles in which region points fall. */ + regiontris = (struct triedge *) new struct triedge[regions]; + if (regiontris == (struct triedge *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + + if (((holes > 0) && !noholes) || !convex || (regions > 0)) { + /* Initialize a pool of viri to be used for holes, concavities, */ + /* regional attributes, and/or regional area constraints. */ + viri.init(sizeof(triangle *), VIRUSPERBLOCK, POINTER, 0); + } + + if (!convex) { + /* Mark as infected any unprotected triangles on the boundary. */ + /* This is one way by which concavities are created. */ + infecthull(); + } + + if ((holes > 0) && !noholes) { + /* Infect each triangle in which a hole lies. */ + for (i = 0; i < 2 * holes; i += 2) { + /* 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)) { + /* Start searching from some triangle on the outer boundary. */ + searchtri.tri = dummytri; + searchtri.orient = 0; + symself(searchtri); + /* Ensure that the hole is to the left of this boundary edge; */ + /* otherwise, locate() will falsely report that the hole */ + /* falls within the starting triangle. */ + org(searchtri, searchorg); + dest(searchtri, searchdest); + if (counterclockwise(searchorg, searchdest, &holelist[i]) > 0.0) { + /* Find a triangle that contains the hole. */ + intersect = locate(&holelist[i], &searchtri); + if ((intersect != OUTSIDE) && (!infected(searchtri))) { + /* Infect the triangle. This is done by marking the triangle */ + /* as infect and including the triangle in the virus pool. */ + infect(searchtri); + holetri = (triangle **) viri.alloc(); + *holetri = searchtri.tri; + } + } + } + } + } + + /* Now, we have to find all the regions BEFORE we carve the holes, because */ + /* locate() won't work when the triangulation is no longer convex. */ + /* (Incidentally, this is the reason why regional attributes and area */ + /* constraints can't be used when refining a preexisting mesh, which */ + /* might not be convex; they can only be used with a freshly */ + /* triangulated PSLG.) */ + if (regions > 0) { + /* Find the starting triangle for each region. */ + for (i = 0; i < regions; i++) { + regiontris[i].tri = dummytri; + /* Ignore region points that aren't within the bounds of the mesh. */ + if ((regionlist[4 * i] >= xmin) && (regionlist[4 * i] <= xmax) && + (regionlist[4 * i + 1] >= ymin) && (regionlist[4 * i + 1] <= ymax)) { + /* Start searching from some triangle on the outer boundary. */ + searchtri.tri = dummytri; + searchtri.orient = 0; + symself(searchtri); + /* Ensure that the region point is to the left of this boundary */ + /* edge; otherwise, locate() will falsely report that the */ + /* region point falls within the starting triangle. */ + org(searchtri, searchorg); + dest(searchtri, searchdest); + if (counterclockwise(searchorg, searchdest, ®ionlist[4 * i]) > + 0.0) { + /* Find a triangle that contains the region point. */ + intersect = locate(®ionlist[4 * i], &searchtri); + if ((intersect != OUTSIDE) && (!infected(searchtri))) { + /* Record the triangle for processing after the */ + /* holes have been carved. */ + triedgecopy(searchtri, regiontris[i]); + } + } + } + } + } + + if (viri.items > 0) { + /* Carve the holes and concavities. */ + plague(); + } + /* The virus pool should be empty now. */ + + if (regions > 0) { + if (!quiet) { + if (regionattrib) { + if (vararea) { + printf("Spreading regional attributes and area constraints.\n"); + } else { + printf("Spreading regional attributes.\n"); + } + } else { + printf("Spreading regional area constraints.\n"); + } + } + if (regionattrib && !refine) { + /* Assign every triangle a regional attribute of zero. */ + triangles.traversalinit(); + triangleloop.orient = 0; + triangleloop.tri = triangletraverse(); + while (triangleloop.tri != (triangle *) NULL) { + setelemattribute(triangleloop, eextras, 0.0); + triangleloop.tri = triangletraverse(); + } + } + for (i = 0; i < regions; i++) { + if (regiontris[i].tri != dummytri) { + /* Make sure the triangle under consideration still exists. */ + /* It may have been eaten by the virus. */ + if (regiontris[i].tri[3] != (triangle) NULL) { + /* Put one triangle in the virus pool. */ + infect(regiontris[i]); + regiontri = (triangle **) viri.alloc(); + *regiontri = regiontris[i].tri; + /* Apply one region's attribute and/or area constraint. */ + regionplague(regionlist[4 * i + 2], regionlist[4 * i + 3]); + /* The virus pool should be empty now. */ + } + } + } + if (regionattrib && !refine) { + /* Note the fact that each triangle has an additional attribute. */ + eextras++; + } + } + + /* Free up memory. */ + if (((holes > 0) && !noholes) || !convex || (regions > 0)) { + viri.deinit(); + } + if (regions > 0) { + delete [] regiontris; + } +} + +/** **/ +/** **/ +/********* Carving out holes and concavities ends here *********/ + +/********* I/O routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* transfernodes() Read the points from memory. */ +/* */ +/*****************************************************************************/ + +void mesh2d::transfernodes(REAL *pointlist, REAL *pointattriblist, + int *pointmarkerlist, int numberofpoints, + int numberofpointattribs) +{ + point pointloop; + REAL x, y; + int i, j; + int coordindex; + int attribindex; + + inpoints = numberofpoints; + mesh_dim = 2; + nextras = numberofpointattribs; + readnodefile = 0; + if (inpoints < 3) { + printf("Error: Input must have at least three input points.\n"); + exit(1); + } + + if (!restartsymbol) { + initializepointpool(); + } + + /* Read the points. */ + coordindex = 0; + attribindex = 0; + for (i = 0; i < inpoints; i++) { + pointloop = (point) points.alloc(); + /* Read the point coordinates. */ + x = pointloop[0] = pointlist[coordindex++]; + y = pointloop[1] = pointlist[coordindex++]; + /* Read the point attributes. */ + for (j = 0; j < numberofpointattribs; j++) { + pointloop[2 + j] = pointattriblist[attribindex++]; + } + if (pointmarkerlist != (int *) NULL) { + /* Read a point marker. */ + setpointmark(pointloop, pointmarkerlist[i]); + } else { + /* If no markers are specified, they default to zero. */ + setpointmark(pointloop, 0); + } + x = pointloop[0]; + y = pointloop[1]; + /* Determine the smallest and largest x and y coordinates. */ + if (i == 0) { + xmin = xmax = x; + ymin = ymax = y; + } else { + xmin = (x < xmin) ? x : xmin; + xmax = (x > xmax) ? x : xmax; + ymin = (y < ymin) ? y : ymin; + ymax = (y > ymax) ? y : ymax; + } + } + + /* Nonexistent x value used as a flag to mark circle events in sweepline */ + /* Delaunay algorithm. */ + xminextreme = 10 * xmin - 9 * xmax; +} + +/*****************************************************************************/ +/* */ +/* writenodes() Number the points and write them to a .node file. */ +/* */ +/* To save memory, the point numbers are written over the shell markers */ +/* after the points are written to a file. */ +/* */ +/*****************************************************************************/ + +void mesh2d:: +writenodes(REAL **pointlist, REAL **pointattriblist, int **pointmarkerlist) +{ + REAL *plist; + REAL *palist; + int *pmlist; + int coordindex; + int attribindex; + point pointloop; + int pointnumber; + int i; + + if (!quiet) { + printf("Writing points.\n"); + } + /* Allocate memory for output points if necessary. */ + if (*pointlist == (REAL *) NULL) { + *pointlist = (REAL *) new REAL[points.items * 2]; + if (*pointlist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for output point attributes if necessary. */ + if ((nextras > 0) && (*pointattriblist == (REAL *) NULL)) { + *pointattriblist = (REAL *) new REAL[points.items * nextras]; + if (*pointattriblist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for output point markers if necessary. */ + if (!nobound && (*pointmarkerlist == (int *) NULL)) { + *pointmarkerlist = (int *) new int[points.items]; + if (*pointmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + plist = *pointlist; + palist = *pointattriblist; + pmlist = *pointmarkerlist; + coordindex = 0; + attribindex = 0; + + points.traversalinit(); + pointloop = pointtraverse(); + pointnumber = firstnumber; + while (pointloop != (point) NULL) { + /* X and y coordinates. */ + plist[coordindex++] = pointloop[0]; + plist[coordindex++] = pointloop[1]; + /* Point attributes. */ + for (i = 0; i < nextras; i++) { + palist[attribindex++] = pointloop[2 + i]; + } + if (!nobound) { + /* Copy the boundary marker. */ + pmlist[pointnumber - firstnumber] = pointmark(pointloop); + } + + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } +} + +/*****************************************************************************/ +/* */ +/* numbernodes() Number the points. */ +/* */ +/* Each point is assigned a marker equal to its number. */ +/* */ +/* Used when writenodes() is not called because no .node file is written. */ +/* */ +/*****************************************************************************/ + +void mesh2d::numbernodes() +{ + point pointloop; + int pointnumber; + + points.traversalinit(); + pointloop = pointtraverse(); + pointnumber = firstnumber; + while (pointloop != (point) NULL) { + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } +} + +/*****************************************************************************/ +/* */ +/* writeelements() Write the triangles to an .ele file. */ +/* */ +/*****************************************************************************/ + +void mesh2d::writeelements(int **trianglelist, REAL **triangleattriblist) +{ + int *tlist; + REAL *talist; + int pointindex; + int attribindex; + struct triedge triangleloop; + point p1, p2, p3; + point mid1, mid2, mid3; + int elementnumber; + int i; + + if (!quiet) { + printf("Writing triangles.\n"); + } + /* Allocate memory for output triangles if necessary. */ + if (*trianglelist == (int *) NULL) { + *trianglelist = (int *) new int[triangles.items * + ((order + 1) * (order + 2) / 2)]; + if (*trianglelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for output triangle attributes if necessary. */ + if ((eextras > 0) && (*triangleattriblist == (REAL *) NULL)) { + *triangleattriblist = (REAL *) new REAL[triangles.items * eextras]; + if (*triangleattriblist == (REAL *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + tlist = *trianglelist; + talist = *triangleattriblist; + pointindex = 0; + attribindex = 0; + + triangles.traversalinit(); + triangleloop.tri = triangletraverse(); + triangleloop.orient = 0; + elementnumber = firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, p1); + dest(triangleloop, p2); + apex(triangleloop, p3); + if (order == 1) { + tlist[pointindex++] = pointmark(p1); + tlist[pointindex++] = pointmark(p2); + tlist[pointindex++] = pointmark(p3); + } else { + mid1 = (point) triangleloop.tri[highorderindex + 1]; + mid2 = (point) triangleloop.tri[highorderindex + 2]; + mid3 = (point) triangleloop.tri[highorderindex]; + tlist[pointindex++] = pointmark(p1); + tlist[pointindex++] = pointmark(p2); + tlist[pointindex++] = pointmark(p3); + tlist[pointindex++] = pointmark(mid1); + tlist[pointindex++] = pointmark(mid2); + tlist[pointindex++] = pointmark(mid3); + } + + for (i = 0; i < eextras; i++) { + talist[attribindex++] = elemattribute(triangleloop, i); + } + + triangleloop.tri = triangletraverse(); + elementnumber++; + } +} + +/*****************************************************************************/ +/* */ +/* writepoly() Write the segments and holes to a .poly file. */ +/* */ +/*****************************************************************************/ + +void mesh2d::writepoly(int **segmentlist, int **segmentmarkerlist) +{ + int *slist; + int *smlist; + int index; + struct edge shelleloop; + point endpoint1, endpoint2; + int shellenumber; + + if (!quiet) { + printf("Writing segments.\n"); + } + /* Allocate memory for output segments if necessary. */ + if (*segmentlist == (int *) NULL) { + *segmentlist = (int *) new int[shelles.items * 2]; + if (*segmentlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for output segment markers if necessary. */ + if (!nobound && (*segmentmarkerlist == (int *) NULL)) { + *segmentmarkerlist = (int *) new int[shelles.items]; + if (*segmentmarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + slist = *segmentlist; + smlist = *segmentmarkerlist; + index = 0; + + shelles.traversalinit(); + shelleloop.sh = shelletraverse(); + shelleloop.shorient = 0; + shellenumber = firstnumber; + while (shelleloop.sh != (shelle *) NULL) { + sorg(shelleloop, endpoint1); + sdest(shelleloop, endpoint2); + /* Copy indices of the segment's two endpoints. */ + slist[index++] = pointmark(endpoint1); + slist[index++] = pointmark(endpoint2); + if (!nobound) { + /* Copy the boundary marker. */ + smlist[shellenumber - firstnumber] = mark(shelleloop); + } + + shelleloop.sh = shelletraverse(); + shellenumber++; + } +} + +void mesh2d::writeedges(int **edgelist, int **edgemarkerlist) +{ + int *elist; + int *emlist; + int index; + struct triedge triangleloop, trisym; + struct edge checkmark; + point p1, p2; + int edgenumber; + triangle ptr; /* Temporary variable used by sym(). */ + shelle sptr; /* Temporary variable used by tspivot(). */ + + if (!quiet) { + printf("Writing edges.\n"); + } + /* Allocate memory for edges if necessary. */ + if (*edgelist == (int *) NULL) { + *edgelist = (int *) new int[edges * 2]; + if (*edgelist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + /* Allocate memory for edge markers if necessary. */ + if (!nobound && (*edgemarkerlist == (int *) NULL)) { + *edgemarkerlist = (int *) new int[edges]; + if (*edgemarkerlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + elist = *edgelist; + emlist = *edgemarkerlist; + index = 0; + + triangles.traversalinit(); + triangleloop.tri = triangletraverse(); + edgenumber = firstnumber; + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; + triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == dummytri)) { + org(triangleloop, p1); + dest(triangleloop, p2); + elist[index++] = pointmark(p1); + elist[index++] = pointmark(p2); + if (nobound) { + } else { + /* Edge number, indices of two endpoints, and a boundary marker. */ + /* If there's no shell edge, the boundary marker is zero. */ + if (useshelles) { + tspivot(triangleloop, checkmark); + if (checkmark.sh == dummysh) { + emlist[edgenumber - firstnumber] = 0; + } else { + emlist[edgenumber - firstnumber] = mark(checkmark); + } + } else { + emlist[edgenumber - firstnumber] = trisym.tri == dummytri; + } + } + edgenumber++; + } + } + triangleloop.tri = triangletraverse(); + } +} + +void mesh2d::writeneighbors(int **neighborlist) +{ + int *nlist; + int index; + struct triedge triangleloop, trisym; + int elementnumber; + int neighbor1, neighbor2, neighbor3; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!quiet) { + printf("Writing neighbors.\n"); + } + /* Allocate memory for neighbors if necessary. */ + if (*neighborlist == (int *) NULL) { + *neighborlist = (int *) new int[triangles.items * 3]; + if (*neighborlist == (int *) NULL) { + printf("Error: Out of memory.\n"); + exit(1); + } + } + nlist = *neighborlist; + index = 0; + + triangles.traversalinit(); + triangleloop.tri = triangletraverse(); + triangleloop.orient = 0; + elementnumber = firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + * (int *) (triangleloop.tri + 6) = elementnumber; + triangleloop.tri = triangletraverse(); + elementnumber++; + } + * (int *) (dummytri + 6) = -1; + + triangles.traversalinit(); + triangleloop.tri = triangletraverse(); + elementnumber = firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + triangleloop.orient = 1; + sym(triangleloop, trisym); + neighbor1 = * (int *) (trisym.tri + 6); + triangleloop.orient = 2; + sym(triangleloop, trisym); + neighbor2 = * (int *) (trisym.tri + 6); + triangleloop.orient = 0; + sym(triangleloop, trisym); + neighbor3 = * (int *) (trisym.tri + 6); + nlist[index++] = neighbor1; + nlist[index++] = neighbor2; + nlist[index++] = neighbor3; + + triangleloop.tri = triangletraverse(); + elementnumber++; + } +} + +void mesh2d::writegid(int trilibrary) +{ + FILE *outfile; + point pointloop; + char gidfilename[FILENAMESIZE]; + int pointnumber; + int i; + + if (trilibrary) { + sprintf(gidfilename, "tmpmesh.gid"); + } + + if (!quiet) { + printf("Writing %s.\n", gidfilename); + } + outfile = fopen(gidfilename, "w"); + if (outfile == (FILE *) NULL) { + printf(" Error: Cannot create file %s.\n", gidfilename); + return; + } + + fprintf(outfile, "mesh dimension = 2 elemtype triangle nnode = 3\n"); + fprintf(outfile, "coordinates\n"); + + points.traversalinit(); + pointloop = pointtraverse(); + pointnumber = firstnumber; + while (pointloop != (point) NULL) { + // Point number, x and y coordinates. + if (firstnumber == 0) { + // Gid need start index from one. + fprintf(outfile, "%4d %.17g %.17g", pointnumber + 1, pointloop[0], + pointloop[1]); + } else { + fprintf(outfile, "%4d %.17g %.17g", pointnumber, pointloop[0], + pointloop[1]); + } + for (i = 0; i < nextras; i++) { + // Write an attribute. + fprintf(outfile, " %.17g", pointloop[i + 2]); + } + fprintf(outfile, "\n"); + setpointmark(pointloop, pointnumber); + pointloop = pointtraverse(); + pointnumber++; + } + + fprintf(outfile, "end coordinates\n"); + + struct triedge triangleloop; + point p1, p2, p3; + int elementnumber; + + fprintf(outfile, "elements\n"); + + triangles.traversalinit(); + triangleloop.tri = triangletraverse(); + triangleloop.orient = 0; + elementnumber = 1; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, p1); + dest(triangleloop, p2); + apex(triangleloop, p3); + // Triangle number, indices for three points. + if (firstnumber == 0) { + // Gid need start index from one. + fprintf(outfile, "%4d %4d %4d %4d", elementnumber, + pointmark(p1)+1, pointmark(p2)+1, pointmark(p3)+1); + } else { + fprintf(outfile, "%4d %4d %4d %4d", elementnumber, + pointmark(p1), pointmark(p2), pointmark(p3)); + } + + for (i = 0; i < eextras; i++) { + fprintf(outfile, " %.17g", elemattribute(triangleloop, i)); + } + fprintf(outfile, "\n"); + + triangleloop.tri = triangletraverse(); + elementnumber++; + } + + fprintf(outfile, "end elements\n"); + fclose(outfile); +} + +/** **/ +/** **/ +/********* I/O routines end here *********/ + +/*****************************************************************************/ +/* */ +/* main() or triangulate() Gosh, do everything. */ +/* */ +/* 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 points from a file and either */ +/* - triangulate them (no -r), or */ +/* - read an old mesh from files and reconstruct it (-r). */ +/* - Insert the PSLG segments (-p), and possibly segments on the convex */ +/* hull (-c). */ +/* - Read the holes (-p), regional attributes (-pA), and regional area */ +/* constraints (-pa). Carve the holes and concavities, and spread the */ +/* regional attributes and area constraints. */ +/* - Enforce the constraints on minimum angle (-q) and maximum area (-a). */ +/* Also enforce the conforming Delaunay property (-q and -a). */ +/* - Compute the number of edges in the resulting mesh. */ +/* - Promote the mesh's linear triangles to higher order elements (-o). */ +/* - Write the output files and print the statistics. */ +/* - Check the consistency and Delaunay property of the mesh (-C). */ +/* */ +/*****************************************************************************/ + +void mesh2d::triangulate(struct triangulateio *in, struct triangulateio *out, + struct triangulateio *vorout) +{ + REAL *holearray; /* Array of holes. */ + REAL *regionarray; /* Array of regional attributes and area constraints. */ + + transfernodes(in->pointlist, in->pointattributelist, in->pointmarkerlist, + in->numberofpoints, in->numberofpointattributes); + + hullsize = delaunay(); /* Triangulate the points. */ + + /* Ensure that no point can be mistaken for a triangular bounding */ + /* box point in insertsite(). */ + infpoint1 = (point) NULL; + infpoint2 = (point) NULL; + infpoint3 = (point) NULL; + + if (useshelles) { + checksegments = 1; /* Segments will be introduced next. */ + if (!refine) { + /* Insert PSLG segments and/or convex hull segments. */ + insegments = formskeleton(in->segmentlist, in->segmentmarkerlist, + in->numberofsegments); + } + } + + if (poly) { + holearray = in->holelist; + holes = in->numberofholes; + regionarray = in->regionlist; + regions = in->numberofregions; + if (!refine) { + /* Carve out holes and concavities. */ + carveholes(holearray, holes, regionarray, regions); + } + } else { + /* Without a PSLG, there can be no holes or regional attributes */ + /* or area constraints. The following are set to zero to avoid */ + /* an accidental free() later. */ + holes = 0; + regions = 0; + } + + /* Compute the number of edges. */ + edges = (3l * triangles.items + hullsize) / 2l; + + if (!quiet) { + printf("\n"); + } + + if (out) { + out->numberofpoints = points.items; + out->numberofpointattributes = nextras; + out->numberoftriangles = triangles.items; + out->numberofcorners = (order + 1) * (order + 2) / 2; + out->numberoftriangleattributes = eextras; + out->numberofedges = edges; + if (useshelles) { + out->numberofsegments = shelles.items; + } else { + out->numberofsegments = hullsize; + } + } + if (vorout != (struct triangulateio *) NULL) { + vorout->numberofpoints = triangles.items; + vorout->numberofpointattributes = nextras; + vorout->numberofedges = edges; + } + if (out) { + /* If not using iteration numbers, don't write a .node file if one was */ + /* read, because the original one would be overwritten! */ + if (nonodewritten || (noiterationnum && readnodefile)) { + if (!quiet) { + printf("NOT writing points.\n"); + } + numbernodes(); /* We must remember to number the points. */ + } else { + writenodes(&out->pointlist, &out->pointattributelist, + &out->pointmarkerlist); + } + if (noelewritten) { + if (!quiet) { + printf("NOT writing triangles.\n"); + } + } else { + writeelements(&out->trianglelist, &out->triangleattributelist); + } + /* The -c switch (convex switch) causes a PSLG to be written */ + /* even if none was read. */ + if (poly || convex) { + /* If not using iteration numbers, don't overwrite the .poly file. */ + if (nopolywritten || noiterationnum) { + if (!quiet) { + printf("NOT writing segments.\n"); + } + } else { + writepoly(&out->segmentlist, &out->segmentmarkerlist); + out->numberofholes = holes; + out->numberofregions = regions; + if (poly) { + if (holes > 0) { + out->holelist = new REAL[holes * 2]; + memcpy(out->holelist, in->holelist, holes * 2* sizeof(REAL)); + } + if (regions > 0) { + out->regionlist = new REAL[regions * 4]; + memcpy(out->regionlist, in->regionlist, regions * 4 * sizeof(REAL)); + } + } else { + out->holelist = (REAL *) NULL; + out->regionlist = (REAL *) NULL; + } + } + } + if (edgesout) { + writeedges(&out->edgelist, &out->edgemarkerlist); + } + if (neighbors) { + writeneighbors(&out->neighborlist); + } + } + if (geomview) { + writegid(1); + } +} diff --git a/Tetgen/trilib.h b/Tetgen/trilib.h new file mode 100644 index 0000000000..f873e4c155 --- /dev/null +++ b/Tetgen/trilib.h @@ -0,0 +1,515 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// trilib.h Declaration class mesh2d for two-dimensional mesh generator. // +// // +// Tetgen Version 1.0 beta // +// July, 2001 // +// // +// Si hang // +// Email: sihang@weboo.com // +// http://www.weboo.com/sh/tetgen.htm // +// // +// You are free to use, copy and modify the sources under certain // +// circumstances, provided this copyright notice remains intact. // +// See the file LICENSE for details. // +// // +// This file with it's couple file trilib.cpp are modified version of the // +// triangle program of Jonathan Richard Shewchuk, which is public available // +// from the following Web page with its license(see below): // +// // +// http://www.cs.cmu.edu/~quake/triangle.html // +// // +// In Tetgen, there frequently need to generate a constrained Delaunay // +// triangulation of a given facet. For example, in facet recovery stage, for // +// each facet, it is necessary maintain a two-dimensional Delaunay triangul- // +// ation of its vertices, independent from the tetrahedralization in which // +// we hope its subfaces will eventually appear. For each triangular subface // +// in a facet triangulation, look for a matching face in the tetrahedraliza- // +// tion. // +// // +// For this purpose, I embeded an object of two-dimensional Delaunay triang- // +// ulator as member variable of class mesh3d(declared in tetlib.h). The type // +// (class mesh2d) of this object is declared in this file, it encapsulates // +// most of the variables and functions of triangle program. // +// // +// The usage of class mesh2d is very simple. To call mesh2d in functions, // +// use the triangulateio structure(defined below). You only write following // +// lines in functions: // +// // +// mesh2d mymesh; // +// struct triangulateio in, out; // +// char switches[] = "pznQXPN"; // Commandline switches. // +// // +// // Initialize 'in' and 'out' // +// triangulateioinit(&in); // +// triangulateioinit(&out); // +// // +// // Set input PSLG data to 'in' // +// ... // +// // +// // Do mesh, the corresponding result will store in 'out' // +// mymesh(switches, &in, &out, NULL); // +// // +// ... // +// // +// // Before return, don't forget to free 'in' and 'out' // +// triangulateiodeinit(&in); // +// triangulateiodeinit(&out); // +// // +// Please see the above Web page to get more detail descripton of command // +// line switches. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/*****************************************************************************/ +/* */ +/* Traingle program license: */ +/* */ +/* Triangle */ +/* A Two-Dimensional Quality Mesh Generator */ +/* and Delaunay Triangulator. */ +/* Version 1.3 */ +/* */ +/* Copyright 1996 */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This program may be freely redistributed under the condition that the */ +/* copyright notices (including this entire header and the copyright */ +/* notice printed when the `-h' switch is selected) are not removed, and */ +/* no compensation is received. Private, research, and institutional */ +/* use is free. You may distribute modified versions of this code UNDER */ +/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */ +/* SAME FILE 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 as */ +/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */ +/* WITH THE AUTHOR. (If you are not directly supplying this code to a */ +/* customer, and you are instead telling them how they can obtain it for */ +/* free, then you are not required to make any arrangement with me.) */ +/* */ +/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ +/* whatsoever. This code is provided "as-is". Use at your own risk. */ +/* */ +/*****************************************************************************/ + +#ifndef trilibH +#define trilibH + +#include "defines.h" +#include "linklist.h" + +/*****************************************************************************/ +/* */ +/* The `triangulateio' structure. */ +/* */ +/* Used to pass data into and out of the triangulate() procedure. */ +/* */ +/* */ +/* Arrays are used to store points, triangles, markers, and so forth. In */ +/* all cases, the first item in any array is stored starting at index [0]. */ +/* However, that item is item number `1' unless the `z' switch is used, in */ +/* which case it is item number `0'. Hence, you may find it easier to */ +/* index points (and triangles in the neighbor list) if you use the `z' */ +/* switch. Unless, of course, you're calling Triangle from a Fortran */ +/* program. */ +/* */ +/* Description of fields (except the `numberof' fields, which are obvious): */ +/* */ +/* `pointlist': An array of point coordinates. The first point's x */ +/* coordinate is at index [0] and its y coordinate at index [1], followed */ +/* by the coordinates of the remaining points. Each point occupies two */ +/* REALs. */ +/* `pointattributelist': An array of point attributes. Each point's */ +/* attributes occupy `numberofpointattributes' REALs. */ +/* `pointmarkerlist': An array of point markers; one int per point. */ +/* */ +/* `trianglelist': An array of triangle corners. The first triangle's */ +/* first corner is at index [0], followed by its other two corners in */ +/* counterclockwise order, followed by any other nodes if the triangle */ +/* represents a nonlinear element. Each triangle occupies */ +/* `numberofcorners' ints. */ +/* `triangleattributelist': An array of triangle attributes. Each */ +/* triangle's attributes occupy `numberoftriangleattributes' REALs. */ +/* `trianglearealist': An array of triangle area constraints; one REAL per */ +/* triangle. Input only. */ +/* `neighborlist': An array of triangle neighbors; three ints per */ +/* triangle. Output only. */ +/* */ +/* `segmentlist': An array of segment endpoints. The first segment's */ +/* endpoints are at indices [0] and [1], followed by the remaining */ +/* segments. Two ints per segment. */ +/* `segmentmarkerlist': An array of segment markers; one int per segment. */ +/* */ +/* `holelist': An array of holes. The first hole's x and y coordinates */ +/* are at indices [0] and [1], followed by the remaining holes. Two */ +/* REALs per hole. Input only, although the pointer is copied to the */ +/* output structure for your convenience. */ +/* */ +/* `regionlist': An array of regional attributes and area constraints. */ +/* The first constraint's x and y coordinates are at indices [0] and [1], */ +/* followed by the regional attribute and index [2], followed by the */ +/* maximum area at index [3], followed by the remaining area constraints. */ +/* Four REALs per area constraint. Note that each regional attribute is */ +/* used only if you select the `A' switch, and each area constraint is */ +/* used only if you select the `a' switch (with no number following), but */ +/* omitting one of these switches does not change the memory layout. */ +/* Input only, although the pointer is copied to the output structure for */ +/* your convenience. */ +/* */ +/* `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. Output only. */ +/* `edgemarkerlist': An array of edge markers; one int per edge. Output */ +/* only. */ +/* `normlist': An array of normal vectors, used for infinite rays in */ +/* Voronoi diagrams. The first normal vector's x and y magnitudes are */ +/* at indices [0] and [1], followed by the remaining vectors. For each */ +/* finite edge in a Voronoi diagram, the normal vector written is the */ +/* zero vector. Two REALs per edge. Output only. */ +/* */ +/* */ +/* Any input fields that Triangle will examine must be initialized. */ +/* Furthermore, for each output array that Triangle will write to, you */ +/* must either provide space by setting the appropriate pointer to point */ +/* to the space you want the data written to, or you must initialize the */ +/* pointer to NULL, which tells Triangle to allocate space for the results. */ +/* The latter option is preferable, because Triangle always knows exactly */ +/* how much space to allocate. The former option is provided mainly for */ +/* people who need to call Triangle from Fortran code, though it also makes */ +/* possible some nasty space-saving tricks, like writing the output to the */ +/* same arrays as the input. */ +/* */ +/* Triangle will not free() any input or output arrays, including those it */ +/* allocates itself; that's up to you. */ +/* */ +/* Here's a guide to help you decide which fields you must initialize */ +/* before you call triangulate(). */ +/* */ +/* `in': */ +/* */ +/* - `pointlist' must always point to a list of points; `numberofpoints' */ +/* and `numberofpointattributes' must be properly set. */ +/* `pointmarkerlist' must either be set to NULL (in which case all */ +/* markers default to zero), or must point to a list of markers. If */ +/* `numberofpointattributes' is not zero, `pointattributelist' must */ +/* point to a list of point attributes. */ +/* - If the `r' switch is used, `trianglelist' must point to a list of */ +/* triangles, and `numberoftriangles', `numberofcorners', and */ +/* `numberoftriangleattributes' must be properly set. If */ +/* `numberoftriangleattributes' is not zero, `triangleattributelist' */ +/* must point to a list of triangle attributes. If the `a' switch is */ +/* used (with no number following), `trianglearealist' must point to a */ +/* list of triangle area constraints. `neighborlist' may be ignored. */ +/* - If the `p' switch is used, `segmentlist' must point to a list of */ +/* segments, `numberofsegments' must be properly set, and */ +/* `segmentmarkerlist' must either be set to NULL (in which case all */ +/* markers default to zero), or must point to a list of markers. */ +/* - If the `p' switch is used without the `r' switch, then */ +/* `numberofholes' and `numberofregions' must be properly set. If */ +/* `numberofholes' is not zero, `holelist' must point to a list of */ +/* holes. If `numberofregions' is not zero, `regionlist' must point to */ +/* a list of region constraints. */ +/* - If the `p' switch is used, `holelist', `numberofholes', */ +/* `regionlist', and `numberofregions' is copied to `out'. (You can */ +/* nonetheless get away with not initializing them if the `r' switch is */ +/* used.) */ +/* - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */ +/* ignored. */ +/* */ +/* `out': */ +/* */ +/* - `pointlist' must be initialized (NULL or pointing to memory) unless */ +/* the `N' switch is used. `pointmarkerlist' must be initialized */ +/* unless the `N' or `B' switch is used. If `N' is not used and */ +/* `in->numberofpointattributes' is not zero, `pointattributelist' must */ +/* be initialized. */ +/* - `trianglelist' must be initialized unless the `E' switch is used. */ +/* `neighborlist' must be initialized if the `n' switch is used. If */ +/* the `E' switch is not used and (`in->numberofelementattributes' is */ +/* not zero or the `A' switch is used), `elementattributelist' must be */ +/* initialized. `trianglearealist' may be ignored. */ +/* - `segmentlist' must be initialized if the `p' or `c' switch is used, */ +/* and the `P' switch is not used. `segmentmarkerlist' must also be */ +/* initialized under these circumstances unless the `B' switch is used. */ +/* - `edgelist' must be initialized if the `e' switch is used. */ +/* `edgemarkerlist' must be initialized if the `e' switch is used and */ +/* the `B' switch is not. */ +/* - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/ +/* */ +/* `vorout' (only needed if `v' switch is used): */ +/* */ +/* - `pointlist' must be initialized. If `in->numberofpointattributes' */ +/* is not zero, `pointattributelist' must be initialized. */ +/* `pointmarkerlist' may be ignored. */ +/* - `edgelist' and `normlist' must both be initialized. */ +/* `edgemarkerlist' may be ignored. */ +/* - Everything else may be ignored. */ +/* */ +/* After a call to triangulate(), the valid fields of `out' and `vorout' */ +/* will depend, in an obvious way, on the choice of switches used. Note */ +/* that when the `p' switch is used, the pointers `holelist' and */ +/* `regionlist' are copied from `in' to `out', but no new space is */ +/* allocated; be careful that you don't free() the same array twice. On */ +/* the other hand, Triangle will never copy the `pointlist' pointer (or any */ +/* others); new space is allocated for `out->pointlist', or if the `N' */ +/* switch is used, `out->pointlist' remains uninitialized. */ +/* */ +/* All of the meaningful `numberof' fields will be properly set; for */ +/* instance, `numberofedges' will represent the number of edges in the */ +/* triangulation whether or not the edges were written. If segments are */ +/* not used, `numberofsegments' will indicate the number of boundary edges. */ +/* */ +/*****************************************************************************/ + +struct triangulateio { + REAL *pointlist; /* In / out */ + REAL *pointattributelist; /* In / out */ + int *pointmarkerlist; /* In / out */ + int numberofpoints; /* In / out */ + int numberofpointattributes; /* In / out */ + + int *trianglelist; /* In / out */ + REAL *triangleattributelist; /* In / out */ + REAL *trianglearealist; /* In only */ + int *neighborlist; /* Out only */ + int numberoftriangles; /* In / out */ + int numberofcorners; /* In / out */ + int numberoftriangleattributes; /* In / out */ + + int *segmentlist; /* In / out */ + int *segmentmarkerlist; /* In / out */ + int numberofsegments; /* In / out */ + + REAL *holelist; /* In / pointer to array copied out */ + int numberofholes; /* In / copied out */ + + REAL *regionlist; /* In / pointer to array copied out */ + int numberofregions; /* In / copied out */ + + int *edgelist; /* Out only */ + int *edgemarkerlist; /* Not used with Voronoi diagram; out only */ + REAL *normlist; /* Used only with Voronoi diagram; out only */ + int numberofedges; /* Out only */ +}; + +void triangulateioinit(struct triangulateio*); +void triangulateiodeinit(struct triangulateio*); +void triangulateioreport(struct triangulateio*, int, int, int, int, int, int); + +/* The triangle data structure. Each triangle contains three pointers to */ +/* adjoining triangles, plus three pointers to vertex points, plus three */ +/* pointers to shell edges (defined below; these pointers are usually */ +/* `dummysh'). It may or may not also contain user-defined attributes */ +/* and/or a floating-point "area constraint". It may also contain extra */ +/* pointers for nodes, when the user asks for high-order elements. */ +/* Because the size and structure of a `triangle' is not decided until */ +/* runtime, I haven't simply defined the type `triangle' to be a struct. */ + +typedef REAL **triangle; /* Really: typedef triangle *triangle */ + +/* An oriented triangle: includes a pointer to a triangle and orientation. */ +/* The orientation denotes an edge of the triangle. Hence, there are */ +/* three possible orientations. By convention, each edge is always */ +/* directed to point counterclockwise about the corresponding triangle. */ + +struct triedge { + triangle *tri; + int orient; /* Ranges from 0 to 2. */ +}; + +/* The shell data structure. Each shell edge contains two pointers to */ +/* adjoining shell edges, plus two pointers to vertex points, plus two */ +/* pointers to adjoining triangles, plus one shell marker. */ + +typedef REAL **shelle; /* Really: typedef shelle *shelle */ + +/* An oriented shell edge: includes a pointer to a shell edge and an */ +/* orientation. The orientation denotes a side of the edge. Hence, there */ +/* are two possible orientations. By convention, the edge is always */ +/* directed so that the "side" denoted is the right side of the edge. */ + +struct edge { + shelle *sh; + int shorient; /* Ranges from 0 to 1. */ +}; + +/* The point data structure. Each point is actually an array of REALs. */ +/* The number of REALs is unknown until runtime. An integer boundary */ +/* marker, and sometimes a pointer to a triangle, is appended after the */ +/* REALs. */ + +typedef REAL *point; + +/////////////////////////////////////////////////////////////////////////////// +// // +// class mesh2d // +// // +// The class simply encapsulating the well-coded and well-performed 2D unst- // +// ructed mesh generator TRIANGLE's functions into a single calss so it can // +// be called directly from my 3D mesh program. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class mesh2d { + + public: + + enum locateresult {INTRIANGLE, ONEDGE, ONVERTEX, OUTSIDE}; + enum insertsiteresult {SUCCESSFULPOINT, ENCROACHINGPOINT, VIOLATINGPOINT, + DUPLICATEPOINT}; + enum finddirectionresult {WITHIN, LEFTCOLLINEAR, RIGHTCOLLINEAR}; + + public: + + memorypool triangles; + memorypool shelles; + memorypool points; + memorypool viri; + + REAL xmin, xmax, ymin, ymax; + REAL xminextreme; + int inpoints, inelements, insegments, holes, regions; + long edges, hullsize; + int mesh_dim; + int nextras, eextras; + int triwords, shwords; + int pointmarkindex, point2triindex; + int highorderindex, elemattribindex, areaboundindex; + int checksegments; + int readnodefile; + long samples; + unsigned long randomseed; + long incirclecount, counterclockcount; + long circumcentercount; + + int poly, refine, quality, vararea, fixedarea, regionattrib, convex; + int firstnumber; + int edgesout, voronoi, neighbors, geomview; + int nopolywritten, nonodewritten, noelewritten, noiterationnum; + int nobound, noholes, nobisect, noexact; + int incremental, sweepline, dwyer; + int splitseg, steiner, steinerleft; + int docheck, quiet, verbose; + int useshelles, order; + int restartsymbol; + REAL minangle, goodangle; + REAL maxarea; + + point infpoint1, infpoint2, infpoint3; + + triangle *dummytri; + triangle *dummytribase; + shelle *dummysh; + shelle *dummyshbase; + + struct triedge recenttri; + + static int plus1mod3[3]; + static int minus1mod3[3]; + + public: + + // User interaction routines + void syntax(); + void info(); + void internalerror(); + void parsecommandline(int, char**, int); + + // Debugging routines + void printtriangle(struct triedge*); + void printshelle(struct edge*); + + // Memory management routines + void dummyinit(int, int); + void initializepointpool(); + void initializetrisegpools(); + void triangledealloc(triangle*); + triangle *triangletraverse(); + void shelledealloc(shelle*); + shelle *shelletraverse(); + void pointdealloc(point); + point pointtraverse(); + point getpoint(int number); + + // Constructors + void maketriangle(struct triedge*); + void makeshelle(struct edge*); + + void triangleinit(); + void triangledeinit(); + void trianglerestart(); + + // Geometric predicates + REAL counterclockwise(point, point, point); + REAL iincircle(point, point, point, point); + + // Point location routines + unsigned long randomnation(unsigned int); + void makepointmap(); + enum locateresult preciselocate(point, struct triedge*); + enum locateresult locate(point, struct triedge*); + + // Mesh transformation routines + void insertshelle(struct triedge*, int); + void flip(struct triedge*); + enum insertsiteresult insertsite(point, struct triedge*, struct edge*, + int, int); + + // Divide-and-conquer Delaunay triangulation + void pointsort(point*, int); + void pointmedian(point*, int, int, int); + void alternateaxes(point*, int, int); + void mergehulls(struct triedge*, struct triedge*, struct triedge*, + struct triedge*, int); + void divconqrecurse(point*, int, int, struct triedge*, struct triedge*); + long removeghosts(struct triedge*); + long divconqdelaunay(); + + // General mesh construction routines + long delaunay(); + + // Segment (shell edge) insertion routines + enum finddirectionresult finddirection(struct triedge*, point); + void segmentintersection(struct triedge*, struct edge*, point); + int scoutsegment(struct triedge*, point, int); + void conformingedge(point, point, int); + void delaunayfixup(struct triedge*, int); + void constrainededge(struct triedge*, point, int); + void insertsegment(point endpoint1, point endpoint2, int newmark); + void markhull(); + int formskeleton(int*, int*, int); + + // Carving out holes and concavities routines + void infecthull(); + void plague(); + void regionplague(REAL attribute, REAL area); + void carveholes(REAL*, int, REAL*, int); + + // I/O routines + void transfernodes(REAL*, REAL*, int*, int, int); + void numbernodes(); + void writenodes(REAL**, REAL**, int**); + void writeelements(int**, REAL**); + void writepoly(int**, int**); + void writeedges(int**, int**); + void writeneighbors(int**); + void writegid(int); + + public: + + mesh2d(char* triswtches) { + triangleinit(); + parsecommandline(1, &triswtches, 1); + } + ~mesh2d() { triangledeinit(); } + + void triangulate(struct triangulateio *in, struct triangulateio *out, + struct triangulateio *vorout); +}; + +#endif // ifndef trilibH -- GitLab