From 9ba8c2d1c63e53c025cff11df5bc70f270c3b5f8 Mon Sep 17 00:00:00 2001 From: Christophe Geuzaine <cgeuzaine@ulg.ac.be> Date: Sat, 18 May 2002 18:52:32 +0000 Subject: [PATCH] remove tetgen from cvs until we actually use it... --- 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 deletions(-) delete mode 100644 Tetgen/Makefile delete mode 100644 Tetgen/constrain.cpp delete mode 100644 Tetgen/defines.h delete mode 100644 Tetgen/linklist.cpp delete mode 100644 Tetgen/linklist.h delete mode 100644 Tetgen/predicate.cpp delete mode 100644 Tetgen/quality.cpp delete mode 100644 Tetgen/tetlib.cpp delete mode 100644 Tetgen/tetlib.h delete mode 100644 Tetgen/tetmain.cpp delete mode 100644 Tetgen/trilib.cpp delete mode 100644 Tetgen/trilib.h diff --git a/Tetgen/Makefile b/Tetgen/Makefile deleted file mode 100644 index adff2be3b3..0000000000 --- a/Tetgen/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# 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 deleted file mode 100644 index fc9c96fed0..0000000000 --- a/Tetgen/constrain.cpp +++ /dev/null @@ -1,3479 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 deleted file mode 100644 index 289dec79ae..0000000000 --- a/Tetgen/defines.h +++ /dev/null @@ -1,175 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 deleted file mode 100644 index 24d9aa23cc..0000000000 --- a/Tetgen/linklist.cpp +++ /dev/null @@ -1,1149 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 deleted file mode 100644 index 599b5f2115..0000000000 --- a/Tetgen/linklist.h +++ /dev/null @@ -1,470 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 deleted file mode 100644 index 1fa2a405c1..0000000000 --- a/Tetgen/predicate.cpp +++ /dev/null @@ -1,2945 +0,0 @@ -/*****************************************************************************/ -/* */ -/* Routines for Arbitrary Precision Floating-point Arithmetic */ -/* and Fast Robust Geometric Predicates */ -/* (predicates.c) */ -/* */ -/* May 18, 1996 */ -/* */ -/* Placed in the public domain by */ -/* Jonathan Richard Shewchuk */ -/* School of Computer Science */ -/* Carnegie Mellon University */ -/* 5000 Forbes Avenue */ -/* Pittsburgh, Pennsylvania 15213-3891 */ -/* jrs@cs.cmu.edu */ -/* */ -/* This file contains C implementation of algorithms for exact addition */ -/* and multiplication of floating-point numbers, and predicates for */ -/* robustly performing the orientation and incircle tests used in */ -/* computational geometry. The algorithms and underlying theory are */ -/* described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- */ -/* Point Arithmetic and Fast Robust Geometric Predicates." Technical */ -/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */ -/* University, Pittsburgh, Pennsylvania, May 1996. (Submitted to */ -/* Discrete & Computational Geometry.) */ -/* */ -/* This file, the paper listed above, and other information are available */ -/* from the Web page http://www.cs.cmu.edu/~quake/robust.html . */ -/* */ -/*****************************************************************************/ - -/*****************************************************************************/ -/* */ -/* Using this code: */ -/* */ -/* First, read the short or long version of the paper (from the Web page */ -/* above). */ -/* */ -/* Be sure to call exactinit() once, before calling any of the arithmetic */ -/* functions or geometric predicates. Also be sure to turn on the */ -/* optimizer when compiling this file. */ -/* */ -/* */ -/* Several geometric predicates are defined. Their parameters are all */ -/* points. Each point is an array of two or three floating-point */ -/* numbers. The geometric predicates, described in the papers, are */ -/* */ -/* orient2d(pa, pb, pc) */ -/* orient2dfast(pa, pb, pc) */ -/* orient3d(pa, pb, pc, pd) */ -/* orient3dfast(pa, pb, pc, pd) */ -/* incircle(pa, pb, pc, pd) */ -/* incirclefast(pa, pb, pc, pd) */ -/* insphere(pa, pb, pc, pd, pe) */ -/* inspherefast(pa, pb, pc, pd, pe) */ -/* */ -/* Those with suffix "fast" are approximate, non-robust versions. Those */ -/* without the suffix are adaptive precision, robust versions. There */ -/* are also versions with the suffices "exact" and "slow", which are */ -/* non-adaptive, exact arithmetic versions, which I use only for timings */ -/* in my arithmetic papers. */ -/* */ -/* */ -/* An expansion is represented by an array of floating-point numbers, */ -/* sorted from smallest to largest magnitude (possibly with interspersed */ -/* zeros). The length of each expansion is stored as a separate integer, */ -/* and each arithmetic function returns an integer which is the length */ -/* of the expansion it created. */ -/* */ -/* Several arithmetic functions are defined. Their parameters are */ -/* */ -/* e, f Input expansions */ -/* elen, flen Lengths of input expansions (must be >= 1) */ -/* h Output expansion */ -/* b Input scalar */ -/* */ -/* The arithmetic functions are */ -/* */ -/* grow_expansion(elen, e, b, h) */ -/* grow_expansion_zeroelim(elen, e, b, h) */ -/* expansion_sum(elen, e, flen, f, h) */ -/* expansion_sum_zeroelim1(elen, e, flen, f, h) */ -/* expansion_sum_zeroelim2(elen, e, flen, f, h) */ -/* fast_expansion_sum(elen, e, flen, f, h) */ -/* fast_expansion_sum_zeroelim(elen, e, flen, f, h) */ -/* linear_expansion_sum(elen, e, flen, f, h) */ -/* linear_expansion_sum_zeroelim(elen, e, flen, f, h) */ -/* scale_expansion(elen, e, b, h) */ -/* scale_expansion_zeroelim(elen, e, b, h) */ -/* compress(elen, e, h) */ -/* */ -/* All of these are described in the long version of the paper; some are */ -/* described in the short version. All return an integer that is the */ -/* length of h. Those with suffix _zeroelim perform zero elimination, */ -/* and are recommended over their counterparts. The procedure */ -/* fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on */ -/* processors that do not use the round-to-even tiebreaking rule) is */ -/* recommended over expansion_sum_zeroelim(). Each procedure has a */ -/* little note next to it (in the code below) that tells you whether or */ -/* not the output expansion may be the same array as one of the input */ -/* expansions. */ -/* */ -/* */ -/* If you look around below, you'll also find macros for a bunch of */ -/* simple unrolled arithmetic operations, and procedures for printing */ -/* expansions (commented out because they don't work with all C */ -/* compilers) and for generating random floating-point numbers whose */ -/* significand bits are all random. Most of the macros have undocumented */ -/* requirements that certain of their parameters should not be the same */ -/* variable; for safety, better to make sure all the parameters are */ -/* distinct variables. Feel free to send email to jrs@cs.cmu.edu if you */ -/* have questions. */ -/* */ -/*****************************************************************************/ - -#include "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 deleted file mode 100644 index 879673f8d8..0000000000 --- a/Tetgen/quality.cpp +++ /dev/null @@ -1,1723 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 deleted file mode 100644 index 19e74aabde..0000000000 --- a/Tetgen/tetlib.cpp +++ /dev/null @@ -1,10847 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 deleted file mode 100644 index 3c860cb03e..0000000000 --- a/Tetgen/tetlib.h +++ /dev/null @@ -1,1301 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 deleted file mode 100644 index ded9d1649a..0000000000 --- a/Tetgen/tetmain.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -#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 deleted file mode 100644 index b4cab2f132..0000000000 --- a/Tetgen/trilib.cpp +++ /dev/null @@ -1,5602 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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 deleted file mode 100644 index f873e4c155..0000000000 --- a/Tetgen/trilib.h +++ /dev/null @@ -1,515 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// 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