diff --git a/CHANGELOG b/CHANGELOG index 509eb2aefe21..7da2e125841f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,11 @@ +2013-11-28 Added qhull extension module to perform Delaunay triangulation more + robustly than before. It is used by tri.Triangulation (and hence + all pyplot.tri* methods) and mlab.griddata. Deprecated + matplotlib.delaunay module. - IMT + 2013-10-27 Added get_rlabel_position and set_rlabel_position methods to - PolarAxes to control angular position of radial tick labels. - + PolarAxes to control angular position of radial tick labels. + 2013-10-06 Add stride-based functions to mlab for easy creation of 2D arrays with less memory. @@ -27,16 +32,16 @@ and matplotlib.units.Registry. 2013-06-26 Refactored the axes module: the axes module is now a folder, - containing the following submodule: - - _subplots.py, containing all the subplots helper methods - - _base.py, containing several private methods and a new - _AxesBase class. This _AxesBase class contains all the methods - that are not directly linked to plots of the "old" Axes - - _axes.py contains the Axes class. This class now inherits from - _AxesBase: it contains all "plotting" methods and labelling - methods. - This refactoring should not affect the API. Only private methods - are not importable from the axes module anymore. + containing the following submodule: + - _subplots.py, containing all the subplots helper methods + - _base.py, containing several private methods and a new + _AxesBase class. This _AxesBase class contains all the methods + that are not directly linked to plots of the "old" Axes + - _axes.py contains the Axes class. This class now inherits from + _AxesBase: it contains all "plotting" methods and labelling + methods. + This refactoring should not affect the API. Only private methods + are not importable from the axes module anymore. 2013-05-18 Added support for arbitrary rasterization resolutions to the SVG backend. Previously the resolution was hard coded to 72 @@ -52,22 +57,21 @@ 2013-04-25 Changed all instances of: - from matplotlib import MatplotlibDeprecationWarning as mplDeprecation - to: - - from cbook import mplDeprecation + from matplotlib import MatplotlibDeprecationWarning as mplDeprecation + to: - and removed the import into the matplotlib namespace in __init__.py - Thomas Caswell + from cbook import mplDeprecation + and removed the import into the matplotlib namespace in __init__.py + Thomas Caswell 2013-04-15 Added 'axes.xmargin' and 'axes.ymargin' to rpParams to set default - margins on auto-scaleing. - TAC + margins on auto-scaleing. - TAC 2013-04-16 Added patheffect support for Line2D objects. -JJL 2013-03-19 Added support for passing `linestyle` kwarg to `step` so all `plot` - kwargs are passed to the underlying `plot` call. -TAC + kwargs are passed to the underlying `plot` call. -TAC 2013-02-25 Added classes CubicTriInterpolator, UniformTriRefiner, TriAnalyzer to matplotlib.tri module. - GBy diff --git a/MANIFEST.in b/MANIFEST.in index 542205f1f0a3..92f11d0dd7fe 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -14,7 +14,5 @@ recursive-include LICENSE * recursive-include examples * recursive-include doc * recursive-include src *.cpp *.c *.h *.m -recursive-include CXX *.cxx *.hxx *.c *.h -recursive-include agg24 * recursive-include lib * -recursive-include ttconv *.cpp *.h +recursive-include extern * diff --git a/examples/pylab_examples/griddata_demo.py b/examples/pylab_examples/griddata_demo.py index 5c11be137d8d..af04748567db 100644 --- a/examples/pylab_examples/griddata_demo.py +++ b/examples/pylab_examples/griddata_demo.py @@ -6,36 +6,22 @@ #npts = int(raw_input('enter # of random points to plot:')) seed(0) npts = 200 -x = uniform(-2,2,npts) -y = uniform(-2,2,npts) -z = x*np.exp(-x**2-y**2) +x = uniform(-2, 2, npts) +y = uniform(-2, 2, npts) +z = x*np.exp(-x**2 - y**2) # define grid. -xi = np.linspace(-2.1,2.1,100) -yi = np.linspace(-2.1,2.1,200) +xi = np.linspace(-2.1, 2.1, 100) +yi = np.linspace(-2.1, 2.1, 200) # grid the data. -zi = griddata(x,y,z,xi,yi,interp='linear') +zi = griddata(x, y, z, xi, yi, interp='linear') # contour the gridded data, plotting dots at the nonuniform data points. -CS = plt.contour(xi,yi,zi,15,linewidths=0.5,colors='k') -CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.rainbow, +CS = plt.contour(xi, yi, zi, 15, linewidths=0.5, colors='k') +CS = plt.contourf(xi, yi, zi, 15, cmap=plt.cm.rainbow, vmax=abs(zi).max(), vmin=-abs(zi).max()) -plt.colorbar() # draw colorbar +plt.colorbar() # draw colorbar # plot data points. -plt.scatter(x,y,marker='o',c='b',s=5,zorder=10) -plt.xlim(-2,2) -plt.ylim(-2,2) +plt.scatter(x, y, marker='o', c='b', s=5, zorder=10) +plt.xlim(-2, 2) +plt.ylim(-2, 2) plt.title('griddata test (%d points)' % npts) plt.show() - -# test case that scikits.delaunay fails on, but natgrid passes.. -#data = np.array([[-1, -1], [-1, 0], [-1, 1], -# [ 0, -1], [ 0, 0], [ 0, 1], -# [ 1, -1 - np.finfo(np.float_).eps], [ 1, 0], [ 1, 1], -# ]) -#x = data[:,0] -#y = data[:,1] -#z = x*np.exp(-x**2-y**2) -## define grid. -#xi = np.linspace(-1.1,1.1,100) -#yi = np.linspace(-1.1,1.1,100) -## grid the data. -#zi = griddata(x,y,z,xi,yi) diff --git a/extern/qhull/COPYING.txt b/extern/qhull/COPYING.txt new file mode 100644 index 000000000000..a4a4ade56e19 --- /dev/null +++ b/extern/qhull/COPYING.txt @@ -0,0 +1,38 @@ + Qhull, Copyright (c) 1993-2012 + + C.B. Barber + Arlington, MA + + and + + The National Science and Technology Research Center for + Computation and Visualization of Geometric Structures + (The Geometry Center) + University of Minnesota + + email: qhull@qhull.org + +This software includes Qhull from C.B. Barber and The Geometry Center. +Qhull is copyrighted as noted above. Qhull is free software and may +be obtained via http from www.qhull.org. It may be freely copied, modified, +and redistributed under the following conditions: + +1. All copyright notices must remain intact in all files. + +2. A copy of this text file must be distributed along with any copies + of Qhull that you redistribute; this includes copies that you have + modified, or copies of programs or other software products that + include Qhull. + +3. If you modify Qhull, you must include a notice giving the + name of the person performing the modification, the date of + modification, and the reason for such modification. + +4. When distributing modified versions of Qhull, or other software + products that include Qhull, you must provide notice that the original + source code may be obtained as noted above. + +5. There is no warranty or other guarantee of fitness for Qhull, it is + provided solely "as is". Bug reports or fixes may be sent to + qhull_bug@qhull.org; the authors may or may not act on them as + they desire. diff --git a/extern/qhull/geom.c b/extern/qhull/geom.c new file mode 100644 index 000000000000..a1517874aab4 --- /dev/null +++ b/extern/qhull/geom.c @@ -0,0 +1,1231 @@ +/*
--------------------------------- + + geom.c + geometric routines of qhull + + see qh-geom.htm and geom.h + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/geom.c#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ + + infrequent code goes into geom2.c +*/ + +#include "qhull_a.h" + +/*--------------------------------- + + qh_distplane( point, facet, dist ) + return distance from point to facet + + returns: + dist + if qh.RANDOMdist, joggles result + + notes: + dist > 0 if point is above facet (i.e., outside) + does not error (for qh_sortfacets, qh_outerinner) + + see: + qh_distnorm in geom2.c + qh_distplane [geom.c], QhullFacet::distance, and QhullHyperplane::distance are copies +*/ +void qh_distplane(pointT *point, facetT *facet, realT *dist) { + coordT *normal= facet->normal, *coordp, randr; + int k; + + switch (qh hull_dim){ + case 2: + *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1]; + break; + case 3: + *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2]; + break; + case 4: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]; + break; + case 5: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]; + break; + case 6: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]; + break; + case 7: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]; + break; + case 8: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7]; + break; + default: + *dist= facet->offset; + coordp= point; + for (k=qh hull_dim; k--; ) + *dist += *coordp++ * *normal++; + break; + } + zinc_(Zdistplane); + if (!qh RANDOMdist && qh IStracing < 4) + return; + if (qh RANDOMdist) { + randr= qh_RANDOMint; + *dist += (2.0 * randr / qh_RANDOMmax - 1.0) * + qh RANDOMfactor * qh MAXabs_coord; + } + if (qh IStracing >= 4) { + qh_fprintf(qh ferr, 8001, "qh_distplane: "); + qh_fprintf(qh ferr, 8002, qh_REAL_1, *dist); + qh_fprintf(qh ferr, 8003, "from p%d to f%d\n", qh_pointid(point), facet->id); + } + return; +} /* distplane */ + + +/*--------------------------------- + + qh_findbest( point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart ) + find facet that is furthest below a point + for upperDelaunay facets + returns facet only if !qh_NOupper and clearly above + + input: + starts search at 'startfacet' (can not be flipped) + if !bestoutside(qh_ALL), stops at qh.MINoutside + + returns: + best facet (reports error if NULL) + early out if isoutside defined and bestdist > qh.MINoutside + dist is distance to facet + isoutside is true if point is outside of facet + numpart counts the number of distance tests + + see also: + qh_findbestnew() + + notes: + If merging (testhorizon), searches horizon facets of coplanar best facets because + after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d + avoid calls to distplane, function calls, and real number operations. + caller traces result + Optimized for outside points. Tried recording a search set for qh_findhorizon. + Made code more complicated. + + when called by qh_partitionvisible(): + indicated by qh_ISnewfacets + qh.newfacet_list is list of simplicial, new facets + qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew) + qh.bestfacet_notsharp set if qh_sharpnewfacets returns False + + when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(), + qh_check_bestdist(), qh_addpoint() + indicated by !qh_ISnewfacets + returns best facet in neighborhood of given facet + this is best facet overall if dist > - qh.MAXcoplanar + or hull has at least a "spherical" curvature + + design: + initialize and test for early exit + repeat while there are better facets + for each neighbor of facet + exit if outside facet found + test for better facet + if point is inside and partitioning + test for new facets with a "sharp" intersection + if so, future calls go to qh_findbestnew() + test horizon facets +*/ +facetT *qh_findbest(pointT *point, facetT *startfacet, + boolT bestoutside, boolT isnewfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart) { + realT bestdist= -REALmax/2 /* avoid underflow */; + facetT *facet, *neighbor, **neighborp; + facetT *bestfacet= NULL, *lastfacet= NULL; + int oldtrace= qh IStracing; + unsigned int visitid= ++qh visit_id; + int numpartnew=0; + boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */ + + zinc_(Zfindbest); + if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) { + if (qh TRACElevel > qh IStracing) + qh IStracing= qh TRACElevel; + qh_fprintf(qh ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n", + qh_pointid(point), startfacet->id, isnewfacets, bestoutside, qh MINoutside); + qh_fprintf(qh ferr, 8005, " testhorizon? %d noupper? %d", testhorizon, noupper); + qh_fprintf(qh ferr, 8006, " Last point added was p%d.", qh furthest_id); + qh_fprintf(qh ferr, 8007, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh max_outside); + } + if (isoutside) + *isoutside= True; + if (!startfacet->flipped) { /* test startfacet */ + *numpart= 1; + qh_distplane(point, startfacet, dist); /* this code is duplicated below */ + if (!bestoutside && *dist >= qh MINoutside + && (!startfacet->upperdelaunay || !noupper)) { + bestfacet= startfacet; + goto LABELreturn_best; + } + bestdist= *dist; + if (!startfacet->upperdelaunay) { + bestfacet= startfacet; + } + }else + *numpart= 0; + startfacet->visitid= visitid; + facet= startfacet; + while (facet) { + trace4((qh ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n", + facet->id, bestdist, getid_(bestfacet))); + lastfacet= facet; + FOREACHneighbor_(facet) { + if (!neighbor->newfacet && isnewfacets) + continue; + if (neighbor->visitid == visitid) + continue; + neighbor->visitid= visitid; + if (!neighbor->flipped) { /* code duplicated above */ + (*numpart)++; + qh_distplane(point, neighbor, dist); + if (*dist > bestdist) { + if (!bestoutside && *dist >= qh MINoutside + && (!neighbor->upperdelaunay || !noupper)) { + bestfacet= neighbor; + goto LABELreturn_best; + } + if (!neighbor->upperdelaunay) { + bestfacet= neighbor; + bestdist= *dist; + break; /* switch to neighbor */ + }else if (!bestfacet) { + bestdist= *dist; + break; /* switch to neighbor */ + } + } /* end of *dist>bestdist */ + } /* end of !flipped */ + } /* end of FOREACHneighbor */ + facet= neighbor; /* non-NULL only if *dist>bestdist */ + } /* end of while facet (directed search) */ + if (isnewfacets) { + if (!bestfacet) { + bestdist= -REALmax/2; + bestfacet= qh_findbestnew(point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew); + testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */ + }else if (!qh findbest_notsharp && bestdist < - qh DISTround) { + if (qh_sharpnewfacets()) { + /* seldom used, qh_findbestnew will retest all facets */ + zinc_(Zfindnewsharp); + bestfacet= qh_findbestnew(point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew); + testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */ + qh findbestnew= True; + }else + qh findbest_notsharp= True; + } + } + if (!bestfacet) + bestfacet= qh_findbestlower(lastfacet, point, &bestdist, numpart); + if (testhorizon) + bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew); + *dist= bestdist; + if (isoutside && bestdist < qh MINoutside) + *isoutside= False; +LABELreturn_best: + zadd_(Zfindbesttot, *numpart); + zmax_(Zfindbestmax, *numpart); + (*numpart) += numpartnew; + qh IStracing= oldtrace; + return bestfacet; +} /* findbest */ + + +/*--------------------------------- + + qh_findbesthorizon( qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart ) + search coplanar and better horizon facets from startfacet/bestdist + ischeckmax turns off statistics and minsearch update + all arguments must be initialized + returns(ischeckmax): + best facet + returns(!ischeckmax): + best facet that is not upperdelaunay + allows upperdelaunay that is clearly outside + returns: + bestdist is distance to bestfacet + numpart -- updates number of distance tests + + notes: + no early out -- use qh_findbest() or qh_findbestnew() + Searches coplanar or better horizon facets + + when called by qh_check_maxout() (qh_IScheckmax) + startfacet must be closest to the point + Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum + even though other facets are below the point. + updates facet->maxoutside for good, visited facets + may return NULL + + searchdist is qh.max_outside + 2 * DISTround + + max( MINvisible('Vn'), MAXcoplanar('Un')); + This setting is a guess. It must be at least max_outside + 2*DISTround + because a facet may have a geometric neighbor across a vertex + + design: + for each horizon facet of coplanar best facets + continue if clearly inside + unless upperdelaunay or clearly outside + update best facet +*/ +facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) { + facetT *bestfacet= startfacet; + realT dist; + facetT *neighbor, **neighborp, *facet; + facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */ + int numpartinit= *numpart, coplanarfacetset_size; + unsigned int visitid= ++qh visit_id; + boolT newbest= False; /* for tracing */ + realT minsearch, searchdist; /* skip facets that are too far from point */ + + if (!ischeckmax) { + zinc_(Zfindhorizon); + }else { +#if qh_MAXoutside + if ((!qh ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside) + startfacet->maxoutside= *bestdist; +#endif + } + searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */ + minsearch= *bestdist - searchdist; + if (ischeckmax) { + /* Always check coplanar facets. Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */ + minimize_(minsearch, -searchdist); + } + coplanarfacetset_size= 0; + facet= startfacet; + while (True) { + trace4((qh ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n", + facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper, + minsearch, searchdist)); + FOREACHneighbor_(facet) { + if (neighbor->visitid == visitid) + continue; + neighbor->visitid= visitid; + if (!neighbor->flipped) { + qh_distplane(point, neighbor, &dist); + (*numpart)++; + if (dist > *bestdist) { + if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh MINoutside)) { + bestfacet= neighbor; + *bestdist= dist; + newbest= True; + if (!ischeckmax) { + minsearch= dist - searchdist; + if (dist > *bestdist + searchdist) { + zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */ + coplanarfacetset_size= 0; + } + } + } + }else if (dist < minsearch) + continue; /* if ischeckmax, dist can't be positive */ +#if qh_MAXoutside + if (ischeckmax && dist > neighbor->maxoutside) + neighbor->maxoutside= dist; +#endif + } /* end of !flipped */ + if (nextfacet) { + if (!coplanarfacetset_size++) { + SETfirst_(qh coplanarfacetset)= nextfacet; + SETtruncate_(qh coplanarfacetset, 1); + }else + qh_setappend(&qh coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv + and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */ + } + nextfacet= neighbor; + } /* end of EACHneighbor */ + facet= nextfacet; + if (facet) + nextfacet= NULL; + else if (!coplanarfacetset_size) + break; + else if (!--coplanarfacetset_size) { + facet= SETfirstt_(qh coplanarfacetset, facetT); + SETtruncate_(qh coplanarfacetset, 0); + }else + facet= (facetT*)qh_setdellast(qh coplanarfacetset); + } /* while True, for each facet in qh.coplanarfacetset */ + if (!ischeckmax) { + zadd_(Zfindhorizontot, *numpart - numpartinit); + zmax_(Zfindhorizonmax, *numpart - numpartinit); + if (newbest) + zinc_(Zparthorizon); + } + trace4((qh ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist)); + return bestfacet; +} /* findbesthorizon */ + +/*--------------------------------- + + qh_findbestnew( point, startfacet, dist, isoutside, numpart ) + find best newfacet for point + searches all of qh.newfacet_list starting at startfacet + searches horizon facets of coplanar best newfacets + searches all facets if startfacet == qh.facet_list + returns: + best new or horizon facet that is not upperdelaunay + early out if isoutside and not 'Qf' + dist is distance to facet + isoutside is true if point is outside of facet + numpart is number of distance tests + + notes: + Always used for merged new facets (see qh_USEfindbestnew) + Avoids upperdelaunay facet unless (isoutside and outside) + + Uses qh.visit_id, qh.coplanarfacetset. + If share visit_id with qh_findbest, coplanarfacetset is incorrect. + + If merging (testhorizon), searches horizon facets of coplanar best facets because + a point maybe coplanar to the bestfacet, below its horizon facet, + and above a horizon facet of a coplanar newfacet. For example, + rbox 1000 s Z1 G1e-13 | qhull + rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc + + qh_findbestnew() used if + qh_sharpnewfacets -- newfacets contains a sharp angle + if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew) + + see also: + qh_partitionall() and qh_findbest() + + design: + for each new facet starting from startfacet + test distance from point to facet + return facet if clearly outside + unless upperdelaunay and a lowerdelaunay exists + update best facet + test horizon facets +*/ +facetT *qh_findbestnew(pointT *point, facetT *startfacet, + realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) { + realT bestdist= -REALmax/2; + facetT *bestfacet= NULL, *facet; + int oldtrace= qh IStracing, i; + unsigned int visitid= ++qh visit_id; + realT distoutside= 0.0; + boolT isdistoutside; /* True if distoutside is defined */ + boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */ + + if (!startfacet) { + if (qh MERGING) + qh_fprintf(qh ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n"); + else + qh_fprintf(qh ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n", + qh furthest_id); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + zinc_(Zfindnew); + if (qh BESToutside || bestoutside) + isdistoutside= False; + else { + isdistoutside= True; + distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */ + } + if (isoutside) + *isoutside= True; + *numpart= 0; + if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) { + if (qh TRACElevel > qh IStracing) + qh IStracing= qh TRACElevel; + qh_fprintf(qh ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n", + qh_pointid(point), startfacet->id, isdistoutside, distoutside); + qh_fprintf(qh ferr, 8009, " Last point added p%d visitid %d.", qh furthest_id, visitid); + qh_fprintf(qh ferr, 8010, " Last merge was #%d.\n", zzval_(Ztotmerge)); + } + /* visit all new facets starting with startfacet, maybe qh facet_list */ + for (i=0, facet=startfacet; i < 2; i++, facet= qh newfacet_list) { + FORALLfacet_(facet) { + if (facet == startfacet && i) + break; + facet->visitid= visitid; + if (!facet->flipped) { + qh_distplane(point, facet, dist); + (*numpart)++; + if (*dist > bestdist) { + if (!facet->upperdelaunay || *dist >= qh MINoutside) { + bestfacet= facet; + if (isdistoutside && *dist >= distoutside) + goto LABELreturn_bestnew; + bestdist= *dist; + } + } + } /* end of !flipped */ + } /* FORALLfacet from startfacet or qh newfacet_list */ + } + if (testhorizon || !bestfacet) + bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet ? bestfacet : startfacet, + !qh_NOupper, &bestdist, numpart); + *dist= bestdist; + if (isoutside && *dist < qh MINoutside) + *isoutside= False; +LABELreturn_bestnew: + zadd_(Zfindnewtot, *numpart); + zmax_(Zfindnewmax, *numpart); + trace4((qh ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist)); + qh IStracing= oldtrace; + return bestfacet; +} /* findbestnew */ + +/* ============ hyperplane functions -- keep code together [?] ============ */ + +/*--------------------------------- + + qh_backnormal( rows, numrow, numcol, sign, normal, nearzero ) + given an upper-triangular rows array and a sign, + solve for normal equation x using back substitution over rows U + + returns: + normal= x + + if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2), + if fails on last row + this means that the hyperplane intersects [0,..,1] + sets last coordinate of normal to sign + otherwise + sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0] + sets nearzero + + notes: + assumes numrow == numcol-1 + + see Golub & van Loan 4.4-9 for back substitution + + solves Ux=b where Ax=b and PA=LU + b= [0,...,0,sign or 0] (sign is either -1 or +1) + last row of A= [0,...,0,1] + + 1) Ly=Pb == y=b since P only permutes the 0's of b + + design: + for each row from end + perform back substitution + if near zero + use qh_divzero for division + if zero divide and not last row + set tail of normal to 0 +*/ +void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign, + coordT *normal, boolT *nearzero) { + int i, j; + coordT *normalp, *normal_tail, *ai, *ak; + realT diagonal; + boolT waszero; + int zerocol= -1; + + normalp= normal + numcol - 1; + *normalp--= (sign ? -1.0 : 1.0); + for (i=numrow; i--; ) { + *normalp= 0.0; + ai= rows[i] + i + 1; + ak= normalp+1; + for (j=i+1; j < numcol; j++) + *normalp -= *ai++ * *ak++; + diagonal= (rows[i])[i]; + if (fabs_(diagonal) > qh MINdenom_2) + *(normalp--) /= diagonal; + else { + waszero= False; + *normalp= qh_divzero(*normalp, diagonal, qh MINdenom_1_2, &waszero); + if (waszero) { + zerocol= i; + *(normalp--)= (sign ? -1.0 : 1.0); + for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++) + *normal_tail= 0.0; + }else + normalp--; + } + } + if (zerocol != -1) { + zzinc_(Zback0); + *nearzero= True; + trace4((qh ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i)); + qh_precision("zero diagonal on back substitution"); + } +} /* backnormal */ + +/*--------------------------------- + + qh_gausselim( rows, numrow, numcol, sign ) + Gaussian elimination with partial pivoting + + returns: + rows is upper triangular (includes row exchanges) + flips sign for each row exchange + sets nearzero if pivot[k] < qh.NEARzero[k], else clears it + + notes: + if nearzero, the determinant's sign may be incorrect. + assumes numrow <= numcol + + design: + for each row + determine pivot and exchange rows if necessary + test for near zero + perform gaussian elimination step +*/ +void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) { + realT *ai, *ak, *rowp, *pivotrow; + realT n, pivot, pivot_abs= 0.0, temp; + int i, j, k, pivoti, flip=0; + + *nearzero= False; + for (k=0; k < numrow; k++) { + pivot_abs= fabs_((rows[k])[k]); + pivoti= k; + for (i=k+1; i < numrow; i++) { + if ((temp= fabs_((rows[i])[k])) > pivot_abs) { + pivot_abs= temp; + pivoti= i; + } + } + if (pivoti != k) { + rowp= rows[pivoti]; + rows[pivoti]= rows[k]; + rows[k]= rowp; + *sign ^= 1; + flip ^= 1; + } + if (pivot_abs <= qh NEARzero[k]) { + *nearzero= True; + if (pivot_abs == 0.0) { /* remainder of column == 0 */ + if (qh IStracing >= 4) { + qh_fprintf(qh ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh DISTround); + qh_printmatrix(qh ferr, "Matrix:", rows, numrow, numcol); + } + zzinc_(Zgauss0); + qh_precision("zero pivot for Gaussian elimination"); + goto LABELnextcol; + } + } + pivotrow= rows[k] + k; + pivot= *pivotrow++; /* signed value of pivot, and remainder of row */ + for (i=k+1; i < numrow; i++) { + ai= rows[i] + k; + ak= pivotrow; + n= (*ai++)/pivot; /* divzero() not needed since |pivot| >= |*ai| */ + for (j= numcol - (k+1); j--; ) + *ai++ -= n * *ak++; + } + LABELnextcol: + ; + } + wmin_(Wmindenom, pivot_abs); /* last pivot element */ + if (qh IStracing >= 5) + qh_printmatrix(qh ferr, "qh_gausselem: result", rows, numrow, numcol); +} /* gausselim */ + + +/*--------------------------------- + + qh_getangle( vect1, vect2 ) + returns the dot product of two vectors + if qh.RANDOMdist, joggles result + + notes: + the angle may be > 1.0 or < -1.0 because of roundoff errors + +*/ +realT qh_getangle(pointT *vect1, pointT *vect2) { + realT angle= 0, randr; + int k; + + for (k=qh hull_dim; k--; ) + angle += *vect1++ * *vect2++; + if (qh RANDOMdist) { + randr= qh_RANDOMint; + angle += (2.0 * randr / qh_RANDOMmax - 1.0) * + qh RANDOMfactor; + } + trace4((qh ferr, 4006, "qh_getangle: %2.2g\n", angle)); + return(angle); +} /* getangle */ + + +/*--------------------------------- + + qh_getcenter( vertices ) + returns arithmetic center of a set of vertices as a new point + + notes: + allocates point array for center +*/ +pointT *qh_getcenter(setT *vertices) { + int k; + pointT *center, *coord; + vertexT *vertex, **vertexp; + int count= qh_setsize(vertices); + + if (count < 2) { + qh_fprintf(qh ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + center= (pointT *)qh_memalloc(qh normal_size); + for (k=0; k < qh hull_dim; k++) { + coord= center+k; + *coord= 0.0; + FOREACHvertex_(vertices) + *coord += vertex->point[k]; + *coord /= count; + } + return(center); +} /* getcenter */ + + +/*--------------------------------- + + qh_getcentrum( facet ) + returns the centrum for a facet as a new point + + notes: + allocates the centrum +*/ +pointT *qh_getcentrum(facetT *facet) { + realT dist; + pointT *centrum, *point; + + point= qh_getcenter(facet->vertices); + zzinc_(Zcentrumtests); + qh_distplane(point, facet, &dist); + centrum= qh_projectpoint(point, facet, dist); + qh_memfree(point, qh normal_size); + trace4((qh ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n", + facet->id, qh_setsize(facet->vertices), dist)); + return centrum; +} /* getcentrum */ + + +/*--------------------------------- + + qh_getdistance( facet, neighbor, mindist, maxdist ) + returns the maxdist and mindist distance of any vertex from neighbor + + returns: + the max absolute value + + design: + for each vertex of facet that is not in neighbor + test the distance from vertex to neighbor +*/ +realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) { + vertexT *vertex, **vertexp; + realT dist, maxd, mind; + + FOREACHvertex_(facet->vertices) + vertex->seen= False; + FOREACHvertex_(neighbor->vertices) + vertex->seen= True; + mind= 0.0; + maxd= 0.0; + FOREACHvertex_(facet->vertices) { + if (!vertex->seen) { + zzinc_(Zbestdist); + qh_distplane(vertex->point, neighbor, &dist); + if (dist < mind) + mind= dist; + else if (dist > maxd) + maxd= dist; + } + } + *mindist= mind; + *maxdist= maxd; + mind= -mind; + if (maxd > mind) + return maxd; + else + return mind; +} /* getdistance */ + + +/*--------------------------------- + + qh_normalize( normal, dim, toporient ) + normalize a vector and report if too small + does not use min norm + + see: + qh_normalize2 +*/ +void qh_normalize(coordT *normal, int dim, boolT toporient) { + qh_normalize2( normal, dim, toporient, NULL, NULL); +} /* normalize */ + +/*--------------------------------- + + qh_normalize2( normal, dim, toporient, minnorm, ismin ) + normalize a vector and report if too small + qh.MINdenom/MINdenom1 are the upper limits for divide overflow + + returns: + normalized vector + flips sign if !toporient + if minnorm non-NULL, + sets ismin if normal < minnorm + + notes: + if zero norm + sets all elements to sqrt(1.0/dim) + if divide by zero (divzero()) + sets largest element to +/-1 + bumps Znearlysingular + + design: + computes norm + test for minnorm + if not near zero + normalizes normal + else if zero norm + sets normal to standard value + else + uses qh_divzero to normalize + if nearzero + sets norm to direction of maximum value +*/ +void qh_normalize2 (coordT *normal, int dim, boolT toporient, + realT *minnorm, boolT *ismin) { + int k; + realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3; + boolT zerodiv; + + norm1= normal+1; + norm2= normal+2; + norm3= normal+3; + if (dim == 2) + norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1)); + else if (dim == 3) + norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)); + else if (dim == 4) { + norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2) + + (*norm3)*(*norm3)); + }else if (dim > 4) { + norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2) + + (*norm3)*(*norm3); + for (k=dim-4, colp=normal+4; k--; colp++) + norm += (*colp) * (*colp); + norm= sqrt(norm); + } + if (minnorm) { + if (norm < *minnorm) + *ismin= True; + else + *ismin= False; + } + wmin_(Wmindenom, norm); + if (norm > qh MINdenom) { + if (!toporient) + norm= -norm; + *normal /= norm; + *norm1 /= norm; + if (dim == 2) + ; /* all done */ + else if (dim == 3) + *norm2 /= norm; + else if (dim == 4) { + *norm2 /= norm; + *norm3 /= norm; + }else if (dim >4) { + *norm2 /= norm; + *norm3 /= norm; + for (k=dim-4, colp=normal+4; k--; ) + *colp++ /= norm; + } + }else if (norm == 0.0) { + temp= sqrt(1.0/dim); + for (k=dim, colp=normal; k--; ) + *colp++ = temp; + }else { + if (!toporient) + norm= -norm; + for (k=dim, colp=normal; k--; colp++) { /* k used below */ + temp= qh_divzero(*colp, norm, qh MINdenom_1, &zerodiv); + if (!zerodiv) + *colp= temp; + else { + maxp= qh_maxabsval(normal, dim); + temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0); + for (k=dim, colp=normal; k--; colp++) + *colp= 0.0; + *maxp= temp; + zzinc_(Znearlysingular); + trace0((qh ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n", + norm, qh furthest_id)); + return; + } + } + } +} /* normalize */ + + +/*--------------------------------- + + qh_projectpoint( point, facet, dist ) + project point onto a facet by dist + + returns: + returns a new point + + notes: + if dist= distplane(point,facet) + this projects point to hyperplane + assumes qh_memfree_() is valid for normal_size +*/ +pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist) { + pointT *newpoint, *np, *normal; + int normsize= qh normal_size; + int k; + void **freelistp; /* used !qh_NOmem */ + + qh_memalloc_(normsize, freelistp, newpoint, pointT); + np= newpoint; + normal= facet->normal; + for (k=qh hull_dim; k--; ) + *(np++)= *point++ - dist * *normal++; + return(newpoint); +} /* projectpoint */ + + +/*--------------------------------- + + qh_setfacetplane( facet ) + sets the hyperplane for a facet + if qh.RANDOMdist, joggles hyperplane + + notes: + uses global buffers qh.gm_matrix and qh.gm_row + overwrites facet->normal if already defined + updates Wnewvertex if PRINTstatistics + sets facet->upperdelaunay if upper envelope of Delaunay triangulation + + design: + copy vertex coordinates to qh.gm_matrix/gm_row + compute determinate + if nearzero + recompute determinate with gaussian elimination + if nearzero + force outside orientation by testing interior point +*/ +void qh_setfacetplane(facetT *facet) { + pointT *point; + vertexT *vertex, **vertexp; + int normsize= qh normal_size; + int k,i, oldtrace= 0; + realT dist; + void **freelistp; /* used !qh_NOmem */ + coordT *coord, *gmcoord; + pointT *point0= SETfirstt_(facet->vertices, vertexT)->point; + boolT nearzero= False; + + zzinc_(Zsetplane); + if (!facet->normal) + qh_memalloc_(normsize, freelistp, facet->normal, coordT); + if (facet == qh tracefacet) { + oldtrace= qh IStracing; + qh IStracing= 5; + qh_fprintf(qh ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id); + qh_fprintf(qh ferr, 8013, " Last point added to hull was p%d.", qh furthest_id); + if (zzval_(Ztotmerge)) + qh_fprintf(qh ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge)); + qh_fprintf(qh ferr, 8015, "\n\nCurrent summary is:\n"); + qh_printsummary(qh ferr); + } + if (qh hull_dim <= 4) { + i= 0; + if (qh RANDOMdist) { + gmcoord= qh gm_matrix; + FOREACHvertex_(facet->vertices) { + qh gm_row[i++]= gmcoord; + coord= vertex->point; + for (k=qh hull_dim; k--; ) + *(gmcoord++)= *coord++ * qh_randomfactor(qh RANDOMa, qh RANDOMb); + } + }else { + FOREACHvertex_(facet->vertices) + qh gm_row[i++]= vertex->point; + } + qh_sethyperplane_det(qh hull_dim, qh gm_row, point0, facet->toporient, + facet->normal, &facet->offset, &nearzero); + } + if (qh hull_dim > 4 || nearzero) { + i= 0; + gmcoord= qh gm_matrix; + FOREACHvertex_(facet->vertices) { + if (vertex->point != point0) { + qh gm_row[i++]= gmcoord; + coord= vertex->point; + point= point0; + for (k=qh hull_dim; k--; ) + *(gmcoord++)= *coord++ - *point++; + } + } + qh gm_row[i]= gmcoord; /* for areasimplex */ + if (qh RANDOMdist) { + gmcoord= qh gm_matrix; + for (i=qh hull_dim-1; i--; ) { + for (k=qh hull_dim; k--; ) + *(gmcoord++) *= qh_randomfactor(qh RANDOMa, qh RANDOMb); + } + } + qh_sethyperplane_gauss(qh hull_dim, qh gm_row, point0, facet->toporient, + facet->normal, &facet->offset, &nearzero); + if (nearzero) { + if (qh_orientoutside(facet)) { + trace0((qh ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh furthest_id)); + /* this is part of using Gaussian Elimination. For example in 5-d + 1 1 1 1 0 + 1 1 1 1 1 + 0 0 0 1 0 + 0 1 0 0 0 + 1 0 0 0 0 + norm= 0.38 0.38 -0.76 0.38 0 + has a determinate of 1, but g.e. after subtracting pt. 0 has + 0's in the diagonal, even with full pivoting. It does work + if you subtract pt. 4 instead. */ + } + } + } + facet->upperdelaunay= False; + if (qh DELAUNAY) { + if (qh UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */ + if (facet->normal[qh hull_dim -1] >= qh ANGLEround * qh_ZEROdelaunay) + facet->upperdelaunay= True; + }else { + if (facet->normal[qh hull_dim -1] > -qh ANGLEround * qh_ZEROdelaunay) + facet->upperdelaunay= True; + } + } + if (qh PRINTstatistics || qh IStracing || qh TRACElevel || qh JOGGLEmax < REALmax) { + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + FOREACHvertex_(facet->vertices) { + if (vertex->point != point0) { + boolT istrace= False; + zinc_(Zdiststat); + qh_distplane(vertex->point, facet, &dist); + dist= fabs_(dist); + zinc_(Znewvertex); + wadd_(Wnewvertex, dist); + if (dist > wwval_(Wnewvertexmax)) { + wwval_(Wnewvertexmax)= dist; + if (dist > qh max_outside) { + qh max_outside= dist; /* used by qh_maxouter() */ + if (dist > qh TRACEdist) + istrace= True; + } + }else if (-dist > qh TRACEdist) + istrace= True; + if (istrace) { + qh_fprintf(qh ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n", + qh_pointid(vertex->point), vertex->id, dist, facet->id, qh furthest_id); + qh_errprint("DISTANT", facet, NULL, NULL, NULL); + } + } + } + qh RANDOMdist= qh old_randomdist; + } + if (qh IStracing >= 3) { + qh_fprintf(qh ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ", + facet->id, facet->offset); + for (k=0; k < qh hull_dim; k++) + qh_fprintf(qh ferr, 8018, "%2.2g ", facet->normal[k]); + qh_fprintf(qh ferr, 8019, "\n"); + } + if (facet == qh tracefacet) + qh IStracing= oldtrace; +} /* setfacetplane */ + + +/*--------------------------------- + + qh_sethyperplane_det( dim, rows, point0, toporient, normal, offset, nearzero ) + given dim X dim array indexed by rows[], one row per point, + toporient(flips all signs), + and point0 (any row) + set normalized hyperplane equation from oriented simplex + + returns: + normal (normalized) + offset (places point0 on the hyperplane) + sets nearzero if hyperplane not through points + + notes: + only defined for dim == 2..4 + rows[] is not modified + solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane + see Bower & Woodworth, A programmer's geometry, Butterworths 1983. + + derivation of 3-d minnorm + Goal: all vertices V_i within qh.one_merge of hyperplane + Plan: exactly translate the facet so that V_0 is the origin + exactly rotate the facet so that V_1 is on the x-axis and y_2=0. + exactly rotate the effective perturbation to only effect n_0 + this introduces a factor of sqrt(3) + n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm + Let M_d be the max coordinate difference + Let M_a be the greater of M_d and the max abs. coordinate + Let u be machine roundoff and distround be max error for distance computation + The max error for n_0 is sqrt(3) u M_a M_d / norm. n_1 is approx. 1 and n_2 is approx. 0 + The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm. Offset=0 at origin + Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge + Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d + + derivation of 4-d minnorm + same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0 + [if two vertices fixed on x-axis, can rotate the other two in yzw.] + n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3 + [all other terms contain at least two factors nearly zero.] + The max error for n_0 is sqrt(4) u M_a M_d M_d / norm + Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge + Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d +*/ +void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0, + boolT toporient, coordT *normal, realT *offset, boolT *nearzero) { + realT maxround, dist; + int i; + pointT *point; + + + if (dim == 2) { + normal[0]= dY(1,0); + normal[1]= dX(0,1); + qh_normalize2 (normal, dim, toporient, NULL, NULL); + *offset= -(point0[0]*normal[0]+point0[1]*normal[1]); + *nearzero= False; /* since nearzero norm => incident points */ + }else if (dim == 3) { + normal[0]= det2_(dY(2,0), dZ(2,0), + dY(1,0), dZ(1,0)); + normal[1]= det2_(dX(1,0), dZ(1,0), + dX(2,0), dZ(2,0)); + normal[2]= det2_(dX(2,0), dY(2,0), + dX(1,0), dY(1,0)); + qh_normalize2 (normal, dim, toporient, NULL, NULL); + *offset= -(point0[0]*normal[0] + point0[1]*normal[1] + + point0[2]*normal[2]); + maxround= qh DISTround; + for (i=dim; i--; ) { + point= rows[i]; + if (point != point0) { + dist= *offset + (point[0]*normal[0] + point[1]*normal[1] + + point[2]*normal[2]); + if (dist > maxround || dist < -maxround) { + *nearzero= True; + break; + } + } + } + }else if (dim == 4) { + normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0), + dY(1,0), dZ(1,0), dW(1,0), + dY(3,0), dZ(3,0), dW(3,0)); + normal[1]= det3_(dX(2,0), dZ(2,0), dW(2,0), + dX(1,0), dZ(1,0), dW(1,0), + dX(3,0), dZ(3,0), dW(3,0)); + normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0), + dX(1,0), dY(1,0), dW(1,0), + dX(3,0), dY(3,0), dW(3,0)); + normal[3]= det3_(dX(2,0), dY(2,0), dZ(2,0), + dX(1,0), dY(1,0), dZ(1,0), + dX(3,0), dY(3,0), dZ(3,0)); + qh_normalize2 (normal, dim, toporient, NULL, NULL); + *offset= -(point0[0]*normal[0] + point0[1]*normal[1] + + point0[2]*normal[2] + point0[3]*normal[3]); + maxround= qh DISTround; + for (i=dim; i--; ) { + point= rows[i]; + if (point != point0) { + dist= *offset + (point[0]*normal[0] + point[1]*normal[1] + + point[2]*normal[2] + point[3]*normal[3]); + if (dist > maxround || dist < -maxround) { + *nearzero= True; + break; + } + } + } + } + if (*nearzero) { + zzinc_(Zminnorm); + trace0((qh ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id)); + zzinc_(Znearlysingular); + } +} /* sethyperplane_det */ + + +/*--------------------------------- + + qh_sethyperplane_gauss( dim, rows, point0, toporient, normal, offset, nearzero ) + given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0) + set normalized hyperplane equation from oriented simplex + + returns: + normal (normalized) + offset (places point0 on the hyperplane) + + notes: + if nearzero + orientation may be incorrect because of incorrect sign flips in gausselim + solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1] + or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0] + i.e., N is normal to the hyperplane, and the unnormalized + distance to [0 .. 1] is either 1 or 0 + + design: + perform gaussian elimination + flip sign for negative values + perform back substitution + normalize result + compute offset +*/ +void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0, + boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) { + coordT *pointcoord, *normalcoef; + int k; + boolT sign= toporient, nearzero2= False; + + qh_gausselim(rows, dim-1, dim, &sign, nearzero); + for (k=dim-1; k--; ) { + if ((rows[k])[k] < 0) + sign ^= 1; + } + if (*nearzero) { + zzinc_(Znearlysingular); + trace0((qh ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh furthest_id)); + qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2); + }else { + qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2); + if (nearzero2) { + zzinc_(Znearlysingular); + trace0((qh ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh furthest_id)); + } + } + if (nearzero2) + *nearzero= True; + qh_normalize2(normal, dim, True, NULL, NULL); + pointcoord= point0; + normalcoef= normal; + *offset= -(*pointcoord++ * *normalcoef++); + for (k=dim-1; k--; ) + *offset -= *pointcoord++ * *normalcoef++; +} /* sethyperplane_gauss */ diff --git a/extern/qhull/geom.h b/extern/qhull/geom.h new file mode 100644 index 000000000000..b1a3aeccc7c0 --- /dev/null +++ b/extern/qhull/geom.h @@ -0,0 +1,173 @@ +/*--------------------------------- + + geom.h + header file for geometric routines + + see qh-geom.htm and geom.c + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/geom.h#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#ifndef qhDEFgeom +#define qhDEFgeom 1 + +#include "libqhull.h" + +/* ============ -macros- ======================== */ + +/*---------------------------------- + + fabs_(a) + returns the absolute value of a +*/ +#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a )) + +/*---------------------------------- + + fmax_(a,b) + returns the maximum value of a and b +*/ +#define fmax_( a,b ) ( ( a ) < ( b ) ? ( b ) : ( a ) ) + +/*---------------------------------- + + fmin_(a,b) + returns the minimum value of a and b +*/ +#define fmin_( a,b ) ( ( a ) > ( b ) ? ( b ) : ( a ) ) + +/*---------------------------------- + + maximize_(maxval, val) + set maxval to val if val is greater than maxval +*/ +#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); } + +/*---------------------------------- + + minimize_(minval, val) + set minval to val if val is less than minval +*/ +#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); } + +/*---------------------------------- + + det2_(a1, a2, + b1, b2) + + compute a 2-d determinate +*/ +#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 )) + +/*---------------------------------- + + det3_(a1, a2, a3, + b1, b2, b3, + c1, c2, c3) + + compute a 3-d determinate +*/ +#define det3_( a1,a2,a3,b1,b2,b3,c1,c2,c3 ) ( ( a1 )*det2_( b2,b3,c2,c3 ) \ + - ( b1 )*det2_( a2,a3,c2,c3 ) + ( c1 )*det2_( a2,a3,b2,b3 ) ) + +/*---------------------------------- + + dX( p1, p2 ) + dY( p1, p2 ) + dZ( p1, p2 ) + + given two indices into rows[], + + compute the difference between X, Y, or Z coordinates +*/ +#define dX( p1,p2 ) ( *( rows[p1] ) - *( rows[p2] )) +#define dY( p1,p2 ) ( *( rows[p1]+1 ) - *( rows[p2]+1 )) +#define dZ( p1,p2 ) ( *( rows[p1]+2 ) - *( rows[p2]+2 )) +#define dW( p1,p2 ) ( *( rows[p1]+3 ) - *( rows[p2]+3 )) + +/*============= prototypes in alphabetical order, infrequent at end ======= */ + +void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero); +void qh_distplane(pointT *point, facetT *facet, realT *dist); +facetT *qh_findbest(pointT *point, facetT *startfacet, + boolT bestoutside, boolT isnewfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart); +facetT *qh_findbesthorizon(boolT ischeckmax, pointT *point, + facetT *startfacet, boolT noupper, realT *bestdist, int *numpart); +facetT *qh_findbestnew(pointT *point, facetT *startfacet, realT *dist, + boolT bestoutside, boolT *isoutside, int *numpart); +void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero); +realT qh_getangle(pointT *vect1, pointT *vect2); +pointT *qh_getcenter(setT *vertices); +pointT *qh_getcentrum(facetT *facet); +realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist); +void qh_normalize(coordT *normal, int dim, boolT toporient); +void qh_normalize2 (coordT *normal, int dim, boolT toporient, + realT *minnorm, boolT *ismin); +pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist); + +void qh_setfacetplane(facetT *newfacets); +void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0, + boolT toporient, coordT *normal, realT *offset, boolT *nearzero); +void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0, + boolT toporient, coordT *normal, coordT *offset, boolT *nearzero); +boolT qh_sharpnewfacets(void); + +/*========= infrequently used code in geom2.c =============*/ + +coordT *qh_copypoints(coordT *points, int numpoints, int dimension); +void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]); +realT qh_determinant(realT **rows, int dim, boolT *nearzero); +realT qh_detjoggle(pointT *points, int numpoints, int dimension); +void qh_detroundoff(void); +realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero); +realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp); +realT qh_distround(int dimension, realT maxabs, realT maxsumabs); +realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv); +realT qh_facetarea(facetT *facet); +realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices, + vertexT *notvertex, boolT toporient, coordT *normal, realT *offset); +pointT *qh_facetcenter(setT *vertices); +facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, facetT **facetlist); +void qh_getarea(facetT *facetlist); +boolT qh_gram_schmidt(int dim, realT **rows); +boolT qh_inthresholds(coordT *normal, realT *angle); +void qh_joggleinput(void); +realT *qh_maxabsval(realT *normal, int dim); +setT *qh_maxmin(pointT *points, int numpoints, int dimension); +realT qh_maxouter(void); +void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex); +realT qh_minabsval(realT *normal, int dim); +int qh_mindiff(realT *vecA, realT *vecB, int dim); +boolT qh_orientoutside(facetT *facet); +void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane); +coordT qh_pointdist(pointT *point1, pointT *point2, int dim); +void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol); +void qh_printpoints(FILE *fp, const char *string, setT *points); +void qh_projectinput(void); +void qh_projectpoints(signed char *project, int n, realT *points, + int numpoints, int dim, realT *newpoints, int newdim); +void qh_rotateinput(realT **rows); +void qh_rotatepoints(realT *points, int numpoints, int dim, realT **rows); +void qh_scaleinput(void); +void qh_scalelast(coordT *points, int numpoints, int dim, coordT low, + coordT high, coordT newhigh); +void qh_scalepoints(pointT *points, int numpoints, int dim, + realT *newlows, realT *newhighs); +boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp, + coordT *normal, coordT *offset, coordT *feasible); +coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible); +pointT *qh_voronoi_center(int dim, setT *points); + +#endif /* qhDEFgeom */ diff --git a/extern/qhull/geom2.c b/extern/qhull/geom2.c new file mode 100644 index 000000000000..2ba4a9f251f1 --- /dev/null +++ b/extern/qhull/geom2.c @@ -0,0 +1,2080 @@ +/*--------------------------------- + + + geom2.c + infrequently used geometric routines of qhull + + see qh-geom.htm and geom.h + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/geom2.c#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ + + frequently used code goes into geom.c +*/ + +#include "qhull_a.h" + +/*================== functions in alphabetic order ============*/ + +/*--------------------------------- + + qh_copypoints( points, numpoints, dimension) + return qh_malloc'd copy of points +*/ +coordT *qh_copypoints(coordT *points, int numpoints, int dimension) { + int size; + coordT *newpoints; + + size= numpoints * dimension * (int)sizeof(coordT); + if (!(newpoints=(coordT*)qh_malloc((size_t)size))) { + qh_fprintf(qh ferr, 6004, "qhull error: insufficient memory to copy %d points\n", + numpoints); + qh_errexit(qh_ERRmem, NULL, NULL); + } + memcpy((char *)newpoints, (char *)points, (size_t)size); + return newpoints; +} /* copypoints */ + +/*--------------------------------- + + qh_crossproduct( dim, vecA, vecB, vecC ) + crossproduct of 2 dim vectors + C= A x B + + notes: + from Glasner, Graphics Gems I, p. 639 + only defined for dim==3 +*/ +void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){ + + if (dim == 3) { + vecC[0]= det2_(vecA[1], vecA[2], + vecB[1], vecB[2]); + vecC[1]= - det2_(vecA[0], vecA[2], + vecB[0], vecB[2]); + vecC[2]= det2_(vecA[0], vecA[1], + vecB[0], vecB[1]); + } +} /* vcross */ + +/*--------------------------------- + + qh_determinant( rows, dim, nearzero ) + compute signed determinant of a square matrix + uses qh.NEARzero to test for degenerate matrices + + returns: + determinant + overwrites rows and the matrix + if dim == 2 or 3 + nearzero iff determinant < qh NEARzero[dim-1] + (!quite correct, not critical) + if dim >= 4 + nearzero iff diagonal[k] < qh NEARzero[k] +*/ +realT qh_determinant(realT **rows, int dim, boolT *nearzero) { + realT det=0; + int i; + boolT sign= False; + + *nearzero= False; + if (dim < 2) { + qh_fprintf(qh ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + }else if (dim == 2) { + det= det2_(rows[0][0], rows[0][1], + rows[1][0], rows[1][1]); + if (fabs_(det) < qh NEARzero[1]) /* not really correct, what should this be? */ + *nearzero= True; + }else if (dim == 3) { + det= det3_(rows[0][0], rows[0][1], rows[0][2], + rows[1][0], rows[1][1], rows[1][2], + rows[2][0], rows[2][1], rows[2][2]); + if (fabs_(det) < qh NEARzero[2]) /* not really correct, what should this be? */ + *nearzero= True; + }else { + qh_gausselim(rows, dim, dim, &sign, nearzero); /* if nearzero, diagonal still ok*/ + det= 1.0; + for (i=dim; i--; ) + det *= (rows[i])[i]; + if (sign) + det= -det; + } + return det; +} /* determinant */ + +/*--------------------------------- + + qh_detjoggle( points, numpoints, dimension ) + determine default max joggle for point array + as qh_distround * qh_JOGGLEdefault + + returns: + initial value for JOGGLEmax from points and REALepsilon + + notes: + computes DISTround since qh_maxmin not called yet + if qh SCALElast, last dimension will be scaled later to MAXwidth + + loop duplicated from qh_maxmin +*/ +realT qh_detjoggle(pointT *points, int numpoints, int dimension) { + realT abscoord, distround, joggle, maxcoord, mincoord; + pointT *point, *pointtemp; + realT maxabs= -REALmax; + realT sumabs= 0; + realT maxwidth= 0; + int k; + + for (k=0; k < dimension; k++) { + if (qh SCALElast && k == dimension-1) + abscoord= maxwidth; + else if (qh DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */ + abscoord= 2 * maxabs * maxabs; /* may be low by qh hull_dim/2 */ + else { + maxcoord= -REALmax; + mincoord= REALmax; + FORALLpoint_(points, numpoints) { + maximize_(maxcoord, point[k]); + minimize_(mincoord, point[k]); + } + maximize_(maxwidth, maxcoord-mincoord); + abscoord= fmax_(maxcoord, -mincoord); + } + sumabs += abscoord; + maximize_(maxabs, abscoord); + } /* for k */ + distround= qh_distround(qh hull_dim, maxabs, sumabs); + joggle= distround * qh_JOGGLEdefault; + maximize_(joggle, REALepsilon * qh_JOGGLEdefault); + trace2((qh ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth)); + return joggle; +} /* detjoggle */ + +/*--------------------------------- + + qh_detroundoff() + determine maximum roundoff errors from + REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord, + qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1 + + accounts for qh.SETroundoff, qh.RANDOMdist, qh MERGEexact + qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum, + qh.postmerge_centrum, qh.MINoutside, + qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar + + returns: + sets qh.DISTround, etc. (see below) + appends precision constants to qh.qhull_options + + see: + qh_maxmin() for qh.NEARzero + + design: + determine qh.DISTround for distance computations + determine minimum denominators for qh_divzero + determine qh.ANGLEround for angle computations + adjust qh.premerge_cos,... for roundoff error + determine qh.ONEmerge for maximum error due to a single merge + determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible, + qh.MINoutside, qh.WIDEfacet + initialize qh.max_vertex and qh.minvertex +*/ +void qh_detroundoff(void) { + + qh_option("_max-width", NULL, &qh MAXwidth); + if (!qh SETroundoff) { + qh DISTround= qh_distround(qh hull_dim, qh MAXabs_coord, qh MAXsumcoord); + if (qh RANDOMdist) + qh DISTround += qh RANDOMfactor * qh MAXabs_coord; + qh_option("Error-roundoff", NULL, &qh DISTround); + } + qh MINdenom= qh MINdenom_1 * qh MAXabs_coord; + qh MINdenom_1_2= sqrt(qh MINdenom_1 * qh hull_dim) ; /* if will be normalized */ + qh MINdenom_2= qh MINdenom_1_2 * qh MAXabs_coord; + /* for inner product */ + qh ANGLEround= 1.01 * qh hull_dim * REALepsilon; + if (qh RANDOMdist) + qh ANGLEround += qh RANDOMfactor; + if (qh premerge_cos < REALmax/2) { + qh premerge_cos -= qh ANGLEround; + if (qh RANDOMdist) + qh_option("Angle-premerge-with-random", NULL, &qh premerge_cos); + } + if (qh postmerge_cos < REALmax/2) { + qh postmerge_cos -= qh ANGLEround; + if (qh RANDOMdist) + qh_option("Angle-postmerge-with-random", NULL, &qh postmerge_cos); + } + qh premerge_centrum += 2 * qh DISTround; /*2 for centrum and distplane()*/ + qh postmerge_centrum += 2 * qh DISTround; + if (qh RANDOMdist && (qh MERGEexact || qh PREmerge)) + qh_option("Centrum-premerge-with-random", NULL, &qh premerge_centrum); + if (qh RANDOMdist && qh POSTmerge) + qh_option("Centrum-postmerge-with-random", NULL, &qh postmerge_centrum); + { /* compute ONEmerge, max vertex offset for merging simplicial facets */ + realT maxangle= 1.0, maxrho; + + minimize_(maxangle, qh premerge_cos); + minimize_(maxangle, qh postmerge_cos); + /* max diameter * sin theta + DISTround for vertex to its hyperplane */ + qh ONEmerge= sqrt((realT)qh hull_dim) * qh MAXwidth * + sqrt(1.0 - maxangle * maxangle) + qh DISTround; + maxrho= qh hull_dim * qh premerge_centrum + qh DISTround; + maximize_(qh ONEmerge, maxrho); + maxrho= qh hull_dim * qh postmerge_centrum + qh DISTround; + maximize_(qh ONEmerge, maxrho); + if (qh MERGING) + qh_option("_one-merge", NULL, &qh ONEmerge); + } + qh NEARinside= qh ONEmerge * qh_RATIOnearinside; /* only used if qh KEEPnearinside */ + if (qh JOGGLEmax < REALmax/2 && (qh KEEPcoplanar || qh KEEPinside)) { + realT maxdist; /* adjust qh.NEARinside for joggle */ + qh KEEPnearinside= True; + maxdist= sqrt((realT)qh hull_dim) * qh JOGGLEmax + qh DISTround; + maxdist= 2*maxdist; /* vertex and coplanar point can joggle in opposite directions */ + maximize_(qh NEARinside, maxdist); /* must agree with qh_nearcoplanar() */ + } + if (qh KEEPnearinside) + qh_option("_near-inside", NULL, &qh NEARinside); + if (qh JOGGLEmax < qh DISTround) { + qh_fprintf(qh ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n", + qh JOGGLEmax, qh DISTround); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh MINvisible > REALmax/2) { + if (!qh MERGING) + qh MINvisible= qh DISTround; + else if (qh hull_dim <= 3) + qh MINvisible= qh premerge_centrum; + else + qh MINvisible= qh_COPLANARratio * qh premerge_centrum; + if (qh APPROXhull && qh MINvisible > qh MINoutside) + qh MINvisible= qh MINoutside; + qh_option("Visible-distance", NULL, &qh MINvisible); + } + if (qh MAXcoplanar > REALmax/2) { + qh MAXcoplanar= qh MINvisible; + qh_option("U-coplanar-distance", NULL, &qh MAXcoplanar); + } + if (!qh APPROXhull) { /* user may specify qh MINoutside */ + qh MINoutside= 2 * qh MINvisible; + if (qh premerge_cos < REALmax/2) + maximize_(qh MINoutside, (1- qh premerge_cos) * qh MAXabs_coord); + qh_option("Width-outside", NULL, &qh MINoutside); + } + qh WIDEfacet= qh MINoutside; + maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MAXcoplanar); + maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MINvisible); + qh_option("_wide-facet", NULL, &qh WIDEfacet); + if (qh MINvisible > qh MINoutside + 3 * REALepsilon + && !qh BESToutside && !qh FORCEoutput) + qh_fprintf(qh ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g. Flipped facets are likely.\n", + qh MINvisible, qh MINoutside); + qh max_vertex= qh DISTround; + qh min_vertex= -qh DISTround; + /* numeric constants reported in printsummary */ +} /* detroundoff */ + +/*--------------------------------- + + qh_detsimplex( apex, points, dim, nearzero ) + compute determinant of a simplex with point apex and base points + + returns: + signed determinant and nearzero from qh_determinant + + notes: + uses qh.gm_matrix/qh.gm_row (assumes they're big enough) + + design: + construct qm_matrix by subtracting apex from points + compute determinate +*/ +realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero) { + pointT *coorda, *coordp, *gmcoord, *point, **pointp; + coordT **rows; + int k, i=0; + realT det; + + zinc_(Zdetsimplex); + gmcoord= qh gm_matrix; + rows= qh gm_row; + FOREACHpoint_(points) { + if (i == dim) + break; + rows[i++]= gmcoord; + coordp= point; + coorda= apex; + for (k=dim; k--; ) + *(gmcoord++)= *coordp++ - *coorda++; + } + if (i < dim) { + qh_fprintf(qh ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n", + i, dim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + det= qh_determinant(rows, dim, nearzero); + trace2((qh ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n", + det, qh_pointid(apex), dim, *nearzero)); + return det; +} /* detsimplex */ + +/*--------------------------------- + + qh_distnorm( dim, point, normal, offset ) + return distance from point to hyperplane at normal/offset + + returns: + dist + + notes: + dist > 0 if point is outside of hyperplane + + see: + qh_distplane in geom.c +*/ +realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) { + coordT *normalp= normal, *coordp= point; + realT dist; + int k; + + dist= *offsetp; + for (k=dim; k--; ) + dist += *(coordp++) * *(normalp++); + return dist; +} /* distnorm */ + +/*--------------------------------- + + qh_distround(dimension, maxabs, maxsumabs ) + compute maximum round-off error for a distance computation + to a normalized hyperplane + maxabs is the maximum absolute value of a coordinate + maxsumabs is the maximum possible sum of absolute coordinate values + + returns: + max dist round for REALepsilon + + notes: + calculate roundoff error according to + Lemma 3.2-1 of Golub and van Loan "Matrix Computation" + use sqrt(dim) since one vector is normalized + or use maxsumabs since one vector is < 1 +*/ +realT qh_distround(int dimension, realT maxabs, realT maxsumabs) { + realT maxdistsum, maxround; + + maxdistsum= sqrt((realT)dimension) * maxabs; + minimize_( maxdistsum, maxsumabs); + maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs); + /* adds maxabs for offset */ + trace4((qh ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n", + maxround, maxabs, maxsumabs, maxdistsum)); + return maxround; +} /* distround */ + +/*--------------------------------- + + qh_divzero( numer, denom, mindenom1, zerodiv ) + divide by a number that's nearly zero + mindenom1= minimum denominator for dividing into 1.0 + + returns: + quotient + sets zerodiv and returns 0.0 if it would overflow + + design: + if numer is nearly zero and abs(numer) < abs(denom) + return numer/denom + else if numer is nearly zero + return 0 and zerodiv + else if denom/numer non-zero + return numer/denom + else + return 0 and zerodiv +*/ +realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) { + realT temp, numerx, denomx; + + + if (numer < mindenom1 && numer > -mindenom1) { + numerx= fabs_(numer); + denomx= fabs_(denom); + if (numerx < denomx) { + *zerodiv= False; + return numer/denom; + }else { + *zerodiv= True; + return 0.0; + } + } + temp= denom/numer; + if (temp > mindenom1 || temp < -mindenom1) { + *zerodiv= False; + return numer/denom; + }else { + *zerodiv= True; + return 0.0; + } +} /* divzero */ + + +/*--------------------------------- + + qh_facetarea( facet ) + return area for a facet + + notes: + if non-simplicial, + uses centrum to triangulate facet and sums the projected areas. + if (qh DELAUNAY), + computes projected area instead for last coordinate + assumes facet->normal exists + projecting tricoplanar facets to the hyperplane does not appear to make a difference + + design: + if simplicial + compute area + else + for each ridge + compute area from centrum to ridge + negate area if upper Delaunay facet +*/ +realT qh_facetarea(facetT *facet) { + vertexT *apex; + pointT *centrum; + realT area= 0.0; + ridgeT *ridge, **ridgep; + + if (facet->simplicial) { + apex= SETfirstt_(facet->vertices, vertexT); + area= qh_facetarea_simplex(qh hull_dim, apex->point, facet->vertices, + apex, facet->toporient, facet->normal, &facet->offset); + }else { + if (qh CENTERtype == qh_AScentrum) + centrum= facet->center; + else + centrum= qh_getcentrum(facet); + FOREACHridge_(facet->ridges) + area += qh_facetarea_simplex(qh hull_dim, centrum, ridge->vertices, + NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset); + if (qh CENTERtype != qh_AScentrum) + qh_memfree(centrum, qh normal_size); + } + if (facet->upperdelaunay && qh DELAUNAY) + area= -area; /* the normal should be [0,...,1] */ + trace4((qh ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area)); + return area; +} /* facetarea */ + +/*--------------------------------- + + qh_facetarea_simplex( dim, apex, vertices, notvertex, toporient, normal, offset ) + return area for a simplex defined by + an apex, a base of vertices, an orientation, and a unit normal + if simplicial or tricoplanar facet, + notvertex is defined and it is skipped in vertices + + returns: + computes area of simplex projected to plane [normal,offset] + returns 0 if vertex too far below plane (qh WIDEfacet) + vertex can't be apex of tricoplanar facet + + notes: + if (qh DELAUNAY), + computes projected area instead for last coordinate + uses qh gm_matrix/gm_row and qh hull_dim + helper function for qh_facetarea + + design: + if Notvertex + translate simplex to apex + else + project simplex to normal/offset + translate simplex to apex + if Delaunay + set last row/column to 0 with -1 on diagonal + else + set last row to Normal + compute determinate + scale and flip sign for area +*/ +realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices, + vertexT *notvertex, boolT toporient, coordT *normal, realT *offset) { + pointT *coorda, *coordp, *gmcoord; + coordT **rows, *normalp; + int k, i=0; + realT area, dist; + vertexT *vertex, **vertexp; + boolT nearzero; + + gmcoord= qh gm_matrix; + rows= qh gm_row; + FOREACHvertex_(vertices) { + if (vertex == notvertex) + continue; + rows[i++]= gmcoord; + coorda= apex; + coordp= vertex->point; + normalp= normal; + if (notvertex) { + for (k=dim; k--; ) + *(gmcoord++)= *coordp++ - *coorda++; + }else { + dist= *offset; + for (k=dim; k--; ) + dist += *coordp++ * *normalp++; + if (dist < -qh WIDEfacet) { + zinc_(Znoarea); + return 0.0; + } + coordp= vertex->point; + normalp= normal; + for (k=dim; k--; ) + *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++; + } + } + if (i != dim-1) { + qh_fprintf(qh ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n", + i, dim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + rows[i]= gmcoord; + if (qh DELAUNAY) { + for (i=0; i < dim-1; i++) + rows[i][dim-1]= 0.0; + for (k=dim; k--; ) + *(gmcoord++)= 0.0; + rows[dim-1][dim-1]= -1.0; + }else { + normalp= normal; + for (k=dim; k--; ) + *(gmcoord++)= *normalp++; + } + zinc_(Zdetsimplex); + area= qh_determinant(rows, dim, &nearzero); + if (toporient) + area= -area; + area *= qh AREAfactor; + trace4((qh ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n", + area, qh_pointid(apex), toporient, nearzero)); + return area; +} /* facetarea_simplex */ + +/*--------------------------------- + + qh_facetcenter( vertices ) + return Voronoi center (Voronoi vertex) for a facet's vertices + + returns: + return temporary point equal to the center + + see: + qh_voronoi_center() +*/ +pointT *qh_facetcenter(setT *vertices) { + setT *points= qh_settemp(qh_setsize(vertices)); + vertexT *vertex, **vertexp; + pointT *center; + + FOREACHvertex_(vertices) + qh_setappend(&points, vertex->point); + center= qh_voronoi_center(qh hull_dim-1, points); + qh_settempfree(&points); + return center; +} /* facetcenter */ + +/*--------------------------------- + + qh_findgooddist( point, facetA, dist, facetlist ) + find best good facet visible for point from facetA + assumes facetA is visible from point + + returns: + best facet, i.e., good facet that is furthest from point + distance to best facet + NULL if none + + moves good, visible facets (and some other visible facets) + to end of qh facet_list + + notes: + uses qh visit_id + + design: + initialize bestfacet if facetA is good + move facetA to end of facetlist + for each facet on facetlist + for each unvisited neighbor of facet + move visible neighbors to end of facetlist + update best good neighbor + if no good neighbors, update best facet +*/ +facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, + facetT **facetlist) { + realT bestdist= -REALmax, dist; + facetT *neighbor, **neighborp, *bestfacet=NULL, *facet; + boolT goodseen= False; + + if (facetA->good) { + zzinc_(Zcheckpart); /* calls from check_bestdist occur after print stats */ + qh_distplane(point, facetA, &bestdist); + bestfacet= facetA; + goodseen= True; + } + qh_removefacet(facetA); + qh_appendfacet(facetA); + *facetlist= facetA; + facetA->visitid= ++qh visit_id; + FORALLfacet_(*facetlist) { + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh visit_id) + continue; + neighbor->visitid= qh visit_id; + if (goodseen && !neighbor->good) + continue; + zzinc_(Zcheckpart); + qh_distplane(point, neighbor, &dist); + if (dist > 0) { + qh_removefacet(neighbor); + qh_appendfacet(neighbor); + if (neighbor->good) { + goodseen= True; + if (dist > bestdist) { + bestdist= dist; + bestfacet= neighbor; + } + } + } + } + } + if (bestfacet) { + *distp= bestdist; + trace2((qh ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n", + qh_pointid(point), bestdist, bestfacet->id)); + return bestfacet; + } + trace4((qh ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n", + qh_pointid(point), facetA->id)); + return NULL; +} /* findgooddist */ + +/*--------------------------------- + + qh_getarea( facetlist ) + set area of all facets in facetlist + collect statistics + nop if hasAreaVolume + + returns: + sets qh totarea/totvol to total area and volume of convex hull + for Delaunay triangulation, computes projected area of the lower or upper hull + ignores upper hull if qh ATinfinity + + notes: + could compute outer volume by expanding facet area by rays from interior + the following attempt at perpendicular projection underestimated badly: + qh.totoutvol += (-dist + facet->maxoutside + qh DISTround) + * area/ qh hull_dim; + design: + for each facet on facetlist + compute facet->area + update qh.totarea and qh.totvol +*/ +void qh_getarea(facetT *facetlist) { + realT area; + realT dist; + facetT *facet; + + if (qh hasAreaVolume) + return; + if (qh REPORTfreq) + qh_fprintf(qh ferr, 8020, "computing area of each facet and volume of the convex hull\n"); + else + trace1((qh ferr, 1001, "qh_getarea: computing volume and area for each facet\n")); + qh totarea= qh totvol= 0.0; + FORALLfacet_(facetlist) { + if (!facet->normal) + continue; + if (facet->upperdelaunay && qh ATinfinity) + continue; + if (!facet->isarea) { + facet->f.area= qh_facetarea(facet); + facet->isarea= True; + } + area= facet->f.area; + if (qh DELAUNAY) { + if (facet->upperdelaunay == qh UPPERdelaunay) + qh totarea += area; + }else { + qh totarea += area; + qh_distplane(qh interior_point, facet, &dist); + qh totvol += -dist * area/ qh hull_dim; + } + if (qh PRINTstatistics) { + wadd_(Wareatot, area); + wmax_(Wareamax, area); + wmin_(Wareamin, area); + } + } + qh hasAreaVolume= True; +} /* getarea */ + +/*--------------------------------- + + qh_gram_schmidt( dim, row ) + implements Gram-Schmidt orthogonalization by rows + + returns: + false if zero norm + overwrites rows[dim][dim] + + notes: + see Golub & van Loan Algorithm 6.2-2 + overflow due to small divisors not handled + + design: + for each row + compute norm for row + if non-zero, normalize row + for each remaining rowA + compute inner product of row and rowA + reduce rowA by row * inner product +*/ +boolT qh_gram_schmidt(int dim, realT **row) { + realT *rowi, *rowj, norm; + int i, j, k; + + for (i=0; i < dim; i++) { + rowi= row[i]; + for (norm= 0.0, k= dim; k--; rowi++) + norm += *rowi * *rowi; + norm= sqrt(norm); + wmin_(Wmindenom, norm); + if (norm == 0.0) /* either 0 or overflow due to sqrt */ + return False; + for (k=dim; k--; ) + *(--rowi) /= norm; + for (j=i+1; j < dim; j++) { + rowj= row[j]; + for (norm= 0.0, k=dim; k--; ) + norm += *rowi++ * *rowj++; + for (k=dim; k--; ) + *(--rowj) -= *(--rowi) * norm; + } + } + return True; +} /* gram_schmidt */ + + +/*--------------------------------- + + qh_inthresholds( normal, angle ) + return True if normal within qh.lower_/upper_threshold + + returns: + estimate of angle by summing of threshold diffs + angle may be NULL + smaller "angle" is better + + notes: + invalid if qh.SPLITthresholds + + see: + qh.lower_threshold in qh_initbuild() + qh_initthresholds() + + design: + for each dimension + test threshold +*/ +boolT qh_inthresholds(coordT *normal, realT *angle) { + boolT within= True; + int k; + realT threshold; + + if (angle) + *angle= 0.0; + for (k=0; k < qh hull_dim; k++) { + threshold= qh lower_threshold[k]; + if (threshold > -REALmax/2) { + if (normal[k] < threshold) + within= False; + if (angle) { + threshold -= normal[k]; + *angle += fabs_(threshold); + } + } + if (qh upper_threshold[k] < REALmax/2) { + threshold= qh upper_threshold[k]; + if (normal[k] > threshold) + within= False; + if (angle) { + threshold -= normal[k]; + *angle += fabs_(threshold); + } + } + } + return within; +} /* inthresholds */ + + +/*--------------------------------- + + qh_joggleinput() + randomly joggle input to Qhull by qh.JOGGLEmax + initial input is qh.first_point/qh.num_points of qh.hull_dim + repeated calls use qh.input_points/qh.num_points + + returns: + joggles points at qh.first_point/qh.num_points + copies data to qh.input_points/qh.input_malloc if first time + determines qh.JOGGLEmax if it was zero + if qh.DELAUNAY + computes the Delaunay projection of the joggled points + + notes: + if qh.DELAUNAY, unnecessarily joggles the last coordinate + the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease + + design: + if qh.DELAUNAY + set qh.SCALElast for reduced precision errors + if first call + initialize qh.input_points to the original input points + if qh.JOGGLEmax == 0 + determine default qh.JOGGLEmax + else + increase qh.JOGGLEmax according to qh.build_cnt + joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax] + if qh.DELAUNAY + sets the Delaunay projection +*/ +void qh_joggleinput(void) { + int i, seed, size; + coordT *coordp, *inputp; + realT randr, randa, randb; + + if (!qh input_points) { /* first call */ + qh input_points= qh first_point; + qh input_malloc= qh POINTSmalloc; + size= qh num_points * qh hull_dim * sizeof(coordT); + if (!(qh first_point=(coordT*)qh_malloc((size_t)size))) { + qh_fprintf(qh ferr, 6009, "qhull error: insufficient memory to joggle %d points\n", + qh num_points); + qh_errexit(qh_ERRmem, NULL, NULL); + } + qh POINTSmalloc= True; + if (qh JOGGLEmax == 0.0) { + qh JOGGLEmax= qh_detjoggle(qh input_points, qh num_points, qh hull_dim); + qh_option("QJoggle", NULL, &qh JOGGLEmax); + } + }else { /* repeated call */ + if (!qh RERUN && qh build_cnt > qh_JOGGLEretry) { + if (((qh build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) { + realT maxjoggle= qh MAXwidth * qh_JOGGLEmaxincrease; + if (qh JOGGLEmax < maxjoggle) { + qh JOGGLEmax *= qh_JOGGLEincrease; + minimize_(qh JOGGLEmax, maxjoggle); + } + } + } + qh_option("QJoggle", NULL, &qh JOGGLEmax); + } + if (qh build_cnt > 1 && qh JOGGLEmax > fmax_(qh MAXwidth/4, 0.1)) { + qh_fprintf(qh ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n", + qh JOGGLEmax); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + /* for some reason, using qh ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */ + seed= qh_RANDOMint; + qh_option("_joggle-seed", &seed, NULL); + trace0((qh ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n", + qh JOGGLEmax, seed)); + inputp= qh input_points; + coordp= qh first_point; + randa= 2.0 * qh JOGGLEmax/qh_RANDOMmax; + randb= -qh JOGGLEmax; + size= qh num_points * qh hull_dim; + for (i=size; i--; ) { + randr= qh_RANDOMint; + *(coordp++)= *(inputp++) + (randr * randa + randb); + } + if (qh DELAUNAY) { + qh last_low= qh last_high= qh last_newhigh= REALmax; + qh_setdelaunay(qh hull_dim, qh num_points, qh first_point); + } +} /* joggleinput */ + +/*--------------------------------- + + qh_maxabsval( normal, dim ) + return pointer to maximum absolute value of a dim vector + returns NULL if dim=0 +*/ +realT *qh_maxabsval(realT *normal, int dim) { + realT maxval= -REALmax; + realT *maxp= NULL, *colp, absval; + int k; + + for (k=dim, colp= normal; k--; colp++) { + absval= fabs_(*colp); + if (absval > maxval) { + maxval= absval; + maxp= colp; + } + } + return maxp; +} /* maxabsval */ + + +/*--------------------------------- + + qh_maxmin( points, numpoints, dimension ) + return max/min points for each dimension + determine max and min coordinates + + returns: + returns a temporary set of max and min points + may include duplicate points. Does not include qh.GOODpoint + sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth + qh.MAXlastcoord, qh.MINlastcoord + initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok + + notes: + loop duplicated in qh_detjoggle() + + design: + initialize global precision variables + checks definition of REAL... + for each dimension + for each point + collect maximum and minimum point + collect maximum of maximums and minimum of minimums + determine qh.NEARzero for Gaussian Elimination +*/ +setT *qh_maxmin(pointT *points, int numpoints, int dimension) { + int k; + realT maxcoord, temp; + pointT *minimum, *maximum, *point, *pointtemp; + setT *set; + + qh max_outside= 0.0; + qh MAXabs_coord= 0.0; + qh MAXwidth= -REALmax; + qh MAXsumcoord= 0.0; + qh min_vertex= 0.0; + qh WAScoplanar= False; + if (qh ZEROcentrum) + qh ZEROall_ok= True; + if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax + && REALmax > 0.0 && -REALmax < 0.0) + ; /* all ok */ + else { + qh_fprintf(qh ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\ +REALepsilon %g REALmin %g REALmax %g -REALmax %g\n", + REALepsilon, REALmin, REALmax, -REALmax); + qh_errexit(qh_ERRinput, NULL, NULL); + } + set= qh_settemp(2*dimension); + for (k=0; k < dimension; k++) { + if (points == qh GOODpointp) + minimum= maximum= points + dimension; + else + minimum= maximum= points; + FORALLpoint_(points, numpoints) { + if (point == qh GOODpointp) + continue; + if (maximum[k] < point[k]) + maximum= point; + else if (minimum[k] > point[k]) + minimum= point; + } + if (k == dimension-1) { + qh MINlastcoord= minimum[k]; + qh MAXlastcoord= maximum[k]; + } + if (qh SCALElast && k == dimension-1) + maxcoord= qh MAXwidth; + else { + maxcoord= fmax_(maximum[k], -minimum[k]); + if (qh GOODpointp) { + temp= fmax_(qh GOODpointp[k], -qh GOODpointp[k]); + maximize_(maxcoord, temp); + } + temp= maximum[k] - minimum[k]; + maximize_(qh MAXwidth, temp); + } + maximize_(qh MAXabs_coord, maxcoord); + qh MAXsumcoord += maxcoord; + qh_setappend(&set, maximum); + qh_setappend(&set, minimum); + /* calculation of qh NEARzero is based on error formula 4.4-13 of + Golub & van Loan, authors say n^3 can be ignored and 10 be used in + place of rho */ + qh NEARzero[k]= 80 * qh MAXsumcoord * REALepsilon; + } + if (qh IStracing >=1) + qh_printpoints(qh ferr, "qh_maxmin: found the max and min points(by dim):", set); + return(set); +} /* maxmin */ + +/*--------------------------------- + + qh_maxouter() + return maximum distance from facet to outer plane + normally this is qh.max_outside+qh.DISTround + does not include qh.JOGGLEmax + + see: + qh_outerinner() + + notes: + need to add another qh.DISTround if testing actual point with computation + + for joggle: + qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex) + need to use Wnewvertexmax since could have a coplanar point for a high + facet that is replaced by a low facet + need to add qh.JOGGLEmax if testing input points +*/ +realT qh_maxouter(void) { + realT dist; + + dist= fmax_(qh max_outside, qh DISTround); + dist += qh DISTround; + trace4((qh ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh max_outside)); + return dist; +} /* maxouter */ + +/*--------------------------------- + + qh_maxsimplex( dim, maxpoints, points, numpoints, simplex ) + determines maximum simplex for a set of points + starts from points already in simplex + skips qh.GOODpointp (assumes that it isn't in maxpoints) + + returns: + simplex with dim+1 points + + notes: + assumes at least pointsneeded points in points + maximizes determinate for x,y,z,w, etc. + uses maxpoints as long as determinate is clearly non-zero + + design: + initialize simplex with at least two points + (find points with max or min x coordinate) + for each remaining dimension + add point that maximizes the determinate + (use points from maxpoints first) +*/ +void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) { + pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL; + boolT nearzero, maxnearzero= False; + int k, sizinit; + realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax; + + sizinit= qh_setsize(*simplex); + if (sizinit < 2) { + if (qh_setsize(maxpoints) >= 2) { + FOREACHpoint_(maxpoints) { + if (maxcoord < point[0]) { + maxcoord= point[0]; + maxx= point; + } + if (mincoord > point[0]) { + mincoord= point[0]; + minx= point; + } + } + }else { + FORALLpoint_(points, numpoints) { + if (point == qh GOODpointp) + continue; + if (maxcoord < point[0]) { + maxcoord= point[0]; + maxx= point; + } + if (mincoord > point[0]) { + mincoord= point[0]; + minx= point; + } + } + } + qh_setunique(simplex, minx); + if (qh_setsize(*simplex) < 2) + qh_setunique(simplex, maxx); + sizinit= qh_setsize(*simplex); + if (sizinit < 2) { + qh_precision("input has same x coordinate"); + if (zzval_(Zsetplane) > qh hull_dim+1) { + qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n", + qh_setsize(maxpoints)+numpoints); + qh_errexit(qh_ERRprec, NULL, NULL); + }else { + qh_fprintf(qh ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh hull_dim); + qh_errexit(qh_ERRinput, NULL, NULL); + } + } + } + for (k=sizinit; k < dim+1; k++) { + maxpoint= NULL; + maxdet= -REALmax; + FOREACHpoint_(maxpoints) { + if (!qh_setin(*simplex, point)) { + det= qh_detsimplex(point, *simplex, k, &nearzero); + if ((det= fabs_(det)) > maxdet) { + maxdet= det; + maxpoint= point; + maxnearzero= nearzero; + } + } + } + if (!maxpoint || maxnearzero) { + zinc_(Zsearchpoints); + if (!maxpoint) { + trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1)); + }else { + trace0((qh ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n", + k+1, qh_pointid(maxpoint), maxdet)); + } + FORALLpoint_(points, numpoints) { + if (point == qh GOODpointp) + continue; + if (!qh_setin(*simplex, point)) { + det= qh_detsimplex(point, *simplex, k, &nearzero); + if ((det= fabs_(det)) > maxdet) { + maxdet= det; + maxpoint= point; + maxnearzero= nearzero; + } + } + } + } /* !maxpoint */ + if (!maxpoint) { + qh_fprintf(qh ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_setappend(simplex, maxpoint); + trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n", + qh_pointid(maxpoint), k+1, maxdet)); + } /* k */ +} /* maxsimplex */ + +/*--------------------------------- + + qh_minabsval( normal, dim ) + return minimum absolute value of a dim vector +*/ +realT qh_minabsval(realT *normal, int dim) { + realT minval= 0; + realT maxval= 0; + realT *colp; + int k; + + for (k=dim, colp=normal; k--; colp++) { + maximize_(maxval, *colp); + minimize_(minval, *colp); + } + return fmax_(maxval, -minval); +} /* minabsval */ + + +/*--------------------------------- + + qh_mindif ( vecA, vecB, dim ) + return index of min abs. difference of two vectors +*/ +int qh_mindiff(realT *vecA, realT *vecB, int dim) { + realT mindiff= REALmax, diff; + realT *vecAp= vecA, *vecBp= vecB; + int k, mink= 0; + + for (k=0; k < dim; k++) { + diff= *vecAp++ - *vecBp++; + diff= fabs_(diff); + if (diff < mindiff) { + mindiff= diff; + mink= k; + } + } + return mink; +} /* mindiff */ + + + +/*--------------------------------- + + qh_orientoutside( facet ) + make facet outside oriented via qh.interior_point + + returns: + True if facet reversed orientation. +*/ +boolT qh_orientoutside(facetT *facet) { + int k; + realT dist; + + qh_distplane(qh interior_point, facet, &dist); + if (dist > 0) { + for (k=qh hull_dim; k--; ) + facet->normal[k]= -facet->normal[k]; + facet->offset= -facet->offset; + return True; + } + return False; +} /* orientoutside */ + +/*--------------------------------- + + qh_outerinner( facet, outerplane, innerplane ) + if facet and qh.maxoutdone (i.e., qh_check_maxout) + returns outer and inner plane for facet + else + returns maximum outer and inner plane + accounts for qh.JOGGLEmax + + see: + qh_maxouter(), qh_check_bestdist(), qh_check_points() + + notes: + outerplaner or innerplane may be NULL + facet is const + Does not error (QhullFacet) + + includes qh.DISTround for actual points + adds another qh.DISTround if testing with floating point arithmetic +*/ +void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane) { + realT dist, mindist; + vertexT *vertex, **vertexp; + + if (outerplane) { + if (!qh_MAXoutside || !facet || !qh maxoutdone) { + *outerplane= qh_maxouter(); /* includes qh.DISTround */ + }else { /* qh_MAXoutside ... */ +#if qh_MAXoutside + *outerplane= facet->maxoutside + qh DISTround; +#endif + + } + if (qh JOGGLEmax < REALmax/2) + *outerplane += qh JOGGLEmax * sqrt((realT)qh hull_dim); + } + if (innerplane) { + if (facet) { + mindist= REALmax; + FOREACHvertex_(facet->vertices) { + zinc_(Zdistio); + qh_distplane(vertex->point, facet, &dist); + minimize_(mindist, dist); + } + *innerplane= mindist - qh DISTround; + }else + *innerplane= qh min_vertex - qh DISTround; + if (qh JOGGLEmax < REALmax/2) + *innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim); + } +} /* outerinner */ + +/*--------------------------------- + + qh_pointdist( point1, point2, dim ) + return distance between two points + + notes: + returns distance squared if 'dim' is negative +*/ +coordT qh_pointdist(pointT *point1, pointT *point2, int dim) { + coordT dist, diff; + int k; + + dist= 0.0; + for (k= (dim > 0 ? dim : -dim); k--; ) { + diff= *point1++ - *point2++; + dist += diff * diff; + } + if (dim > 0) + return(sqrt(dist)); + return dist; +} /* pointdist */ + + +/*--------------------------------- + + qh_printmatrix( fp, string, rows, numrow, numcol ) + print matrix to fp given by row vectors + print string as header + + notes: + print a vector by qh_printmatrix(fp, "", &vect, 1, len) +*/ +void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol) { + realT *rowp; + realT r; /*bug fix*/ + int i,k; + + qh_fprintf(fp, 9001, "%s\n", string); + for (i=0; i < numrow; i++) { + rowp= rows[i]; + for (k=0; k < numcol; k++) { + r= *rowp++; + qh_fprintf(fp, 9002, "%6.3g ", r); + } + qh_fprintf(fp, 9003, "\n"); + } +} /* printmatrix */ + + +/*--------------------------------- + + qh_printpoints( fp, string, points ) + print pointids to fp for a set of points + if string, prints string and 'p' point ids +*/ +void qh_printpoints(FILE *fp, const char *string, setT *points) { + pointT *point, **pointp; + + if (string) { + qh_fprintf(fp, 9004, "%s", string); + FOREACHpoint_(points) + qh_fprintf(fp, 9005, " p%d", qh_pointid(point)); + qh_fprintf(fp, 9006, "\n"); + }else { + FOREACHpoint_(points) + qh_fprintf(fp, 9007, " %d", qh_pointid(point)); + qh_fprintf(fp, 9008, "\n"); + } +} /* printpoints */ + + +/*--------------------------------- + + qh_projectinput() + project input points using qh.lower_bound/upper_bound and qh DELAUNAY + if qh.lower_bound[k]=qh.upper_bound[k]= 0, + removes dimension k + if halfspace intersection + removes dimension k from qh.feasible_point + input points in qh first_point, num_points, input_dim + + returns: + new point array in qh first_point of qh hull_dim coordinates + sets qh POINTSmalloc + if qh DELAUNAY + projects points to paraboloid + lowbound/highbound is also projected + if qh ATinfinity + adds point "at-infinity" + if qh POINTSmalloc + frees old point array + + notes: + checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY + + + design: + sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay) + determines newdim and newnum for qh hull_dim and qh num_points + projects points to newpoints + projects qh.lower_bound to itself + projects qh.upper_bound to itself + if qh DELAUNAY + if qh ATINFINITY + projects points to paraboloid + computes "infinity" point as vertex average and 10% above all points + else + uses qh_setdelaunay to project points to paraboloid +*/ +void qh_projectinput(void) { + int k,i; + int newdim= qh input_dim, newnum= qh num_points; + signed char *project; + int size= (qh input_dim+1)*sizeof(*project); + pointT *newpoints, *coord, *infinity; + realT paraboloid, maxboloid= 0; + + project= (signed char*)qh_memalloc(size); + memset((char*)project, 0, (size_t)size); + for (k=0; k < qh input_dim; k++) { /* skip Delaunay bound */ + if (qh lower_bound[k] == 0 && qh upper_bound[k] == 0) { + project[k]= -1; + newdim--; + } + } + if (qh DELAUNAY) { + project[k]= 1; + newdim++; + if (qh ATinfinity) + newnum++; + } + if (newdim != qh hull_dim) { + qh_fprintf(qh ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh hull_dim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (!(newpoints=(coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){ + qh_fprintf(qh ferr, 6016, "qhull error: insufficient memory to project %d points\n", + qh num_points); + qh_errexit(qh_ERRmem, NULL, NULL); + } + qh_projectpoints(project, qh input_dim+1, qh first_point, + qh num_points, qh input_dim, newpoints, newdim); + trace1((qh ferr, 1003, "qh_projectinput: updating lower and upper_bound\n")); + qh_projectpoints(project, qh input_dim+1, qh lower_bound, + 1, qh input_dim+1, qh lower_bound, newdim+1); + qh_projectpoints(project, qh input_dim+1, qh upper_bound, + 1, qh input_dim+1, qh upper_bound, newdim+1); + if (qh HALFspace) { + if (!qh feasible_point) { + qh_fprintf(qh ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_projectpoints(project, qh input_dim, qh feasible_point, + 1, qh input_dim, qh feasible_point, newdim); + } + qh_memfree(project, (qh input_dim+1)*sizeof(*project)); + if (qh POINTSmalloc) + qh_free(qh first_point); + qh first_point= newpoints; + qh POINTSmalloc= True; + if (qh DELAUNAY && qh ATinfinity) { + coord= qh first_point; + infinity= qh first_point + qh hull_dim * qh num_points; + for (k=qh hull_dim-1; k--; ) + infinity[k]= 0.0; + for (i=qh num_points; i--; ) { + paraboloid= 0.0; + for (k=0; k < qh hull_dim-1; k++) { + paraboloid += *coord * *coord; + infinity[k] += *coord; + coord++; + } + *(coord++)= paraboloid; + maximize_(maxboloid, paraboloid); + } + /* coord == infinity */ + for (k=qh hull_dim-1; k--; ) + *(coord++) /= qh num_points; + *(coord++)= maxboloid * 1.1; + qh num_points++; + trace0((qh ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n")); + }else if (qh DELAUNAY) /* !qh ATinfinity */ + qh_setdelaunay( qh hull_dim, qh num_points, qh first_point); +} /* projectinput */ + + +/*--------------------------------- + + qh_projectpoints( project, n, points, numpoints, dim, newpoints, newdim ) + project points/numpoints/dim to newpoints/newdim + if project[k] == -1 + delete dimension k + if project[k] == 1 + add dimension k by duplicating previous column + n is size of project + + notes: + newpoints may be points if only adding dimension at end + + design: + check that 'project' and 'newdim' agree + for each dimension + if project == -1 + skip dimension + else + determine start of column in newpoints + determine start of column in points + if project == +1, duplicate previous column + copy dimension (column) from points to newpoints +*/ +void qh_projectpoints(signed char *project, int n, realT *points, + int numpoints, int dim, realT *newpoints, int newdim) { + int testdim= dim, oldk=0, newk=0, i,j=0,k; + realT *newp, *oldp; + + for (k=0; k < n; k++) + testdim += project[k]; + if (testdim != newdim) { + qh_fprintf(qh ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n", + newdim, testdim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + for (j=0; j= dim) + continue; + oldp= points+oldk; + }else + oldp= points+oldk++; + for (i=numpoints; i--; ) { + *newp= *oldp; + newp += newdim; + oldp += dim; + } + } + if (oldk >= dim) + break; + } + trace1((qh ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n", + numpoints, dim, newdim)); +} /* projectpoints */ + + +/*--------------------------------- + + qh_rotateinput( rows ) + rotate input using row matrix + input points given by qh first_point, num_points, hull_dim + assumes rows[dim] is a scratch buffer + if qh POINTSmalloc, overwrites input points, else mallocs a new array + + returns: + rotated input + sets qh POINTSmalloc + + design: + see qh_rotatepoints +*/ +void qh_rotateinput(realT **rows) { + + if (!qh POINTSmalloc) { + qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim); + qh POINTSmalloc= True; + } + qh_rotatepoints(qh first_point, qh num_points, qh hull_dim, rows); +} /* rotateinput */ + +/*--------------------------------- + + qh_rotatepoints( points, numpoints, dim, row ) + rotate numpoints points by a d-dim row matrix + assumes rows[dim] is a scratch buffer + + returns: + rotated points in place + + design: + for each point + for each coordinate + use row[dim] to compute partial inner product + for each coordinate + rotate by partial inner product +*/ +void qh_rotatepoints(realT *points, int numpoints, int dim, realT **row) { + realT *point, *rowi, *coord= NULL, sum, *newval; + int i,j,k; + + if (qh IStracing >= 1) + qh_printmatrix(qh ferr, "qh_rotatepoints: rotate points by", row, dim, dim); + for (point= points, j= numpoints; j--; point += dim) { + newval= row[dim]; + for (i=0; i < dim; i++) { + rowi= row[i]; + coord= point; + for (sum= 0.0, k= dim; k--; ) + sum += *rowi++ * *coord++; + *(newval++)= sum; + } + for (k=dim; k--; ) + *(--coord)= *(--newval); + } +} /* rotatepoints */ + + +/*--------------------------------- + + qh_scaleinput() + scale input points using qh low_bound/high_bound + input points given by qh first_point, num_points, hull_dim + if qh POINTSmalloc, overwrites input points, else mallocs a new array + + returns: + scales coordinates of points to low_bound[k], high_bound[k] + sets qh POINTSmalloc + + design: + see qh_scalepoints +*/ +void qh_scaleinput(void) { + + if (!qh POINTSmalloc) { + qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim); + qh POINTSmalloc= True; + } + qh_scalepoints(qh first_point, qh num_points, qh hull_dim, + qh lower_bound, qh upper_bound); +} /* scaleinput */ + +/*--------------------------------- + + qh_scalelast( points, numpoints, dim, low, high, newhigh ) + scale last coordinate to [0,m] for Delaunay triangulations + input points given by points, numpoints, dim + + returns: + changes scale of last coordinate from [low, high] to [0, newhigh] + overwrites last coordinate of each point + saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay() + + notes: + when called by qh_setdelaunay, low/high may not match actual data + + design: + compute scale and shift factors + apply to last coordinate of each point +*/ +void qh_scalelast(coordT *points, int numpoints, int dim, coordT low, + coordT high, coordT newhigh) { + realT scale, shift; + coordT *coord; + int i; + boolT nearzero= False; + + trace4((qh ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n", + low, high, newhigh)); + qh last_low= low; + qh last_high= high; + qh last_newhigh= newhigh; + scale= qh_divzero(newhigh, high - low, + qh MINdenom_1, &nearzero); + if (nearzero) { + if (qh DELAUNAY) + qh_fprintf(qh ferr, 6019, "qhull input error: can not scale last coordinate. Input is cocircular\n or cospherical. Use option 'Qz' to add a point at infinity.\n"); + else + qh_fprintf(qh ferr, 6020, "qhull input error: can not scale last coordinate. New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n", + newhigh, low, high, high-low); + qh_errexit(qh_ERRinput, NULL, NULL); + } + shift= - low * newhigh / (high-low); + coord= points + dim - 1; + for (i=numpoints; i--; coord += dim) + *coord= *coord * scale + shift; +} /* scalelast */ + +/*--------------------------------- + + qh_scalepoints( points, numpoints, dim, newlows, newhighs ) + scale points to new lowbound and highbound + retains old bound when newlow= -REALmax or newhigh= +REALmax + + returns: + scaled points + overwrites old points + + design: + for each coordinate + compute current low and high bound + compute scale and shift factors + scale all points + enforce new low and high bound for all points +*/ +void qh_scalepoints(pointT *points, int numpoints, int dim, + realT *newlows, realT *newhighs) { + int i,k; + realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord; + boolT nearzero= False; + + for (k=0; k < dim; k++) { + newhigh= newhighs[k]; + newlow= newlows[k]; + if (newhigh > REALmax/2 && newlow < -REALmax/2) + continue; + low= REALmax; + high= -REALmax; + for (i=numpoints, coord=points+k; i--; coord += dim) { + minimize_(low, *coord); + maximize_(high, *coord); + } + if (newhigh > REALmax/2) + newhigh= high; + if (newlow < -REALmax/2) + newlow= low; + if (qh DELAUNAY && k == dim-1 && newhigh < newlow) { + qh_fprintf(qh ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n", + k, k, newhigh, newlow); + qh_errexit(qh_ERRinput, NULL, NULL); + } + scale= qh_divzero(newhigh - newlow, high - low, + qh MINdenom_1, &nearzero); + if (nearzero) { + qh_fprintf(qh ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n", + k, newlow, newhigh, low, high); + qh_errexit(qh_ERRinput, NULL, NULL); + } + shift= (newlow * high - low * newhigh)/(high-low); + coord= points+k; + for (i=numpoints; i--; coord += dim) + *coord= *coord * scale + shift; + coord= points+k; + if (newlow < newhigh) { + mincoord= newlow; + maxcoord= newhigh; + }else { + mincoord= newhigh; + maxcoord= newlow; + } + for (i=numpoints; i--; coord += dim) { + minimize_(*coord, maxcoord); /* because of roundoff error */ + maximize_(*coord, mincoord); + } + trace0((qh ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n", + k, low, high, newlow, newhigh, numpoints, scale, shift)); + } +} /* scalepoints */ + + +/*--------------------------------- + + qh_setdelaunay( dim, count, points ) + project count points to dim-d paraboloid for Delaunay triangulation + + dim is one more than the dimension of the input set + assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation) + + points is a dim*count realT array. The first dim-1 coordinates + are the coordinates of the first input point. array[dim] is + the first coordinate of the second input point. array[2*dim] is + the first coordinate of the third input point. + + if qh.last_low defined (i.e., 'Qbb' called qh_scalelast) + calls qh_scalelast to scale the last coordinate the same as the other points + + returns: + for each point + sets point[dim-1] to sum of squares of coordinates + scale points to 'Qbb' if needed + + notes: + to project one point, use + qh_setdelaunay(qh hull_dim, 1, point) + + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale + the coordinates after the original projection. + +*/ +void qh_setdelaunay(int dim, int count, pointT *points) { + int i, k; + coordT *coordp, coord; + realT paraboloid; + + trace0((qh ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count)); + coordp= points; + for (i=0; i < count; i++) { + coord= *coordp++; + paraboloid= coord*coord; + for (k=dim-2; k--; ) { + coord= *coordp++; + paraboloid += coord*coord; + } + *coordp++ = paraboloid; + } + if (qh last_low < REALmax/2) + qh_scalelast(points, count, dim, qh last_low, qh last_high, qh last_newhigh); +} /* setdelaunay */ + + +/*--------------------------------- + + qh_sethalfspace( dim, coords, nextp, normal, offset, feasible ) + set point to dual of halfspace relative to feasible point + halfspace is normal coefficients and offset. + + returns: + false if feasible point is outside of hull (error message already reported) + overwrites coordinates for point at dim coords + nextp= next point (coords) + + design: + compute distance from feasible point to halfspace + divide each normal coefficient by -dist +*/ +boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp, + coordT *normal, coordT *offset, coordT *feasible) { + coordT *normp= normal, *feasiblep= feasible, *coordp= coords; + realT dist; + realT r; /*bug fix*/ + int k; + boolT zerodiv; + + dist= *offset; + for (k=dim; k--; ) + dist += *(normp++) * *(feasiblep++); + if (dist > 0) + goto LABELerroroutside; + normp= normal; + if (dist < -qh MINdenom) { + for (k=dim; k--; ) + *(coordp++)= *(normp++) / -dist; + }else { + for (k=dim; k--; ) { + *(coordp++)= qh_divzero(*(normp++), -dist, qh MINdenom_1, &zerodiv); + if (zerodiv) + goto LABELerroroutside; + } + } + *nextp= coordp; + if (qh IStracing >= 4) { + qh_fprintf(qh ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset); + for (k=dim, coordp=coords; k--; ) { + r= *coordp++; + qh_fprintf(qh ferr, 8022, " %6.2g", r); + } + qh_fprintf(qh ferr, 8023, "\n"); + } + return True; +LABELerroroutside: + feasiblep= feasible; + normp= normal; + qh_fprintf(qh ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: "); + for (k=dim; k--; ) + qh_fprintf(qh ferr, 8024, qh_REAL_1, r=*(feasiblep++)); + qh_fprintf(qh ferr, 8025, "\n halfspace: "); + for (k=dim; k--; ) + qh_fprintf(qh ferr, 8026, qh_REAL_1, r=*(normp++)); + qh_fprintf(qh ferr, 8027, "\n at offset: "); + qh_fprintf(qh ferr, 8028, qh_REAL_1, *offset); + qh_fprintf(qh ferr, 8029, " and distance: "); + qh_fprintf(qh ferr, 8030, qh_REAL_1, dist); + qh_fprintf(qh ferr, 8031, "\n"); + return False; +} /* sethalfspace */ + +/*--------------------------------- + + qh_sethalfspace_all( dim, count, halfspaces, feasible ) + generate dual for halfspace intersection with feasible point + array of count halfspaces + each halfspace is normal coefficients followed by offset + the origin is inside the halfspace if the offset is negative + + returns: + malloc'd array of count X dim-1 points + + notes: + call before qh_init_B or qh_initqhull_globals + unused/untested code: please email bradb@shore.net if this works ok for you + If using option 'Fp', also set qh feasible_point. It is a malloc'd array + that is freed by qh_freebuffers. + + design: + see qh_sethalfspace +*/ +coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible) { + int i, newdim; + pointT *newpoints; + coordT *coordp, *normalp, *offsetp; + + trace0((qh ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n")); + newdim= dim - 1; + if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){ + qh_fprintf(qh ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n", + count); + qh_errexit(qh_ERRmem, NULL, NULL); + } + coordp= newpoints; + normalp= halfspaces; + for (i=0; i < count; i++) { + offsetp= normalp + newdim; + if (!qh_sethalfspace(newdim, coordp, &coordp, normalp, offsetp, feasible)) { + qh_fprintf(qh ferr, 8032, "The halfspace was at index %d\n", i); + qh_errexit(qh_ERRinput, NULL, NULL); + } + normalp= offsetp + 1; + } + return newpoints; +} /* sethalfspace_all */ + + +/*--------------------------------- + + qh_sharpnewfacets() + + returns: + true if could be an acute angle (facets in different quadrants) + + notes: + for qh_findbest + + design: + for all facets on qh.newfacet_list + if two facets are in different quadrants + set issharp +*/ +boolT qh_sharpnewfacets() { + facetT *facet; + boolT issharp = False; + int *quadrant, k; + + quadrant= (int*)qh_memalloc(qh hull_dim * sizeof(int)); + FORALLfacet_(qh newfacet_list) { + if (facet == qh newfacet_list) { + for (k=qh hull_dim; k--; ) + quadrant[ k]= (facet->normal[ k] > 0); + }else { + for (k=qh hull_dim; k--; ) { + if (quadrant[ k] != (facet->normal[ k] > 0)) { + issharp= True; + break; + } + } + } + if (issharp) + break; + } + qh_memfree( quadrant, qh hull_dim * sizeof(int)); + trace3((qh ferr, 3001, "qh_sharpnewfacets: %d\n", issharp)); + return issharp; +} /* sharpnewfacets */ + +/*--------------------------------- + + qh_voronoi_center( dim, points ) + return Voronoi center for a set of points + dim is the orginal dimension of the points + gh.gm_matrix/qh.gm_row are scratch buffers + + returns: + center as a temporary point + if non-simplicial, + returns center for max simplex of points + + notes: + from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65 + + design: + if non-simplicial + determine max simplex for points + translate point0 of simplex to origin + compute sum of squares of diagonal + compute determinate + compute Voronoi center (see Bowyer & Woodwark) +*/ +pointT *qh_voronoi_center(int dim, setT *points) { + pointT *point, **pointp, *point0; + pointT *center= (pointT*)qh_memalloc(qh center_size); + setT *simplex; + int i, j, k, size= qh_setsize(points); + coordT *gmcoord; + realT *diffp, sum2, *sum2row, *sum2p, det, factor; + boolT nearzero, infinite; + + if (size == dim+1) + simplex= points; + else if (size < dim+1) { + qh_fprintf(qh ferr, 6025, "qhull internal error (qh_voronoi_center):\n need at least %d points to construct a Voronoi center\n", + dim+1); + qh_errexit(qh_ERRqhull, NULL, NULL); + simplex= points; /* never executed -- avoids warning */ + }else { + simplex= qh_settemp(dim+1); + qh_maxsimplex(dim, points, NULL, 0, &simplex); + } + point0= SETfirstt_(simplex, pointT); + gmcoord= qh gm_matrix; + for (k=0; k < dim; k++) { + qh gm_row[k]= gmcoord; + FOREACHpoint_(simplex) { + if (point != point0) + *(gmcoord++)= point[k] - point0[k]; + } + } + sum2row= gmcoord; + for (i=0; i < dim; i++) { + sum2= 0.0; + for (k=0; k < dim; k++) { + diffp= qh gm_row[k] + i; + sum2 += *diffp * *diffp; + } + *(gmcoord++)= sum2; + } + det= qh_determinant(qh gm_row, dim, &nearzero); + factor= qh_divzero(0.5, det, qh MINdenom, &infinite); + if (infinite) { + for (k=dim; k--; ) + center[k]= qh_INFINITE; + if (qh IStracing) + qh_printpoints(qh ferr, "qh_voronoi_center: at infinity for ", simplex); + }else { + for (i=0; i < dim; i++) { + gmcoord= qh gm_matrix; + sum2p= sum2row; + for (k=0; k < dim; k++) { + qh gm_row[k]= gmcoord; + if (k == i) { + for (j=dim; j--; ) + *(gmcoord++)= *sum2p++; + }else { + FOREACHpoint_(simplex) { + if (point != point0) + *(gmcoord++)= point[k] - point0[k]; + } + } + } + center[i]= qh_determinant(qh gm_row, dim, &nearzero)*factor + point0[i]; + } +#ifndef qh_NOtrace + if (qh IStracing >= 3) { + qh_fprintf(qh ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor); + qh_printmatrix(qh ferr, "center:", ¢er, 1, dim); + if (qh IStracing >= 5) { + qh_printpoints(qh ferr, "points", simplex); + FOREACHpoint_(simplex) + qh_fprintf(qh ferr, 8034, "p%d dist %.2g, ", qh_pointid(point), + qh_pointdist(point, center, dim)); + qh_fprintf(qh ferr, 8035, "\n"); + } + } +#endif + } + if (simplex != points) + qh_settempfree(&simplex); + return center; +} /* voronoi_center */ diff --git a/extern/qhull/global.c b/extern/qhull/global.c new file mode 100644 index 000000000000..90f956d7e885 --- /dev/null +++ b/extern/qhull/global.c @@ -0,0 +1,2126 @@ + +/* --------------------------------- + + global.c + initializes all the globals of the qhull application + + see README + + see libqhull.h for qh.globals and function prototypes + + see qhull_a.h for internal functions + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/global.c#15 $$Change: 1490 $ + $DateTime: 2012/02/19 20:27:01 $$Author: bbarber $ + */ + +#include "qhull_a.h" + +/*========= qh definition -- globals defined in libqhull.h =======================*/ + +int qhull_inuse= 0; /* not used */ + +#if qh_QHpointer +qhT *qh_qh= NULL; /* pointer to all global variables */ +#else +qhT qh_qh; /* all global variables. + Add "= {0}" if this causes a compiler error. + Also qh_qhstat in stat.c and qhmem in mem.c. */ +#endif + +/*---------------------------------- + + qh_version + version string by year and date + + the revision increases on code changes only + + notes: + change date: Changes.txt, Announce.txt, index.htm, README.txt, + qhull-news.html, Eudora signatures, CMakeLists.txt + change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt + change year: Copying.txt + check download size + recompile user_eg.c, rbox.c, libqhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c, testqset.c +*/ + +const char *qh_version = "2012.1 2012/02/18"; + +/*--------------------------------- + + qh_appendprint( printFormat ) + append printFormat to qh.PRINTout unless already defined +*/ +void qh_appendprint(qh_PRINT format) { + int i; + + for (i=0; i < qh_PRINTEND; i++) { + if (qh PRINTout[i] == format && format != qh_PRINTqhull) + break; + if (!qh PRINTout[i]) { + qh PRINTout[i]= format; + break; + } + } +} /* appendprint */ + +/*--------------------------------- + + qh_checkflags( commandStr, hiddenFlags ) + errors if commandStr contains hiddenFlags + hiddenFlags starts and ends with a space and is space deliminated (checked) + + notes: + ignores first word (e.g., "qconvex i") + use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces + + see: + qh_initflags() initializes Qhull according to commandStr +*/ +void qh_checkflags(char *command, char *hiddenflags) { + char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */ + char key, opt, prevopt; + char chkkey[]= " "; + char chkopt[]= " "; + char chkopt2[]= " "; + boolT waserr= False; + + if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') { + qh_fprintf(qh ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (strpbrk(hiddenflags, ",\n\r\t")) { + qh_fprintf(qh ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags); + qh_errexit(qh_ERRinput, NULL, NULL); + } + while (*s && !isspace(*s)) /* skip program name */ + s++; + while (*s) { + while (*s && isspace(*s)) + s++; + if (*s == '-') + s++; + if (!*s) + break; + key = *s++; + chkerr = NULL; + if (key == 'T' && (*s == 'I' || *s == 'O')) { /* TI or TO 'file name' */ + s= qh_skipfilename(++s); + continue; + } + chkkey[1]= key; + if (strstr(hiddenflags, chkkey)) { + chkerr= chkkey; + }else if (isupper(key)) { + opt= ' '; + prevopt= ' '; + chkopt[1]= key; + chkopt2[1]= key; + while (!chkerr && *s && !isspace(*s)) { + opt= *s++; + if (isalpha(opt)) { + chkopt[2]= opt; + if (strstr(hiddenflags, chkopt)) + chkerr= chkopt; + if (prevopt != ' ') { + chkopt2[2]= prevopt; + chkopt2[3]= opt; + if (strstr(hiddenflags, chkopt2)) + chkerr= chkopt2; + } + }else if (key == 'Q' && isdigit(opt) && prevopt != 'b' + && (prevopt == ' ' || islower(prevopt))) { + chkopt[2]= opt; + if (strstr(hiddenflags, chkopt)) + chkerr= chkopt; + }else { + qh_strtod(s-1, &t); + if (s < t) + s= t; + } + prevopt= opt; + } + } + if (chkerr) { + *chkerr= '\''; + chkerr[strlen(chkerr)-1]= '\''; + qh_fprintf(qh ferr, 6029, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr); + waserr= True; + } + } + if (waserr) + qh_errexit(qh_ERRinput, NULL, NULL); +} /* checkflags */ + +/*--------------------------------- + + qh_clear_outputflags() + Clear output flags for QhullPoints +*/ +void qh_clear_outputflags(void) { + int i,k; + + qh ANNOTATEoutput= False; + qh DOintersections= False; + qh DROPdim= -1; + qh FORCEoutput= False; + qh GETarea= False; + qh GOODpoint= 0; + qh GOODpointp= NULL; + qh GOODthreshold= False; + qh GOODvertex= 0; + qh GOODvertexp= NULL; + qh IStracing= 0; + qh KEEParea= False; + qh KEEPmerge= False; + qh KEEPminArea= REALmax; + qh PRINTcentrums= False; + qh PRINTcoplanar= False; + qh PRINTdots= False; + qh PRINTgood= False; + qh PRINTinner= False; + qh PRINTneighbors= False; + qh PRINTnoplanes= False; + qh PRINToptions1st= False; + qh PRINTouter= False; + qh PRINTprecision= True; + qh PRINTridges= False; + qh PRINTspheres= False; + qh PRINTstatistics= False; + qh PRINTsummary= False; + qh PRINTtransparent= False; + qh SPLITthresholds= False; + qh TRACElevel= 0; + qh TRInormals= False; + qh USEstdout= False; + qh VERIFYoutput= False; + for (k=qh input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_ouputflags */ + qh lower_threshold[k]= -REALmax; + qh upper_threshold[k]= REALmax; + qh lower_bound[k]= -REALmax; + qh upper_bound[k]= REALmax; + } + + for (i=0; i < qh_PRINTEND; i++) { + qh PRINTout[i]= qh_PRINTnone; + } + + if (!qh qhull_commandsiz2) + qh qhull_commandsiz2= (int)strlen(qh qhull_command); /* WARN64 */ + else { + qh qhull_command[qh qhull_commandsiz2]= '\0'; + } + if (!qh qhull_optionsiz2) + qh qhull_optionsiz2= (int)strlen(qh qhull_options); /* WARN64 */ + else { + qh qhull_options[qh qhull_optionsiz2]= '\0'; + qh qhull_optionlen= qh_OPTIONline; /* start a new line */ + } +} /* clear_outputflags */ + +/*--------------------------------- + + qh_clock() + return user CPU time in 100ths (qh_SECtick) + only defined for qh_CLOCKtype == 2 + + notes: + use first value to determine time 0 + from Stevens '92 8.15 +*/ +unsigned long qh_clock(void) { + +#if (qh_CLOCKtype == 2) + struct tms time; + static long clktck; /* initialized first call */ + double ratio, cpu; + unsigned long ticks; + + if (!clktck) { + if ((clktck= sysconf(_SC_CLK_TCK)) < 0) { + qh_fprintf(qh ferr, 6030, "qhull internal error (qh_clock): sysconf() failed. Use qh_CLOCKtype 1 in user.h\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + } + if (times(&time) == -1) { + qh_fprintf(qh ferr, 6031, "qhull internal error (qh_clock): times() failed. Use qh_CLOCKtype 1 in user.h\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + ratio= qh_SECticks / (double)clktck; + ticks= time.tms_utime * ratio; + return ticks; +#else + qh_fprintf(qh ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); /* never returns */ + return 0; +#endif +} /* clock */ + +/*--------------------------------- + + qh_freebuffers() + free up global memory buffers + + notes: + must match qh_initbuffers() +*/ +void qh_freebuffers(void) { + + trace5((qh ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n")); + /* allocated by qh_initqhull_buffers */ + qh_memfree(qh NEARzero, qh hull_dim * sizeof(realT)); + qh_memfree(qh lower_threshold, (qh input_dim+1) * sizeof(realT)); + qh_memfree(qh upper_threshold, (qh input_dim+1) * sizeof(realT)); + qh_memfree(qh lower_bound, (qh input_dim+1) * sizeof(realT)); + qh_memfree(qh upper_bound, (qh input_dim+1) * sizeof(realT)); + qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * sizeof(coordT)); + qh_memfree(qh gm_row, (qh hull_dim+1) * sizeof(coordT *)); + qh NEARzero= qh lower_threshold= qh upper_threshold= NULL; + qh lower_bound= qh upper_bound= NULL; + qh gm_matrix= NULL; + qh gm_row= NULL; + qh_setfree(&qh other_points); + qh_setfree(&qh del_vertices); + qh_setfree(&qh coplanarfacetset); + if (qh line) /* allocated by qh_readinput, freed if no error */ + qh_free(qh line); + if (qh half_space) + qh_free(qh half_space); + if (qh temp_malloc) + qh_free(qh temp_malloc); + if (qh feasible_point) /* allocated by qh_readfeasible */ + qh_free(qh feasible_point); + if (qh feasible_string) /* allocated by qh_initflags */ + qh_free(qh feasible_string); + qh line= qh feasible_string= NULL; + qh half_space= qh feasible_point= qh temp_malloc= NULL; + /* usually allocated by qh_readinput */ + if (qh first_point && qh POINTSmalloc) { + qh_free(qh first_point); + qh first_point= NULL; + } + if (qh input_points && qh input_malloc) { /* set by qh_joggleinput */ + qh_free(qh input_points); + qh input_points= NULL; + } + trace5((qh ferr, 5002, "qh_freebuffers: finished\n")); +} /* freebuffers */ + + +/*--------------------------------- + + qh_freebuild( allmem ) + free global memory used by qh_initbuild and qh_buildhull + if !allmem, + does not free short memory (e.g., facetT, freed by qh_memfreeshort) + + design: + free centrums + free each vertex + mark unattached ridges + for each facet + free ridges + free outside set, coplanar set, neighbor set, ridge set, vertex set + free facet + free hash table + free interior point + free merge set + free temporary sets +*/ +void qh_freebuild(boolT allmem) { + facetT *facet; + vertexT *vertex; + ridgeT *ridge, **ridgep; + mergeT *merge, **mergep; + + trace1((qh ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n")); + if (qh del_vertices) + qh_settruncate(qh del_vertices, 0); + if (allmem) { + while ((vertex= qh vertex_list)) { + if (vertex->next) + qh_delvertex(vertex); + else { + qh_memfree(vertex, (int)sizeof(vertexT)); + qh newvertex_list= qh vertex_list= NULL; + } + } + }else if (qh VERTEXneighbors) { + FORALLvertices + qh_setfreelong(&(vertex->neighbors)); + } + qh VERTEXneighbors= False; + qh GOODclosest= NULL; + if (allmem) { + FORALLfacets { + FOREACHridge_(facet->ridges) + ridge->seen= False; + } + FORALLfacets { + if (facet->visible) { + FOREACHridge_(facet->ridges) { + if (!otherfacet_(ridge, facet)->visible) + ridge->seen= True; /* an unattached ridge */ + } + } + } + while ((facet= qh facet_list)) { + FOREACHridge_(facet->ridges) { + if (ridge->seen) { + qh_setfree(&(ridge->vertices)); + qh_memfree(ridge, (int)sizeof(ridgeT)); + }else + ridge->seen= True; + } + qh_setfree(&(facet->outsideset)); + qh_setfree(&(facet->coplanarset)); + qh_setfree(&(facet->neighbors)); + qh_setfree(&(facet->ridges)); + qh_setfree(&(facet->vertices)); + if (facet->next) + qh_delfacet(facet); + else { + qh_memfree(facet, (int)sizeof(facetT)); + qh visible_list= qh newfacet_list= qh facet_list= NULL; + } + } + }else { + FORALLfacets { + qh_setfreelong(&(facet->outsideset)); + qh_setfreelong(&(facet->coplanarset)); + if (!facet->simplicial) { + qh_setfreelong(&(facet->neighbors)); + qh_setfreelong(&(facet->ridges)); + qh_setfreelong(&(facet->vertices)); + } + } + } + qh_setfree(&(qh hash_table)); + qh_memfree(qh interior_point, qh normal_size); + qh interior_point= NULL; + FOREACHmerge_(qh facet_mergeset) /* usually empty */ + qh_memfree(merge, (int)sizeof(mergeT)); + qh facet_mergeset= NULL; /* temp set */ + qh degen_mergeset= NULL; /* temp set */ + qh_settempfree_all(); +} /* freebuild */ + +/*--------------------------------- + + qh_freeqhull( allmem ) + see qh_freeqhull2 + if qh_QHpointer, frees qh_qh +*/ +void qh_freeqhull(boolT allmem) { + qh_freeqhull2(allmem); +#if qh_QHpointer + qh_free(qh_qh); + qh_qh= NULL; +#endif +} + +/*--------------------------------- + +qh_freeqhull2( allmem ) + free global memory + if !allmem, + does not free short memory (freed by qh_memfreeshort) + +notes: + sets qh.NOerrexit in case caller forgets to + +see: + see qh_initqhull_start2() + +design: + free global and temporary memory from qh_initbuild and qh_buildhull + free buffers + free statistics +*/ +void qh_freeqhull2(boolT allmem) { + + trace1((qh ferr, 1006, "qh_freeqhull2: free global memory\n")); + qh NOerrexit= True; /* no more setjmp since called at exit and ~QhullQh */ + qh_freebuild(allmem); + qh_freebuffers(); + qh_freestatistics(); +#if qh_QHpointer + memset((char *)qh_qh, 0, sizeof(qhT)); + /* qh_qh freed by caller, qh_freeqhull() */ +#else + memset((char *)&qh_qh, 0, sizeof(qhT)); +#endif + qh NOerrexit= True; +} /* freeqhull2 */ + +/*--------------------------------- + + qh_init_A( infile, outfile, errfile, argc, argv ) + initialize memory and stdio files + convert input options to option string (qh.qhull_command) + + notes: + infile may be NULL if qh_readpoints() is not called + + errfile should always be defined. It is used for reporting + errors. outfile is used for output and format options. + + argc/argv may be 0/NULL + + called before error handling initialized + qh_errexit() may not be used +*/ +void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) { + qh_meminit(errfile); + qh_initqhull_start(infile, outfile, errfile); + qh_init_qhull_command(argc, argv); +} /* init_A */ + +/*--------------------------------- + + qh_init_B( points, numpoints, dim, ismalloc ) + initialize globals for points array + + points has numpoints dim-dimensional points + points[0] is the first coordinate of the first point + points[1] is the second coordinate of the first point + points[dim] is the first coordinate of the second point + + ismalloc=True + Qhull will call qh_free(points) on exit or input transformation + ismalloc=False + Qhull will allocate a new point array if needed for input transformation + + qh.qhull_command + is the option string. + It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags + + returns: + if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay) + projects the input to a new point array + + if qh.DELAUNAY, + qh.hull_dim is increased by one + if qh.ATinfinity, + qh_projectinput adds point-at-infinity for Delaunay tri. + + if qh.SCALEinput + changes the upper and lower bounds of the input, see qh_scaleinput() + + if qh.ROTATEinput + rotates the input by a random rotation, see qh_rotateinput() + if qh.DELAUNAY + rotates about the last coordinate + + notes: + called after points are defined + qh_errexit() may be used +*/ +void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc) { + qh_initqhull_globals(points, numpoints, dim, ismalloc); + if (qhmem.LASTsize == 0) + qh_initqhull_mem(); + /* mem.c and qset.c are initialized */ + qh_initqhull_buffers(); + qh_initthresholds(qh qhull_command); + if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) + qh_projectinput(); + if (qh SCALEinput) + qh_scaleinput(); + if (qh ROTATErandom >= 0) { + qh_randommatrix(qh gm_matrix, qh hull_dim, qh gm_row); + if (qh DELAUNAY) { + int k, lastk= qh hull_dim-1; + for (k=0; k < lastk; k++) { + qh gm_row[k][lastk]= 0.0; + qh gm_row[lastk][k]= 0.0; + } + qh gm_row[lastk][lastk]= 1.0; + } + qh_gram_schmidt(qh hull_dim, qh gm_row); + qh_rotateinput(qh gm_row); + } +} /* init_B */ + +/*--------------------------------- + + qh_init_qhull_command( argc, argv ) + build qh.qhull_command from argc/argv + + returns: + a space-delimited string of options (just as typed) + + notes: + makes option string easy to input and output + + argc/argv may be 0/NULL +*/ +void qh_init_qhull_command(int argc, char *argv[]) { + + if (!qh_argv_to_command(argc, argv, qh qhull_command, (int)sizeof(qh qhull_command))){ + /* Assumes qh.ferr is defined. */ + qh_fprintf(qh ferr, 6033, "qhull input error: more than %d characters in command line\n", + (int)sizeof(qh qhull_command)); + qh_exit(qh_ERRinput); /* error reported, can not use qh_errexit */ + } +} /* init_qhull_command */ + +/*--------------------------------- + + qh_initflags( commandStr ) + set flags and initialized constants from commandStr + + returns: + sets qh.qhull_command to command if needed + + notes: + ignores first word (e.g., "qhull d") + use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces + + see: + qh_initthresholds() continues processing of 'Pdn' and 'PDn' + 'prompt' in unix.c for documentation + + design: + for each space-deliminated option group + if top-level option + check syntax + append approriate option to option string + set appropriate global variable or append printFormat to print options + else + for each sub-option + check syntax + append approriate option to option string + set appropriate global variable or append printFormat to print options +*/ +void qh_initflags(char *command) { + int k, i, lastproject; + char *s= command, *t, *prev_s, *start, key; + boolT isgeom= False, wasproject; + realT r; + + if (command <= &qh qhull_command[0] || command > &qh qhull_command[0] + sizeof(qh qhull_command)) { + if (command != &qh qhull_command[0]) { + *qh qhull_command= '\0'; + strncat(qh qhull_command, command, sizeof(qh qhull_command)-strlen(qh qhull_command)-1); + } + while (*s && !isspace(*s)) /* skip program name */ + s++; + } + while (*s) { + while (*s && isspace(*s)) + s++; + if (*s == '-') + s++; + if (!*s) + break; + prev_s= s; + switch (*s++) { + case 'd': + qh_option("delaunay", NULL, NULL); + qh DELAUNAY= True; + break; + case 'f': + qh_option("facets", NULL, NULL); + qh_appendprint(qh_PRINTfacets); + break; + case 'i': + qh_option("incidence", NULL, NULL); + qh_appendprint(qh_PRINTincidences); + break; + case 'm': + qh_option("mathematica", NULL, NULL); + qh_appendprint(qh_PRINTmathematica); + break; + case 'n': + qh_option("normals", NULL, NULL); + qh_appendprint(qh_PRINTnormals); + break; + case 'o': + qh_option("offFile", NULL, NULL); + qh_appendprint(qh_PRINToff); + break; + case 'p': + qh_option("points", NULL, NULL); + qh_appendprint(qh_PRINTpoints); + break; + case 's': + qh_option("summary", NULL, NULL); + qh PRINTsummary= True; + break; + case 'v': + qh_option("voronoi", NULL, NULL); + qh VORONOI= True; + qh DELAUNAY= True; + break; + case 'A': + if (!isdigit(*s) && *s != '.' && *s != '-') + qh_fprintf(qh ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'. Ignored.\n"); + else { + if (*s == '-') { + qh premerge_cos= -qh_strtod(s, &s); + qh_option("Angle-premerge-", NULL, &qh premerge_cos); + qh PREmerge= True; + }else { + qh postmerge_cos= qh_strtod(s, &s); + qh_option("Angle-postmerge", NULL, &qh postmerge_cos); + qh POSTmerge= True; + } + qh MERGING= True; + } + break; + case 'C': + if (!isdigit(*s) && *s != '.' && *s != '-') + qh_fprintf(qh ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'. Ignored.\n"); + else { + if (*s == '-') { + qh premerge_centrum= -qh_strtod(s, &s); + qh_option("Centrum-premerge-", NULL, &qh premerge_centrum); + qh PREmerge= True; + }else { + qh postmerge_centrum= qh_strtod(s, &s); + qh_option("Centrum-postmerge", NULL, &qh postmerge_centrum); + qh POSTmerge= True; + } + qh MERGING= True; + } + break; + case 'E': + if (*s == '-') + qh_fprintf(qh ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n"); + else if (!isdigit(*s)) + qh_fprintf(qh ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'. Ignored.\n"); + else { + qh DISTround= qh_strtod(s, &s); + qh_option("Distance-roundoff", NULL, &qh DISTround); + qh SETroundoff= True; + } + break; + case 'H': + start= s; + qh HALFspace= True; + qh_strtod(s, &t); + while (t > s) { + if (*t && !isspace(*t)) { + if (*t == ',') + t++; + else + qh_fprintf(qh ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n"); + } + s= t; + qh_strtod(s, &t); + } + if (start < t) { + if (!(qh feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) { + qh_fprintf(qh ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh_ERRmem, NULL, NULL); + } + strncpy(qh feasible_string, start, (size_t)(t-start)); + qh_option("Halfspace-about", NULL, NULL); + qh_option(qh feasible_string, NULL, NULL); + }else + qh_option("Halfspace", NULL, NULL); + break; + case 'R': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'. Ignored\n"); + else { + qh RANDOMfactor= qh_strtod(s, &s); + qh_option("Random_perturb", NULL, &qh RANDOMfactor); + qh RANDOMdist= True; + } + break; + case 'V': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh ferr, 7008, "qhull warning: missing visible distance for option 'Vn'. Ignored\n"); + else { + qh MINvisible= qh_strtod(s, &s); + qh_option("Visible", NULL, &qh MINvisible); + } + break; + case 'U': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'. Ignored\n"); + else { + qh MAXcoplanar= qh_strtod(s, &s); + qh_option("U-coplanar", NULL, &qh MAXcoplanar); + } + break; + case 'W': + if (*s == '-') + qh_fprintf(qh ferr, 7010, "qhull warning: negative outside width for option 'Wn'. Ignored.\n"); + else if (!isdigit(*s)) + qh_fprintf(qh ferr, 7011, "qhull warning: missing outside width for option 'Wn'. Ignored\n"); + else { + qh MINoutside= qh_strtod(s, &s); + qh_option("W-outside", NULL, &qh MINoutside); + qh APPROXhull= True; + } + break; + /************ sub menus ***************/ + case 'F': + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option("Farea", NULL, NULL); + qh_appendprint(qh_PRINTarea); + qh GETarea= True; + break; + case 'A': + qh_option("FArea-total", NULL, NULL); + qh GETarea= True; + break; + case 'c': + qh_option("Fcoplanars", NULL, NULL); + qh_appendprint(qh_PRINTcoplanars); + break; + case 'C': + qh_option("FCentrums", NULL, NULL); + qh_appendprint(qh_PRINTcentrums); + break; + case 'd': + qh_option("Fd-cdd-in", NULL, NULL); + qh CDDinput= True; + break; + case 'D': + qh_option("FD-cdd-out", NULL, NULL); + qh CDDoutput= True; + break; + case 'F': + qh_option("FFacets-xridge", NULL, NULL); + qh_appendprint(qh_PRINTfacets_xridge); + break; + case 'i': + qh_option("Finner", NULL, NULL); + qh_appendprint(qh_PRINTinner); + break; + case 'I': + qh_option("FIDs", NULL, NULL); + qh_appendprint(qh_PRINTids); + break; + case 'm': + qh_option("Fmerges", NULL, NULL); + qh_appendprint(qh_PRINTmerges); + break; + case 'M': + qh_option("FMaple", NULL, NULL); + qh_appendprint(qh_PRINTmaple); + break; + case 'n': + qh_option("Fneighbors", NULL, NULL); + qh_appendprint(qh_PRINTneighbors); + break; + case 'N': + qh_option("FNeighbors-vertex", NULL, NULL); + qh_appendprint(qh_PRINTvneighbors); + break; + case 'o': + qh_option("Fouter", NULL, NULL); + qh_appendprint(qh_PRINTouter); + break; + case 'O': + if (qh PRINToptions1st) { + qh_option("FOptions", NULL, NULL); + qh_appendprint(qh_PRINToptions); + }else + qh PRINToptions1st= True; + break; + case 'p': + qh_option("Fpoint-intersect", NULL, NULL); + qh_appendprint(qh_PRINTpointintersect); + break; + case 'P': + qh_option("FPoint-nearest", NULL, NULL); + qh_appendprint(qh_PRINTpointnearest); + break; + case 'Q': + qh_option("FQhull", NULL, NULL); + qh_appendprint(qh_PRINTqhull); + break; + case 's': + qh_option("Fsummary", NULL, NULL); + qh_appendprint(qh_PRINTsummary); + break; + case 'S': + qh_option("FSize", NULL, NULL); + qh_appendprint(qh_PRINTsize); + qh GETarea= True; + break; + case 't': + qh_option("Ftriangles", NULL, NULL); + qh_appendprint(qh_PRINTtriangles); + break; + case 'v': + /* option set in qh_initqhull_globals */ + qh_appendprint(qh_PRINTvertices); + break; + case 'V': + qh_option("FVertex-average", NULL, NULL); + qh_appendprint(qh_PRINTaverage); + break; + case 'x': + qh_option("Fxtremes", NULL, NULL); + qh_appendprint(qh_PRINTextremes); + break; + default: + s--; + qh_fprintf(qh ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'G': + isgeom= True; + qh_appendprint(qh_PRINTgeom); + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option("Gall-points", NULL, NULL); + qh PRINTdots= True; + break; + case 'c': + qh_option("Gcentrums", NULL, NULL); + qh PRINTcentrums= True; + break; + case 'h': + qh_option("Gintersections", NULL, NULL); + qh DOintersections= True; + break; + case 'i': + qh_option("Ginner", NULL, NULL); + qh PRINTinner= True; + break; + case 'n': + qh_option("Gno-planes", NULL, NULL); + qh PRINTnoplanes= True; + break; + case 'o': + qh_option("Gouter", NULL, NULL); + qh PRINTouter= True; + break; + case 'p': + qh_option("Gpoints", NULL, NULL); + qh PRINTcoplanar= True; + break; + case 'r': + qh_option("Gridges", NULL, NULL); + qh PRINTridges= True; + break; + case 't': + qh_option("Gtransparent", NULL, NULL); + qh PRINTtransparent= True; + break; + case 'v': + qh_option("Gvertices", NULL, NULL); + qh PRINTspheres= True; + break; + case 'D': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n"); + else { + if (qh DROPdim >= 0) + qh_fprintf(qh ferr, 7013, "qhull warning: can only drop one dimension. Previous 'GD%d' ignored\n", + qh DROPdim); + qh DROPdim= qh_strtol(s, &s); + qh_option("GDrop-dim", &qh DROPdim, NULL); + } + break; + default: + s--; + qh_fprintf(qh ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'P': + while (*s && !isspace(*s)) { + switch (*s++) { + case 'd': case 'D': /* see qh_initthresholds() */ + key= s[-1]; + i= qh_strtol(s, &s); + r= 0; + if (*s == ':') { + s++; + r= qh_strtod(s, &s); + } + if (key == 'd') + qh_option("Pdrop-facets-dim-less", &i, &r); + else + qh_option("PDrop-facets-dim-more", &i, &r); + break; + case 'g': + qh_option("Pgood-facets", NULL, NULL); + qh PRINTgood= True; + break; + case 'G': + qh_option("PGood-facet-neighbors", NULL, NULL); + qh PRINTneighbors= True; + break; + case 'o': + qh_option("Poutput-forced", NULL, NULL); + qh FORCEoutput= True; + break; + case 'p': + qh_option("Pprecision-ignore", NULL, NULL); + qh PRINTprecision= False; + break; + case 'A': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n"); + else { + qh KEEParea= qh_strtol(s, &s); + qh_option("PArea-keep", &qh KEEParea, NULL); + qh GETarea= True; + } + break; + case 'F': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n"); + else { + qh KEEPminArea= qh_strtod(s, &s); + qh_option("PFacet-area-keep", NULL, &qh KEEPminArea); + qh GETarea= True; + } + break; + case 'M': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n"); + else { + qh KEEPmerge= qh_strtol(s, &s); + qh_option("PMerge-keep", &qh KEEPmerge, NULL); + } + break; + default: + s--; + qh_fprintf(qh ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'Q': + lastproject= -1; + while (*s && !isspace(*s)) { + switch (*s++) { + case 'b': case 'B': /* handled by qh_initthresholds */ + key= s[-1]; + if (key == 'b' && *s == 'B') { + s++; + r= qh_DEFAULTbox; + qh SCALEinput= True; + qh_option("QbBound-unit-box", NULL, &r); + break; + } + if (key == 'b' && *s == 'b') { + s++; + qh SCALElast= True; + qh_option("Qbbound-last", NULL, NULL); + break; + } + k= qh_strtol(s, &s); + r= 0.0; + wasproject= False; + if (*s == ':') { + s++; + if ((r= qh_strtod(s, &s)) == 0.0) { + t= s; /* need true dimension for memory allocation */ + while (*t && !isspace(*t)) { + if (toupper(*t++) == 'B' + && k == qh_strtol(t, &t) + && *t++ == ':' + && qh_strtod(t, &t) == 0.0) { + qh PROJECTinput++; + trace2((qh ferr, 2004, "qh_initflags: project dimension %d\n", k)); + qh_option("Qb-project-dim", &k, NULL); + wasproject= True; + lastproject= k; + break; + } + } + } + } + if (!wasproject) { + if (lastproject == k && r == 0.0) + lastproject= -1; /* doesn't catch all possible sequences */ + else if (key == 'b') { + qh SCALEinput= True; + if (r == 0.0) + r= -qh_DEFAULTbox; + qh_option("Qbound-dim-low", &k, &r); + }else { + qh SCALEinput= True; + if (r == 0.0) + r= qh_DEFAULTbox; + qh_option("QBound-dim-high", &k, &r); + } + } + break; + case 'c': + qh_option("Qcoplanar-keep", NULL, NULL); + qh KEEPcoplanar= True; + break; + case 'f': + qh_option("Qfurthest-outside", NULL, NULL); + qh BESToutside= True; + break; + case 'g': + qh_option("Qgood-facets-only", NULL, NULL); + qh ONLYgood= True; + break; + case 'i': + qh_option("Qinterior-keep", NULL, NULL); + qh KEEPinside= True; + break; + case 'm': + qh_option("Qmax-outside-only", NULL, NULL); + qh ONLYmax= True; + break; + case 'r': + qh_option("Qrandom-outside", NULL, NULL); + qh RANDOMoutside= True; + break; + case 's': + qh_option("Qsearch-initial-simplex", NULL, NULL); + qh ALLpoints= True; + break; + case 't': + qh_option("Qtriangulate", NULL, NULL); + qh TRIangulate= True; + break; + case 'T': + qh_option("QTestPoints", NULL, NULL); + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n"); + else { + qh TESTpoints= qh_strtol(s, &s); + qh_option("QTestPoints", &qh TESTpoints, NULL); + } + break; + case 'u': + qh_option("QupperDelaunay", NULL, NULL); + qh UPPERdelaunay= True; + break; + case 'v': + qh_option("Qvertex-neighbors-convex", NULL, NULL); + qh TESTvneighbors= True; + break; + case 'x': + qh_option("Qxact-merge", NULL, NULL); + qh MERGEexact= True; + break; + case 'z': + qh_option("Qz-infinity-point", NULL, NULL); + qh ATinfinity= True; + break; + case '0': + qh_option("Q0-no-premerge", NULL, NULL); + qh NOpremerge= True; + break; + case '1': + if (!isdigit(*s)) { + qh_option("Q1-no-angle-sort", NULL, NULL); + qh ANGLEmerge= False; + break; + } + switch (*s++) { + case '0': + qh_option("Q10-no-narrow", NULL, NULL); + qh NOnarrow= True; + break; + case '1': + qh_option("Q11-trinormals Qtriangulate", NULL, NULL); + qh TRInormals= True; + qh TRIangulate= True; + break; + default: + s--; + qh_fprintf(qh ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + break; + case '2': + qh_option("Q2-no-merge-independent", NULL, NULL); + qh MERGEindependent= False; + goto LABELcheckdigit; + break; /* no warnings */ + case '3': + qh_option("Q3-no-merge-vertices", NULL, NULL); + qh MERGEvertices= False; + LABELcheckdigit: + if (isdigit(*s)) + qh_fprintf(qh ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit. '%c' skipped.\n", + *s++); + break; + case '4': + qh_option("Q4-avoid-old-into-new", NULL, NULL); + qh AVOIDold= True; + break; + case '5': + qh_option("Q5-no-check-outer", NULL, NULL); + qh SKIPcheckmax= True; + break; + case '6': + qh_option("Q6-no-concave-merge", NULL, NULL); + qh SKIPconvex= True; + break; + case '7': + qh_option("Q7-no-breadth-first", NULL, NULL); + qh VIRTUALmemory= True; + break; + case '8': + qh_option("Q8-no-near-inside", NULL, NULL); + qh NOnearinside= True; + break; + case '9': + qh_option("Q9-pick-furthest", NULL, NULL); + qh PICKfurthest= True; + break; + case 'G': + i= qh_strtol(s, &t); + if (qh GOODpoint) + qh_fprintf(qh ferr, 7018, "qhull warning: good point already defined for option 'QGn'. Ignored\n"); + else if (s == t) + qh_fprintf(qh ferr, 7019, "qhull warning: missing good point id for option 'QGn'. Ignored\n"); + else if (i < 0 || *s == '-') { + qh GOODpoint= i-1; + qh_option("QGood-if-dont-see-point", &i, NULL); + }else { + qh GOODpoint= i+1; + qh_option("QGood-if-see-point", &i, NULL); + } + s= t; + break; + case 'J': + if (!isdigit(*s) && *s != '-') + qh JOGGLEmax= 0.0; + else { + qh JOGGLEmax= (realT) qh_strtod(s, &s); + qh_option("QJoggle", NULL, &qh JOGGLEmax); + } + break; + case 'R': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh ferr, 7020, "qhull warning: missing random seed for option 'QRn'. Ignored\n"); + else { + qh ROTATErandom= i= qh_strtol(s, &s); + if (i > 0) + qh_option("QRotate-id", &i, NULL ); + else if (i < -1) + qh_option("QRandom-seed", &i, NULL ); + } + break; + case 'V': + i= qh_strtol(s, &t); + if (qh GOODvertex) + qh_fprintf(qh ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n"); + else if (s == t) + qh_fprintf(qh ferr, 7022, "qhull warning: no good point id given for option 'QVn'. Ignored\n"); + else if (i < 0) { + qh GOODvertex= i - 1; + qh_option("QV-good-facets-not-point", &i, NULL); + }else { + qh_option("QV-good-facets-point", &i, NULL); + qh GOODvertex= i + 1; + } + s= t; + break; + default: + s--; + qh_fprintf(qh ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'T': + while (*s && !isspace(*s)) { + if (isdigit(*s) || *s == '-') + qh IStracing= qh_strtol(s, &s); + else switch (*s++) { + case 'a': + qh_option("Tannotate-output", NULL, NULL); + qh ANNOTATEoutput= True; + break; + case 'c': + qh_option("Tcheck-frequently", NULL, NULL); + qh CHECKfrequently= True; + break; + case 's': + qh_option("Tstatistics", NULL, NULL); + qh PRINTstatistics= True; + break; + case 'v': + qh_option("Tverify", NULL, NULL); + qh VERIFYoutput= True; + break; + case 'z': + if (qh ferr == qh_FILEstderr) { + /* The C++ interface captures the output in qh_fprint_qhull() */ + qh_option("Tz-stdout", NULL, NULL); + qh USEstdout= True; + }else if (!qh fout) + qh_fprintf(qh ferr, 7024, "qhull warning: output file undefined(stdout). Option 'Tz' ignored.\n"); + else { + qh_option("Tz-stdout", NULL, NULL); + qh USEstdout= True; + qh ferr= qh fout; + qhmem.ferr= qh fout; + } + break; + case 'C': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'. Ignored\n"); + else { + i= qh_strtol(s, &s); + qh_option("TCone-stop", &i, NULL); + qh STOPcone= i + 1; + } + break; + case 'F': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'. Ignored\n"); + else { + qh REPORTfreq= qh_strtol(s, &s); + qh_option("TFacet-log", &qh REPORTfreq, NULL); + qh REPORTfreq2= qh REPORTfreq/2; /* for tracemerging() */ + } + break; + case 'I': + if (!isspace(*s)) + qh_fprintf(qh ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s); + while (isspace(*s)) + s++; + t= qh_skipfilename(s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + s= t; + if (!freopen(filename, "r", stdin)) { + qh_fprintf(qh ferr, 6041, "qhull error: could not open file \"%s\".", filename); + qh_errexit(qh_ERRinput, NULL, NULL); + }else { + qh_option("TInput-file", NULL, NULL); + qh_option(filename, NULL, NULL); + } + } + break; + case 'O': + if (!isspace(*s)) + qh_fprintf(qh ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s); + while (isspace(*s)) + s++; + t= qh_skipfilename(s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + s= t; + if (!freopen(filename, "w", stdout)) { + qh_fprintf(qh ferr, 6044, "qhull error: could not open file \"%s\".", filename); + qh_errexit(qh_ERRinput, NULL, NULL); + }else { + qh_option("TOutput-file", NULL, NULL); + qh_option(filename, NULL, NULL); + } + } + break; + case 'P': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7029, "qhull warning: missing point id for trace option 'TPn'. Ignored\n"); + else { + qh TRACEpoint= qh_strtol(s, &s); + qh_option("Trace-point", &qh TRACEpoint, NULL); + } + break; + case 'M': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'. Ignored\n"); + else { + qh TRACEmerge= qh_strtol(s, &s); + qh_option("Trace-merge", &qh TRACEmerge, NULL); + } + break; + case 'R': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'. Ignored\n"); + else { + qh RERUN= qh_strtol(s, &s); + qh_option("TRerun", &qh RERUN, NULL); + } + break; + case 'V': + i= qh_strtol(s, &t); + if (s == t) + qh_fprintf(qh ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'. Ignored\n"); + else if (i < 0) { + qh STOPpoint= i - 1; + qh_option("TV-stop-before-point", &i, NULL); + }else { + qh STOPpoint= i + 1; + qh_option("TV-stop-after-point", &i, NULL); + } + s= t; + break; + case 'W': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7033, "qhull warning: missing max width for trace option 'TWn'. Ignored\n"); + else { + qh TRACEdist= (realT) qh_strtod(s, &s); + qh_option("TWide-trace", NULL, &qh TRACEdist); + } + break; + default: + s--; + qh_fprintf(qh ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + default: + qh_fprintf(qh ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1], + (int)s[-1]); + break; + } + if (s-1 == prev_s && *s && !isspace(*s)) { + qh_fprintf(qh ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n", + (int)*prev_s, (int)*prev_s); + while (*s && !isspace(*s)) + s++; + } + } + if (qh STOPcone && qh JOGGLEmax < REALmax/2) + qh_fprintf(qh ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n"); + if (isgeom && !qh FORCEoutput && qh PRINTout[1]) + qh_fprintf(qh ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n"); + /* set derived values in qh_initqhull_globals */ +} /* initflags */ + + +/*--------------------------------- + + qh_initqhull_buffers() + initialize global memory buffers + + notes: + must match qh_freebuffers() +*/ +void qh_initqhull_buffers(void) { + int k; + + qh TEMPsize= (qhmem.LASTsize - sizeof(setT))/SETelemsize; + if (qh TEMPsize <= 0 || qh TEMPsize > qhmem.LASTsize) + qh TEMPsize= 8; /* e.g., if qh_NOmem */ + qh other_points= qh_setnew(qh TEMPsize); + qh del_vertices= qh_setnew(qh TEMPsize); + qh coplanarfacetset= qh_setnew(qh TEMPsize); + qh NEARzero= (realT *)qh_memalloc(qh hull_dim * sizeof(realT)); + qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + qh upper_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + for (k=qh input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_ouputflags */ + qh lower_threshold[k]= -REALmax; + qh upper_threshold[k]= REALmax; + qh lower_bound[k]= -REALmax; + qh upper_bound[k]= REALmax; + } + qh gm_matrix= (coordT *)qh_memalloc((qh hull_dim+1) * qh hull_dim * sizeof(coordT)); + qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * sizeof(coordT *)); +} /* initqhull_buffers */ + +/*--------------------------------- + + qh_initqhull_globals( points, numpoints, dim, ismalloc ) + initialize globals + if ismalloc + points were malloc'd and qhull should free at end + + returns: + sets qh.first_point, num_points, input_dim, hull_dim and others + seeds random number generator (seed=1 if tracing) + modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput) + adjust user flags as needed + also checks DIM3 dependencies and constants + + notes: + do not use qh_point() since an input transformation may move them elsewhere + + see: + qh_initqhull_start() sets default values for non-zero globals + + design: + initialize points array from input arguments + test for qh.ZEROcentrum + (i.e., use opposite vertex instead of cetrum for convexity testing) + initialize qh.CENTERtype, qh.normal_size, + qh.center_size, qh.TRACEpoint/level, + initialize and test random numbers + qh_initqhull_outputflags() -- adjust and test output flags +*/ +void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc) { + int seed, pointsneeded, extra= 0, i, randi, k; + realT randr; + realT factorial; + + time_t timedata; + + trace0((qh ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh rbox_command, + qh qhull_command)); + qh POINTSmalloc= ismalloc; + qh first_point= points; + qh num_points= numpoints; + qh hull_dim= qh input_dim= dim; + if (!qh NOpremerge && !qh MERGEexact && !qh PREmerge && qh JOGGLEmax > REALmax/2) { + qh MERGING= True; + if (qh hull_dim <= 4) { + qh PREmerge= True; + qh_option("_pre-merge", NULL, NULL); + }else { + qh MERGEexact= True; + qh_option("Qxact_merge", NULL, NULL); + } + }else if (qh MERGEexact) + qh MERGING= True; + if (!qh NOpremerge && qh JOGGLEmax > REALmax/2) { +#ifdef qh_NOmerge + qh JOGGLEmax= 0.0; +#endif + } + if (qh TRIangulate && qh JOGGLEmax < REALmax/2 && qh PRINTprecision) + qh_fprintf(qh ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output. Triangulated output('Qt') does nothing.\n"); + if (qh JOGGLEmax < REALmax/2 && qh DELAUNAY && !qh SCALEinput && !qh SCALElast) { + qh SCALElast= True; + qh_option("Qbbound-last-qj", NULL, NULL); + } + if (qh MERGING && !qh POSTmerge && qh premerge_cos > REALmax/2 + && qh premerge_centrum == 0) { + qh ZEROcentrum= True; + qh ZEROall_ok= True; + qh_option("_zero-centrum", NULL, NULL); + } + if (qh JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh PRINTprecision) + qh_fprintf(qh ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n", + REALepsilon); +#ifdef qh_NOmerge + if (qh MERGING) { + qh_fprintf(qh ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } +#endif + if (qh DELAUNAY && qh KEEPcoplanar && !qh KEEPinside) { + qh KEEPinside= True; + qh_option("Qinterior-keep", NULL, NULL); + } + if (qh DELAUNAY && qh HALFspace) { + qh_fprintf(qh ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (!qh DELAUNAY && (qh UPPERdelaunay || qh ATinfinity)) { + qh_fprintf(qh ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh UPPERdelaunay && qh ATinfinity) { + qh_fprintf(qh ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh SCALElast && !qh DELAUNAY && qh PRINTprecision) + qh_fprintf(qh ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n"); + qh DOcheckmax= (!qh SKIPcheckmax && qh MERGING ); + qh KEEPnearinside= (qh DOcheckmax && !(qh KEEPinside && qh KEEPcoplanar) + && !qh NOnearinside); + if (qh MERGING) + qh CENTERtype= qh_AScentrum; + else if (qh VORONOI) + qh CENTERtype= qh_ASvoronoi; + if (qh TESTvneighbors && !qh MERGING) { + qh_fprintf(qh ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n"); + qh_errexit(qh_ERRinput, NULL ,NULL); + } + if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) { + qh hull_dim -= qh PROJECTinput; + if (qh DELAUNAY) { + qh hull_dim++; + if (qh ATinfinity) + extra= 1; + } + } + if (qh hull_dim <= 1) { + qh_fprintf(qh ferr, 6050, "qhull error: dimension %d must be > 1\n", qh hull_dim); + qh_errexit(qh_ERRinput, NULL, NULL); + } + for (k=2, factorial=1.0; k < qh hull_dim; k++) + factorial *= k; + qh AREAfactor= 1.0 / factorial; + trace2((qh ferr, 2005, "qh_initqhull_globals: initialize globals. dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n", + dim, numpoints, ismalloc, qh PROJECTinput, qh hull_dim)); + qh normal_size= qh hull_dim * sizeof(coordT); + qh center_size= qh normal_size - sizeof(coordT); + pointsneeded= qh hull_dim+1; + if (qh hull_dim > qh_DIMmergeVertex) { + qh MERGEvertices= False; + qh_option("Q3-no-merge-vertices-dim-high", NULL, NULL); + } + if (qh GOODpoint) + pointsneeded++; +#ifdef qh_NOtrace + if (qh IStracing) { + qh_fprintf(qh ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +#endif + if (qh RERUN > 1) { + qh TRACElastrun= qh IStracing; /* qh_build_withrestart duplicates next conditional */ + if (qh IStracing != -1) + qh IStracing= 0; + }else if (qh TRACEpoint != -1 || qh TRACEdist < REALmax/2 || qh TRACEmerge) { + qh TRACElevel= (qh IStracing? qh IStracing : 3); + qh IStracing= 0; + } + if (qh ROTATErandom == 0 || qh ROTATErandom == -1) { + seed= (int)time(&timedata); + if (qh ROTATErandom == -1) { + seed= -seed; + qh_option("QRandom-seed", &seed, NULL ); + }else + qh_option("QRotate-random", &seed, NULL); + qh ROTATErandom= seed; + } + seed= qh ROTATErandom; + if (seed == INT_MIN) /* default value */ + seed= 1; + else if (seed < 0) + seed= -seed; + qh_RANDOMseed_(seed); + randr= 0.0; + for (i=1000; i--; ) { + randi= qh_RANDOMint; + randr += randi; + if (randi > qh_RANDOMmax) { + qh_fprintf(qh ferr, 8036, "\ +qhull configuration error (qh_RANDOMmax in user.h):\n\ + random integer %d > qh_RANDOMmax(%.8g)\n", + randi, qh_RANDOMmax); + qh_errexit(qh_ERRinput, NULL, NULL); + } + } + qh_RANDOMseed_(seed); + randr = randr/1000; + if (randr < qh_RANDOMmax * 0.1 + || randr > qh_RANDOMmax * 0.9) + qh_fprintf(qh ferr, 8037, "\ +qhull configuration warning (qh_RANDOMmax in user.h):\n\ + average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\ + Is qh_RANDOMmax (%.2g) wrong?\n", + randr, qh_RANDOMmax * 0.5, qh_RANDOMmax); + qh RANDOMa= 2.0 * qh RANDOMfactor/qh_RANDOMmax; + qh RANDOMb= 1.0 - qh RANDOMfactor; + if (qh_HASHfactor < 1.1) { + qh_fprintf(qh ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1. Qhull uses linear hash probing\n", + qh_HASHfactor); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (numpoints+extra < pointsneeded) { + qh_fprintf(qh ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n", + numpoints, pointsneeded); + qh_errexit(qh_ERRinput, NULL, NULL); + } + qh_initqhull_outputflags(); +} /* initqhull_globals */ + +/*--------------------------------- + + qh_initqhull_mem( ) + initialize mem.c for qhull + qh.hull_dim and qh.normal_size determine some of the allocation sizes + if qh.MERGING, + includes ridgeT + calls qh_user_memsizes() to add up to 10 additional sizes for quick allocation + (see numsizes below) + + returns: + mem.c already for qh_memalloc/qh_memfree (errors if called beforehand) + + notes: + qh_produceoutput() prints memsizes + +*/ +void qh_initqhull_mem(void) { + int numsizes; + int i; + + numsizes= 8+10; + qh_meminitbuffers(qh IStracing, qh_MEMalign, numsizes, + qh_MEMbufsize,qh_MEMinitbuf); + qh_memsize((int)sizeof(vertexT)); + if (qh MERGING) { + qh_memsize((int)sizeof(ridgeT)); + qh_memsize((int)sizeof(mergeT)); + } + qh_memsize((int)sizeof(facetT)); + i= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; /* ridge.vertices */ + qh_memsize(i); + qh_memsize(qh normal_size); /* normal */ + i += SETelemsize; /* facet.vertices, .ridges, .neighbors */ + qh_memsize(i); + qh_user_memsizes(); + qh_memsetup(); +} /* initqhull_mem */ + +/*--------------------------------- + + qh_initqhull_outputflags + initialize flags concerned with output + + returns: + adjust user flags as needed + + see: + qh_clear_outputflags() resets the flags + + design: + test for qh.PRINTgood (i.e., only print 'good' facets) + check for conflicting print output options +*/ +void qh_initqhull_outputflags(void) { + boolT printgeom= False, printmath= False, printcoplanar= False; + int i; + + trace3((qh ferr, 3024, "qh_initqhull_outputflags: %s\n", qh qhull_command)); + if (!(qh PRINTgood || qh PRINTneighbors)) { + if (qh KEEParea || qh KEEPminArea < REALmax/2 || qh KEEPmerge || qh DELAUNAY + || (!qh ONLYgood && (qh GOODvertex || qh GOODpoint))) { + qh PRINTgood= True; + qh_option("Pgood", NULL, NULL); + } + } + if (qh PRINTtransparent) { + if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) { + qh_fprintf(qh ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + qh DROPdim = 3; + qh PRINTridges = True; + } + for (i=qh_PRINTEND; i--; ) { + if (qh PRINTout[i] == qh_PRINTgeom) + printgeom= True; + else if (qh PRINTout[i] == qh_PRINTmathematica || qh PRINTout[i] == qh_PRINTmaple) + printmath= True; + else if (qh PRINTout[i] == qh_PRINTcoplanars) + printcoplanar= True; + else if (qh PRINTout[i] == qh_PRINTpointnearest) + printcoplanar= True; + else if (qh PRINTout[i] == qh_PRINTpointintersect && !qh HALFspace) { + qh_fprintf(qh ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + }else if (qh PRINTout[i] == qh_PRINTtriangles && (qh HALFspace || qh VORONOI)) { + qh_fprintf(qh ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + }else if (qh PRINTout[i] == qh_PRINTcentrums && qh VORONOI) { + qh_fprintf(qh ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + }else if (qh PRINTout[i] == qh_PRINTvertices) { + if (qh VORONOI) + qh_option("Fvoronoi", NULL, NULL); + else + qh_option("Fvertices", NULL, NULL); + } + } + if (printcoplanar && qh DELAUNAY && qh JOGGLEmax < REALmax/2) { + if (qh PRINTprecision) + qh_fprintf(qh ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n"); + } + if (printmath && (qh hull_dim > 3 || qh VORONOI)) { + qh_fprintf(qh ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (printgeom) { + if (qh hull_dim > 4) { + qh_fprintf(qh ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh PRINTnoplanes && !(qh PRINTcoplanar + qh PRINTcentrums + + qh PRINTdots + qh PRINTspheres + qh DOintersections + qh PRINTridges)) { + qh_fprintf(qh ferr, 6058, "qhull input error: no output specified for Geomview\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh VORONOI && (qh hull_dim > 3 || qh DROPdim >= 0)) { + qh_fprintf(qh ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + /* can not warn about furthest-site Geomview output: no lower_threshold */ + if (qh hull_dim == 4 && qh DROPdim == -1 && + (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) { + qh_fprintf(qh ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\ +available for 4-d output(ignored). Could use 'GDn' instead.\n"); + qh PRINTcoplanar= qh PRINTspheres= qh PRINTcentrums= False; + } + } + if (!qh KEEPcoplanar && !qh KEEPinside && !qh ONLYgood) { + if ((qh PRINTcoplanar && qh PRINTspheres) || printcoplanar) { + if (qh QHULLfinished) { + qh_fprintf(qh ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n"); + }else { + qh KEEPcoplanar = True; + qh_option("Qcoplanar", NULL, NULL); + } + } + } + qh PRINTdim= qh hull_dim; + if (qh DROPdim >=0) { /* after Geomview checks */ + if (qh DROPdim < qh hull_dim) { + qh PRINTdim--; + if (!printgeom || qh hull_dim < 3) + qh_fprintf(qh ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh DROPdim); + }else + qh DROPdim= -1; + }else if (qh VORONOI) { + qh DROPdim= qh hull_dim-1; + qh PRINTdim= qh hull_dim-1; + } +} /* qh_initqhull_outputflags */ + +/*--------------------------------- + + qh_initqhull_start( infile, outfile, errfile ) + allocate memory if needed and call qh_initqhull_start2() +*/ +void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile) { + +#if qh_QHpointer + if (qh_qh) { + qh_fprintf(errfile, 6205, "qhull error (qh_initqhull_start): qh_qh already defined. Call qh_save_qhull() first\n"); + qh_exit(qh_ERRqhull); /* no error handler */ + } + if (!(qh_qh= (qhT *)qh_malloc(sizeof(qhT)))) { + qh_fprintf(errfile, 6060, "qhull error (qh_initqhull_start): insufficient memory\n"); + qh_exit(qh_ERRmem); /* no error handler */ + } +#endif + qh_initstatistics(); + qh_initqhull_start2(infile, outfile, errfile); +} /* initqhull_start */ + +/*--------------------------------- + + qh_initqhull_start2( infile, outfile, errfile ) + start initialization of qhull + initialize statistics, stdio, default values for global variables + assumes qh_qh is defined + notes: + report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized + see: + qh_maxmin() determines the precision constants + qh_freeqhull2() +*/ +void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) { + time_t timedata; + int seed; + + qh_CPUclock; /* start the clock(for qh_clock). One-shot. */ +#if qh_QHpointer + memset((char *)qh_qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */ +#else + memset((char *)&qh_qh, 0, sizeof(qhT)); +#endif + qh ANGLEmerge= True; + qh DROPdim= -1; + qh ferr= errfile; + qh fin= infile; + qh fout= outfile; + qh furthest_id= -1; + qh JOGGLEmax= REALmax; + qh KEEPminArea = REALmax; + qh last_low= REALmax; + qh last_high= REALmax; + qh last_newhigh= REALmax; + qh max_outside= 0.0; + qh max_vertex= 0.0; + qh MAXabs_coord= 0.0; + qh MAXsumcoord= 0.0; + qh MAXwidth= -REALmax; + qh MERGEindependent= True; + qh MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */ + qh MINoutside= 0.0; + qh MINvisible= REALmax; + qh MAXcoplanar= REALmax; + qh outside_err= REALmax; + qh premerge_centrum= 0.0; + qh premerge_cos= REALmax; + qh PRINTprecision= True; + qh PRINTradius= 0.0; + qh postmerge_cos= REALmax; + qh postmerge_centrum= 0.0; + qh ROTATErandom= INT_MIN; + qh MERGEvertices= True; + qh totarea= 0.0; + qh totvol= 0.0; + qh TRACEdist= REALmax; + qh TRACEpoint= -1; /* recompile or use 'TPn' */ + qh tracefacet_id= UINT_MAX; /* recompile to trace a facet */ + qh tracevertex_id= UINT_MAX; /* recompile to trace a vertex */ + seed= (int)time(&timedata); + qh_RANDOMseed_(seed); + qh run_id= qh_RANDOMint+1; /* disallow 0 [UsingLibQhull::NOqhRunId] */ + qh_option("run-id", &qh run_id, NULL); + strcat(qh qhull, "qhull"); +} /* initqhull_start2 */ + +/*--------------------------------- + + qh_initthresholds( commandString ) + set thresholds for printing and scaling from commandString + + returns: + sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used + + see: + qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk' + qh_inthresholds() + + design: + for each 'Pdn' or 'PDn' option + check syntax + set qh.lower_threshold or qh.upper_threshold + set qh.GOODthreshold if an unbounded threshold is used + set qh.SPLITthreshold if a bounded threshold is used +*/ +void qh_initthresholds(char *command) { + realT value; + int idx, maxdim, k; + char *s= command; /* non-const due to strtol */ + char key; + + maxdim= qh input_dim; + if (qh DELAUNAY && (qh PROJECTdelaunay || qh PROJECTinput)) + maxdim++; + while (*s) { + if (*s == '-') + s++; + if (*s == 'P') { + s++; + while (*s && !isspace(key= *s++)) { + if (key == 'd' || key == 'D') { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n", + key, s-1); + continue; + } + idx= qh_strtol(s, &s); + if (idx >= qh hull_dim) { + qh_fprintf(qh ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n", + idx, key, qh hull_dim); + continue; + } + if (*s == ':') { + s++; + value= qh_strtod(s, &s); + if (fabs((double)value) > 1.0) { + qh_fprintf(qh ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1. Ignored\n", + value, key); + continue; + } + }else + value= 0.0; + if (key == 'd') + qh lower_threshold[idx]= value; + else + qh upper_threshold[idx]= value; + } + } + }else if (*s == 'Q') { + s++; + while (*s && !isspace(key= *s++)) { + if (key == 'b' && *s == 'B') { + s++; + for (k=maxdim; k--; ) { + qh lower_bound[k]= -qh_DEFAULTbox; + qh upper_bound[k]= qh_DEFAULTbox; + } + }else if (key == 'b' && *s == 'b') + s++; + else if (key == 'b' || key == 'B') { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7047, "qhull warning: no dimension given for Qhull option %c. Ignored\n", + key); + continue; + } + idx= qh_strtol(s, &s); + if (idx >= maxdim) { + qh_fprintf(qh ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n", + idx, key, maxdim); + continue; + } + if (*s == ':') { + s++; + value= qh_strtod(s, &s); + }else if (key == 'b') + value= -qh_DEFAULTbox; + else + value= qh_DEFAULTbox; + if (key == 'b') + qh lower_bound[idx]= value; + else + qh upper_bound[idx]= value; + } + } + }else { + while (*s && !isspace(*s)) + s++; + } + while (isspace(*s)) + s++; + } + for (k=qh hull_dim; k--; ) { + if (qh lower_threshold[k] > -REALmax/2) { + qh GOODthreshold= True; + if (qh upper_threshold[k] < REALmax/2) { + qh SPLITthresholds= True; + qh GOODthreshold= False; + break; + } + }else if (qh upper_threshold[k] < REALmax/2) + qh GOODthreshold= True; + } +} /* initthresholds */ + +/*--------------------------------- + + qh_option( option, intVal, realVal ) + add an option description to qh.qhull_options + + notes: + NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2] + will be printed with statistics ('Ts') and errors + strlen(option) < 40 +*/ +void qh_option(const char *option, int *i, realT *r) { + char buf[200]; + int len, maxlen; + + sprintf(buf, " %s", option); + if (i) + sprintf(buf+strlen(buf), " %d", *i); + if (r) + sprintf(buf+strlen(buf), " %2.2g", *r); + len= (int)strlen(buf); /* WARN64 */ + qh qhull_optionlen += len; + maxlen= sizeof(qh qhull_options) - len -1; + maximize_(maxlen, 0); + if (qh qhull_optionlen >= qh_OPTIONline && maxlen > 0) { + qh qhull_optionlen= len; + strncat(qh qhull_options, "\n", (size_t)(maxlen--)); + } + strncat(qh qhull_options, buf, (size_t)maxlen); +} /* option */ + +#if qh_QHpointer +/*--------------------------------- + + qh_restore_qhull( oldqh ) + restores a previously saved qhull + also restores qh_qhstat and qhmem.tempstack + Sets *oldqh to NULL + notes: + errors if current qhull hasn't been saved or freed + uses qhmem for error reporting + + NOTE 1998/5/11: + Freeing memory after qh_save_qhull and qh_restore_qhull + is complicated. The procedures will be redesigned. + + see: + qh_save_qhull(), UsingLibQhull +*/ +void qh_restore_qhull(qhT **oldqh) { + + if (*oldqh && strcmp((*oldqh)->qhull, "qhull")) { + qh_fprintf(qhmem.ferr, 6061, "qhull internal error (qh_restore_qhull): %p is not a qhull data structure\n", + *oldqh); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (qh_qh) { + qh_fprintf(qhmem.ferr, 6062, "qhull internal error (qh_restore_qhull): did not save or free existing qhull\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (!*oldqh || !(*oldqh)->old_qhstat) { + qh_fprintf(qhmem.ferr, 6063, "qhull internal error (qh_restore_qhull): did not previously save qhull %p\n", + *oldqh); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_qh= *oldqh; + *oldqh= NULL; + qh_qhstat= qh old_qhstat; + qhmem.tempstack= qh old_tempstack; + qh old_qhstat= 0; + qh old_tempstack= 0; + trace1((qh ferr, 1007, "qh_restore_qhull: restored qhull from %p\n", *oldqh)); +} /* restore_qhull */ + +/*--------------------------------- + + qh_save_qhull( ) + saves qhull for a later qh_restore_qhull + also saves qh_qhstat and qhmem.tempstack + + returns: + qh_qh=NULL + + notes: + need to initialize qhull or call qh_restore_qhull before continuing + + NOTE 1998/5/11: + Freeing memory after qh_save_qhull and qh_restore_qhull + is complicated. The procedures will be redesigned. + + see: + qh_restore_qhull() +*/ +qhT *qh_save_qhull(void) { + qhT *oldqh; + + trace1((qhmem.ferr, 1045, "qh_save_qhull: save qhull %p\n", qh_qh)); + if (!qh_qh) { + qh_fprintf(qhmem.ferr, 6064, "qhull internal error (qh_save_qhull): qhull not initialized\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh old_qhstat= qh_qhstat; + qh_qhstat= NULL; + qh old_tempstack= qhmem.tempstack; + qhmem.tempstack= NULL; + oldqh= qh_qh; + qh_qh= NULL; + return oldqh; +} /* save_qhull */ + +#endif diff --git a/extern/qhull/io.c b/extern/qhull/io.c new file mode 100644 index 000000000000..092e3ef79a53 --- /dev/null +++ b/extern/qhull/io.c @@ -0,0 +1,4059 @@ +/*--------------------------------- + + io.c + Input/Output routines of qhull application + + see qh-io.htm and io.h + + see user.c for qh_errprint and qh_printfacetlist + + unix.c calls qh_readpoints and qh_produce_output + + unix.c and user.c are the only callers of io.c functions + This allows the user to avoid loading io.o from qhull.a + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/io.c#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*========= -functions in alphabetical order after qh_produce_output() =====*/ + +/*--------------------------------- + + qh_produce_output() + qh_produce_output2() + prints out the result of qhull in desired format + qh_produce_output2() does not call qh_prepare_output() + if qh.GETarea + computes and prints area and volume + qh.PRINTout[] is an array of output formats + + notes: + prints output in qh.PRINTout order +*/ +void qh_produce_output(void) { + int tempsize= qh_setsize(qhmem.tempstack); + + qh_prepare_output(); + qh_produce_output2(); + if (qh_setsize(qhmem.tempstack) != tempsize) { + qh_fprintf(qh ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n", + qh_setsize(qhmem.tempstack)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +} /* produce_output */ + + +void qh_produce_output2(void) { + int i, tempsize= qh_setsize(qhmem.tempstack), d_1; + + if (qh PRINTsummary) + qh_printsummary(qh ferr); + else if (qh PRINTout[0] == qh_PRINTnone) + qh_printsummary(qh fout); + for (i=0; i < qh_PRINTEND; i++) + qh_printfacets(qh fout, qh PRINTout[i], qh facet_list, NULL, !qh_ALL); + qh_allstatistics(); + if (qh PRINTprecision && !qh MERGING && (qh JOGGLEmax > REALmax/2 || qh RERUN)) + qh_printstats(qh ferr, qhstat precision, NULL); + if (qh VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0)) + qh_printstats(qh ferr, qhstat vridges, NULL); + if (qh PRINTstatistics) { + qh_printstatistics(qh ferr, ""); + qh_memstatistics(qh ferr); + d_1= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; + qh_fprintf(qh ferr, 8040, "\ + size in bytes: merge %d ridge %d vertex %d facet %d\n\ + normal %d ridge vertices %d facet vertices or neighbors %d\n", + (int)sizeof(mergeT), (int)sizeof(ridgeT), + (int)sizeof(vertexT), (int)sizeof(facetT), + qh normal_size, d_1, d_1 + SETelemsize); + } + if (qh_setsize(qhmem.tempstack) != tempsize) { + qh_fprintf(qh ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n", + qh_setsize(qhmem.tempstack)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +} /* produce_output2 */ + +/*--------------------------------- + + dfacet( id ) + print facet by id, for debugging + +*/ +void dfacet(unsigned id) { + facetT *facet; + + FORALLfacets { + if (facet->id == id) { + qh_printfacet(qh fout, facet); + break; + } + } +} /* dfacet */ + + +/*--------------------------------- + + dvertex( id ) + print vertex by id, for debugging +*/ +void dvertex(unsigned id) { + vertexT *vertex; + + FORALLvertices { + if (vertex->id == id) { + qh_printvertex(qh fout, vertex); + break; + } + } +} /* dvertex */ + + +/*--------------------------------- + + qh_compare_vertexpoint( p1, p2 ) + used by qsort() to order vertices by point id +*/ +int qh_compare_vertexpoint(const void *p1, const void *p2) { + const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); + + return((qh_pointid(a->point) > qh_pointid(b->point)?1:-1)); +} /* compare_vertexpoint */ + +/*--------------------------------- + + qh_compare_facetarea( p1, p2 ) + used by qsort() to order facets by area +*/ +int qh_compare_facetarea(const void *p1, const void *p2) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); + + if (!a->isarea) + return -1; + if (!b->isarea) + return 1; + if (a->f.area > b->f.area) + return 1; + else if (a->f.area == b->f.area) + return 0; + return -1; +} /* compare_facetarea */ + +/*--------------------------------- + + qh_compare_facetmerge( p1, p2 ) + used by qsort() to order facets by number of merges +*/ +int qh_compare_facetmerge(const void *p1, const void *p2) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); + + return(a->nummerge - b->nummerge); +} /* compare_facetvisit */ + +/*--------------------------------- + + qh_compare_facetvisit( p1, p2 ) + used by qsort() to order facets by visit id or id +*/ +int qh_compare_facetvisit(const void *p1, const void *p2) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); + int i,j; + + if (!(i= a->visitid)) + i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */ + if (!(j= b->visitid)) + j= 0 - b->id; + return(i - j); +} /* compare_facetvisit */ + +/*--------------------------------- + + qh_copyfilename( dest, size, source, length ) + copy filename identified by qh_skipfilename() + + notes: + see qh_skipfilename() for syntax +*/ +void qh_copyfilename(char *filename, int size, const char* source, int length) { + char c= *source; + + if (length > size + 1) { + qh_fprintf(qh ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source); + qh_errexit(qh_ERRinput, NULL, NULL); + } + strncpy(filename, source, length); + filename[length]= '\0'; + if (c == '\'' || c == '"') { + char *s= filename + 1; + char *t= filename; + while (*s) { + if (*s == c) { + if (s[-1] == '\\') + t[-1]= c; + }else + *t++= *s; + s++; + } + *t= '\0'; + } +} /* copyfilename */ + +/*--------------------------------- + + qh_countfacets( facetlist, facets, printall, + numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars ) + count good facets for printing and set visitid + if allfacets, ignores qh_skipfacet() + + notes: + qh_printsummary and qh_countfacets must match counts + + returns: + numfacets, numsimplicial, total neighbors, numridges, coplanars + each facet with ->visitid indicating 1-relative position + ->visitid==0 indicates not good + + notes + numfacets >= numsimplicial + if qh.NEWfacets, + does not count visible facets (matches qh_printafacet) + + design: + for all facets on facetlist and in facets set + unless facet is skipped or visible (i.e., will be deleted) + mark facet->visitid + update counts +*/ +void qh_countfacets(facetT *facetlist, setT *facets, boolT printall, + int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) { + facetT *facet, **facetp; + int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0; + + FORALLfacet_(facetlist) { + if ((facet->visible && qh NEWfacets) + || (!printall && qh_skipfacet(facet))) + facet->visitid= 0; + else { + facet->visitid= ++numfacets; + totneighbors += qh_setsize(facet->neighbors); + if (facet->simplicial) { + numsimplicial++; + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else + numridges += qh_setsize(facet->ridges); + if (facet->coplanarset) + numcoplanars += qh_setsize(facet->coplanarset); + } + } + + FOREACHfacet_(facets) { + if ((facet->visible && qh NEWfacets) + || (!printall && qh_skipfacet(facet))) + facet->visitid= 0; + else { + facet->visitid= ++numfacets; + totneighbors += qh_setsize(facet->neighbors); + if (facet->simplicial){ + numsimplicial++; + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else + numridges += qh_setsize(facet->ridges); + if (facet->coplanarset) + numcoplanars += qh_setsize(facet->coplanarset); + } + } + qh visit_id += numfacets+1; + *numfacetsp= numfacets; + *numsimplicialp= numsimplicial; + *totneighborsp= totneighbors; + *numridgesp= numridges; + *numcoplanarsp= numcoplanars; + *numtricoplanarsp= numtricoplanars; +} /* countfacets */ + +/*--------------------------------- + + qh_detvnorm( vertex, vertexA, centers, offset ) + compute separating plane of the Voronoi diagram for a pair of input sites + centers= set of facets (i.e., Voronoi vertices) + facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded) + + assumes: + qh_ASvoronoi and qh_vertexneighbors() already set + + returns: + norm + a pointer into qh.gm_matrix to qh.hull_dim-1 reals + copy the data before reusing qh.gm_matrix + offset + if 'QVn' + sign adjusted so that qh.GOODvertexp is inside + else + sign adjusted so that vertex is inside + + qh.gm_matrix= simplex of points from centers relative to first center + + notes: + in io.c so that code for 'v Tv' can be removed by removing io.c + returns pointer into qh.gm_matrix to avoid tracking of temporary memory + + design: + determine midpoint of input sites + build points as the set of Voronoi vertices + select a simplex from points (if necessary) + include midpoint if the Voronoi region is unbounded + relocate the first vertex of the simplex to the origin + compute the normalized hyperplane through the simplex + orient the hyperplane toward 'QVn' or 'vertex' + if 'Tv' or 'Ts' + if bounded + test that hyperplane is the perpendicular bisector of the input sites + test that Voronoi vertices not in the simplex are still on the hyperplane + free up temporary memory +*/ +pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) { + facetT *facet, **facetp; + int i, k, pointid, pointidA, point_i, point_n; + setT *simplex= NULL; + pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint; + coordT *coord, *gmcoord, *normalp; + setT *points= qh_settemp(qh TEMPsize); + boolT nearzero= False; + boolT unbounded= False; + int numcenters= 0; + int dim= qh hull_dim - 1; + realT dist, offset, angle, zero= 0.0; + + midpoint= qh gm_matrix + qh hull_dim * qh hull_dim; /* last row */ + for (k=0; k < dim; k++) + midpoint[k]= (vertex->point[k] + vertexA->point[k])/2; + FOREACHfacet_(centers) { + numcenters++; + if (!facet->visitid) + unbounded= True; + else { + if (!facet->center) + facet->center= qh_facetcenter(facet->vertices); + qh_setappend(&points, facet->center); + } + } + if (numcenters > dim) { + simplex= qh_settemp(qh TEMPsize); + qh_setappend(&simplex, vertex->point); + if (unbounded) + qh_setappend(&simplex, midpoint); + qh_maxsimplex(dim, points, NULL, 0, &simplex); + qh_setdelnth(simplex, 0); + }else if (numcenters == dim) { + if (unbounded) + qh_setappend(&points, midpoint); + simplex= points; + }else { + qh_fprintf(qh ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + i= 0; + gmcoord= qh gm_matrix; + point0= SETfirstt_(simplex, pointT); + FOREACHpoint_(simplex) { + if (qh IStracing >= 4) + qh_printmatrix(qh ferr, "qh_detvnorm: Voronoi vertex or midpoint", + &point, 1, dim); + if (point != point0) { + qh gm_row[i++]= gmcoord; + coord= point0; + for (k=dim; k--; ) + *(gmcoord++)= *point++ - *coord++; + } + } + qh gm_row[i]= gmcoord; /* does not overlap midpoint, may be used later for qh_areasimplex */ + normal= gmcoord; + qh_sethyperplane_gauss(dim, qh gm_row, point0, True, + normal, &offset, &nearzero); + if (qh GOODvertexp == vertexA->point) + inpoint= vertexA->point; + else + inpoint= vertex->point; + zinc_(Zdistio); + dist= qh_distnorm(dim, inpoint, normal, &offset); + if (dist > 0) { + offset= -offset; + normalp= normal; + for (k=dim; k--; ) { + *normalp= -(*normalp); + normalp++; + } + } + if (qh VERIFYoutput || qh PRINTstatistics) { + pointid= qh_pointid(vertex->point); + pointidA= qh_pointid(vertexA->point); + if (!unbounded) { + zinc_(Zdiststat); + dist= qh_distnorm(dim, midpoint, normal, &offset); + if (dist < 0) + dist= -dist; + zzinc_(Zridgemid); + wwmax_(Wridgemidmax, dist); + wwadd_(Wridgemid, dist); + trace4((qh ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n", + pointid, pointidA, dist)); + for (k=0; k < dim; k++) + midpoint[k]= vertexA->point[k] - vertex->point[k]; /* overwrites midpoint! */ + qh_normalize(midpoint, dim, False); + angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */ + if (angle < 0.0) + angle= angle + 1.0; + else + angle= angle - 1.0; + if (angle < 0.0) + angle -= angle; + trace4((qh ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n", + pointid, pointidA, angle, nearzero)); + if (nearzero) { + zzinc_(Zridge0); + wwmax_(Wridge0max, angle); + wwadd_(Wridge0, angle); + }else { + zzinc_(Zridgeok) + wwmax_(Wridgeokmax, angle); + wwadd_(Wridgeok, angle); + } + } + if (simplex != points) { + FOREACHpoint_i_(points) { + if (!qh_setin(simplex, point)) { + facet= SETelemt_(centers, point_i, facetT); + zinc_(Zdiststat); + dist= qh_distnorm(dim, point, normal, &offset); + if (dist < 0) + dist= -dist; + zzinc_(Zridge); + wwmax_(Wridgemax, dist); + wwadd_(Wridge, dist); + trace4((qh ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n", + pointid, pointidA, facet->visitid, dist)); + } + } + } + } + *offsetp= offset; + if (simplex != points) + qh_settempfree(&simplex); + qh_settempfree(&points); + return normal; +} /* detvnorm */ + +/*--------------------------------- + + qh_detvridge( vertexA ) + determine Voronoi ridge from 'seen' neighbors of vertexA + include one vertex-at-infinite if an !neighbor->visitid + + returns: + temporary set of centers (facets, i.e., Voronoi vertices) + sorted by center id +*/ +setT *qh_detvridge(vertexT *vertex) { + setT *centers= qh_settemp(qh TEMPsize); + setT *tricenters= qh_settemp(qh TEMPsize); + facetT *neighbor, **neighborp; + boolT firstinf= True; + + FOREACHneighbor_(vertex) { + if (neighbor->seen) { + if (neighbor->visitid) { + if (!neighbor->tricoplanar || qh_setunique(&tricenters, neighbor->center)) + qh_setappend(¢ers, neighbor); + }else if (firstinf) { + firstinf= False; + qh_setappend(¢ers, neighbor); + } + } + } + qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(centers), + sizeof(facetT *), qh_compare_facetvisit); + qh_settempfree(&tricenters); + return centers; +} /* detvridge */ + +/*--------------------------------- + + qh_detvridge3( atvertex, vertex ) + determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex + include one vertex-at-infinite for !neighbor->visitid + assumes all facet->seen2= True + + returns: + temporary set of centers (facets, i.e., Voronoi vertices) + listed in adjacency order (!oriented) + all facet->seen2= True + + design: + mark all neighbors of atvertex + for each adjacent neighbor of both atvertex and vertex + if neighbor selected + add neighbor to set of Voronoi vertices +*/ +setT *qh_detvridge3 (vertexT *atvertex, vertexT *vertex) { + setT *centers= qh_settemp(qh TEMPsize); + setT *tricenters= qh_settemp(qh TEMPsize); + facetT *neighbor, **neighborp, *facet= NULL; + boolT firstinf= True; + + FOREACHneighbor_(atvertex) + neighbor->seen2= False; + FOREACHneighbor_(vertex) { + if (!neighbor->seen2) { + facet= neighbor; + break; + } + } + while (facet) { + facet->seen2= True; + if (neighbor->seen) { + if (facet->visitid) { + if (!facet->tricoplanar || qh_setunique(&tricenters, facet->center)) + qh_setappend(¢ers, facet); + }else if (firstinf) { + firstinf= False; + qh_setappend(¢ers, facet); + } + } + FOREACHneighbor_(facet) { + if (!neighbor->seen2) { + if (qh_setin(vertex->neighbors, neighbor)) + break; + else + neighbor->seen2= True; + } + } + facet= neighbor; + } + if (qh CHECKfrequently) { + FOREACHneighbor_(vertex) { + if (!neighbor->seen2) { + qh_fprintf(qh ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n", + qh_pointid(vertex->point), neighbor->id); + qh_errexit(qh_ERRqhull, neighbor, NULL); + } + } + } + FOREACHneighbor_(atvertex) + neighbor->seen2= True; + qh_settempfree(&tricenters); + return centers; +} /* detvridge3 */ + +/*--------------------------------- + + qh_eachvoronoi( fp, printvridge, vertex, visitall, innerouter, inorder ) + if visitall, + visit all Voronoi ridges for vertex (i.e., an input site) + else + visit all unvisited Voronoi ridges for vertex + all vertex->seen= False if unvisited + assumes + all facet->seen= False + all facet->seen2= True (for qh_detvridge3) + all facet->visitid == 0 if vertex_at_infinity + == index of Voronoi vertex + >= qh.num_facets if ignored + innerouter: + qh_RIDGEall-- both inner (bounded) and outer(unbounded) ridges + qh_RIDGEinner- only inner + qh_RIDGEouter- only outer + + if inorder + orders vertices for 3-d Voronoi diagrams + + returns: + number of visited ridges (does not include previously visited ridges) + + if printvridge, + calls printvridge( fp, vertex, vertexA, centers) + fp== any pointer (assumes FILE*) + vertex,vertexA= pair of input sites that define a Voronoi ridge + centers= set of facets (i.e., Voronoi vertices) + ->visitid == index or 0 if vertex_at_infinity + ordered for 3-d Voronoi diagram + notes: + uses qh.vertex_visit + + see: + qh_eachvoronoi_all() + + design: + mark selected neighbors of atvertex + for each selected neighbor (either Voronoi vertex or vertex-at-infinity) + for each unvisited vertex + if atvertex and vertex share more than d-1 neighbors + bump totalcount + if printvridge defined + build the set of shared neighbors (i.e., Voronoi vertices) + call printvridge +*/ +int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) { + boolT unbounded; + int count; + facetT *neighbor, **neighborp, *neighborA, **neighborAp; + setT *centers; + setT *tricenters= qh_settemp(qh TEMPsize); + + vertexT *vertex, **vertexp; + boolT firstinf; + unsigned int numfacets= (unsigned int)qh num_facets; + int totridges= 0; + + qh vertex_visit++; + atvertex->seen= True; + if (visitall) { + FORALLvertices + vertex->seen= False; + } + FOREACHneighbor_(atvertex) { + if (neighbor->visitid < numfacets) + neighbor->seen= True; + } + FOREACHneighbor_(atvertex) { + if (neighbor->seen) { + FOREACHvertex_(neighbor->vertices) { + if (vertex->visitid != qh vertex_visit && !vertex->seen) { + vertex->visitid= qh vertex_visit; + count= 0; + firstinf= True; + qh_settruncate(tricenters, 0); + FOREACHneighborA_(vertex) { + if (neighborA->seen) { + if (neighborA->visitid) { + if (!neighborA->tricoplanar || qh_setunique(&tricenters, neighborA->center)) + count++; + }else if (firstinf) { + count++; + firstinf= False; + } + } + } + if (count >= qh hull_dim - 1) { /* e.g., 3 for 3-d Voronoi */ + if (firstinf) { + if (innerouter == qh_RIDGEouter) + continue; + unbounded= False; + }else { + if (innerouter == qh_RIDGEinner) + continue; + unbounded= True; + } + totridges++; + trace4((qh ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n", + count, qh_pointid(atvertex->point), qh_pointid(vertex->point))); + if (printvridge && fp) { + if (inorder && qh hull_dim == 3+1) /* 3-d Voronoi diagram */ + centers= qh_detvridge3 (atvertex, vertex); + else + centers= qh_detvridge(vertex); + (*printvridge) (fp, atvertex, vertex, centers, unbounded); + qh_settempfree(¢ers); + } + } + } + } + } + } + FOREACHneighbor_(atvertex) + neighbor->seen= False; + qh_settempfree(&tricenters); + return totridges; +} /* eachvoronoi */ + + +/*--------------------------------- + + qh_eachvoronoi_all( fp, printvridge, isUpper, innerouter, inorder ) + visit all Voronoi ridges + + innerouter: + see qh_eachvoronoi() + + if inorder + orders vertices for 3-d Voronoi diagrams + + returns + total number of ridges + + if isUpper == facet->upperdelaunay (i.e., a Vornoi vertex) + facet->visitid= Voronoi vertex index(same as 'o' format) + else + facet->visitid= 0 + + if printvridge, + calls printvridge( fp, vertex, vertexA, centers) + [see qh_eachvoronoi] + + notes: + Not used for qhull.exe + same effect as qh_printvdiagram but ridges not sorted by point id +*/ +int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) { + facetT *facet; + vertexT *vertex; + int numcenters= 1; /* vertex 0 is vertex-at-infinity */ + int totridges= 0; + + qh_clearcenters(qh_ASvoronoi); + qh_vertexneighbors(); + maximize_(qh visit_id, (unsigned) qh num_facets); + FORALLfacets { + facet->visitid= 0; + facet->seen= False; + facet->seen2= True; + } + FORALLfacets { + if (facet->upperdelaunay == isUpper) + facet->visitid= numcenters++; + } + FORALLvertices + vertex->seen= False; + FORALLvertices { + if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex) + continue; + totridges += qh_eachvoronoi(fp, printvridge, vertex, + !qh_ALL, innerouter, inorder); + } + return totridges; +} /* eachvoronoi_all */ + +/*--------------------------------- + + qh_facet2point( facet, point0, point1, mindist ) + return two projected temporary vertices for a 2-d facet + may be non-simplicial + + returns: + point0 and point1 oriented and projected to the facet + returns mindist (maximum distance below plane) +*/ +void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist) { + vertexT *vertex0, *vertex1; + realT dist; + + if (facet->toporient ^ qh_ORIENTclock) { + vertex0= SETfirstt_(facet->vertices, vertexT); + vertex1= SETsecondt_(facet->vertices, vertexT); + }else { + vertex1= SETfirstt_(facet->vertices, vertexT); + vertex0= SETsecondt_(facet->vertices, vertexT); + } + zadd_(Zdistio, 2); + qh_distplane(vertex0->point, facet, &dist); + *mindist= dist; + *point0= qh_projectpoint(vertex0->point, facet, dist); + qh_distplane(vertex1->point, facet, &dist); + minimize_(*mindist, dist); + *point1= qh_projectpoint(vertex1->point, facet, dist); +} /* facet2point */ + + +/*--------------------------------- + + qh_facetvertices( facetlist, facets, allfacets ) + returns temporary set of vertices in a set and/or list of facets + if allfacets, ignores qh_skipfacet() + + returns: + vertices with qh.vertex_visit + + notes: + optimized for allfacets of facet_list + + design: + if allfacets of facet_list + create vertex set from vertex_list + else + for each selected facet in facets or facetlist + append unvisited vertices to vertex set +*/ +setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets) { + setT *vertices; + facetT *facet, **facetp; + vertexT *vertex, **vertexp; + + qh vertex_visit++; + if (facetlist == qh facet_list && allfacets && !facets) { + vertices= qh_settemp(qh num_vertices); + FORALLvertices { + vertex->visitid= qh vertex_visit; + qh_setappend(&vertices, vertex); + } + }else { + vertices= qh_settemp(qh TEMPsize); + FORALLfacet_(facetlist) { + if (!allfacets && qh_skipfacet(facet)) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + qh_setappend(&vertices, vertex); + } + } + } + } + FOREACHfacet_(facets) { + if (!allfacets && qh_skipfacet(facet)) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + qh_setappend(&vertices, vertex); + } + } + } + return vertices; +} /* facetvertices */ + +/*--------------------------------- + + qh_geomplanes( facet, outerplane, innerplane ) + return outer and inner planes for Geomview + qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax) + + notes: + assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon +*/ +void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane) { + realT radius; + + if (qh MERGING || qh JOGGLEmax < REALmax/2) { + qh_outerinner(facet, outerplane, innerplane); + radius= qh PRINTradius; + if (qh JOGGLEmax < REALmax/2) + radius -= qh JOGGLEmax * sqrt((realT)qh hull_dim); /* already accounted for in qh_outerinner() */ + *outerplane += radius; + *innerplane -= radius; + if (qh PRINTcoplanar || qh PRINTspheres) { + *outerplane += qh MAXabs_coord * qh_GEOMepsilon; + *innerplane -= qh MAXabs_coord * qh_GEOMepsilon; + } + }else + *innerplane= *outerplane= 0; +} /* geomplanes */ + + +/*--------------------------------- + + qh_markkeep( facetlist ) + mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea + ignores visible facets (!part of convex hull) + + returns: + may clear facet->good + recomputes qh.num_good + + design: + get set of good facets + if qh.KEEParea + sort facets by area + clear facet->good for all but n largest facets + if qh.KEEPmerge + sort facets by merge count + clear facet->good for all but n most merged facets + if qh.KEEPminarea + clear facet->good if area too small + update qh.num_good +*/ +void qh_markkeep(facetT *facetlist) { + facetT *facet, **facetp; + setT *facets= qh_settemp(qh num_facets); + int size, count; + + trace2((qh ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n", + qh KEEParea, qh KEEPmerge, qh KEEPminArea)); + FORALLfacet_(facetlist) { + if (!facet->visible && facet->good) + qh_setappend(&facets, facet); + } + size= qh_setsize(facets); + if (qh KEEParea) { + qsort(SETaddr_(facets, facetT), (size_t)size, + sizeof(facetT *), qh_compare_facetarea); + if ((count= size - qh KEEParea) > 0) { + FOREACHfacet_(facets) { + facet->good= False; + if (--count == 0) + break; + } + } + } + if (qh KEEPmerge) { + qsort(SETaddr_(facets, facetT), (size_t)size, + sizeof(facetT *), qh_compare_facetmerge); + if ((count= size - qh KEEPmerge) > 0) { + FOREACHfacet_(facets) { + facet->good= False; + if (--count == 0) + break; + } + } + } + if (qh KEEPminArea < REALmax/2) { + FOREACHfacet_(facets) { + if (!facet->isarea || facet->f.area < qh KEEPminArea) + facet->good= False; + } + } + qh_settempfree(&facets); + count= 0; + FORALLfacet_(facetlist) { + if (facet->good) + count++; + } + qh num_good= count; +} /* markkeep */ + + +/*--------------------------------- + + qh_markvoronoi( facetlist, facets, printall, isLower, numcenters ) + mark voronoi vertices for printing by site pairs + + returns: + temporary set of vertices indexed by pointid + isLower set if printing lower hull (i.e., at least one facet is lower hull) + numcenters= total number of Voronoi vertices + bumps qh.printoutnum for vertex-at-infinity + clears all facet->seen and sets facet->seen2 + + if selected + facet->visitid= Voronoi vertex id + else if upper hull (or 'Qu' and lower hull) + facet->visitid= 0 + else + facet->visitid >= qh num_facets + + notes: + ignores qh.ATinfinity, if defined +*/ +setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) { + int numcenters=0; + facetT *facet, **facetp; + setT *vertices; + boolT isLower= False; + + qh printoutnum++; + qh_clearcenters(qh_ASvoronoi); /* in case, qh_printvdiagram2 called by user */ + qh_vertexneighbors(); + vertices= qh_pointvertex(); + if (qh ATinfinity) + SETelem_(vertices, qh num_points-1)= NULL; + qh visit_id++; + maximize_(qh visit_id, (unsigned) qh num_facets); + FORALLfacet_(facetlist) { + if (printall || !qh_skipfacet(facet)) { + if (!facet->upperdelaunay) { + isLower= True; + break; + } + } + } + FOREACHfacet_(facets) { + if (printall || !qh_skipfacet(facet)) { + if (!facet->upperdelaunay) { + isLower= True; + break; + } + } + } + FORALLfacets { + if (facet->normal && (facet->upperdelaunay == isLower)) + facet->visitid= 0; /* facetlist or facets may overwrite */ + else + facet->visitid= qh visit_id; + facet->seen= False; + facet->seen2= True; + } + numcenters++; /* qh_INFINITE */ + FORALLfacet_(facetlist) { + if (printall || !qh_skipfacet(facet)) + facet->visitid= numcenters++; + } + FOREACHfacet_(facets) { + if (printall || !qh_skipfacet(facet)) + facet->visitid= numcenters++; + } + *isLowerp= isLower; + *numcentersp= numcenters; + trace2((qh ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters)); + return vertices; +} /* markvoronoi */ + +/*--------------------------------- + + qh_order_vertexneighbors( vertex ) + order facet neighbors of a 2-d or 3-d vertex by adjacency + + notes: + does not orient the neighbors + + design: + initialize a new neighbor set with the first facet in vertex->neighbors + while vertex->neighbors non-empty + select next neighbor in the previous facet's neighbor set + set vertex->neighbors to the new neighbor set +*/ +void qh_order_vertexneighbors(vertexT *vertex) { + setT *newset; + facetT *facet, *neighbor, **neighborp; + + trace4((qh ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id)); + newset= qh_settemp(qh_setsize(vertex->neighbors)); + facet= (facetT*)qh_setdellast(vertex->neighbors); + qh_setappend(&newset, facet); + while (qh_setsize(vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (qh_setin(facet->neighbors, neighbor)) { + qh_setdel(vertex->neighbors, neighbor); + qh_setappend(&newset, neighbor); + facet= neighbor; + break; + } + } + if (!neighbor) { + qh_fprintf(qh ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n", + vertex->id, facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + } + qh_setfree(&vertex->neighbors); + qh_settemppop(); + vertex->neighbors= newset; +} /* order_vertexneighbors */ + +/*--------------------------------- + + qh_prepare_output( ) + prepare for qh_produce_output2() according to + qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds + does not reset facet->good + + notes + except for PRINTstatistics, no-op if previously called with same options +*/ +void qh_prepare_output(void) { + if (qh VORONOI) { + qh_clearcenters (qh_ASvoronoi); + qh_vertexneighbors(); + } + if (qh TRIangulate && !qh hasTriangulation) { + qh_triangulate(); + if (qh VERIFYoutput && !qh CHECKfrequently) + qh_checkpolygon (qh facet_list); + } + qh_findgood_all (qh facet_list); + if (qh GETarea) + qh_getarea(qh facet_list); + if (qh KEEParea || qh KEEPmerge || qh KEEPminArea < REALmax/2) + qh_markkeep (qh facet_list); + if (qh PRINTstatistics) + qh_collectstatistics(); +} + +/*--------------------------------- + + qh_printafacet( fp, format, facet, printall ) + print facet to fp in given output format (see qh.PRINTout) + + returns: + nop if !printall and qh_skipfacet() + nop if visible facet and NEWfacets and format != PRINTfacets + must match qh_countfacets + + notes + preserves qh.visit_id + facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge + + see + qh_printbegin() and qh_printend() + + design: + test for printing facet + call appropriate routine for format + or output results directly +*/ +void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall) { + realT color[4], offset, dist, outerplane, innerplane; + boolT zerodiv; + coordT *point, *normp, *coordp, **pointp, *feasiblep; + int k; + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + + if (!printall && qh_skipfacet(facet)) + return; + if (facet->visible && qh NEWfacets && format != qh_PRINTfacets) + return; + qh printoutnum++; + switch (format) { + case qh_PRINTarea: + if (facet->isarea) { + qh_fprintf(fp, 9009, qh_REAL_1, facet->f.area); + qh_fprintf(fp, 9010, "\n"); + }else + qh_fprintf(fp, 9011, "0\n"); + break; + case qh_PRINTcoplanars: + qh_fprintf(fp, 9012, "%d", qh_setsize(facet->coplanarset)); + FOREACHpoint_(facet->coplanarset) + qh_fprintf(fp, 9013, " %d", qh_pointid(point)); + qh_fprintf(fp, 9014, "\n"); + break; + case qh_PRINTcentrums: + qh_printcenter(fp, format, NULL, facet); + break; + case qh_PRINTfacets: + qh_printfacet(fp, facet); + break; + case qh_PRINTfacets_xridge: + qh_printfacetheader(fp, facet); + break; + case qh_PRINTgeom: /* either 2 , 3, or 4-d by qh_printbegin */ + if (!facet->normal) + break; + for (k=qh hull_dim; k--; ) { + color[k]= (facet->normal[k]+1.0)/2.0; + maximize_(color[k], -1.0); + minimize_(color[k], +1.0); + } + qh_projectdim3 (color, color); + if (qh PRINTdim != qh hull_dim) + qh_normalize2 (color, 3, True, NULL, NULL); + if (qh hull_dim <= 2) + qh_printfacet2geom(fp, facet, color); + else if (qh hull_dim == 3) { + if (facet->simplicial) + qh_printfacet3geom_simplicial(fp, facet, color); + else + qh_printfacet3geom_nonsimplicial(fp, facet, color); + }else { + if (facet->simplicial) + qh_printfacet4geom_simplicial(fp, facet, color); + else + qh_printfacet4geom_nonsimplicial(fp, facet, color); + } + break; + case qh_PRINTids: + qh_fprintf(fp, 9015, "%d\n", facet->id); + break; + case qh_PRINTincidences: + case qh_PRINToff: + case qh_PRINTtriangles: + if (qh hull_dim == 3 && format != qh_PRINTtriangles) + qh_printfacet3vertex(fp, facet, format); + else if (facet->simplicial || qh hull_dim == 2 || format == qh_PRINToff) + qh_printfacetNvertex_simplicial(fp, facet, format); + else + qh_printfacetNvertex_nonsimplicial(fp, facet, qh printoutvar++, format); + break; + case qh_PRINTinner: + qh_outerinner(facet, NULL, &innerplane); + offset= facet->offset - innerplane; + goto LABELprintnorm; + break; /* prevent warning */ + case qh_PRINTmerges: + qh_fprintf(fp, 9016, "%d\n", facet->nummerge); + break; + case qh_PRINTnormals: + offset= facet->offset; + goto LABELprintnorm; + break; /* prevent warning */ + case qh_PRINTouter: + qh_outerinner(facet, &outerplane, NULL); + offset= facet->offset - outerplane; + LABELprintnorm: + if (!facet->normal) { + qh_fprintf(fp, 9017, "no normal for facet f%d\n", facet->id); + break; + } + if (qh CDDoutput) { + qh_fprintf(fp, 9018, qh_REAL_1, -offset); + for (k=0; k < qh hull_dim; k++) + qh_fprintf(fp, 9019, qh_REAL_1, -facet->normal[k]); + }else { + for (k=0; k < qh hull_dim; k++) + qh_fprintf(fp, 9020, qh_REAL_1, facet->normal[k]); + qh_fprintf(fp, 9021, qh_REAL_1, offset); + } + qh_fprintf(fp, 9022, "\n"); + break; + case qh_PRINTmathematica: /* either 2 or 3-d by qh_printbegin */ + case qh_PRINTmaple: + if (qh hull_dim == 2) + qh_printfacet2math(fp, facet, format, qh printoutvar++); + else + qh_printfacet3math(fp, facet, format, qh printoutvar++); + break; + case qh_PRINTneighbors: + qh_fprintf(fp, 9023, "%d", qh_setsize(facet->neighbors)); + FOREACHneighbor_(facet) + qh_fprintf(fp, 9024, " %d", + neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id); + qh_fprintf(fp, 9025, "\n"); + break; + case qh_PRINTpointintersect: + if (!qh feasible_point) { + qh_fprintf(qh ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh feasible_point\n"); + qh_errexit( qh_ERRinput, NULL, NULL); + } + if (facet->offset > 0) + goto LABELprintinfinite; + point= coordp= (coordT*)qh_memalloc(qh normal_size); + normp= facet->normal; + feasiblep= qh feasible_point; + if (facet->offset < -qh MINdenom) { + for (k=qh hull_dim; k--; ) + *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++); + }else { + for (k=qh hull_dim; k--; ) { + *(coordp++)= qh_divzero(*(normp++), facet->offset, qh MINdenom_1, + &zerodiv) + *(feasiblep++); + if (zerodiv) { + qh_memfree(point, qh normal_size); + goto LABELprintinfinite; + } + } + } + qh_printpoint(fp, NULL, point); + qh_memfree(point, qh normal_size); + break; + LABELprintinfinite: + for (k=qh hull_dim; k--; ) + qh_fprintf(fp, 9026, qh_REAL_1, qh_INFINITE); + qh_fprintf(fp, 9027, "\n"); + break; + case qh_PRINTpointnearest: + FOREACHpoint_(facet->coplanarset) { + int id, id2; + vertex= qh_nearvertex(facet, point, &dist); + id= qh_pointid(vertex->point); + id2= qh_pointid(point); + qh_fprintf(fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist); + } + break; + case qh_PRINTpoints: /* VORONOI only by qh_printbegin */ + if (qh CDDoutput) + qh_fprintf(fp, 9029, "1 "); + qh_printcenter(fp, format, NULL, facet); + break; + case qh_PRINTvertices: + qh_fprintf(fp, 9030, "%d", qh_setsize(facet->vertices)); + FOREACHvertex_(facet->vertices) + qh_fprintf(fp, 9031, " %d", qh_pointid(vertex->point)); + qh_fprintf(fp, 9032, "\n"); + break; + default: + break; + } +} /* printafacet */ + +/*--------------------------------- + + qh_printbegin( ) + prints header for all output formats + + returns: + checks for valid format + + notes: + uses qh.visit_id for 3/4off + changes qh.interior_point if printing centrums + qh_countfacets clears facet->visitid for non-good facets + + see + qh_printend() and qh_printafacet() + + design: + count facets and related statistics + print header for format +*/ +void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars; + int i, num; + facetT *facet, **facetp; + vertexT *vertex, **vertexp; + setT *vertices; + pointT *point, **pointp, *pointtemp; + + qh printoutnum= 0; + qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + switch (format) { + case qh_PRINTnone: + break; + case qh_PRINTarea: + qh_fprintf(fp, 9033, "%d\n", numfacets); + break; + case qh_PRINTcoplanars: + qh_fprintf(fp, 9034, "%d\n", numfacets); + break; + case qh_PRINTcentrums: + if (qh CENTERtype == qh_ASnone) + qh_clearcenters(qh_AScentrum); + qh_fprintf(fp, 9035, "%d\n%d\n", qh hull_dim, numfacets); + break; + case qh_PRINTfacets: + case qh_PRINTfacets_xridge: + if (facetlist) + qh_printvertexlist(fp, "Vertices and facets:\n", facetlist, facets, printall); + break; + case qh_PRINTgeom: + if (qh hull_dim > 4) /* qh_initqhull_globals also checks */ + goto LABELnoformat; + if (qh VORONOI && qh hull_dim > 3) /* PRINTdim == DROPdim == hull_dim-1 */ + goto LABELnoformat; + if (qh hull_dim == 2 && (qh PRINTridges || qh DOintersections)) + qh_fprintf(qh ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n"); + if (qh hull_dim == 4 && (qh PRINTinner || qh PRINTouter || + (qh PRINTdim == 4 && qh PRINTcentrums))) + qh_fprintf(qh ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n"); + if (qh PRINTdim == 4 && (qh PRINTspheres)) + qh_fprintf(qh ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n"); + if (qh PRINTdim == 4 && qh DOintersections && qh PRINTnoplanes) + qh_fprintf(qh ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n"); + if (qh PRINTdim == 2) { + qh_fprintf(fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n", + qh rbox_command, qh qhull_command); + }else if (qh PRINTdim == 3) { + qh_fprintf(fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n", + qh rbox_command, qh qhull_command); + }else if (qh PRINTdim == 4) { + qh visit_id++; + num= 0; + FORALLfacet_(facetlist) /* get number of ridges to be printed */ + qh_printend4geom(NULL, facet, &num, printall); + FOREACHfacet_(facets) + qh_printend4geom(NULL, facet, &num, printall); + qh ridgeoutnum= num; + qh printoutvar= 0; /* counts number of ridges in output */ + qh_fprintf(fp, 9038, "LIST # %s | %s\n", qh rbox_command, qh qhull_command); + } + + if (qh PRINTdots) { + qh printoutnum++; + num= qh num_points + qh_setsize(qh other_points); + if (qh DELAUNAY && qh ATinfinity) + num--; + if (qh PRINTdim == 4) + qh_fprintf(fp, 9039, "4VECT %d %d 1\n", num, num); + else + qh_fprintf(fp, 9040, "VECT %d %d 1\n", num, num); + + for (i=num; i--; ) { + if (i % 20 == 0) + qh_fprintf(fp, 9041, "\n"); + qh_fprintf(fp, 9042, "1 "); + } + qh_fprintf(fp, 9043, "# 1 point per line\n1 "); + for (i=num-1; i--; ) { /* num at least 3 for D2 */ + if (i % 20 == 0) + qh_fprintf(fp, 9044, "\n"); + qh_fprintf(fp, 9045, "0 "); + } + qh_fprintf(fp, 9046, "# 1 color for all\n"); + FORALLpoints { + if (!qh DELAUNAY || !qh ATinfinity || qh_pointid(point) != qh num_points-1) { + if (qh PRINTdim == 4) + qh_printpoint(fp, NULL, point); + else + qh_printpoint3 (fp, point); + } + } + FOREACHpoint_(qh other_points) { + if (qh PRINTdim == 4) + qh_printpoint(fp, NULL, point); + else + qh_printpoint3 (fp, point); + } + qh_fprintf(fp, 9047, "0 1 1 1 # color of points\n"); + } + + if (qh PRINTdim == 4 && !qh PRINTnoplanes) + /* 4dview loads up multiple 4OFF objects slowly */ + qh_fprintf(fp, 9048, "4OFF %d %d 1\n", 3*qh ridgeoutnum, qh ridgeoutnum); + qh PRINTcradius= 2 * qh DISTround; /* include test DISTround */ + if (qh PREmerge) { + maximize_(qh PRINTcradius, qh premerge_centrum + qh DISTround); + }else if (qh POSTmerge) + maximize_(qh PRINTcradius, qh postmerge_centrum + qh DISTround); + qh PRINTradius= qh PRINTcradius; + if (qh PRINTspheres + qh PRINTcoplanar) + maximize_(qh PRINTradius, qh MAXabs_coord * qh_MINradius); + if (qh premerge_cos < REALmax/2) { + maximize_(qh PRINTradius, (1- qh premerge_cos) * qh MAXabs_coord); + }else if (!qh PREmerge && qh POSTmerge && qh postmerge_cos < REALmax/2) { + maximize_(qh PRINTradius, (1- qh postmerge_cos) * qh MAXabs_coord); + } + maximize_(qh PRINTradius, qh MINvisible); + if (qh JOGGLEmax < REALmax/2) + qh PRINTradius += qh JOGGLEmax * sqrt((realT)qh hull_dim); + if (qh PRINTdim != 4 && + (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) { + vertices= qh_facetvertices(facetlist, facets, printall); + if (qh PRINTspheres && qh PRINTdim <= 3) + qh_printspheres(fp, vertices, qh PRINTradius); + if (qh PRINTcoplanar || qh PRINTcentrums) { + qh firstcentrum= True; + if (qh PRINTcoplanar&& !qh PRINTspheres) { + FOREACHvertex_(vertices) + qh_printpointvect2 (fp, vertex->point, NULL, qh interior_point, qh PRINTradius); + } + FORALLfacet_(facetlist) { + if (!printall && qh_skipfacet(facet)) + continue; + if (!facet->normal) + continue; + if (qh PRINTcentrums && qh PRINTdim <= 3) + qh_printcentrum(fp, facet, qh PRINTcradius); + if (!qh PRINTcoplanar) + continue; + FOREACHpoint_(facet->coplanarset) + qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius); + FOREACHpoint_(facet->outsideset) + qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius); + } + FOREACHfacet_(facets) { + if (!printall && qh_skipfacet(facet)) + continue; + if (!facet->normal) + continue; + if (qh PRINTcentrums && qh PRINTdim <= 3) + qh_printcentrum(fp, facet, qh PRINTcradius); + if (!qh PRINTcoplanar) + continue; + FOREACHpoint_(facet->coplanarset) + qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius); + FOREACHpoint_(facet->outsideset) + qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius); + } + } + qh_settempfree(&vertices); + } + qh visit_id++; /* for printing hyperplane intersections */ + break; + case qh_PRINTids: + qh_fprintf(fp, 9049, "%d\n", numfacets); + break; + case qh_PRINTincidences: + if (qh VORONOI && qh PRINTprecision) + qh_fprintf(qh ferr, 7053, "qhull warning: writing Delaunay. Use 'p' or 'o' for Voronoi centers\n"); + qh printoutvar= qh vertex_id; /* centrum id for non-simplicial facets */ + if (qh hull_dim <= 3) + qh_fprintf(fp, 9050, "%d\n", numfacets); + else + qh_fprintf(fp, 9051, "%d\n", numsimplicial+numridges); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh CDDoutput) + qh_fprintf(fp, 9052, "%s | %s\nbegin\n %d %d real\n", qh rbox_command, + qh qhull_command, numfacets, qh hull_dim+1); + else + qh_fprintf(fp, 9053, "%d\n%d\n", qh hull_dim+1, numfacets); + break; + case qh_PRINTmathematica: + case qh_PRINTmaple: + if (qh hull_dim > 3) /* qh_initbuffers also checks */ + goto LABELnoformat; + if (qh VORONOI) + qh_fprintf(qh ferr, 7054, "qhull warning: output is the Delaunay triangulation\n"); + if (format == qh_PRINTmaple) { + if (qh hull_dim == 2) + qh_fprintf(fp, 9054, "PLOT(CURVES(\n"); + else + qh_fprintf(fp, 9055, "PLOT3D(POLYGONS(\n"); + }else + qh_fprintf(fp, 9056, "{\n"); + qh printoutvar= 0; /* counts number of facets for notfirst */ + break; + case qh_PRINTmerges: + qh_fprintf(fp, 9057, "%d\n", numfacets); + break; + case qh_PRINTpointintersect: + qh_fprintf(fp, 9058, "%d\n%d\n", qh hull_dim, numfacets); + break; + case qh_PRINTneighbors: + qh_fprintf(fp, 9059, "%d\n", numfacets); + break; + case qh_PRINToff: + case qh_PRINTtriangles: + if (qh VORONOI) + goto LABELnoformat; + num = qh hull_dim; + if (format == qh_PRINToff || qh hull_dim == 2) + qh_fprintf(fp, 9060, "%d\n%d %d %d\n", num, + qh num_points+qh_setsize(qh other_points), numfacets, totneighbors/2); + else { /* qh_PRINTtriangles */ + qh printoutvar= qh num_points+qh_setsize(qh other_points); /* first centrum */ + if (qh DELAUNAY) + num--; /* drop last dimension */ + qh_fprintf(fp, 9061, "%d\n%d %d %d\n", num, qh printoutvar + + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2); + } + FORALLpoints + qh_printpointid(qh fout, NULL, num, point, -1); + FOREACHpoint_(qh other_points) + qh_printpointid(qh fout, NULL, num, point, -1); + if (format == qh_PRINTtriangles && qh hull_dim > 2) { + FORALLfacets { + if (!facet->simplicial && facet->visitid) + qh_printcenter(qh fout, format, NULL, facet); + } + } + break; + case qh_PRINTpointnearest: + qh_fprintf(fp, 9062, "%d\n", numcoplanars); + break; + case qh_PRINTpoints: + if (!qh VORONOI) + goto LABELnoformat; + if (qh CDDoutput) + qh_fprintf(fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh rbox_command, + qh qhull_command, numfacets, qh hull_dim); + else + qh_fprintf(fp, 9064, "%d\n%d\n", qh hull_dim-1, numfacets); + break; + case qh_PRINTvertices: + qh_fprintf(fp, 9065, "%d\n", numfacets); + break; + case qh_PRINTsummary: + default: + LABELnoformat: + qh_fprintf(qh ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n", + qh hull_dim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +} /* printbegin */ + +/*--------------------------------- + + qh_printcenter( fp, string, facet ) + print facet->center as centrum or Voronoi center + string may be NULL. Don't include '%' codes. + nop if qh CENTERtype neither CENTERvoronoi nor CENTERcentrum + if upper envelope of Delaunay triangulation and point at-infinity + prints qh_INFINITE instead; + + notes: + defines facet->center if needed + if format=PRINTgeom, adds a 0 if would otherwise be 2-d + Same as QhullFacet::printCenter +*/ +void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet) { + int k, num; + + if (qh CENTERtype != qh_ASvoronoi && qh CENTERtype != qh_AScentrum) + return; + if (string) + qh_fprintf(fp, 9066, string); + if (qh CENTERtype == qh_ASvoronoi) { + num= qh hull_dim-1; + if (!facet->normal || !facet->upperdelaunay || !qh ATinfinity) { + if (!facet->center) + facet->center= qh_facetcenter(facet->vertices); + for (k=0; k < num; k++) + qh_fprintf(fp, 9067, qh_REAL_1, facet->center[k]); + }else { + for (k=0; k < num; k++) + qh_fprintf(fp, 9068, qh_REAL_1, qh_INFINITE); + } + }else /* qh CENTERtype == qh_AScentrum */ { + num= qh hull_dim; + if (format == qh_PRINTtriangles && qh DELAUNAY) + num--; + if (!facet->center) + facet->center= qh_getcentrum(facet); + for (k=0; k < num; k++) + qh_fprintf(fp, 9069, qh_REAL_1, facet->center[k]); + } + if (format == qh_PRINTgeom && num == 2) + qh_fprintf(fp, 9070, " 0\n"); + else + qh_fprintf(fp, 9071, "\n"); +} /* printcenter */ + +/*--------------------------------- + + qh_printcentrum( fp, facet, radius ) + print centrum for a facet in OOGL format + radius defines size of centrum + 2-d or 3-d only + + returns: + defines facet->center if needed +*/ +void qh_printcentrum(FILE *fp, facetT *facet, realT radius) { + pointT *centrum, *projpt; + boolT tempcentrum= False; + realT xaxis[4], yaxis[4], normal[4], dist; + realT green[3]={0, 1, 0}; + vertexT *apex; + int k; + + if (qh CENTERtype == qh_AScentrum) { + if (!facet->center) + facet->center= qh_getcentrum(facet); + centrum= facet->center; + }else { + centrum= qh_getcentrum(facet); + tempcentrum= True; + } + qh_fprintf(fp, 9072, "{appearance {-normal -edge normscale 0} "); + if (qh firstcentrum) { + qh firstcentrum= False; + qh_fprintf(fp, 9073, "{INST geom { define centrum CQUAD # f%d\n\ +-0.3 -0.3 0.0001 0 0 1 1\n\ + 0.3 -0.3 0.0001 0 0 1 1\n\ + 0.3 0.3 0.0001 0 0 1 1\n\ +-0.3 0.3 0.0001 0 0 1 1 } transform { \n", facet->id); + }else + qh_fprintf(fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id); + apex= SETfirstt_(facet->vertices, vertexT); + qh_distplane(apex->point, facet, &dist); + projpt= qh_projectpoint(apex->point, facet, dist); + for (k=qh hull_dim; k--; ) { + xaxis[k]= projpt[k] - centrum[k]; + normal[k]= facet->normal[k]; + } + if (qh hull_dim == 2) { + xaxis[2]= 0; + normal[2]= 0; + }else if (qh hull_dim == 4) { + qh_projectdim3 (xaxis, xaxis); + qh_projectdim3 (normal, normal); + qh_normalize2 (normal, qh PRINTdim, True, NULL, NULL); + } + qh_crossproduct(3, xaxis, normal, yaxis); + qh_fprintf(fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]); + qh_fprintf(fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]); + qh_fprintf(fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]); + qh_printpoint3 (fp, centrum); + qh_fprintf(fp, 9078, "1 }}}\n"); + qh_memfree(projpt, qh normal_size); + qh_printpointvect(fp, centrum, facet->normal, NULL, radius, green); + if (tempcentrum) + qh_memfree(centrum, qh normal_size); +} /* printcentrum */ + +/*--------------------------------- + + qh_printend( fp, format ) + prints trailer for all output formats + + see: + qh_printbegin() and qh_printafacet() + +*/ +void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int num; + facetT *facet, **facetp; + + if (!qh printoutnum) + qh_fprintf(qh ferr, 7055, "qhull warning: no facets printed\n"); + switch (format) { + case qh_PRINTgeom: + if (qh hull_dim == 4 && qh DROPdim < 0 && !qh PRINTnoplanes) { + qh visit_id++; + num= 0; + FORALLfacet_(facetlist) + qh_printend4geom(fp, facet,&num, printall); + FOREACHfacet_(facets) + qh_printend4geom(fp, facet, &num, printall); + if (num != qh ridgeoutnum || qh printoutvar != qh ridgeoutnum) { + qh_fprintf(qh ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh ridgeoutnum, qh printoutvar, num); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + }else + qh_fprintf(fp, 9079, "}\n"); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh CDDoutput) + qh_fprintf(fp, 9080, "end\n"); + break; + case qh_PRINTmaple: + qh_fprintf(fp, 9081, "));\n"); + break; + case qh_PRINTmathematica: + qh_fprintf(fp, 9082, "}\n"); + break; + case qh_PRINTpoints: + if (qh CDDoutput) + qh_fprintf(fp, 9083, "end\n"); + break; + default: + break; + } +} /* printend */ + +/*--------------------------------- + + qh_printend4geom( fp, facet, numridges, printall ) + helper function for qh_printbegin/printend + + returns: + number of printed ridges + + notes: + just counts printed ridges if fp=NULL + uses facet->visitid + must agree with qh_printfacet4geom... + + design: + computes color for facet from its normal + prints each ridge of facet +*/ +void qh_printend4geom(FILE *fp, facetT *facet, int *nump, boolT printall) { + realT color[3]; + int i, num= *nump; + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + + if (!printall && qh_skipfacet(facet)) + return; + if (qh PRINTnoplanes || (facet->visible && qh NEWfacets)) + return; + if (!facet->normal) + return; + if (fp) { + for (i=0; i < 3; i++) { + color[i]= (facet->normal[i]+1.0)/2.0; + maximize_(color[i], -1.0); + minimize_(color[i], +1.0); + } + } + facet->visitid= qh visit_id; + if (facet->simplicial) { + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + if (fp) + qh_fprintf(fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n", + 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2], + facet->id, neighbor->id); + num++; + } + } + }else { + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid != qh visit_id) { + if (fp) + qh_fprintf(fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n", + 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2], + ridge->id, facet->id, neighbor->id); + num++; + } + } + } + *nump= num; +} /* printend4geom */ + +/*--------------------------------- + + qh_printextremes( fp, facetlist, facets, printall ) + print extreme points for convex hulls or halfspace intersections + + notes: + #points, followed by ids, one per line + + sorted by id + same order as qh_printpoints_out if no coplanar/interior points +*/ +void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices, *points; + pointT *point; + vertexT *vertex, **vertexp; + int id; + int numpoints=0, point_i, point_n; + int allpoints= qh num_points + qh_setsize(qh other_points); + + points= qh_settemp(allpoints); + qh_setzero(points, 0, allpoints); + vertices= qh_facetvertices(facetlist, facets, printall); + FOREACHvertex_(vertices) { + id= qh_pointid(vertex->point); + if (id >= 0) { + SETelem_(points, id)= vertex->point; + numpoints++; + } + } + qh_settempfree(&vertices); + qh_fprintf(fp, 9086, "%d\n", numpoints); + FOREACHpoint_i_(points) { + if (point) + qh_fprintf(fp, 9087, "%d\n", point_i); + } + qh_settempfree(&points); +} /* printextremes */ + +/*--------------------------------- + + qh_printextremes_2d( fp, facetlist, facets, printall ) + prints point ids for facets in qh_ORIENTclock order + + notes: + #points, followed by ids, one per line + if facetlist/facets are disjoint than the output includes skips + errors if facets form a loop + does not print coplanar points +*/ +void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars; + setT *vertices; + facetT *facet, *startfacet, *nextfacet; + vertexT *vertexA, *vertexB; + + qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh visit_id */ + vertices= qh_facetvertices(facetlist, facets, printall); + qh_fprintf(fp, 9088, "%d\n", qh_setsize(vertices)); + qh_settempfree(&vertices); + if (!numfacets) + return; + facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT); + qh vertex_visit++; + qh visit_id++; + do { + if (facet->toporient ^ qh_ORIENTclock) { + vertexA= SETfirstt_(facet->vertices, vertexT); + vertexB= SETsecondt_(facet->vertices, vertexT); + nextfacet= SETfirstt_(facet->neighbors, facetT); + }else { + vertexA= SETsecondt_(facet->vertices, vertexT); + vertexB= SETfirstt_(facet->vertices, vertexT); + nextfacet= SETsecondt_(facet->neighbors, facetT); + } + if (facet->visitid == qh visit_id) { + qh_fprintf(qh ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n", + facet->id, nextfacet->id); + qh_errexit2 (qh_ERRqhull, facet, nextfacet); + } + if (facet->visitid) { + if (vertexA->visitid != qh vertex_visit) { + vertexA->visitid= qh vertex_visit; + qh_fprintf(fp, 9089, "%d\n", qh_pointid(vertexA->point)); + } + if (vertexB->visitid != qh vertex_visit) { + vertexB->visitid= qh vertex_visit; + qh_fprintf(fp, 9090, "%d\n", qh_pointid(vertexB->point)); + } + } + facet->visitid= qh visit_id; + facet= nextfacet; + }while (facet && facet != startfacet); +} /* printextremes_2d */ + +/*--------------------------------- + + qh_printextremes_d( fp, facetlist, facets, printall ) + print extreme points of input sites for Delaunay triangulations + + notes: + #points, followed by ids, one per line + + unordered +*/ +void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices; + vertexT *vertex, **vertexp; + boolT upperseen, lowerseen; + facetT *neighbor, **neighborp; + int numpoints=0; + + vertices= qh_facetvertices(facetlist, facets, printall); + qh_vertexneighbors(); + FOREACHvertex_(vertices) { + upperseen= lowerseen= False; + FOREACHneighbor_(vertex) { + if (neighbor->upperdelaunay) + upperseen= True; + else + lowerseen= True; + } + if (upperseen && lowerseen) { + vertex->seen= True; + numpoints++; + }else + vertex->seen= False; + } + qh_fprintf(fp, 9091, "%d\n", numpoints); + FOREACHvertex_(vertices) { + if (vertex->seen) + qh_fprintf(fp, 9092, "%d\n", qh_pointid(vertex->point)); + } + qh_settempfree(&vertices); +} /* printextremes_d */ + +/*--------------------------------- + + qh_printfacet( fp, facet ) + prints all fields of a facet to fp + + notes: + ridges printed in neighbor order +*/ +void qh_printfacet(FILE *fp, facetT *facet) { + + qh_printfacetheader(fp, facet); + if (facet->ridges) + qh_printfacetridges(fp, facet); +} /* printfacet */ + + +/*--------------------------------- + + qh_printfacet2geom( fp, facet, color ) + print facet as part of a 2-d VECT for Geomview + + notes: + assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon + mindist is calculated within io.c. maxoutside is calculated elsewhere + so a DISTround error may have occured. +*/ +void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]) { + pointT *point0, *point1; + realT mindist, innerplane, outerplane; + int k; + + qh_facet2point(facet, &point0, &point1, &mindist); + qh_geomplanes(facet, &outerplane, &innerplane); + if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner)) + qh_printfacet2geom_points(fp, point0, point1, facet, outerplane, color); + if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter && + outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) { + for (k=3; k--; ) + color[k]= 1.0 - color[k]; + qh_printfacet2geom_points(fp, point0, point1, facet, innerplane, color); + } + qh_memfree(point1, qh normal_size); + qh_memfree(point0, qh normal_size); +} /* printfacet2geom */ + +/*--------------------------------- + + qh_printfacet2geom_points( fp, point1, point2, facet, offset, color ) + prints a 2-d facet as a VECT with 2 points at some offset. + The points are on the facet's plane. +*/ +void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2, + facetT *facet, realT offset, realT color[3]) { + pointT *p1= point1, *p2= point2; + + qh_fprintf(fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id); + if (offset != 0.0) { + p1= qh_projectpoint(p1, facet, -offset); + p2= qh_projectpoint(p2, facet, -offset); + } + qh_fprintf(fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n", + p1[0], p1[1], 0.0, p2[0], p2[1], 0.0); + if (offset != 0.0) { + qh_memfree(p1, qh normal_size); + qh_memfree(p2, qh normal_size); + } + qh_fprintf(fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printfacet2geom_points */ + + +/*--------------------------------- + + qh_printfacet2math( fp, facet, format, notfirst ) + print 2-d Maple or Mathematica output for a facet + may be non-simplicial + + notes: + use %16.8f since Mathematica 2.2 does not handle exponential format + see qh_printfacet3math +*/ +void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) { + pointT *point0, *point1; + realT mindist; + const char *pointfmt; + + qh_facet2point(facet, &point0, &point1, &mindist); + if (notfirst) + qh_fprintf(fp, 9096, ","); + if (format == qh_PRINTmaple) + pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n"; + else + pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n"; + qh_fprintf(fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]); + qh_memfree(point1, qh normal_size); + qh_memfree(point0, qh normal_size); +} /* printfacet2math */ + + +/*--------------------------------- + + qh_printfacet3geom_nonsimplicial( fp, facet, color ) + print Geomview OFF for a 3-d nonsimplicial facet. + if DOintersections, prints ridges to unvisited neighbors(qh visit_id) + + notes + uses facet->visitid for intersections and ridges +*/ +void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) { + ridgeT *ridge, **ridgep; + setT *projectedpoints, *vertices; + vertexT *vertex, **vertexp, *vertexA, *vertexB; + pointT *projpt, *point, **pointp; + facetT *neighbor; + realT dist, outerplane, innerplane; + int cntvertices, k; + realT black[3]={0, 0, 0}, green[3]={0, 1, 0}; + + qh_geomplanes(facet, &outerplane, &innerplane); + vertices= qh_facet3vertex(facet); /* oriented */ + cntvertices= qh_setsize(vertices); + projectedpoints= qh_settemp(cntvertices); + FOREACHvertex_(vertices) { + zinc_(Zdistio); + qh_distplane(vertex->point, facet, &dist); + projpt= qh_projectpoint(vertex->point, facet, dist); + qh_setappend(&projectedpoints, projpt); + } + if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner)) + qh_printfacet3geom_points(fp, projectedpoints, facet, outerplane, color); + if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter && + outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) { + for (k=3; k--; ) + color[k]= 1.0 - color[k]; + qh_printfacet3geom_points(fp, projectedpoints, facet, innerplane, color); + } + FOREACHpoint_(projectedpoints) + qh_memfree(point, qh normal_size); + qh_settempfree(&projectedpoints); + qh_settempfree(&vertices); + if ((qh DOintersections || qh PRINTridges) + && (!facet->visible || !qh NEWfacets)) { + facet->visitid= qh visit_id; + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid != qh visit_id) { + if (qh DOintersections) + qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, black); + if (qh PRINTridges) { + vertexA= SETfirstt_(ridge->vertices, vertexT); + vertexB= SETsecondt_(ridge->vertices, vertexT); + qh_printline3geom(fp, vertexA->point, vertexB->point, green); + } + } + } + } +} /* printfacet3geom_nonsimplicial */ + +/*--------------------------------- + + qh_printfacet3geom_points( fp, points, facet, offset ) + prints a 3-d facet as OFF Geomview object. + offset is relative to the facet's hyperplane + Facet is determined as a list of points +*/ +void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) { + int k, n= qh_setsize(points), i; + pointT *point, **pointp; + setT *printpoints; + + qh_fprintf(fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id); + if (offset != 0.0) { + printpoints= qh_settemp(n); + FOREACHpoint_(points) + qh_setappend(&printpoints, qh_projectpoint(point, facet, -offset)); + }else + printpoints= points; + FOREACHpoint_(printpoints) { + for (k=0; k < qh hull_dim; k++) { + if (k == qh DROPdim) + qh_fprintf(fp, 9099, "0 "); + else + qh_fprintf(fp, 9100, "%8.4g ", point[k]); + } + if (printpoints != points) + qh_memfree(point, qh normal_size); + qh_fprintf(fp, 9101, "\n"); + } + if (printpoints != points) + qh_settempfree(&printpoints); + qh_fprintf(fp, 9102, "%d ", n); + for (i=0; i < n; i++) + qh_fprintf(fp, 9103, "%d ", i); + qh_fprintf(fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]); +} /* printfacet3geom_points */ + + +/*--------------------------------- + + qh_printfacet3geom_simplicial( ) + print Geomview OFF for a 3-d simplicial facet. + + notes: + may flip color + uses facet->visitid for intersections and ridges + + assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon + innerplane may be off by qh DISTround. Maxoutside is calculated elsewhere + so a DISTround error may have occured. +*/ +void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]) { + setT *points, *vertices; + vertexT *vertex, **vertexp, *vertexA, *vertexB; + facetT *neighbor, **neighborp; + realT outerplane, innerplane; + realT black[3]={0, 0, 0}, green[3]={0, 1, 0}; + int k; + + qh_geomplanes(facet, &outerplane, &innerplane); + vertices= qh_facet3vertex(facet); + points= qh_settemp(qh TEMPsize); + FOREACHvertex_(vertices) + qh_setappend(&points, vertex->point); + if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner)) + qh_printfacet3geom_points(fp, points, facet, outerplane, color); + if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter && + outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) { + for (k=3; k--; ) + color[k]= 1.0 - color[k]; + qh_printfacet3geom_points(fp, points, facet, innerplane, color); + } + qh_settempfree(&points); + qh_settempfree(&vertices); + if ((qh DOintersections || qh PRINTridges) + && (!facet->visible || !qh NEWfacets)) { + facet->visitid= qh visit_id; + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim, + SETindex_(facet->neighbors, neighbor), 0); + if (qh DOintersections) + qh_printhyperplaneintersection(fp, facet, neighbor, vertices, black); + if (qh PRINTridges) { + vertexA= SETfirstt_(vertices, vertexT); + vertexB= SETsecondt_(vertices, vertexT); + qh_printline3geom(fp, vertexA->point, vertexB->point, green); + } + qh_setfree(&vertices); + } + } + } +} /* printfacet3geom_simplicial */ + +/*--------------------------------- + + qh_printfacet3math( fp, facet, notfirst ) + print 3-d Maple or Mathematica output for a facet + + notes: + may be non-simplicial + use %16.8f since Mathematica 2.2 does not handle exponential format + see qh_printfacet2math +*/ +void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) { + vertexT *vertex, **vertexp; + setT *points, *vertices; + pointT *point, **pointp; + boolT firstpoint= True; + realT dist; + const char *pointfmt, *endfmt; + + if (notfirst) + qh_fprintf(fp, 9105, ",\n"); + vertices= qh_facet3vertex(facet); + points= qh_settemp(qh_setsize(vertices)); + FOREACHvertex_(vertices) { + zinc_(Zdistio); + qh_distplane(vertex->point, facet, &dist); + point= qh_projectpoint(vertex->point, facet, dist); + qh_setappend(&points, point); + } + if (format == qh_PRINTmaple) { + qh_fprintf(fp, 9106, "["); + pointfmt= "[%16.8f, %16.8f, %16.8f]"; + endfmt= "]"; + }else { + qh_fprintf(fp, 9107, "Polygon[{"); + pointfmt= "{%16.8f, %16.8f, %16.8f}"; + endfmt= "}]"; + } + FOREACHpoint_(points) { + if (firstpoint) + firstpoint= False; + else + qh_fprintf(fp, 9108, ",\n"); + qh_fprintf(fp, 9109, pointfmt, point[0], point[1], point[2]); + } + FOREACHpoint_(points) + qh_memfree(point, qh normal_size); + qh_settempfree(&points); + qh_settempfree(&vertices); + qh_fprintf(fp, 9110, endfmt); +} /* printfacet3math */ + + +/*--------------------------------- + + qh_printfacet3vertex( fp, facet, format ) + print vertices in a 3-d facet as point ids + + notes: + prints number of vertices first if format == qh_PRINToff + the facet may be non-simplicial +*/ +void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facet3vertex(facet); + if (format == qh_PRINToff) + qh_fprintf(fp, 9111, "%d ", qh_setsize(vertices)); + FOREACHvertex_(vertices) + qh_fprintf(fp, 9112, "%d ", qh_pointid(vertex->point)); + qh_fprintf(fp, 9113, "\n"); + qh_settempfree(&vertices); +} /* printfacet3vertex */ + + +/*--------------------------------- + + qh_printfacet4geom_nonsimplicial( ) + print Geomview 4OFF file for a 4d nonsimplicial facet + prints all ridges to unvisited neighbors (qh.visit_id) + if qh.DROPdim + prints in OFF format + + notes: + must agree with printend4geom() +*/ +void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) { + facetT *neighbor; + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + pointT *point; + int k; + realT dist; + + facet->visitid= qh visit_id; + if (qh PRINTnoplanes || (facet->visible && qh NEWfacets)) + return; + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid == qh visit_id) + continue; + if (qh PRINTtransparent && !neighbor->good) + continue; + if (qh DOintersections) + qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, color); + else { + if (qh DROPdim >= 0) + qh_fprintf(fp, 9114, "OFF 3 1 1 # f%d\n", facet->id); + else { + qh printoutvar++; + qh_fprintf(fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id); + } + FOREACHvertex_(ridge->vertices) { + zinc_(Zdistio); + qh_distplane(vertex->point,facet, &dist); + point=qh_projectpoint(vertex->point,facet, dist); + for (k=0; k < qh hull_dim; k++) { + if (k != qh DROPdim) + qh_fprintf(fp, 9116, "%8.4g ", point[k]); + } + qh_fprintf(fp, 9117, "\n"); + qh_memfree(point, qh normal_size); + } + if (qh DROPdim >= 0) + qh_fprintf(fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + } +} /* printfacet4geom_nonsimplicial */ + + +/*--------------------------------- + + qh_printfacet4geom_simplicial( fp, facet, color ) + print Geomview 4OFF file for a 4d simplicial facet + prints triangles for unvisited neighbors (qh.visit_id) + + notes: + must agree with printend4geom() +*/ +void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]) { + setT *vertices; + facetT *neighbor, **neighborp; + vertexT *vertex, **vertexp; + int k; + + facet->visitid= qh visit_id; + if (qh PRINTnoplanes || (facet->visible && qh NEWfacets)) + return; + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh visit_id) + continue; + if (qh PRINTtransparent && !neighbor->good) + continue; + vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim, + SETindex_(facet->neighbors, neighbor), 0); + if (qh DOintersections) + qh_printhyperplaneintersection(fp, facet, neighbor, vertices, color); + else { + if (qh DROPdim >= 0) + qh_fprintf(fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n", + facet->id, neighbor->id); + else { + qh printoutvar++; + qh_fprintf(fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id); + } + FOREACHvertex_(vertices) { + for (k=0; k < qh hull_dim; k++) { + if (k != qh DROPdim) + qh_fprintf(fp, 9121, "%8.4g ", vertex->point[k]); + } + qh_fprintf(fp, 9122, "\n"); + } + if (qh DROPdim >= 0) + qh_fprintf(fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + qh_setfree(&vertices); + } +} /* printfacet4geom_simplicial */ + + +/*--------------------------------- + + qh_printfacetNvertex_nonsimplicial( fp, facet, id, format ) + print vertices for an N-d non-simplicial facet + triangulates each ridge to the id +*/ +void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format) { + vertexT *vertex, **vertexp; + ridgeT *ridge, **ridgep; + + if (facet->visible && qh NEWfacets) + return; + FOREACHridge_(facet->ridges) { + if (format == qh_PRINTtriangles) + qh_fprintf(fp, 9124, "%d ", qh hull_dim); + qh_fprintf(fp, 9125, "%d ", id); + if ((ridge->top == facet) ^ qh_ORIENTclock) { + FOREACHvertex_(ridge->vertices) + qh_fprintf(fp, 9126, "%d ", qh_pointid(vertex->point)); + }else { + FOREACHvertexreverse12_(ridge->vertices) + qh_fprintf(fp, 9127, "%d ", qh_pointid(vertex->point)); + } + qh_fprintf(fp, 9128, "\n"); + } +} /* printfacetNvertex_nonsimplicial */ + + +/*--------------------------------- + + qh_printfacetNvertex_simplicial( fp, facet, format ) + print vertices for an N-d simplicial facet + prints vertices for non-simplicial facets + 2-d facets (orientation preserved by qh_mergefacet2d) + PRINToff ('o') for 4-d and higher +*/ +void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format) { + vertexT *vertex, **vertexp; + + if (format == qh_PRINToff || format == qh_PRINTtriangles) + qh_fprintf(fp, 9129, "%d ", qh_setsize(facet->vertices)); + if ((facet->toporient ^ qh_ORIENTclock) + || (qh hull_dim > 2 && !facet->simplicial)) { + FOREACHvertex_(facet->vertices) + qh_fprintf(fp, 9130, "%d ", qh_pointid(vertex->point)); + }else { + FOREACHvertexreverse12_(facet->vertices) + qh_fprintf(fp, 9131, "%d ", qh_pointid(vertex->point)); + } + qh_fprintf(fp, 9132, "\n"); +} /* printfacetNvertex_simplicial */ + + +/*--------------------------------- + + qh_printfacetheader( fp, facet ) + prints header fields of a facet to fp + + notes: + for 'f' output and debugging + Same as QhullFacet::printHeader() +*/ +void qh_printfacetheader(FILE *fp, facetT *facet) { + pointT *point, **pointp, *furthest; + facetT *neighbor, **neighborp; + realT dist; + + if (facet == qh_MERGEridge) { + qh_fprintf(fp, 9133, " MERGEridge\n"); + return; + }else if (facet == qh_DUPLICATEridge) { + qh_fprintf(fp, 9134, " DUPLICATEridge\n"); + return; + }else if (!facet) { + qh_fprintf(fp, 9135, " NULLfacet\n"); + return; + } + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + qh_fprintf(fp, 9136, "- f%d\n", facet->id); + qh_fprintf(fp, 9137, " - flags:"); + if (facet->toporient) + qh_fprintf(fp, 9138, " top"); + else + qh_fprintf(fp, 9139, " bottom"); + if (facet->simplicial) + qh_fprintf(fp, 9140, " simplicial"); + if (facet->tricoplanar) + qh_fprintf(fp, 9141, " tricoplanar"); + if (facet->upperdelaunay) + qh_fprintf(fp, 9142, " upperDelaunay"); + if (facet->visible) + qh_fprintf(fp, 9143, " visible"); + if (facet->newfacet) + qh_fprintf(fp, 9144, " new"); + if (facet->tested) + qh_fprintf(fp, 9145, " tested"); + if (!facet->good) + qh_fprintf(fp, 9146, " notG"); + if (facet->seen) + qh_fprintf(fp, 9147, " seen"); + if (facet->coplanar) + qh_fprintf(fp, 9148, " coplanar"); + if (facet->mergehorizon) + qh_fprintf(fp, 9149, " mergehorizon"); + if (facet->keepcentrum) + qh_fprintf(fp, 9150, " keepcentrum"); + if (facet->dupridge) + qh_fprintf(fp, 9151, " dupridge"); + if (facet->mergeridge && !facet->mergeridge2) + qh_fprintf(fp, 9152, " mergeridge1"); + if (facet->mergeridge2) + qh_fprintf(fp, 9153, " mergeridge2"); + if (facet->newmerge) + qh_fprintf(fp, 9154, " newmerge"); + if (facet->flipped) + qh_fprintf(fp, 9155, " flipped"); + if (facet->notfurthest) + qh_fprintf(fp, 9156, " notfurthest"); + if (facet->degenerate) + qh_fprintf(fp, 9157, " degenerate"); + if (facet->redundant) + qh_fprintf(fp, 9158, " redundant"); + qh_fprintf(fp, 9159, "\n"); + if (facet->isarea) + qh_fprintf(fp, 9160, " - area: %2.2g\n", facet->f.area); + else if (qh NEWfacets && facet->visible && facet->f.replace) + qh_fprintf(fp, 9161, " - replacement: f%d\n", facet->f.replace->id); + else if (facet->newfacet) { + if (facet->f.samecycle && facet->f.samecycle != facet) + qh_fprintf(fp, 9162, " - shares same visible/horizon as f%d\n", facet->f.samecycle->id); + }else if (facet->tricoplanar /* !isarea */) { + if (facet->f.triowner) + qh_fprintf(fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id); + }else if (facet->f.newcycle) + qh_fprintf(fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id); + if (facet->nummerge) + qh_fprintf(fp, 9165, " - merges: %d\n", facet->nummerge); + qh_printpointid(fp, " - normal: ", qh hull_dim, facet->normal, -1); + qh_fprintf(fp, 9166, " - offset: %10.7g\n", facet->offset); + if (qh CENTERtype == qh_ASvoronoi || facet->center) + qh_printcenter(fp, qh_PRINTfacets, " - center: ", facet); +#if qh_MAXoutside + if (facet->maxoutside > qh DISTround) + qh_fprintf(fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside); +#endif + if (!SETempty_(facet->outsideset)) { + furthest= (pointT*)qh_setlast(facet->outsideset); + if (qh_setsize(facet->outsideset) < 6) { + qh_fprintf(fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(furthest)); + FOREACHpoint_(facet->outsideset) + qh_printpoint(fp, " ", point); + }else if (qh_setsize(facet->outsideset) < 21) { + qh_printpoints(fp, " - outside set:", facet->outsideset); + }else { + qh_fprintf(fp, 9169, " - outside set: %d points.", qh_setsize(facet->outsideset)); + qh_printpoint(fp, " Furthest", furthest); + } +#if !qh_COMPUTEfurthest + qh_fprintf(fp, 9170, " - furthest distance= %2.2g\n", facet->furthestdist); +#endif + } + if (!SETempty_(facet->coplanarset)) { + furthest= (pointT*)qh_setlast(facet->coplanarset); + if (qh_setsize(facet->coplanarset) < 6) { + qh_fprintf(fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(furthest)); + FOREACHpoint_(facet->coplanarset) + qh_printpoint(fp, " ", point); + }else if (qh_setsize(facet->coplanarset) < 21) { + qh_printpoints(fp, " - coplanar set:", facet->coplanarset); + }else { + qh_fprintf(fp, 9172, " - coplanar set: %d points.", qh_setsize(facet->coplanarset)); + qh_printpoint(fp, " Furthest", furthest); + } + zinc_(Zdistio); + qh_distplane(furthest, facet, &dist); + qh_fprintf(fp, 9173, " furthest distance= %2.2g\n", dist); + } + qh_printvertices(fp, " - vertices:", facet->vertices); + qh_fprintf(fp, 9174, " - neighboring facets:"); + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) + qh_fprintf(fp, 9175, " MERGE"); + else if (neighbor == qh_DUPLICATEridge) + qh_fprintf(fp, 9176, " DUP"); + else + qh_fprintf(fp, 9177, " f%d", neighbor->id); + } + qh_fprintf(fp, 9178, "\n"); + qh RANDOMdist= qh old_randomdist; +} /* printfacetheader */ + + +/*--------------------------------- + + qh_printfacetridges( fp, facet ) + prints ridges of a facet to fp + + notes: + ridges printed in neighbor order + assumes the ridges exist + for 'f' output + same as QhullFacet::printRidges +*/ +void qh_printfacetridges(FILE *fp, facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int numridges= 0; + + + if (facet->visible && qh NEWfacets) { + qh_fprintf(fp, 9179, " - ridges(ids may be garbage):"); + FOREACHridge_(facet->ridges) + qh_fprintf(fp, 9180, " r%d", ridge->id); + qh_fprintf(fp, 9181, "\n"); + }else { + qh_fprintf(fp, 9182, " - ridges:\n"); + FOREACHridge_(facet->ridges) + ridge->seen= False; + if (qh hull_dim == 3) { + ridge= SETfirstt_(facet->ridges, ridgeT); + while (ridge && !ridge->seen) { + ridge->seen= True; + qh_printridge(fp, ridge); + numridges++; + ridge= qh_nextridge3d(ridge, facet, NULL); + } + }else { + FOREACHneighbor_(facet) { + FOREACHridge_(facet->ridges) { + if (otherfacet_(ridge,facet) == neighbor) { + ridge->seen= True; + qh_printridge(fp, ridge); + numridges++; + } + } + } + } + if (numridges != qh_setsize(facet->ridges)) { + qh_fprintf(fp, 9183, " - all ridges:"); + FOREACHridge_(facet->ridges) + qh_fprintf(fp, 9184, " r%d", ridge->id); + qh_fprintf(fp, 9185, "\n"); + } + FOREACHridge_(facet->ridges) { + if (!ridge->seen) + qh_printridge(fp, ridge); + } + } +} /* printfacetridges */ + +/*--------------------------------- + + qh_printfacets( fp, format, facetlist, facets, printall ) + prints facetlist and/or facet set in output format + + notes: + also used for specialized formats ('FO' and summary) + turns off 'Rn' option since want actual numbers +*/ +void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars; + facetT *facet, **facetp; + setT *vertices; + coordT *center; + realT outerplane, innerplane; + + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + if (qh CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff)) + qh_fprintf(qh ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n"); + if (format == qh_PRINTnone) + ; /* print nothing */ + else if (format == qh_PRINTaverage) { + vertices= qh_facetvertices(facetlist, facets, printall); + center= qh_getcenter(vertices); + qh_fprintf(fp, 9186, "%d 1\n", qh hull_dim); + qh_printpointid(fp, NULL, qh hull_dim, center, -1); + qh_memfree(center, qh normal_size); + qh_settempfree(&vertices); + }else if (format == qh_PRINTextremes) { + if (qh DELAUNAY) + qh_printextremes_d(fp, facetlist, facets, printall); + else if (qh hull_dim == 2) + qh_printextremes_2d(fp, facetlist, facets, printall); + else + qh_printextremes(fp, facetlist, facets, printall); + }else if (format == qh_PRINToptions) + qh_fprintf(fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options); + else if (format == qh_PRINTpoints && !qh VORONOI) + qh_printpoints_out(fp, facetlist, facets, printall); + else if (format == qh_PRINTqhull) + qh_fprintf(fp, 9188, "%s | %s\n", qh rbox_command, qh qhull_command); + else if (format == qh_PRINTsize) { + qh_fprintf(fp, 9189, "0\n2 "); + qh_fprintf(fp, 9190, qh_REAL_1, qh totarea); + qh_fprintf(fp, 9191, qh_REAL_1, qh totvol); + qh_fprintf(fp, 9192, "\n"); + }else if (format == qh_PRINTsummary) { + qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + vertices= qh_facetvertices(facetlist, facets, printall); + qh_fprintf(fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh hull_dim, + qh num_points + qh_setsize(qh other_points), + qh num_vertices, qh num_facets - qh num_visible, + qh_setsize(vertices), numfacets, numcoplanars, + numfacets - numsimplicial, zzval_(Zdelvertextot), + numtricoplanars); + qh_settempfree(&vertices); + qh_outerinner(NULL, &outerplane, &innerplane); + qh_fprintf(fp, 9194, qh_REAL_2n, outerplane, innerplane); + }else if (format == qh_PRINTvneighbors) + qh_printvneighbors(fp, facetlist, facets, printall); + else if (qh VORONOI && format == qh_PRINToff) + qh_printvoronoi(fp, format, facetlist, facets, printall); + else if (qh VORONOI && format == qh_PRINTgeom) { + qh_printbegin(fp, format, facetlist, facets, printall); + qh_printvoronoi(fp, format, facetlist, facets, printall); + qh_printend(fp, format, facetlist, facets, printall); + }else if (qh VORONOI + && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter)) + qh_printvdiagram(fp, format, facetlist, facets, printall); + else { + qh_printbegin(fp, format, facetlist, facets, printall); + FORALLfacet_(facetlist) + qh_printafacet(fp, format, facet, printall); + FOREACHfacet_(facets) + qh_printafacet(fp, format, facet, printall); + qh_printend(fp, format, facetlist, facets, printall); + } + qh RANDOMdist= qh old_randomdist; +} /* printfacets */ + + +/*--------------------------------- + + qh_printhyperplaneintersection( fp, facet1, facet2, vertices, color ) + print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d +*/ +void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2, + setT *vertices, realT color[3]) { + realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4]; + vertexT *vertex, **vertexp; + int i, k; + boolT nearzero1, nearzero2; + + costheta= qh_getangle(facet1->normal, facet2->normal); + denominator= 1 - costheta * costheta; + i= qh_setsize(vertices); + if (qh hull_dim == 3) + qh_fprintf(fp, 9195, "VECT 1 %d 1 %d 1 ", i, i); + else if (qh hull_dim == 4 && qh DROPdim >= 0) + qh_fprintf(fp, 9196, "OFF 3 1 1 "); + else + qh printoutvar++; + qh_fprintf(fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id); + mindenom= 1 / (10.0 * qh MAXabs_coord); + FOREACHvertex_(vertices) { + zadd_(Zdistio, 2); + qh_distplane(vertex->point, facet1, &dist1); + qh_distplane(vertex->point, facet2, &dist2); + s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1); + t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2); + if (nearzero1 || nearzero2) + s= t= 0.0; + for (k=qh hull_dim; k--; ) + p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t; + if (qh PRINTdim <= 3) { + qh_projectdim3 (p, p); + qh_fprintf(fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]); + }else + qh_fprintf(fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]); + if (nearzero1+nearzero2) + qh_fprintf(fp, 9200, "p%d(coplanar facets)\n", qh_pointid(vertex->point)); + else + qh_fprintf(fp, 9201, "projected p%d\n", qh_pointid(vertex->point)); + } + if (qh hull_dim == 3) + qh_fprintf(fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); + else if (qh hull_dim == 4 && qh DROPdim >= 0) + qh_fprintf(fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printhyperplaneintersection */ + +/*--------------------------------- + + qh_printline3geom( fp, pointA, pointB, color ) + prints a line as a VECT + prints 0's for qh.DROPdim + + notes: + if pointA == pointB, + it's a 1 point VECT +*/ +void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) { + int k; + realT pA[4], pB[4]; + + qh_projectdim3(pointA, pA); + qh_projectdim3(pointB, pB); + if ((fabs(pA[0] - pB[0]) > 1e-3) || + (fabs(pA[1] - pB[1]) > 1e-3) || + (fabs(pA[2] - pB[2]) > 1e-3)) { + qh_fprintf(fp, 9204, "VECT 1 2 1 2 1\n"); + for (k=0; k < 3; k++) + qh_fprintf(fp, 9205, "%8.4g ", pB[k]); + qh_fprintf(fp, 9206, " # p%d\n", qh_pointid(pointB)); + }else + qh_fprintf(fp, 9207, "VECT 1 1 1 1 1\n"); + for (k=0; k < 3; k++) + qh_fprintf(fp, 9208, "%8.4g ", pA[k]); + qh_fprintf(fp, 9209, " # p%d\n", qh_pointid(pointA)); + qh_fprintf(fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]); +} + +/*--------------------------------- + + qh_printneighborhood( fp, format, facetA, facetB, printall ) + print neighborhood of one or two facets + + notes: + calls qh_findgood_all() + bumps qh.visit_id +*/ +void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) { + facetT *neighbor, **neighborp, *facet; + setT *facets; + + if (format == qh_PRINTnone) + return; + qh_findgood_all(qh facet_list); + if (facetA == facetB) + facetB= NULL; + facets= qh_settemp(2*(qh_setsize(facetA->neighbors)+1)); + qh visit_id++; + for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) { + if (facet->visitid != qh visit_id) { + facet->visitid= qh visit_id; + qh_setappend(&facets, facet); + } + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh visit_id) + continue; + neighbor->visitid= qh visit_id; + if (printall || !qh_skipfacet(neighbor)) + qh_setappend(&facets, neighbor); + } + } + qh_printfacets(fp, format, NULL, facets, printall); + qh_settempfree(&facets); +} /* printneighborhood */ + +/*--------------------------------- + + qh_printpoint( fp, string, point ) + qh_printpointid( fp, string, dim, point, id ) + prints the coordinates of a point + + returns: + if string is defined + prints 'string p%d' (skips p%d if id=-1) + + notes: + nop if point is NULL + prints id unless it is undefined (-1) + Same as QhullPoint's printPoint +*/ +void qh_printpoint(FILE *fp, const char *string, pointT *point) { + int id= qh_pointid( point); + + qh_printpointid( fp, string, qh hull_dim, point, id); +} /* printpoint */ + +void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id) { + int k; + realT r; /*bug fix*/ + + if (!point) + return; + if (string) { + qh_fprintf(fp, 9211, "%s", string); + if (id != -1) + qh_fprintf(fp, 9212, " p%d: ", id); + } + for (k=dim; k--; ) { + r= *point++; + if (string) + qh_fprintf(fp, 9213, " %8.4g", r); + else + qh_fprintf(fp, 9214, qh_REAL_1, r); + } + qh_fprintf(fp, 9215, "\n"); +} /* printpointid */ + +/*--------------------------------- + + qh_printpoint3( fp, point ) + prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates +*/ +void qh_printpoint3 (FILE *fp, pointT *point) { + int k; + realT p[4]; + + qh_projectdim3 (point, p); + for (k=0; k < 3; k++) + qh_fprintf(fp, 9216, "%8.4g ", p[k]); + qh_fprintf(fp, 9217, " # p%d\n", qh_pointid(point)); +} /* printpoint3 */ + +/*---------------------------------------- +-printpoints- print pointids for a set of points starting at index + see geom.c +*/ + +/*--------------------------------- + + qh_printpoints_out( fp, facetlist, facets, printall ) + prints vertices, coplanar/inside points, for facets by their point coordinates + allows qh.CDDoutput + + notes: + same format as qhull input + if no coplanar/interior points, + same order as qh_printextremes +*/ +void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + int allpoints= qh num_points + qh_setsize(qh other_points); + int numpoints=0, point_i, point_n; + setT *vertices, *points; + facetT *facet, **facetp; + pointT *point, **pointp; + vertexT *vertex, **vertexp; + int id; + + points= qh_settemp(allpoints); + qh_setzero(points, 0, allpoints); + vertices= qh_facetvertices(facetlist, facets, printall); + FOREACHvertex_(vertices) { + id= qh_pointid(vertex->point); + if (id >= 0) + SETelem_(points, id)= vertex->point; + } + if (qh KEEPinside || qh KEEPcoplanar || qh KEEPnearinside) { + FORALLfacet_(facetlist) { + if (!printall && qh_skipfacet(facet)) + continue; + FOREACHpoint_(facet->coplanarset) { + id= qh_pointid(point); + if (id >= 0) + SETelem_(points, id)= point; + } + } + FOREACHfacet_(facets) { + if (!printall && qh_skipfacet(facet)) + continue; + FOREACHpoint_(facet->coplanarset) { + id= qh_pointid(point); + if (id >= 0) + SETelem_(points, id)= point; + } + } + } + qh_settempfree(&vertices); + FOREACHpoint_i_(points) { + if (point) + numpoints++; + } + if (qh CDDoutput) + qh_fprintf(fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh rbox_command, + qh qhull_command, numpoints, qh hull_dim + 1); + else + qh_fprintf(fp, 9219, "%d\n%d\n", qh hull_dim, numpoints); + FOREACHpoint_i_(points) { + if (point) { + if (qh CDDoutput) + qh_fprintf(fp, 9220, "1 "); + qh_printpoint(fp, NULL, point); + } + } + if (qh CDDoutput) + qh_fprintf(fp, 9221, "end\n"); + qh_settempfree(&points); +} /* printpoints_out */ + + +/*--------------------------------- + + qh_printpointvect( fp, point, normal, center, radius, color ) + prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point +*/ +void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) { + realT diff[4], pointA[4]; + int k; + + for (k=qh hull_dim; k--; ) { + if (center) + diff[k]= point[k]-center[k]; + else if (normal) + diff[k]= normal[k]; + else + diff[k]= 0; + } + if (center) + qh_normalize2 (diff, qh hull_dim, True, NULL, NULL); + for (k=qh hull_dim; k--; ) + pointA[k]= point[k]+diff[k] * radius; + qh_printline3geom(fp, point, pointA, color); +} /* printpointvect */ + +/*--------------------------------- + + qh_printpointvect2( fp, point, normal, center, radius ) + prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point +*/ +void qh_printpointvect2 (FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) { + realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0}; + + qh_printpointvect(fp, point, normal, center, radius, red); + qh_printpointvect(fp, point, normal, center, -radius, yellow); +} /* printpointvect2 */ + +/*--------------------------------- + + qh_printridge( fp, ridge ) + prints the information in a ridge + + notes: + for qh_printfacetridges() + same as operator<< [QhullRidge.cpp] +*/ +void qh_printridge(FILE *fp, ridgeT *ridge) { + + qh_fprintf(fp, 9222, " - r%d", ridge->id); + if (ridge->tested) + qh_fprintf(fp, 9223, " tested"); + if (ridge->nonconvex) + qh_fprintf(fp, 9224, " nonconvex"); + qh_fprintf(fp, 9225, "\n"); + qh_printvertices(fp, " vertices:", ridge->vertices); + if (ridge->top && ridge->bottom) + qh_fprintf(fp, 9226, " between f%d and f%d\n", + ridge->top->id, ridge->bottom->id); +} /* printridge */ + +/*--------------------------------- + + qh_printspheres( fp, vertices, radius ) + prints 3-d vertices as OFF spheres + + notes: + inflated octahedron from Stuart Levy earth/mksphere2 +*/ +void qh_printspheres(FILE *fp, setT *vertices, realT radius) { + vertexT *vertex, **vertexp; + + qh printoutnum++; + qh_fprintf(fp, 9227, "{appearance {-edge -normal normscale 0} {\n\ +INST geom {define vsphere OFF\n\ +18 32 48\n\ +\n\ +0 0 1\n\ +1 0 0\n\ +0 1 0\n\ +-1 0 0\n\ +0 -1 0\n\ +0 0 -1\n\ +0.707107 0 0.707107\n\ +0 -0.707107 0.707107\n\ +0.707107 -0.707107 0\n\ +-0.707107 0 0.707107\n\ +-0.707107 -0.707107 0\n\ +0 0.707107 0.707107\n\ +-0.707107 0.707107 0\n\ +0.707107 0.707107 0\n\ +0.707107 0 -0.707107\n\ +0 0.707107 -0.707107\n\ +-0.707107 0 -0.707107\n\ +0 -0.707107 -0.707107\n\ +\n\ +3 0 6 11\n\ +3 0 7 6 \n\ +3 0 9 7 \n\ +3 0 11 9\n\ +3 1 6 8 \n\ +3 1 8 14\n\ +3 1 13 6\n\ +3 1 14 13\n\ +3 2 11 13\n\ +3 2 12 11\n\ +3 2 13 15\n\ +3 2 15 12\n\ +3 3 9 12\n\ +3 3 10 9\n\ +3 3 12 16\n\ +3 3 16 10\n\ +3 4 7 10\n\ +3 4 8 7\n\ +3 4 10 17\n\ +3 4 17 8\n\ +3 5 14 17\n\ +3 5 15 14\n\ +3 5 16 15\n\ +3 5 17 16\n\ +3 6 13 11\n\ +3 7 8 6\n\ +3 9 10 7\n\ +3 11 12 9\n\ +3 14 8 17\n\ +3 15 13 14\n\ +3 16 12 15\n\ +3 17 10 16\n} transforms { TLIST\n"); + FOREACHvertex_(vertices) { + qh_fprintf(fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n", + radius, vertex->id, radius, radius); + qh_printpoint3 (fp, vertex->point); + qh_fprintf(fp, 9229, "1\n"); + } + qh_fprintf(fp, 9230, "}}}\n"); +} /* printspheres */ + + +/*---------------------------------------------- +-printsummary- + see libqhull.c +*/ + +/*--------------------------------- + + qh_printvdiagram( fp, format, facetlist, facets, printall ) + print voronoi diagram + # of pairs of input sites + #indices site1 site2 vertex1 ... + + sites indexed by input point id + point 0 is the first input point + vertices indexed by 'o' and 'p' order + vertex 0 is the 'vertex-at-infinity' + vertex 1 is the first Voronoi vertex + + see: + qh_printvoronoi() + qh_eachvoronoi_all() + + notes: + if all facets are upperdelaunay, + prints upper hull (furthest-site Voronoi diagram) +*/ +void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices; + int totcount, numcenters; + boolT isLower; + qh_RIDGE innerouter= qh_RIDGEall; + printvridgeT printvridge= NULL; + + if (format == qh_PRINTvertices) { + innerouter= qh_RIDGEall; + printvridge= qh_printvridge; + }else if (format == qh_PRINTinner) { + innerouter= qh_RIDGEinner; + printvridge= qh_printvnorm; + }else if (format == qh_PRINTouter) { + innerouter= qh_RIDGEouter; + printvridge= qh_printvnorm; + }else { + qh_fprintf(qh ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format); + qh_errexit(qh_ERRinput, NULL, NULL); + } + vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters); + totcount= qh_printvdiagram2 (NULL, NULL, vertices, innerouter, False); + qh_fprintf(fp, 9231, "%d\n", totcount); + totcount= qh_printvdiagram2 (fp, printvridge, vertices, innerouter, True /* inorder*/); + qh_settempfree(&vertices); +#if 0 /* for testing qh_eachvoronoi_all */ + qh_fprintf(fp, 9232, "\n"); + totcount= qh_eachvoronoi_all(fp, printvridge, qh UPPERdelaunay, innerouter, True /* inorder*/); + qh_fprintf(fp, 9233, "%d\n", totcount); +#endif +} /* printvdiagram */ + +/*--------------------------------- + + qh_printvdiagram2( fp, printvridge, vertices, innerouter, inorder ) + visit all pairs of input sites (vertices) for selected Voronoi vertices + vertices may include NULLs + + innerouter: + qh_RIDGEall print inner ridges(bounded) and outer ridges(unbounded) + qh_RIDGEinner print only inner ridges + qh_RIDGEouter print only outer ridges + + inorder: + print 3-d Voronoi vertices in order + + assumes: + qh_markvoronoi marked facet->visitid for Voronoi vertices + all facet->seen= False + all facet->seen2= True + + returns: + total number of Voronoi ridges + if printvridge, + calls printvridge( fp, vertex, vertexA, centers) for each ridge + [see qh_eachvoronoi()] + + see: + qh_eachvoronoi_all() +*/ +int qh_printvdiagram2 (FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) { + int totcount= 0; + int vertex_i, vertex_n; + vertexT *vertex; + + FORALLvertices + vertex->seen= False; + FOREACHvertex_i_(vertices) { + if (vertex) { + if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex) + continue; + totcount += qh_eachvoronoi(fp, printvridge, vertex, !qh_ALL, innerouter, inorder); + } + } + return totcount; +} /* printvdiagram2 */ + +/*--------------------------------- + + qh_printvertex( fp, vertex ) + prints the information in a vertex + Duplicated as operator<< [QhullVertex.cpp] +*/ +void qh_printvertex(FILE *fp, vertexT *vertex) { + pointT *point; + int k, count= 0; + facetT *neighbor, **neighborp; + realT r; /*bug fix*/ + + if (!vertex) { + qh_fprintf(fp, 9234, " NULLvertex\n"); + return; + } + qh_fprintf(fp, 9235, "- p%d(v%d):", qh_pointid(vertex->point), vertex->id); + point= vertex->point; + if (point) { + for (k=qh hull_dim; k--; ) { + r= *point++; + qh_fprintf(fp, 9236, " %5.2g", r); + } + } + if (vertex->deleted) + qh_fprintf(fp, 9237, " deleted"); + if (vertex->delridge) + qh_fprintf(fp, 9238, " ridgedeleted"); + qh_fprintf(fp, 9239, "\n"); + if (vertex->neighbors) { + qh_fprintf(fp, 9240, " neighbors:"); + FOREACHneighbor_(vertex) { + if (++count % 100 == 0) + qh_fprintf(fp, 9241, "\n "); + qh_fprintf(fp, 9242, " f%d", neighbor->id); + } + qh_fprintf(fp, 9243, "\n"); + } +} /* printvertex */ + + +/*--------------------------------- + + qh_printvertexlist( fp, string, facetlist, facets, printall ) + prints vertices used by a facetlist or facet set + tests qh_skipfacet() if !printall +*/ +void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist, + setT *facets, boolT printall) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facetvertices(facetlist, facets, printall); + qh_fprintf(fp, 9244, "%s", string); + FOREACHvertex_(vertices) + qh_printvertex(fp, vertex); + qh_settempfree(&vertices); +} /* printvertexlist */ + + +/*--------------------------------- + + qh_printvertices( fp, string, vertices ) + prints vertices in a set + duplicated as printVertexSet [QhullVertex.cpp] +*/ +void qh_printvertices(FILE *fp, const char* string, setT *vertices) { + vertexT *vertex, **vertexp; + + qh_fprintf(fp, 9245, "%s", string); + FOREACHvertex_(vertices) + qh_fprintf(fp, 9246, " p%d(v%d)", qh_pointid(vertex->point), vertex->id); + qh_fprintf(fp, 9247, "\n"); +} /* printvertices */ + +/*--------------------------------- + + qh_printvneighbors( fp, facetlist, facets, printall ) + print vertex neighbors of vertices in facetlist and facets ('FN') + + notes: + qh_countfacets clears facet->visitid for non-printed facets + + design: + collect facet count and related statistics + if necessary, build neighbor sets for each vertex + collect vertices in facetlist and facets + build a point array for point->vertex and point->coplanar facet + for each point + list vertex neighbors or coplanar facet +*/ +void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall) { + int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars; + setT *vertices, *vertex_points, *coplanar_points; + int numpoints= qh num_points + qh_setsize(qh other_points); + vertexT *vertex, **vertexp; + int vertex_i, vertex_n; + facetT *facet, **facetp, *neighbor, **neighborp; + pointT *point, **pointp; + + qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* sets facet->visitid */ + qh_fprintf(fp, 9248, "%d\n", numpoints); + qh_vertexneighbors(); + vertices= qh_facetvertices(facetlist, facets, printall); + vertex_points= qh_settemp(numpoints); + coplanar_points= qh_settemp(numpoints); + qh_setzero(vertex_points, 0, numpoints); + qh_setzero(coplanar_points, 0, numpoints); + FOREACHvertex_(vertices) + qh_point_add(vertex_points, vertex->point, vertex); + FORALLfacet_(facetlist) { + FOREACHpoint_(facet->coplanarset) + qh_point_add(coplanar_points, point, facet); + } + FOREACHfacet_(facets) { + FOREACHpoint_(facet->coplanarset) + qh_point_add(coplanar_points, point, facet); + } + FOREACHvertex_i_(vertex_points) { + if (vertex) { + numneighbors= qh_setsize(vertex->neighbors); + qh_fprintf(fp, 9249, "%d", numneighbors); + if (qh hull_dim == 3) + qh_order_vertexneighbors(vertex); + else if (qh hull_dim >= 4) + qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors, + sizeof(facetT *), qh_compare_facetvisit); + FOREACHneighbor_(vertex) + qh_fprintf(fp, 9250, " %d", + neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id); + qh_fprintf(fp, 9251, "\n"); + }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT))) + qh_fprintf(fp, 9252, "1 %d\n", + facet->visitid ? facet->visitid - 1 : 0 - facet->id); + else + qh_fprintf(fp, 9253, "0\n"); + } + qh_settempfree(&coplanar_points); + qh_settempfree(&vertex_points); + qh_settempfree(&vertices); +} /* printvneighbors */ + +/*--------------------------------- + + qh_printvoronoi( fp, format, facetlist, facets, printall ) + print voronoi diagram in 'o' or 'G' format + for 'o' format + prints voronoi centers for each facet and for infinity + for each vertex, lists ids of printed facets or infinity + assumes facetlist and facets are disjoint + for 'G' format + prints an OFF object + adds a 0 coordinate to center + prints infinity but does not list in vertices + + see: + qh_printvdiagram() + + notes: + if 'o', + prints a line for each point except "at-infinity" + if all facets are upperdelaunay, + reverses lower and upper hull +*/ +void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n; + facetT *facet, **facetp, *neighbor, **neighborp; + setT *vertices; + vertexT *vertex; + boolT isLower; + unsigned int numfacets= (unsigned int) qh num_facets; + + vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters); + FOREACHvertex_i_(vertices) { + if (vertex) { + numvertices++; + numneighbors = numinf = 0; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) + numinf= 1; + else if (neighbor->visitid < numfacets) + numneighbors++; + } + if (numinf && !numneighbors) { + SETelem_(vertices, vertex_i)= NULL; + numvertices--; + } + } + } + if (format == qh_PRINTgeom) + qh_fprintf(fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n", + numcenters, numvertices); + else + qh_fprintf(fp, 9255, "%d\n%d %d 1\n", qh hull_dim-1, numcenters, qh_setsize(vertices)); + if (format == qh_PRINTgeom) { + for (k=qh hull_dim-1; k--; ) + qh_fprintf(fp, 9256, qh_REAL_1, 0.0); + qh_fprintf(fp, 9257, " 0 # infinity not used\n"); + }else { + for (k=qh hull_dim-1; k--; ) + qh_fprintf(fp, 9258, qh_REAL_1, qh_INFINITE); + qh_fprintf(fp, 9259, "\n"); + } + FORALLfacet_(facetlist) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + qh_fprintf(fp, 9260, "# %d f%d\n", vid++, facet->id); + qh_printcenter(fp, format, NULL, facet); + } + } + FOREACHfacet_(facets) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + qh_fprintf(fp, 9261, "# %d f%d\n", vid++, facet->id); + qh_printcenter(fp, format, NULL, facet); + } + } + FOREACHvertex_i_(vertices) { + numneighbors= 0; + numinf=0; + if (vertex) { + if (qh hull_dim == 3) + qh_order_vertexneighbors(vertex); + else if (qh hull_dim >= 4) + qsort(SETaddr_(vertex->neighbors, facetT), + (size_t)qh_setsize(vertex->neighbors), + sizeof(facetT *), qh_compare_facetvisit); + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) + numinf= 1; + else if (neighbor->visitid < numfacets) + numneighbors++; + } + } + if (format == qh_PRINTgeom) { + if (vertex) { + qh_fprintf(fp, 9262, "%d", numneighbors); + FOREACHneighbor_(vertex) { + if (neighbor->visitid && neighbor->visitid < numfacets) + qh_fprintf(fp, 9263, " %d", neighbor->visitid); + } + qh_fprintf(fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id); + }else + qh_fprintf(fp, 9265, " # p%d is coplanar or isolated\n", vertex_i); + }else { + if (numinf) + numneighbors++; + qh_fprintf(fp, 9266, "%d", numneighbors); + if (vertex) { + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) { + if (numinf) { + numinf= 0; + qh_fprintf(fp, 9267, " %d", neighbor->visitid); + } + }else if (neighbor->visitid < numfacets) + qh_fprintf(fp, 9268, " %d", neighbor->visitid); + } + } + qh_fprintf(fp, 9269, "\n"); + } + } + if (format == qh_PRINTgeom) + qh_fprintf(fp, 9270, "}\n"); + qh_settempfree(&vertices); +} /* printvoronoi */ + +/*--------------------------------- + + qh_printvnorm( fp, vertex, vertexA, centers, unbounded ) + print one separating plane of the Voronoi diagram for a pair of input sites + unbounded==True if centers includes vertex-at-infinity + + assumes: + qh_ASvoronoi and qh_vertexneighbors() already set + + note: + parameter unbounded is UNUSED by this callback + + see: + qh_printvdiagram() + qh_eachvoronoi() +*/ +void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + pointT *normal; + realT offset; + int k; + QHULL_UNUSED(unbounded); + + normal= qh_detvnorm(vertex, vertexA, centers, &offset); + qh_fprintf(fp, 9271, "%d %d %d ", + 2+qh hull_dim, qh_pointid(vertex->point), qh_pointid(vertexA->point)); + for (k=0; k< qh hull_dim-1; k++) + qh_fprintf(fp, 9272, qh_REAL_1, normal[k]); + qh_fprintf(fp, 9273, qh_REAL_1, offset); + qh_fprintf(fp, 9274, "\n"); +} /* printvnorm */ + +/*--------------------------------- + + qh_printvridge( fp, vertex, vertexA, centers, unbounded ) + print one ridge of the Voronoi diagram for a pair of input sites + unbounded==True if centers includes vertex-at-infinity + + see: + qh_printvdiagram() + + notes: + the user may use a different function + parameter unbounded is UNUSED +*/ +void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + facetT *facet, **facetp; + QHULL_UNUSED(unbounded); + + qh_fprintf(fp, 9275, "%d %d %d", qh_setsize(centers)+2, + qh_pointid(vertex->point), qh_pointid(vertexA->point)); + FOREACHfacet_(centers) + qh_fprintf(fp, 9276, " %d", facet->visitid); + qh_fprintf(fp, 9277, "\n"); +} /* printvridge */ + +/*--------------------------------- + + qh_projectdim3( source, destination ) + project 2-d 3-d or 4-d point to a 3-d point + uses qh.DROPdim and qh.hull_dim + source and destination may be the same + + notes: + allocate 4 elements to destination just in case +*/ +void qh_projectdim3 (pointT *source, pointT *destination) { + int i,k; + + for (k=0, i=0; k < qh hull_dim; k++) { + if (qh hull_dim == 4) { + if (k != qh DROPdim) + destination[i++]= source[k]; + }else if (k == qh DROPdim) + destination[i++]= 0; + else + destination[i++]= source[k]; + } + while (i < 3) + destination[i++]= 0.0; +} /* projectdim3 */ + +/*--------------------------------- + + qh_readfeasible( dim, curline ) + read feasible point from current line and qh.fin + + returns: + number of lines read from qh.fin + sets qh.FEASIBLEpoint with malloc'd coordinates + + notes: + checks for qh.HALFspace + assumes dim > 1 + + see: + qh_setfeasible +*/ +int qh_readfeasible(int dim, const char *curline) { + boolT isfirst= True; + int linecount= 0, tokcount= 0; + const char *s; + char *t, firstline[qh_MAXfirst+1]; + coordT *coords, value; + + if (!qh HALFspace) { + qh_fprintf(qh ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh feasible_string) + qh_fprintf(qh ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n"); + if (!(qh feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) { + qh_fprintf(qh ferr, 6071, "qhull error: insufficient memory for feasible point\n"); + qh_errexit(qh_ERRmem, NULL, NULL); + } + coords= qh feasible_point; + while ((s= (isfirst ? curline : fgets(firstline, qh_MAXfirst, qh fin)))) { + if (isfirst) + isfirst= False; + else + linecount++; + while (*s) { + while (isspace(*s)) + s++; + value= qh_strtod(s, &t); + if (s == t) + break; + s= t; + *(coords++)= value; + if (++tokcount == dim) { + while (isspace(*s)) + s++; + qh_strtod(s, &t); + if (s != t) { + qh_fprintf(qh ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n", + s); + qh_errexit(qh_ERRinput, NULL, NULL); + } + return linecount; + } + } + } + qh_fprintf(qh ferr, 6073, "qhull input error: only %d coordinates. Could not read %d-d feasible point.\n", + tokcount, dim); + qh_errexit(qh_ERRinput, NULL, NULL); + return 0; +} /* readfeasible */ + +/*--------------------------------- + + qh_readpoints( numpoints, dimension, ismalloc ) + read points from qh.fin into qh.first_point, qh.num_points + qh.fin is lines of coordinates, one per vertex, first line number of points + if 'rbox D4', + gives message + if qh.ATinfinity, + adds point-at-infinity for Delaunay triangulations + + returns: + number of points, array of point coordinates, dimension, ismalloc True + if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid + and clears qh.PROJECTdelaunay + if qh.HALFspace, reads optional feasible point, reads halfspaces, + converts to dual. + + for feasible point in "cdd format" in 3-d: + 3 1 + coordinates + comments + begin + n 4 real/integer + ... + end + + notes: + dimension will change in qh_initqhull_globals if qh.PROJECTinput + uses malloc() since qh_mem not initialized + FIXUP QH11012: qh_readpoints needs rewriting, too long +*/ +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) { + coordT *points, *coords, *infinity= NULL; + realT paraboloid, maxboloid= -REALmax, value; + realT *coordp= NULL, *offsetp= NULL, *normalp= NULL; + char *s= 0, *t, firstline[qh_MAXfirst+1]; + int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi; + int firsttext=0, firstshort=0, firstlong=0, firstpoint=0; + int tokcount= 0, linecount=0, maxcount, coordcount=0; + boolT islong, isfirst= True, wasbegin= False; + boolT isdelaunay= qh DELAUNAY && !qh PROJECTinput; + + if (qh CDDinput) { + while ((s= fgets(firstline, qh_MAXfirst, qh fin))) { + linecount++; + if (qh HALFspace && linecount == 1 && isdigit(*s)) { + dimfeasible= qh_strtol(s, &s); + while (isspace(*s)) + s++; + if (qh_strtol(s, &s) == 1) + linecount += qh_readfeasible(dimfeasible, s); + else + dimfeasible= 0; + }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5)) + break; + else if (!*qh rbox_command) + strncat(qh rbox_command, s, sizeof(qh rbox_command)-1); + } + if (!s) { + qh_fprintf(qh ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + } + while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh fin))) { + linecount++; + if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5)) + wasbegin= True; + while (*s) { + while (isspace(*s)) + s++; + if (!*s) + break; + if (!isdigit(*s)) { + if (!*qh rbox_command) { + strncat(qh rbox_command, s, sizeof(qh rbox_command)-1); + firsttext= linecount; + } + break; + } + if (!diminput) + diminput= qh_strtol(s, &s); + else { + numinput= qh_strtol(s, &s); + if (numinput == 1 && diminput >= 2 && qh HALFspace && !qh CDDinput) { + linecount += qh_readfeasible(diminput, s); /* checks if ok */ + dimfeasible= diminput; + diminput= numinput= 0; + }else + break; + } + } + } + if (!s) { + qh_fprintf(qh ferr, 6075, "qhull input error: short input file. Did not find dimension and number of points\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (diminput > numinput) { + tempi= diminput; /* exchange dim and n, e.g., for cdd input format */ + diminput= numinput; + numinput= tempi; + } + if (diminput < 2) { + qh_fprintf(qh ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n", + diminput); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (isdelaunay) { + qh PROJECTdelaunay= False; + if (qh CDDinput) + *dimension= diminput; + else + *dimension= diminput+1; + *numpoints= numinput; + if (qh ATinfinity) + (*numpoints)++; + }else if (qh HALFspace) { + *dimension= diminput - 1; + *numpoints= numinput; + if (diminput < 3) { + qh_fprintf(qh ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n", + diminput); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (dimfeasible) { + if (dimfeasible != *dimension) { + qh_fprintf(qh ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n", + dimfeasible, diminput); + qh_errexit(qh_ERRinput, NULL, NULL); + } + }else + qh_setfeasible(*dimension); + }else { + if (qh CDDinput) + *dimension= diminput-1; + else + *dimension= diminput; + *numpoints= numinput; + } + qh normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */ + if (qh HALFspace) { + qh half_space= coordp= (coordT*)qh_malloc(qh normal_size + sizeof(coordT)); + if (qh CDDinput) { + offsetp= qh half_space; + normalp= offsetp + 1; + }else { + normalp= qh half_space; + offsetp= normalp + *dimension; + } + } + qh maxline= diminput * (qh_REALdigits + 5); + maximize_(qh maxline, 500); + qh line= (char*)qh_malloc((qh maxline+1) * sizeof(char)); + *ismalloc= True; /* use malloc since memory not setup */ + coords= points= qh temp_malloc= + (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT)); + if (!coords || !qh line || (qh HALFspace && !qh half_space)) { + qh_fprintf(qh ferr, 6076, "qhull error: insufficient memory to read %d points\n", + numinput); + qh_errexit(qh_ERRmem, NULL, NULL); + } + if (isdelaunay && qh ATinfinity) { + infinity= points + numinput * (*dimension); + for (k= (*dimension) - 1; k--; ) + infinity[k]= 0.0; + } + maxcount= numinput * diminput; + paraboloid= 0.0; + while ((s= (isfirst ? s : fgets(qh line, qh maxline, qh fin)))) { + if (!isfirst) { + linecount++; + if (*s == 'e' || *s == 'E') { + if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) { + if (qh CDDinput ) + break; + else if (wasbegin) + qh_fprintf(qh ferr, 7058, "qhull input warning: the input appears to be in cdd format. If so, use 'Fd'\n"); + } + } + } + islong= False; + while (*s) { + while (isspace(*s)) + s++; + value= qh_strtod(s, &t); + if (s == t) { + if (!*qh rbox_command) + strncat(qh rbox_command, s, sizeof(qh rbox_command)-1); + if (*s && !firsttext) + firsttext= linecount; + if (!islong && !firstshort && coordcount) + firstshort= linecount; + break; + } + if (!firstpoint) + firstpoint= linecount; + s= t; + if (++tokcount > maxcount) + continue; + if (qh HALFspace) { + if (qh CDDinput) + *(coordp++)= -value; /* both coefficients and offset */ + else + *(coordp++)= value; + }else { + *(coords++)= value; + if (qh CDDinput && !coordcount) { + if (value != 1.0) { + qh_fprintf(qh ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n", + linecount); + qh_errexit(qh_ERRinput, NULL, NULL); + } + coords--; + }else if (isdelaunay) { + paraboloid += value * value; + if (qh ATinfinity) { + if (qh CDDinput) + infinity[coordcount-1] += value; + else + infinity[coordcount] += value; + } + } + } + if (++coordcount == diminput) { + coordcount= 0; + if (isdelaunay) { + *(coords++)= paraboloid; + maximize_(maxboloid, paraboloid); + paraboloid= 0.0; + }else if (qh HALFspace) { + if (!qh_sethalfspace(*dimension, coords, &coords, normalp, offsetp, qh feasible_point)) { + qh_fprintf(qh ferr, 8048, "The halfspace was on line %d\n", linecount); + if (wasbegin) + qh_fprintf(qh ferr, 8049, "The input appears to be in cdd format. If so, you should use option 'Fd'\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + coordp= qh half_space; + } + while (isspace(*s)) + s++; + if (*s) { + islong= True; + if (!firstlong) + firstlong= linecount; + } + } + } + if (!islong && !firstshort && coordcount) + firstshort= linecount; + if (!isfirst && s - qh line >= qh maxline) { + qh_fprintf(qh ferr, 6078, "qhull input error: line %d contained more than %d characters\n", + linecount, (int) (s - qh line)); /* WARN64 */ + qh_errexit(qh_ERRinput, NULL, NULL); + } + isfirst= False; + } + if (tokcount != maxcount) { + newnum= fmin_(numinput, tokcount/diminput); + qh_fprintf(qh ferr, 7073,"\ +qhull warning: instead of %d %d-dimensional points, input contains\n\ +%d points and %d extra coordinates. Line %d is the first\npoint", + numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint); + if (firsttext) + qh_fprintf(qh ferr, 8051, ", line %d is the first comment", firsttext); + if (firstshort) + qh_fprintf(qh ferr, 8052, ", line %d is the first short\nline", firstshort); + if (firstlong) + qh_fprintf(qh ferr, 8053, ", line %d is the first long line", firstlong); + qh_fprintf(qh ferr, 8054, ". Continue with %d points.\n", newnum); + numinput= newnum; + if (isdelaunay && qh ATinfinity) { + for (k= tokcount % diminput; k--; ) + infinity[k] -= *(--coords); + *numpoints= newnum+1; + }else { + coords -= tokcount % diminput; + *numpoints= newnum; + } + } + if (isdelaunay && qh ATinfinity) { + for (k= (*dimension) -1; k--; ) + infinity[k] /= numinput; + if (coords == infinity) + coords += (*dimension) -1; + else { + for (k=0; k < (*dimension) -1; k++) + *(coords++)= infinity[k]; + } + *(coords++)= maxboloid * 1.1; + } + if (qh rbox_command[0]) { + qh rbox_command[strlen(qh rbox_command)-1]= '\0'; + if (!strcmp(qh rbox_command, "./rbox D4")) + qh_fprintf(qh ferr, 8055, "\n\ +This is the qhull test case. If any errors or core dumps occur,\n\ +recompile qhull with 'make new'. If errors still occur, there is\n\ +an incompatibility. You should try a different compiler. You can also\n\ +change the choices in user.h. If you discover the source of the problem,\n\ +please send mail to qhull_bug@qhull.org.\n\ +\n\ +Type 'qhull' for a short list of options.\n"); + } + qh_free(qh line); + qh line= NULL; + if (qh half_space) { + qh_free(qh half_space); + qh half_space= NULL; + } + qh temp_malloc= NULL; + trace1((qh ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n", + numinput, diminput)); + return(points); +} /* readpoints */ + + +/*--------------------------------- + + qh_setfeasible( dim ) + set qh.FEASIBLEpoint from qh.feasible_string in "n,n,n" or "n n n" format + + notes: + "n,n,n" already checked by qh_initflags() + see qh_readfeasible() +*/ +void qh_setfeasible(int dim) { + int tokcount= 0; + char *s; + coordT *coords, value; + + if (!(s= qh feasible_string)) { + qh_fprintf(qh ferr, 6223, "\ +qhull input error: halfspace intersection needs a feasible point.\n\ +Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (!(qh feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) { + qh_fprintf(qh ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh_ERRmem, NULL, NULL); + } + coords= qh feasible_point; + while (*s) { + value= qh_strtod(s, &s); + if (++tokcount > dim) { + qh_fprintf(qh ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n", + qh feasible_string, dim); + break; + } + *(coords++)= value; + if (*s) + s++; + } + while (++tokcount <= dim) + *(coords++)= 0.0; +} /* setfeasible */ + +/*--------------------------------- + + qh_skipfacet( facet ) + returns 'True' if this facet is not to be printed + + notes: + based on the user provided slice thresholds and 'good' specifications +*/ +boolT qh_skipfacet(facetT *facet) { + facetT *neighbor, **neighborp; + + if (qh PRINTneighbors) { + if (facet->good) + return !qh PRINTgood; + FOREACHneighbor_(facet) { + if (neighbor->good) + return False; + } + return True; + }else if (qh PRINTgood) + return !facet->good; + else if (!facet->normal) + return True; + return(!qh_inthresholds(facet->normal, NULL)); +} /* skipfacet */ + +/*--------------------------------- + + qh_skipfilename( string ) + returns pointer to character after filename + + notes: + skips leading spaces + ends with spacing or eol + if starts with ' or " ends with the same, skipping \' or \" + For qhull, qh_argv_to_command() only uses double quotes +*/ +char *qh_skipfilename(char *filename) { + char *s= filename; /* non-const due to return */ + char c; + + while (*s && isspace(*s)) + s++; + c= *s++; + if (c == '\0') { + qh_fprintf(qh ferr, 6204, "qhull input error: filename expected, none found.\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (c == '\'' || c == '"') { + while (*s !=c || s[-1] == '\\') { + if (!*s) { + qh_fprintf(qh ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename); + qh_errexit(qh_ERRinput, NULL, NULL); + } + s++; + } + s++; + } + else while (*s && !isspace(*s)) + s++; + return s; +} /* skipfilename */ diff --git a/extern/qhull/io.h b/extern/qhull/io.h new file mode 100644 index 000000000000..580d51b9b3fd --- /dev/null +++ b/extern/qhull/io.h @@ -0,0 +1,159 @@ +/*--------------------------------- + + io.h + declarations of Input/Output functions + + see README, libqhull.h and io.c + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/io.h#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#ifndef qhDEFio +#define qhDEFio 1 + +#include "libqhull.h" + +/*============ constants and flags ==================*/ + +/*---------------------------------- + + qh_MAXfirst + maximum length of first two lines of stdin +*/ +#define qh_MAXfirst 200 + +/*---------------------------------- + + qh_MINradius + min radius for Gp and Gv, fraction of maxcoord +*/ +#define qh_MINradius 0.02 + +/*---------------------------------- + + qh_GEOMepsilon + adjust outer planes for 'lines closer' and geomview roundoff. + This prevents bleed through. +*/ +#define qh_GEOMepsilon 2e-3 + +/*---------------------------------- + + qh_WHITESPACE + possible values of white space +*/ +#define qh_WHITESPACE " \n\t\v\r\f" + + +/*---------------------------------- + + qh_RIDGE + to select which ridges to print in qh_eachvoronoi +*/ +typedef enum +{ + qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter +} +qh_RIDGE; + +/*---------------------------------- + + printvridgeT + prints results of qh_printvdiagram + + see: + qh_printvridge for an example +*/ +typedef void (*printvridgeT)(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); + +/*============== -prototypes in alphabetical order =========*/ + +void dfacet(unsigned id); +void dvertex(unsigned id); +int qh_compare_facetarea(const void *p1, const void *p2); +int qh_compare_facetmerge(const void *p1, const void *p2); +int qh_compare_facetvisit(const void *p1, const void *p2); +int qh_compare_vertexpoint(const void *p1, const void *p2); /* not used */ +void qh_copyfilename(char *filename, int size, const char* source, int length); +void qh_countfacets(facetT *facetlist, setT *facets, boolT printall, + int *numfacetsp, int *numsimplicialp, int *totneighborsp, + int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp); +pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp); +setT *qh_detvridge(vertexT *vertex); +setT *qh_detvridge3 (vertexT *atvertex, vertexT *vertex); +int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder); +int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder); +void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist); +setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets); +void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane); +void qh_markkeep(facetT *facetlist); +setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp); +void qh_order_vertexneighbors(vertexT *vertex); +void qh_prepare_output(void); +void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall); +void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet); +void qh_printcentrum(FILE *fp, facetT *facet, realT radius); +void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printend4geom(FILE *fp, facetT *facet, int *num, boolT printall); +void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printfacet(FILE *fp, facetT *facet); +void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst); +void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2, + facetT *facet, realT offset, realT color[3]); +void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst); +void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]); +void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format); +void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format); +void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format); +void qh_printfacetheader(FILE *fp, facetT *facet); +void qh_printfacetridges(FILE *fp, facetT *facet); +void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2, + setT *vertices, realT color[3]); +void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); +void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]); +void qh_printpoint(FILE *fp, const char *string, pointT *point); +void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id); +void qh_printpoint3 (FILE *fp, pointT *point); +void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]); +void qh_printpointvect2 (FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius); +void qh_printridge(FILE *fp, ridgeT *ridge); +void qh_printspheres(FILE *fp, setT *vertices, realT radius); +void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +int qh_printvdiagram2 (FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder); +void qh_printvertex(FILE *fp, vertexT *vertex); +void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist, + setT *facets, boolT printall); +void qh_printvertices(FILE *fp, const char* string, setT *vertices); +void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall); +void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); +void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); +void qh_produce_output(void); +void qh_produce_output2(void); +void qh_projectdim3 (pointT *source, pointT *destination); +int qh_readfeasible(int dim, const char *curline); +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc); +void qh_setfeasible(int dim); +boolT qh_skipfacet(facetT *facet); +char *qh_skipfilename(char *filename); + +#endif /* qhDEFio */ diff --git a/extern/qhull/libqhull.c b/extern/qhull/libqhull.c new file mode 100644 index 000000000000..34a0fc611289 --- /dev/null +++ b/extern/qhull/libqhull.c @@ -0,0 +1,1399 @@ +/*--------------------------------- + + libqhull.c + Quickhull algorithm for convex hulls + + qhull() and top-level routines + + see qh-qhull.htm, libqhull.h, unix.c + + see qhull_a.h for internal functions + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/libqhull.c#4 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*============= functions in alphabetic order after qhull() =======*/ + +/*--------------------------------- + + qh_qhull() + compute DIM3 convex hull of qh.num_points starting at qh.first_point + qh contains all global options and variables + + returns: + returns polyhedron + qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices, + + returns global variables + qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex + + returns precision constants + qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge + + notes: + unless needed for output + qh.max_vertex and qh.min_vertex are max/min due to merges + + see: + to add individual points to either qh.num_points + use qh_addpoint() + + if qh.GETarea + qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea() + + design: + record starting time + initialize hull and partition points + build convex hull + unless early termination + update facet->maxoutside for vertices, coplanar, and near-inside points + error if temporary sets exist + record end time +*/ + +void qh_qhull(void) { + int numoutside; + + qh hulltime= qh_CPUclock; + if (qh RERUN || qh JOGGLEmax < REALmax/2) + qh_build_withrestart(); + else { + qh_initbuild(); + qh_buildhull(); + } + if (!qh STOPpoint && !qh STOPcone) { + if (qh ZEROall_ok && !qh TESTvneighbors && qh MERGEexact) + qh_checkzero( qh_ALL); + if (qh ZEROall_ok && !qh TESTvneighbors && !qh WAScoplanar) { + trace2((qh ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points. Post-merging and check of maxout not needed.\n")); + qh DOcheckmax= False; + }else { + if (qh MERGEexact || (qh hull_dim > qh_DIMreduceBuild && qh PREmerge)) + qh_postmerge("First post-merge", qh premerge_centrum, qh premerge_cos, + (qh POSTmerge ? False : qh TESTvneighbors)); + else if (!qh POSTmerge && qh TESTvneighbors) + qh_postmerge("For testing vertex neighbors", qh premerge_centrum, + qh premerge_cos, True); + if (qh POSTmerge) + qh_postmerge("For post-merging", qh postmerge_centrum, + qh postmerge_cos, qh TESTvneighbors); + if (qh visible_list == qh facet_list) { /* i.e., merging done */ + qh findbestnew= True; + qh_partitionvisible(/*visible_list, newfacet_list*/ !qh_ALL, &numoutside); + qh findbestnew= False; + qh_deletevisible(/*qh visible_list*/); + qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */); + } + } + if (qh DOcheckmax){ + if (qh REPORTfreq) { + qh_buildtracing(NULL, NULL); + qh_fprintf(qh ferr, 8115, "\nTesting all coplanar points.\n"); + } + qh_check_maxout(); + } + if (qh KEEPnearinside && !qh maxoutdone) + qh_nearcoplanar(); + } + if (qh_setsize(qhmem.tempstack) != 0) { + qh_fprintf(qh ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n", + qh_setsize(qhmem.tempstack)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh hulltime= qh_CPUclock - qh hulltime; + qh QHULLfinished= True; + trace1((qh ferr, 1036, "Qhull: algorithm completed\n")); +} /* qhull */ + +/*--------------------------------- + + qh_addpoint( furthest, facet, checkdist ) + add point (usually furthest point) above facet to hull + if checkdist, + check that point is above facet. + if point is not outside of the hull, uses qh_partitioncoplanar() + assumes that facet is defined by qh_findbestfacet() + else if facet specified, + assumes that point is above facet (major damage if below) + for Delaunay triangulations, + Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates. + + returns: + returns False if user requested an early termination + qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside) + if unknown point, adds a pointer to qh.other_points + do not deallocate the point's coordinates + + notes: + assumes point is near its best facet and not at a local minimum of a lens + distributions. Use qh_findbestfacet to avoid this case. + uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets + + see also: + qh_triangulate() -- triangulate non-simplicial facets + + design: + add point to other_points if needed + if checkdist + if point not above facet + partition coplanar point + exit + exit if pre STOPpoint requested + find horizon and visible facets for point + make new facets for point to horizon + make hyperplanes for point + compute balance statistics + match neighboring new facets + update vertex neighbors and delete interior vertices + exit if STOPcone requested + merge non-convex new facets + if merge found, many merges, or 'Qf' + use qh_findbestnew() instead of qh_findbest() + partition outside points from visible facets + delete visible facets + check polyhedron if requested + exit if post STOPpoint requested + reset working lists of facets and vertices +*/ +boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) { + int goodvisible, goodhorizon; + vertexT *vertex; + facetT *newfacet; + realT dist, newbalance, pbalance; + boolT isoutside= False; + int numpart, numpoints, numnew, firstnew; + + qh maxoutdone= False; + if (qh_pointid(furthest) == -1) + qh_setappend(&qh other_points, furthest); + if (!facet) { + qh_fprintf(qh ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (checkdist) { + facet= qh_findbest(furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper, + &dist, &isoutside, &numpart); + zzadd_(Zpartition, numpart); + if (!isoutside) { + zinc_(Znotmax); /* last point of outsideset is no longer furthest. */ + facet->notfurthest= True; + qh_partitioncoplanar(furthest, facet, &dist); + return True; + } + } + qh_buildtracing(furthest, facet); + if (qh STOPpoint < 0 && qh furthest_id == -qh STOPpoint-1) { + facet->notfurthest= True; + return False; + } + qh_findhorizon(furthest, facet, &goodvisible, &goodhorizon); + if (qh ONLYgood && !(goodvisible+goodhorizon) && !qh GOODclosest) { + zinc_(Znotgood); + facet->notfurthest= True; + /* last point of outsideset is no longer furthest. This is ok + since all points of the outside are likely to be bad */ + qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */); + return True; + } + zzinc_(Zprocessed); + firstnew= qh facet_id; + vertex= qh_makenewfacets(furthest /*visible_list, attaches if !ONLYgood */); + qh_makenewplanes(/* newfacet_list */); + numnew= qh facet_id - firstnew; + newbalance= numnew - (realT) (qh num_facets-qh num_visible) + * qh hull_dim/qh num_vertices; + wadd_(Wnewbalance, newbalance); + wadd_(Wnewbalance2, newbalance * newbalance); + if (qh ONLYgood + && !qh_findgood(qh newfacet_list, goodhorizon) && !qh GOODclosest) { + FORALLnew_facets + qh_delfacet(newfacet); + qh_delvertex(vertex); + qh_resetlists(True, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */); + zinc_(Znotgoodnew); + facet->notfurthest= True; + return True; + } + if (qh ONLYgood) + qh_attachnewfacets(/*visible_list*/); + qh_matchnewfacets(); + qh_updatevertices(); + if (qh STOPcone && qh furthest_id == qh STOPcone-1) { + facet->notfurthest= True; + return False; /* visible_list etc. still defined */ + } + qh findbestnew= False; + if (qh PREmerge || qh MERGEexact) { + qh_premerge(vertex, qh premerge_centrum, qh premerge_cos); + if (qh_USEfindbestnew) + qh findbestnew= True; + else { + FORALLnew_facets { + if (!newfacet->simplicial) { + qh findbestnew= True; /* use qh_findbestnew instead of qh_findbest*/ + break; + } + } + } + }else if (qh BESToutside) + qh findbestnew= True; + qh_partitionvisible(/*visible_list, newfacet_list*/ !qh_ALL, &numpoints); + qh findbestnew= False; + qh findbest_notsharp= False; + zinc_(Zpbalance); + pbalance= numpoints - (realT) qh hull_dim /* assumes all points extreme */ + * (qh num_points - qh num_vertices)/qh num_vertices; + wadd_(Wpbalance, pbalance); + wadd_(Wpbalance2, pbalance * pbalance); + qh_deletevisible(/*qh visible_list*/); + zmax_(Zmaxvertex, qh num_vertices); + qh NEWfacets= False; + if (qh IStracing >= 4) { + if (qh num_facets < 2000) + qh_printlists(); + qh_printfacetlist(qh newfacet_list, NULL, True); + qh_checkpolygon(qh facet_list); + }else if (qh CHECKfrequently) { + if (qh num_facets < 50) + qh_checkpolygon(qh facet_list); + else + qh_checkpolygon(qh newfacet_list); + } + if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1) + return False; + qh_resetlists(True, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */); + /* qh_triangulate(); to test qh.TRInormals */ + trace2((qh ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n", + qh_pointid(furthest), numnew, newbalance, pbalance)); + return True; +} /* addpoint */ + +/*--------------------------------- + + qh_build_withrestart() + allow restarts due to qh.JOGGLEmax while calling qh_buildhull() + qh.FIRSTpoint/qh.NUMpoints is point array + it may be moved by qh_joggleinput() +*/ +void qh_build_withrestart(void) { + int restart; + + qh ALLOWrestart= True; + while (True) { + restart= setjmp(qh restartexit); /* simple statement for CRAY J916 */ + if (restart) { /* only from qh_precision() */ + zzinc_(Zretry); + wmax_(Wretrymax, qh JOGGLEmax); + /* QH7078 warns about using 'TCn' with 'QJn' */ + qh STOPcone= -1; /* if break from joggle, prevents normal output */ + } + if (!qh RERUN && qh JOGGLEmax < REALmax/2) { + if (qh build_cnt > qh_JOGGLEmaxretry) { + qh_fprintf(qh ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\ + with joggled input. Increase joggle above 'QJ%2.2g'\n\ + or modify qh_JOGGLE... parameters in user.h\n", + qh build_cnt, qh JOGGLEmax); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (qh build_cnt && !restart) + break; + }else if (qh build_cnt && qh build_cnt >= qh RERUN) + break; + qh STOPcone= 0; + qh_freebuild(True); /* first call is a nop */ + qh build_cnt++; + if (!qh qhull_optionsiz) + qh qhull_optionsiz= (int)strlen(qh qhull_options); /* WARN64 */ + else { + qh qhull_options [qh qhull_optionsiz]= '\0'; + qh qhull_optionlen= qh_OPTIONline; /* starts a new line */ + } + qh_option("_run", &qh build_cnt, NULL); + if (qh build_cnt == qh RERUN) { + qh IStracing= qh TRACElastrun; /* duplicated from qh_initqhull_globals */ + if (qh TRACEpoint != -1 || qh TRACEdist < REALmax/2 || qh TRACEmerge) { + qh TRACElevel= (qh IStracing? qh IStracing : 3); + qh IStracing= 0; + } + qhmem.IStracing= qh IStracing; + } + if (qh JOGGLEmax < REALmax/2) + qh_joggleinput(); + qh_initbuild(); + qh_buildhull(); + if (qh JOGGLEmax < REALmax/2 && !qh MERGING) + qh_checkconvex(qh facet_list, qh_ALGORITHMfault); + } + qh ALLOWrestart= False; +} /* qh_build_withrestart */ + +/*--------------------------------- + + qh_buildhull() + construct a convex hull by adding outside points one at a time + + returns: + + notes: + may be called multiple times + checks facet and vertex lists for incorrect flags + to recover from STOPcone, call qh_deletevisible and qh_resetlists + + design: + check visible facet and newfacet flags + check newlist vertex flags and qh.STOPcone/STOPpoint + for each facet with a furthest outside point + add point to facet + exit if qh.STOPcone or qh.STOPpoint requested + if qh.NARROWhull for initial simplex + partition remaining outside points to coplanar sets +*/ +void qh_buildhull(void) { + facetT *facet; + pointT *furthest; + vertexT *vertex; + int id; + + trace1((qh ferr, 1037, "qh_buildhull: start build hull\n")); + FORALLfacets { + if (facet->visible || facet->newfacet) { + qh_fprintf(qh ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n", + facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + } + FORALLvertices { + if (vertex->newlist) { + qh_fprintf(qh ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n", + vertex->id); + qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + id= qh_pointid(vertex->point); + if ((qh STOPpoint>0 && id == qh STOPpoint-1) || + (qh STOPpoint<0 && id == -qh STOPpoint-1) || + (qh STOPcone>0 && id == qh STOPcone-1)) { + trace1((qh ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id)); + return; + } + } + qh facet_next= qh facet_list; /* advance facet when processed */ + while ((furthest= qh_nextfurthest(&facet))) { + qh num_outside--; /* if ONLYmax, furthest may not be outside */ + if (!qh_addpoint(furthest, facet, qh ONLYmax)) + break; + } + if (qh NARROWhull) /* move points from outsideset to coplanarset */ + qh_outcoplanar( /* facet_list */ ); + if (qh num_outside && !furthest) { + qh_fprintf(qh ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh num_outside); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + trace1((qh ferr, 1039, "qh_buildhull: completed the hull construction\n")); +} /* buildhull */ + + +/*--------------------------------- + + qh_buildtracing( furthest, facet ) + trace an iteration of qh_buildhull() for furthest point and facet + if !furthest, prints progress message + + returns: + tracks progress with qh.lastreport + updates qh.furthest_id (-3 if furthest is NULL) + also resets visit_id, vertext_visit on wrap around + + see: + qh_tracemerging() + + design: + if !furthest + print progress message + exit + if 'TFn' iteration + print progress message + else if tracing + trace furthest point and facet + reset qh.visit_id and qh.vertex_visit if overflow may occur + set qh.furthest_id for tracing +*/ +void qh_buildtracing(pointT *furthest, facetT *facet) { + realT dist= 0; + float cpu; + int total, furthestid; + time_t timedata; + struct tm *tp; + vertexT *vertex; + + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + if (!furthest) { + time(&timedata); + tp= localtime(&timedata); + cpu= (float)qh_CPUclock - (float)qh hulltime; + cpu /= (float)qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + qh_fprintf(qh ferr, 8118, "\n\ +At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\ + The current hull contains %d facets and %d vertices. Last point was p%d\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1, + total, qh num_facets, qh num_vertices, qh furthest_id); + return; + } + furthestid= qh_pointid(furthest); + if (qh TRACEpoint == furthestid) { + qh IStracing= qh TRACElevel; + qhmem.IStracing= qh TRACElevel; + }else if (qh TRACEpoint != -1 && qh TRACEdist < REALmax/2) { + qh IStracing= 0; + qhmem.IStracing= 0; + } + if (qh REPORTfreq && (qh facet_id-1 > qh lastreport+qh REPORTfreq)) { + qh lastreport= qh facet_id-1; + time(&timedata); + tp= localtime(&timedata); + cpu= (float)qh_CPUclock - (float)qh hulltime; + cpu /= (float)qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + zinc_(Zdistio); + qh_distplane(furthest, facet, &dist); + qh_fprintf(qh ferr, 8119, "\n\ +At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\ + The current hull contains %d facets and %d vertices. There are %d\n\ + outside points. Next is point p%d(v%d), %2.2g above f%d.\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1, + total, qh num_facets, qh num_vertices, qh num_outside+1, + furthestid, qh vertex_id, dist, getid_(facet)); + }else if (qh IStracing >=1) { + cpu= (float)qh_CPUclock - (float)qh hulltime; + cpu /= (float)qh_SECticks; + qh_distplane(furthest, facet, &dist); + qh_fprintf(qh ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs. Previous was p%d.\n", + furthestid, qh vertex_id, qh num_facets, dist, + getid_(facet), qh num_outside+1, cpu, qh furthest_id); + } + zmax_(Zvisit2max, (int)qh visit_id/2); + if (qh visit_id > (unsigned) INT_MAX) { + zinc_(Zvisit); + qh visit_id= 0; + FORALLfacets + facet->visitid= 0; + } + zmax_(Zvvisit2max, (int)qh vertex_visit/2); + if (qh vertex_visit > (unsigned) INT_MAX/2) { /* 31 bits */ + zinc_(Zvvisit); + qh vertex_visit= 0; + FORALLvertices + vertex->visitid= 0; + } + qh furthest_id= furthestid; + qh RANDOMdist= qh old_randomdist; +} /* buildtracing */ + +/*--------------------------------- + + qh_errexit2( exitcode, facet, otherfacet ) + return exitcode to system after an error + report two facets + + returns: + assumes exitcode non-zero + + see: + normally use qh_errexit() in user.c(reports a facet and a ridge) +*/ +void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet) { + + qh_errprint("ERRONEOUS", facet, otherfacet, NULL, NULL); + qh_errexit(exitcode, NULL, NULL); +} /* errexit2 */ + + +/*--------------------------------- + + qh_findhorizon( point, facet, goodvisible, goodhorizon ) + given a visible facet, find the point's horizon and visible facets + for all facets, !facet-visible + + returns: + returns qh.visible_list/num_visible with all visible facets + marks visible facets with ->visible + updates count of good visible and good horizon facets + updates qh.max_outside, qh.max_vertex, facet->maxoutside + + see: + similar to qh_delpoint() + + design: + move facet to qh.visible_list at end of qh.facet_list + for all visible facets + for each unvisited neighbor of a visible facet + compute distance of point to neighbor + if point above neighbor + move neighbor to end of qh.visible_list + else if point is coplanar with neighbor + update qh.max_outside, qh.max_vertex, neighbor->maxoutside + mark neighbor coplanar (will create a samecycle later) + update horizon statistics +*/ +void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) { + facetT *neighbor, **neighborp, *visible; + int numhorizon= 0, coplanar= 0; + realT dist; + + trace1((qh ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(point),facet->id)); + *goodvisible= *goodhorizon= 0; + zinc_(Ztotvisible); + qh_removefacet(facet); /* visible_list at end of qh facet_list */ + qh_appendfacet(facet); + qh num_visible= 1; + if (facet->good) + (*goodvisible)++; + qh visible_list= facet; + facet->visible= True; + facet->f.replace= NULL; + if (qh IStracing >=4) + qh_errprint("visible", facet, NULL, NULL, NULL); + qh visit_id++; + FORALLvisible_facets { + if (visible->tricoplanar && !qh TRInormals) { + qh_fprintf(qh ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh_ERRqhull, visible, NULL); + } + visible->visitid= qh visit_id; + FOREACHneighbor_(visible) { + if (neighbor->visitid == qh visit_id) + continue; + neighbor->visitid= qh visit_id; + zzinc_(Znumvisibility); + qh_distplane(point, neighbor, &dist); + if (dist > qh MINvisible) { + zinc_(Ztotvisible); + qh_removefacet(neighbor); /* append to end of qh visible_list */ + qh_appendfacet(neighbor); + neighbor->visible= True; + neighbor->f.replace= NULL; + qh num_visible++; + if (neighbor->good) + (*goodvisible)++; + if (qh IStracing >=4) + qh_errprint("visible", neighbor, NULL, NULL, NULL); + }else { + if (dist > - qh MAXcoplanar) { + neighbor->coplanar= True; + zzinc_(Zcoplanarhorizon); + qh_precision("coplanar horizon"); + coplanar++; + if (qh MERGING) { + if (dist > 0) { + maximize_(qh max_outside, dist); + maximize_(qh max_vertex, dist); +#if qh_MAXoutside + maximize_(neighbor->maxoutside, dist); +#endif + }else + minimize_(qh min_vertex, dist); /* due to merge later */ + } + trace2((qh ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh MINvisible(%2.7g)\n", + qh_pointid(point), neighbor->id, dist, qh MINvisible)); + }else + neighbor->coplanar= False; + zinc_(Ztothorizon); + numhorizon++; + if (neighbor->good) + (*goodhorizon)++; + if (qh IStracing >=4) + qh_errprint("horizon", neighbor, NULL, NULL, NULL); + } + } + } + if (!numhorizon) { + qh_precision("empty horizon"); + qh_fprintf(qh ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\ +QhullPoint p%d was above all facets.\n", qh_pointid(point)); + qh_printfacetlist(qh facet_list, NULL, True); + qh_errexit(qh_ERRprec, NULL, NULL); + } + trace1((qh ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n", + numhorizon, *goodhorizon, qh num_visible, *goodvisible, coplanar)); + if (qh IStracing >= 4 && qh num_facets < 50) + qh_printlists(); +} /* findhorizon */ + +/*--------------------------------- + + qh_nextfurthest( visible ) + returns next furthest point and visible facet for qh_addpoint() + starts search at qh.facet_next + + returns: + removes furthest point from outside set + NULL if none available + advances qh.facet_next over facets with empty outside sets + + design: + for each facet from qh.facet_next + if empty outside set + advance qh.facet_next + else if qh.NARROWhull + determine furthest outside point + if furthest point is not outside + advance qh.facet_next(point will be coplanar) + remove furthest point from outside set +*/ +pointT *qh_nextfurthest(facetT **visible) { + facetT *facet; + int size, idx; + realT randr, dist; + pointT *furthest; + + while ((facet= qh facet_next) != qh facet_tail) { + if (!facet->outsideset) { + qh facet_next= facet->next; + continue; + } + SETreturnsize_(facet->outsideset, size); + if (!size) { + qh_setfree(&facet->outsideset); + qh facet_next= facet->next; + continue; + } + if (qh NARROWhull) { + if (facet->notfurthest) + qh_furthestout(facet); + furthest= (pointT*)qh_setlast(facet->outsideset); +#if qh_COMPUTEfurthest + qh_distplane(furthest, facet, &dist); + zinc_(Zcomputefurthest); +#else + dist= facet->furthestdist; +#endif + if (dist < qh MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */ + qh facet_next= facet->next; + continue; + } + } + if (!qh RANDOMoutside && !qh VIRTUALmemory) { + if (qh PICKfurthest) { + qh_furthestnext(/* qh facet_list */); + facet= qh facet_next; + } + *visible= facet; + return((pointT*)qh_setdellast(facet->outsideset)); + } + if (qh RANDOMoutside) { + int outcoplanar = 0; + if (qh NARROWhull) { + FORALLfacets { + if (facet == qh facet_next) + break; + if (facet->outsideset) + outcoplanar += qh_setsize( facet->outsideset); + } + } + randr= qh_RANDOMint; + randr= randr/(qh_RANDOMmax+1); + idx= (int)floor((qh num_outside - outcoplanar) * randr); + FORALLfacet_(qh facet_next) { + if (facet->outsideset) { + SETreturnsize_(facet->outsideset, size); + if (!size) + qh_setfree(&facet->outsideset); + else if (size > idx) { + *visible= facet; + return((pointT*)qh_setdelnth(facet->outsideset, idx)); + }else + idx -= size; + } + } + qh_fprintf(qh ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n", + qh num_outside, idx+1, randr); + qh_errexit(qh_ERRqhull, NULL, NULL); + }else { /* VIRTUALmemory */ + facet= qh facet_tail->previous; + if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) { + if (facet->outsideset) + qh_setfree(&facet->outsideset); + qh_removefacet(facet); + qh_prependfacet(facet, &qh facet_list); + continue; + } + *visible= facet; + return furthest; + } + } + return NULL; +} /* nextfurthest */ + +/*--------------------------------- + + qh_partitionall( vertices, points, numpoints ) + partitions all points in points/numpoints to the outsidesets of facets + vertices= vertices in qh.facet_list(!partitioned) + + returns: + builds facet->outsideset + does not partition qh.GOODpoint + if qh.ONLYgood && !qh.MERGING, + does not partition qh.GOODvertex + + notes: + faster if qh.facet_list sorted by anticipated size of outside set + + design: + initialize pointset with all points + remove vertices from pointset + remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint) + for all facets + for all remaining points in pointset + compute distance from point to facet + if point is outside facet + remove point from pointset (by not reappending) + update bestpoint + append point or old bestpoint to facet's outside set + append bestpoint to facet's outside set (furthest) + for all points remaining in pointset + partition point into facets' outside sets and coplanar sets +*/ +void qh_partitionall(setT *vertices, pointT *points, int numpoints){ + setT *pointset; + vertexT *vertex, **vertexp; + pointT *point, **pointp, *bestpoint; + int size, point_i, point_n, point_end, remaining, i, id; + facetT *facet; + realT bestdist= -REALmax, dist, distoutside; + + trace1((qh ferr, 1042, "qh_partitionall: partition all points into outside sets\n")); + pointset= qh_settemp(numpoints); + qh num_outside= 0; + pointp= SETaddr_(pointset, pointT); + for (i=numpoints, point= points; i--; point += qh hull_dim) + *(pointp++)= point; + qh_settruncate(pointset, numpoints); + FOREACHvertex_(vertices) { + if ((id= qh_pointid(vertex->point)) >= 0) + SETelem_(pointset, id)= NULL; + } + id= qh_pointid(qh GOODpointp); + if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id) + SETelem_(pointset, id)= NULL; + if (qh GOODvertexp && qh ONLYgood && !qh MERGING) { /* matches qhull()*/ + if ((id= qh_pointid(qh GOODvertexp)) >= 0) + SETelem_(pointset, id)= NULL; + } + if (!qh BESToutside) { /* matches conditional for qh_partitionpoint below */ + distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */ + zval_(Ztotpartition)= qh num_points - qh hull_dim - 1; /*misses GOOD... */ + remaining= qh num_facets; + point_end= numpoints; + FORALLfacets { + size= point_end/(remaining--) + 100; + facet->outsideset= qh_setnew(size); + bestpoint= NULL; + point_end= 0; + FOREACHpoint_i_(pointset) { + if (point) { + zzinc_(Zpartitionall); + qh_distplane(point, facet, &dist); + if (dist < distoutside) + SETelem_(pointset, point_end++)= point; + else { + qh num_outside++; + if (!bestpoint) { + bestpoint= point; + bestdist= dist; + }else if (dist > bestdist) { + qh_setappend(&facet->outsideset, bestpoint); + bestpoint= point; + bestdist= dist; + }else + qh_setappend(&facet->outsideset, point); + } + } + } + if (bestpoint) { + qh_setappend(&facet->outsideset, bestpoint); +#if !qh_COMPUTEfurthest + facet->furthestdist= bestdist; +#endif + }else + qh_setfree(&facet->outsideset); + qh_settruncate(pointset, point_end); + } + } + /* if !qh BESToutside, pointset contains points not assigned to outsideset */ + if (qh BESToutside || qh MERGING || qh KEEPcoplanar || qh KEEPinside) { + qh findbestnew= True; + FOREACHpoint_i_(pointset) { + if (point) + qh_partitionpoint(point, qh facet_list); + } + qh findbestnew= False; + } + zzadd_(Zpartitionall, zzval_(Zpartition)); + zzval_(Zpartition)= 0; + qh_settempfree(&pointset); + if (qh IStracing >= 4) + qh_printfacetlist(qh facet_list, NULL, True); +} /* partitionall */ + + +/*--------------------------------- + + qh_partitioncoplanar( point, facet, dist ) + partition coplanar point to a facet + dist is distance from point to facet + if dist NULL, + searches for bestfacet and does nothing if inside + if qh.findbestnew set, + searches new facets instead of using qh_findbest() + + returns: + qh.max_ouside updated + if qh.KEEPcoplanar or qh.KEEPinside + point assigned to best coplanarset + + notes: + facet->maxoutside is updated at end by qh_check_maxout + + design: + if dist undefined + find best facet for point + if point sufficiently below facet (depends on qh.NEARinside and qh.KEEPinside) + exit + if keeping coplanar/nearinside/inside points + if point is above furthest coplanar point + append point to coplanar set (it is the new furthest) + update qh.max_outside + else + append point one before end of coplanar set + else if point is clearly outside of qh.max_outside and bestfacet->coplanarset + and bestfacet is more than perpendicular to facet + repartition the point using qh_findbest() -- it may be put on an outsideset + else + update qh.max_outside +*/ +void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) { + facetT *bestfacet; + pointT *oldfurthest; + realT bestdist, dist2= 0, angle; + int numpart= 0, oldfindbest; + boolT isoutside; + + qh WAScoplanar= True; + if (!dist) { + if (qh findbestnew) + bestfacet= qh_findbestnew(point, facet, &bestdist, qh_ALL, &isoutside, &numpart); + else + bestfacet= qh_findbest(point, facet, qh_ALL, !qh_ISnewfacets, qh DELAUNAY, + &bestdist, &isoutside, &numpart); + zinc_(Ztotpartcoplanar); + zzadd_(Zpartcoplanar, numpart); + if (!qh DELAUNAY && !qh KEEPinside) { /* for 'd', bestdist skips upperDelaunay facets */ + if (qh KEEPnearinside) { + if (bestdist < -qh NEARinside) { + zinc_(Zcoplanarinside); + trace4((qh ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n", + qh_pointid(point), bestfacet->id, bestdist, qh findbestnew)); + return; + } + }else if (bestdist < -qh MAXcoplanar) { + trace4((qh ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n", + qh_pointid(point), bestfacet->id, bestdist, qh findbestnew)); + zinc_(Zcoplanarinside); + return; + } + } + }else { + bestfacet= facet; + bestdist= *dist; + } + if (bestdist > qh max_outside) { + if (!dist && facet != bestfacet) { + zinc_(Zpartangle); + angle= qh_getangle(facet->normal, bestfacet->normal); + if (angle < 0) { + /* typically due to deleted vertex and coplanar facets, e.g., + RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */ + zinc_(Zpartflip); + trace2((qh ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d. It is above flipped facet f%d dist %2.2g\n", + qh_pointid(point), facet->id, bestfacet->id, bestdist)); + oldfindbest= qh findbestnew; + qh findbestnew= False; + qh_partitionpoint(point, bestfacet); + qh findbestnew= oldfindbest; + return; + } + } + qh max_outside= bestdist; + if (bestdist > qh TRACEdist) { + qh_fprintf(qh ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n", + qh_pointid(point), facet->id, bestdist, bestfacet->id, qh furthest_id); + qh_errprint("DISTANT", facet, bestfacet, NULL, NULL); + } + } + if (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside) { + oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset); + if (oldfurthest) { + zinc_(Zcomputefurthest); + qh_distplane(oldfurthest, bestfacet, &dist2); + } + if (!oldfurthest || dist2 < bestdist) + qh_setappend(&bestfacet->coplanarset, point); + else + qh_setappend2ndlast(&bestfacet->coplanarset, point); + } + trace4((qh ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n", + qh_pointid(point), bestfacet->id, bestdist)); +} /* partitioncoplanar */ + +/*--------------------------------- + + qh_partitionpoint( point, facet ) + assigns point to an outside set, coplanar set, or inside set (i.e., dropt) + if qh.findbestnew + uses qh_findbestnew() to search all new facets + else + uses qh_findbest() + + notes: + after qh_distplane(), this and qh_findbest() are most expensive in 3-d + + design: + find best facet for point + (either exhaustive search of new facets or directed search from facet) + if qh.NARROWhull + retain coplanar and nearinside points as outside points + if point is outside bestfacet + if point above furthest point for bestfacet + append point to outside set (it becomes the new furthest) + if outside set was empty + move bestfacet to end of qh.facet_list (i.e., after qh.facet_next) + update bestfacet->furthestdist + else + append point one before end of outside set + else if point is coplanar to bestfacet + if keeping coplanar points or need to update qh.max_outside + partition coplanar point into bestfacet + else if near-inside point + partition as coplanar point into bestfacet + else is an inside point + if keeping inside points + partition as coplanar point into bestfacet +*/ +void qh_partitionpoint(pointT *point, facetT *facet) { + realT bestdist; + boolT isoutside; + facetT *bestfacet; + int numpart; +#if qh_COMPUTEfurthest + realT dist; +#endif + + if (qh findbestnew) + bestfacet= qh_findbestnew(point, facet, &bestdist, qh BESToutside, &isoutside, &numpart); + else + bestfacet= qh_findbest(point, facet, qh BESToutside, qh_ISnewfacets, !qh_NOupper, + &bestdist, &isoutside, &numpart); + zinc_(Ztotpartition); + zzadd_(Zpartition, numpart); + if (qh NARROWhull) { + if (qh DELAUNAY && !isoutside && bestdist >= -qh MAXcoplanar) + qh_precision("nearly incident point(narrow hull)"); + if (qh KEEPnearinside) { + if (bestdist >= -qh NEARinside) + isoutside= True; + }else if (bestdist >= -qh MAXcoplanar) + isoutside= True; + } + + if (isoutside) { + if (!bestfacet->outsideset + || !qh_setlast(bestfacet->outsideset)) { + qh_setappend(&(bestfacet->outsideset), point); + if (!bestfacet->newfacet) { + qh_removefacet(bestfacet); /* make sure it's after qh facet_next */ + qh_appendfacet(bestfacet); + } +#if !qh_COMPUTEfurthest + bestfacet->furthestdist= bestdist; +#endif + }else { +#if qh_COMPUTEfurthest + zinc_(Zcomputefurthest); + qh_distplane(oldfurthest, bestfacet, &dist); + if (dist < bestdist) + qh_setappend(&(bestfacet->outsideset), point); + else + qh_setappend2ndlast(&(bestfacet->outsideset), point); +#else + if (bestfacet->furthestdist < bestdist) { + qh_setappend(&(bestfacet->outsideset), point); + bestfacet->furthestdist= bestdist; + }else + qh_setappend2ndlast(&(bestfacet->outsideset), point); +#endif + } + qh num_outside++; + trace4((qh ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n", + qh_pointid(point), bestfacet->id, bestfacet->newfacet)); + }else if (qh DELAUNAY || bestdist >= -qh MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */ + zzinc_(Zcoplanarpart); + if (qh DELAUNAY) + qh_precision("nearly incident point"); + if ((qh KEEPcoplanar + qh KEEPnearinside) || bestdist > qh max_outside) + qh_partitioncoplanar(point, bestfacet, &bestdist); + else { + trace4((qh ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n", + qh_pointid(point), bestfacet->id)); + } + }else if (qh KEEPnearinside && bestdist > -qh NEARinside) { + zinc_(Zpartnear); + qh_partitioncoplanar(point, bestfacet, &bestdist); + }else { + zinc_(Zpartinside); + trace4((qh ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n", + qh_pointid(point), bestfacet->id, bestdist)); + if (qh KEEPinside) + qh_partitioncoplanar(point, bestfacet, &bestdist); + } +} /* partitionpoint */ + +/*--------------------------------- + + qh_partitionvisible( allpoints, numoutside ) + partitions points in visible facets to qh.newfacet_list + qh.visible_list= visible facets + for visible facets + 1st neighbor (if any) points to a horizon facet or a new facet + if allpoints(!used), + repartitions coplanar points + + returns: + updates outside sets and coplanar sets of qh.newfacet_list + updates qh.num_outside (count of outside points) + + notes: + qh.findbest_notsharp should be clear (extra work if set) + + design: + for all visible facets with outside set or coplanar set + select a newfacet for visible facet + if outside set + partition outside set into new facets + if coplanar set and keeping coplanar/near-inside/inside points + if allpoints + partition coplanar set into new facets, may be assigned outside + else + partition coplanar set into coplanar sets of new facets + for each deleted vertex + if allpoints + partition vertex into new facets, may be assigned outside + else + partition vertex into coplanar sets of new facets +*/ +void qh_partitionvisible(/*visible_list*/ boolT allpoints, int *numoutside) { + facetT *visible, *newfacet; + pointT *point, **pointp; + int coplanar=0, size; + unsigned count; + vertexT *vertex, **vertexp; + + if (qh ONLYmax) + maximize_(qh MINoutside, qh max_vertex); + *numoutside= 0; + FORALLvisible_facets { + if (!visible->outsideset && !visible->coplanarset) + continue; + newfacet= visible->f.replace; + count= 0; + while (newfacet && newfacet->visible) { + newfacet= newfacet->f.replace; + if (count++ > qh facet_id) + qh_infiniteloop(visible); + } + if (!newfacet) + newfacet= qh newfacet_list; + if (newfacet == qh facet_tail) { + qh_fprintf(qh ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n"); + qh_errexit(qh_ERRprec, NULL, NULL); + } + if (visible->outsideset) { + size= qh_setsize(visible->outsideset); + *numoutside += size; + qh num_outside -= size; + FOREACHpoint_(visible->outsideset) + qh_partitionpoint(point, newfacet); + } + if (visible->coplanarset && (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside)) { + size= qh_setsize(visible->coplanarset); + coplanar += size; + FOREACHpoint_(visible->coplanarset) { + if (allpoints) /* not used */ + qh_partitionpoint(point, newfacet); + else + qh_partitioncoplanar(point, newfacet, NULL); + } + } + } + FOREACHvertex_(qh del_vertices) { + if (vertex->point) { + if (allpoints) /* not used */ + qh_partitionpoint(vertex->point, qh newfacet_list); + else + qh_partitioncoplanar(vertex->point, qh newfacet_list, NULL); + } + } + trace1((qh ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar)); +} /* partitionvisible */ + + + +/*--------------------------------- + + qh_precision( reason ) + restart on precision errors if not merging and if 'QJn' +*/ +void qh_precision(const char *reason) { + + if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) { + if (qh JOGGLEmax < REALmax/2) { + trace0((qh ferr, 26, "qh_precision: qhull restart because of %s\n", reason)); + longjmp(qh restartexit, qh_ERRprec); + } + } +} /* qh_precision */ + +/*--------------------------------- + + qh_printsummary( fp ) + prints summary to fp + + notes: + not in io.c so that user_eg.c can prevent io.c from loading + qh_printsummary and qh_countfacets must match counts + + design: + determine number of points, vertices, and coplanar points + print summary +*/ +void qh_printsummary(FILE *fp) { + realT ratio, outerplane, innerplane; + float cpu; + int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0; + int goodused; + facetT *facet; + const char *s; + int numdel= zzval_(Zdelvertextot); + int numtricoplanars= 0; + + size= qh num_points + qh_setsize(qh other_points); + numvertices= qh num_vertices - qh_setsize(qh del_vertices); + id= qh_pointid(qh GOODpointp); + FORALLfacets { + if (facet->coplanarset) + numcoplanars += qh_setsize( facet->coplanarset); + if (facet->good) { + if (facet->simplicial) { + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else if (qh_setsize(facet->vertices) != qh hull_dim) + nonsimplicial++; + } + } + if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id) + size--; + if (qh STOPcone || qh STOPpoint) + qh_fprintf(fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'."); + if (qh UPPERdelaunay) + goodused= qh GOODvertex + qh GOODpoint + qh SPLITthresholds; + else if (qh DELAUNAY) + goodused= qh GOODvertex + qh GOODpoint + qh GOODthreshold; + else + goodused= qh num_good; + nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + if (qh VORONOI) { + if (qh UPPERdelaunay) + qh_fprintf(fp, 9289, "\n\ +Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + else + qh_fprintf(fp, 9290, "\n\ +Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + qh_fprintf(fp, 9291, " Number of Voronoi regions%s: %d\n", + qh ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + qh_fprintf(fp, 9292, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + qh_fprintf(fp, 9293, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + qh_fprintf(fp, 9294, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + qh_fprintf(fp, 9295, " Number of%s Voronoi vertices: %d\n", + goodused ? " 'good'" : "", qh num_good); + if (nonsimplicial) + qh_fprintf(fp, 9296, " Number of%s non-simplicial Voronoi vertices: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh DELAUNAY) { + if (qh UPPERdelaunay) + qh_fprintf(fp, 9297, "\n\ +Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + else + qh_fprintf(fp, 9298, "\n\ +Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + qh_fprintf(fp, 9299, " Number of input sites%s: %d\n", + qh ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + qh_fprintf(fp, 9300, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + qh_fprintf(fp, 9301, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + qh_fprintf(fp, 9302, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + qh_fprintf(fp, 9303, " Number of%s Delaunay regions: %d\n", + goodused ? " 'good'" : "", qh num_good); + if (nonsimplicial) + qh_fprintf(fp, 9304, " Number of%s non-simplicial Delaunay regions: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh HALFspace) { + qh_fprintf(fp, 9305, "\n\ +Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + qh_fprintf(fp, 9306, " Number of halfspaces: %d\n", size); + qh_fprintf(fp, 9307, " Number of non-redundant halfspaces: %d\n", numvertices); + if (numcoplanars) { + if (qh KEEPinside && qh KEEPcoplanar) + s= "similar and redundant"; + else if (qh KEEPinside) + s= "redundant"; + else + s= "similar"; + qh_fprintf(fp, 9308, " Number of %s halfspaces: %d\n", s, numcoplanars); + } + qh_fprintf(fp, 9309, " Number of intersection points: %d\n", qh num_facets - qh num_visible); + if (goodused) + qh_fprintf(fp, 9310, " Number of 'good' intersection points: %d\n", qh num_good); + if (nonsimplicial) + qh_fprintf(fp, 9311, " Number of%s non-simplicial intersection points: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else { + qh_fprintf(fp, 9312, "\n\ +Convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + qh_fprintf(fp, 9313, " Number of vertices: %d\n", numvertices); + if (numcoplanars) { + if (qh KEEPinside && qh KEEPcoplanar) + s= "coplanar and interior"; + else if (qh KEEPinside) + s= "interior"; + else + s= "coplanar"; + qh_fprintf(fp, 9314, " Number of %s points: %d\n", s, numcoplanars); + } + qh_fprintf(fp, 9315, " Number of facets: %d\n", qh num_facets - qh num_visible); + if (goodused) + qh_fprintf(fp, 9316, " Number of 'good' facets: %d\n", qh num_good); + if (nonsimplicial) + qh_fprintf(fp, 9317, " Number of%s non-simplicial facets: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + } + if (numtricoplanars) + qh_fprintf(fp, 9318, " Number of triangulated facets: %d\n", numtricoplanars); + qh_fprintf(fp, 9319, "\nStatistics for: %s | %s", + qh rbox_command, qh qhull_command); + if (qh ROTATErandom != INT_MIN) + qh_fprintf(fp, 9320, " QR%d\n\n", qh ROTATErandom); + else + qh_fprintf(fp, 9321, "\n\n"); + qh_fprintf(fp, 9322, " Number of points processed: %d\n", zzval_(Zprocessed)); + qh_fprintf(fp, 9323, " Number of hyperplanes created: %d\n", zzval_(Zsetplane)); + if (qh DELAUNAY) + qh_fprintf(fp, 9324, " Number of facets in hull: %d\n", qh num_facets - qh num_visible); + qh_fprintf(fp, 9325, " Number of distance tests for qhull: %d\n", zzval_(Zpartition)+ + zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar)); +#if 0 /* NOTE: must print before printstatistics() */ + {realT stddev, ave; + qh_fprintf(fp, 9326, " average new facet balance: %2.2g\n", + wval_(Wnewbalance)/zval_(Zprocessed)); + stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance), + wval_(Wnewbalance2), &ave); + qh_fprintf(fp, 9327, " new facet standard deviation: %2.2g\n", stddev); + qh_fprintf(fp, 9328, " average partition balance: %2.2g\n", + wval_(Wpbalance)/zval_(Zpbalance)); + stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + qh_fprintf(fp, 9329, " partition standard deviation: %2.2g\n", stddev); + } +#endif + if (nummerged) { + qh_fprintf(fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+ + zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+ + zzval_(Zdistzero)); + qh_fprintf(fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart)); + qh_fprintf(fp, 9332," Number of merged facets: %d\n", nummerged); + } + if (!qh RANDOMoutside && qh QHULLfinished) { + cpu= (float)qh hulltime; + cpu /= (float)qh_SECticks; + wval_(Wcpu)= cpu; + qh_fprintf(fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu); + } + if (qh RERUN) { + if (!qh PREmerge && !qh MERGEexact) + qh_fprintf(fp, 9334, " Percentage of runs with precision errors: %4.1f\n", + zzval_(Zretry)*100.0/qh build_cnt); /* careful of order */ + }else if (qh JOGGLEmax < REALmax/2) { + if (zzval_(Zretry)) + qh_fprintf(fp, 9335, " After %d retries, input joggled by: %2.2g\n", + zzval_(Zretry), qh JOGGLEmax); + else + qh_fprintf(fp, 9336, " Input joggled by: %2.2g\n", qh JOGGLEmax); + } + if (qh totarea != 0.0) + qh_fprintf(fp, 9337, " %s facet area: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh totarea); + if (qh totvol != 0.0) + qh_fprintf(fp, 9338, " %s volume: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh totvol); + if (qh MERGING) { + qh_outerinner(NULL, &outerplane, &innerplane); + if (outerplane > 2 * qh DISTround) { + qh_fprintf(fp, 9339, " Maximum distance of %spoint above facet: %2.2g", + (qh QHULLfinished ? "" : "merged "), outerplane); + ratio= outerplane/(qh ONEmerge + qh DISTround); + /* don't report ratio if MINoutside is large */ + if (ratio > 0.05 && 2* qh ONEmerge > qh MINoutside && qh JOGGLEmax > REALmax/2) + qh_fprintf(fp, 9340, " (%.1fx)\n", ratio); + else + qh_fprintf(fp, 9341, "\n"); + } + if (innerplane < -2 * qh DISTround) { + qh_fprintf(fp, 9342, " Maximum distance of %svertex below facet: %2.2g", + (qh QHULLfinished ? "" : "merged "), innerplane); + ratio= -innerplane/(qh ONEmerge+qh DISTround); + if (ratio > 0.05 && qh JOGGLEmax > REALmax/2) + qh_fprintf(fp, 9343, " (%.1fx)\n", ratio); + else + qh_fprintf(fp, 9344, "\n"); + } + } + qh_fprintf(fp, 9345, "\n"); +} /* printsummary */ diff --git a/extern/qhull/libqhull.h b/extern/qhull/libqhull.h new file mode 100644 index 000000000000..7a132267bde9 --- /dev/null +++ b/extern/qhull/libqhull.h @@ -0,0 +1,1100 @@ +/*--------------------------------- + + libqhull.h + user-level header file for using qhull.a library + + see qh-qhull.htm, qhull_a.h + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/libqhull.h#7 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ + + NOTE: access to qh_qh is via the 'qh' macro. This allows + qh_qh to be either a pointer or a structure. An example + of using qh is "qh DROPdim" which accesses the DROPdim + field of qh_qh. Similarly, access to qh_qhstat is via + the 'qhstat' macro. + + includes function prototypes for libqhull.c, geom.c, global.c, io.c, user.c + + use mem.h for mem.c + use qset.h for qset.c + + see unix.c for an example of using libqhull.h + + recompile qhull if you change this file +*/ + +#ifndef qhDEFlibqhull +#define qhDEFlibqhull 1 + +/*=========================== -included files ==============*/ + +#include "user.h" /* user definable constants (e.g., qh_QHpointer) */ + +#include+#include +#include +#include + +#if __MWERKS__ && __POWERPC__ +#include +#include +#include +#endif + +#ifndef __STDC__ +#ifndef __cplusplus +#if !_MSC_VER +#error Neither __STDC__ nor __cplusplus is defined. Please use strict ANSI C or C++ to compile +#error Qhull. You may need to turn off compiler extensions in your project configuration. If +#error your compiler is a standard C compiler, you can delete this warning from libqhull.h +#endif +#endif +#endif + +/*============ constants and basic types ====================*/ + +extern const char *qh_version; /* defined in global.c */ + +/*---------------------------------- + + coordT + coordinates and coefficients are stored as realT (i.e., double) + + notes: + Qhull works well if realT is 'float'. If so joggle (QJ) is not effective. + + Could use 'float' for data and 'double' for calculations (realT vs. coordT) + This requires many type casts, and adjusted error bounds. + Also C compilers may do expressions in double anyway. +*/ +#define coordT realT + +/*---------------------------------- + + pointT + a point is an array of coordinates, usually qh.hull_dim +*/ +#define pointT coordT + +/*---------------------------------- + + flagT + Boolean flag as a bit +*/ +#define flagT unsigned int + +/*---------------------------------- + + boolT + boolean value, either True or False + + notes: + needed for portability + Use qh_False/qh_True as synonyms +*/ +#define boolT unsigned int +#ifdef False +#undef False +#endif +#ifdef True +#undef True +#endif +#define False 0 +#define True 1 +#define qh_False 0 +#define qh_True 1 + +/*---------------------------------- + + qh_CENTER + to distinguish facet->center +*/ +typedef enum +{ + qh_ASnone = 0, qh_ASvoronoi, qh_AScentrum +} +qh_CENTER; + +/*---------------------------------- + + qh_PRINT + output formats for printing (qh.PRINTout). + 'Fa' 'FV' 'Fc' 'FC' + + + notes: + some of these names are similar to qh names. The similar names are only + used in switch statements in qh_printbegin() etc. +*/ +typedef enum {qh_PRINTnone= 0, + qh_PRINTarea, qh_PRINTaverage, /* 'Fa' 'FV' 'Fc' 'FC' */ + qh_PRINTcoplanars, qh_PRINTcentrums, + qh_PRINTfacets, qh_PRINTfacets_xridge, /* 'f' 'FF' 'G' 'FI' 'Fi' 'Fn' */ + qh_PRINTgeom, qh_PRINTids, qh_PRINTinner, qh_PRINTneighbors, + qh_PRINTnormals, qh_PRINTouter, qh_PRINTmaple, /* 'n' 'Fo' 'i' 'm' 'Fm' 'FM', 'o' */ + qh_PRINTincidences, qh_PRINTmathematica, qh_PRINTmerges, qh_PRINToff, + qh_PRINToptions, qh_PRINTpointintersect, /* 'FO' 'Fp' 'FP' 'p' 'FQ' 'FS' */ + qh_PRINTpointnearest, qh_PRINTpoints, qh_PRINTqhull, qh_PRINTsize, + qh_PRINTsummary, qh_PRINTtriangles, /* 'Fs' 'Ft' 'Fv' 'FN' 'Fx' */ + qh_PRINTvertices, qh_PRINTvneighbors, qh_PRINTextremes, + qh_PRINTEND} qh_PRINT; + +/*---------------------------------- + + qh_ALL + argument flag for selecting everything +*/ +#define qh_ALL True +#define qh_NOupper True /* argument for qh_findbest */ +#define qh_IScheckmax True /* argument for qh_findbesthorizon */ +#define qh_ISnewfacets True /* argument for qh_findbest */ +#define qh_RESETvisible True /* argument for qh_resetlists */ + +/*---------------------------------- + + qh_ERR + Qhull exit codes, for indicating errors + See: MSG_ERROR and MSG_WARNING [user.h] +*/ +#define qh_ERRnone 0 /* no error occurred during qhull */ +#define qh_ERRinput 1 /* input inconsistency */ +#define qh_ERRsingular 2 /* singular input data */ +#define qh_ERRprec 3 /* precision error */ +#define qh_ERRmem 4 /* insufficient memory, matches mem.h */ +#define qh_ERRqhull 5 /* internal error detected, matches mem.h */ + +/*---------------------------------- + +qh_FILEstderr +Fake stderr to distinguish error output from normal output +For C++ interface. Must redefine qh_fprintf_qhull +*/ +#define qh_FILEstderr (FILE*)1 + +/* ============ -structures- ==================== + each of the following structures is defined by a typedef + all realT and coordT fields occur at the beginning of a structure + (otherwise space may be wasted due to alignment) + define all flags together and pack into 32-bit number +*/ + +typedef struct vertexT vertexT; +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* defined in qset.h */ +#endif + +#ifndef DEFqhstatT +#define DEFqhstatT 1 +typedef struct qhstatT qhstatT; /* defined in stat.h */ +#endif + +/*---------------------------------- + + facetT + defines a facet + + notes: + qhull() generates the hull as a list of facets. + + topological information: + f.previous,next doubly-linked list of facets + f.vertices set of vertices + f.ridges set of ridges + f.neighbors set of neighbors + f.toporient True if facet has top-orientation (else bottom) + + geometric information: + f.offset,normal hyperplane equation + f.maxoutside offset to outer plane -- all points inside + f.center centrum for testing convexity + f.simplicial True if facet is simplicial + f.flipped True if facet does not include qh.interior_point + + for constructing hull: + f.visible True if facet on list of visible facets (will be deleted) + f.newfacet True if facet on list of newly created facets + f.coplanarset set of points coplanar with this facet + (includes near-inside points for later testing) + f.outsideset set of points outside of this facet + f.furthestdist distance to furthest point of outside set + f.visitid marks visited facets during a loop + f.replace replacement facet for to-be-deleted, visible facets + f.samecycle,newcycle cycle of facets for merging into horizon facet + + see below for other flags and fields +*/ +struct facetT { +#if !qh_COMPUTEfurthest + coordT furthestdist;/* distance to furthest point of outsideset */ +#endif +#if qh_MAXoutside + coordT maxoutside; /* max computed distance of point to facet + Before QHULLfinished this is an approximation + since maxdist not always set for mergefacet + Actual outer plane is +DISTround and + computed outer plane is +2*DISTround */ +#endif + coordT offset; /* exact offset of hyperplane from origin */ + coordT *normal; /* normal of hyperplane, hull_dim coefficients */ + /* if tricoplanar, shared with a neighbor */ + union { /* in order of testing */ + realT area; /* area of facet, only in io.c if ->isarea */ + facetT *replace; /* replacement facet if ->visible and NEWfacets + is NULL only if qh_mergedegen_redundant or interior */ + facetT *samecycle; /* cycle of facets from the same visible/horizon intersection, + if ->newfacet */ + facetT *newcycle; /* in horizon facet, current samecycle of new facets */ + facetT *trivisible; /* visible facet for ->tricoplanar facets during qh_triangulate() */ + facetT *triowner; /* owner facet for ->tricoplanar, !isarea facets w/ ->keepcentrum */ + }f; + coordT *center; /* centrum for convexity, qh CENTERtype == qh_AScentrum */ + /* Voronoi center, qh CENTERtype == qh_ASvoronoi */ + /* if tricoplanar, shared with a neighbor */ + facetT *previous; /* previous facet in the facet_list */ + facetT *next; /* next facet in the facet_list */ + setT *vertices; /* vertices for this facet, inverse sorted by ID + if simplicial, 1st vertex was apex/furthest */ + setT *ridges; /* explicit ridges for nonsimplicial facets. + for simplicial facets, neighbors define the ridges */ + setT *neighbors; /* neighbors of the facet. If simplicial, the kth + neighbor is opposite the kth vertex, and the first + neighbor is the horizon facet for the first vertex*/ + setT *outsideset; /* set of points outside this facet + if non-empty, last point is furthest + if NARROWhull, includes coplanars for partitioning*/ + setT *coplanarset; /* set of points coplanar with this facet + > qh.min_vertex and <= facet->max_outside + a point is assigned to the furthest facet + if non-empty, last point is furthest away */ + unsigned visitid; /* visit_id, for visiting all neighbors, + all uses are independent */ + unsigned id; /* unique identifier from qh facet_id */ + unsigned nummerge:9; /* number of merges */ +#define qh_MAXnummerge 511 /* 2^9-1, 32 flags total, see "flags:" in io.c */ + flagT tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */ + /* all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */ + /* all tricoplanars share the same apex */ + /* if ->degenerate, does not span facet (one logical ridge) */ + /* one tricoplanar has ->keepcentrum and ->coplanarset */ + /* during qh_triangulate, f.trivisible points to original facet */ + flagT newfacet:1; /* True if facet on qh newfacet_list (new or merged) */ + flagT visible:1; /* True if visible facet (will be deleted) */ + flagT toporient:1; /* True if created with top orientation + after merging, use ridge orientation */ + flagT simplicial:1;/* True if simplicial facet, ->ridges may be implicit */ + flagT seen:1; /* used to perform operations only once, like visitid */ + flagT seen2:1; /* used to perform operations only once, like visitid */ + flagT flipped:1; /* True if facet is flipped */ + flagT upperdelaunay:1; /* True if facet is upper envelope of Delaunay triangulation */ + flagT notfurthest:1; /* True if last point of outsideset is not furthest*/ + +/*-------- flags primarily for output ---------*/ + flagT good:1; /* True if a facet marked good for output */ + flagT isarea:1; /* True if facet->f.area is defined */ + +/*-------- flags for merging ------------------*/ + flagT dupridge:1; /* True if duplicate ridge in facet */ + flagT mergeridge:1; /* True if facet or neighbor contains a qh_MERGEridge + ->normal defined (also defined for mergeridge2) */ + flagT mergeridge2:1; /* True if neighbor contains a qh_MERGEridge (mark_dupridges */ + flagT coplanar:1; /* True if horizon facet is coplanar at last use */ + flagT mergehorizon:1; /* True if will merge into horizon (->coplanar) */ + flagT cycledone:1;/* True if mergecycle_all already done */ + flagT tested:1; /* True if facet convexity has been tested (false after merge */ + flagT keepcentrum:1; /* True if keep old centrum after a merge, or marks owner for ->tricoplanar */ + flagT newmerge:1; /* True if facet is newly merged for reducevertices */ + flagT degenerate:1; /* True if facet is degenerate (degen_mergeset or ->tricoplanar) */ + flagT redundant:1; /* True if facet is redundant (degen_mergeset) */ +}; + + +/*---------------------------------- + + ridgeT + defines a ridge + + notes: + a ridge is hull_dim-1 simplex between two neighboring facets. If the + facets are non-simplicial, there may be more than one ridge between + two facets. E.G. a 4-d hypercube has two triangles between each pair + of neighboring facets. + + topological information: + vertices a set of vertices + top,bottom neighboring facets with orientation + + geometric information: + tested True if ridge is clearly convex + nonconvex True if ridge is non-convex +*/ +struct ridgeT { + setT *vertices; /* vertices belonging to this ridge, inverse sorted by ID + NULL if a degen ridge (matchsame) */ + facetT *top; /* top facet this ridge is part of */ + facetT *bottom; /* bottom facet this ridge is part of */ + unsigned id:24; /* unique identifier, =>room for 8 flags, bit field matches qh.ridge_id */ + flagT seen:1; /* used to perform operations only once */ + flagT tested:1; /* True when ridge is tested for convexity */ + flagT nonconvex:1; /* True if getmergeset detected a non-convex neighbor + only one ridge between neighbors may have nonconvex */ +}; + +/*---------------------------------- + + vertexT + defines a vertex + + topological information: + next,previous doubly-linked list of all vertices + neighbors set of adjacent facets (only if qh.VERTEXneighbors) + + geometric information: + point array of DIM3 coordinates +*/ +struct vertexT { + vertexT *next; /* next vertex in vertex_list */ + vertexT *previous; /* previous vertex in vertex_list */ + pointT *point; /* hull_dim coordinates (coordT) */ + setT *neighbors; /* neighboring facets of vertex, qh_vertexneighbors() + inits in io.c or after first merge */ + unsigned visitid:31; /* for use with qh vertex_visit, size must match */ + flagT seen2:1; /* another seen flag */ + unsigned id:24; /* unique identifier, bit field matches qh.vertex_id */ + unsigned dim:4; /* dimension of point if non-zero, used by cpp */ + /* =>room for 4 flags */ + flagT seen:1; /* used to perform operations only once */ + flagT delridge:1; /* vertex was part of a deleted ridge */ + flagT deleted:1; /* true if vertex on qh del_vertices */ + flagT newlist:1; /* true if vertex on qh newvertex_list */ +}; + +#define MAX_vdim 15 /* Maximum size of vertex->dim */ + +/*======= -global variables -qh ============================*/ + +/*---------------------------------- + + qh + all global variables for qhull are in qh, qhmem, and qhstat + + notes: + qhmem is defined in mem.h, qhstat is defined in stat.h, qhrbox is defined in rboxpoints.h + Access to qh_qh is via the "qh" macro. See qh_QHpointer in user.h + + All global variables for qhull are in qh, qhmem, and qhstat + qh must be unique for each instance of qhull + qhstat may be shared between qhull instances. + qhmem may be shared across multiple instances of Qhull. + Rbox uses global variables rbox_inuse and rbox, but does not persist data across calls. + + Qhull is not multithreaded. Global state could be stored in thread-local storage. +*/ + +extern int qhull_inuse; + +typedef struct qhT qhT; +#if qh_QHpointer_dllimport +#define qh qh_qh-> +__declspec(dllimport) extern qhT *qh_qh; /* allocated in global.c */ +#elif qh_QHpointer +#define qh qh_qh-> +extern qhT *qh_qh; /* allocated in global.c */ +#elif qh_dllimport +#define qh qh_qh. +__declspec(dllimport) extern qhT qh_qh; /* allocated in global.c */ +#else +#define qh qh_qh. +extern qhT qh_qh; +#endif + +struct qhT { + +/*---------------------------------- + + qh constants + configuration flags and constants for Qhull + + notes: + The user configures Qhull by defining flags. They are + copied into qh by qh_setflags(). qh-quick.htm#options defines the flags. +*/ + boolT ALLpoints; /* true 'Qs' if search all points for initial simplex */ + boolT ANGLEmerge; /* true 'Qa' if sort potential merges by angle */ + boolT APPROXhull; /* true 'Wn' if MINoutside set */ + realT MINoutside; /* 'Wn' min. distance for an outside point */ + boolT ANNOTATEoutput; /* true 'Ta' if annotate output with message codes */ + boolT ATinfinity; /* true 'Qz' if point num_points-1 is "at-infinity" + for improving precision in Delaunay triangulations */ + boolT AVOIDold; /* true 'Q4' if avoid old->new merges */ + boolT BESToutside; /* true 'Qf' if partition points into best outsideset */ + boolT CDDinput; /* true 'Pc' if input uses CDD format (1.0/offset first) */ + boolT CDDoutput; /* true 'PC' if print normals in CDD format (offset first) */ + boolT CHECKfrequently; /* true 'Tc' if checking frequently */ + realT premerge_cos; /* 'A-n' cos_max when pre merging */ + realT postmerge_cos; /* 'An' cos_max when post merging */ + boolT DELAUNAY; /* true 'd' if computing DELAUNAY triangulation */ + boolT DOintersections; /* true 'Gh' if print hyperplane intersections */ + int DROPdim; /* drops dim 'GDn' for 4-d -> 3-d output */ + boolT FORCEoutput; /* true 'Po' if forcing output despite degeneracies */ + int GOODpoint; /* 1+n for 'QGn', good facet if visible/not(-) from point n*/ + pointT *GOODpointp; /* the actual point */ + boolT GOODthreshold; /* true if qh lower_threshold/upper_threshold defined + false if qh SPLITthreshold */ + int GOODvertex; /* 1+n, good facet if vertex for point n */ + pointT *GOODvertexp; /* the actual point */ + boolT HALFspace; /* true 'Hn,n,n' if halfspace intersection */ + int IStracing; /* trace execution, 0=none, 1=least, 4=most, -1=events */ + int KEEParea; /* 'PAn' number of largest facets to keep */ + boolT KEEPcoplanar; /* true 'Qc' if keeping nearest facet for coplanar points */ + boolT KEEPinside; /* true 'Qi' if keeping nearest facet for inside points + set automatically if 'd Qc' */ + int KEEPmerge; /* 'PMn' number of facets to keep with most merges */ + realT KEEPminArea; /* 'PFn' minimum facet area to keep */ + realT MAXcoplanar; /* 'Un' max distance below a facet to be coplanar*/ + boolT MERGEexact; /* true 'Qx' if exact merges (coplanar, degen, dupridge, flipped) */ + boolT MERGEindependent; /* true 'Q2' if merging independent sets */ + boolT MERGING; /* true if exact-, pre- or post-merging, with angle and centrum tests */ + realT premerge_centrum; /* 'C-n' centrum_radius when pre merging. Default is round-off */ + realT postmerge_centrum; /* 'Cn' centrum_radius when post merging. Default is round-off */ + boolT MERGEvertices; /* true 'Q3' if merging redundant vertices */ + realT MINvisible; /* 'Vn' min. distance for a facet to be visible */ + boolT NOnarrow; /* true 'Q10' if no special processing for narrow distributions */ + boolT NOnearinside; /* true 'Q8' if ignore near-inside points when partitioning */ + boolT NOpremerge; /* true 'Q0' if no defaults for C-0 or Qx */ + boolT ONLYgood; /* true 'Qg' if process points with good visible or horizon facets */ + boolT ONLYmax; /* true 'Qm' if only process points that increase max_outside */ + boolT PICKfurthest; /* true 'Q9' if process furthest of furthest points*/ + boolT POSTmerge; /* true if merging after buildhull (Cn or An) */ + boolT PREmerge; /* true if merging during buildhull (C-n or A-n) */ + /* NOTE: some of these names are similar to qh_PRINT names */ + boolT PRINTcentrums; /* true 'Gc' if printing centrums */ + boolT PRINTcoplanar; /* true 'Gp' if printing coplanar points */ + int PRINTdim; /* print dimension for Geomview output */ + boolT PRINTdots; /* true 'Ga' if printing all points as dots */ + boolT PRINTgood; /* true 'Pg' if printing good facets */ + boolT PRINTinner; /* true 'Gi' if printing inner planes */ + boolT PRINTneighbors; /* true 'PG' if printing neighbors of good facets */ + boolT PRINTnoplanes; /* true 'Gn' if printing no planes */ + boolT PRINToptions1st; /* true 'FO' if printing options to stderr */ + boolT PRINTouter; /* true 'Go' if printing outer planes */ + boolT PRINTprecision; /* false 'Pp' if not reporting precision problems */ + qh_PRINT PRINTout[qh_PRINTEND]; /* list of output formats to print */ + boolT PRINTridges; /* true 'Gr' if print ridges */ + boolT PRINTspheres; /* true 'Gv' if print vertices as spheres */ + boolT PRINTstatistics; /* true 'Ts' if printing statistics to stderr */ + boolT PRINTsummary; /* true 's' if printing summary to stderr */ + boolT PRINTtransparent; /* true 'Gt' if print transparent outer ridges */ + boolT PROJECTdelaunay; /* true if DELAUNAY, no readpoints() and + need projectinput() for Delaunay in qh_init_B */ + int PROJECTinput; /* number of projected dimensions 'bn:0Bn:0' */ + boolT QUICKhelp; /* true if quick help message for degen input */ + boolT RANDOMdist; /* true if randomly change distplane and setfacetplane */ + realT RANDOMfactor; /* maximum random perturbation */ + realT RANDOMa; /* qh_randomfactor is randr * RANDOMa + RANDOMb */ + realT RANDOMb; + boolT RANDOMoutside; /* true if select a random outside point */ + int REPORTfreq; /* buildtracing reports every n facets */ + int REPORTfreq2; /* tracemerging reports every REPORTfreq/2 facets */ + int RERUN; /* 'TRn' rerun qhull n times (qh.build_cnt) */ + int ROTATErandom; /* 'QRn' seed, 0 time, >= rotate input */ + boolT SCALEinput; /* true 'Qbk' if scaling input */ + boolT SCALElast; /* true 'Qbb' if scale last coord to max prev coord */ + boolT SETroundoff; /* true 'E' if qh DISTround is predefined */ + boolT SKIPcheckmax; /* true 'Q5' if skip qh_check_maxout */ + boolT SKIPconvex; /* true 'Q6' if skip convexity testing during pre-merge */ + boolT SPLITthresholds; /* true if upper_/lower_threshold defines a region + used only for printing (!for qh ONLYgood) */ + int STOPcone; /* 'TCn' 1+n for stopping after cone for point n */ + /* also used by qh_build_withresart for err exit*/ + int STOPpoint; /* 'TVn' 'TV-n' 1+n for stopping after/before(-) + adding point n */ + int TESTpoints; /* 'QTn' num of test points after qh.num_points. Test points always coplanar. */ + boolT TESTvneighbors; /* true 'Qv' if test vertex neighbors at end */ + int TRACElevel; /* 'Tn' conditional IStracing level */ + int TRACElastrun; /* qh.TRACElevel applies to last qh.RERUN */ + int TRACEpoint; /* 'TPn' start tracing when point n is a vertex */ + realT TRACEdist; /* 'TWn' start tracing when merge distance too big */ + int TRACEmerge; /* 'TMn' start tracing before this merge */ + boolT TRIangulate; /* true 'Qt' if triangulate non-simplicial facets */ + boolT TRInormals; /* true 'Q11' if triangulate duplicates normals (sets Qt) */ + boolT UPPERdelaunay; /* true 'Qu' if computing furthest-site Delaunay */ + boolT USEstdout; /* true 'Tz' if using stdout instead of stderr */ + boolT VERIFYoutput; /* true 'Tv' if verify output at end of qhull */ + boolT VIRTUALmemory; /* true 'Q7' if depth-first processing in buildhull */ + boolT VORONOI; /* true 'v' if computing Voronoi diagram */ + + /*--------input constants ---------*/ + realT AREAfactor; /* 1/(hull_dim-1)! for converting det's to area */ + boolT DOcheckmax; /* true if calling qh_check_maxout (qh_initqhull_globals) */ + char *feasible_string; /* feasible point 'Hn,n,n' for halfspace intersection */ + coordT *feasible_point; /* as coordinates, both malloc'd */ + boolT GETarea; /* true 'Fa', 'FA', 'FS', 'PAn', 'PFn' if compute facet area/Voronoi volume in io.c */ + boolT KEEPnearinside; /* true if near-inside points in coplanarset */ + int hull_dim; /* dimension of hull, set by initbuffers */ + int input_dim; /* dimension of input, set by initbuffers */ + int num_points; /* number of input points */ + pointT *first_point; /* array of input points, see POINTSmalloc */ + boolT POINTSmalloc; /* true if qh first_point/num_points allocated */ + pointT *input_points; /* copy of original qh.first_point for input points for qh_joggleinput */ + boolT input_malloc; /* true if qh input_points malloc'd */ + char qhull_command[256];/* command line that invoked this program */ + int qhull_commandsiz2; /* size of qhull_command at qh_clear_outputflags */ + char rbox_command[256]; /* command line that produced the input points */ + char qhull_options[512];/* descriptive list of options */ + int qhull_optionlen; /* length of last line */ + int qhull_optionsiz; /* size of qhull_options at qh_build_withrestart */ + int qhull_optionsiz2; /* size of qhull_options at qh_clear_outputflags */ + int run_id; /* non-zero, random identifier for this instance of qhull */ + boolT VERTEXneighbors; /* true if maintaining vertex neighbors */ + boolT ZEROcentrum; /* true if 'C-0' or 'C-0 Qx'. sets ZEROall_ok */ + realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k] + must set either GOODthreshold or SPLITthreshold + if Delaunay, default is 0.0 for upper envelope */ + realT *lower_threshold; /* don't print if facet->normal[k] <=lower_threshold[k] */ + realT *upper_bound; /* scale point[k] to new upper bound */ + realT *lower_bound; /* scale point[k] to new lower bound + project if both upper_ and lower_bound == 0 */ + +/*---------------------------------- + + qh precision constants + precision constants for Qhull + + notes: + qh_detroundoff() computes the maximum roundoff error for distance + and other computations. It also sets default values for the + qh constants above. +*/ + realT ANGLEround; /* max round off error for angles */ + realT centrum_radius; /* max centrum radius for convexity (roundoff added) */ + realT cos_max; /* max cosine for convexity (roundoff added) */ + realT DISTround; /* max round off error for distances, 'E' overrides */ + realT MAXabs_coord; /* max absolute coordinate */ + realT MAXlastcoord; /* max last coordinate for qh_scalelast */ + realT MAXsumcoord; /* max sum of coordinates */ + realT MAXwidth; /* max rectilinear width of point coordinates */ + realT MINdenom_1; /* min. abs. value for 1/x */ + realT MINdenom; /* use divzero if denominator < MINdenom */ + realT MINdenom_1_2; /* min. abs. val for 1/x that allows normalization */ + realT MINdenom_2; /* use divzero if denominator < MINdenom_2 */ + realT MINlastcoord; /* min. last coordinate for qh_scalelast */ + boolT NARROWhull; /* set in qh_initialhull if angle < qh_MAXnarrow */ + realT *NEARzero; /* hull_dim array for near zero in gausselim */ + realT NEARinside; /* keep points for qh_check_maxout if close to facet */ + realT ONEmerge; /* max distance for merging simplicial facets */ + realT outside_err; /* application's epsilon for coplanar points + qh_check_bestdist() qh_check_points() reports error if point outside */ + realT WIDEfacet; /* size of wide facet for skipping ridge in + area computation and locking centrum */ + +/*---------------------------------- + + qh internal constants + internal constants for Qhull +*/ + char qhull[sizeof("qhull")]; /* "qhull" for checking ownership while debugging */ + jmp_buf errexit; /* exit label for qh_errexit, defined by setjmp() */ + char jmpXtra[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */ + jmp_buf restartexit; /* restart label for qh_errexit, defined by setjmp() */ + char jmpXtra2[40]; /* extra bytes in case jmp_buf is defined wrong by compiler*/ + FILE *fin; /* pointer to input file, init by qh_meminit */ + FILE *fout; /* pointer to output file */ + FILE *ferr; /* pointer to error file */ + pointT *interior_point; /* center point of the initial simplex*/ + int normal_size; /* size in bytes for facet normals and point coords*/ + int center_size; /* size in bytes for Voronoi centers */ + int TEMPsize; /* size for small, temporary sets (in quick mem) */ + +/*---------------------------------- + + qh facet and vertex lists + defines lists of facets, new facets, visible facets, vertices, and + new vertices. Includes counts, next ids, and trace ids. + see: + qh_resetlists() +*/ + facetT *facet_list; /* first facet */ + facetT *facet_tail; /* end of facet_list (dummy facet) */ + facetT *facet_next; /* next facet for buildhull() + previous facets do not have outside sets + NARROWhull: previous facets may have coplanar outside sets for qh_outcoplanar */ + facetT *newfacet_list; /* list of new facets to end of facet_list */ + facetT *visible_list; /* list of visible facets preceeding newfacet_list, + facet->visible set */ + int num_visible; /* current number of visible facets */ + unsigned tracefacet_id; /* set at init, then can print whenever */ + facetT *tracefacet; /* set in newfacet/mergefacet, undone in delfacet*/ + unsigned tracevertex_id; /* set at buildtracing, can print whenever */ + vertexT *tracevertex; /* set in newvertex, undone in delvertex*/ + vertexT *vertex_list; /* list of all vertices, to vertex_tail */ + vertexT *vertex_tail; /* end of vertex_list (dummy vertex) */ + vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail + all vertices have 'newlist' set */ + int num_facets; /* number of facets in facet_list + includes visble faces (num_visible) */ + int num_vertices; /* number of vertices in facet_list */ + int num_outside; /* number of points in outsidesets (for tracing and RANDOMoutside) + includes coplanar outsideset points for NARROWhull/qh_outcoplanar() */ + int num_good; /* number of good facets (after findgood_all) */ + unsigned facet_id; /* ID of next, new facet from newfacet() */ + unsigned ridge_id:24; /* ID of next, new ridge from newridge() */ + unsigned vertex_id:24; /* ID of next, new vertex from newvertex() */ + +/*---------------------------------- + + qh global variables + defines minimum and maximum distances, next visit ids, several flags, + and other global variables. + initialize in qh_initbuild or qh_maxmin if used in qh_buildhull +*/ + unsigned long hulltime; /* ignore time to set up input and randomize */ + /* use unsigned to avoid wrap-around errors */ + boolT ALLOWrestart; /* true if qh_precision can use qh.restartexit */ + int build_cnt; /* number of calls to qh_initbuild */ + qh_CENTER CENTERtype; /* current type of facet->center, qh_CENTER */ + int furthest_id; /* pointid of furthest point, for tracing */ + facetT *GOODclosest; /* closest facet to GOODthreshold in qh_findgood */ + boolT hasAreaVolume; /* true if totarea, totvol was defined by qh_getarea */ + boolT hasTriangulation; /* true if triangulation created by qh_triangulate */ + realT JOGGLEmax; /* set 'QJn' if randomly joggle input */ + boolT maxoutdone; /* set qh_check_maxout(), cleared by qh_addpoint() */ + realT max_outside; /* maximum distance from a point to a facet, + before roundoff, not simplicial vertices + actual outer plane is +DISTround and + computed outer plane is +2*DISTround */ + realT max_vertex; /* maximum distance (>0) from vertex to a facet, + before roundoff, due to a merge */ + realT min_vertex; /* minimum distance (<0) from vertex to a facet, + before roundoff, due to a merge + if qh.JOGGLEmax, qh_makenewplanes sets it + recomputed if qh.DOcheckmax, default -qh.DISTround */ + boolT NEWfacets; /* true while visible facets invalid due to new or merge + from makecone/attachnewfacets to deletevisible */ + boolT findbestnew; /* true if partitioning calls qh_findbestnew */ + boolT findbest_notsharp; /* true if new facets are at least 90 degrees */ + boolT NOerrexit; /* true if qh.errexit is not available */ + realT PRINTcradius; /* radius for printing centrums */ + realT PRINTradius; /* radius for printing vertex spheres and points */ + boolT POSTmerging; /* true when post merging */ + int printoutvar; /* temporary variable for qh_printbegin, etc. */ + int printoutnum; /* number of facets printed */ + boolT QHULLfinished; /* True after qhull() is finished */ + realT totarea; /* 'FA': total facet area computed by qh_getarea, hasAreaVolume */ + realT totvol; /* 'FA': total volume computed by qh_getarea, hasAreaVolume */ + unsigned int visit_id; /* unique ID for searching neighborhoods, */ + unsigned int vertex_visit:31; /* unique ID for searching vertices, reset with qh_buildtracing */ + boolT ZEROall_ok; /* True if qh_checkzero always succeeds */ + boolT WAScoplanar; /* True if qh_partitioncoplanar (qh_check_maxout) */ + +/*---------------------------------- + + qh global sets + defines sets for merging, initial simplex, hashing, extra input points, + and deleted vertices +*/ + setT *facet_mergeset; /* temporary set of merges to be done */ + setT *degen_mergeset; /* temporary set of degenerate and redundant merges */ + setT *hash_table; /* hash table for matching ridges in qh_matchfacets + size is setsize() */ + setT *other_points; /* additional points */ + setT *del_vertices; /* vertices to partition and delete with visible + facets. Have deleted set for checkfacet */ + +/*---------------------------------- + + qh global buffers + defines buffers for maxtrix operations, input, and error messages +*/ + coordT *gm_matrix; /* (dim+1)Xdim matrix for geom.c */ + coordT **gm_row; /* array of gm_matrix rows */ + char* line; /* malloc'd input line of maxline+1 chars */ + int maxline; + coordT *half_space; /* malloc'd input array for halfspace (qh normal_size+coordT) */ + coordT *temp_malloc; /* malloc'd input array for points */ + +/*---------------------------------- + + qh static variables + defines static variables for individual functions + + notes: + do not use 'static' within a function. Multiple instances of qhull + may exist. + + do not assume zero initialization, 'QPn' may cause a restart +*/ + boolT ERREXITcalled; /* true during qh_errexit (prevents duplicate calls */ + boolT firstcentrum; /* for qh_printcentrum */ + boolT old_randomdist; /* save RANDOMdist flag during io, tracing, or statistics */ + setT *coplanarfacetset; /* set of coplanar facets for searching qh_findbesthorizon() */ + realT last_low; /* qh_scalelast parameters for qh_setdelaunay */ + realT last_high; + realT last_newhigh; + unsigned lastreport; /* for qh_buildtracing */ + int mergereport; /* for qh_tracemerging */ + qhstatT *old_qhstat; /* for saving qh_qhstat in save_qhull() and UsingLibQhull. Free with qh_free() */ + setT *old_tempstack; /* for saving qhmem.tempstack in save_qhull */ + int ridgeoutnum; /* number of ridges for 4OFF output (qh_printbegin,etc) */ +}; + +/*=========== -macros- =========================*/ + +/*---------------------------------- + + otherfacet_(ridge, facet) + return neighboring facet for a ridge in facet +*/ +#define otherfacet_(ridge, facet) \ + (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top) + +/*---------------------------------- + + getid_(p) + return int ID for facet, ridge, or vertex + return -1 if NULL +*/ +#define getid_(p) ((p) ? (int)((p)->id) : -1) + +/*============== FORALL macros ===================*/ + +/*---------------------------------- + + FORALLfacets { ... } + assign 'facet' to each facet in qh.facet_list + + notes: + uses 'facetT *facet;' + assumes last facet is a sentinel + + see: + FORALLfacet_( facetlist ) +*/ +#define FORALLfacets for (facet=qh facet_list;facet && facet->next;facet=facet->next) + +/*---------------------------------- + + FORALLpoints { ... } + assign 'point' to each point in qh.first_point, qh.num_points + + declare: + coordT *point, *pointtemp; +*/ +#define FORALLpoints FORALLpoint_(qh first_point, qh num_points) + +/*---------------------------------- + + FORALLpoint_( points, num) { ... } + assign 'point' to each point in points array of num points + + declare: + coordT *point, *pointtemp; +*/ +#define FORALLpoint_(points, num) for (point= (points), \ + pointtemp= (points)+qh hull_dim*(num); point < pointtemp; point += qh hull_dim) + +/*---------------------------------- + + FORALLvertices { ... } + assign 'vertex' to each vertex in qh.vertex_list + + declare: + vertexT *vertex; + + notes: + assumes qh.vertex_list terminated with a sentinel +*/ +#define FORALLvertices for (vertex=qh vertex_list;vertex && vertex->next;vertex= vertex->next) + +/*---------------------------------- + + FOREACHfacet_( facets ) { ... } + assign 'facet' to each facet in facets + + declare: + facetT *facet, **facetp; + + see: + FOREACHsetelement_ +*/ +#define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet) + +/*---------------------------------- + + FOREACHneighbor_( facet ) { ... } + assign 'neighbor' to each neighbor in facet->neighbors + + FOREACHneighbor_( vertex ) { ... } + assign 'neighbor' to each neighbor in vertex->neighbors + + declare: + facetT *neighbor, **neighborp; + + see: + FOREACHsetelement_ +*/ +#define FOREACHneighbor_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighbor) + +/*---------------------------------- + + FOREACHpoint_( points ) { ... } + assign 'point' to each point in points set + + declare: + pointT *point, **pointp; + + see: + FOREACHsetelement_ +*/ +#define FOREACHpoint_(points) FOREACHsetelement_(pointT, points, point) + +/*---------------------------------- + + FOREACHridge_( ridges ) { ... } + assign 'ridge' to each ridge in ridges set + + declare: + ridgeT *ridge, **ridgep; + + see: + FOREACHsetelement_ +*/ +#define FOREACHridge_(ridges) FOREACHsetelement_(ridgeT, ridges, ridge) + +/*---------------------------------- + + FOREACHvertex_( vertices ) { ... } + assign 'vertex' to each vertex in vertices set + + declare: + vertexT *vertex, **vertexp; + + see: + FOREACHsetelement_ +*/ +#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex) + +/*---------------------------------- + + FOREACHfacet_i_( facets ) { ... } + assign 'facet' and 'facet_i' for each facet in facets set + + declare: + facetT *facet; + int facet_n, facet_i; + + see: + FOREACHsetelement_i_ +*/ +#define FOREACHfacet_i_(facets) FOREACHsetelement_i_(facetT, facets, facet) + +/*---------------------------------- + + FOREACHneighbor_i_( facet ) { ... } + assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors + + FOREACHneighbor_i_( vertex ) { ... } + assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors + + declare: + facetT *neighbor; + int neighbor_n, neighbor_i; + + see: + FOREACHsetelement_i_ +*/ +#define FOREACHneighbor_i_(facet) FOREACHsetelement_i_(facetT, facet->neighbors, neighbor) + +/*---------------------------------- + + FOREACHpoint_i_( points ) { ... } + assign 'point' and 'point_i' for each point in points set + + declare: + pointT *point; + int point_n, point_i; + + see: + FOREACHsetelement_i_ +*/ +#define FOREACHpoint_i_(points) FOREACHsetelement_i_(pointT, points, point) + +/*---------------------------------- + + FOREACHridge_i_( ridges ) { ... } + assign 'ridge' and 'ridge_i' for each ridge in ridges set + + declare: + ridgeT *ridge; + int ridge_n, ridge_i; + + see: + FOREACHsetelement_i_ +*/ +#define FOREACHridge_i_(ridges) FOREACHsetelement_i_(ridgeT, ridges, ridge) + +/*---------------------------------- + + FOREACHvertex_i_( vertices ) { ... } + assign 'vertex' and 'vertex_i' for each vertex in vertices set + + declare: + vertexT *vertex; + int vertex_n, vertex_i; + + see: + FOREACHsetelement_i_ +*/ +#define FOREACHvertex_i_(vertices) FOREACHsetelement_i_(vertexT, vertices,vertex) + +/********* -libqhull.c prototypes (duplicated from qhull_a.h) **********************/ + +void qh_qhull(void); +boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist); +void qh_printsummary(FILE *fp); + +/********* -user.c prototypes (alphabetical) **********************/ + +void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge); +void qh_errprint(const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex); +int qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile); +void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall); +void qh_printhelp_degenerate(FILE *fp); +void qh_printhelp_narrowhull(FILE *fp, realT minangle); +void qh_printhelp_singular(FILE *fp); +void qh_user_memsizes(void); + +/********* -usermem.c prototypes (alphabetical) **********************/ +void qh_exit(int exitcode); +void qh_free(void *mem); +void *qh_malloc(size_t size); + +/********* -userprintf.c and userprintf_rbox.c prototypes **********************/ +void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ); +void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ); + +/***** -geom.c/geom2.c/random.c prototypes (duplicated from geom.h, random.h) ****************/ + +facetT *qh_findbest(pointT *point, facetT *startfacet, + boolT bestoutside, boolT newfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart); +facetT *qh_findbestnew(pointT *point, facetT *startfacet, + realT *dist, boolT bestoutside, boolT *isoutside, int *numpart); +boolT qh_gram_schmidt(int dim, realT **rows); +void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane); +void qh_printsummary(FILE *fp); +void qh_projectinput(void); +void qh_randommatrix(realT *buffer, int dim, realT **row); +void qh_rotateinput(realT **rows); +void qh_scaleinput(void); +void qh_setdelaunay(int dim, int count, pointT *points); +coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible); + +/***** -global.c prototypes (alphabetical) ***********************/ + +unsigned long qh_clock(void); +void qh_checkflags(char *command, char *hiddenflags); +void qh_clear_outputflags(void); +void qh_freebuffers(void); +void qh_freeqhull(boolT allmem); +void qh_freeqhull2(boolT allmem); +void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]); +void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_init_qhull_command(int argc, char *argv[]); +void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_initflags(char *command); +void qh_initqhull_buffers(void); +void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_initqhull_mem(void); +void qh_initqhull_outputflags(void); +void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile); +void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile); +void qh_initthresholds(char *command); +void qh_option(const char *option, int *i, realT *r); +#if qh_QHpointer +void qh_restore_qhull(qhT **oldqh); +qhT *qh_save_qhull(void); +#endif + +/***** -io.c prototypes (duplicated from io.h) ***********************/ + +void dfacet( unsigned id); +void dvertex( unsigned id); +void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); +void qh_produce_output(void); +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc); + + +/********* -mem.c prototypes (duplicated from mem.h) **********************/ + +void qh_meminit(FILE *ferr); +void qh_memfreeshort(int *curlong, int *totlong); + +/********* -poly.c/poly2.c prototypes (duplicated from poly.h) **********************/ + +void qh_check_output(void); +void qh_check_points(void); +setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets); +facetT *qh_findbestfacet(pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside); +vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp); +pointT *qh_point(int id); +setT *qh_pointfacet(void /*qh.facet_list*/); +int qh_pointid(pointT *point); +setT *qh_pointvertex(void /*qh.facet_list*/); +void qh_setvoronoi_all(void); +void qh_triangulate(void /*qh facet_list*/); + +/********* -rboxpoints.c prototypes **********************/ +int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command); +void qh_errexit_rbox(int exitcode); + +/********* -stat.c prototypes (duplicated from stat.h) **********************/ + +void qh_collectstatistics(void); +void qh_printallstatistics(FILE *fp, const char *string); + +#endif /* qhDEFlibqhull */ diff --git a/extern/qhull/mem.c b/extern/qhull/mem.c new file mode 100644 index 000000000000..f0a8a0fc3273 --- /dev/null +++ b/extern/qhull/mem.c @@ -0,0 +1,542 @@ +/* --------------------------------- + + mem.c + memory management routines for qhull + + This is a standalone program. + + To initialize memory: + + qh_meminit(stderr); + qh_meminitbuffers(qh IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf); + qh_memsize((int)sizeof(facetT)); + qh_memsize((int)sizeof(facetT)); + ... + qh_memsetup(); + + To free up all memory buffers: + qh_memfreeshort(&curlong, &totlong); + + if qh_NOmem, + malloc/free is used instead of mem.c + + notes: + uses Quickfit algorithm (freelists for commonly allocated sizes) + assumes small sizes for freelists (it discards the tail of memory buffers) + + see: + qh-mem.htm and mem.h + global.c (qh_initbuffers) for an example of using mem.c + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/mem.c#4 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#include "mem.h" +#include+#include +#include + +#ifndef qhDEFlibqhull +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +#pragma warning( disable : 4127) /* conditional expression is constant */ +#pragma warning( disable : 4706) /* assignment within conditional function */ +#endif +void qh_errexit(int exitcode, facetT *, ridgeT *); +void qh_exit(int exitcode); +void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ); +void qh_free(void *mem); +void *qh_malloc(size_t size); +#endif + +/*============ -global data structure ============== + see mem.h for definition +*/ + +qhmemT qhmem= {0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0}; /* remove "= {0}" if this causes a compiler error */ + +#ifndef qh_NOmem + +/*============= internal functions ==============*/ + +static int qh_intcompare(const void *i, const void *j); + +/*========== functions in alphabetical order ======== */ + +/*--------------------------------- + + qh_intcompare( i, j ) + used by qsort and bsearch to compare two integers +*/ +static int qh_intcompare(const void *i, const void *j) { + return(*((const int *)i) - *((const int *)j)); +} /* intcompare */ + + +/*---------------------------------- + + qh_memalloc( insize ) + returns object of insize bytes + qhmem is the global memory structure + + returns: + pointer to allocated memory + errors if insufficient memory + + notes: + use explicit type conversion to avoid type warnings on some compilers + actual object may be larger than insize + use qh_memalloc_() for inline code for quick allocations + logs allocations if 'T5' + + design: + if size < qhmem.LASTsize + if qhmem.freelists[size] non-empty + return first object on freelist + else + round up request to size of qhmem.freelists[size] + allocate new allocation buffer if necessary + allocate object from allocation buffer + else + allocate object with qh_malloc() in user.c +*/ +void *qh_memalloc(int insize) { + void **freelistp, *newbuffer; + int idx, size, n; + int outsize, bufsize; + void *object; + + if (insize<0) { + qh_fprintf(qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d). Did int overflow due to high-D?\n", insize); /* WARN64 */ + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (insize>=0 && insize <= qhmem.LASTsize) { + idx= qhmem.indextable[insize]; + outsize= qhmem.sizetable[idx]; + qhmem.totshort += outsize; + freelistp= qhmem.freelists+idx; + if ((object= *freelistp)) { + qhmem.cntquick++; + qhmem.totfree -= outsize; + *freelistp= *((void **)*freelistp); /* replace freelist with next object */ +#ifdef qh_TRACEshort + n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort); +#endif + return(object); + }else { + qhmem.cntshort++; + if (outsize > qhmem .freesize) { + qhmem .totdropped += qhmem .freesize; + if (!qhmem.curbuffer) + bufsize= qhmem.BUFinit; + else + bufsize= qhmem.BUFsize; + if (!(newbuffer= qh_malloc((size_t)bufsize))) { + qh_fprintf(qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + *((void **)newbuffer)= qhmem.curbuffer; /* prepend newbuffer to curbuffer + list */ + qhmem.curbuffer= newbuffer; + size= (sizeof(void **) + qhmem.ALIGNmask) & ~qhmem.ALIGNmask; + qhmem.freemem= (void *)((char *)newbuffer+size); + qhmem.freesize= bufsize - size; + qhmem.totbuffer += bufsize - size; /* easier to check */ + /* Periodically test totbuffer. It matches at beginning and exit of every call */ + n = qhmem.totshort + qhmem.totfree + qhmem.totdropped + qhmem.freesize - outsize; + if (qhmem.totbuffer != n) { + qh_fprintf(qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qhmem.totbuffer, n); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + } + object= qhmem.freemem; + qhmem.freemem= (void *)((char *)qhmem.freemem + outsize); + qhmem.freesize -= outsize; + qhmem.totunused += outsize - insize; +#ifdef qh_TRACEshort + n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort); +#endif + return object; + } + }else { /* long allocation */ + if (!qhmem.indextable) { + qh_fprintf(qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n"); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + outsize= insize; + qhmem .cntlong++; + qhmem .totlong += outsize; + if (qhmem.maxlong < qhmem.totlong) + qhmem.maxlong= qhmem.totlong; + if (!(object= qh_malloc((size_t)outsize))) { + qh_fprintf(qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, outsize, qhmem.totlong, qhmem.cntlong-qhmem.freelong); + } + return(object); +} /* memalloc */ + + +/*---------------------------------- + + qh_memfree( object, insize ) + free up an object of size bytes + size is insize from qh_memalloc + + notes: + object may be NULL + type checking warns if using (void **)object + use qh_memfree_() for quick free's of small objects + + design: + if size <= qhmem.LASTsize + append object to corresponding freelist + else + call qh_free(object) +*/ +void qh_memfree(void *object, int insize) { + void **freelistp; + int idx, outsize; + + if (!object) + return; + if (insize <= qhmem.LASTsize) { + qhmem .freeshort++; + idx= qhmem.indextable[insize]; + outsize= qhmem.sizetable[idx]; + qhmem .totfree += outsize; + qhmem .totshort -= outsize; + freelistp= qhmem.freelists + idx; + *((void **)object)= *freelistp; + *freelistp= object; +#ifdef qh_TRACEshort + idx= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort); +#endif + }else { + qhmem .freelong++; + qhmem .totlong -= insize; + qh_free(object); + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong); + } +} /* memfree */ + + +/*--------------------------------- + + qh_memfreeshort( curlong, totlong ) + frees up all short and qhmem memory allocations + + returns: + number and size of current long allocations + + see: + qh_freeqhull(allMem) + qh_memtotal(curlong, totlong, curshort, totshort, maxlong, totbuffer); +*/ +void qh_memfreeshort(int *curlong, int *totlong) { + void *buffer, *nextbuffer; + FILE *ferr; + + *curlong= qhmem .cntlong - qhmem .freelong; + *totlong= qhmem .totlong; + for (buffer= qhmem.curbuffer; buffer; buffer= nextbuffer) { + nextbuffer= *((void **) buffer); + qh_free(buffer); + } + qhmem.curbuffer= NULL; + if (qhmem .LASTsize) { + qh_free(qhmem .indextable); + qh_free(qhmem .freelists); + qh_free(qhmem .sizetable); + } + ferr= qhmem.ferr; + memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */ + qhmem.ferr= ferr; +} /* memfreeshort */ + + +/*---------------------------------- + + qh_meminit( ferr ) + initialize qhmem and test sizeof( void*) +*/ +void qh_meminit(FILE *ferr) { + + memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */ + qhmem.ferr= ferr; + if (sizeof(void*) < sizeof(int)) { + qh_fprintf(ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int)); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + if (sizeof(void*) > sizeof(ptr_intT)) { + qh_fprintf(ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT)); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } +} /* meminit */ + +/*--------------------------------- + + qh_meminitbuffers( tracelevel, alignment, numsizes, bufsize, bufinit ) + initialize qhmem + if tracelevel >= 5, trace memory allocations + alignment= desired address alignment for memory allocations + numsizes= number of freelists + bufsize= size of additional memory buffers for short allocations + bufinit= size of initial memory buffer for short allocations +*/ +void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) { + + qhmem.IStracing= tracelevel; + qhmem.NUMsizes= numsizes; + qhmem.BUFsize= bufsize; + qhmem.BUFinit= bufinit; + qhmem.ALIGNmask= alignment-1; + if (qhmem.ALIGNmask & ~qhmem.ALIGNmask) { + qh_fprintf(qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int)); + qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *)); + if (!qhmem.sizetable || !qhmem.freelists) { + qh_fprintf(qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n"); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (qhmem.IStracing >= 1) + qh_fprintf(qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment); +} /* meminitbuffers */ + +/*--------------------------------- + + qh_memsetup() + set up memory after running memsize() +*/ +void qh_memsetup(void) { + int k,i; + + qsort(qhmem.sizetable, (size_t)qhmem.TABLEsize, sizeof(int), qh_intcompare); + qhmem.LASTsize= qhmem.sizetable[qhmem.TABLEsize-1]; + if (qhmem .LASTsize >= qhmem .BUFsize || qhmem.LASTsize >= qhmem .BUFinit) { + qh_fprintf(qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n", + qhmem .LASTsize, qhmem .BUFsize, qhmem .BUFinit); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (!(qhmem.indextable= (int *)qh_malloc((qhmem.LASTsize+1) * sizeof(int)))) { + qh_fprintf(qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n"); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + for (k=qhmem.LASTsize+1; k--; ) + qhmem.indextable[k]= k; + i= 0; + for (k=0; k <= qhmem.LASTsize; k++) { + if (qhmem.indextable[k] <= qhmem.sizetable[i]) + qhmem.indextable[k]= i; + else + qhmem.indextable[k]= ++i; + } +} /* memsetup */ + +/*--------------------------------- + + qh_memsize( size ) + define a free list for this size +*/ +void qh_memsize(int size) { + int k; + + if (qhmem .LASTsize) { + qh_fprintf(qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n"); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + size= (size + qhmem.ALIGNmask) & ~qhmem.ALIGNmask; + for (k=qhmem.TABLEsize; k--; ) { + if (qhmem.sizetable[k] == size) + return; + } + if (qhmem.TABLEsize < qhmem.NUMsizes) + qhmem.sizetable[qhmem.TABLEsize++]= size; + else + qh_fprintf(qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qhmem.NUMsizes); +} /* memsize */ + + +/*--------------------------------- + + qh_memstatistics( fp ) + print out memory statistics + + Verifies that qhmem.totfree == sum of freelists +*/ +void qh_memstatistics(FILE *fp) { + int i, count, totfree= 0; + void *object; + + for (i=0; i < qhmem.TABLEsize; i++) { + count=0; + for (object= qhmem .freelists[i]; object; object= *((void **)object)) + count++; + totfree += qhmem.sizetable[i] * count; + } + if (totfree != qhmem .totfree) { + qh_fprintf(qhmem.ferr, 6211, "qh_memstatistics internal error: totfree %d not equal to freelist total %d\n", qhmem.totfree, totfree); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + qh_fprintf(fp, 9278, "\nmemory statistics:\n\ +%7d quick allocations\n\ +%7d short allocations\n\ +%7d long allocations\n\ +%7d short frees\n\ +%7d long frees\n\ +%7d bytes of short memory in use\n\ +%7d bytes of short memory in freelists\n\ +%7d bytes of dropped short memory\n\ +%7d bytes of unused short memory (estimated)\n\ +%7d bytes of long memory allocated (max, except for input)\n\ +%7d bytes of long memory in use (in %d pieces)\n\ +%7d bytes of short memory buffers (minus links)\n\ +%7d bytes per short memory buffer (initially %d bytes)\n", + qhmem .cntquick, qhmem .cntshort, qhmem .cntlong, + qhmem .freeshort, qhmem .freelong, + qhmem .totshort, qhmem .totfree, + qhmem .totdropped + qhmem .freesize, qhmem .totunused, + qhmem .maxlong, qhmem .totlong, qhmem .cntlong - qhmem .freelong, + qhmem .totbuffer, qhmem .BUFsize, qhmem .BUFinit); + if (qhmem.cntlarger) { + qh_fprintf(fp, 9279, "%7d calls to qh_setlarger\n%7.2g average copy size\n", + qhmem.cntlarger, ((float)qhmem.totlarger)/(float)qhmem.cntlarger); + qh_fprintf(fp, 9280, " freelists(bytes->count):"); + } + for (i=0; i < qhmem.TABLEsize; i++) { + count=0; + for (object= qhmem .freelists[i]; object; object= *((void **)object)) + count++; + qh_fprintf(fp, 9281, " %d->%d", qhmem.sizetable[i], count); + } + qh_fprintf(fp, 9282, "\n\n"); +} /* memstatistics */ + + +/*--------------------------------- + + qh_NOmem + turn off quick-fit memory allocation + + notes: + uses qh_malloc() and qh_free() instead +*/ +#else /* qh_NOmem */ + +void *qh_memalloc(int insize) { + void *object; + + if (!(object= qh_malloc((size_t)insize))) { + qh_fprintf(qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n"); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + qhmem .cntlong++; + qhmem .totlong += insize; + if (qhmem.maxlong < qhmem.totlong) + qhmem.maxlong= qhmem.totlong; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong); + return object; +} + +void qh_memfree(void *object, int insize) { + + if (!object) + return; + qh_free(object); + qhmem .freelong++; + qhmem .totlong -= insize; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong); +} + +void qh_memfreeshort(int *curlong, int *totlong) { + *totlong= qhmem .totlong; + *curlong= qhmem .cntlong - qhmem .freelong; + memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */ +} + +void qh_meminit(FILE *ferr) { + + memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */ + qhmem.ferr= ferr; + if (sizeof(void*) < sizeof(int)) { + qh_fprintf(ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int)); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } +} + +void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) { + + qhmem.IStracing= tracelevel; +} + +void qh_memsetup(void) { + +} + +void qh_memsize(int size) { + +} + +void qh_memstatistics(FILE *fp) { + + qh_fprintf(fp, 9409, "\nmemory statistics:\n\ +%7d long allocations\n\ +%7d long frees\n\ +%7d bytes of long memory allocated (max, except for input)\n\ +%7d bytes of long memory in use (in %d pieces)\n", + qhmem .cntlong, + qhmem .freelong, + qhmem .maxlong, qhmem .totlong, qhmem .cntlong - qhmem .freelong); +} + +#endif /* qh_NOmem */ + +/*--------------------------------- + + qh_memtotal( totlong, curlong, totshort, curshort, maxlong, totbuffer ) + Return the total, allocated long and short memory + + returns: + Returns the total current bytes of long and short allocations + Returns the current count of long and short allocations + Returns the maximum long memory and total short buffer (minus one link per buffer) + Does not error (UsingLibQhull.cpp) +*/ +void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) { + *totlong= qhmem .totlong; + *curlong= qhmem .cntlong - qhmem .freelong; + *totshort= qhmem .totshort; + *curshort= qhmem .cntshort + qhmem .cntquick - qhmem .freeshort; + *maxlong= qhmem .maxlong; + *totbuffer= qhmem .totbuffer; +} /* memtotlong */ diff --git a/extern/qhull/mem.h b/extern/qhull/mem.h new file mode 100644 index 000000000000..d65cbe128e48 --- /dev/null +++ b/extern/qhull/mem.h @@ -0,0 +1,219 @@ +/* --------------------------------- + + mem.h + prototypes for memory management functions + + see qh-mem.htm, mem.c and qset.h + + for error handling, writes message and calls + qh_errexit(qhmem_ERRmem, NULL, NULL) if insufficient memory + and + qh_errexit(qhmem_ERRqhull, NULL, NULL) otherwise + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/mem.h#4 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#ifndef qhDEFmem +#define qhDEFmem 1 + +#include+ +/*--------------------------------- + + qh_NOmem + turn off quick-fit memory allocation + + notes: + mem.c implements Quickfit memory allocation for about 20% time + savings. If it fails on your machine, try to locate the + problem, and send the answer to qhull@qhull.org. If this can + not be done, define qh_NOmem to use malloc/free instead. + + #define qh_NOmem +*/ + +/*--------------------------------- + +qh_TRACEshort +Trace short and quick memory allocations at T5 + +*/ +#define qh_TRACEshort + +/*------------------------------------------- + to avoid bus errors, memory allocation must consider alignment requirements. + malloc() automatically takes care of alignment. Since mem.c manages + its own memory, we need to explicitly specify alignment in + qh_meminitbuffers(). + + A safe choice is sizeof(double). sizeof(float) may be used if doubles + do not occur in data structures and pointers are the same size. Be careful + of machines (e.g., DEC Alpha) with large pointers. If gcc is available, + use __alignof__(double) or fmax_(__alignof__(float), __alignof__(void *)). + + see qh_MEMalign in user.h for qhull's alignment +*/ + +#define qhmem_ERRmem 4 /* matches qh_ERRmem in libqhull.h */ +#define qhmem_ERRqhull 5 /* matches qh_ERRqhull in libqhull.h */ + +/*---------------------------------- + + ptr_intT + for casting a void * to an integer-type that holds a pointer + Used for integer expressions (e.g., computing qh_gethash() in poly.c) + + notes: + WARN64 -- these notes indicate 64-bit issues + On 64-bit machines, a pointer may be larger than an 'int'. + qh_meminit()/mem.c checks that 'ptr_intT' holds a 'void*' + ptr_intT is typically a signed value, but not necessarily so + size_t is typically unsigned, but should match the parameter type + Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc. + This matches Qt convention and is easier to work with. +*/ +#if _MSC_VER && defined(_WIN64) +typedef long long ptr_intT; +#else +typedef long ptr_intT; +#endif + +/*---------------------------------- + + qhmemT + global memory structure for mem.c + + notes: + users should ignore qhmem except for writing extensions + qhmem is allocated in mem.c + + qhmem could be swapable like qh and qhstat, but then + multiple qh's and qhmem's would need to keep in synch. + A swapable qhmem would also waste memory buffers. As long + as memory operations are atomic, there is no problem with + multiple qh structures being active at the same time. + If you need separate address spaces, you can swap the + contents of qhmem. +*/ +typedef struct qhmemT qhmemT; +extern qhmemT qhmem; + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* defined in qset.h */ +#endif + +/* Update qhmem in mem.c if add or remove fields */ +struct qhmemT { /* global memory management variables */ + int BUFsize; /* size of memory allocation buffer */ + int BUFinit; /* initial size of memory allocation buffer */ + int TABLEsize; /* actual number of sizes in free list table */ + int NUMsizes; /* maximum number of sizes in free list table */ + int LASTsize; /* last size in free list table */ + int ALIGNmask; /* worst-case alignment, must be 2^n-1 */ + void **freelists; /* free list table, linked by offset 0 */ + int *sizetable; /* size of each freelist */ + int *indextable; /* size->index table */ + void *curbuffer; /* current buffer, linked by offset 0 */ + void *freemem; /* free memory in curbuffer */ + int freesize; /* size of freemem in bytes */ + setT *tempstack; /* stack of temporary memory, managed by users */ + FILE *ferr; /* file for reporting errors, only user is qh_fprintf() */ + int IStracing; /* =5 if tracing memory allocations */ + int cntquick; /* count of quick allocations */ + /* Note: removing statistics doesn't effect speed */ + int cntshort; /* count of short allocations */ + int cntlong; /* count of long allocations */ + int freeshort; /* count of short memfrees */ + int freelong; /* count of long memfrees */ + int totbuffer; /* total short memory buffers minus buffer links */ + int totdropped; /* total dropped memory at end of short memory buffers (e.g., freesize) */ + int totfree; /* total size of free, short memory on freelists */ + int totlong; /* total size of long memory in use */ + int maxlong; /* maximum totlong */ + int totshort; /* total size of short memory in use */ + int totunused; /* total unused short memory (estimated, short size - request size of first allocations) */ + int cntlarger; /* count of setlarger's */ + int totlarger; /* total copied by setlarger */ +}; + + +/*==================== -macros ====================*/ + +/*---------------------------------- + + qh_memalloc_(insize, object, type) + returns object of size bytes + assumes size<=qhmem.LASTsize and void **freelistp is a temp +*/ + +#if defined qh_NOmem +#define qh_memalloc_(insize, freelistp, object, type) {\ + object= (type*)qh_memalloc(insize); } +#elif defined qh_TRACEshort +#define qh_memalloc_(insize, freelistp, object, type) {\ + freelistp= NULL; /* Avoid warnings */ \ + object= (type*)qh_memalloc(insize); } +#else /* !qh_NOmem */ + +#define qh_memalloc_(insize, freelistp, object, type) {\ + freelistp= qhmem.freelists + qhmem.indextable[insize];\ + if ((object= (type*)*freelistp)) {\ + qhmem.totshort += qhmem.sizetable[qhmem.indextable[insize]]; \ + qhmem.totfree -= qhmem.sizetable[qhmem.indextable[insize]]; \ + qhmem.cntquick++; \ + *freelistp= *((void **)*freelistp);\ + }else object= (type*)qh_memalloc(insize);} +#endif + +/*---------------------------------- + + qh_memfree_(object, insize) + free up an object + + notes: + object may be NULL + assumes size<=qhmem.LASTsize and void **freelistp is a temp +*/ +#if defined qh_NOmem +#define qh_memfree_(object, insize, freelistp) {\ + qh_memfree(object, insize); } +#elif defined qh_TRACEshort +#define qh_memfree_(object, insize, freelistp) {\ + freelistp= NULL; /* Avoid warnings */ \ + qh_memfree(object, insize); } +#else /* !qh_NOmem */ + +#define qh_memfree_(object, insize, freelistp) {\ + if (object) { \ + qhmem .freeshort++;\ + freelistp= qhmem.freelists + qhmem.indextable[insize];\ + qhmem.totshort -= qhmem.sizetable[qhmem.indextable[insize]]; \ + qhmem.totfree += qhmem.sizetable[qhmem.indextable[insize]]; \ + *((void **)object)= *freelistp;\ + *freelistp= object;}} +#endif + +/*=============== prototypes in alphabetical order ============*/ + +void *qh_memalloc(int insize); +void qh_memfree(void *object, int insize); +void qh_memfreeshort(int *curlong, int *totlong); +void qh_meminit(FILE *ferr); +void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, + int bufsize, int bufinit); +void qh_memsetup(void); +void qh_memsize(int size); +void qh_memstatistics(FILE *fp); +void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer); + +#endif /* qhDEFmem */ diff --git a/extern/qhull/merge.c b/extern/qhull/merge.c new file mode 100644 index 000000000000..71215d772a4b --- /dev/null +++ b/extern/qhull/merge.c @@ -0,0 +1,3622 @@ +/* --------------------------------- + + merge.c + merges non-convex facets + + see qh-merge.htm and merge.h + + other modules call qh_premerge() and qh_postmerge() + + the user may call qh_postmerge() to perform additional merges. + + To remove deleted facets and vertices (qhull() in libqhull.c): + qh_partitionvisible(!qh_ALL, &numoutside); // visible_list, newfacet_list + qh_deletevisible(); // qh.visible_list + qh_resetlists(False, qh_RESETvisible); // qh.visible_list newvertex_list newfacet_list + + assumes qh.CENTERtype= centrum + + merges occur in qh_mergefacet and in qh_mergecycle + vertex->neighbors not set until the first merge occurs + + Copyright (c) 1993-2012 C.B. Barber. + $Id: //main/2011/qhull/src/libqhull/merge.c#4 $$Change: 1490 $ + $DateTime: 2012/02/19 20:27:01 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +#ifndef qh_NOmerge + +/*===== functions(alphabetical after premerge and postmerge) ======*/ + +/*--------------------------------- + + qh_premerge( apex, maxcentrum ) + pre-merge nonconvex facets in qh.newfacet_list for apex + maxcentrum defines coplanar and concave (qh_test_appendmerge) + + returns: + deleted facets added to qh.visible_list with facet->visible set + + notes: + uses globals, qh.MERGEexact, qh.PREmerge + + design: + mark duplicate ridges in qh.newfacet_list + merge facet cycles in qh.newfacet_list + merge duplicate ridges and concave facets in qh.newfacet_list + check merged facet cycles for degenerate and redundant facets + merge degenerate and redundant facets + collect coplanar and concave facets + merge concave, coplanar, degenerate, and redundant facets +*/ +void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) { + boolT othermerge= False; + facetT *newfacet; + + if (qh ZEROcentrum && qh_checkzero(!qh_ALL)) + return; + trace2((qh ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n", + maxcentrum, maxangle, apex->id, getid_(qh newfacet_list))); + if (qh IStracing >= 4 && qh num_facets < 50) + qh_printlists(); + qh centrum_radius= maxcentrum; + qh cos_max= maxangle; + qh degen_mergeset= qh_settemp(qh TEMPsize); + qh facet_mergeset= qh_settemp(qh TEMPsize); + if (qh hull_dim >=3) { + qh_mark_dupridges(qh newfacet_list); /* facet_mergeset */ + qh_mergecycle_all(qh newfacet_list, &othermerge); + qh_forcedmerges(&othermerge /* qh facet_mergeset */); + FORALLnew_facets { /* test samecycle merges */ + if (!newfacet->simplicial && !newfacet->mergeridge) + qh_degen_redundant_neighbors(newfacet, NULL); + } + if (qh_merge_degenredundant()) + othermerge= True; + }else /* qh hull_dim == 2 */ + qh_mergecycle_all(qh newfacet_list, &othermerge); + qh_flippedmerges(qh newfacet_list, &othermerge); + if (!qh MERGEexact || zzval_(Ztotmerge)) { + zinc_(Zpremergetot); + qh POSTmerging= False; + qh_getmergeset_initial(qh newfacet_list); + qh_all_merges(othermerge, False); + } + qh_settempfree(&qh facet_mergeset); + qh_settempfree(&qh degen_mergeset); +} /* premerge */ + +/*--------------------------------- + + qh_postmerge( reason, maxcentrum, maxangle, vneighbors ) + post-merge nonconvex facets as defined by maxcentrum and maxangle + 'reason' is for reporting progress + if vneighbors, + calls qh_test_vneighbors at end of qh_all_merge + if firstmerge, + calls qh_reducevertices before qh_getmergeset + + returns: + if first call (qh.visible_list != qh.facet_list), + builds qh.facet_newlist, qh.newvertex_list + deleted facets added to qh.visible_list with facet->visible + qh.visible_list == qh.facet_list + + notes: + + + design: + if first call + set qh.visible_list and qh.newfacet_list to qh.facet_list + add all facets to qh.newfacet_list + mark non-simplicial facets, facet->newmerge + set qh.newvertext_list to qh.vertex_list + add all vertices to qh.newvertex_list + if a pre-merge occured + set vertex->delridge {will retest the ridge} + if qh.MERGEexact + call qh_reducevertices() + if no pre-merging + merge flipped facets + determine non-convex facets + merge all non-convex facets +*/ +void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { + facetT *newfacet; + boolT othermerges= False; + vertexT *vertex; + + if (qh REPORTfreq || qh IStracing) { + qh_buildtracing(NULL, NULL); + qh_printsummary(qh ferr); + if (qh PRINTstatistics) + qh_printallstatistics(qh ferr, "reason"); + qh_fprintf(qh ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n", + reason, maxcentrum, maxangle); + } + trace2((qh ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n", + vneighbors)); + qh centrum_radius= maxcentrum; + qh cos_max= maxangle; + qh POSTmerging= True; + qh degen_mergeset= qh_settemp(qh TEMPsize); + qh facet_mergeset= qh_settemp(qh TEMPsize); + if (qh visible_list != qh facet_list) { /* first call */ + qh NEWfacets= True; + qh visible_list= qh newfacet_list= qh facet_list; + FORALLnew_facets { + newfacet->newfacet= True; + if (!newfacet->simplicial) + newfacet->newmerge= True; + zinc_(Zpostfacets); + } + qh newvertex_list= qh vertex_list; + FORALLvertices + vertex->newlist= True; + if (qh VERTEXneighbors) { /* a merge has occurred */ + FORALLvertices + vertex->delridge= True; /* test for redundant, needed? */ + if (qh MERGEexact) { + if (qh hull_dim <= qh_DIMreduceBuild) + qh_reducevertices(); /* was skipped during pre-merging */ + } + } + if (!qh PREmerge && !qh MERGEexact) + qh_flippedmerges(qh newfacet_list, &othermerges); + } + qh_getmergeset_initial(qh newfacet_list); + qh_all_merges(False, vneighbors); + qh_settempfree(&qh facet_mergeset); + qh_settempfree(&qh degen_mergeset); +} /* post_merge */ + +/*--------------------------------- + + qh_all_merges( othermerge, vneighbors ) + merge all non-convex facets + + set othermerge if already merged facets (for qh_reducevertices) + if vneighbors + tests vertex neighbors for convexity at end + qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list + qh.degen_mergeset is defined + if qh.MERGEexact && !qh.POSTmerging, + does not merge coplanar facets + + returns: + deleted facets added to qh.visible_list with facet->visible + deleted vertices added qh.delvertex_list with vertex->delvertex + + notes: + unless !qh.MERGEindependent, + merges facets in independent sets + uses qh.newfacet_list as argument since merges call qh_removefacet() + + design: + while merges occur + for each merge in qh.facet_mergeset + unless one of the facets was already merged in this pass + merge the facets + test merged facets for additional merges + add merges to qh.facet_mergeset + if vertices record neighboring facets + rename redundant vertices + update qh.facet_mergeset + if vneighbors ?? + tests vertex neighbors for convexity at end +*/ +void qh_all_merges(boolT othermerge, boolT vneighbors) { + facetT *facet1, *facet2; + mergeT *merge; + boolT wasmerge= True, isreduce; + void **freelistp; /* used !qh_NOmem */ + vertexT *vertex; + mergeType mergetype; + int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0; + + trace2((qh ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n", + getid_(qh newfacet_list))); + while (True) { + wasmerge= False; + while (qh_setsize(qh facet_mergeset)) { + while ((merge= (mergeT*)qh_setdellast(qh facet_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + mergetype= merge->type; + qh_memfree_(merge, (int)sizeof(mergeT), freelistp); + if (facet1->visible || facet2->visible) /*deleted facet*/ + continue; + if ((facet1->newfacet && !facet1->tested) + || (facet2->newfacet && !facet2->tested)) { + if (qh MERGEindependent && mergetype <= MRGanglecoplanar) + continue; /* perform independent sets of merges */ + } + qh_merge_nonconvex(facet1, facet2, mergetype); + numdegenredun += qh_merge_degenredundant(); + numnewmerges++; + wasmerge= True; + if (mergetype == MRGconcave) + numconcave++; + else /* MRGcoplanar or MRGanglecoplanar */ + numcoplanar++; + } /* while setdellast */ + if (qh POSTmerging && qh hull_dim <= qh_DIMreduceBuild + && numnewmerges > qh_MAXnewmerges) { + numnewmerges= 0; + qh_reducevertices(); /* otherwise large post merges too slow */ + } + qh_getmergeset(qh newfacet_list); /* facet_mergeset */ + } /* while mergeset */ + if (qh VERTEXneighbors) { + isreduce= False; + if (qh hull_dim >=4 && qh POSTmerging) { + FORALLvertices + vertex->delridge= True; + isreduce= True; + } + if ((wasmerge || othermerge) && (!qh MERGEexact || qh POSTmerging) + && qh hull_dim <= qh_DIMreduceBuild) { + othermerge= False; + isreduce= True; + } + if (isreduce) { + if (qh_reducevertices()) { + qh_getmergeset(qh newfacet_list); /* facet_mergeset */ + continue; + } + } + } + if (vneighbors && qh_test_vneighbors(/* qh newfacet_list */)) + continue; + break; + } /* while (True) */ + if (qh CHECKfrequently && !qh MERGEexact) { + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + qh_checkconvex(qh newfacet_list, qh_ALGORITHMfault); + /* qh_checkconnect(); [this is slow and it changes the facet order] */ + qh RANDOMdist= qh old_randomdist; + } + trace1((qh ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n", + numcoplanar, numconcave, numdegenredun)); + if (qh IStracing >= 4 && qh num_facets < 50) + qh_printlists(); +} /* all_merges */ + + +/*--------------------------------- + + qh_appendmergeset( facet, neighbor, mergetype, angle ) + appends an entry to qh.facet_mergeset or qh.degen_mergeset + + angle ignored if NULL or !qh.ANGLEmerge + + returns: + merge appended to facet_mergeset or degen_mergeset + sets ->degenerate or ->redundant if degen_mergeset + + see: + qh_test_appendmerge() + + design: + allocate merge entry + if regular merge + append to qh.facet_mergeset + else if degenerate merge and qh.facet_mergeset is all degenerate + append to qh.degen_mergeset + else if degenerate merge + prepend to qh.degen_mergeset + else if redundant merge + append to qh.degen_mergeset +*/ +void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) { + mergeT *merge, *lastmerge; + void **freelistp; /* used !qh_NOmem */ + + if (facet->redundant) + return; + if (facet->degenerate && mergetype == MRGdegen) + return; + qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT); + merge->facet1= facet; + merge->facet2= neighbor; + merge->type= mergetype; + if (angle && qh ANGLEmerge) + merge->angle= *angle; + if (mergetype < MRGdegen) + qh_setappend(&(qh facet_mergeset), merge); + else if (mergetype == MRGdegen) { + facet->degenerate= True; + if (!(lastmerge= (mergeT*)qh_setlast(qh degen_mergeset)) + || lastmerge->type == MRGdegen) + qh_setappend(&(qh degen_mergeset), merge); + else + qh_setaddnth(&(qh degen_mergeset), 0, merge); + }else if (mergetype == MRGredundant) { + facet->redundant= True; + qh_setappend(&(qh degen_mergeset), merge); + }else /* mergetype == MRGmirror */ { + if (facet->redundant || neighbor->redundant) { + qh_fprintf(qh ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n", + facet->id, neighbor->id); + qh_errexit2 (qh_ERRqhull, facet, neighbor); + } + if (!qh_setequal(facet->vertices, neighbor->vertices)) { + qh_fprintf(qh ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n", + facet->id, neighbor->id); + qh_errexit2 (qh_ERRqhull, facet, neighbor); + } + facet->redundant= True; + neighbor->redundant= True; + qh_setappend(&(qh degen_mergeset), merge); + } +} /* appendmergeset */ + + +/*--------------------------------- + + qh_basevertices( samecycle ) + return temporary set of base vertices for samecycle + samecycle is first facet in the cycle + assumes apex is SETfirst_( samecycle->vertices ) + + returns: + vertices(settemp) + all ->seen are cleared + + notes: + uses qh_vertex_visit; + + design: + for each facet in samecycle + for each unseen vertex in facet->vertices + append to result +*/ +setT *qh_basevertices(facetT *samecycle) { + facetT *same; + vertexT *apex, *vertex, **vertexp; + setT *vertices= qh_settemp(qh TEMPsize); + + apex= SETfirstt_(samecycle->vertices, vertexT); + apex->visitid= ++qh vertex_visit; + FORALLsame_cycle_(samecycle) { + if (same->mergeridge) + continue; + FOREACHvertex_(same->vertices) { + if (vertex->visitid != qh vertex_visit) { + qh_setappend(&vertices, vertex); + vertex->visitid= qh vertex_visit; + vertex->seen= False; + } + } + } + trace4((qh ferr, 4019, "qh_basevertices: found %d vertices\n", + qh_setsize(vertices))); + return vertices; +} /* basevertices */ + +/*--------------------------------- + + qh_checkconnect() + check that new facets are connected + new facets are on qh.newfacet_list + + notes: + this is slow and it changes the order of the facets + uses qh.visit_id + + design: + move first new facet to end of qh.facet_list + for all newly appended facets + append unvisited neighbors to end of qh.facet_list + for all new facets + report error if unvisited +*/ +void qh_checkconnect(void /* qh newfacet_list */) { + facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp; + + facet= qh newfacet_list; + qh_removefacet(facet); + qh_appendfacet(facet); + facet->visitid= ++qh visit_id; + FORALLfacet_(facet) { + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + qh_removefacet(neighbor); + qh_appendfacet(neighbor); + neighbor->visitid= qh visit_id; + } + } + } + FORALLnew_facets { + if (newfacet->visitid == qh visit_id) + break; + qh_fprintf(qh ferr, 6094, "qhull error: f%d is not attached to the new facets\n", + newfacet->id); + errfacet= newfacet; + } + if (errfacet) + qh_errexit(qh_ERRqhull, errfacet, NULL); +} /* checkconnect */ + +/*--------------------------------- + + qh_checkzero( testall ) + check that facets are clearly convex for qh.DISTround with qh.MERGEexact + + if testall, + test all facets for qh.MERGEexact post-merging + else + test qh.newfacet_list + + if qh.MERGEexact, + allows coplanar ridges + skips convexity test while qh.ZEROall_ok + + returns: + True if all facets !flipped, !dupridge, normal + if all horizon facets are simplicial + if all vertices are clearly below neighbor + if all opposite vertices of horizon are below + clears qh.ZEROall_ok if any problems or coplanar facets + + notes: + uses qh.vertex_visit + horizon facets may define multiple new facets + + design: + for all facets in qh.newfacet_list or qh.facet_list + check for flagged faults (flipped, etc.) + for all facets in qh.newfacet_list or qh.facet_list + for each neighbor of facet + skip horizon facets for qh.newfacet_list + test the opposite vertex + if qh.newfacet_list + test the other vertices in the facet's horizon facet +*/ +boolT qh_checkzero(boolT testall) { + facetT *facet, *neighbor, **neighborp; + facetT *horizon, *facetlist; + int neighbor_i; + vertexT *vertex, **vertexp; + realT dist; + + if (testall) + facetlist= qh facet_list; + else { + facetlist= qh newfacet_list; + FORALLfacet_(facetlist) { + horizon= SETfirstt_(facet->neighbors, facetT); + if (!horizon->simplicial) + goto LABELproblem; + if (facet->flipped || facet->dupridge || !facet->normal) + goto LABELproblem; + } + if (qh MERGEexact && qh ZEROall_ok) { + trace2((qh ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n")); + return True; + } + } + FORALLfacet_(facetlist) { + qh vertex_visit++; + neighbor_i= 0; + horizon= NULL; + FOREACHneighbor_(facet) { + if (!neighbor_i && !testall) { + horizon= neighbor; + neighbor_i++; + continue; /* horizon facet tested in qh_findhorizon */ + } + vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT); + vertex->visitid= qh vertex_visit; + zzinc_(Zdistzero); + qh_distplane(vertex->point, neighbor, &dist); + if (dist >= -qh DISTround) { + qh ZEROall_ok= False; + if (!qh MERGEexact || testall || dist > qh DISTround) + goto LABELnonconvex; + } + } + if (!testall) { + FOREACHvertex_(horizon->vertices) { + if (vertex->visitid != qh vertex_visit) { + zzinc_(Zdistzero); + qh_distplane(vertex->point, facet, &dist); + if (dist >= -qh DISTround) { + qh ZEROall_ok= False; + if (!qh MERGEexact || dist > qh DISTround) + goto LABELnonconvex; + } + break; + } + } + } + } + trace2((qh ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall, + (qh MERGEexact && !testall) ? + "not concave, flipped, or duplicate ridged" : "clearly convex")); + return True; + + LABELproblem: + qh ZEROall_ok= False; + trace2((qh ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n", + facet->id)); + return False; + + LABELnonconvex: + trace2((qh ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n", + facet->id, neighbor->id, vertex->id, dist)); + return False; +} /* checkzero */ + +/*--------------------------------- + + qh_compareangle( angle1, angle2 ) + used by qsort() to order merges by angle +*/ +int qh_compareangle(const void *p1, const void *p2) { + const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); + + return((a->angle > b->angle) ? 1 : -1); +} /* compareangle */ + +/*--------------------------------- + + qh_comparemerge( merge1, merge2 ) + used by qsort() to order merges +*/ +int qh_comparemerge(const void *p1, const void *p2) { + const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); + + return(a->type - b->type); +} /* comparemerge */ + +/*--------------------------------- + + qh_comparevisit( vertex1, vertex2 ) + used by qsort() to order vertices by their visitid +*/ +int qh_comparevisit(const void *p1, const void *p2) { + const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); + + return(a->visitid - b->visitid); +} /* comparevisit */ + +/*--------------------------------- + + qh_copynonconvex( atridge ) + set non-convex flag on other ridges (if any) between same neighbors + + notes: + may be faster if use smaller ridge set + + design: + for each ridge of atridge's top facet + if ridge shares the same neighbor + set nonconvex flag +*/ +void qh_copynonconvex(ridgeT *atridge) { + facetT *facet, *otherfacet; + ridgeT *ridge, **ridgep; + + facet= atridge->top; + otherfacet= atridge->bottom; + FOREACHridge_(facet->ridges) { + if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) { + ridge->nonconvex= True; + trace4((qh ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n", + atridge->id, ridge->id)); + break; + } + } +} /* copynonconvex */ + +/*--------------------------------- + + qh_degen_redundant_facet( facet ) + check facet for degen. or redundancy + + notes: + bumps vertex_visit + called if a facet was redundant but no longer is (qh_merge_degenredundant) + qh_appendmergeset() only appends first reference to facet (i.e., redundant) + + see: + qh_degen_redundant_neighbors() + + design: + test for redundant neighbor + test for degenerate facet +*/ +void qh_degen_redundant_facet(facetT *facet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + + trace4((qh ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n", + facet->id)); + FOREACHneighbor_(facet) { + qh vertex_visit++; + FOREACHvertex_(neighbor->vertices) + vertex->visitid= qh vertex_visit; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) + break; + } + if (!vertex) { + qh_appendmergeset(facet, neighbor, MRGredundant, NULL); + trace2((qh ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id)); + return; + } + } + if (qh_setsize(facet->neighbors) < qh hull_dim) { + qh_appendmergeset(facet, facet, MRGdegen, NULL); + trace2((qh ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id)); + } +} /* degen_redundant_facet */ + + +/*--------------------------------- + + qh_degen_redundant_neighbors( facet, delfacet, ) + append degenerate and redundant neighbors to facet_mergeset + if delfacet, + only checks neighbors of both delfacet and facet + also checks current facet for degeneracy + + notes: + bumps vertex_visit + called for each qh_mergefacet() and qh_mergecycle() + merge and statistics occur in merge_nonconvex + qh_appendmergeset() only appends first reference to facet (i.e., redundant) + it appends redundant facets after degenerate ones + + a degenerate facet has fewer than hull_dim neighbors + a redundant facet's vertices is a subset of its neighbor's vertices + tests for redundant merges first (appendmergeset is nop for others) + in a merge, only needs to test neighbors of merged facet + + see: + qh_merge_degenredundant() and qh_degen_redundant_facet() + + design: + test for degenerate facet + test for redundant neighbor + test for degenerate neighbor +*/ +void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + int size; + + trace4((qh ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n", + facet->id, getid_(delfacet))); + if ((size= qh_setsize(facet->neighbors)) < qh hull_dim) { + qh_appendmergeset(facet, facet, MRGdegen, NULL); + trace2((qh ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size)); + } + if (!delfacet) + delfacet= facet; + qh vertex_visit++; + FOREACHvertex_(facet->vertices) + vertex->visitid= qh vertex_visit; + FOREACHneighbor_(delfacet) { + /* uses early out instead of checking vertex count */ + if (neighbor == facet) + continue; + FOREACHvertex_(neighbor->vertices) { + if (vertex->visitid != qh vertex_visit) + break; + } + if (!vertex) { + qh_appendmergeset(neighbor, facet, MRGredundant, NULL); + trace2((qh ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id)); + } + } + FOREACHneighbor_(delfacet) { /* redundant merges occur first */ + if (neighbor == facet) + continue; + if ((size= qh_setsize(neighbor->neighbors)) < qh hull_dim) { + qh_appendmergeset(neighbor, neighbor, MRGdegen, NULL); + trace2((qh ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id)); + } + } +} /* degen_redundant_neighbors */ + + +/*--------------------------------- + + qh_find_newvertex( oldvertex, vertices, ridges ) + locate new vertex for renaming old vertex + vertices is a set of possible new vertices + vertices sorted by number of deleted ridges + + returns: + newvertex or NULL + each ridge includes both vertex and oldvertex + vertices sorted by number of deleted ridges + + notes: + modifies vertex->visitid + new vertex is in one of the ridges + renaming will not cause a duplicate ridge + renaming will minimize the number of deleted ridges + newvertex may not be adjacent in the dual (though unlikely) + + design: + for each vertex in vertices + set vertex->visitid to number of references in ridges + remove unvisited vertices + set qh.vertex_visit above all possible values + sort vertices by number of references in ridges + add each ridge to qh.hash_table + for each vertex in vertices + look for a vertex that would not cause a duplicate ridge after a rename +*/ +vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) { + vertexT *vertex, **vertexp; + setT *newridges; + ridgeT *ridge, **ridgep; + int size, hashsize; + int hash; + +#ifndef qh_NOtrace + if (qh IStracing >= 4) { + qh_fprintf(qh ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ", + oldvertex->id); + FOREACHvertex_(vertices) + qh_fprintf(qh ferr, 8064, "v%d ", vertex->id); + FOREACHridge_(ridges) + qh_fprintf(qh ferr, 8065, "r%d ", ridge->id); + qh_fprintf(qh ferr, 8066, "\n"); + } +#endif + FOREACHvertex_(vertices) + vertex->visitid= 0; + FOREACHridge_(ridges) { + FOREACHvertex_(ridge->vertices) + vertex->visitid++; + } + FOREACHvertex_(vertices) { + if (!vertex->visitid) { + qh_setdelnth(vertices, SETindex_(vertices,vertex)); + vertexp--; /* repeat since deleted this vertex */ + } + } + qh vertex_visit += (unsigned int)qh_setsize(ridges); + if (!qh_setsize(vertices)) { + trace4((qh ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n", + oldvertex->id)); + return NULL; + } + qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(vertices), + sizeof(vertexT *), qh_comparevisit); + /* can now use qh vertex_visit */ + if (qh PRINTstatistics) { + size= qh_setsize(vertices); + zinc_(Zintersect); + zadd_(Zintersecttot, size); + zmax_(Zintersectmax, size); + } + hashsize= qh_newhashtable(qh_setsize(ridges)); + FOREACHridge_(ridges) + qh_hashridge(qh hash_table, hashsize, ridge, oldvertex); + FOREACHvertex_(vertices) { + newridges= qh_vertexridges(vertex); + FOREACHridge_(newridges) { + if (qh_hashridge_find(qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) { + zinc_(Zdupridge); + break; + } + } + qh_settempfree(&newridges); + if (!ridge) + break; /* found a rename */ + } + if (vertex) { + /* counted in qh_renamevertex */ + trace2((qh ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n", + vertex->id, oldvertex->id, qh_setsize(vertices), qh_setsize(ridges))); + }else { + zinc_(Zfindfail); + trace0((qh ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n", + oldvertex->id, qh furthest_id)); + } + qh_setfree(&qh hash_table); + return vertex; +} /* find_newvertex */ + +/*--------------------------------- + + qh_findbest_test( testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist ) + test neighbor of facet for qh_findbestneighbor() + if testcentrum, + tests centrum (assumes it is defined) + else + tests vertices + + returns: + if a better facet (i.e., vertices/centrum of facet closer to neighbor) + updates bestfacet, dist, mindist, and maxdist +*/ +void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor, + facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) { + realT dist, mindist, maxdist; + + if (testcentrum) { + zzinc_(Zbestdist); + qh_distplane(facet->center, neighbor, &dist); + dist *= qh hull_dim; /* estimate furthest vertex */ + if (dist < 0) { + maxdist= 0; + mindist= dist; + dist= -dist; + }else { + mindist= 0; + maxdist= dist; + } + }else + dist= qh_getdistance(facet, neighbor, &mindist, &maxdist); + if (dist < *distp) { + *bestfacet= neighbor; + *mindistp= mindist; + *maxdistp= maxdist; + *distp= dist; + } +} /* findbest_test */ + +/*--------------------------------- + + qh_findbestneighbor( facet, dist, mindist, maxdist ) + finds best neighbor (least dist) of a facet for merging + + returns: + returns min and max distances and their max absolute value + + notes: + avoids merging old into new + assumes ridge->nonconvex only set on one ridge between a pair of facets + could use an early out predicate but not worth it + + design: + if a large facet + will test centrum + else + will test vertices + if a large facet + test nonconvex neighbors for best merge + else + test all neighbors for the best merge + if testing centrum + get distance information +*/ +facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) { + facetT *neighbor, **neighborp, *bestfacet= NULL; + ridgeT *ridge, **ridgep; + boolT nonconvex= True, testcentrum= False; + int size= qh_setsize(facet->vertices); + + *distp= REALmax; + if (size > qh_BESTcentrum2 * qh hull_dim + qh_BESTcentrum) { + testcentrum= True; + zinc_(Zbestcentrum); + if (!facet->center) + facet->center= qh_getcentrum(facet); + } + if (size > qh hull_dim + qh_BESTnonconvex) { + FOREACHridge_(facet->ridges) { + if (ridge->nonconvex) { + neighbor= otherfacet_(ridge, facet); + qh_findbest_test(testcentrum, facet, neighbor, + &bestfacet, distp, mindistp, maxdistp); + } + } + } + if (!bestfacet) { + nonconvex= False; + FOREACHneighbor_(facet) + qh_findbest_test(testcentrum, facet, neighbor, + &bestfacet, distp, mindistp, maxdistp); + } + if (!bestfacet) { + qh_fprintf(qh ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id); + + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (testcentrum) + qh_getdistance(facet, bestfacet, mindistp, maxdistp); + trace3((qh ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n", + bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp)); + return(bestfacet); +} /* findbestneighbor */ + + +/*--------------------------------- + + qh_flippedmerges( facetlist, wasmerge ) + merge flipped facets into best neighbor + assumes qh.facet_mergeset at top of temporary stack + + returns: + no flipped facets on facetlist + sets wasmerge if merge occurred + degen/redundant merges passed through + + notes: + othermerges not needed since qh.facet_mergeset is empty before & after + keep it in case of change + + design: + append flipped facets to qh.facetmergeset + for each flipped merge + find best neighbor + merge facet into neighbor + merge degenerate and redundant facets + remove flipped merges from qh.facet_mergeset +*/ +void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) { + facetT *facet, *neighbor, *facet1; + realT dist, mindist, maxdist; + mergeT *merge, **mergep; + setT *othermerges; + int nummerge=0; + + trace4((qh ferr, 4024, "qh_flippedmerges: begin\n")); + FORALLfacet_(facetlist) { + if (facet->flipped && !facet->visible) + qh_appendmergeset(facet, facet, MRGflip, NULL); + } + othermerges= qh_settemppop(); /* was facet_mergeset */ + qh facet_mergeset= qh_settemp(qh TEMPsize); + qh_settemppush(othermerges); + FOREACHmerge_(othermerges) { + facet1= merge->facet1; + if (merge->type != MRGflip || facet1->visible) + continue; + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist); + trace0((qh ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n", + facet1->id, neighbor->id, dist, qh furthest_id)); + qh_mergefacet(facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex); + nummerge++; + if (qh PRINTstatistics) { + zinc_(Zflipped); + wadd_(Wflippedtot, dist); + wmax_(Wflippedmax, dist); + } + qh_merge_degenredundant(); + } + FOREACHmerge_(othermerges) { + if (merge->facet1->visible || merge->facet2->visible) + qh_memfree(merge, (int)sizeof(mergeT)); + else + qh_setappend(&qh facet_mergeset, merge); + } + qh_settempfree(&othermerges); + if (nummerge) + *wasmerge= True; + trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge)); +} /* flippedmerges */ + + +/*--------------------------------- + + qh_forcedmerges( wasmerge ) + merge duplicated ridges + + returns: + removes all duplicate ridges on facet_mergeset + wasmerge set if merge + qh.facet_mergeset may include non-forced merges(none for now) + qh.degen_mergeset includes degen/redun merges + + notes: + duplicate ridges occur when the horizon is pinched, + i.e. a subridge occurs in more than two horizon ridges. + could rename vertices that pinch the horizon + assumes qh_merge_degenredundant() has not be called + othermerges isn't needed since facet_mergeset is empty afterwards + keep it in case of change + + design: + for each duplicate ridge + find current facets by chasing f.replace links + determine best direction for facet + merge one facet into the other + remove duplicate ridges from qh.facet_mergeset +*/ +void qh_forcedmerges(boolT *wasmerge) { + facetT *facet1, *facet2; + mergeT *merge, **mergep; + realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2; + setT *othermerges; + int nummerge=0, numflip=0; + + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + trace4((qh ferr, 4025, "qh_forcedmerges: begin\n")); + othermerges= qh_settemppop(); /* was facet_mergeset */ + qh facet_mergeset= qh_settemp(qh TEMPsize); + qh_settemppush(othermerges); + FOREACHmerge_(othermerges) { + if (merge->type != MRGridge) + continue; + facet1= merge->facet1; + facet2= merge->facet2; + while (facet1->visible) /* must exist, no qh_merge_degenredunant */ + facet1= facet1->f.replace; /* previously merged facet */ + while (facet2->visible) + facet2= facet2->f.replace; /* previously merged facet */ + if (facet1 == facet2) + continue; + if (!qh_setin(facet2->neighbors, facet1)) { + qh_fprintf(qh ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n", + merge->facet1->id, merge->facet2->id, facet1->id, facet2->id); + qh_errexit2 (qh_ERRqhull, facet1, facet2); + } + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + dist1= qh_getdistance(facet1, facet2, &mindist1, &maxdist1); + dist2= qh_getdistance(facet2, facet1, &mindist2, &maxdist2); + trace0((qh ferr, 16, "qh_forcedmerges: duplicate ridge between f%d and f%d, dist %2.2g and reverse dist %2.2g during p%d\n", + facet1->id, facet2->id, dist1, dist2, qh furthest_id)); + if (dist1 < dist2) + qh_mergefacet(facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex); + else { + qh_mergefacet(facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex); + dist1= dist2; + facet1= facet2; + } + if (facet1->flipped) { + zinc_(Zmergeflipdup); + numflip++; + }else + nummerge++; + if (qh PRINTstatistics) { + zinc_(Zduplicate); + wadd_(Wduplicatetot, dist1); + wmax_(Wduplicatemax, dist1); + } + } + FOREACHmerge_(othermerges) { + if (merge->type == MRGridge) + qh_memfree(merge, (int)sizeof(mergeT)); + else + qh_setappend(&qh facet_mergeset, merge); + } + qh_settempfree(&othermerges); + if (nummerge) + *wasmerge= True; + trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n", + nummerge, numflip)); +} /* forcedmerges */ + + +/*--------------------------------- + + qh_getmergeset( facetlist ) + determines nonconvex facets on facetlist + tests !tested ridges and nonconvex ridges of !tested facets + + returns: + returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged + all ridges tested + + notes: + assumes no nonconvex ridges with both facets tested + uses facet->tested/ridge->tested to prevent duplicate tests + can not limit tests to modified ridges since the centrum changed + uses qh.visit_id + + see: + qh_getmergeset_initial() + + design: + for each facet on facetlist + for each ridge of facet + if untested ridge + test ridge for convexity + if non-convex + append ridge to qh.facet_mergeset + sort qh.facet_mergeset by angle +*/ +void qh_getmergeset(facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int nummerges; + + nummerges= qh_setsize(qh facet_mergeset); + trace4((qh ferr, 4026, "qh_getmergeset: started.\n")); + qh visit_id++; + FORALLfacet_(facetlist) { + if (facet->tested) + continue; + facet->visitid= qh visit_id; + facet->tested= True; /* must be non-simplicial due to merge */ + FOREACHneighbor_(facet) + neighbor->seen= False; + FOREACHridge_(facet->ridges) { + if (ridge->tested && !ridge->nonconvex) + continue; + /* if tested & nonconvex, need to append merge */ + neighbor= otherfacet_(ridge, facet); + if (neighbor->seen) { + ridge->tested= True; + ridge->nonconvex= False; + }else if (neighbor->visitid != qh visit_id) { + ridge->tested= True; + ridge->nonconvex= False; + neighbor->seen= True; /* only one ridge is marked nonconvex */ + if (qh_test_appendmerge(facet, neighbor)) + ridge->nonconvex= True; + } + } + } + nummerges= qh_setsize(qh facet_mergeset); + if (qh ANGLEmerge) + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle); + else + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge); + if (qh POSTmerging) { + zadd_(Zmergesettot2, nummerges); + }else { + zadd_(Zmergesettot, nummerges); + zmax_(Zmergesetmax, nummerges); + } + trace2((qh ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges)); +} /* getmergeset */ + + +/*--------------------------------- + + qh_getmergeset_initial( facetlist ) + determine initial qh.facet_mergeset for facets + tests all facet/neighbor pairs on facetlist + + returns: + sorted qh.facet_mergeset with nonconvex ridges + sets facet->tested, ridge->tested, and ridge->nonconvex + + notes: + uses visit_id, assumes ridge->nonconvex is False + + see: + qh_getmergeset() + + design: + for each facet on facetlist + for each untested neighbor of facet + test facet and neighbor for convexity + if non-convex + append merge to qh.facet_mergeset + mark one of the ridges as nonconvex + sort qh.facet_mergeset by angle +*/ +void qh_getmergeset_initial(facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int nummerges; + + qh visit_id++; + FORALLfacet_(facetlist) { + facet->visitid= qh visit_id; + facet->tested= True; + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + if (qh_test_appendmerge(facet, neighbor)) { + FOREACHridge_(neighbor->ridges) { + if (facet == otherfacet_(ridge, neighbor)) { + ridge->nonconvex= True; + break; /* only one ridge is marked nonconvex */ + } + } + } + } + } + FOREACHridge_(facet->ridges) + ridge->tested= True; + } + nummerges= qh_setsize(qh facet_mergeset); + if (qh ANGLEmerge) + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle); + else + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge); + if (qh POSTmerging) { + zadd_(Zmergeinittot2, nummerges); + }else { + zadd_(Zmergeinittot, nummerges); + zmax_(Zmergeinitmax, nummerges); + } + trace2((qh ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges)); +} /* getmergeset_initial */ + + +/*--------------------------------- + + qh_hashridge( hashtable, hashsize, ridge, oldvertex ) + add ridge to hashtable without oldvertex + + notes: + assumes hashtable is large enough + + design: + determine hash value for ridge without oldvertex + find next empty slot for ridge +*/ +void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) { + int hash; + ridgeT *ridgeA; + + hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, oldvertex); + while (True) { + if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) { + SETelem_(hashtable, hash)= ridge; + break; + }else if (ridgeA == ridge) + break; + if (++hash == hashsize) + hash= 0; + } +} /* hashridge */ + + +/*--------------------------------- + + qh_hashridge_find( hashtable, hashsize, ridge, vertex, oldvertex, hashslot ) + returns matching ridge without oldvertex in hashtable + for ridge without vertex + if oldvertex is NULL + matches with any one skip + + returns: + matching ridge or NULL + if no match, + if ridge already in table + hashslot= -1 + else + hashslot= next NULL index + + notes: + assumes hashtable is large enough + can't match ridge to itself + + design: + get hash value for ridge without vertex + for each hashslot + return match if ridge matches ridgeA without oldvertex +*/ +ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, + vertexT *vertex, vertexT *oldvertex, int *hashslot) { + int hash; + ridgeT *ridgeA; + + *hashslot= 0; + zinc_(Zhashridge); + hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, vertex); + while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) { + if (ridgeA == ridge) + *hashslot= -1; + else { + zinc_(Zhashridgetest); + if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex)) + return ridgeA; + } + if (++hash == hashsize) + hash= 0; + } + if (!*hashslot) + *hashslot= hash; + return NULL; +} /* hashridge_find */ + + +/*--------------------------------- + + qh_makeridges( facet ) + creates explicit ridges between simplicial facets + + returns: + facet with ridges and without qh_MERGEridge + ->simplicial is False + + notes: + allows qh_MERGEridge flag + uses existing ridges + duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges) + + see: + qh_mergecycle_ridges() + + design: + look for qh_MERGEridge neighbors + mark neighbors that already have ridges + for each unprocessed neighbor of facet + create a ridge for neighbor and facet + if any qh_MERGEridge neighbors + delete qh_MERGEridge flags (already handled by qh_mark_dupridges) +*/ +void qh_makeridges(facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int neighbor_i, neighbor_n; + boolT toporient, mergeridge= False; + + if (!facet->simplicial) + return; + trace4((qh ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id)); + facet->simplicial= False; + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) + mergeridge= True; + else + neighbor->seen= False; + } + FOREACHridge_(facet->ridges) + otherfacet_(ridge, facet)->seen= True; + FOREACHneighbor_i_(facet) { + if (neighbor == qh_MERGEridge) + continue; /* fixed by qh_mark_dupridges */ + else if (!neighbor->seen) { /* no current ridges */ + ridge= qh_newridge(); + ridge->vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim, + neighbor_i, 0); + toporient= facet->toporient ^ (neighbor_i & 0x1); + if (toporient) { + ridge->top= facet; + ridge->bottom= neighbor; + }else { + ridge->top= neighbor; + ridge->bottom= facet; + } +#if 0 /* this also works */ + flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1); + if (facet->toporient ^ (skip1 & 0x1) ^ flip) { + ridge->top= neighbor; + ridge->bottom= facet; + }else { + ridge->top= facet; + ridge->bottom= neighbor; + } +#endif + qh_setappend(&(facet->ridges), ridge); + qh_setappend(&(neighbor->ridges), ridge); + } + } + if (mergeridge) { + while (qh_setdel(facet->neighbors, qh_MERGEridge)) + ; /* delete each one */ + } +} /* makeridges */ + + +/*--------------------------------- + + qh_mark_dupridges( facetlist ) + add duplicated ridges to qh.facet_mergeset + facet->dupridge is true + + returns: + duplicate ridges on qh.facet_mergeset + ->mergeridge/->mergeridge2 set + duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge + no MERGEridges in neighbor sets + + notes: + duplicate ridges occur when the horizon is pinched, + i.e. a subridge occurs in more than two horizon ridges. + could rename vertices that pinch the horizon + uses qh.visit_id + + design: + for all facets on facetlist + if facet contains a duplicate ridge + for each neighbor of facet + if neighbor marked qh_MERGEridge (one side of the merge) + set facet->mergeridge + else + if neighbor contains a duplicate ridge + and the back link is qh_MERGEridge + append duplicate ridge to qh.facet_mergeset + for each duplicate ridge + make ridge sets in preparation for merging + remove qh_MERGEridge from neighbor set + for each duplicate ridge + restore the missing neighbor from the neighbor set that was qh_MERGEridge + add the missing ridge for this neighbor +*/ +void qh_mark_dupridges(facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + int nummerge=0; + mergeT *merge, **mergep; + + + trace4((qh ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n")); + FORALLfacet_(facetlist) { + if (facet->dupridge) { + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) { + facet->mergeridge= True; + continue; + } + if (neighbor->dupridge + && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */ + qh_appendmergeset(facet, neighbor, MRGridge, NULL); + facet->mergeridge2= True; + facet->mergeridge= True; + nummerge++; + } + } + } + } + if (!nummerge) + return; + FORALLfacet_(facetlist) { /* gets rid of qh_MERGEridge */ + if (facet->mergeridge && !facet->mergeridge2) + qh_makeridges(facet); + } + FOREACHmerge_(qh facet_mergeset) { /* restore the missing neighbors */ + if (merge->type == MRGridge) { + qh_setappend(&merge->facet2->neighbors, merge->facet1); + qh_makeridges(merge->facet1); /* and the missing ridges */ + } + } + trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n", + nummerge)); +} /* mark_dupridges */ + +/*--------------------------------- + + qh_maydropneighbor( facet ) + drop neighbor relationship if no ridge between facet and neighbor + + returns: + neighbor sets updated + appends degenerate facets to qh.facet_mergeset + + notes: + won't cause redundant facets since vertex inclusion is the same + may drop vertex and neighbor if no ridge + uses qh.visit_id + + design: + visit all neighbors with ridges + for each unvisited neighbor of facet + delete neighbor and facet from the neighbor sets + if neighbor becomes degenerate + append neighbor to qh.degen_mergeset + if facet is degenerate + append facet to qh.degen_mergeset +*/ +void qh_maydropneighbor(facetT *facet) { + ridgeT *ridge, **ridgep; + realT angledegen= qh_ANGLEdegen; + facetT *neighbor, **neighborp; + + qh visit_id++; + trace4((qh ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n", + facet->id)); + FOREACHridge_(facet->ridges) { + ridge->top->visitid= qh visit_id; + ridge->bottom->visitid= qh visit_id; + } + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + trace0((qh ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n", + facet->id, neighbor->id, qh furthest_id)); + zinc_(Zdropneighbor); + qh_setdel(facet->neighbors, neighbor); + neighborp--; /* repeat, deleted a neighbor */ + qh_setdel(neighbor->neighbors, facet); + if (qh_setsize(neighbor->neighbors) < qh hull_dim) { + zinc_(Zdropdegen); + qh_appendmergeset(neighbor, neighbor, MRGdegen, &angledegen); + trace2((qh ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id)); + } + } + } + if (qh_setsize(facet->neighbors) < qh hull_dim) { + zinc_(Zdropdegen); + qh_appendmergeset(facet, facet, MRGdegen, &angledegen); + trace2((qh ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id)); + } +} /* maydropneighbor */ + + +/*--------------------------------- + + qh_merge_degenredundant() + merge all degenerate and redundant facets + qh.degen_mergeset contains merges from qh_degen_redundant_neighbors() + + returns: + number of merges performed + resets facet->degenerate/redundant + if deleted (visible) facet has no neighbors + sets ->f.replace to NULL + + notes: + redundant merges happen before degenerate ones + merging and renaming vertices can result in degen/redundant facets + + design: + for each merge on qh.degen_mergeset + if redundant merge + if non-redundant facet merged into redundant facet + recheck facet for redundancy + else + merge redundant facet into other facet +*/ +int qh_merge_degenredundant(void) { + int size; + mergeT *merge; + facetT *bestneighbor, *facet1, *facet2; + realT dist, mindist, maxdist; + vertexT *vertex, **vertexp; + int nummerges= 0; + mergeType mergetype; + + while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + mergetype= merge->type; + qh_memfree(merge, (int)sizeof(mergeT)); + if (facet1->visible) + continue; + facet1->degenerate= False; + facet1->redundant= False; + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + if (mergetype == MRGredundant) { + zinc_(Zneighbor); + while (facet2->visible) { + if (!facet2->f.replace) { + qh_fprintf(qh ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n", + facet1->id, facet2->id); + qh_errexit2 (qh_ERRqhull, facet1, facet2); + } + facet2= facet2->f.replace; + } + if (facet1 == facet2) { + qh_degen_redundant_facet(facet1); /* in case of others */ + continue; + } + trace2((qh ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n", + facet1->id, facet2->id)); + qh_mergefacet(facet1, facet2, NULL, NULL, !qh_MERGEapex); + /* merge distance is already accounted for */ + nummerges++; + }else { /* mergetype == MRGdegen, other merges may have fixed */ + if (!(size= qh_setsize(facet1->neighbors))) { + zinc_(Zdelfacetdup); + trace2((qh ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id)); + qh_willdelete(facet1, NULL); + FOREACHvertex_(facet1->vertices) { + qh_setdel(vertex->neighbors, facet1); + if (!SETfirst_(vertex->neighbors)) { + zinc_(Zdegenvertex); + trace2((qh ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n", + vertex->id, facet1->id)); + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + } + } + nummerges++; + }else if (size < qh hull_dim) { + bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist); + trace2((qh ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n", + facet1->id, size, bestneighbor->id, dist)); + qh_mergefacet(facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + nummerges++; + if (qh PRINTstatistics) { + zinc_(Zdegen); + wadd_(Wdegentot, dist); + wmax_(Wdegenmax, dist); + } + } /* else, another merge fixed the degeneracy and redundancy tested */ + } + } + return nummerges; +} /* merge_degenredundant */ + +/*--------------------------------- + + qh_merge_nonconvex( facet1, facet2, mergetype ) + remove non-convex ridge between facet1 into facet2 + mergetype gives why the facet's are non-convex + + returns: + merges one of the facets into the best neighbor + + design: + if one of the facets is a new facet + prefer merging new facet into old facet + find best neighbors for both facets + merge the nearest facet into its best neighbor + update the statistics +*/ +void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) { + facetT *bestfacet, *bestneighbor, *neighbor; + realT dist, dist2, mindist, mindist2, maxdist, maxdist2; + + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + trace3((qh ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n", + zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype)); + /* concave or coplanar */ + if (!facet1->newfacet) { + bestfacet= facet2; /* avoid merging old facet if new is ok */ + facet2= facet1; + facet1= bestfacet; + }else + bestfacet= facet1; + bestneighbor= qh_findbestneighbor(bestfacet, &dist, &mindist, &maxdist); + neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2); + if (dist < dist2) { + qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + }else if (qh AVOIDold && !facet2->newfacet + && ((mindist >= -qh MAXcoplanar && maxdist <= qh max_outside) + || dist * 1.5 < dist2)) { + zinc_(Zavoidold); + wadd_(Wavoidoldtot, dist); + wmax_(Wavoidoldmax, dist); + trace2((qh ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n", + facet2->id, dist2, facet1->id, dist2)); + qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + }else { + qh_mergefacet(facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex); + dist= dist2; + } + if (qh PRINTstatistics) { + if (mergetype == MRGanglecoplanar) { + zinc_(Zacoplanar); + wadd_(Wacoplanartot, dist); + wmax_(Wacoplanarmax, dist); + }else if (mergetype == MRGconcave) { + zinc_(Zconcave); + wadd_(Wconcavetot, dist); + wmax_(Wconcavemax, dist); + }else { /* MRGcoplanar */ + zinc_(Zcoplanar); + wadd_(Wcoplanartot, dist); + wmax_(Wcoplanarmax, dist); + } + } +} /* merge_nonconvex */ + +/*--------------------------------- + + qh_mergecycle( samecycle, newfacet ) + merge a cycle of facets starting at samecycle into a newfacet + newfacet is a horizon facet with ->normal + samecycle facets are simplicial from an apex + + returns: + initializes vertex neighbors on first merge + samecycle deleted (placed on qh.visible_list) + newfacet at end of qh.facet_list + deleted vertices on qh.del_vertices + + see: + qh_mergefacet() + called by qh_mergecycle_all() for multiple, same cycle facets + + design: + make vertex neighbors if necessary + make ridges for newfacet + merge neighbor sets of samecycle into newfacet + merge ridges of samecycle into newfacet + merge vertex neighbors of samecycle into newfacet + make apex of samecycle the apex of newfacet + if newfacet wasn't a new facet + add its vertices to qh.newvertex_list + delete samecycle facets a make newfacet a newfacet +*/ +void qh_mergecycle(facetT *samecycle, facetT *newfacet) { + int traceonce= False, tracerestore= 0; + vertexT *apex; +#ifndef qh_NOtrace + facetT *same; +#endif + + if (newfacet->tricoplanar) { + if (!qh TRInormals) { + qh_fprintf(qh ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh_ERRqhull, newfacet, NULL); + } + newfacet->tricoplanar= False; + newfacet->keepcentrum= False; + } + if (!qh VERTEXneighbors) + qh_vertexneighbors(); + zzinc_(Ztotmerge); + if (qh REPORTfreq2 && qh POSTmerging) { + if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2) + qh_tracemerging(); + } +#ifndef qh_NOtrace + if (qh TRACEmerge == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + trace2((qh ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n", + zzval_(Ztotmerge), samecycle->id, newfacet->id)); + if (newfacet == qh tracefacet) { + tracerestore= qh IStracing; + qh IStracing= 4; + qh_fprintf(qh ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n", + zzval_(Ztotmerge), samecycle->id, newfacet->id, qh furthest_id); + traceonce= True; + } + if (qh IStracing >=4) { + qh_fprintf(qh ferr, 8069, " same cycle:"); + FORALLsame_cycle_(samecycle) + qh_fprintf(qh ferr, 8070, " f%d", same->id); + qh_fprintf(qh ferr, 8071, "\n"); + } + if (qh IStracing >=4) + qh_errprint("MERGING CYCLE", samecycle, newfacet, NULL, NULL); +#endif /* !qh_NOtrace */ + apex= SETfirstt_(samecycle->vertices, vertexT); + qh_makeridges(newfacet); + qh_mergecycle_neighbors(samecycle, newfacet); + qh_mergecycle_ridges(samecycle, newfacet); + qh_mergecycle_vneighbors(samecycle, newfacet); + if (SETfirstt_(newfacet->vertices, vertexT) != apex) + qh_setaddnth(&newfacet->vertices, 0, apex); /* apex has last id */ + if (!newfacet->newfacet) + qh_newvertices(newfacet->vertices); + qh_mergecycle_facets(samecycle, newfacet); + qh_tracemerge(samecycle, newfacet); + /* check for degen_redundant_neighbors after qh_forcedmerges() */ + if (traceonce) { + qh_fprintf(qh ferr, 8072, "qh_mergecycle: end of trace facet\n"); + qh IStracing= tracerestore; + } +} /* mergecycle */ + +/*--------------------------------- + + qh_mergecycle_all( facetlist, wasmerge ) + merge all samecycles of coplanar facets into horizon + don't merge facets with ->mergeridge (these already have ->normal) + all facets are simplicial from apex + all facet->cycledone == False + + returns: + all newfacets merged into coplanar horizon facets + deleted vertices on qh.del_vertices + sets wasmerge if any merge + + see: + calls qh_mergecycle for multiple, same cycle facets + + design: + for each facet on facetlist + skip facets with duplicate ridges and normals + check that facet is in a samecycle (->mergehorizon) + if facet only member of samecycle + sets vertex->delridge for all vertices except apex + merge facet into horizon + else + mark all facets in samecycle + remove facets with duplicate ridges from samecycle + merge samecycle into horizon (deletes facets from facetlist) +*/ +void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) { + facetT *facet, *same, *prev, *horizon; + facetT *samecycle= NULL, *nextfacet, *nextsame; + vertexT *apex, *vertex, **vertexp; + int cycles=0, total=0, facets, nummerge; + + trace2((qh ferr, 2031, "qh_mergecycle_all: begin\n")); + for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) { + if (facet->normal) + continue; + if (!facet->mergehorizon) { + qh_fprintf(qh ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + horizon= SETfirstt_(facet->neighbors, facetT); + if (facet->f.samecycle == facet) { + zinc_(Zonehorizon); + /* merge distance done in qh_findhorizon */ + apex= SETfirstt_(facet->vertices, vertexT); + FOREACHvertex_(facet->vertices) { + if (vertex != apex) + vertex->delridge= True; + } + horizon->f.newcycle= NULL; + qh_mergefacet(facet, horizon, NULL, NULL, qh_MERGEapex); + }else { + samecycle= facet; + facets= 0; + prev= facet; + for (same= facet->f.samecycle; same; /* FORALLsame_cycle_(facet) */ + same= (same == facet ? NULL :nextsame)) { /* ends at facet */ + nextsame= same->f.samecycle; + if (same->cycledone || same->visible) + qh_infiniteloop(same); + same->cycledone= True; + if (same->normal) { + prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */ + same->f.samecycle= NULL; + }else { + prev= same; + facets++; + } + } + while (nextfacet && nextfacet->cycledone) /* will delete samecycle */ + nextfacet= nextfacet->next; + horizon->f.newcycle= NULL; + qh_mergecycle(samecycle, horizon); + nummerge= horizon->nummerge + facets; + if (nummerge > qh_MAXnummerge) + horizon->nummerge= qh_MAXnummerge; + else + horizon->nummerge= (short unsigned int)nummerge; + zzinc_(Zcyclehorizon); + total += facets; + zzadd_(Zcyclefacettot, facets); + zmax_(Zcyclefacetmax, facets); + } + cycles++; + } + if (cycles) + *wasmerge= True; + trace1((qh ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles)); +} /* mergecycle_all */ + +/*--------------------------------- + + qh_mergecycle_facets( samecycle, newfacet ) + finish merge of samecycle into newfacet + + returns: + samecycle prepended to visible_list for later deletion and partitioning + each facet->f.replace == newfacet + + newfacet moved to end of qh.facet_list + makes newfacet a newfacet (get's facet1->id if it was old) + sets newfacet->newmerge + clears newfacet->center (unless merging into a large facet) + clears newfacet->tested and ridge->tested for facet1 + + adds neighboring facets to facet_mergeset if redundant or degenerate + + design: + make newfacet a new facet and set its flags + move samecycle facets to qh.visible_list for later deletion + unless newfacet is large + remove its centrum +*/ +void qh_mergecycle_facets(facetT *samecycle, facetT *newfacet) { + facetT *same, *next; + + trace4((qh ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n")); + qh_removefacet(newfacet); /* append as a newfacet to end of qh facet_list */ + qh_appendfacet(newfacet); + newfacet->newfacet= True; + newfacet->simplicial= False; + newfacet->newmerge= True; + + for (same= samecycle->f.samecycle; same; same= (same == samecycle ? NULL : next)) { + next= same->f.samecycle; /* reused by willdelete */ + qh_willdelete(same, newfacet); + } + if (newfacet->center + && qh_setsize(newfacet->vertices) <= qh hull_dim + qh_MAXnewcentrum) { + qh_memfree(newfacet->center, qh normal_size); + newfacet->center= NULL; + } + trace3((qh ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_facets */ + +/*--------------------------------- + + qh_mergecycle_neighbors( samecycle, newfacet ) + add neighbors for samecycle facets to newfacet + + returns: + newfacet with updated neighbors and vice-versa + newfacet has ridges + all neighbors of newfacet marked with qh.visit_id + samecycle facets marked with qh.visit_id-1 + ridges updated for simplicial neighbors of samecycle with a ridge + + notes: + assumes newfacet not in samecycle + usually, samecycle facets are new, simplicial facets without internal ridges + not so if horizon facet is coplanar to two different samecycles + + see: + qh_mergeneighbors() + + design: + check samecycle + delete neighbors from newfacet that are also in samecycle + for each neighbor of a facet in samecycle + if neighbor is simplicial + if first visit + move the neighbor relation to newfacet + update facet links for its ridges + else + make ridges for neighbor + remove samecycle reference + else + update neighbor sets +*/ +void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet) { + facetT *same, *neighbor, **neighborp; + int delneighbors= 0, newneighbors= 0; + unsigned int samevisitid; + ridgeT *ridge, **ridgep; + + samevisitid= ++qh visit_id; + FORALLsame_cycle_(samecycle) { + if (same->visitid == samevisitid || same->visible) + qh_infiniteloop(samecycle); + same->visitid= samevisitid; + } + newfacet->visitid= ++qh visit_id; + trace4((qh ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n")); + FOREACHneighbor_(newfacet) { + if (neighbor->visitid == samevisitid) { + SETref_(neighbor)= NULL; /* samecycle neighbors deleted */ + delneighbors++; + }else + neighbor->visitid= qh visit_id; + } + qh_setcompact(newfacet->neighbors); + + trace4((qh ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n")); + FORALLsame_cycle_(samecycle) { + FOREACHneighbor_(same) { + if (neighbor->visitid == samevisitid) + continue; + if (neighbor->simplicial) { + if (neighbor->visitid != qh visit_id) { + qh_setappend(&newfacet->neighbors, neighbor); + qh_setreplace(neighbor->neighbors, same, newfacet); + newneighbors++; + neighbor->visitid= qh visit_id; + FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */ + if (ridge->top == same) { + ridge->top= newfacet; + break; + }else if (ridge->bottom == same) { + ridge->bottom= newfacet; + break; + } + } + }else { + qh_makeridges(neighbor); + qh_setdel(neighbor->neighbors, same); + /* same can't be horizon facet for neighbor */ + } + }else { /* non-simplicial neighbor */ + qh_setdel(neighbor->neighbors, same); + if (neighbor->visitid != qh visit_id) { + qh_setappend(&neighbor->neighbors, newfacet); + qh_setappend(&newfacet->neighbors, neighbor); + neighbor->visitid= qh visit_id; + newneighbors++; + } + } + } + } + trace2((qh ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n", + delneighbors, newneighbors)); +} /* mergecycle_neighbors */ + +/*--------------------------------- + + qh_mergecycle_ridges( samecycle, newfacet ) + add ridges/neighbors for facets in samecycle to newfacet + all new/old neighbors of newfacet marked with qh.visit_id + facets in samecycle marked with qh.visit_id-1 + newfacet marked with qh.visit_id + + returns: + newfacet has merged ridges + + notes: + ridge already updated for simplicial neighbors of samecycle with a ridge + + see: + qh_mergeridges() + qh_makeridges() + + design: + remove ridges between newfacet and samecycle + for each facet in samecycle + for each ridge in facet + update facet pointers in ridge + skip ridges processed in qh_mergecycle_neighors + free ridges between newfacet and samecycle + free ridges between facets of samecycle (on 2nd visit) + append remaining ridges to newfacet + if simpilicial facet + for each neighbor of facet + if simplicial facet + and not samecycle facet or newfacet + make ridge between neighbor and newfacet +*/ +void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet) { + facetT *same, *neighbor= NULL; + int numold=0, numnew=0; + int neighbor_i, neighbor_n; + unsigned int samevisitid; + ridgeT *ridge, **ridgep; + boolT toporient; + void **freelistp; /* used !qh_NOmem */ + + trace4((qh ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n")); + samevisitid= qh visit_id -1; + FOREACHridge_(newfacet->ridges) { + neighbor= otherfacet_(ridge, newfacet); + if (neighbor->visitid == samevisitid) + SETref_(ridge)= NULL; /* ridge free'd below */ + } + qh_setcompact(newfacet->ridges); + + trace4((qh ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n")); + FORALLsame_cycle_(samecycle) { + FOREACHridge_(same->ridges) { + if (ridge->top == same) { + ridge->top= newfacet; + neighbor= ridge->bottom; + }else if (ridge->bottom == same) { + ridge->bottom= newfacet; + neighbor= ridge->top; + }else if (ridge->top == newfacet || ridge->bottom == newfacet) { + qh_setappend(&newfacet->ridges, ridge); + numold++; /* already set by qh_mergecycle_neighbors */ + continue; + }else { + qh_fprintf(qh ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id); + qh_errexit(qh_ERRqhull, NULL, ridge); + } + if (neighbor == newfacet) { + qh_setfree(&(ridge->vertices)); + qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); + numold++; + }else if (neighbor->visitid == samevisitid) { + qh_setdel(neighbor->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); + numold++; + }else { + qh_setappend(&newfacet->ridges, ridge); + numold++; + } + } + if (same->ridges) + qh_settruncate(same->ridges, 0); + if (!same->simplicial) + continue; + FOREACHneighbor_i_(same) { /* note: !newfact->simplicial */ + if (neighbor->visitid != samevisitid && neighbor->simplicial) { + ridge= qh_newridge(); + ridge->vertices= qh_setnew_delnthsorted(same->vertices, qh hull_dim, + neighbor_i, 0); + toporient= same->toporient ^ (neighbor_i & 0x1); + if (toporient) { + ridge->top= newfacet; + ridge->bottom= neighbor; + }else { + ridge->top= neighbor; + ridge->bottom= newfacet; + } + qh_setappend(&(newfacet->ridges), ridge); + qh_setappend(&(neighbor->ridges), ridge); + numnew++; + } + } + } + + trace2((qh ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n", + numold, numnew)); +} /* mergecycle_ridges */ + +/*--------------------------------- + + qh_mergecycle_vneighbors( samecycle, newfacet ) + create vertex neighbors for newfacet from vertices of facets in samecycle + samecycle marked with visitid == qh.visit_id - 1 + + returns: + newfacet vertices with updated neighbors + marks newfacet with qh.visit_id-1 + deletes vertices that are merged away + sets delridge on all vertices (faster here than in mergecycle_ridges) + + see: + qh_mergevertex_neighbors() + + design: + for each vertex of samecycle facet + set vertex->delridge + delete samecycle facets from vertex neighbors + append newfacet to vertex neighbors + if vertex only in newfacet + delete it from newfacet + add it to qh.del_vertices for later deletion +*/ +void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) { + facetT *neighbor, **neighborp; + unsigned int mergeid; + vertexT *vertex, **vertexp, *apex; + setT *vertices; + + trace4((qh ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n")); + mergeid= qh visit_id - 1; + newfacet->visitid= mergeid; + vertices= qh_basevertices(samecycle); /* temp */ + apex= SETfirstt_(samecycle->vertices, vertexT); + qh_setappend(&vertices, apex); + FOREACHvertex_(vertices) { + vertex->delridge= True; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == mergeid) + SETref_(neighbor)= NULL; + } + qh_setcompact(vertex->neighbors); + qh_setappend(&vertex->neighbors, newfacet); + if (!SETsecond_(vertex->neighbors)) { + zinc_(Zcyclevertex); + trace2((qh ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n", + vertex->id, samecycle->id, newfacet->id)); + qh_setdelsorted(newfacet->vertices, vertex); + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + } + } + qh_settempfree(&vertices); + trace3((qh ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_vneighbors */ + +/*--------------------------------- + + qh_mergefacet( facet1, facet2, mindist, maxdist, mergeapex ) + merges facet1 into facet2 + mergeapex==qh_MERGEapex if merging new facet into coplanar horizon + + returns: + qh.max_outside and qh.min_vertex updated + initializes vertex neighbors on first merge + + returns: + facet2 contains facet1's vertices, neighbors, and ridges + facet2 moved to end of qh.facet_list + makes facet2 a newfacet + sets facet2->newmerge set + clears facet2->center (unless merging into a large facet) + clears facet2->tested and ridge->tested for facet1 + + facet1 prepended to visible_list for later deletion and partitioning + facet1->f.replace == facet2 + + adds neighboring facets to facet_mergeset if redundant or degenerate + + notes: + mindist/maxdist may be NULL (only if both NULL) + traces merge if fmax_(maxdist,-mindist) > TRACEdist + + see: + qh_mergecycle() + + design: + trace merge and check for degenerate simplex + make ridges for both facets + update qh.max_outside, qh.max_vertex, qh.min_vertex + update facet2->maxoutside and keepcentrum + update facet2->nummerge + update tested flags for facet2 + if facet1 is simplicial + merge facet1 into facet2 + else + merge facet1's neighbors into facet2 + merge facet1's ridges into facet2 + merge facet1's vertices into facet2 + merge facet1's vertex neighbors into facet2 + add facet2's vertices to qh.new_vertexlist + unless qh_MERGEapex + test facet2 for degenerate or redundant neighbors + move facet1 to qh.visible_list for later deletion + move facet2 to end of qh.newfacet_list +*/ +void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) { + boolT traceonce= False; + vertexT *vertex, **vertexp; + int tracerestore=0, nummerge; + + if (facet1->tricoplanar || facet2->tricoplanar) { + if (!qh TRInormals) { + qh_fprintf(qh ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit2 (qh_ERRqhull, facet1, facet2); + } + if (facet2->tricoplanar) { + facet2->tricoplanar= False; + facet2->keepcentrum= False; + } + } + zzinc_(Ztotmerge); + if (qh REPORTfreq2 && qh POSTmerging) { + if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2) + qh_tracemerging(); + } +#ifndef qh_NOtrace + if (qh build_cnt >= qh RERUN) { + if (mindist && (-*mindist > qh TRACEdist || *maxdist > qh TRACEdist)) { + tracerestore= 0; + qh IStracing= qh TRACElevel; + traceonce= True; + qh_fprintf(qh ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge), + fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh furthest_id); + }else if (facet1 == qh tracefacet || facet2 == qh tracefacet) { + tracerestore= qh IStracing; + qh IStracing= 4; + traceonce= True; + qh_fprintf(qh ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n", + zzval_(Ztotmerge), qh tracefacet_id, qh furthest_id); + } + } + if (qh IStracing >= 2) { + realT mergemin= -2; + realT mergemax= -2; + + if (mindist) { + mergemin= *mindist; + mergemax= *maxdist; + } + qh_fprintf(qh ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n", + zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax); + } +#endif /* !qh_NOtrace */ + if (facet1 == facet2 || facet1->visible || facet2->visible) { + qh_fprintf(qh ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n", + facet1->id, facet2->id); + qh_errexit2 (qh_ERRqhull, facet1, facet2); + } + if (qh num_facets - qh num_visible <= qh hull_dim + 1) { + qh_fprintf(qh ferr, 6227, "\n\ +qhull precision error: Only %d facets remain. Can not merge another\n\ +pair. The input is too degenerate or the convexity constraints are\n\ +too strong.\n", qh hull_dim+1); + if (qh hull_dim >= 5 && !qh MERGEexact) + qh_fprintf(qh ferr, 8079, "Option 'Qx' may avoid this problem.\n"); + qh_errexit(qh_ERRprec, NULL, NULL); + } + if (!qh VERTEXneighbors) + qh_vertexneighbors(); + qh_makeridges(facet1); + qh_makeridges(facet2); + if (qh IStracing >=4) + qh_errprint("MERGING", facet1, facet2, NULL, NULL); + if (mindist) { + maximize_(qh max_outside, *maxdist); + maximize_(qh max_vertex, *maxdist); +#if qh_MAXoutside + maximize_(facet2->maxoutside, *maxdist); +#endif + minimize_(qh min_vertex, *mindist); + if (!facet2->keepcentrum + && (*maxdist > qh WIDEfacet || *mindist < -qh WIDEfacet)) { + facet2->keepcentrum= True; + zinc_(Zwidefacet); + } + } + nummerge= facet1->nummerge + facet2->nummerge + 1; + if (nummerge >= qh_MAXnummerge) + facet2->nummerge= qh_MAXnummerge; + else + facet2->nummerge= (short unsigned int)nummerge; + facet2->newmerge= True; + facet2->dupridge= False; + qh_updatetested (facet1, facet2); + if (qh hull_dim > 2 && qh_setsize(facet1->vertices) == qh hull_dim) + qh_mergesimplex(facet1, facet2, mergeapex); + else { + qh vertex_visit++; + FOREACHvertex_(facet2->vertices) + vertex->visitid= qh vertex_visit; + if (qh hull_dim == 2) + qh_mergefacet2d(facet1, facet2); + else { + qh_mergeneighbors(facet1, facet2); + qh_mergevertices(facet1->vertices, &facet2->vertices); + } + qh_mergeridges(facet1, facet2); + qh_mergevertex_neighbors(facet1, facet2); + if (!facet2->newfacet) + qh_newvertices(facet2->vertices); + } + if (!mergeapex) + qh_degen_redundant_neighbors(facet2, facet1); + if (facet2->coplanar || !facet2->newfacet) { + zinc_(Zmergeintohorizon); + }else if (!facet1->newfacet && facet2->newfacet) { + zinc_(Zmergehorizon); + }else { + zinc_(Zmergenew); + } + qh_willdelete(facet1, facet2); + qh_removefacet(facet2); /* append as a newfacet to end of qh facet_list */ + qh_appendfacet(facet2); + facet2->newfacet= True; + facet2->tested= False; + qh_tracemerge(facet1, facet2); + if (traceonce) { + qh_fprintf(qh ferr, 8080, "qh_mergefacet: end of wide tracing\n"); + qh IStracing= tracerestore; + } +} /* mergefacet */ + + +/*--------------------------------- + + qh_mergefacet2d( facet1, facet2 ) + in 2d, merges neighbors and vertices of facet1 into facet2 + + returns: + build ridges for neighbors if necessary + facet2 looks like a simplicial facet except for centrum, ridges + neighbors are opposite the corresponding vertex + maintains orientation of facet2 + + notes: + qh_mergefacet() retains non-simplicial structures + they are not needed in 2d, but later routines may use them + preserves qh.vertex_visit for qh_mergevertex_neighbors() + + design: + get vertices and neighbors + determine new vertices and neighbors + set new vertices and neighbors and adjust orientation + make ridges for new neighbor if needed +*/ +void qh_mergefacet2d(facetT *facet1, facetT *facet2) { + vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB; + facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB; + + vertex1A= SETfirstt_(facet1->vertices, vertexT); + vertex1B= SETsecondt_(facet1->vertices, vertexT); + vertex2A= SETfirstt_(facet2->vertices, vertexT); + vertex2B= SETsecondt_(facet2->vertices, vertexT); + neighbor1A= SETfirstt_(facet1->neighbors, facetT); + neighbor1B= SETsecondt_(facet1->neighbors, facetT); + neighbor2A= SETfirstt_(facet2->neighbors, facetT); + neighbor2B= SETsecondt_(facet2->neighbors, facetT); + if (vertex1A == vertex2A) { + vertexA= vertex1B; + vertexB= vertex2B; + neighborA= neighbor2A; + neighborB= neighbor1A; + }else if (vertex1A == vertex2B) { + vertexA= vertex1B; + vertexB= vertex2A; + neighborA= neighbor2B; + neighborB= neighbor1A; + }else if (vertex1B == vertex2A) { + vertexA= vertex1A; + vertexB= vertex2B; + neighborA= neighbor2A; + neighborB= neighbor1B; + }else { /* 1B == 2B */ + vertexA= vertex1A; + vertexB= vertex2A; + neighborA= neighbor2B; + neighborB= neighbor1B; + } + /* vertexB always from facet2, neighborB always from facet1 */ + if (vertexA->id > vertexB->id) { + SETfirst_(facet2->vertices)= vertexA; + SETsecond_(facet2->vertices)= vertexB; + if (vertexB == vertex2A) + facet2->toporient= !facet2->toporient; + SETfirst_(facet2->neighbors)= neighborA; + SETsecond_(facet2->neighbors)= neighborB; + }else { + SETfirst_(facet2->vertices)= vertexB; + SETsecond_(facet2->vertices)= vertexA; + if (vertexB == vertex2B) + facet2->toporient= !facet2->toporient; + SETfirst_(facet2->neighbors)= neighborB; + SETsecond_(facet2->neighbors)= neighborA; + } + qh_makeridges(neighborB); + qh_setreplace(neighborB->neighbors, facet1, facet2); + trace4((qh ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n", + vertexA->id, neighborB->id, facet1->id, facet2->id)); +} /* mergefacet2d */ + + +/*--------------------------------- + + qh_mergeneighbors( facet1, facet2 ) + merges the neighbors of facet1 into facet2 + + see: + qh_mergecycle_neighbors() + + design: + for each neighbor of facet1 + if neighbor is also a neighbor of facet2 + if neighbor is simpilicial + make ridges for later deletion as a degenerate facet + update its neighbor set + else + move the neighbor relation to facet2 + remove the neighbor relation for facet1 and facet2 +*/ +void qh_mergeneighbors(facetT *facet1, facetT *facet2) { + facetT *neighbor, **neighborp; + + trace4((qh ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n", + facet1->id, facet2->id)); + qh visit_id++; + FOREACHneighbor_(facet2) { + neighbor->visitid= qh visit_id; + } + FOREACHneighbor_(facet1) { + if (neighbor->visitid == qh visit_id) { + if (neighbor->simplicial) /* is degen, needs ridges */ + qh_makeridges(neighbor); + if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/ + qh_setdel(neighbor->neighbors, facet1); + else { + qh_setdel(neighbor->neighbors, facet2); + qh_setreplace(neighbor->neighbors, facet1, facet2); + } + }else if (neighbor != facet2) { + qh_setappend(&(facet2->neighbors), neighbor); + qh_setreplace(neighbor->neighbors, facet1, facet2); + } + } + qh_setdel(facet1->neighbors, facet2); /* here for makeridges */ + qh_setdel(facet2->neighbors, facet1); +} /* mergeneighbors */ + + +/*--------------------------------- + + qh_mergeridges( facet1, facet2 ) + merges the ridge set of facet1 into facet2 + + returns: + may delete all ridges for a vertex + sets vertex->delridge on deleted ridges + + see: + qh_mergecycle_ridges() + + design: + delete ridges between facet1 and facet2 + mark (delridge) vertices on these ridges for later testing + for each remaining ridge + rename facet1 to facet2 +*/ +void qh_mergeridges(facetT *facet1, facetT *facet2) { + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + + trace4((qh ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n", + facet1->id, facet2->id)); + FOREACHridge_(facet2->ridges) { + if ((ridge->top == facet1) || (ridge->bottom == facet1)) { + FOREACHvertex_(ridge->vertices) + vertex->delridge= True; + qh_delridge(ridge); /* expensive in high-d, could rebuild */ + ridgep--; /*repeat*/ + } + } + FOREACHridge_(facet1->ridges) { + if (ridge->top == facet1) + ridge->top= facet2; + else + ridge->bottom= facet2; + qh_setappend(&(facet2->ridges), ridge); + } +} /* mergeridges */ + + +/*--------------------------------- + + qh_mergesimplex( facet1, facet2, mergeapex ) + merge simplicial facet1 into facet2 + mergeapex==qh_MERGEapex if merging samecycle into horizon facet + vertex id is latest (most recently created) + facet1 may be contained in facet2 + ridges exist for both facets + + returns: + facet2 with updated vertices, ridges, neighbors + updated neighbors for facet1's vertices + facet1 not deleted + sets vertex->delridge on deleted ridges + + notes: + special case code since this is the most common merge + called from qh_mergefacet() + + design: + if qh_MERGEapex + add vertices of facet2 to qh.new_vertexlist if necessary + add apex to facet2 + else + for each ridge between facet1 and facet2 + set vertex->delridge + determine the apex for facet1 (i.e., vertex to be merged) + unless apex already in facet2 + insert apex into vertices for facet2 + add vertices of facet2 to qh.new_vertexlist if necessary + add apex to qh.new_vertexlist if necessary + for each vertex of facet1 + if apex + rename facet1 to facet2 in its vertex neighbors + else + delete facet1 from vertex neighors + if only in facet2 + add vertex to qh.del_vertices for later deletion + for each ridge of facet1 + delete ridges between facet1 and facet2 + append other ridges to facet2 after renaming facet to facet2 +*/ +void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) { + vertexT *vertex, **vertexp, *apex; + ridgeT *ridge, **ridgep; + boolT issubset= False; + int vertex_i= -1, vertex_n; + facetT *neighbor, **neighborp, *otherfacet; + + if (mergeapex) { + if (!facet2->newfacet) + qh_newvertices(facet2->vertices); /* apex is new */ + apex= SETfirstt_(facet1->vertices, vertexT); + if (SETfirstt_(facet2->vertices, vertexT) != apex) + qh_setaddnth(&facet2->vertices, 0, apex); /* apex has last id */ + else + issubset= True; + }else { + zinc_(Zmergesimplex); + FOREACHvertex_(facet1->vertices) + vertex->seen= False; + FOREACHridge_(facet1->ridges) { + if (otherfacet_(ridge, facet1) == facet2) { + FOREACHvertex_(ridge->vertices) { + vertex->seen= True; + vertex->delridge= True; + } + break; + } + } + FOREACHvertex_(facet1->vertices) { + if (!vertex->seen) + break; /* must occur */ + } + apex= vertex; + trace4((qh ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n", + apex->id, facet1->id, facet2->id)); + FOREACHvertex_i_(facet2->vertices) { + if (vertex->id < apex->id) { + break; + }else if (vertex->id == apex->id) { + issubset= True; + break; + } + } + if (!issubset) + qh_setaddnth(&facet2->vertices, vertex_i, apex); + if (!facet2->newfacet) + qh_newvertices(facet2->vertices); + else if (!apex->newlist) { + qh_removevertex(apex); + qh_appendvertex(apex); + } + } + trace4((qh ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n", + facet1->id)); + FOREACHvertex_(facet1->vertices) { + if (vertex == apex && !issubset) + qh_setreplace(vertex->neighbors, facet1, facet2); + else { + qh_setdel(vertex->neighbors, facet1); + if (!SETsecond_(vertex->neighbors)) + qh_mergevertex_del(vertex, facet1, facet2); + } + } + trace4((qh ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n", + facet1->id, facet2->id)); + qh visit_id++; + FOREACHneighbor_(facet2) + neighbor->visitid= qh visit_id; + FOREACHridge_(facet1->ridges) { + otherfacet= otherfacet_(ridge, facet1); + if (otherfacet == facet2) { + qh_setdel(facet2->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree(ridge, (int)sizeof(ridgeT)); + qh_setdel(facet2->neighbors, facet1); + }else { + qh_setappend(&facet2->ridges, ridge); + if (otherfacet->visitid != qh visit_id) { + qh_setappend(&facet2->neighbors, otherfacet); + qh_setreplace(otherfacet->neighbors, facet1, facet2); + otherfacet->visitid= qh visit_id; + }else { + if (otherfacet->simplicial) /* is degen, needs ridges */ + qh_makeridges(otherfacet); + if (SETfirstt_(otherfacet->neighbors, facetT) != facet1) + qh_setdel(otherfacet->neighbors, facet1); + else { /*keep newfacet->neighbors->horizon*/ + qh_setdel(otherfacet->neighbors, facet2); + qh_setreplace(otherfacet->neighbors, facet1, facet2); + } + } + if (ridge->top == facet1) /* wait until after qh_makeridges */ + ridge->top= facet2; + else + ridge->bottom= facet2; + } + } + SETfirst_(facet1->ridges)= NULL; /* it will be deleted */ + trace3((qh ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n", + facet1->id, getid_(apex), facet2->id)); +} /* mergesimplex */ + +/*--------------------------------- + + qh_mergevertex_del( vertex, facet1, facet2 ) + delete a vertex because of merging facet1 into facet2 + + returns: + deletes vertex from facet2 + adds vertex to qh.del_vertices for later deletion +*/ +void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2) { + + zinc_(Zmergevertex); + trace2((qh ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n", + vertex->id, facet1->id, facet2->id)); + qh_setdelsorted(facet2->vertices, vertex); + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); +} /* mergevertex_del */ + +/*--------------------------------- + + qh_mergevertex_neighbors( facet1, facet2 ) + merge the vertex neighbors of facet1 to facet2 + + returns: + if vertex is current qh.vertex_visit + deletes facet1 from vertex->neighbors + else + renames facet1 to facet2 in vertex->neighbors + deletes vertices if only one neighbor + + notes: + assumes vertex neighbor sets are good +*/ +void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2) { + vertexT *vertex, **vertexp; + + trace4((qh ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n", + facet1->id, facet2->id)); + if (qh tracevertex) { + qh_fprintf(qh ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n", + facet1->id, facet2->id, qh furthest_id, qh tracevertex->neighbors->e[0].p); + qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex); + } + FOREACHvertex_(facet1->vertices) { + if (vertex->visitid != qh vertex_visit) + qh_setreplace(vertex->neighbors, facet1, facet2); + else { + qh_setdel(vertex->neighbors, facet1); + if (!SETsecond_(vertex->neighbors)) + qh_mergevertex_del(vertex, facet1, facet2); + } + } + if (qh tracevertex) + qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex); +} /* mergevertex_neighbors */ + + +/*--------------------------------- + + qh_mergevertices( vertices1, vertices2 ) + merges the vertex set of facet1 into facet2 + + returns: + replaces vertices2 with merged set + preserves vertex_visit for qh_mergevertex_neighbors + updates qh.newvertex_list + + design: + create a merged set of both vertices (in inverse id order) +*/ +void qh_mergevertices(setT *vertices1, setT **vertices2) { + int newsize= qh_setsize(vertices1)+qh_setsize(*vertices2) - qh hull_dim + 1; + setT *mergedvertices; + vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT); + + mergedvertices= qh_settemp(newsize); + FOREACHvertex_(vertices1) { + if (!*vertex2 || vertex->id > (*vertex2)->id) + qh_setappend(&mergedvertices, vertex); + else { + while (*vertex2 && (*vertex2)->id > vertex->id) + qh_setappend(&mergedvertices, *vertex2++); + if (!*vertex2 || (*vertex2)->id < vertex->id) + qh_setappend(&mergedvertices, vertex); + else + qh_setappend(&mergedvertices, *vertex2++); + } + } + while (*vertex2) + qh_setappend(&mergedvertices, *vertex2++); + if (newsize < qh_setsize(mergedvertices)) { + qh_fprintf(qh ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_setfree(vertices2); + *vertices2= mergedvertices; + qh_settemppop(); +} /* mergevertices */ + + +/*--------------------------------- + + qh_neighbor_intersections( vertex ) + return intersection of all vertices in vertex->neighbors except for vertex + + returns: + returns temporary set of vertices + does not include vertex + NULL if a neighbor is simplicial + NULL if empty set + + notes: + used for renaming vertices + + design: + initialize the intersection set with vertices of the first two neighbors + delete vertex from the intersection + for each remaining neighbor + intersect its vertex set with the intersection set + return NULL if empty + return the intersection set +*/ +setT *qh_neighbor_intersections(vertexT *vertex) { + facetT *neighbor, **neighborp, *neighborA, *neighborB; + setT *intersect; + int neighbor_i, neighbor_n; + + FOREACHneighbor_(vertex) { + if (neighbor->simplicial) + return NULL; + } + neighborA= SETfirstt_(vertex->neighbors, facetT); + neighborB= SETsecondt_(vertex->neighbors, facetT); + zinc_(Zintersectnum); + if (!neighborA) + return NULL; + if (!neighborB) + intersect= qh_setcopy(neighborA->vertices, 0); + else + intersect= qh_vertexintersect_new(neighborA->vertices, neighborB->vertices); + qh_settemppush(intersect); + qh_setdelsorted(intersect, vertex); + FOREACHneighbor_i_(vertex) { + if (neighbor_i >= 2) { + zinc_(Zintersectnum); + qh_vertexintersect(&intersect, neighbor->vertices); + if (!SETfirst_(intersect)) { + zinc_(Zintersectfail); + qh_settempfree(&intersect); + return NULL; + } + } + } + trace3((qh ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n", + qh_setsize(intersect), vertex->id)); + return intersect; +} /* neighbor_intersections */ + +/*--------------------------------- + + qh_newvertices( vertices ) + add vertices to end of qh.vertex_list (marks as new vertices) + + returns: + vertices on qh.newvertex_list + vertex->newlist set +*/ +void qh_newvertices(setT *vertices) { + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newlist) { + qh_removevertex(vertex); + qh_appendvertex(vertex); + } + } +} /* newvertices */ + +/*--------------------------------- + + qh_reducevertices() + reduce extra vertices, shared vertices, and redundant vertices + facet->newmerge is set if merged since last call + if !qh.MERGEvertices, only removes extra vertices + + returns: + True if also merged degen_redundant facets + vertices are renamed if possible + clears facet->newmerge and vertex->delridge + + notes: + ignored if 2-d + + design: + merge any degenerate or redundant facets + for each newly merged facet + remove extra vertices + if qh.MERGEvertices + for each newly merged facet + for each vertex + if vertex was on a deleted ridge + rename vertex if it is shared + remove delridge flag from new vertices +*/ +boolT qh_reducevertices(void) { + int numshare=0, numrename= 0; + boolT degenredun= False; + facetT *newfacet; + vertexT *vertex, **vertexp; + + if (qh hull_dim == 2) + return False; + if (qh_merge_degenredundant()) + degenredun= True; + LABELrestart: + FORALLnew_facets { + if (newfacet->newmerge) { + if (!qh MERGEvertices) + newfacet->newmerge= False; + qh_remove_extravertices(newfacet); + } + } + if (!qh MERGEvertices) + return False; + FORALLnew_facets { + if (newfacet->newmerge) { + newfacet->newmerge= False; + FOREACHvertex_(newfacet->vertices) { + if (vertex->delridge) { + if (qh_rename_sharedvertex(vertex, newfacet)) { + numshare++; + vertexp--; /* repeat since deleted vertex */ + } + } + } + } + } + FORALLvertex_(qh newvertex_list) { + if (vertex->delridge && !vertex->deleted) { + vertex->delridge= False; + if (qh hull_dim >= 4 && qh_redundant_vertex(vertex)) { + numrename++; + if (qh_merge_degenredundant()) { + degenredun= True; + goto LABELrestart; + } + } + } + } + trace1((qh ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n", + numshare, numrename, degenredun)); + return degenredun; +} /* reducevertices */ + +/*--------------------------------- + + qh_redundant_vertex( vertex ) + detect and rename a redundant vertex + vertices have full vertex->neighbors + + returns: + returns true if find a redundant vertex + deletes vertex(vertex->deleted) + + notes: + only needed if vertex->delridge and hull_dim >= 4 + may add degenerate facets to qh.facet_mergeset + doesn't change vertex->neighbors or create redundant facets + + design: + intersect vertices of all facet neighbors of vertex + determine ridges for these vertices + if find a new vertex for vertex amoung these ridges and vertices + rename vertex to the new vertex +*/ +vertexT *qh_redundant_vertex(vertexT *vertex) { + vertexT *newvertex= NULL; + setT *vertices, *ridges; + + trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id)); + if ((vertices= qh_neighbor_intersections(vertex))) { + ridges= qh_vertexridges(vertex); + if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) + qh_renamevertex(vertex, newvertex, ridges, NULL, NULL); + qh_settempfree(&ridges); + qh_settempfree(&vertices); + } + return newvertex; +} /* redundant_vertex */ + +/*--------------------------------- + + qh_remove_extravertices( facet ) + remove extra vertices from non-simplicial facets + + returns: + returns True if it finds them + + design: + for each vertex in facet + if vertex not in a ridge (i.e., no longer used) + delete vertex from facet + delete facet from vertice's neighbors + unless vertex in another facet + add vertex to qh.del_vertices for later deletion +*/ +boolT qh_remove_extravertices(facetT *facet) { + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + boolT foundrem= False; + + trace4((qh ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n", + facet->id)); + FOREACHvertex_(facet->vertices) + vertex->seen= False; + FOREACHridge_(facet->ridges) { + FOREACHvertex_(ridge->vertices) + vertex->seen= True; + } + FOREACHvertex_(facet->vertices) { + if (!vertex->seen) { + foundrem= True; + zinc_(Zremvertex); + qh_setdelsorted(facet->vertices, vertex); + qh_setdel(vertex->neighbors, facet); + if (!qh_setsize(vertex->neighbors)) { + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + zinc_(Zremvertexdel); + trace2((qh ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id)); + }else + trace3((qh ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id)); + vertexp--; /*repeat*/ + } + } + return foundrem; +} /* remove_extravertices */ + +/*--------------------------------- + + qh_rename_sharedvertex( vertex, facet ) + detect and rename if shared vertex in facet + vertices have full ->neighbors + + returns: + newvertex or NULL + the vertex may still exist in other facets (i.e., a neighbor was pinched) + does not change facet->neighbors + updates vertex->neighbors + + notes: + a shared vertex for a facet is only in ridges to one neighbor + this may undo a pinched facet + + it does not catch pinches involving multiple facets. These appear + to be difficult to detect, since an exhaustive search is too expensive. + + design: + if vertex only has two neighbors + determine the ridges that contain the vertex + determine the vertices shared by both neighbors + if can find a new vertex in this set + rename the vertex to the new vertex +*/ +vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) { + facetT *neighbor, **neighborp, *neighborA= NULL; + setT *vertices, *ridges; + vertexT *newvertex; + + if (qh_setsize(vertex->neighbors) == 2) { + neighborA= SETfirstt_(vertex->neighbors, facetT); + if (neighborA == facet) + neighborA= SETsecondt_(vertex->neighbors, facetT); + }else if (qh hull_dim == 3) + return NULL; + else { + qh visit_id++; + FOREACHneighbor_(facet) + neighbor->visitid= qh visit_id; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == qh visit_id) { + if (neighborA) + return NULL; + neighborA= neighbor; + } + } + if (!neighborA) { + qh_fprintf(qh ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n", + vertex->id, facet->id); + qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + } + /* the vertex is shared by facet and neighborA */ + ridges= qh_settemp(qh TEMPsize); + neighborA->visitid= ++qh visit_id; + qh_vertexridges_facet(vertex, facet, &ridges); + trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n", + qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id)); + zinc_(Zintersectnum); + vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices); + qh_setdel(vertices, vertex); + qh_settemppush(vertices); + if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) + qh_renamevertex(vertex, newvertex, ridges, facet, neighborA); + qh_settempfree(&vertices); + qh_settempfree(&ridges); + return newvertex; +} /* rename_sharedvertex */ + +/*--------------------------------- + + qh_renameridgevertex( ridge, oldvertex, newvertex ) + renames oldvertex as newvertex in ridge + + returns: + + design: + delete oldvertex from ridge + if newvertex already in ridge + copy ridge->noconvex to another ridge if possible + delete the ridge + else + insert newvertex into the ridge + adjust the ridge's orientation +*/ +void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) { + int nth= 0, oldnth; + facetT *temp; + vertexT *vertex, **vertexp; + + oldnth= qh_setindex(ridge->vertices, oldvertex); + qh_setdelnthsorted(ridge->vertices, oldnth); + FOREACHvertex_(ridge->vertices) { + if (vertex == newvertex) { + zinc_(Zdelridge); + if (ridge->nonconvex) /* only one ridge has nonconvex set */ + qh_copynonconvex(ridge); + qh_delridge(ridge); + trace2((qh ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n", + ridge->id, oldvertex->id, newvertex->id)); + return; + } + if (vertex->id < newvertex->id) + break; + nth++; + } + qh_setaddnth(&ridge->vertices, nth, newvertex); + if (abs(oldnth - nth)%2) { + trace3((qh ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n", + ridge->id)); + temp= ridge->top; + ridge->top= ridge->bottom; + ridge->bottom= temp; + } +} /* renameridgevertex */ + + +/*--------------------------------- + + qh_renamevertex( oldvertex, newvertex, ridges, oldfacet, neighborA ) + renames oldvertex as newvertex in ridges + gives oldfacet/neighborA if oldvertex is shared between two facets + + returns: + oldvertex may still exist afterwards + + + notes: + can not change neighbors of newvertex (since it's a subset) + + design: + for each ridge in ridges + rename oldvertex to newvertex and delete degenerate ridges + if oldfacet not defined + for each neighbor of oldvertex + delete oldvertex from neighbor's vertices + remove extra vertices from neighbor + add oldvertex to qh.del_vertices + else if oldvertex only between oldfacet and neighborA + delete oldvertex from oldfacet and neighborA + add oldvertex to qh.del_vertices + else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched) + delete oldvertex from oldfacet + delete oldfacet from oldvertice's neighbors + remove extra vertices (e.g., oldvertex) from neighborA +*/ +void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + boolT istrace= False; + + if (qh IStracing >= 2 || oldvertex->id == qh tracevertex_id || + newvertex->id == qh tracevertex_id) + istrace= True; + FOREACHridge_(ridges) + qh_renameridgevertex(ridge, oldvertex, newvertex); + if (!oldfacet) { + zinc_(Zrenameall); + if (istrace) + qh_fprintf(qh ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n", + oldvertex->id, newvertex->id); + FOREACHneighbor_(oldvertex) { + qh_maydropneighbor(neighbor); + qh_setdelsorted(neighbor->vertices, oldvertex); + if (qh_remove_extravertices(neighbor)) + neighborp--; /* neighbor may be deleted */ + } + if (!oldvertex->deleted) { + oldvertex->deleted= True; + qh_setappend(&qh del_vertices, oldvertex); + } + }else if (qh_setsize(oldvertex->neighbors) == 2) { + zinc_(Zrenameshare); + if (istrace) + qh_fprintf(qh ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n", + oldvertex->id, newvertex->id, oldfacet->id); + FOREACHneighbor_(oldvertex) + qh_setdelsorted(neighbor->vertices, oldvertex); + oldvertex->deleted= True; + qh_setappend(&qh del_vertices, oldvertex); + }else { + zinc_(Zrenamepinch); + if (istrace || qh IStracing) + qh_fprintf(qh ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n", + oldvertex->id, newvertex->id, oldfacet->id, neighborA->id); + qh_setdelsorted(oldfacet->vertices, oldvertex); + qh_setdel(oldvertex->neighbors, oldfacet); + qh_remove_extravertices(neighborA); + } +} /* renamevertex */ + + +/*--------------------------------- + + qh_test_appendmerge( facet, neighbor ) + tests facet/neighbor for convexity + appends to mergeset if non-convex + if pre-merging, + nop if qh.SKIPconvex, or qh.MERGEexact and coplanar + + returns: + true if appends facet/neighbor to mergeset + sets facet->center as needed + does not change facet->seen + + design: + if qh.cos_max is defined + if the angle between facet normals is too shallow + append an angle-coplanar merge to qh.mergeset + return True + make facet's centrum if needed + if facet's centrum is above the neighbor + set isconcave + else + if facet's centrum is not below the neighbor + set iscoplanar + make neighbor's centrum if needed + if neighbor's centrum is above the facet + set isconcave + else if neighbor's centrum is not below the facet + set iscoplanar + if isconcave or iscoplanar + get angle if needed + append concave or coplanar merge to qh.mergeset +*/ +boolT qh_test_appendmerge(facetT *facet, facetT *neighbor) { + realT dist, dist2= -REALmax, angle= -REALmax; + boolT isconcave= False, iscoplanar= False, okangle= False; + + if (qh SKIPconvex && !qh POSTmerging) + return False; + if ((!qh MERGEexact || qh POSTmerging) && qh cos_max < REALmax/2) { + angle= qh_getangle(facet->normal, neighbor->normal); + zinc_(Zangletests); + if (angle > qh cos_max) { + zinc_(Zcoplanarangle); + qh_appendmergeset(facet, neighbor, MRGanglecoplanar, &angle); + trace2((qh ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n", + angle, facet->id, neighbor->id)); + return True; + }else + okangle= True; + } + if (!facet->center) + facet->center= qh_getcentrum(facet); + zzinc_(Zcentrumtests); + qh_distplane(facet->center, neighbor, &dist); + if (dist > qh centrum_radius) + isconcave= True; + else { + if (dist > -qh centrum_radius) + iscoplanar= True; + if (!neighbor->center) + neighbor->center= qh_getcentrum(neighbor); + zzinc_(Zcentrumtests); + qh_distplane(neighbor->center, facet, &dist2); + if (dist2 > qh centrum_radius) + isconcave= True; + else if (!iscoplanar && dist2 > -qh centrum_radius) + iscoplanar= True; + } + if (!isconcave && (!iscoplanar || (qh MERGEexact && !qh POSTmerging))) + return False; + if (!okangle && qh ANGLEmerge) { + angle= qh_getangle(facet->normal, neighbor->normal); + zinc_(Zangletests); + } + if (isconcave) { + zinc_(Zconcaveridge); + if (qh ANGLEmerge) + angle += qh_ANGLEconcave + 0.5; + qh_appendmergeset(facet, neighbor, MRGconcave, &angle); + trace0((qh ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n", + facet->id, neighbor->id, dist, dist2, angle, qh furthest_id)); + }else /* iscoplanar */ { + zinc_(Zcoplanarcentrum); + qh_appendmergeset(facet, neighbor, MRGcoplanar, &angle); + trace2((qh ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n", + facet->id, neighbor->id, dist, dist2, angle)); + } + return True; +} /* test_appendmerge */ + +/*--------------------------------- + + qh_test_vneighbors() + test vertex neighbors for convexity + tests all facets on qh.newfacet_list + + returns: + true if non-convex vneighbors appended to qh.facet_mergeset + initializes vertex neighbors if needed + + notes: + assumes all facet neighbors have been tested + this can be expensive + this does not guarantee that a centrum is below all facets + but it is unlikely + uses qh.visit_id + + design: + build vertex neighbors if necessary + for all new facets + for all vertices + for each unvisited facet neighbor of the vertex + test new facet and neighbor for convexity +*/ +boolT qh_test_vneighbors(void /* qh newfacet_list */) { + facetT *newfacet, *neighbor, **neighborp; + vertexT *vertex, **vertexp; + int nummerges= 0; + + trace1((qh ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n")); + if (!qh VERTEXneighbors) + qh_vertexneighbors(); + FORALLnew_facets + newfacet->seen= False; + FORALLnew_facets { + newfacet->seen= True; + newfacet->visitid= qh visit_id++; + FOREACHneighbor_(newfacet) + newfacet->visitid= qh visit_id; + FOREACHvertex_(newfacet->vertices) { + FOREACHneighbor_(vertex) { + if (neighbor->seen || neighbor->visitid == qh visit_id) + continue; + if (qh_test_appendmerge(newfacet, neighbor)) + nummerges++; + } + } + } + zadd_(Ztestvneighbor, nummerges); + trace1((qh ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n", + nummerges)); + return (nummerges > 0); +} /* test_vneighbors */ + +/*--------------------------------- + + qh_tracemerge( facet1, facet2 ) + print trace message after merge +*/ +void qh_tracemerge(facetT *facet1, facetT *facet2) { + boolT waserror= False; + +#ifndef qh_NOtrace + if (qh IStracing >= 4) + qh_errprint("MERGED", facet2, NULL, NULL, NULL); + if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newlist)) { + qh_fprintf(qh ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh furthest_id); + if (facet2 != qh tracefacet) + qh_errprint("TRACE", qh tracefacet, + (qh tracevertex && qh tracevertex->neighbors) ? + SETfirstt_(qh tracevertex->neighbors, facetT) : NULL, + NULL, qh tracevertex); + } + if (qh tracevertex) { + if (qh tracevertex->deleted) + qh_fprintf(qh ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n", + qh furthest_id); + else + qh_checkvertex(qh tracevertex); + } + if (qh tracefacet) { + qh_checkfacet(qh tracefacet, True, &waserror); + if (waserror) + qh_errexit(qh_ERRqhull, qh tracefacet, NULL); + } +#endif /* !qh_NOtrace */ + if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */ + qh_checkfacet(facet2, True, &waserror); + if (waserror) + qh_errexit(qh_ERRqhull, NULL, NULL); + } +} /* tracemerge */ + +/*--------------------------------- + + qh_tracemerging() + print trace message during POSTmerging + + returns: + updates qh.mergereport + + notes: + called from qh_mergecycle() and qh_mergefacet() + + see: + qh_buildtracing() +*/ +void qh_tracemerging(void) { + realT cpu; + int total; + time_t timedata; + struct tm *tp; + + qh mergereport= zzval_(Ztotmerge); + time(&timedata); + tp= localtime(&timedata); + cpu= qh_CPUclock; + cpu /= qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + qh_fprintf(qh ferr, 8087, "\n\ +At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\ + contains %d facets and %d vertices.\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, + total, qh num_facets - qh num_visible, + qh num_vertices-qh_setsize(qh del_vertices)); +} /* tracemerging */ + +/*--------------------------------- + + qh_updatetested( facet1, facet2 ) + clear facet2->tested and facet1->ridge->tested for merge + + returns: + deletes facet2->center unless it's already large + if so, clears facet2->ridge->tested + + design: + clear facet2->tested + clear ridge->tested for facet1's ridges + if facet2 has a centrum + if facet2 is large + set facet2->keepcentrum + else if facet2 has 3 vertices due to many merges, or not large and post merging + clear facet2->keepcentrum + unless facet2->keepcentrum + clear facet2->center to recompute centrum later + clear ridge->tested for facet2's ridges +*/ +void qh_updatetested(facetT *facet1, facetT *facet2) { + ridgeT *ridge, **ridgep; + int size; + + facet2->tested= False; + FOREACHridge_(facet1->ridges) + ridge->tested= False; + if (!facet2->center) + return; + size= qh_setsize(facet2->vertices); + if (!facet2->keepcentrum) { + if (size > qh hull_dim + qh_MAXnewcentrum) { + facet2->keepcentrum= True; + zinc_(Zwidevertices); + } + }else if (size <= qh hull_dim + qh_MAXnewcentrum) { + /* center and keepcentrum was set */ + if (size == qh hull_dim || qh POSTmerging) + facet2->keepcentrum= False; /* if many merges need to recompute centrum */ + } + if (!facet2->keepcentrum) { + qh_memfree(facet2->center, qh normal_size); + facet2->center= NULL; + FOREACHridge_(facet2->ridges) + ridge->tested= False; + } +} /* updatetested */ + +/*--------------------------------- + + qh_vertexridges( vertex ) + return temporary set of ridges adjacent to a vertex + vertex->neighbors defined + + ntoes: + uses qh.visit_id + does not include implicit ridges for simplicial facets + + design: + for each neighbor of vertex + add ridges that include the vertex to ridges +*/ +setT *qh_vertexridges(vertexT *vertex) { + facetT *neighbor, **neighborp; + setT *ridges= qh_settemp(qh TEMPsize); + int size; + + qh visit_id++; + FOREACHneighbor_(vertex) + neighbor->visitid= qh visit_id; + FOREACHneighbor_(vertex) { + if (*neighborp) /* no new ridges in last neighbor */ + qh_vertexridges_facet(vertex, neighbor, &ridges); + } + if (qh PRINTstatistics || qh IStracing) { + size= qh_setsize(ridges); + zinc_(Zvertexridge); + zadd_(Zvertexridgetot, size); + zmax_(Zvertexridgemax, size); + trace3((qh ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n", + size, vertex->id)); + } + return ridges; +} /* vertexridges */ + +/*--------------------------------- + + qh_vertexridges_facet( vertex, facet, ridges ) + add adjacent ridges for vertex in facet + neighbor->visitid==qh.visit_id if it hasn't been visited + + returns: + ridges updated + sets facet->visitid to qh.visit_id-1 + + design: + for each ridge of facet + if ridge of visited neighbor (i.e., unprocessed) + if vertex in ridge + append ridge to vertex + mark facet processed +*/ +void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges) { + ridgeT *ridge, **ridgep; + facetT *neighbor; + + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid == qh visit_id + && qh_setin(ridge->vertices, vertex)) + qh_setappend(ridges, ridge); + } + facet->visitid= qh visit_id-1; +} /* vertexridges_facet */ + +/*--------------------------------- + + qh_willdelete( facet, replace ) + moves facet to visible list + sets facet->f.replace to replace (may be NULL) + + returns: + bumps qh.num_visible +*/ +void qh_willdelete(facetT *facet, facetT *replace) { + + qh_removefacet(facet); + qh_prependfacet(facet, &qh visible_list); + qh num_visible++; + facet->visible= True; + facet->f.replace= replace; +} /* willdelete */ + +#else /* qh_NOmerge */ +void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) { +} +void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { +} +boolT qh_checkzero(boolT testall) { + } +#endif /* qh_NOmerge */ diff --git a/extern/qhull/merge.h b/extern/qhull/merge.h new file mode 100644 index 000000000000..da0fb53e7815 --- /dev/null +++ b/extern/qhull/merge.h @@ -0,0 +1,178 @@ +/*--------------------------------- + + merge.h + header file for merge.c + + see qh-merge.htm and merge.c + + Copyright (c) 1993-2012 C.B. Barber. + $Id: //main/2011/qhull/src/libqhull/merge.h#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#ifndef qhDEFmerge +#define qhDEFmerge 1 + +#include "libqhull.h" + + +/*============ -constants- ==============*/ + +/*---------------------------------- + + qh_ANGLEredundant + indicates redundant merge in mergeT->angle +*/ +#define qh_ANGLEredundant 6.0 + +/*---------------------------------- + + qh_ANGLEdegen + indicates degenerate facet in mergeT->angle +*/ +#define qh_ANGLEdegen 5.0 + +/*---------------------------------- + + qh_ANGLEconcave + offset to indicate concave facets in mergeT->angle + + notes: + concave facets are assigned the range of [2,4] in mergeT->angle + roundoff error may make the angle less than 2 +*/ +#define qh_ANGLEconcave 1.5 + +/*---------------------------------- + + MRG... (mergeType) + indicates the type of a merge (mergeT->type) +*/ +typedef enum { /* in sort order for facet_mergeset */ + MRGnone= 0, + MRGcoplanar, /* centrum coplanar */ + MRGanglecoplanar, /* angle coplanar */ + /* could detect half concave ridges */ + MRGconcave, /* concave ridge */ + MRGflip, /* flipped facet. facet1 == facet2 */ + MRGridge, /* duplicate ridge (qh_MERGEridge) */ + /* degen and redundant go onto degen_mergeset */ + MRGdegen, /* degenerate facet (!enough neighbors) facet1 == facet2 */ + MRGredundant, /* redundant facet (vertex subset) */ + /* merge_degenredundant assumes degen < redundant */ + MRGmirror, /* mirror facet from qh_triangulate */ + ENDmrg +} mergeType; + +/*---------------------------------- + + qh_MERGEapex + flag for qh_mergefacet() to indicate an apex merge +*/ +#define qh_MERGEapex True + +/*============ -structures- ====================*/ + +/*---------------------------------- + + mergeT + structure used to merge facets +*/ + +typedef struct mergeT mergeT; +struct mergeT { /* initialize in qh_appendmergeset */ + realT angle; /* angle between normals of facet1 and facet2 */ + facetT *facet1; /* will merge facet1 into facet2 */ + facetT *facet2; + mergeType type; +}; + + +/*=========== -macros- =========================*/ + +/*---------------------------------- + + FOREACHmerge_( merges ) {...} + assign 'merge' to each merge in merges + + notes: + uses 'mergeT *merge, **mergep;' + if qh_mergefacet(), + restart since qh.facet_mergeset may change + see FOREACHsetelement_ +*/ +#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge) + +/*============ prototypes in alphabetical order after pre/postmerge =======*/ + +void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle); +void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors); +void qh_all_merges(boolT othermerge, boolT vneighbors); +void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle); +setT *qh_basevertices( facetT *samecycle); +void qh_checkconnect(void /* qh new_facets */); +boolT qh_checkzero(boolT testall); +int qh_compareangle(const void *p1, const void *p2); +int qh_comparemerge(const void *p1, const void *p2); +int qh_comparevisit(const void *p1, const void *p2); +void qh_copynonconvex(ridgeT *atridge); +void qh_degen_redundant_facet(facetT *facet); +void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet); +vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges); +void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor, + facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp); +facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp); +void qh_flippedmerges(facetT *facetlist, boolT *wasmerge); +void qh_forcedmerges( boolT *wasmerge); +void qh_getmergeset(facetT *facetlist); +void qh_getmergeset_initial(facetT *facetlist); +void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex); +ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, + vertexT *vertex, vertexT *oldvertex, int *hashslot); +void qh_makeridges(facetT *facet); +void qh_mark_dupridges(facetT *facetlist); +void qh_maydropneighbor(facetT *facet); +int qh_merge_degenredundant(void); +void qh_merge_nonconvex( facetT *facet1, facetT *facet2, mergeType mergetype); +void qh_mergecycle(facetT *samecycle, facetT *newfacet); +void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge); +void qh_mergecycle_facets( facetT *samecycle, facetT *newfacet); +void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet); +void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet); +void qh_mergecycle_vneighbors( facetT *samecycle, facetT *newfacet); +void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex); +void qh_mergefacet2d(facetT *facet1, facetT *facet2); +void qh_mergeneighbors(facetT *facet1, facetT *facet2); +void qh_mergeridges(facetT *facet1, facetT *facet2); +void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex); +void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2); +void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2); +void qh_mergevertices(setT *vertices1, setT **vertices); +setT *qh_neighbor_intersections(vertexT *vertex); +void qh_newvertices(setT *vertices); +boolT qh_reducevertices(void); +vertexT *qh_redundant_vertex(vertexT *vertex); +boolT qh_remove_extravertices(facetT *facet); +vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet); +void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex); +void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, + facetT *oldfacet, facetT *neighborA); +boolT qh_test_appendmerge(facetT *facet, facetT *neighbor); +boolT qh_test_vneighbors(void /* qh newfacet_list */); +void qh_tracemerge(facetT *facet1, facetT *facet2); +void qh_tracemerging(void); +void qh_updatetested( facetT *facet1, facetT *facet2); +setT *qh_vertexridges(vertexT *vertex); +void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges); +void qh_willdelete(facetT *facet, facetT *replace); + +#endif /* qhDEFmerge */ diff --git a/extern/qhull/poly.c b/extern/qhull/poly.c new file mode 100644 index 000000000000..436cbad20547 --- /dev/null +++ b/extern/qhull/poly.c @@ -0,0 +1,1196 @@ +/*--------------------------------- + + poly.c + implements polygons and simplices + + see qh-poly.htm, poly.h and libqhull.h + + infrequent code is in poly2.c + (all but top 50 and their callers 12/3/95) + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/poly.c#5 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*======== functions in alphabetical order ==========*/ + +/*--------------------------------- + + qh_appendfacet( facet ) + appends facet to end of qh.facet_list, + + returns: + updates qh.newfacet_list, facet_next, facet_list + increments qh.numfacets + + notes: + assumes qh.facet_list/facet_tail is defined (createsimplex) + + see: + qh_removefacet() + +*/ +void qh_appendfacet(facetT *facet) { + facetT *tail= qh facet_tail; + + if (tail == qh newfacet_list) + qh newfacet_list= facet; + if (tail == qh facet_next) + qh facet_next= facet; + facet->previous= tail->previous; + facet->next= tail; + if (tail->previous) + tail->previous->next= facet; + else + qh facet_list= facet; + tail->previous= facet; + qh num_facets++; + trace4((qh ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id)); +} /* appendfacet */ + + +/*--------------------------------- + + qh_appendvertex( vertex ) + appends vertex to end of qh.vertex_list, + + returns: + sets vertex->newlist + updates qh.vertex_list, newvertex_list + increments qh.num_vertices + + notes: + assumes qh.vertex_list/vertex_tail is defined (createsimplex) + +*/ +void qh_appendvertex(vertexT *vertex) { + vertexT *tail= qh vertex_tail; + + if (tail == qh newvertex_list) + qh newvertex_list= vertex; + vertex->newlist= True; + vertex->previous= tail->previous; + vertex->next= tail; + if (tail->previous) + tail->previous->next= vertex; + else + qh vertex_list= vertex; + tail->previous= vertex; + qh num_vertices++; + trace4((qh ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id)); +} /* appendvertex */ + + +/*--------------------------------- + + qh_attachnewfacets( ) + attach horizon facets to new facets in qh.newfacet_list + newfacets have neighbor and ridge links to horizon but not vice versa + only needed for qh.ONLYgood + + returns: + set qh.NEWfacets + horizon facets linked to new facets + ridges changed from visible facets to new facets + simplicial ridges deleted + qh.visible_list, no ridges valid + facet->f.replace is a newfacet (if any) + + design: + delete interior ridges and neighbor sets by + for each visible, non-simplicial facet + for each ridge + if last visit or if neighbor is simplicial + if horizon neighbor + delete ridge for horizon's ridge set + delete ridge + erase neighbor set + attach horizon facets and new facets by + for all new facets + if corresponding horizon facet is simplicial + locate corresponding visible facet {may be more than one} + link visible facet to new facet + replace visible facet with new facet in horizon + else it's non-simplicial + for all visible neighbors of the horizon facet + link visible neighbor to new facet + delete visible neighbor from horizon facet + append new facet to horizon's neighbors + the first ridge of the new facet is the horizon ridge + link the new facet into the horizon ridge +*/ +void qh_attachnewfacets(void ) { + facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible; + ridgeT *ridge, **ridgep; + + qh NEWfacets= True; + trace3((qh ferr, 3012, "qh_attachnewfacets: delete interior ridges\n")); + qh visit_id++; + FORALLvisible_facets { + visible->visitid= qh visit_id; + if (visible->ridges) { + FOREACHridge_(visible->ridges) { + neighbor= otherfacet_(ridge, visible); + if (neighbor->visitid == qh visit_id + || (!neighbor->visible && neighbor->simplicial)) { + if (!neighbor->visible) /* delete ridge for simplicial horizon */ + qh_setdel(neighbor->ridges, ridge); + qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */ + qh_memfree(ridge, (int)sizeof(ridgeT)); + } + } + SETfirst_(visible->ridges)= NULL; + } + SETfirst_(visible->neighbors)= NULL; + } + trace1((qh ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n")); + FORALLnew_facets { + horizon= SETfirstt_(newfacet->neighbors, facetT); + if (horizon->simplicial) { + visible= NULL; + FOREACHneighbor_(horizon) { /* may have more than one horizon ridge */ + if (neighbor->visible) { + if (visible) { + if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices, + SETindex_(horizon->neighbors, neighbor))) { + visible= neighbor; + break; + } + }else + visible= neighbor; + } + } + if (visible) { + visible->f.replace= newfacet; + qh_setreplace(horizon->neighbors, visible, newfacet); + }else { + qh_fprintf(qh ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n", + horizon->id, newfacet->id); + qh_errexit2 (qh_ERRqhull, horizon, newfacet); + } + }else { /* non-simplicial, with a ridge for newfacet */ + FOREACHneighbor_(horizon) { /* may hold for many new facets */ + if (neighbor->visible) { + neighbor->f.replace= newfacet; + qh_setdelnth(horizon->neighbors, + SETindex_(horizon->neighbors, neighbor)); + neighborp--; /* repeat */ + } + } + qh_setappend(&horizon->neighbors, newfacet); + ridge= SETfirstt_(newfacet->ridges, ridgeT); + if (ridge->top == horizon) + ridge->bottom= newfacet; + else + ridge->top= newfacet; + } + } /* newfacets */ + if (qh PRINTstatistics) { + FORALLvisible_facets { + if (!visible->f.replace) + zinc_(Zinsidevisible); + } + } +} /* attachnewfacets */ + +/*--------------------------------- + + qh_checkflipped( facet, dist, allerror ) + checks facet orientation to interior point + + if allerror set, + tests against qh.DISTround + else + tests against 0 since tested against DISTround before + + returns: + False if it flipped orientation (sets facet->flipped) + distance if non-NULL +*/ +boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) { + realT dist; + + if (facet->flipped && !distp) + return False; + zzinc_(Zdistcheck); + qh_distplane(qh interior_point, facet, &dist); + if (distp) + *distp= dist; + if ((allerror && dist > -qh DISTround)|| (!allerror && dist >= 0.0)) { + facet->flipped= True; + zzinc_(Zflippedfacets); + trace0((qh ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n", + facet->id, dist, qh furthest_id)); + qh_precision("flipped facet"); + return False; + } + return True; +} /* checkflipped */ + +/*--------------------------------- + + qh_delfacet( facet ) + removes facet from facet_list and frees up its memory + + notes: + assumes vertices and ridges already freed +*/ +void qh_delfacet(facetT *facet) { + void **freelistp; /* used !qh_NOmem */ + + trace4((qh ferr, 4046, "qh_delfacet: delete f%d\n", facet->id)); + if (facet == qh tracefacet) + qh tracefacet= NULL; + if (facet == qh GOODclosest) + qh GOODclosest= NULL; + qh_removefacet(facet); + if (!facet->tricoplanar || facet->keepcentrum) { + qh_memfree_(facet->normal, qh normal_size, freelistp); + if (qh CENTERtype == qh_ASvoronoi) { /* uses macro calls */ + qh_memfree_(facet->center, qh center_size, freelistp); + }else /* AScentrum */ { + qh_memfree_(facet->center, qh normal_size, freelistp); + } + } + qh_setfree(&(facet->neighbors)); + if (facet->ridges) + qh_setfree(&(facet->ridges)); + qh_setfree(&(facet->vertices)); + if (facet->outsideset) + qh_setfree(&(facet->outsideset)); + if (facet->coplanarset) + qh_setfree(&(facet->coplanarset)); + qh_memfree_(facet, (int)sizeof(facetT), freelistp); +} /* delfacet */ + + +/*--------------------------------- + + qh_deletevisible() + delete visible facets and vertices + + returns: + deletes each facet and removes from facetlist + at exit, qh.visible_list empty (== qh.newfacet_list) + + notes: + ridges already deleted + horizon facets do not reference facets on qh.visible_list + new facets in qh.newfacet_list + uses qh.visit_id; +*/ +void qh_deletevisible(void /*qh visible_list*/) { + facetT *visible, *nextfacet; + vertexT *vertex, **vertexp; + int numvisible= 0, numdel= qh_setsize(qh del_vertices); + + trace1((qh ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n", + qh num_visible, numdel)); + for (visible= qh visible_list; visible && visible->visible; + visible= nextfacet) { /* deleting current */ + nextfacet= visible->next; + numvisible++; + qh_delfacet(visible); + } + if (numvisible != qh num_visible) { + qh_fprintf(qh ferr, 6103, "qhull internal error (qh_deletevisible): qh num_visible %d is not number of visible facets %d\n", + qh num_visible, numvisible); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh num_visible= 0; + zadd_(Zvisfacettot, numvisible); + zmax_(Zvisfacetmax, numvisible); + zzadd_(Zdelvertextot, numdel); + zmax_(Zdelvertexmax, numdel); + FOREACHvertex_(qh del_vertices) + qh_delvertex(vertex); + qh_settruncate(qh del_vertices, 0); +} /* deletevisible */ + +/*--------------------------------- + + qh_facetintersect( facetA, facetB, skipa, skipB, prepend ) + return vertices for intersection of two simplicial facets + may include 1 prepended entry (if more, need to settemppush) + + returns: + returns set of qh.hull_dim-1 + prepend vertices + returns skipped index for each test and checks for exactly one + + notes: + does not need settemp since set in quick memory + + see also: + qh_vertexintersect and qh_vertexintersect_new + use qh_setnew_delnthsorted to get nth ridge (no skip information) + + design: + locate skipped vertex by scanning facet A's neighbors + locate skipped vertex by scanning facet B's neighbors + intersect the vertex sets +*/ +setT *qh_facetintersect(facetT *facetA, facetT *facetB, + int *skipA,int *skipB, int prepend) { + setT *intersect; + int dim= qh hull_dim, i, j; + facetT **neighborsA, **neighborsB; + + neighborsA= SETaddr_(facetA->neighbors, facetT); + neighborsB= SETaddr_(facetB->neighbors, facetT); + i= j= 0; + if (facetB == *neighborsA++) + *skipA= 0; + else if (facetB == *neighborsA++) + *skipA= 1; + else if (facetB == *neighborsA++) + *skipA= 2; + else { + for (i=3; i < dim; i++) { + if (facetB == *neighborsA++) { + *skipA= i; + break; + } + } + } + if (facetA == *neighborsB++) + *skipB= 0; + else if (facetA == *neighborsB++) + *skipB= 1; + else if (facetA == *neighborsB++) + *skipB= 2; + else { + for (j=3; j < dim; j++) { + if (facetA == *neighborsB++) { + *skipB= j; + break; + } + } + } + if (i >= dim || j >= dim) { + qh_fprintf(qh ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n", + facetA->id, facetB->id); + qh_errexit2 (qh_ERRqhull, facetA, facetB); + } + intersect= qh_setnew_delnthsorted(facetA->vertices, qh hull_dim, *skipA, prepend); + trace4((qh ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n", + facetA->id, *skipA, facetB->id, *skipB)); + return(intersect); +} /* facetintersect */ + +/*--------------------------------- + + qh_gethash( hashsize, set, size, firstindex, skipelem ) + return hashvalue for a set with firstindex and skipelem + + notes: + returned hash is in [0,hashsize) + assumes at least firstindex+1 elements + assumes skipelem is NULL, in set, or part of hash + + hashes memory addresses which may change over different runs of the same data + using sum for hash does badly in high d +*/ +int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem) { + void **elemp= SETelemaddr_(set, firstindex, void); + ptr_intT hash = 0, elem; + unsigned result; + int i; +#ifdef _MSC_VER /* Microsoft Visual C++ -- warn about 64-bit issues */ +#pragma warning( push) /* WARN64 -- ptr_intT holds a 64-bit pointer */ +#pragma warning( disable : 4311) /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */ +#endif + + switch (size-firstindex) { + case 1: + hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem; + break; + case 2: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem; + break; + case 3: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + - (ptr_intT) skipelem; + break; + case 4: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] - (ptr_intT) skipelem; + break; + case 5: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem; + break; + case 6: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5] + - (ptr_intT) skipelem; + break; + default: + hash= 0; + i= 3; + do { /* this is about 10% in 10-d */ + if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) { + hash ^= (elem << i) + (elem >> (32-i)); + i += 3; + if (i >= 32) + i -= 32; + } + }while (*elemp); + break; + } + if (hashsize<0) { + qh_fprintf(qh ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize); + qh_errexit2 (qh_ERRqhull, NULL, NULL); + } + result= (unsigned)hash; + result %= (unsigned)hashsize; + /* result= 0; for debugging */ + return result; +#ifdef _MSC_VER +#pragma warning( pop) +#endif +} /* gethash */ + +/*--------------------------------- + + qh_makenewfacet( vertices, toporient, horizon ) + creates a toporient? facet from vertices + + returns: + returns newfacet + adds newfacet to qh.facet_list + newfacet->vertices= vertices + if horizon + newfacet->neighbor= horizon, but not vice versa + newvertex_list updated with vertices +*/ +facetT *qh_makenewfacet(setT *vertices, boolT toporient,facetT *horizon) { + facetT *newfacet; + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newlist) { + qh_removevertex(vertex); + qh_appendvertex(vertex); + } + } + newfacet= qh_newfacet(); + newfacet->vertices= vertices; + newfacet->toporient= (unsigned char)toporient; + if (horizon) + qh_setappend(&(newfacet->neighbors), horizon); + qh_appendfacet(newfacet); + return(newfacet); +} /* makenewfacet */ + + +/*--------------------------------- + + qh_makenewplanes() + make new hyperplanes for facets on qh.newfacet_list + + returns: + all facets have hyperplanes or are marked for merging + doesn't create hyperplane if horizon is coplanar (will merge) + updates qh.min_vertex if qh.JOGGLEmax + + notes: + facet->f.samecycle is defined for facet->mergehorizon facets +*/ +void qh_makenewplanes(void /* newfacet_list */) { + facetT *newfacet; + + FORALLnew_facets { + if (!newfacet->mergehorizon) + qh_setfacetplane(newfacet); + } + if (qh JOGGLEmax < REALmax/2) + minimize_(qh min_vertex, -wwval_(Wnewvertexmax)); +} /* makenewplanes */ + +/*--------------------------------- + + qh_makenew_nonsimplicial( visible, apex, numnew ) + make new facets for ridges of a visible facet + + returns: + first newfacet, bumps numnew as needed + attaches new facets if !qh.ONLYgood + marks ridge neighbors for simplicial visible + if (qh.ONLYgood) + ridges on newfacet, horizon, and visible + else + ridge and neighbors between newfacet and horizon + visible facet's ridges are deleted + + notes: + qh.visit_id if visible has already been processed + sets neighbor->seen for building f.samecycle + assumes all 'seen' flags initially false + + design: + for each ridge of visible facet + get neighbor of visible facet + if neighbor was already processed + delete the ridge (will delete all visible facets later) + if neighbor is a horizon facet + create a new facet + if neighbor coplanar + adds newfacet to f.samecycle for later merging + else + updates neighbor's neighbor set + (checks for non-simplicial facet with multiple ridges to visible facet) + updates neighbor's ridge set + (checks for simplicial neighbor to non-simplicial visible facet) + (deletes ridge if neighbor is simplicial) + +*/ +#ifndef qh_NOmerge +facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { + void **freelistp; /* used !qh_NOmem */ + ridgeT *ridge, **ridgep; + facetT *neighbor, *newfacet= NULL, *samecycle; + setT *vertices; + boolT toporient; + int ridgeid; + + FOREACHridge_(visible->ridges) { + ridgeid= ridge->id; + neighbor= otherfacet_(ridge, visible); + if (neighbor->visible) { + if (!qh ONLYgood) { + if (neighbor->visitid == qh visit_id) { + qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */ + qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); + } + } + }else { /* neighbor is an horizon facet */ + toporient= (ridge->top == visible); + vertices= qh_setnew(qh hull_dim); /* makes sure this is quick */ + qh_setappend(&vertices, apex); + qh_setappend_set(&vertices, ridge->vertices); + newfacet= qh_makenewfacet(vertices, toporient, neighbor); + (*numnew)++; + if (neighbor->coplanar) { + newfacet->mergehorizon= True; + if (!neighbor->seen) { + newfacet->f.samecycle= newfacet; + neighbor->f.newcycle= newfacet; + }else { + samecycle= neighbor->f.newcycle; + newfacet->f.samecycle= samecycle->f.samecycle; + samecycle->f.samecycle= newfacet; + } + } + if (qh ONLYgood) { + if (!neighbor->simplicial) + qh_setappend(&(newfacet->ridges), ridge); + }else { /* qh_attachnewfacets */ + if (neighbor->seen) { + if (neighbor->simplicial) { + qh_fprintf(qh ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n", + neighbor->id, visible->id); + qh_errexit2 (qh_ERRqhull, neighbor, visible); + } + qh_setappend(&(neighbor->neighbors), newfacet); + }else + qh_setreplace(neighbor->neighbors, visible, newfacet); + if (neighbor->simplicial) { + qh_setdel(neighbor->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree(ridge, (int)sizeof(ridgeT)); + }else { + qh_setappend(&(newfacet->ridges), ridge); + if (toporient) + ridge->top= newfacet; + else + ridge->bottom= newfacet; + } + trace4((qh ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n", + newfacet->id, apex->id, ridgeid, neighbor->id)); + } + } + neighbor->seen= True; + } /* for each ridge */ + if (!qh ONLYgood) + SETfirst_(visible->ridges)= NULL; + return newfacet; +} /* makenew_nonsimplicial */ +#else /* qh_NOmerge */ +facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { + return NULL; +} +#endif /* qh_NOmerge */ + +/*--------------------------------- + + qh_makenew_simplicial( visible, apex, numnew ) + make new facets for simplicial visible facet and apex + + returns: + attaches new facets if (!qh.ONLYgood) + neighbors between newfacet and horizon + + notes: + nop if neighbor->seen or neighbor->visible(see qh_makenew_nonsimplicial) + + design: + locate neighboring horizon facet for visible facet + determine vertices and orientation + create new facet + if coplanar, + add new facet to f.samecycle + update horizon facet's neighbor list +*/ +facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) { + facetT *neighbor, **neighborp, *newfacet= NULL; + setT *vertices; + boolT flip, toporient; + int horizonskip, visibleskip; + + FOREACHneighbor_(visible) { + if (!neighbor->seen && !neighbor->visible) { + vertices= qh_facetintersect(neighbor,visible, &horizonskip, &visibleskip, 1); + SETfirst_(vertices)= apex; + flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1)); + if (neighbor->toporient) + toporient= horizonskip & 0x1; + else + toporient= (horizonskip & 0x1) ^ 0x1; + newfacet= qh_makenewfacet(vertices, toporient, neighbor); + (*numnew)++; + if (neighbor->coplanar && (qh PREmerge || qh MERGEexact)) { +#ifndef qh_NOmerge + newfacet->f.samecycle= newfacet; + newfacet->mergehorizon= True; +#endif + } + if (!qh ONLYgood) + SETelem_(neighbor->neighbors, horizonskip)= newfacet; + trace4((qh ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n", + newfacet->id, toporient, apex->id, neighbor->id, horizonskip, + neighbor->toporient, visible->id, visibleskip, flip)); + } + } + return newfacet; +} /* makenew_simplicial */ + +/*--------------------------------- + + qh_matchneighbor( newfacet, newskip, hashsize, hashcount ) + either match subridge of newfacet with neighbor or add to hash_table + + returns: + duplicate ridges are unmatched and marked by qh_DUPLICATEridge + + notes: + ridge is newfacet->vertices w/o newskip vertex + do not allocate memory (need to free hash_table cleanly) + uses linear hash chains + + see also: + qh_matchduplicates + + design: + for each possible matching facet in qh.hash_table + if vertices match + set ismatch, if facets have opposite orientation + if ismatch and matching facet doesn't have a match + match the facets by updating their neighbor sets + else + indicate a duplicate ridge + set facet hyperplane for later testing + add facet to hashtable + unless the other facet was already a duplicate ridge + mark both facets with a duplicate ridge + add other facet (if defined) to hash table +*/ +void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcount) { + boolT newfound= False; /* True, if new facet is already in hash chain */ + boolT same, ismatch; + int hash, scan; + facetT *facet, *matchfacet; + int skip, matchskip; + + hash= qh_gethash(hashsize, newfacet->vertices, qh hull_dim, 1, + SETelem_(newfacet->vertices, newskip)); + trace4((qh ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n", + newfacet->id, newskip, hash, *hashcount)); + zinc_(Zhashlookup); + for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT)); + scan= (++scan >= hashsize ? 0 : scan)) { + if (facet == newfacet) { + newfound= True; + continue; + } + zinc_(Zhashtests); + if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + if (SETelem_(newfacet->vertices, newskip) == + SETelem_(facet->vertices, skip)) { + qh_precision("two facets with the same vertices"); + qh_fprintf(qh ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d. Can not force output.\n", + facet->id, newfacet->id); + qh_errexit2 (qh_ERRprec, facet, newfacet); + } + ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient))); + matchfacet= SETelemt_(facet->neighbors, skip, facetT); + if (ismatch && !matchfacet) { + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= facet; + (*hashcount)--; + trace4((qh ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n", + facet->id, skip, newfacet->id, newskip)); + return; + } + if (!qh PREmerge && !qh MERGEexact) { + qh_precision("a ridge with more than two neighbors"); + qh_fprintf(qh ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue.\n", + facet->id, newfacet->id, getid_(matchfacet)); + qh_errexit2 (qh_ERRprec, facet, newfacet); + } + SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge; + newfacet->dupridge= True; + if (!newfacet->normal) + qh_setfacetplane(newfacet); + qh_addhash(newfacet, qh hash_table, hashsize, hash); + (*hashcount)++; + if (!facet->normal) + qh_setfacetplane(facet); + if (matchfacet != qh_DUPLICATEridge) { + SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge; + facet->dupridge= True; + if (!facet->normal) + qh_setfacetplane(facet); + if (matchfacet) { + matchskip= qh_setindex(matchfacet->neighbors, facet); + SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; + matchfacet->dupridge= True; + if (!matchfacet->normal) + qh_setfacetplane(matchfacet); + qh_addhash(matchfacet, qh hash_table, hashsize, hash); + *hashcount += 2; + } + } + trace4((qh ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n", + newfacet->id, newskip, facet->id, skip, + (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)), + ismatch, hash)); + return; /* end of duplicate ridge */ + } + } + if (!newfound) + SETelem_(qh hash_table, scan)= newfacet; /* same as qh_addhash */ + (*hashcount)++; + trace4((qh ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n", + newfacet->id, newskip, hash)); +} /* matchneighbor */ + + +/*--------------------------------- + + qh_matchnewfacets() + match newfacets in qh.newfacet_list to their newfacet neighbors + + returns: + qh.newfacet_list with full neighbor sets + get vertices with nth neighbor by deleting nth vertex + if qh.PREmerge/MERGEexact or qh.FORCEoutput + sets facet->flippped if flipped normal (also prevents point partitioning) + if duplicate ridges and qh.PREmerge/MERGEexact + sets facet->dupridge + missing neighbor links identifies extra ridges to be merging (qh_MERGEridge) + + notes: + newfacets already have neighbor[0] (horizon facet) + assumes qh.hash_table is NULL + vertex->neighbors has not been updated yet + do not allocate memory after qh.hash_table (need to free it cleanly) + + design: + delete neighbor sets for all new facets + initialize a hash table + for all new facets + match facet with neighbors + if unmatched facets (due to duplicate ridges) + for each new facet with a duplicate ridge + match it with a facet + check for flipped facets +*/ +void qh_matchnewfacets(void /* qh newfacet_list */) { + int numnew=0, hashcount=0, newskip; + facetT *newfacet, *neighbor; + int dim= qh hull_dim, hashsize, neighbor_i, neighbor_n; + setT *neighbors; +#ifndef qh_NOtrace + int facet_i, facet_n, numfree= 0; + facetT *facet; +#endif + + trace1((qh ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n")); + FORALLnew_facets { + numnew++; + { /* inline qh_setzero(newfacet->neighbors, 1, qh hull_dim); */ + neighbors= newfacet->neighbors; + neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/ + memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize); + } + } + + qh_newhashtable(numnew*(qh hull_dim-1)); /* twice what is normally needed, + but every ridge could be DUPLICATEridge */ + hashsize= qh_setsize(qh hash_table); + FORALLnew_facets { + for (newskip=1; newskipneighbors, k, facetT); + if (!neighbor || neighbor == qh_DUPLICATEridge) + count++; + } + if (facet == newfacet) + break; + } + if (count != hashcount) { + qh_fprintf(qh ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n", + newfacet->id, hashcount, count); + qh_errexit(qh_ERRqhull, newfacet, NULL); + } + } +#endif /* end of trap code */ + } + if (hashcount) { + FORALLnew_facets { + if (newfacet->dupridge) { + FOREACHneighbor_i_(newfacet) { + if (neighbor == qh_DUPLICATEridge) { + qh_matchduplicates(newfacet, neighbor_i, hashsize, &hashcount); + /* this may report MERGEfacet */ + } + } + } + } + } + if (hashcount) { + qh_fprintf(qh ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n", + hashcount); + qh_printhashtable(qh ferr); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +#ifndef qh_NOtrace + if (qh IStracing >= 2) { + FOREACHfacet_i_(qh hash_table) { + if (!facet) + numfree++; + } + qh_fprintf(qh ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries . hashsize %d\n", + numnew, numfree, qh_setsize(qh hash_table)); + } +#endif /* !qh_NOtrace */ + qh_setfree(&qh hash_table); + if (qh PREmerge || qh MERGEexact) { + if (qh IStracing >= 4) + qh_printfacetlist(qh newfacet_list, NULL, qh_ALL); + FORALLnew_facets { + if (newfacet->normal) + qh_checkflipped(newfacet, NULL, qh_ALL); + } + }else if (qh FORCEoutput) + qh_checkflipped_all(qh newfacet_list); /* prints warnings for flipped */ +} /* matchnewfacets */ + + +/*--------------------------------- + + qh_matchvertices( firstindex, verticesA, skipA, verticesB, skipB, same ) + tests whether vertices match with a single skip + starts match at firstindex since all new facets have a common vertex + + returns: + true if matched vertices + skip index for each set + sets same iff vertices have the same orientation + + notes: + assumes skipA is in A and both sets are the same size + + design: + set up pointers + scan both sets checking for a match + test orientation +*/ +boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA, + setT *verticesB, int *skipB, boolT *same) { + vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp; + + elemAp= SETelemaddr_(verticesA, firstindex, vertexT); + elemBp= SETelemaddr_(verticesB, firstindex, vertexT); + skipAp= SETelemaddr_(verticesA, skipA, vertexT); + do if (elemAp != skipAp) { + while (*elemAp != *elemBp++) { + if (skipBp) + return False; + skipBp= elemBp; /* one extra like FOREACH */ + } + }while (*(++elemAp)); + if (!skipBp) + skipBp= ++elemBp; + *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */ + *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */ + trace4((qh ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n", + skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same)); + return(True); +} /* matchvertices */ + +/*--------------------------------- + + qh_newfacet() + return a new facet + + returns: + all fields initialized or cleared (NULL) + preallocates neighbors set +*/ +facetT *qh_newfacet(void) { + facetT *facet; + void **freelistp; /* used !qh_NOmem */ + + qh_memalloc_((int)sizeof(facetT), freelistp, facet, facetT); + memset((char *)facet, (size_t)0, sizeof(facetT)); + if (qh facet_id == qh tracefacet_id) + qh tracefacet= facet; + facet->id= qh facet_id++; + facet->neighbors= qh_setnew(qh hull_dim); +#if !qh_COMPUTEfurthest + facet->furthestdist= 0.0; +#endif +#if qh_MAXoutside + if (qh FORCEoutput && qh APPROXhull) + facet->maxoutside= qh MINoutside; + else + facet->maxoutside= qh DISTround; +#endif + facet->simplicial= True; + facet->good= True; + facet->newfacet= True; + trace4((qh ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id)); + return(facet); +} /* newfacet */ + + +/*--------------------------------- + + qh_newridge() + return a new ridge +*/ +ridgeT *qh_newridge(void) { + ridgeT *ridge; + void **freelistp; /* used !qh_NOmem */ + + qh_memalloc_((int)sizeof(ridgeT), freelistp, ridge, ridgeT); + memset((char *)ridge, (size_t)0, sizeof(ridgeT)); + zinc_(Ztotridges); + if (qh ridge_id == 0xFFFFFF) { + qh_fprintf(qh ferr, 7074, "\ +qhull warning: more than %d ridges. ID field overflows and two ridges\n\ +may have the same identifier. Otherwise output ok.\n", 0xFFFFFF); + } + ridge->id= qh ridge_id++; + trace4((qh ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id)); + return(ridge); +} /* newridge */ + + +/*--------------------------------- + + qh_pointid( ) + return id for a point, + returns -3 if null, -2 if interior, or -1 if not known + + alternative code: + unsigned long id; + id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size; + + notes: + WARN64 -- id truncated to 32-bits, at most 2G points + NOerrors returned (QhullPoint::id) + if point not in point array + the code does a comparison of unrelated pointers. +*/ +int qh_pointid(pointT *point) { + ptr_intT offset, id; + + if (!point) + return -3; + else if (point == qh interior_point) + return -2; + else if (point >= qh first_point + && point < qh first_point + qh num_points * qh hull_dim) { + offset= (ptr_intT)(point - qh first_point); + id= offset / qh hull_dim; + }else if ((id= qh_setindex(qh other_points, point)) != -1) + id += qh num_points; + else + return -1; + return (int)id; +} /* pointid */ + +/*--------------------------------- + + qh_removefacet( facet ) + unlinks facet from qh.facet_list, + + returns: + updates qh.facet_list .newfacet_list .facet_next visible_list + decrements qh.num_facets + + see: + qh_appendfacet +*/ +void qh_removefacet(facetT *facet) { + facetT *next= facet->next, *previous= facet->previous; + + if (facet == qh newfacet_list) + qh newfacet_list= next; + if (facet == qh facet_next) + qh facet_next= next; + if (facet == qh visible_list) + qh visible_list= next; + if (previous) { + previous->next= next; + next->previous= previous; + }else { /* 1st facet in qh facet_list */ + qh facet_list= next; + qh facet_list->previous= NULL; + } + qh num_facets--; + trace4((qh ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id)); +} /* removefacet */ + + +/*--------------------------------- + + qh_removevertex( vertex ) + unlinks vertex from qh.vertex_list, + + returns: + updates qh.vertex_list .newvertex_list + decrements qh.num_vertices +*/ +void qh_removevertex(vertexT *vertex) { + vertexT *next= vertex->next, *previous= vertex->previous; + + if (vertex == qh newvertex_list) + qh newvertex_list= next; + if (previous) { + previous->next= next; + next->previous= previous; + }else { /* 1st vertex in qh vertex_list */ + qh vertex_list= vertex->next; + qh vertex_list->previous= NULL; + } + qh num_vertices--; + trace4((qh ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id)); +} /* removevertex */ + + +/*--------------------------------- + + qh_updatevertices() + update vertex neighbors and delete interior vertices + + returns: + if qh.VERTEXneighbors, updates neighbors for each vertex + if qh.newvertex_list, + removes visible neighbors from vertex neighbors + if qh.newfacet_list + adds new facets to vertex neighbors + if qh.visible_list + interior vertices added to qh.del_vertices for later partitioning + + design: + if qh.VERTEXneighbors + deletes references to visible facets from vertex neighbors + appends new facets to the neighbor list for each vertex + checks all vertices of visible facets + removes visible facets from neighbor lists + marks unused vertices for deletion +*/ +void qh_updatevertices(void /*qh newvertex_list, newfacet_list, visible_list*/) { + facetT *newfacet= NULL, *neighbor, **neighborp, *visible; + vertexT *vertex, **vertexp; + + trace3((qh ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n")); + if (qh VERTEXneighbors) { + FORALLvertex_(qh newvertex_list) { + FOREACHneighbor_(vertex) { + if (neighbor->visible) + SETref_(neighbor)= NULL; + } + qh_setcompact(vertex->neighbors); + } + FORALLnew_facets { + FOREACHvertex_(newfacet->vertices) + qh_setappend(&vertex->neighbors, newfacet); + } + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newlist && !vertex->deleted) { + FOREACHneighbor_(vertex) { /* this can happen under merging */ + if (!neighbor->visible) + break; + } + if (neighbor) + qh_setdel(vertex->neighbors, visible); + else { + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + trace2((qh ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n", + qh_pointid(vertex->point), vertex->id, visible->id)); + } + } + } + } + }else { /* !VERTEXneighbors */ + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newlist && !vertex->deleted) { + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + trace2((qh ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n", + qh_pointid(vertex->point), vertex->id, visible->id)); + } + } + } + } +} /* updatevertices */ diff --git a/extern/qhull/poly.h b/extern/qhull/poly.h new file mode 100644 index 000000000000..9cf04cfbacf7 --- /dev/null +++ b/extern/qhull/poly.h @@ -0,0 +1,295 @@ +/* --------------------------------- + + poly.h + header file for poly.c and poly2.c + + see qh-poly.htm, libqhull.h and poly.c + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/poly.h#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#ifndef qhDEFpoly +#define qhDEFpoly 1 + +#include "libqhull.h" + +/*=============== constants ========================== */ + +/*---------------------------------- + + ALGORITHMfault + use as argument to checkconvex() to report errors during buildhull +*/ +#define qh_ALGORITHMfault 0 + +/*---------------------------------- + + DATAfault + use as argument to checkconvex() to report errors during initialhull +*/ +#define qh_DATAfault 1 + +/*---------------------------------- + + DUPLICATEridge + special value for facet->neighbor to indicate a duplicate ridge + + notes: + set by matchneighbor, used by matchmatch and mark_dupridge +*/ +#define qh_DUPLICATEridge (facetT *)1L + +/*---------------------------------- + + MERGEridge flag in facet + special value for facet->neighbor to indicate a merged ridge + + notes: + set by matchneighbor, used by matchmatch and mark_dupridge +*/ +#define qh_MERGEridge (facetT *)2L + + +/*============ -structures- ====================*/ + +/*=========== -macros- =========================*/ + +/*---------------------------------- + + FORALLfacet_( facetlist ) { ... } + assign 'facet' to each facet in facetlist + + notes: + uses 'facetT *facet;' + assumes last facet is a sentinel + + see: + FORALLfacets +*/ +#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next ) + +/*---------------------------------- + + FORALLnew_facets { ... } + assign 'newfacet' to each facet in qh.newfacet_list + + notes: + uses 'facetT *newfacet;' + at exit, newfacet==NULL +*/ +#define FORALLnew_facets for ( newfacet=qh newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next ) + +/*---------------------------------- + + FORALLvertex_( vertexlist ) { ... } + assign 'vertex' to each vertex in vertexlist + + notes: + uses 'vertexT *vertex;' + at exit, vertex==NULL +*/ +#define FORALLvertex_( vertexlist ) for (vertex=( vertexlist );vertex && vertex->next;vertex= vertex->next ) + +/*---------------------------------- + + FORALLvisible_facets { ... } + assign 'visible' to each visible facet in qh.visible_list + + notes: + uses 'vacetT *visible;' + at exit, visible==NULL +*/ +#define FORALLvisible_facets for (visible=qh visible_list; visible && visible->visible; visible= visible->next) + +/*---------------------------------- + + FORALLsame_( newfacet ) { ... } + assign 'same' to each facet in newfacet->f.samecycle + + notes: + uses 'facetT *same;' + stops when it returns to newfacet +*/ +#define FORALLsame_(newfacet) for (same= newfacet->f.samecycle; same != newfacet; same= same->f.samecycle) + +/*---------------------------------- + + FORALLsame_cycle_( newfacet ) { ... } + assign 'same' to each facet in newfacet->f.samecycle + + notes: + uses 'facetT *same;' + at exit, same == NULL +*/ +#define FORALLsame_cycle_(newfacet) \ + for (same= newfacet->f.samecycle; \ + same; same= (same == newfacet ? NULL : same->f.samecycle)) + +/*---------------------------------- + + FOREACHneighborA_( facet ) { ... } + assign 'neighborA' to each neighbor in facet->neighbors + + FOREACHneighborA_( vertex ) { ... } + assign 'neighborA' to each neighbor in vertex->neighbors + + declare: + facetT *neighborA, **neighborAp; + + see: + FOREACHsetelement_ +*/ +#define FOREACHneighborA_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighborA) + +/*---------------------------------- + + FOREACHvisible_( facets ) { ... } + assign 'visible' to each facet in facets + + notes: + uses 'facetT *facet, *facetp;' + see FOREACHsetelement_ +*/ +#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible) + +/*---------------------------------- + + FOREACHnewfacet_( facets ) { ... } + assign 'newfacet' to each facet in facets + + notes: + uses 'facetT *newfacet, *newfacetp;' + see FOREACHsetelement_ +*/ +#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet) + +/*---------------------------------- + + FOREACHvertexA_( vertices ) { ... } + assign 'vertexA' to each vertex in vertices + + notes: + uses 'vertexT *vertexA, *vertexAp;' + see FOREACHsetelement_ +*/ +#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA) + +/*---------------------------------- + + FOREACHvertexreverse12_( vertices ) { ... } + assign 'vertex' to each vertex in vertices + reverse order of first two vertices + + notes: + uses 'vertexT *vertex, *vertexp;' + see FOREACHsetelement_ +*/ +#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex) + + +/*=============== prototypes poly.c in alphabetical order ================*/ + +void qh_appendfacet(facetT *facet); +void qh_appendvertex(vertexT *vertex); +void qh_attachnewfacets(void); +boolT qh_checkflipped(facetT *facet, realT *dist, boolT allerror); +void qh_delfacet(facetT *facet); +void qh_deletevisible(void /*qh visible_list, qh horizon_list*/); +setT *qh_facetintersect(facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra); +int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem); +facetT *qh_makenewfacet(setT *vertices, boolT toporient, facetT *facet); +void qh_makenewplanes(void /* newfacet_list */); +facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew); +facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew); +void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, + int *hashcount); +void qh_matchnewfacets(void); +boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA, + setT *verticesB, int *skipB, boolT *same); +facetT *qh_newfacet(void); +ridgeT *qh_newridge(void); +int qh_pointid(pointT *point); +void qh_removefacet(facetT *facet); +void qh_removevertex(vertexT *vertex); +void qh_updatevertices(void); + + +/*========== -prototypes poly2.c in alphabetical order ===========*/ + +void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash); +void qh_check_bestdist(void); +void qh_check_maxout(void); +void qh_check_output(void); +void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2); +void qh_check_points(void); +void qh_checkconvex(facetT *facetlist, int fault); +void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp); +void qh_checkflipped_all(facetT *facetlist); +void qh_checkpolygon(facetT *facetlist); +void qh_checkvertex(vertexT *vertex); +void qh_clearcenters(qh_CENTER type); +void qh_createsimplex(setT *vertices); +void qh_delridge(ridgeT *ridge); +void qh_delvertex(vertexT *vertex); +setT *qh_facet3vertex(facetT *facet); +facetT *qh_findbestfacet(pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside); +facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart); +facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, + int *numpart); +int qh_findgood(facetT *facetlist, int goodhorizon); +void qh_findgood_all(facetT *facetlist); +void qh_furthestnext(void /* qh facet_list */); +void qh_furthestout(facetT *facet); +void qh_infiniteloop(facetT *facet); +void qh_initbuild(void); +void qh_initialhull(setT *vertices); +setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints); +vertexT *qh_isvertex(pointT *point, setT *vertices); +vertexT *qh_makenewfacets(pointT *point /*horizon_list, visible_list*/); +void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount); +void qh_nearcoplanar(void /* qh.facet_list */); +vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp); +int qh_newhashtable(int newsize); +vertexT *qh_newvertex(pointT *point); +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp); +void qh_outcoplanar(void /* facet_list */); +pointT *qh_point(int id); +void qh_point_add(setT *set, pointT *point, void *elem); +setT *qh_pointfacet(void /*qh facet_list*/); +setT *qh_pointvertex(void /*qh facet_list*/); +void qh_prependfacet(facetT *facet, facetT **facetlist); +void qh_printhashtable(FILE *fp); +void qh_printlists(void); +void qh_resetlists(boolT stats, boolT resetVisible /*qh newvertex_list newfacet_list visible_list*/); +void qh_setvoronoi_all(void); +void qh_triangulate(void /*qh facet_list*/); +void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex); +void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB); +void qh_triangulate_mirror(facetT *facetA, facetT *facetB); +void qh_triangulate_null(facetT *facetA); +void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB); +setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB); +void qh_vertexneighbors(void /*qh facet_list*/); +boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB); + + +#endif /* qhDEFpoly */ diff --git a/extern/qhull/poly2.c b/extern/qhull/poly2.c new file mode 100644 index 000000000000..317461fc54ef --- /dev/null +++ b/extern/qhull/poly2.c @@ -0,0 +1,3154 @@ +/*--------------------------------- + + poly2.c + implements polygons and simplices + + see qh-poly.htm, poly.h and libqhull.h + + frequently used code is in poly.c + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/poly2.c#5 $$Change: 1490 $ + $DateTime: 2012/02/19 20:27:01 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*======== functions in alphabetical order ==========*/ + +/*--------------------------------- + + qh_addhash( newelem, hashtable, hashsize, hash ) + add newelem to linear hash table at hash if not already there +*/ +void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash) { + int scan; + void *elem; + + for (scan= (int)hash; (elem= SETelem_(hashtable, scan)); + scan= (++scan >= hashsize ? 0 : scan)) { + if (elem == newelem) + break; + } + /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */ + if (!elem) + SETelem_(hashtable, scan)= newelem; +} /* addhash */ + +/*--------------------------------- + + qh_check_bestdist() + check that all points are within max_outside of the nearest facet + if qh.ONLYgood, + ignores !good facets + + see: + qh_check_maxout(), qh_outerinner() + + notes: + only called from qh_check_points() + seldom used since qh.MERGING is almost always set + if notverified>0 at end of routine + some points were well inside the hull. If the hull contains + a lens-shaped component, these points were not verified. Use + options 'Qi Tv' to verify all points. (Exhaustive check also verifies) + + design: + determine facet for each point (if any) + for each point + start with the assigned facet or with the first facet + find the best facet for the point and check all coplanar facets + error if point is outside of facet +*/ +void qh_check_bestdist(void) { + boolT waserror= False, unassigned; + facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL; + facetT *facetlist; + realT dist, maxoutside, maxdist= -REALmax; + pointT *point; + int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0; + setT *facets; + + trace1((qh ferr, 1020, "qh_check_bestdist: check points below nearest facet. Facet_list f%d\n", + qh facet_list->id)); + maxoutside= qh_maxouter(); + maxoutside += qh DISTround; + /* one more qh.DISTround for check computation */ + trace1((qh ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside)); + facets= qh_pointfacet(/*qh facet_list*/); + if (!qh_QUICKhelp && qh PRINTprecision) + qh_fprintf(qh ferr, 8091, "\n\ +qhull output completed. Verifying that %d points are\n\ +below %2.2g of the nearest %sfacet.\n", + qh_setsize(facets), maxoutside, (qh ONLYgood ? "good " : "")); + FOREACHfacet_i_(facets) { /* for each point with facet assignment */ + if (facet) + unassigned= False; + else { + unassigned= True; + facet= qh facet_list; + } + point= qh_point(facet_i); + if (point == qh GOODpointp) + continue; + qh_distplane(point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart); + /* occurs after statistics reported */ + maximize_(maxdist, dist); + if (dist > maxoutside) { + if (qh ONLYgood && !bestfacet->good + && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) + notgood++; + else { + waserror= True; + qh_fprintf(qh ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n", + facet_i, bestfacet->id, dist, maxoutside); + if (errfacet1 != bestfacet) { + errfacet2= errfacet1; + errfacet1= bestfacet; + } + } + }else if (unassigned && dist < -qh MAXcoplanar) + notverified++; + } + qh_settempfree(&facets); + if (notverified && !qh DELAUNAY && !qh_QUICKhelp && qh PRINTprecision) + qh_fprintf(qh ferr, 8092, "\n%d points were well inside the hull. If the hull contains\n\ +a lens-shaped component, these points were not verified. Use\n\ +options 'Qci Tv' to verify all points.\n", notverified); + if (maxdist > qh outside_err) { + qh_fprintf(qh ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n", + maxdist, qh outside_err); + qh_errexit2 (qh_ERRprec, errfacet1, errfacet2); + }else if (waserror && qh outside_err > REALmax/2) + qh_errexit2 (qh_ERRprec, errfacet1, errfacet2); + /* else if waserror, the error was logged to qh.ferr but does not effect the output */ + trace0((qh ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist)); +} /* check_bestdist */ + +/*--------------------------------- + + qh_check_maxout() + updates qh.max_outside by checking all points against bestfacet + if qh.ONLYgood, ignores !good facets + + returns: + updates facet->maxoutside via qh_findbesthorizon() + sets qh.maxoutdone + if printing qh.min_vertex (qh_outerinner), + it is updated to the current vertices + removes inside/coplanar points from coplanarset as needed + + notes: + defines coplanar as min_vertex instead of MAXcoplanar + may not need to check near-inside points because of qh.MAXcoplanar + and qh.KEEPnearinside (before it was -DISTround) + + see also: + qh_check_bestdist() + + design: + if qh.min_vertex is needed + for all neighbors of all vertices + test distance from vertex to neighbor + determine facet for each point (if any) + for each point with an assigned facet + find the best facet for the point and check all coplanar facets + (updates outer planes) + remove near-inside points from coplanar sets +*/ +#ifndef qh_NOmerge +void qh_check_maxout(void) { + facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist; + realT dist, maxoutside, minvertex, old_maxoutside; + pointT *point; + int numpart= 0, facet_i, facet_n, notgood= 0; + setT *facets, *vertices; + vertexT *vertex; + + trace1((qh ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n")); + maxoutside= minvertex= 0; + if (qh VERTEXneighbors + && (qh PRINTsummary || qh KEEPinside || qh KEEPcoplanar + || qh TRACElevel || qh PRINTstatistics + || qh PRINTout[0] == qh_PRINTsummary || qh PRINTout[0] == qh_PRINTnone)) { + trace1((qh ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n")); + vertices= qh_pointvertex(/*qh facet_list*/); + FORALLvertices { + FOREACHneighbor_(vertex) { + zinc_(Zdistvertex); /* distance also computed by main loop below */ + qh_distplane(vertex->point, neighbor, &dist); + minimize_(minvertex, dist); + if (-dist > qh TRACEdist || dist > qh TRACEdist + || neighbor == qh tracefacet || vertex == qh tracevertex) + qh_fprintf(qh ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n", + qh_pointid(vertex->point), vertex->id, dist, neighbor->id); + } + } + if (qh MERGING) { + wmin_(Wminvertex, qh min_vertex); + } + qh min_vertex= minvertex; + qh_settempfree(&vertices); + } + facets= qh_pointfacet(/*qh facet_list*/); + do { + old_maxoutside= fmax_(qh max_outside, maxoutside); + FOREACHfacet_i_(facets) { /* for each point with facet assignment */ + if (facet) { + point= qh_point(facet_i); + if (point == qh GOODpointp) + continue; + zzinc_(Ztotcheck); + qh_distplane(point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart); + if (bestfacet && dist > maxoutside) { + if (qh ONLYgood && !bestfacet->good + && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) + notgood++; + else + maxoutside= dist; + } + if (dist > qh TRACEdist || (bestfacet && bestfacet == qh tracefacet)) + qh_fprintf(qh ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n", + qh_pointid(point), dist, bestfacet->id); + } + } + }while + (maxoutside > 2*old_maxoutside); + /* if qh.maxoutside increases substantially, qh_SEARCHdist is not valid + e.g., RBOX 5000 s Z1 G1e-13 t1001200614 | qhull */ + zzadd_(Zcheckpart, numpart); + qh_settempfree(&facets); + wval_(Wmaxout)= maxoutside - qh max_outside; + wmax_(Wmaxoutside, qh max_outside); + qh max_outside= maxoutside; + qh_nearcoplanar(/*qh.facet_list*/); + qh maxoutdone= True; + trace1((qh ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n", + maxoutside, qh min_vertex, notgood)); +} /* check_maxout */ +#else /* qh_NOmerge */ +void qh_check_maxout(void) { +} +#endif + +/*--------------------------------- + + qh_check_output() + performs the checks at the end of qhull algorithm + Maybe called after voronoi output. Will recompute otherwise centrums are Voronoi centers instead +*/ +void qh_check_output(void) { + int i; + + if (qh STOPcone) + return; + if (qh VERIFYoutput | qh IStracing | qh CHECKfrequently) { + qh_checkpolygon(qh facet_list); + qh_checkflipped_all(qh facet_list); + qh_checkconvex(qh facet_list, qh_ALGORITHMfault); + }else if (!qh MERGING && qh_newstats(qhstat precision, &i)) { + qh_checkflipped_all(qh facet_list); + qh_checkconvex(qh facet_list, qh_ALGORITHMfault); + } +} /* check_output */ + + + +/*--------------------------------- + + qh_check_point( point, facet, maxoutside, maxdist, errfacet1, errfacet2 ) + check that point is less than maxoutside from facet +*/ +void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) { + realT dist; + + /* occurs after statistics reported */ + qh_distplane(point, facet, &dist); + if (dist > *maxoutside) { + if (*errfacet1 != facet) { + *errfacet2= *errfacet1; + *errfacet1= facet; + } + qh_fprintf(qh ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n", + qh_pointid(point), facet->id, dist, *maxoutside); + } + maximize_(*maxdist, dist); +} /* qh_check_point */ + + +/*--------------------------------- + + qh_check_points() + checks that all points are inside all facets + + notes: + if many points and qh_check_maxout not called (i.e., !qh.MERGING), + calls qh_findbesthorizon (seldom done). + ignores flipped facets + maxoutside includes 2 qh.DISTrounds + one qh.DISTround for the computed distances in qh_check_points + qh_printafacet and qh_printsummary needs only one qh.DISTround + the computation for qh.VERIFYdirect does not account for qh.other_points + + design: + if many points + use qh_check_bestdist() + else + for all facets + for all points + check that point is inside facet +*/ +void qh_check_points(void) { + facetT *facet, *errfacet1= NULL, *errfacet2= NULL; + realT total, maxoutside, maxdist= -REALmax; + pointT *point, **pointp, *pointtemp; + boolT testouter; + + maxoutside= qh_maxouter(); + maxoutside += qh DISTround; + /* one more qh.DISTround for check computation */ + trace1((qh ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n", + maxoutside)); + if (qh num_good) /* miss counts other_points and !good facets */ + total= (float)qh num_good * (float)qh num_points; + else + total= (float)qh num_facets * (float)qh num_points; + if (total >= qh_VERIFYdirect && !qh maxoutdone) { + if (!qh_QUICKhelp && qh SKIPcheckmax && qh MERGING) + qh_fprintf(qh ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\ +Verify may report that a point is outside of a facet.\n"); + qh_check_bestdist(); + }else { + if (qh_MAXoutside && qh maxoutdone) + testouter= True; + else + testouter= False; + if (!qh_QUICKhelp) { + if (qh MERGEexact) + qh_fprintf(qh ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point\n\ +is outside of a facet. See qh-optq.htm#Qx\n"); + else if (qh SKIPcheckmax || qh NOnearinside) + qh_fprintf(qh ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\ +near-inside points ('Q8'). Verify may report that a point is outside\n\ +of a facet.\n"); + } + if (qh PRINTprecision) { + if (testouter) + qh_fprintf(qh ferr, 8098, "\n\ +Output completed. Verifying that all points are below outer planes of\n\ +all %sfacets. Will make %2.0f distance computations.\n", + (qh ONLYgood ? "good " : ""), total); + else + qh_fprintf(qh ferr, 8099, "\n\ +Output completed. Verifying that all points are below %2.2g of\n\ +all %sfacets. Will make %2.0f distance computations.\n", + maxoutside, (qh ONLYgood ? "good " : ""), total); + } + FORALLfacets { + if (!facet->good && qh ONLYgood) + continue; + if (facet->flipped) + continue; + if (!facet->normal) { + qh_fprintf(qh ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id); + continue; + } + if (testouter) { +#if qh_MAXoutside + maxoutside= facet->maxoutside + 2* qh DISTround; + /* one DISTround to actual point and another to computed point */ +#endif + } + FORALLpoints { + if (point != qh GOODpointp) + qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2); + } + FOREACHpoint_(qh other_points) { + if (point != qh GOODpointp) + qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2); + } + } + if (maxdist > qh outside_err) { + qh_fprintf(qh ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n", + maxdist, qh outside_err ); + qh_errexit2( qh_ERRprec, errfacet1, errfacet2 ); + }else if (errfacet1 && qh outside_err > REALmax/2) + qh_errexit2( qh_ERRprec, errfacet1, errfacet2 ); + /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */ + trace0((qh ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist)); + } +} /* check_points */ + + +/*--------------------------------- + + qh_checkconvex( facetlist, fault ) + check that each ridge in facetlist is convex + fault = qh_DATAfault if reporting errors + = qh_ALGORITHMfault otherwise + + returns: + counts Zconcaveridges and Zcoplanarridges + errors if concaveridge or if merging an coplanar ridge + + note: + if not merging, + tests vertices for neighboring simplicial facets + else if ZEROcentrum, + tests vertices for neighboring simplicial facets + else + tests centrums of neighboring facets + + design: + for all facets + report flipped facets + if ZEROcentrum and simplicial neighbors + test vertices for neighboring simplicial facets + else + test centrum against all neighbors +*/ +void qh_checkconvex(facetT *facetlist, int fault) { + facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL; + vertexT *vertex; + realT dist; + pointT *centrum; + boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial; + int neighbor_i; + + trace1((qh ferr, 1026, "qh_checkconvex: check all ridges are convex\n")); + if (!qh RERUN) { + zzval_(Zconcaveridges)= 0; + zzval_(Zcoplanarridges)= 0; + } + FORALLfacet_(facetlist) { + if (facet->flipped) { + qh_precision("flipped facet"); + qh_fprintf(qh ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n", + facet->id); + errfacet1= facet; + waserror= True; + continue; + } + if (qh MERGING && (!qh ZEROcentrum || !facet->simplicial || facet->tricoplanar)) + allsimplicial= False; + else { + allsimplicial= True; + neighbor_i= 0; + FOREACHneighbor_(facet) { + vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT); + if (!neighbor->simplicial || neighbor->tricoplanar) { + allsimplicial= False; + continue; + } + qh_distplane(vertex->point, neighbor, &dist); + if (dist > -qh DISTround) { + if (fault == qh_DATAfault) { + qh_precision("coplanar or concave ridge"); + qh_fprintf(qh ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist); + qh_errexit(qh_ERRsingular, NULL, NULL); + } + if (dist > qh DISTround) { + zzinc_(Zconcaveridges); + qh_precision("concave ridge"); + qh_fprintf(qh ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + }else if (qh ZEROcentrum) { + if (dist > 0) { /* qh_checkzero checks that dist < - qh DISTround */ + zzinc_(Zcoplanarridges); + qh_precision("coplanar ridge"); + qh_fprintf(qh ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + } + }else { + zzinc_(Zcoplanarridges); + qh_precision("coplanar ridge"); + trace0((qh ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, qh furthest_id)); + } + } + } + } + if (!allsimplicial) { + if (qh CENTERtype == qh_AScentrum) { + if (!facet->center) + facet->center= qh_getcentrum(facet); + centrum= facet->center; + }else { + if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) { + centrum_warning= True; + qh_fprintf(qh ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n"); + } + centrum= qh_getcentrum(facet); + tempcentrum= True; + } + FOREACHneighbor_(facet) { + if (qh ZEROcentrum && facet->simplicial && neighbor->simplicial) + continue; + if (facet->tricoplanar || neighbor->tricoplanar) + continue; + zzinc_(Zdistconvex); + qh_distplane(centrum, neighbor, &dist); + if (dist > qh DISTround) { + zzinc_(Zconcaveridges); + qh_precision("concave ridge"); + qh_fprintf(qh ferr, 6117, "qhull precision error: f%d is concave to f%d. Centrum of f%d is %6.4g above f%d\n", + facet->id, neighbor->id, facet->id, dist, neighbor->id); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + }else if (dist >= 0.0) { /* if arithmetic always rounds the same, + can test against centrum radius instead */ + zzinc_(Zcoplanarridges); + qh_precision("coplanar ridge"); + qh_fprintf(qh ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d. Centrum of f%d is %6.4g above f%d\n", + facet->id, neighbor->id, facet->id, dist, neighbor->id); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + } + } + if (tempcentrum) + qh_memfree(centrum, qh normal_size); + } + } + if (waserror && !qh FORCEoutput) + qh_errexit2 (qh_ERRprec, errfacet1, errfacet2); +} /* checkconvex */ + + +/*--------------------------------- + + qh_checkfacet( facet, newmerge, waserror ) + checks for consistency errors in facet + newmerge set if from merge.c + + returns: + sets waserror if any error occurs + + checks: + vertex ids are inverse sorted + unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial) + if non-simplicial, at least as many ridges as neighbors + neighbors are not duplicated + ridges are not duplicated + in 3-d, ridges=verticies + (qh.hull_dim-1) ridge vertices + neighbors are reciprocated + ridge neighbors are facet neighbors and a ridge for every neighbor + simplicial neighbors match facetintersect + vertex intersection matches vertices of common ridges + vertex neighbors and facet vertices agree + all ridges have distinct vertex sets + + notes: + uses neighbor->seen + + design: + check sets + check vertices + check sizes of neighbors and vertices + check for qh_MERGEridge and qh_DUPLICATEridge flags + check neighbor set + check ridge set + check ridges, neighbors, and vertices +*/ +void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { + facetT *neighbor, **neighborp, *errother=NULL; + ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2; + vertexT *vertex, **vertexp; + unsigned previousid= INT_MAX; + int numneighbors, numvertices, numridges=0, numRvertices=0; + boolT waserror= False; + int skipA, skipB, ridge_i, ridge_n, i; + setT *intersection; + + if (facet->visible) { + qh_fprintf(qh ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n", + facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (!facet->normal) { + qh_fprintf(qh ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n", + facet->id); + waserror= True; + } + qh_setcheck(facet->vertices, "vertices for f", facet->id); + qh_setcheck(facet->ridges, "ridges for f", facet->id); + qh_setcheck(facet->outsideset, "outsideset for f", facet->id); + qh_setcheck(facet->coplanarset, "coplanarset for f", facet->id); + qh_setcheck(facet->neighbors, "neighbors for f", facet->id); + FOREACHvertex_(facet->vertices) { + if (vertex->deleted) { + qh_fprintf(qh ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id); + qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex); + waserror= True; + } + if (vertex->id >= previousid) { + qh_fprintf(qh ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id); + waserror= True; + break; + } + previousid= vertex->id; + } + numneighbors= qh_setsize(facet->neighbors); + numvertices= qh_setsize(facet->vertices); + numridges= qh_setsize(facet->ridges); + if (facet->simplicial) { + if (numvertices+numneighbors != 2*qh hull_dim + && !facet->degenerate && !facet->redundant) { + qh_fprintf(qh ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh hull_dim\n", + facet->id, numvertices, numneighbors); + qh_setprint(qh ferr, "", facet->neighbors); + waserror= True; + } + }else { /* non-simplicial */ + if (!newmerge + &&(numvertices < qh hull_dim || numneighbors < qh hull_dim) + && !facet->degenerate && !facet->redundant) { + qh_fprintf(qh ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh hull_dim\n", + facet->id, numvertices, numneighbors); + waserror= True; + } + /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */ + if (numridges < numneighbors + ||(qh hull_dim == 3 && numvertices > numridges && !qh NEWfacets) + ||(qh hull_dim == 2 && numridges + numvertices + numneighbors != 6)) { + if (!facet->degenerate && !facet->redundant) { + qh_fprintf(qh ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n", + facet->id, numridges, numneighbors, numvertices); + waserror= True; + } + } + } + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) { + qh_fprintf(qh ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + neighbor->seen= True; + } + FOREACHneighbor_(facet) { + if (!qh_setin(neighbor->neighbors, facet)) { + qh_fprintf(qh ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n", + facet->id, neighbor->id, neighbor->id, facet->id); + errother= neighbor; + waserror= True; + } + if (!neighbor->seen) { + qh_fprintf(qh ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n", + facet->id, neighbor->id); + errother= neighbor; + waserror= True; + } + neighbor->seen= False; + } + FOREACHridge_(facet->ridges) { + qh_setcheck(ridge->vertices, "vertices for r", ridge->id); + ridge->seen= False; + } + FOREACHridge_(facet->ridges) { + if (ridge->seen) { + qh_fprintf(qh ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n", + facet->id, ridge->id); + errridge= ridge; + waserror= True; + } + ridge->seen= True; + numRvertices= qh_setsize(ridge->vertices); + if (numRvertices != qh hull_dim - 1) { + qh_fprintf(qh ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n", + ridge->top->id, ridge->bottom->id, numRvertices); + errridge= ridge; + waserror= True; + } + neighbor= otherfacet_(ridge, facet); + neighbor->seen= True; + if (!qh_setin(facet->neighbors, neighbor)) { + qh_fprintf(qh ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n", + facet->id, neighbor->id, ridge->id); + errridge= ridge; + waserror= True; + } + } + if (!facet->simplicial) { + FOREACHneighbor_(facet) { + if (!neighbor->seen) { + qh_fprintf(qh ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n", + facet->id, neighbor->id); + errother= neighbor; + waserror= True; + } + intersection= qh_vertexintersect_new(facet->vertices, neighbor->vertices); + qh_settemppush(intersection); + FOREACHvertex_(facet->vertices) { + vertex->seen= False; + vertex->seen2= False; + } + FOREACHvertex_(intersection) + vertex->seen= True; + FOREACHridge_(facet->ridges) { + if (neighbor != otherfacet_(ridge, facet)) + continue; + FOREACHvertex_(ridge->vertices) { + if (!vertex->seen) { + qh_fprintf(qh ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n", + vertex->id, ridge->id, facet->id, neighbor->id); + qh_errexit(qh_ERRqhull, facet, ridge); + } + vertex->seen2= True; + } + } + if (!newmerge) { + FOREACHvertex_(intersection) { + if (!vertex->seen2) { + if (qh IStracing >=3 || !qh MERGING) { + qh_fprintf(qh ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\ + not in a ridge. This is ok under merging. Last point was p%d\n", + vertex->id, facet->id, neighbor->id, qh furthest_id); + if (!qh FORCEoutput && !qh MERGING) { + qh_errprint("ERRONEOUS", facet, neighbor, NULL, vertex); + if (!qh MERGING) + qh_errexit(qh_ERRqhull, NULL, NULL); + } + } + } + } + } + qh_settempfree(&intersection); + } + }else { /* simplicial */ + FOREACHneighbor_(facet) { + if (neighbor->simplicial) { + skipA= SETindex_(facet->neighbors, neighbor); + skipB= qh_setindex(neighbor->neighbors, facet); + if (!qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) { + qh_fprintf(qh ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n", + facet->id, skipA, neighbor->id, skipB); + errother= neighbor; + waserror= True; + } + } + } + } + if (qh hull_dim < 5 && (qh IStracing > 2 || qh CHECKfrequently)) { + FOREACHridge_i_(facet->ridges) { /* expensive */ + for (i=ridge_i+1; i < ridge_n; i++) { + ridge2= SETelemt_(facet->ridges, i, ridgeT); + if (qh_setequal(ridge->vertices, ridge2->vertices)) { + qh_fprintf(qh ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n", + ridge->id, ridge2->id); + errridge= ridge; + waserror= True; + } + } + } + } + if (waserror) { + qh_errprint("ERRONEOUS", facet, errother, errridge, NULL); + *waserrorp= True; + } +} /* checkfacet */ + + +/*--------------------------------- + + qh_checkflipped_all( facetlist ) + checks orientation of facets in list against interior point +*/ +void qh_checkflipped_all(facetT *facetlist) { + facetT *facet; + boolT waserror= False; + realT dist; + + if (facetlist == qh facet_list) + zzval_(Zflippedfacets)= 0; + FORALLfacet_(facetlist) { + if (facet->normal && !qh_checkflipped(facet, &dist, !qh_ALL)) { + qh_fprintf(qh ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n", + facet->id, dist); + if (!qh FORCEoutput) { + qh_errprint("ERRONEOUS", facet, NULL, NULL, NULL); + waserror= True; + } + } + } + if (waserror) { + qh_fprintf(qh ferr, 8101, "\n\ +A flipped facet occurs when its distance to the interior point is\n\ +greater than %2.2g, the maximum roundoff error.\n", -qh DISTround); + qh_errexit(qh_ERRprec, NULL, NULL); + } +} /* checkflipped_all */ + +/*--------------------------------- + + qh_checkpolygon( facetlist ) + checks the correctness of the structure + + notes: + call with either qh.facet_list or qh.newfacet_list + checks num_facets and num_vertices if qh.facet_list + + design: + for each facet + checks facet and outside set + initializes vertexlist + for each facet + checks vertex set + if checking all facets(qh.facetlist) + check facet count + if qh.VERTEXneighbors + check vertex neighbors and count + check vertex count +*/ +void qh_checkpolygon(facetT *facetlist) { + facetT *facet; + vertexT *vertex, **vertexp, *vertexlist; + int numfacets= 0, numvertices= 0, numridges= 0; + int totvneighbors= 0, totvertices= 0; + boolT waserror= False, nextseen= False, visibleseen= False; + + trace1((qh ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id)); + if (facetlist != qh facet_list || qh ONLYgood) + nextseen= True; + FORALLfacet_(facetlist) { + if (facet == qh visible_list) + visibleseen= True; + if (!facet->visible) { + if (!nextseen) { + if (facet == qh facet_next) + nextseen= True; + else if (qh_setsize(facet->outsideset)) { + if (!qh NARROWhull +#if !qh_COMPUTEfurthest + || facet->furthestdist >= qh MINoutside +#endif + ) { + qh_fprintf(qh ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh facet_next\n", + facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + } + } + numfacets++; + qh_checkfacet(facet, False, &waserror); + } + } + if (qh visible_list && !visibleseen && facetlist == qh facet_list) { + qh_fprintf(qh ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh visible_list->id); + qh_printlists(); + qh_errexit(qh_ERRqhull, qh visible_list, NULL); + } + if (facetlist == qh facet_list) + vertexlist= qh vertex_list; + else if (facetlist == qh newfacet_list) + vertexlist= qh newvertex_list; + else + vertexlist= NULL; + FORALLvertex_(vertexlist) { + vertex->seen= False; + vertex->visitid= 0; + } + FORALLfacet_(facetlist) { + if (facet->visible) + continue; + if (facet->simplicial) + numridges += qh hull_dim; + else + numridges += qh_setsize(facet->ridges); + FOREACHvertex_(facet->vertices) { + vertex->visitid++; + if (!vertex->seen) { + vertex->seen= True; + numvertices++; + if (qh_pointid(vertex->point) == -1) { + qh_fprintf(qh ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n", + vertex->point, vertex->id, qh first_point); + waserror= True; + } + } + } + } + qh vertex_visit += (unsigned int)numfacets; + if (facetlist == qh facet_list) { + if (numfacets != qh num_facets - qh num_visible) { + qh_fprintf(qh ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n", + numfacets, qh num_facets, qh num_visible); + waserror= True; + } + qh vertex_visit++; + if (qh VERTEXneighbors) { + FORALLvertices { + qh_setcheck(vertex->neighbors, "neighbors for v", vertex->id); + if (vertex->deleted) + continue; + totvneighbors += qh_setsize(vertex->neighbors); + } + FORALLfacet_(facetlist) + totvertices += qh_setsize(facet->vertices); + if (totvneighbors != totvertices) { + qh_fprintf(qh ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent. Totvneighbors %d, totvertices %d\n", + totvneighbors, totvertices); + waserror= True; + } + } + if (numvertices != qh num_vertices - qh_setsize(qh del_vertices)) { + qh_fprintf(qh ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n", + numvertices, qh num_vertices - qh_setsize(qh del_vertices)); + waserror= True; + } + if (qh hull_dim == 2 && numvertices != numfacets) { + qh_fprintf(qh ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n", + numvertices, numfacets); + waserror= True; + } + if (qh hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) { + qh_fprintf(qh ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\ + A vertex appears twice in a edge list. May occur during merging.", + numvertices, numfacets, numridges/2); + /* occurs if lots of merging and a vertex ends up twice in an edge list. e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */ + } + } + if (waserror) + qh_errexit(qh_ERRqhull, NULL, NULL); +} /* checkpolygon */ + + +/*--------------------------------- + + qh_checkvertex( vertex ) + check vertex for consistency + checks vertex->neighbors + + notes: + neighbors checked efficiently in checkpolygon +*/ +void qh_checkvertex(vertexT *vertex) { + boolT waserror= False; + facetT *neighbor, **neighborp, *errfacet=NULL; + + if (qh_pointid(vertex->point) == -1) { + qh_fprintf(qh ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point); + waserror= True; + } + if (vertex->id >= qh vertex_id) { + qh_fprintf(qh ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id); + waserror= True; + } + if (!waserror && !vertex->deleted) { + if (qh_setsize(vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (!qh_setin(neighbor->vertices, vertex)) { + qh_fprintf(qh ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id); + errfacet= neighbor; + waserror= True; + } + } + } + } + if (waserror) { + qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex); + qh_errexit(qh_ERRqhull, errfacet, NULL); + } +} /* checkvertex */ + +/*--------------------------------- + + qh_clearcenters( type ) + clear old data from facet->center + + notes: + sets new centertype + nop if CENTERtype is the same +*/ +void qh_clearcenters(qh_CENTER type) { + facetT *facet; + + if (qh CENTERtype != type) { + FORALLfacets { + if (facet->tricoplanar && !facet->keepcentrum) + facet->center= NULL; + else if (qh CENTERtype == qh_ASvoronoi){ + if (facet->center) { + qh_memfree(facet->center, qh center_size); + facet->center= NULL; + } + }else /* qh CENTERtype == qh_AScentrum */ { + if (facet->center) { + qh_memfree(facet->center, qh normal_size); + facet->center= NULL; + } + } + } + qh CENTERtype= type; + } + trace2((qh ferr, 2043, "qh_clearcenters: switched to center type %d\n", type)); +} /* clearcenters */ + +/*--------------------------------- + + qh_createsimplex( vertices ) + creates a simplex from a set of vertices + + returns: + initializes qh.facet_list to the simplex + initializes qh.newfacet_list, .facet_tail + initializes qh.vertex_list, .newvertex_list, .vertex_tail + + design: + initializes lists + for each vertex + create a new facet + for each new facet + create its neighbor set +*/ +void qh_createsimplex(setT *vertices) { + facetT *facet= NULL, *newfacet; + boolT toporient= True; + int vertex_i, vertex_n, nth; + setT *newfacets= qh_settemp(qh hull_dim+1); + vertexT *vertex; + + qh facet_list= qh newfacet_list= qh facet_tail= qh_newfacet(); + qh num_facets= qh num_vertices= qh num_visible= 0; + qh vertex_list= qh newvertex_list= qh vertex_tail= qh_newvertex(NULL); + FOREACHvertex_i_(vertices) { + newfacet= qh_newfacet(); + newfacet->vertices= qh_setnew_delnthsorted(vertices, vertex_n, + vertex_i, 0); + newfacet->toporient= (unsigned char)toporient; + qh_appendfacet(newfacet); + newfacet->newfacet= True; + qh_appendvertex(vertex); + qh_setappend(&newfacets, newfacet); + toporient ^= True; + } + FORALLnew_facets { + nth= 0; + FORALLfacet_(qh newfacet_list) { + if (facet != newfacet) + SETelem_(newfacet->neighbors, nth++)= facet; + } + qh_settruncate(newfacet->neighbors, qh hull_dim); + } + qh_settempfree(&newfacets); + trace1((qh ferr, 1028, "qh_createsimplex: created simplex\n")); +} /* createsimplex */ + +/*--------------------------------- + + qh_delridge( ridge ) + deletes ridge from data structures it belongs to + frees up its memory + + notes: + in merge.c, caller sets vertex->delridge for each vertex + ridges also freed in qh_freeqhull +*/ +void qh_delridge(ridgeT *ridge) { + void **freelistp; /* used !qh_NOmem */ + + qh_setdel(ridge->top->ridges, ridge); + qh_setdel(ridge->bottom->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); +} /* delridge */ + + +/*--------------------------------- + + qh_delvertex( vertex ) + deletes a vertex and frees its memory + + notes: + assumes vertex->adjacencies have been updated if needed + unlinks from vertex_list +*/ +void qh_delvertex(vertexT *vertex) { + + if (vertex == qh tracevertex) + qh tracevertex= NULL; + qh_removevertex(vertex); + qh_setfree(&vertex->neighbors); + qh_memfree(vertex, (int)sizeof(vertexT)); +} /* delvertex */ + + +/*--------------------------------- + + qh_facet3vertex( ) + return temporary set of 3-d vertices in qh_ORIENTclock order + + design: + if simplicial facet + build set from facet->vertices with facet->toporient + else + for each ridge in order + build set from ridge's vertices +*/ +setT *qh_facet3vertex(facetT *facet) { + ridgeT *ridge, *firstridge; + vertexT *vertex; + int cntvertices, cntprojected=0; + setT *vertices; + + cntvertices= qh_setsize(facet->vertices); + vertices= qh_settemp(cntvertices); + if (facet->simplicial) { + if (cntvertices != 3) { + qh_fprintf(qh ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n", + cntvertices, facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + qh_setappend(&vertices, SETfirst_(facet->vertices)); + if (facet->toporient ^ qh_ORIENTclock) + qh_setappend(&vertices, SETsecond_(facet->vertices)); + else + qh_setaddnth(&vertices, 0, SETsecond_(facet->vertices)); + qh_setappend(&vertices, SETelem_(facet->vertices, 2)); + }else { + ridge= firstridge= SETfirstt_(facet->ridges, ridgeT); /* no infinite */ + while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) { + qh_setappend(&vertices, vertex); + if (++cntprojected > cntvertices || ridge == firstridge) + break; + } + if (!ridge || cntprojected != cntvertices) { + qh_fprintf(qh ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up. got at least %d\n", + facet->id, cntprojected); + qh_errexit(qh_ERRqhull, facet, ridge); + } + } + return vertices; +} /* facet3vertex */ + +/*--------------------------------- + + qh_findbestfacet( point, bestoutside, bestdist, isoutside ) + find facet that is furthest below a point + + for Delaunay triangulations, + Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates. + + returns: + if bestoutside is set (e.g., qh_ALL) + returns best facet that is not upperdelaunay + if Delaunay and inside, point is outside circumsphere of bestfacet + else + returns first facet below point + if point is inside, returns nearest, !upperdelaunay facet + distance to facet + isoutside set if outside of facet + + notes: + For tricoplanar facets, this finds one of the tricoplanar facets closest + to the point. For Delaunay triangulations, the point may be inside a + different tricoplanar facet. See locate a facet with qh_findbestfacet() + + If inside, qh_findbestfacet performs an exhaustive search + this may be too conservative. Sometimes it is clearly required. + + qh_findbestfacet is not used by qhull. + uses qh.visit_id and qh.coplanarset + + see: + qh_findbest +*/ +facetT *qh_findbestfacet(pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside) { + facetT *bestfacet= NULL; + int numpart, totpart= 0; + + bestfacet= qh_findbest(point, qh facet_list, + bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */, + bestdist, isoutside, &totpart); + if (*bestdist < -qh DISTround) { + bestfacet= qh_findfacet_all(point, bestdist, isoutside, &numpart); + totpart += numpart; + if ((isoutside && bestoutside) + || (!isoutside && bestfacet->upperdelaunay)) { + bestfacet= qh_findbest(point, bestfacet, + bestoutside, False, bestoutside, + bestdist, isoutside, &totpart); + totpart += numpart; + } + } + trace3((qh ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n", + bestfacet->id, *bestdist, *isoutside, totpart)); + return bestfacet; +} /* findbestfacet */ + +/*--------------------------------- + + qh_findbestlower( facet, point, bestdist, numpart ) + returns best non-upper, non-flipped neighbor of facet for point + if needed, searches vertex neighbors + + returns: + returns bestdist and updates numpart + + notes: + if Delaunay and inside, point is outside of circumsphere of bestfacet + called by qh_findbest() for points above an upperdelaunay facet + +*/ +facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) { + facetT *neighbor, **neighborp, *bestfacet= NULL; + realT bestdist= -REALmax/2 /* avoid underflow */; + realT dist; + vertexT *vertex; + + zinc_(Zbestlower); + FOREACHneighbor_(upperfacet) { + if (neighbor->upperdelaunay || neighbor->flipped) + continue; + (*numpart)++; + qh_distplane(point, neighbor, &dist); + if (dist > bestdist) { + bestfacet= neighbor; + bestdist= dist; + } + } + if (!bestfacet) { + zinc_(Zbestlowerv); + /* rarely called, numpart does not count nearvertex computations */ + vertex= qh_nearvertex(upperfacet, point, &dist); + qh_vertexneighbors(); + FOREACHneighbor_(vertex) { + if (neighbor->upperdelaunay || neighbor->flipped) + continue; + (*numpart)++; + qh_distplane(point, neighbor, &dist); + if (dist > bestdist) { + bestfacet= neighbor; + bestdist= dist; + } + } + } + if (!bestfacet) { + qh_fprintf(qh ferr, 6228, "\n\ +Qhull internal error (qh_findbestlower): all neighbors of facet %d are flipped or upper Delaunay.\n\ +Please report this error to qhull_bug@qhull.org with the input and all of the output.\n", + upperfacet->id); + qh_errexit(qh_ERRqhull, upperfacet, NULL); + } + *bestdistp= bestdist; + trace3((qh ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n", + bestfacet->id, bestdist, upperfacet->id, qh_pointid(point))); + return bestfacet; +} /* findbestlower */ + +/*--------------------------------- + + qh_findfacet_all( point, bestdist, isoutside, numpart ) + exhaustive search for facet below a point + + for Delaunay triangulations, + Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates. + + returns: + returns first facet below point + if point is inside, + returns nearest facet + distance to facet + isoutside if point is outside of the hull + number of distance tests + + notes: + for library users, not used by Qhull +*/ +facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, + int *numpart) { + facetT *bestfacet= NULL, *facet; + realT dist; + int totpart= 0; + + *bestdist= -REALmax; + *isoutside= False; + FORALLfacets { + if (facet->flipped || !facet->normal) + continue; + totpart++; + qh_distplane(point, facet, &dist); + if (dist > *bestdist) { + *bestdist= dist; + bestfacet= facet; + if (dist > qh MINoutside) { + *isoutside= True; + break; + } + } + } + *numpart= totpart; + trace3((qh ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n", + getid_(bestfacet), *bestdist, *isoutside, totpart)); + return bestfacet; +} /* findfacet_all */ + +/*--------------------------------- + + qh_findgood( facetlist, goodhorizon ) + identify good facets for qh.PRINTgood + if qh.GOODvertex>0 + facet includes point as vertex + if !match, returns goodhorizon + inactive if qh.MERGING + if qh.GOODpoint + facet is visible or coplanar (>0) or not visible (<0) + if qh.GOODthreshold + facet->normal matches threshold + if !goodhorizon and !match, + selects facet with closest angle + sets GOODclosest + + returns: + number of new, good facets found + determines facet->good + may update qh.GOODclosest + + notes: + qh_findgood_all further reduces the good region + + design: + count good facets + mark good facets for qh.GOODpoint + mark good facets for qh.GOODthreshold + if necessary + update qh.GOODclosest +*/ +int qh_findgood(facetT *facetlist, int goodhorizon) { + facetT *facet, *bestfacet= NULL; + realT angle, bestangle= REALmax, dist; + int numgood=0; + + FORALLfacet_(facetlist) { + if (facet->good) + numgood++; + } + if (qh GOODvertex>0 && !qh MERGING) { + FORALLfacet_(facetlist) { + if (!qh_isvertex(qh GOODvertexp, facet->vertices)) { + facet->good= False; + numgood--; + } + } + } + if (qh GOODpoint && numgood) { + FORALLfacet_(facetlist) { + if (facet->good && facet->normal) { + zinc_(Zdistgood); + qh_distplane(qh GOODpointp, facet, &dist); + if ((qh GOODpoint > 0) ^ (dist > 0.0)) { + facet->good= False; + numgood--; + } + } + } + } + if (qh GOODthreshold && (numgood || goodhorizon || qh GOODclosest)) { + FORALLfacet_(facetlist) { + if (facet->good && facet->normal) { + if (!qh_inthresholds(facet->normal, &angle)) { + facet->good= False; + numgood--; + if (angle < bestangle) { + bestangle= angle; + bestfacet= facet; + } + } + } + } + if (!numgood && (!goodhorizon || qh GOODclosest)) { + if (qh GOODclosest) { + if (qh GOODclosest->visible) + qh GOODclosest= NULL; + else { + qh_inthresholds(qh GOODclosest->normal, &angle); + if (angle < bestangle) + bestfacet= qh GOODclosest; + } + } + if (bestfacet && bestfacet != qh GOODclosest) { + if (qh GOODclosest) + qh GOODclosest->good= False; + qh GOODclosest= bestfacet; + bestfacet->good= True; + numgood++; + trace2((qh ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n", + bestfacet->id, bestangle)); + return numgood; + } + }else if (qh GOODclosest) { /* numgood > 0 */ + qh GOODclosest->good= False; + qh GOODclosest= NULL; + } + } + zadd_(Zgoodfacet, numgood); + trace2((qh ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n", + numgood, goodhorizon)); + if (!numgood && qh GOODvertex>0 && !qh MERGING) + return goodhorizon; + return numgood; +} /* findgood */ + +/*--------------------------------- + + qh_findgood_all( facetlist ) + apply other constraints for good facets (used by qh.PRINTgood) + if qh.GOODvertex + facet includes (>0) or doesn't include (<0) point as vertex + if last good facet and ONLYgood, prints warning and continues + if qh.SPLITthresholds + facet->normal matches threshold, or if none, the closest one + calls qh_findgood + nop if good not used + + returns: + clears facet->good if not good + sets qh.num_good + + notes: + this is like qh_findgood but more restrictive + + design: + uses qh_findgood to mark good facets + marks facets for qh.GOODvertex + marks facets for qh.SPLITthreholds +*/ +void qh_findgood_all(facetT *facetlist) { + facetT *facet, *bestfacet=NULL; + realT angle, bestangle= REALmax; + int numgood=0, startgood; + + if (!qh GOODvertex && !qh GOODthreshold && !qh GOODpoint + && !qh SPLITthresholds) + return; + if (!qh ONLYgood) + qh_findgood(qh facet_list, 0); + FORALLfacet_(facetlist) { + if (facet->good) + numgood++; + } + if (qh GOODvertex <0 || (qh GOODvertex > 0 && qh MERGING)) { + FORALLfacet_(facetlist) { + if (facet->good && ((qh GOODvertex > 0) ^ !!qh_isvertex(qh GOODvertexp, facet->vertices))) { + if (!--numgood) { + if (qh ONLYgood) { + qh_fprintf(qh ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n", + qh_pointid(qh GOODvertexp), facet->id); + return; + }else if (qh GOODvertex > 0) + qh_fprintf(qh ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n", + qh GOODvertex-1, qh GOODvertex-1); + else + qh_fprintf(qh ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n", + -qh GOODvertex - 1, -qh GOODvertex - 1); + } + facet->good= False; + } + } + } + startgood= numgood; + if (qh SPLITthresholds) { + FORALLfacet_(facetlist) { + if (facet->good) { + if (!qh_inthresholds(facet->normal, &angle)) { + facet->good= False; + numgood--; + if (angle < bestangle) { + bestangle= angle; + bestfacet= facet; + } + } + } + } + if (!numgood && bestfacet) { + bestfacet->good= True; + numgood++; + trace0((qh ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n", + bestfacet->id, bestangle)); + return; + } + } + qh num_good= numgood; + trace0((qh ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n", + numgood, startgood)); +} /* findgood_all */ + +/*--------------------------------- + + qh_furthestnext() + set qh.facet_next to facet with furthest of all furthest points + searches all facets on qh.facet_list + + notes: + this may help avoid precision problems +*/ +void qh_furthestnext(void /* qh facet_list */) { + facetT *facet, *bestfacet= NULL; + realT dist, bestdist= -REALmax; + + FORALLfacets { + if (facet->outsideset) { +#if qh_COMPUTEfurthest + pointT *furthest; + furthest= (pointT*)qh_setlast(facet->outsideset); + zinc_(Zcomputefurthest); + qh_distplane(furthest, facet, &dist); +#else + dist= facet->furthestdist; +#endif + if (dist > bestdist) { + bestfacet= facet; + bestdist= dist; + } + } + } + if (bestfacet) { + qh_removefacet(bestfacet); + qh_prependfacet(bestfacet, &qh facet_next); + trace1((qh ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n", + bestfacet->id, bestdist)); + } +} /* furthestnext */ + +/*--------------------------------- + + qh_furthestout( facet ) + make furthest outside point the last point of outsideset + + returns: + updates facet->outsideset + clears facet->notfurthest + sets facet->furthestdist + + design: + determine best point of outsideset + make it the last point of outsideset +*/ +void qh_furthestout(facetT *facet) { + pointT *point, **pointp, *bestpoint= NULL; + realT dist, bestdist= -REALmax; + + FOREACHpoint_(facet->outsideset) { + qh_distplane(point, facet, &dist); + zinc_(Zcomputefurthest); + if (dist > bestdist) { + bestpoint= point; + bestdist= dist; + } + } + if (bestpoint) { + qh_setdel(facet->outsideset, point); + qh_setappend(&facet->outsideset, point); +#if !qh_COMPUTEfurthest + facet->furthestdist= bestdist; +#endif + } + facet->notfurthest= False; + trace3((qh ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n", + qh_pointid(point), facet->id)); +} /* furthestout */ + + +/*--------------------------------- + + qh_infiniteloop( facet ) + report infinite loop error due to facet +*/ +void qh_infiniteloop(facetT *facet) { + + qh_fprintf(qh ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n"); + qh_errexit(qh_ERRqhull, facet, NULL); +} /* qh_infiniteloop */ + +/*--------------------------------- + + qh_initbuild() + initialize hull and outside sets with point array + qh.FIRSTpoint/qh.NUMpoints is point array + if qh.GOODpoint + adds qh.GOODpoint to initial hull + + returns: + qh_facetlist with initial hull + points partioned into outside sets, coplanar sets, or inside + initializes qh.GOODpointp, qh.GOODvertexp, + + design: + initialize global variables used during qh_buildhull + determine precision constants and points with max/min coordinate values + if qh.SCALElast, scale last coordinate(for 'd') + build initial simplex + partition input points into facets of initial simplex + set up lists + if qh.ONLYgood + check consistency + add qh.GOODvertex if defined +*/ +void qh_initbuild( void) { + setT *maxpoints, *vertices; + facetT *facet; + int i, numpart; + realT dist; + boolT isoutside; + + qh furthest_id= -1; + qh lastreport= 0; + qh facet_id= qh vertex_id= qh ridge_id= 0; + qh visit_id= qh vertex_visit= 0; + qh maxoutdone= False; + + if (qh GOODpoint > 0) + qh GOODpointp= qh_point(qh GOODpoint-1); + else if (qh GOODpoint < 0) + qh GOODpointp= qh_point(-qh GOODpoint-1); + if (qh GOODvertex > 0) + qh GOODvertexp= qh_point(qh GOODvertex-1); + else if (qh GOODvertex < 0) + qh GOODvertexp= qh_point(-qh GOODvertex-1); + if ((qh GOODpoint + && (qh GOODpointp < qh first_point /* also catches !GOODpointp */ + || qh GOODpointp > qh_point(qh num_points-1))) + || (qh GOODvertex + && (qh GOODvertexp < qh first_point /* also catches !GOODvertexp */ + || qh GOODvertexp > qh_point(qh num_points-1)))) { + qh_fprintf(qh ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n", + qh num_points-1); + qh_errexit(qh_ERRinput, NULL, NULL); + } + maxpoints= qh_maxmin(qh first_point, qh num_points, qh hull_dim); + if (qh SCALElast) + qh_scalelast(qh first_point, qh num_points, qh hull_dim, + qh MINlastcoord, qh MAXlastcoord, qh MAXwidth); + qh_detroundoff(); + if (qh DELAUNAY && qh upper_threshold[qh hull_dim-1] > REALmax/2 + && qh lower_threshold[qh hull_dim-1] < -REALmax/2) { + for (i=qh_PRINTEND; i--; ) { + if (qh PRINTout[i] == qh_PRINTgeom && qh DROPdim < 0 + && !qh GOODthreshold && !qh SPLITthresholds) + break; /* in this case, don't set upper_threshold */ + } + if (i < 0) { + if (qh UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */ + qh lower_threshold[qh hull_dim-1]= qh ANGLEround * qh_ZEROdelaunay; + qh GOODthreshold= True; + }else { + qh upper_threshold[qh hull_dim-1]= -qh ANGLEround * qh_ZEROdelaunay; + if (!qh GOODthreshold) + qh SPLITthresholds= True; /* build upper-convex hull even if Qg */ + /* qh_initqhull_globals errors if Qg without Pdk/etc. */ + } + } + } + vertices= qh_initialvertices(qh hull_dim, maxpoints, qh first_point, qh num_points); + qh_initialhull(vertices); /* initial qh facet_list */ + qh_partitionall(vertices, qh first_point, qh num_points); + if (qh PRINToptions1st || qh TRACElevel || qh IStracing) { + if (qh TRACElevel || qh IStracing) + qh_fprintf(qh ferr, 8103, "\nTrace level %d for %s | %s\n", + qh IStracing ? qh IStracing : qh TRACElevel, qh rbox_command, qh qhull_command); + qh_fprintf(qh ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options); + } + qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */); + qh facet_next= qh facet_list; + qh_furthestnext(/* qh facet_list */); + if (qh PREmerge) { + qh cos_max= qh premerge_cos; + qh centrum_radius= qh premerge_centrum; + } + if (qh ONLYgood) { + if (qh GOODvertex > 0 && qh MERGING) { + qh_fprintf(qh ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (!(qh GOODthreshold || qh GOODpoint + || (!qh MERGEexact && !qh PREmerge && qh GOODvertexp))) { + qh_fprintf(qh ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\ +good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh GOODvertex > 0 && !qh MERGING /* matches qh_partitionall */ + && !qh_isvertex(qh GOODvertexp, vertices)) { + facet= qh_findbestnew(qh GOODvertexp, qh facet_list, + &dist, !qh_ALL, &isoutside, &numpart); + zadd_(Zdistgood, numpart); + if (!isoutside) { + qh_fprintf(qh ferr, 6153, "qhull input error: point for QV%d is inside initial simplex. It can not be made a vertex.\n", + qh_pointid(qh GOODvertexp)); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (!qh_addpoint(qh GOODvertexp, facet, False)) { + qh_settempfree(&vertices); + qh_settempfree(&maxpoints); + return; + } + } + qh_findgood(qh facet_list, 0); + } + qh_settempfree(&vertices); + qh_settempfree(&maxpoints); + trace1((qh ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n")); +} /* initbuild */ + +/*--------------------------------- + + qh_initialhull( vertices ) + constructs the initial hull as a DIM3 simplex of vertices + + design: + creates a simplex (initializes lists) + determines orientation of simplex + sets hyperplanes for facets + doubles checks orientation (in case of axis-parallel facets with Gaussian elimination) + checks for flipped facets and qh.NARROWhull + checks the result +*/ +void qh_initialhull(setT *vertices) { + facetT *facet, *firstfacet, *neighbor, **neighborp; + realT dist, angle, minangle= REALmax; +#ifndef qh_NOtrace + int k; +#endif + + qh_createsimplex(vertices); /* qh facet_list */ + qh_resetlists(False, qh_RESETvisible); + qh facet_next= qh facet_list; /* advance facet when processed */ + qh interior_point= qh_getcenter(vertices); + firstfacet= qh facet_list; + qh_setfacetplane(firstfacet); + zinc_(Znumvisibility); /* needs to be in printsummary */ + qh_distplane(qh interior_point, firstfacet, &dist); + if (dist > 0) { + FORALLfacets + facet->toporient ^= (unsigned char)True; + } + FORALLfacets + qh_setfacetplane(facet); + FORALLfacets { + if (!qh_checkflipped(facet, NULL, qh_ALL)) {/* due to axis-parallel facet */ + trace1((qh ferr, 1031, "qh_initialhull: initial orientation incorrect. Correct all facets\n")); + facet->flipped= False; + FORALLfacets { + facet->toporient ^= (unsigned char)True; + qh_orientoutside(facet); + } + break; + } + } + FORALLfacets { + if (!qh_checkflipped(facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */ + if (qh DELAUNAY && ! qh ATinfinity) { + if (qh UPPERdelaunay) + qh_fprintf(qh ferr, 6240, "Qhull input error: Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n"); + else + qh_fprintf(qh ferr, 6239, "Qhull input error: Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points. Option 'Qz' adds a point \"at infinity\" (above the corresponding paraboloid).\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + qh_precision("initial facet is coplanar with interior point"); + qh_fprintf(qh ferr, 6154, "qhull precision error: initial facet %d is coplanar with the interior point\n", + facet->id); + qh_errexit(qh_ERRsingular, facet, NULL); + } + FOREACHneighbor_(facet) { + angle= qh_getangle(facet->normal, neighbor->normal); + minimize_( minangle, angle); + } + } + if (minangle < qh_MAXnarrow && !qh NOnarrow) { + realT diff= 1.0 + minangle; + + qh NARROWhull= True; + qh_option("_narrow-hull", NULL, &diff); + if (minangle < qh_WARNnarrow && !qh RERUN && qh PRINTprecision) + qh_printhelp_narrowhull(qh ferr, minangle); + } + zzval_(Zprocessed)= qh hull_dim+1; + qh_checkpolygon(qh facet_list); + qh_checkconvex(qh facet_list, qh_DATAfault); +#ifndef qh_NOtrace + if (qh IStracing >= 1) { + qh_fprintf(qh ferr, 8105, "qh_initialhull: simplex constructed, interior point:"); + for (k=0; k < qh hull_dim; k++) + qh_fprintf(qh ferr, 8106, " %6.4g", qh interior_point[k]); + qh_fprintf(qh ferr, 8107, "\n"); + } +#endif +} /* initialhull */ + +/*--------------------------------- + + qh_initialvertices( dim, maxpoints, points, numpoints ) + determines a non-singular set of initial vertices + maxpoints may include duplicate points + + returns: + temporary set of dim+1 vertices in descending order by vertex id + if qh.RANDOMoutside && !qh.ALLpoints + picks random points + if dim >= qh_INITIALmax, + uses min/max x and max points with non-zero determinants + + notes: + unless qh.ALLpoints, + uses maxpoints as long as determinate is non-zero +*/ +setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints) { + pointT *point, **pointp; + setT *vertices, *simplex, *tested; + realT randr; + int idx, point_i, point_n, k; + boolT nearzero= False; + + vertices= qh_settemp(dim + 1); + simplex= qh_settemp(dim+1); + if (qh ALLpoints) + qh_maxsimplex(dim, NULL, points, numpoints, &simplex); + else if (qh RANDOMoutside) { + while (qh_setsize(simplex) != dim+1) { + randr= qh_RANDOMint; + randr= randr/(qh_RANDOMmax+1); + idx= (int)floor(qh num_points * randr); + while (qh_setin(simplex, qh_point(idx))) { + idx++; /* in case qh_RANDOMint always returns the same value */ + idx= idx < qh num_points ? idx : 0; + } + qh_setappend(&simplex, qh_point(idx)); + } + }else if (qh hull_dim >= qh_INITIALmax) { + tested= qh_settemp(dim+1); + qh_setappend(&simplex, SETfirst_(maxpoints)); /* max and min X coord */ + qh_setappend(&simplex, SETsecond_(maxpoints)); + qh_maxsimplex(fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex); + k= qh_setsize(simplex); + FOREACHpoint_i_(maxpoints) { + if (point_i & 0x1) { /* first pick up max. coord. points */ + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(&tested, point); + else { + qh_setappend(&simplex, point); + if (++k == dim) /* use search for last point */ + break; + } + } + } + } + while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) { + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(&tested, point); + else { + qh_setappend(&simplex, point); + k++; + } + } + } + idx= 0; + while (k != dim && (point= qh_point(idx++))) { + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(point, simplex, k, &nearzero); + if (!nearzero){ + qh_setappend(&simplex, point); + k++; + } + } + } + qh_settempfree(&tested); + qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex); + }else + qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex); + FOREACHpoint_(simplex) + qh_setaddnth(&vertices, 0, qh_newvertex(point)); /* descending order */ + qh_settempfree(&simplex); + return vertices; +} /* initialvertices */ + + +/*--------------------------------- + + qh_isvertex( ) + returns vertex if point is in vertex set, else returns NULL + + notes: + for qh.GOODvertex +*/ +vertexT *qh_isvertex(pointT *point, setT *vertices) { + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (vertex->point == point) + return vertex; + } + return NULL; +} /* isvertex */ + +/*--------------------------------- + + qh_makenewfacets( point ) + make new facets from point and qh.visible_list + + returns: + qh.newfacet_list= list of new facets with hyperplanes and ->newfacet + qh.newvertex_list= list of vertices in new facets with ->newlist set + + if (qh.ONLYgood) + newfacets reference horizon facets, but not vice versa + ridges reference non-simplicial horizon ridges, but not vice versa + does not change existing facets + else + sets qh.NEWfacets + new facets attached to horizon facets and ridges + for visible facets, + visible->r.replace is corresponding new facet + + see also: + qh_makenewplanes() -- make hyperplanes for facets + qh_attachnewfacets() -- attachnewfacets if not done here(qh ONLYgood) + qh_matchnewfacets() -- match up neighbors + qh_updatevertices() -- update vertex neighbors and delvertices + qh_deletevisible() -- delete visible facets + qh_checkpolygon() --check the result + qh_triangulate() -- triangulate a non-simplicial facet + + design: + for each visible facet + make new facets to its horizon facets + update its f.replace + clear its neighbor set +*/ +vertexT *qh_makenewfacets(pointT *point /*visible_list*/) { + facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp; + vertexT *apex; + int numnew=0; + + qh newfacet_list= qh facet_tail; + qh newvertex_list= qh vertex_tail; + apex= qh_newvertex(point); + qh_appendvertex(apex); + qh visit_id++; + if (!qh ONLYgood) + qh NEWfacets= True; + FORALLvisible_facets { + FOREACHneighbor_(visible) + neighbor->seen= False; + if (visible->ridges) { + visible->visitid= qh visit_id; + newfacet2= qh_makenew_nonsimplicial(visible, apex, &numnew); + } + if (visible->simplicial) + newfacet= qh_makenew_simplicial(visible, apex, &numnew); + if (!qh ONLYgood) { + if (newfacet2) /* newfacet is null if all ridges defined */ + newfacet= newfacet2; + if (newfacet) + visible->f.replace= newfacet; + else + zinc_(Zinsidevisible); + SETfirst_(visible->neighbors)= NULL; + } + } + trace1((qh ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n", + numnew, qh_pointid(point))); + if (qh IStracing >= 4) + qh_printfacetlist(qh newfacet_list, NULL, qh_ALL); + return apex; +} /* makenewfacets */ + +/*--------------------------------- + + qh_matchduplicates( atfacet, atskip, hashsize, hashcount ) + match duplicate ridges in qh.hash_table for atfacet/atskip + duplicates marked with ->dupridge and qh_DUPLICATEridge + + returns: + picks match with worst merge (min distance apart) + updates hashcount + + see also: + qh_matchneighbor + + notes: + + design: + compute hash value for atfacet and atskip + repeat twice -- once to make best matches, once to match the rest + for each possible facet in qh.hash_table + if it is a matching facet and pass 2 + make match + unless tricoplanar, mark match for merging (qh_MERGEridge) + [e.g., tricoplanar RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Qt] + if it is a matching facet and pass 1 + test if this is a better match + if pass 1, + make best match (it will not be merged) +*/ +#ifndef qh_NOmerge +void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) { + boolT same, ismatch; + int hash, scan; + facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet; + int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch; + realT maxdist= -REALmax, mindist, dist2, low, high; + + hash= qh_gethash(hashsize, atfacet->vertices, qh hull_dim, 1, + SETelem_(atfacet->vertices, atskip)); + trace2((qh ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n", + atfacet->id, atskip, hash, *hashcount)); + for (makematch= 0; makematch < 2; makematch++) { + qh visit_id++; + for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) { + zinc_(Zhashlookup); + nextfacet= NULL; + newfacet->visitid= qh visit_id; + for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT)); + scan= (++scan >= hashsize ? 0 : scan)) { + if (!facet->dupridge || facet->visitid == qh visit_id) + continue; + zinc_(Zhashtests); + if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient)); + if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) { + if (!makematch) { + qh_fprintf(qh ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n", + facet->id, skip, newfacet->id, newskip, hash); + qh_errexit2 (qh_ERRqhull, facet, newfacet); + } + }else if (ismatch && makematch) { + if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) { + SETelem_(facet->neighbors, skip)= newfacet; + if (newfacet->tricoplanar) + SETelem_(newfacet->neighbors, newskip)= facet; + else + SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge; + *hashcount -= 2; /* removed two unmatched facets */ + trace4((qh ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n", + facet->id, skip, newfacet->id, newskip)); + } + }else if (ismatch) { + mindist= qh_getdistance(facet, newfacet, &low, &high); + dist2= qh_getdistance(newfacet, facet, &low, &high); + minimize_(mindist, dist2); + if (mindist > maxdist) { + maxdist= mindist; + maxmatch= facet; + maxskip= skip; + maxmatch2= newfacet; + maxskip2= newskip; + } + trace3((qh ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n", + facet->id, skip, newfacet->id, newskip, mindist, + maxmatch->id, maxmatch2->id)); + }else { /* !ismatch */ + nextfacet= facet; + nextskip= skip; + } + } + if (makematch && !facet + && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) { + qh_fprintf(qh ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n", + newfacet->id, newskip, hash); + qh_errexit(qh_ERRqhull, newfacet, NULL); + } + } + } /* end of for each new facet at hash */ + if (!makematch) { + if (!maxmatch) { + qh_fprintf(qh ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n", + atfacet->id, atskip, hash); + qh_errexit(qh_ERRqhull, atfacet, NULL); + } + SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; + SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch; + *hashcount -= 2; /* removed two unmatched facets */ + zzinc_(Zmultiridge); + trace0((qh ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n", + maxmatch->id, maxskip, maxmatch2->id, maxskip2)); + qh_precision("ridge with multiple neighbors"); + if (qh IStracing >= 4) + qh_errprint("DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL); + } + } +} /* matchduplicates */ + +/*--------------------------------- + + qh_nearcoplanar() + for all facets, remove near-inside points from facet->coplanarset + coplanar points defined by innerplane from qh_outerinner() + + returns: + if qh KEEPcoplanar && !qh KEEPinside + facet->coplanarset only contains coplanar points + if qh.JOGGLEmax + drops inner plane by another qh.JOGGLEmax diagonal since a + vertex could shift out while a coplanar point shifts in + + notes: + used for qh.PREmerge and qh.JOGGLEmax + must agree with computation of qh.NEARcoplanar in qh_detroundoff() + design: + if not keeping coplanar or inside points + free all coplanar sets + else if not keeping both coplanar and inside points + remove !coplanar or !inside points from coplanar sets +*/ +void qh_nearcoplanar(void /* qh.facet_list */) { + facetT *facet; + pointT *point, **pointp; + int numpart; + realT dist, innerplane; + + if (!qh KEEPcoplanar && !qh KEEPinside) { + FORALLfacets { + if (facet->coplanarset) + qh_setfree( &facet->coplanarset); + } + }else if (!qh KEEPcoplanar || !qh KEEPinside) { + qh_outerinner(NULL, NULL, &innerplane); + if (qh JOGGLEmax < REALmax/2) + innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim); + numpart= 0; + FORALLfacets { + if (facet->coplanarset) { + FOREACHpoint_(facet->coplanarset) { + numpart++; + qh_distplane(point, facet, &dist); + if (dist < innerplane) { + if (!qh KEEPinside) + SETref_(point)= NULL; + }else if (!qh KEEPcoplanar) + SETref_(point)= NULL; + } + qh_setcompact(facet->coplanarset); + } + } + zzadd_(Zcheckpart, numpart); + } +} /* nearcoplanar */ + +/*--------------------------------- + + qh_nearvertex( facet, point, bestdist ) + return nearest vertex in facet to point + + returns: + vertex and its distance + + notes: + if qh.DELAUNAY + distance is measured in the input set + searches neighboring tricoplanar facets (requires vertexneighbors) + Slow implementation. Recomputes vertex set for each point. + The vertex set could be stored in the qh.keepcentrum facet. +*/ +vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp) { + realT bestdist= REALmax, dist; + vertexT *bestvertex= NULL, *vertex, **vertexp, *apex; + coordT *center; + facetT *neighbor, **neighborp; + setT *vertices; + int dim= qh hull_dim; + + if (qh DELAUNAY) + dim--; + if (facet->tricoplanar) { + if (!qh VERTEXneighbors || !facet->center) { + qh_fprintf(qh ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n"); + qh_errexit(qh_ERRqhull, facet, NULL); + } + vertices= qh_settemp(qh TEMPsize); + apex= SETfirstt_(facet->vertices, vertexT); + center= facet->center; + FOREACHneighbor_(apex) { + if (neighbor->center == center) { + FOREACHvertex_(neighbor->vertices) + qh_setappend(&vertices, vertex); + } + } + }else + vertices= facet->vertices; + FOREACHvertex_(vertices) { + dist= qh_pointdist(vertex->point, point, -dim); + if (dist < bestdist) { + bestdist= dist; + bestvertex= vertex; + } + } + if (facet->tricoplanar) + qh_settempfree(&vertices); + *bestdistp= sqrt(bestdist); + trace3((qh ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n", + bestvertex->id, *bestdistp, facet->id, qh_pointid(point))); + return bestvertex; +} /* nearvertex */ + +/*--------------------------------- + + qh_newhashtable( newsize ) + returns size of qh.hash_table of at least newsize slots + + notes: + assumes qh.hash_table is NULL + qh_HASHfactor determines the number of extra slots + size is not divisible by 2, 3, or 5 +*/ +int qh_newhashtable(int newsize) { + int size; + + size= ((newsize+1)*qh_HASHfactor) | 0x1; /* odd number */ + while (True) { + if (newsize<0 || size<0) { + qh_fprintf(qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d). Did int overflow due to high-D?\n", newsize, size); /* WARN64 */ + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if ((size%3) && (size%5)) + break; + size += 2; + /* loop terminates because there is an infinite number of primes */ + } + qh hash_table= qh_setnew(size); + qh_setzero(qh hash_table, 0, size); + return size; +} /* newhashtable */ + +/*--------------------------------- + + qh_newvertex( point ) + returns a new vertex for point +*/ +vertexT *qh_newvertex(pointT *point) { + vertexT *vertex; + + zinc_(Ztotvertices); + vertex= (vertexT *)qh_memalloc((int)sizeof(vertexT)); + memset((char *) vertex, (size_t)0, sizeof(vertexT)); + if (qh vertex_id == 0xFFFFFF) { + qh_fprintf(qh ferr, 6159, "qhull error: more than %d vertices. ID field overflows and two vertices\n\ +may have the same identifier. Vertices will not be sorted correctly.\n", 0xFFFFFF); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (qh vertex_id == qh tracevertex_id) + qh tracevertex= vertex; + vertex->id= qh vertex_id++; + vertex->point= point; + vertex->dim= (unsigned char)(qh hull_dim <= MAX_vdim ? qh hull_dim : 0); + trace4((qh ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(vertex->point), + vertex->id)); + return(vertex); +} /* newvertex */ + +/*--------------------------------- + + qh_nextridge3d( atridge, facet, vertex ) + return next ridge and vertex for a 3d facet + returns NULL on error + [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qh_qh. + + notes: + in qh_ORIENTclock order + this is a O(n^2) implementation to trace all ridges + be sure to stop on any 2nd visit + same as QhullRidge::nextRidge3d + does not use qh_qh or qh_errexit [QhullFacet.cpp] + + design: + for each ridge + exit if it is the ridge after atridge +*/ +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) { + vertexT *atvertex, *vertex, *othervertex; + ridgeT *ridge, **ridgep; + + if ((atridge->top == facet) ^ qh_ORIENTclock) + atvertex= SETsecondt_(atridge->vertices, vertexT); + else + atvertex= SETfirstt_(atridge->vertices, vertexT); + FOREACHridge_(facet->ridges) { + if (ridge == atridge) + continue; + if ((ridge->top == facet) ^ qh_ORIENTclock) { + othervertex= SETsecondt_(ridge->vertices, vertexT); + vertex= SETfirstt_(ridge->vertices, vertexT); + }else { + vertex= SETsecondt_(ridge->vertices, vertexT); + othervertex= SETfirstt_(ridge->vertices, vertexT); + } + if (vertex == atvertex) { + if (vertexp) + *vertexp= othervertex; + return ridge; + } + } + return NULL; +} /* nextridge3d */ +#else /* qh_NOmerge */ +void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) { +} +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) { + + return NULL; +} +#endif /* qh_NOmerge */ + +/*--------------------------------- + + qh_outcoplanar() + move points from all facets' outsidesets to their coplanarsets + + notes: + for post-processing under qh.NARROWhull + + design: + for each facet + for each outside point for facet + partition point into coplanar set +*/ +void qh_outcoplanar(void /* facet_list */) { + pointT *point, **pointp; + facetT *facet; + realT dist; + + trace1((qh ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh NARROWhull\n")); + FORALLfacets { + FOREACHpoint_(facet->outsideset) { + qh num_outside--; + if (qh KEEPcoplanar || qh KEEPnearinside) { + qh_distplane(point, facet, &dist); + zinc_(Zpartition); + qh_partitioncoplanar(point, facet, &dist); + } + } + qh_setfree(&facet->outsideset); + } +} /* outcoplanar */ + +/*--------------------------------- + + qh_point( id ) + return point for a point id, or NULL if unknown + + alternative code: + return((pointT *)((unsigned long)qh.first_point + + (unsigned long)((id)*qh.normal_size))); +*/ +pointT *qh_point(int id) { + + if (id < 0) + return NULL; + if (id < qh num_points) + return qh first_point + id * qh hull_dim; + id -= qh num_points; + if (id < qh_setsize(qh other_points)) + return SETelemt_(qh other_points, id, pointT); + return NULL; +} /* point */ + +/*--------------------------------- + + qh_point_add( set, point, elem ) + stores elem at set[point.id] + + returns: + access function for qh_pointfacet and qh_pointvertex + + notes: + checks point.id +*/ +void qh_point_add(setT *set, pointT *point, void *elem) { + int id, size; + + SETreturnsize_(set, size); + if ((id= qh_pointid(point)) < 0) + qh_fprintf(qh ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n", + point, id); + else if (id >= size) { + qh_fprintf(qh ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n", + id, size); + qh_errexit(qh_ERRqhull, NULL, NULL); + }else + SETelem_(set, id)= elem; +} /* point_add */ + + +/*--------------------------------- + + qh_pointfacet() + return temporary set of facet for each point + the set is indexed by point id + + notes: + vertices assigned to one of the facets + coplanarset assigned to the facet + outside set assigned to the facet + NULL if no facet for point (inside) + includes qh.GOODpointp + + access: + FOREACHfacet_i_(facets) { ... } + SETelem_(facets, i) + + design: + for each facet + add each vertex + add each coplanar point + add each outside point +*/ +setT *qh_pointfacet(void /*qh facet_list*/) { + int numpoints= qh num_points + qh_setsize(qh other_points); + setT *facets; + facetT *facet; + vertexT *vertex, **vertexp; + pointT *point, **pointp; + + facets= qh_settemp(numpoints); + qh_setzero(facets, 0, numpoints); + qh vertex_visit++; + FORALLfacets { + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + qh_point_add(facets, vertex->point, facet); + } + } + FOREACHpoint_(facet->coplanarset) + qh_point_add(facets, point, facet); + FOREACHpoint_(facet->outsideset) + qh_point_add(facets, point, facet); + } + return facets; +} /* pointfacet */ + +/*--------------------------------- + + qh_pointvertex( ) + return temporary set of vertices indexed by point id + entry is NULL if no vertex for a point + this will include qh.GOODpointp + + access: + FOREACHvertex_i_(vertices) { ... } + SETelem_(vertices, i) +*/ +setT *qh_pointvertex(void /*qh facet_list*/) { + int numpoints= qh num_points + qh_setsize(qh other_points); + setT *vertices; + vertexT *vertex; + + vertices= qh_settemp(numpoints); + qh_setzero(vertices, 0, numpoints); + FORALLvertices + qh_point_add(vertices, vertex->point, vertex); + return vertices; +} /* pointvertex */ + + +/*--------------------------------- + + qh_prependfacet( facet, facetlist ) + prepend facet to the start of a facetlist + + returns: + increments qh.numfacets + updates facetlist, qh.facet_list, facet_next + + notes: + be careful of prepending since it can lose a pointer. + e.g., can lose _next by deleting and then prepending before _next +*/ +void qh_prependfacet(facetT *facet, facetT **facetlist) { + facetT *prevfacet, *list; + + + trace4((qh ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n", + facet->id, getid_(*facetlist))); + if (!*facetlist) + (*facetlist)= qh facet_tail; + list= *facetlist; + prevfacet= list->previous; + facet->previous= prevfacet; + if (prevfacet) + prevfacet->next= facet; + list->previous= facet; + facet->next= *facetlist; + if (qh facet_list == list) /* this may change *facetlist */ + qh facet_list= facet; + if (qh facet_next == list) + qh facet_next= facet; + *facetlist= facet; + qh num_facets++; +} /* prependfacet */ + + +/*--------------------------------- + + qh_printhashtable( fp ) + print hash table to fp + + notes: + not in I/O to avoid bringing io.c in + + design: + for each hash entry + if defined + if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge) + print entry and neighbors +*/ +void qh_printhashtable(FILE *fp) { + facetT *facet, *neighbor; + int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0; + vertexT *vertex, **vertexp; + + FOREACHfacet_i_(qh hash_table) { + if (facet) { + FOREACHneighbor_i_(facet) { + if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) + break; + } + if (neighbor_i == neighbor_n) + continue; + qh_fprintf(fp, 9283, "hash %d f%d ", facet_i, facet->id); + FOREACHvertex_(facet->vertices) + qh_fprintf(fp, 9284, "v%d ", vertex->id); + qh_fprintf(fp, 9285, "\n neighbors:"); + FOREACHneighbor_i_(facet) { + if (neighbor == qh_MERGEridge) + id= -3; + else if (neighbor == qh_DUPLICATEridge) + id= -2; + else + id= getid_(neighbor); + qh_fprintf(fp, 9286, " %d", id); + } + qh_fprintf(fp, 9287, "\n"); + } + } +} /* printhashtable */ + + +/*--------------------------------- + + qh_printlists( fp ) + print out facet and vertex list for debugging (without 'f/v' tags) +*/ +void qh_printlists(void) { + facetT *facet; + vertexT *vertex; + int count= 0; + + qh_fprintf(qh ferr, 8108, "qh_printlists: facets:"); + FORALLfacets { + if (++count % 100 == 0) + qh_fprintf(qh ferr, 8109, "\n "); + qh_fprintf(qh ferr, 8110, " %d", facet->id); + } + qh_fprintf(qh ferr, 8111, "\n new facets %d visible facets %d next facet for qh_addpoint %d\n vertices(new %d):", + getid_(qh newfacet_list), getid_(qh visible_list), getid_(qh facet_next), + getid_(qh newvertex_list)); + count = 0; + FORALLvertices { + if (++count % 100 == 0) + qh_fprintf(qh ferr, 8112, "\n "); + qh_fprintf(qh ferr, 8113, " %d", vertex->id); + } + qh_fprintf(qh ferr, 8114, "\n"); +} /* printlists */ + +/*--------------------------------- + + qh_resetlists( stats, qh_RESETvisible ) + reset newvertex_list, newfacet_list, visible_list + if stats, + maintains statistics + + returns: + visible_list is empty if qh_deletevisible was called +*/ +void qh_resetlists(boolT stats, boolT resetVisible /*qh newvertex_list newfacet_list visible_list*/) { + vertexT *vertex; + facetT *newfacet, *visible; + int totnew=0, totver=0; + + if (stats) { + FORALLvertex_(qh newvertex_list) + totver++; + FORALLnew_facets + totnew++; + zadd_(Zvisvertextot, totver); + zmax_(Zvisvertexmax, totver); + zadd_(Znewfacettot, totnew); + zmax_(Znewfacetmax, totnew); + } + FORALLvertex_(qh newvertex_list) + vertex->newlist= False; + qh newvertex_list= NULL; + FORALLnew_facets + newfacet->newfacet= False; + qh newfacet_list= NULL; + if (resetVisible) { + FORALLvisible_facets { + visible->f.replace= NULL; + visible->visible= False; + } + qh num_visible= 0; + } + qh visible_list= NULL; /* may still have visible facets via qh_triangulate */ + qh NEWfacets= False; +} /* resetlists */ + +/*--------------------------------- + + qh_setvoronoi_all() + compute Voronoi centers for all facets + includes upperDelaunay facets if qh.UPPERdelaunay ('Qu') + + returns: + facet->center is the Voronoi center + + notes: + this is unused/untested code + please email bradb@shore.net if this works ok for you + + use: + FORALLvertices {...} to locate the vertex for a point. + FOREACHneighbor_(vertex) {...} to visit the Voronoi centers for a Voronoi cell. +*/ +void qh_setvoronoi_all(void) { + facetT *facet; + + qh_clearcenters(qh_ASvoronoi); + qh_vertexneighbors(); + + FORALLfacets { + if (!facet->normal || !facet->upperdelaunay || qh UPPERdelaunay) { + if (!facet->center) + facet->center= qh_facetcenter(facet->vertices); + } + } +} /* setvoronoi_all */ + +#ifndef qh_NOmerge + +/*--------------------------------- + + qh_triangulate() + triangulate non-simplicial facets on qh.facet_list, + if qh VORONOI, sets Voronoi centers of non-simplicial facets + nop if hasTriangulation + + returns: + all facets simplicial + each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc. + + notes: + call after qh_check_output since may switch to Voronoi centers + Output may overwrite ->f.triowner with ->f.area +*/ +void qh_triangulate(void /*qh facet_list*/) { + facetT *facet, *nextfacet, *owner; + int onlygood= qh ONLYgood; + facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL; + facetT *orig_neighbor= NULL, *otherfacet; + vertexT *new_vertex_list= NULL; + mergeT *merge; + mergeType mergetype; + int neighbor_i, neighbor_n; + + if (qh hasTriangulation) + return; + trace1((qh ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n")); + if (qh hull_dim == 2) + return; + if (qh VORONOI) { /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */ + qh_clearcenters(qh_ASvoronoi); + qh_vertexneighbors(); + } + qh ONLYgood= False; /* for makenew_nonsimplicial */ + qh visit_id++; + qh NEWfacets= True; + qh degen_mergeset= qh_settemp(qh TEMPsize); + qh newvertex_list= qh vertex_tail; + for (facet= qh facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */ + nextfacet= facet->next; + if (facet->visible || facet->simplicial) + continue; + /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */ + if (!new_facet_list) + new_facet_list= facet; /* will be moved to end */ + qh_triangulate_facet(facet, &new_vertex_list); + } + trace2((qh ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list))); + for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */ + nextfacet= facet->next; + if (facet->visible) + continue; + if (facet->ridges) { + if (qh_setsize(facet->ridges) > 0) { + qh_fprintf(qh ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + qh_setfree(&facet->ridges); + } + if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) { + zinc_(Ztrinull); + qh_triangulate_null(facet); + } + } + trace2((qh ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh degen_mergeset))); + qh visible_list= qh facet_tail; + while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + mergetype= merge->type; + qh_memfree(merge, (int)sizeof(mergeT)); + if (mergetype == MRGmirror) { + zinc_(Ztrimirror); + qh_triangulate_mirror(facet1, facet2); + } + } + qh_settempfree(&qh degen_mergeset); + trace2((qh ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list))); + qh newvertex_list= new_vertex_list; /* all vertices of new facets */ + qh visible_list= NULL; + qh_updatevertices(/*qh newvertex_list, empty newfacet_list and visible_list*/); + qh_resetlists(False, !qh_RESETvisible /*qh newvertex_list, empty newfacet_list and visible_list*/); + + trace2((qh ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list))); + trace2((qh ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n")); + FORALLfacet_(new_facet_list) { + if (facet->tricoplanar && !facet->visible) { + FOREACHneighbor_i_(facet) { + if (neighbor_i == 0) { /* first iteration */ + if (neighbor->tricoplanar) + orig_neighbor= neighbor->f.triowner; + else + orig_neighbor= neighbor; + }else { + if (neighbor->tricoplanar) + otherfacet= neighbor->f.triowner; + else + otherfacet= neighbor; + if (orig_neighbor == otherfacet) { + zinc_(Ztridegen); + facet->degenerate= True; + break; + } + } + } + } + } + + trace2((qh ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n")); + owner= NULL; + visible= NULL; + for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */ + nextfacet= facet->next; + if (facet->visible) { + if (facet->tricoplanar) { /* a null or mirrored facet */ + qh_delfacet(facet); + qh num_visible--; + }else { /* a non-simplicial facet followed by its tricoplanars */ + if (visible && !owner) { + /* RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */ + trace2((qh ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n", + visible->id)); + qh_delfacet(visible); + qh num_visible--; + } + visible= facet; + owner= NULL; + } + }else if (facet->tricoplanar) { + if (facet->f.triowner != visible) { + qh_fprintf(qh ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible)); + qh_errexit2 (qh_ERRqhull, facet, visible); + } + if (owner) + facet->f.triowner= owner; + else if (!facet->degenerate) { + owner= facet; + nextfacet= visible->next; /* rescan tricoplanar facets with owner */ + facet->keepcentrum= True; /* one facet owns ->normal, etc. */ + facet->coplanarset= visible->coplanarset; + facet->outsideset= visible->outsideset; + visible->coplanarset= NULL; + visible->outsideset= NULL; + if (!qh TRInormals) { /* center and normal copied to tricoplanar facets */ + visible->center= NULL; + visible->normal= NULL; + } + qh_delfacet(visible); + qh num_visible--; + } + } + } + if (visible && !owner) { + trace2((qh ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n", + visible->id)); + qh_delfacet(visible); + qh num_visible--; + } + qh NEWfacets= False; + qh ONLYgood= onlygood; /* restore value */ + if (qh CHECKfrequently) + qh_checkpolygon(qh facet_list); + qh hasTriangulation= True; +} /* triangulate */ + + +/*--------------------------------- + + qh_triangulate_facet(facetA) + triangulate a non-simplicial facet + if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center + returns: + qh.newfacet_list == simplicial facets + facet->tricoplanar set and ->keepcentrum false + facet->degenerate set if duplicated apex + facet->f.trivisible set to facetA + facet->center copied from facetA (created if qh_ASvoronoi) + qh_eachvoronoi, qh_detvridge, qh_detvridge3 assume centers copied + facet->normal,offset,maxoutside copied from facetA + + notes: + qh_makenew_nonsimplicial uses neighbor->seen for the same + + see also: + qh_addpoint() -- add a point + qh_makenewfacets() -- construct a cone of facets for a new vertex + + design: + if qh_ASvoronoi, + compute Voronoi center (facet->center) + select first vertex (highest ID to preserve ID ordering of ->vertices) + triangulate from vertex to ridges + copy facet->center, normal, offset + update vertex neighbors +*/ +void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex) { + facetT *newfacet; + facetT *neighbor, **neighborp; + vertexT *apex; + int numnew=0; + + trace3((qh ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id)); + + if (qh IStracing >= 4) + qh_printfacet(qh ferr, facetA); + FOREACHneighbor_(facetA) { + neighbor->seen= False; + neighbor->coplanar= False; + } + if (qh CENTERtype == qh_ASvoronoi && !facetA->center /* matches upperdelaunay in qh_setfacetplane() */ + && fabs_(facetA->normal[qh hull_dim -1]) >= qh ANGLEround * qh_ZEROdelaunay) { + facetA->center= qh_facetcenter(facetA->vertices); + } + qh_willdelete(facetA, NULL); + qh newfacet_list= qh facet_tail; + facetA->visitid= qh visit_id; + apex= SETfirstt_(facetA->vertices, vertexT); + qh_makenew_nonsimplicial(facetA, apex, &numnew); + SETfirst_(facetA->neighbors)= NULL; + FORALLnew_facets { + newfacet->tricoplanar= True; + newfacet->f.trivisible= facetA; + newfacet->degenerate= False; + newfacet->upperdelaunay= facetA->upperdelaunay; + newfacet->good= facetA->good; + if (qh TRInormals) { + newfacet->keepcentrum= True; + newfacet->normal= qh_copypoints(facetA->normal, 1, qh hull_dim); + if (qh CENTERtype == qh_AScentrum) + newfacet->center= qh_getcentrum(newfacet); + else + newfacet->center= qh_copypoints(facetA->center, 1, qh hull_dim); + }else { + newfacet->keepcentrum= False; + newfacet->normal= facetA->normal; + newfacet->center= facetA->center; + } + newfacet->offset= facetA->offset; +#if qh_MAXoutside + newfacet->maxoutside= facetA->maxoutside; +#endif + } + qh_matchnewfacets(/*qh newfacet_list*/); + zinc_(Ztricoplanar); + zadd_(Ztricoplanartot, numnew); + zmax_(Ztricoplanarmax, numnew); + qh visible_list= NULL; + if (!(*first_vertex)) + (*first_vertex)= qh newvertex_list; + qh newvertex_list= NULL; + qh_updatevertices(/*qh newfacet_list, empty visible_list and newvertex_list*/); + qh_resetlists(False, !qh_RESETvisible /*qh newfacet_list, empty visible_list and newvertex_list*/); +} /* triangulate_facet */ + +/*--------------------------------- + + qh_triangulate_link(oldfacetA, facetA, oldfacetB, facetB) + relink facetA to facetB via oldfacets + returns: + adds mirror facets to qh degen_mergeset (4-d and up only) + design: + if they are already neighbors, the opposing neighbors become MRGmirror facets +*/ +void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) { + int errmirror= False; + + trace3((qh ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n", + oldfacetA->id, oldfacetB->id, facetA->id, facetB->id)); + if (qh_setin(facetA->neighbors, facetB)) { + if (!qh_setin(facetB->neighbors, facetA)) + errmirror= True; + else + qh_appendmergeset(facetA, facetB, MRGmirror, NULL); + }else if (qh_setin(facetB->neighbors, facetA)) + errmirror= True; + if (errmirror) { + qh_fprintf(qh ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n", + facetA->id, facetB->id, oldfacetA->id, oldfacetB->id); + qh_errexit2 (qh_ERRqhull, facetA, facetB); + } + qh_setreplace(facetB->neighbors, oldfacetB, facetA); + qh_setreplace(facetA->neighbors, oldfacetA, facetB); +} /* triangulate_link */ + +/*--------------------------------- + + qh_triangulate_mirror(facetA, facetB) + delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror + a mirrored facet shares the same vertices of a logical ridge + design: + since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet + if they are already neighbors, the opposing neighbors become MRGmirror facets +*/ +void qh_triangulate_mirror(facetT *facetA, facetT *facetB) { + facetT *neighbor, *neighborB; + int neighbor_i, neighbor_n; + + trace3((qh ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n", + facetA->id, facetB->id)); + FOREACHneighbor_i_(facetA) { + neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT); + if (neighbor == neighborB) + continue; /* occurs twice */ + qh_triangulate_link(facetA, neighbor, facetB, neighborB); + } + qh_willdelete(facetA, NULL); + qh_willdelete(facetB, NULL); +} /* triangulate_mirror */ + +/*--------------------------------- + + qh_triangulate_null(facetA) + remove null facetA from qh_triangulate_facet() + a null facet has vertex #1 (apex) == vertex #2 + returns: + adds facetA to ->visible for deletion after qh_updatevertices + qh degen_mergeset contains mirror facets (4-d and up only) + design: + since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet + if they are already neighbors, the opposing neighbors become MRGmirror facets +*/ +void qh_triangulate_null(facetT *facetA) { + facetT *neighbor, *otherfacet; + + trace3((qh ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id)); + neighbor= SETfirstt_(facetA->neighbors, facetT); + otherfacet= SETsecondt_(facetA->neighbors, facetT); + qh_triangulate_link(facetA, neighbor, facetA, otherfacet); + qh_willdelete(facetA, NULL); +} /* triangulate_null */ + +#else /* qh_NOmerge */ +void qh_triangulate(void) { +} +#endif /* qh_NOmerge */ + + /*--------------------------------- + + qh_vertexintersect( vertexsetA, vertexsetB ) + intersects two vertex sets (inverse id ordered) + vertexsetA is a temporary set at the top of qhmem.tempstack + + returns: + replaces vertexsetA with the intersection + + notes: + could overwrite vertexsetA if currently too slow +*/ +void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB) { + setT *intersection; + + intersection= qh_vertexintersect_new(*vertexsetA, vertexsetB); + qh_settempfree(vertexsetA); + *vertexsetA= intersection; + qh_settemppush(intersection); +} /* vertexintersect */ + +/*--------------------------------- + + qh_vertexintersect_new( ) + intersects two vertex sets (inverse id ordered) + + returns: + a new set +*/ +setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) { + setT *intersection= qh_setnew(qh hull_dim - 1); + vertexT **vertexA= SETaddr_(vertexsetA, vertexT); + vertexT **vertexB= SETaddr_(vertexsetB, vertexT); + + while (*vertexA && *vertexB) { + if (*vertexA == *vertexB) { + qh_setappend(&intersection, *vertexA); + vertexA++; vertexB++; + }else { + if ((*vertexA)->id > (*vertexB)->id) + vertexA++; + else + vertexB++; + } + } + return intersection; +} /* vertexintersect_new */ + +/*--------------------------------- + + qh_vertexneighbors() + for each vertex in qh.facet_list, + determine its neighboring facets + + returns: + sets qh.VERTEXneighbors + nop if qh.VERTEXneighbors already set + qh_addpoint() will maintain them + + notes: + assumes all vertex->neighbors are NULL + + design: + for each facet + for each vertex + append facet to vertex->neighbors +*/ +void qh_vertexneighbors(void /*qh facet_list*/) { + facetT *facet; + vertexT *vertex, **vertexp; + + if (qh VERTEXneighbors) + return; + trace1((qh ferr, 1035, "qh_vertexneighbors: determing neighboring facets for each vertex\n")); + qh vertex_visit++; + FORALLfacets { + if (facet->visible) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + vertex->neighbors= qh_setnew(qh hull_dim); + } + qh_setappend(&vertex->neighbors, facet); + } + } + qh VERTEXneighbors= True; +} /* vertexneighbors */ + +/*--------------------------------- + + qh_vertexsubset( vertexsetA, vertexsetB ) + returns True if vertexsetA is a subset of vertexsetB + assumes vertexsets are sorted + + note: + empty set is a subset of any other set +*/ +boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) { + vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT); + vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT); + + while (True) { + if (!*vertexA) + return True; + if (!*vertexB) + return False; + if ((*vertexA)->id > (*vertexB)->id) + return False; + if (*vertexA == *vertexB) + vertexA++; + vertexB++; + } + return False; /* avoid warnings */ +} /* vertexsubset */ diff --git a/extern/qhull/qhull_a.h b/extern/qhull/qhull_a.h new file mode 100644 index 000000000000..27021b25247f --- /dev/null +++ b/extern/qhull/qhull_a.h @@ -0,0 +1,151 @@ +/*--------------------------------- + + qhull_a.h + all header files for compiling qhull + + see qh-qhull.htm + + see libqhull.h for user-level definitions + + see user.h for user-defineable constants + + defines internal functions for libqhull.c global.c + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/qhull_a.h#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ + + Notes: grep for ((" and (" to catch fprintf("lkasdjf"); + full parens around (x?y:z) + use '#include qhull/qhull_a.h' to avoid name clashes +*/ + +#ifndef qhDEFqhulla +#define qhDEFqhulla 1 + +#include "libqhull.h" /* Defines data types */ + +#include "stat.h" +#include "random.h" +#include "mem.h" +#include "qset.h" +#include "geom.h" +#include "merge.h" +#include "poly.h" +#include "io.h" + +#include+#include +#include +#include /* some compilers will not need float.h */ +#include +#include +#include +#include +#include +/*** uncomment here and qset.c + if string.h does not define memcpy() +#include +*/ + +#if qh_CLOCKtype == 2 /* defined in user.h from libqhull.h */ +#include +#include +#include +#endif + +#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +#pragma warning( disable : 4100) /* unreferenced formal parameter */ +#pragma warning( disable : 4127) /* conditional expression is constant */ +#pragma warning( disable : 4706) /* assignment within conditional function */ +#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */ +#endif + +/* ======= -macros- =========== */ + +/*---------------------------------- + + traceN((qh ferr, 0Nnnn, "format\n", vars)); + calls qh_fprintf if qh.IStracing >= N + + Add debugging traps to the end of qh_fprintf + + notes: + removing tracing reduces code size but doesn't change execution speed +*/ +#ifndef qh_NOtrace +#define trace0(args) {if (qh IStracing) qh_fprintf args;} +#define trace1(args) {if (qh IStracing >= 1) qh_fprintf args;} +#define trace2(args) {if (qh IStracing >= 2) qh_fprintf args;} +#define trace3(args) {if (qh IStracing >= 3) qh_fprintf args;} +#define trace4(args) {if (qh IStracing >= 4) qh_fprintf args;} +#define trace5(args) {if (qh IStracing >= 5) qh_fprintf args;} +#else /* qh_NOtrace */ +#define trace0(args) {} +#define trace1(args) {} +#define trace2(args) {} +#define trace3(args) {} +#define trace4(args) {} +#define trace5(args) {} +#endif /* qh_NOtrace */ + +/*---------------------------------- + +*/ + +/* See Qt's qglobal.h */ +#if !defined(SAG_COM) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) +# define QHULL_OS_WIN +#elif defined(__MWERKS__) && defined(__INTEL__) +# define QHULL_OS_WIN +#endif +#if defined(__INTEL_COMPILER) && !defined(QHULL_OS_WIN) +template +inline void qhullUnused(T &x) { (void)x; } +# define QHULL_UNUSED(x) qhullUnused(x); +#else +# define QHULL_UNUSED(x) (void)x; +#endif + +/***** -libqhull.c prototypes (alphabetical after qhull) ********************/ + +void qh_qhull(void); +boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist); +void qh_buildhull(void); +void qh_buildtracing(pointT *furthest, facetT *facet); +void qh_build_withrestart(void); +void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet); +void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible,int *goodhorizon); +pointT *qh_nextfurthest(facetT **visible); +void qh_partitionall(setT *vertices, pointT *points,int npoints); +void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist); +void qh_partitionpoint(pointT *point, facetT *facet); +void qh_partitionvisible(boolT allpoints, int *numpoints); +void qh_precision(const char *reason); +void qh_printsummary(FILE *fp); + +/***** -global.c internal prototypes (alphabetical) ***********************/ + +void qh_appendprint(qh_PRINT format); +void qh_freebuild(boolT allmem); +void qh_freebuffers(void); +void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc); + +/***** -stat.c internal prototypes (alphabetical) ***********************/ + +void qh_allstatA(void); +void qh_allstatB(void); +void qh_allstatC(void); +void qh_allstatD(void); +void qh_allstatE(void); +void qh_allstatE2 (void); +void qh_allstatF(void); +void qh_allstatG(void); +void qh_allstatH(void); +void qh_freebuffers(void); +void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc); + +#endif /* qhDEFqhulla */ diff --git a/extern/qhull/qset.c b/extern/qhull/qset.c new file mode 100644 index 000000000000..e5038f4ac192 --- /dev/null +++ b/extern/qhull/qset.c @@ -0,0 +1,1337 @@ +/* --------------------------------- + + qset.c + implements set manipulations needed for quickhull + + see qh-set.htm and qset.h + + Be careful of strict aliasing (two pointers of different types + that reference the same location). The last slot of a set is + either the actual size of the set plus 1, or the NULL terminator + of the set (i.e., setelemT). + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/qset.c#6 $$Change: 1475 $ + $DateTime: 2012/01/27 22:32:16 $$Author: bbarber $ +*/ + +#include "qset.h" +#include "mem.h" +#include+#include +/*** uncomment here and qhull_a.h + if string.h does not define memcpy() +#include +*/ + +#ifndef qhDEFlibqhull +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +void qh_errexit(int exitcode, facetT *, ridgeT *); +void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ); +# ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +# pragma warning( disable : 4127) /* conditional expression is constant */ +# pragma warning( disable : 4706) /* assignment within conditional function */ +# endif +#endif + +/*=============== internal macros ===========================*/ + +/*============ functions in alphabetical order ===================*/ + +/*---------------------------------- + + qh_setaddnth( setp, nth, newelem) + adds newelem as n'th element of sorted or unsorted *setp + + notes: + *setp and newelem must be defined + *setp may be a temp set + nth=0 is first element + errors if nth is out of bounds + + design: + expand *setp if empty or full + move tail of *setp up one + insert newelem +*/ +void qh_setaddnth(setT **setp, int nth, void *newelem) { + int oldsize, i; + setelemT *sizep; /* avoid strict aliasing */ + setelemT *oldp, *newp; + + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(setp); + sizep= SETsizeaddr_(*setp); + } + oldsize= sizep->i - 1; + if (nth < 0 || nth > oldsize) { + qh_fprintf(qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qhmem.ferr, "", *setp); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + sizep->i++; + oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void); /* NULL */ + newp= oldp+1; + for (i=oldsize-nth+1; i--; ) /* move at least NULL */ + (newp--)->p= (oldp--)->p; /* may overwrite *sizep */ + newp->p= newelem; +} /* setaddnth */ + + +/*---------------------------------- + + setaddsorted( setp, newelem ) + adds an newelem into sorted *setp + + notes: + *setp and newelem must be defined + *setp may be a temp set + nop if newelem already in set + + design: + find newelem's position in *setp + insert newelem +*/ +void qh_setaddsorted(setT **setp, void *newelem) { + int newindex=0; + void *elem, **elemp; + + FOREACHelem_(*setp) { /* could use binary search instead */ + if (elem < newelem) + newindex++; + else if (elem == newelem) + return; + else + break; + } + qh_setaddnth(setp, newindex, newelem); +} /* setaddsorted */ + + +/*--------------------------------- + + qh_setappend( setp, newelem) + append newelem to *setp + + notes: + *setp may be a temp set + *setp and newelem may be NULL + + design: + expand *setp if empty or full + append newelem to *setp + +*/ +void qh_setappend(setT **setp, void *newelem) { + setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */ + setelemT *endp; + int count; + + if (!newelem) + return; + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(setp); + sizep= SETsizeaddr_(*setp); + } + count= (sizep->i)++ - 1; + endp= (setelemT *)SETelemaddr_(*setp, count, void); + (endp++)->p= newelem; + endp->p= NULL; +} /* setappend */ + +/*--------------------------------- + + qh_setappend_set( setp, setA) + appends setA to *setp + + notes: + *setp can not be a temp set + *setp and setA may be NULL + + design: + setup for copy + expand *setp if it is too small + append all elements of setA to *setp +*/ +void qh_setappend_set(setT **setp, setT *setA) { + int sizeA, size; + setT *oldset; + setelemT *sizep; + + if (!setA) + return; + SETreturnsize_(setA, sizeA); + if (!*setp) + *setp= qh_setnew(sizeA); + sizep= SETsizeaddr_(*setp); + if (!(size= sizep->i)) + size= (*setp)->maxsize; + else + size--; + if (size + sizeA > (*setp)->maxsize) { + oldset= *setp; + *setp= qh_setcopy(oldset, sizeA); + qh_setfree(&oldset); + sizep= SETsizeaddr_(*setp); + } + if (sizeA > 0) { + sizep->i= size+sizeA+1; /* memcpy may overwrite */ + memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize); + } +} /* setappend_set */ + + +/*--------------------------------- + + qh_setappend2ndlast( setp, newelem ) + makes newelem the next to the last element in *setp + + notes: + *setp must have at least one element + newelem must be defined + *setp may be a temp set + + design: + expand *setp if empty or full + move last element of *setp up one + insert newelem +*/ +void qh_setappend2ndlast(setT **setp, void *newelem) { + setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */ + setelemT *endp, *lastp; + int count; + + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(setp); + sizep= SETsizeaddr_(*setp); + } + count= (sizep->i)++ - 1; + endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */ + lastp= endp-1; + *(endp++)= *lastp; + endp->p= NULL; /* may overwrite *sizep */ + lastp->p= newelem; +} /* setappend2ndlast */ + +/*--------------------------------- + + qh_setcheck( set, typename, id ) + check set for validity + report errors with typename and id + + design: + checks that maxsize, actual size, and NULL terminator agree +*/ +void qh_setcheck(setT *set, const char *tname, unsigned id) { + int maxsize, size; + int waserr= 0; + + if (!set) + return; + SETreturnsize_(set, size); + maxsize= set->maxsize; + if (size > maxsize || !maxsize) { + qh_fprintf(qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n", + size, tname, id, maxsize); + waserr= 1; + }else if (set->e[size].p) { + qh_fprintf(qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n", + tname, id, size-1, maxsize); + waserr= 1; + } + if (waserr) { + qh_setprint(qhmem.ferr, "ERRONEOUS", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } +} /* setcheck */ + + +/*--------------------------------- + + qh_setcompact( set ) + remove internal NULLs from an unsorted set + + returns: + updated set + + notes: + set may be NULL + it would be faster to swap tail of set into holes, like qh_setdel + + design: + setup pointers into set + skip NULLs while copying elements to start of set + update the actual size +*/ +void qh_setcompact(setT *set) { + int size; + void **destp, **elemp, **endp, **firstp; + + if (!set) + return; + SETreturnsize_(set, size); + destp= elemp= firstp= SETaddr_(set, void); + endp= destp + size; + while (1) { + if (!(*destp++ = *elemp++)) { + destp--; + if (elemp > endp) + break; + } + } + qh_settruncate(set, (int)(destp-firstp)); /* WARN64 */ +} /* setcompact */ + + +/*--------------------------------- + + qh_setcopy( set, extra ) + make a copy of a sorted or unsorted set with extra slots + + returns: + new set + + design: + create a newset with extra slots + copy the elements to the newset + +*/ +setT *qh_setcopy(setT *set, int extra) { + setT *newset; + int size; + + if (extra < 0) + extra= 0; + SETreturnsize_(set, size); + newset= qh_setnew(size+extra); + SETsizeaddr_(newset)->i= size+1; /* memcpy may overwrite */ + memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize); + return(newset); +} /* setcopy */ + + +/*--------------------------------- + + qh_setdel( set, oldelem ) + delete oldelem from an unsorted set + + returns: + returns oldelem if found + returns NULL otherwise + + notes: + set may be NULL + oldelem must not be NULL; + only deletes one copy of oldelem in set + + design: + locate oldelem + update actual size if it was full + move the last element to the oldelem's location +*/ +void *qh_setdel(setT *set, void *oldelem) { + setelemT *sizep; + setelemT *elemp; + setelemT *lastp; + + if (!set) + return NULL; + elemp= (setelemT *)SETaddr_(set, void); + while (elemp->p != oldelem && elemp->p) + elemp++; + if (elemp->p) { + sizep= SETsizeaddr_(set); + if (!(sizep->i)--) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void); + elemp->p= lastp->p; /* may overwrite itself */ + lastp->p= NULL; + return oldelem; + } + return NULL; +} /* setdel */ + + +/*--------------------------------- + + qh_setdellast( set) + return last element of set or NULL + + notes: + deletes element from set + set may be NULL + + design: + return NULL if empty + if full set + delete last element and set actual size + else + delete last element and update actual size +*/ +void *qh_setdellast(setT *set) { + int setsize; /* actually, actual_size + 1 */ + int maxsize; + setelemT *sizep; + void *returnvalue; + + if (!set || !(set->e[0].p)) + return NULL; + sizep= SETsizeaddr_(set); + if ((setsize= sizep->i)) { + returnvalue= set->e[setsize - 2].p; + set->e[setsize - 2].p= NULL; + sizep->i--; + }else { + maxsize= set->maxsize; + returnvalue= set->e[maxsize - 1].p; + set->e[maxsize - 1].p= NULL; + sizep->i= maxsize; + } + return returnvalue; +} /* setdellast */ + + +/*--------------------------------- + + qh_setdelnth( set, nth ) + deletes nth element from unsorted set + 0 is first element + + returns: + returns the element (needs type conversion) + + notes: + errors if nth invalid + + design: + setup points and check nth + delete nth element and overwrite with last element +*/ +void *qh_setdelnth(setT *set, int nth) { + void *elem; + setelemT *sizep; + setelemT *elemp, *lastp; + + elemp= (setelemT *)SETelemaddr_(set, nth, void); + sizep= SETsizeaddr_(set); + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + if (nth < 0 || nth >= sizep->i) { + qh_fprintf(qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void); + elem= elemp->p; + elemp->p= lastp->p; /* may overwrite itself */ + lastp->p= NULL; + return elem; +} /* setdelnth */ + +/*--------------------------------- + + qh_setdelnthsorted( set, nth ) + deletes nth element from sorted set + + returns: + returns the element (use type conversion) + + notes: + errors if nth invalid + + see also: + setnew_delnthsorted + + design: + setup points and check nth + copy remaining elements down one + update actual size +*/ +void *qh_setdelnthsorted(setT *set, int nth) { + void *elem; + setelemT *sizep; + setelemT *newp, *oldp; + + sizep= SETsizeaddr_(set); + if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) { + qh_fprintf(qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + newp= (setelemT *)SETelemaddr_(set, nth, void); + elem= newp->p; + oldp= newp+1; + while (((newp++)->p= (oldp++)->p)) + ; /* copy remaining elements and NULL */ + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */ + return elem; +} /* setdelnthsorted */ + + +/*--------------------------------- + + qh_setdelsorted( set, oldelem ) + deletes oldelem from sorted set + + returns: + returns oldelem if it was deleted + + notes: + set may be NULL + + design: + locate oldelem in set + copy remaining elements down one + update actual size +*/ +void *qh_setdelsorted(setT *set, void *oldelem) { + setelemT *sizep; + setelemT *newp, *oldp; + + if (!set) + return NULL; + newp= (setelemT *)SETaddr_(set, void); + while(newp->p != oldelem && newp->p) + newp++; + if (newp->p) { + oldp= newp+1; + while (((newp++)->p= (oldp++)->p)) + ; /* copy remaining elements */ + sizep= SETsizeaddr_(set); + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */ + return oldelem; + } + return NULL; +} /* setdelsorted */ + + +/*--------------------------------- + + qh_setduplicate( set, elemsize ) + duplicate a set of elemsize elements + + notes: + use setcopy if retaining old elements + + design: + create a new set + for each elem of the old set + create a newelem + append newelem to newset +*/ +setT *qh_setduplicate(setT *set, int elemsize) { + void *elem, **elemp, *newElem; + setT *newSet; + int size; + + if (!(size= qh_setsize(set))) + return NULL; + newSet= qh_setnew(size); + FOREACHelem_(set) { + newElem= qh_memalloc(elemsize); + memcpy(newElem, elem, (size_t)elemsize); + qh_setappend(&newSet, newElem); + } + return newSet; +} /* setduplicate */ + + +/*--------------------------------- + + qh_setendpointer( set ) + Returns pointer to NULL terminator of a set's elements + set can not be NULL + +*/ +void **qh_setendpointer(setT *set) { + + setelemT *sizep= SETsizeaddr_(set); + int n= sizep->i; + return (n ? &set->e[n-1].p : &sizep->p); +} /* qh_setendpointer */ + +/*--------------------------------- + + qh_setequal( ) + returns 1 if two sorted sets are equal, otherwise returns 0 + + notes: + either set may be NULL + + design: + check size of each set + setup pointers + compare elements of each set +*/ +int qh_setequal(setT *setA, setT *setB) { + void **elemAp, **elemBp; + int sizeA= 0, sizeB= 0; + + if (setA) { + SETreturnsize_(setA, sizeA); + } + if (setB) { + SETreturnsize_(setB, sizeB); + } + if (sizeA != sizeB) + return 0; + if (!sizeA) + return 1; + elemAp= SETaddr_(setA, void); + elemBp= SETaddr_(setB, void); + if (!memcmp((char *)elemAp, (char *)elemBp, sizeA*SETelemsize)) + return 1; + return 0; +} /* setequal */ + + +/*--------------------------------- + + qh_setequal_except( setA, skipelemA, setB, skipelemB ) + returns 1 if sorted setA and setB are equal except for skipelemA & B + + returns: + false if either skipelemA or skipelemB are missing + + notes: + neither set may be NULL + + if skipelemB is NULL, + can skip any one element of setB + + design: + setup pointers + search for skipelemA, skipelemB, and mismatches + check results +*/ +int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB) { + void **elemA, **elemB; + int skip=0; + + elemA= SETaddr_(setA, void); + elemB= SETaddr_(setB, void); + while (1) { + if (*elemA == skipelemA) { + skip++; + elemA++; + } + if (skipelemB) { + if (*elemB == skipelemB) { + skip++; + elemB++; + } + }else if (*elemA != *elemB) { + skip++; + if (!(skipelemB= *elemB++)) + return 0; + } + if (!*elemA) + break; + if (*elemA++ != *elemB++) + return 0; + } + if (skip != 2 || *elemB) + return 0; + return 1; +} /* setequal_except */ + + +/*--------------------------------- + + qh_setequal_skip( setA, skipA, setB, skipB ) + returns 1 if sorted setA and setB are equal except for elements skipA & B + + returns: + false if different size + + notes: + neither set may be NULL + + design: + setup pointers + search for mismatches while skipping skipA and skipB +*/ +int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB) { + void **elemA, **elemB, **skipAp, **skipBp; + + elemA= SETaddr_(setA, void); + elemB= SETaddr_(setB, void); + skipAp= SETelemaddr_(setA, skipA, void); + skipBp= SETelemaddr_(setB, skipB, void); + while (1) { + if (elemA == skipAp) + elemA++; + if (elemB == skipBp) + elemB++; + if (!*elemA) + break; + if (*elemA++ != *elemB++) + return 0; + } + if (*elemB) + return 0; + return 1; +} /* setequal_skip */ + + +/*--------------------------------- + + qh_setfree( setp ) + frees the space occupied by a sorted or unsorted set + + returns: + sets setp to NULL + + notes: + set may be NULL + + design: + free array + free set +*/ +void qh_setfree(setT **setp) { + int size; + void **freelistp; /* used !qh_NOmem */ + + if (*setp) { + size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + if (size <= qhmem.LASTsize) { + qh_memfree_(*setp, size, freelistp); + }else + qh_memfree(*setp, size); + *setp= NULL; + } +} /* setfree */ + + +/*--------------------------------- + + qh_setfree2( setp, elemsize ) + frees the space occupied by a set and its elements + + notes: + set may be NULL + + design: + free each element + free set +*/ +void qh_setfree2 (setT **setp, int elemsize) { + void *elem, **elemp; + + FOREACHelem_(*setp) + qh_memfree(elem, elemsize); + qh_setfree(setp); +} /* setfree2 */ + + + +/*--------------------------------- + + qh_setfreelong( setp ) + frees a set only if it's in long memory + + returns: + sets setp to NULL if it is freed + + notes: + set may be NULL + + design: + if set is large + free it +*/ +void qh_setfreelong(setT **setp) { + int size; + + if (*setp) { + size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + if (size > qhmem.LASTsize) { + qh_memfree(*setp, size); + *setp= NULL; + } + } +} /* setfreelong */ + + +/*--------------------------------- + + qh_setin( set, setelem ) + returns 1 if setelem is in a set, 0 otherwise + + notes: + set may be NULL or unsorted + + design: + scans set for setelem +*/ +int qh_setin(setT *set, void *setelem) { + void *elem, **elemp; + + FOREACHelem_(set) { + if (elem == setelem) + return 1; + } + return 0; +} /* setin */ + + +/*--------------------------------- + + qh_setindex( set, atelem ) + returns the index of atelem in set. + returns -1, if not in set or maxsize wrong + + notes: + set may be NULL and may contain nulls. + NOerrors returned (qh_pointid, QhullPoint::id) + + design: + checks maxsize + scans set for atelem +*/ +int qh_setindex(setT *set, void *atelem) { + void **elem; + int size, i; + + if (!set) + return -1; + SETreturnsize_(set, size); + if (size > set->maxsize) + return -1; + elem= SETaddr_(set, void); + for (i=0; i < size; i++) { + if (*elem++ == atelem) + return i; + } + return -1; +} /* setindex */ + + +/*--------------------------------- + + qh_setlarger( oldsetp ) + returns a larger set that contains all elements of *oldsetp + + notes: + the set is at least twice as large + if temp set, updates qhmem.tempstack + + design: + creates a new set + copies the old set to the new set + updates pointers in tempstack + deletes the old set +*/ +void qh_setlarger(setT **oldsetp) { + int size= 1; + setT *newset, *set, **setp, *oldset; + setelemT *sizep; + setelemT *newp, *oldp; + + if (*oldsetp) { + oldset= *oldsetp; + SETreturnsize_(oldset, size); + qhmem.cntlarger++; + qhmem.totlarger += size+1; + newset= qh_setnew(2 * size); + oldp= (setelemT *)SETaddr_(oldset, void); + newp= (setelemT *)SETaddr_(newset, void); + memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize); + sizep= SETsizeaddr_(newset); + sizep->i= size+1; + FOREACHset_((setT *)qhmem.tempstack) { + if (set == oldset) + *(setp-1)= newset; + } + qh_setfree(oldsetp); + }else + newset= qh_setnew(3); + *oldsetp= newset; +} /* setlarger */ + + +/*--------------------------------- + + qh_setlast( ) + return last element of set or NULL (use type conversion) + + notes: + set may be NULL + + design: + return last element +*/ +void *qh_setlast(setT *set) { + int size; + + if (set) { + size= SETsizeaddr_(set)->i; + if (!size) + return SETelem_(set, set->maxsize - 1); + else if (size > 1) + return SETelem_(set, size - 2); + } + return NULL; +} /* setlast */ + + +/*--------------------------------- + + qh_setnew( setsize ) + creates and allocates space for a set + + notes: + setsize means the number of elements (!including the NULL terminator) + use qh_settemp/qh_setfreetemp if set is temporary + + design: + allocate memory for set + roundup memory if small set + initialize as empty set +*/ +setT *qh_setnew(int setsize) { + setT *set; + int sizereceived; /* used !qh_NOmem */ + int size; + void **freelistp; /* used !qh_NOmem */ + + if (!setsize) + setsize++; + size= sizeof(setT) + setsize * SETelemsize; + if (size>0 && size <= qhmem.LASTsize) { + qh_memalloc_(size, freelistp, set, setT); +#ifndef qh_NOmem + sizereceived= qhmem.sizetable[ qhmem.indextable[size]]; + if (sizereceived > size) + setsize += (sizereceived - size)/SETelemsize; +#endif + }else + set= (setT*)qh_memalloc(size); + set->maxsize= setsize; + set->e[setsize].i= 1; + set->e[0].p= NULL; + return(set); +} /* setnew */ + + +/*--------------------------------- + + qh_setnew_delnthsorted( set, size, nth, prepend ) + creates a sorted set not containing nth element + if prepend, the first prepend elements are undefined + + notes: + set must be defined + checks nth + see also: setdelnthsorted + + design: + create new set + setup pointers and allocate room for prepend'ed entries + append head of old set to new set + append tail of old set to new set +*/ +setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend) { + setT *newset; + void **oldp, **newp; + int tailsize= size - nth -1, newsize; + + if (tailsize < 0) { + qh_fprintf(qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + newsize= size-1 + prepend; + newset= qh_setnew(newsize); + newset->e[newset->maxsize].i= newsize+1; /* may be overwritten */ + oldp= SETaddr_(set, void); + newp= SETaddr_(newset, void) + prepend; + switch (nth) { + case 0: + break; + case 1: + *(newp++)= *oldp++; + break; + case 2: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 3: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 4: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + default: + memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize); + newp += nth; + oldp += nth; + break; + } + oldp++; + switch (tailsize) { + case 0: + break; + case 1: + *(newp++)= *oldp++; + break; + case 2: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 3: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 4: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + default: + memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize); + newp += tailsize; + } + *newp= NULL; + return(newset); +} /* setnew_delnthsorted */ + + +/*--------------------------------- + + qh_setprint( fp, string, set ) + print set elements to fp with identifying string + + notes: + never errors +*/ +void qh_setprint(FILE *fp, const char* string, setT *set) { + int size, k; + + if (!set) + qh_fprintf(fp, 9346, "%s set is null\n", string); + else { + SETreturnsize_(set, size); + qh_fprintf(fp, 9347, "%s set=%p maxsize=%d size=%d elems=", + string, set, set->maxsize, size); + if (size > set->maxsize) + size= set->maxsize+1; + for (k=0; k < size; k++) + qh_fprintf(fp, 9348, " %p", set->e[k].p); + qh_fprintf(fp, 9349, "\n"); + } +} /* setprint */ + +/*--------------------------------- + + qh_setreplace( set, oldelem, newelem ) + replaces oldelem in set with newelem + + notes: + errors if oldelem not in the set + newelem may be NULL, but it turns the set into an indexed set (no FOREACH) + + design: + find oldelem + replace with newelem +*/ +void qh_setreplace(setT *set, void *oldelem, void *newelem) { + void **elemp; + + elemp= SETaddr_(set, void); + while (*elemp != oldelem && *elemp) + elemp++; + if (*elemp) + *elemp= newelem; + else { + qh_fprintf(qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n", + oldelem); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } +} /* setreplace */ + + +/*--------------------------------- + + qh_setsize( set ) + returns the size of a set + + notes: + errors if set's maxsize is incorrect + same as SETreturnsize_(set) + same code for qh_setsize [qset.c] and QhullSetBase::count + + design: + determine actual size of set from maxsize +*/ +int qh_setsize(setT *set) { + int size; + setelemT *sizep; + + if (!set) + return(0); + sizep= SETsizeaddr_(set); + if ((size= sizep->i)) { + size--; + if (size > set->maxsize) { + qh_fprintf(qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n", + size, set->maxsize); + qh_setprint(qhmem.ferr, "set: ", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + }else + size= set->maxsize; + return size; +} /* setsize */ + +/*--------------------------------- + + qh_settemp( setsize ) + return a stacked, temporary set of upto setsize elements + + notes: + use settempfree or settempfree_all to release from qhmem.tempstack + see also qh_setnew + + design: + allocate set + append to qhmem.tempstack + +*/ +setT *qh_settemp(int setsize) { + setT *newset; + + newset= qh_setnew(setsize); + qh_setappend(&qhmem.tempstack, newset); + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n", + newset, newset->maxsize, qh_setsize(qhmem.tempstack)); + return newset; +} /* settemp */ + +/*--------------------------------- + + qh_settempfree( set ) + free temporary set at top of qhmem.tempstack + + notes: + nop if set is NULL + errors if set not from previous qh_settemp + + to locate errors: + use 'T2' to find source and then find mis-matching qh_settemp + + design: + check top of qhmem.tempstack + free it +*/ +void qh_settempfree(setT **set) { + setT *stackedset; + + if (!*set) + return; + stackedset= qh_settemppop(); + if (stackedset != *set) { + qh_settemppush(stackedset); + qh_fprintf(qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n", + *set, qh_setsize(*set), qh_setsize(qhmem.tempstack)+1, + stackedset, qh_setsize(stackedset)); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + qh_setfree(set); +} /* settempfree */ + +/*--------------------------------- + + qh_settempfree_all( ) + free all temporary sets in qhmem.tempstack + + design: + for each set in tempstack + free set + free qhmem.tempstack +*/ +void qh_settempfree_all(void) { + setT *set, **setp; + + FOREACHset_(qhmem.tempstack) + qh_setfree(&set); + qh_setfree(&qhmem.tempstack); +} /* settempfree_all */ + +/*--------------------------------- + + qh_settemppop( ) + pop and return temporary set from qhmem.tempstack + + notes: + the returned set is permanent + + design: + pop and check top of qhmem.tempstack +*/ +setT *qh_settemppop(void) { + setT *stackedset; + + stackedset= (setT*)qh_setdellast(qhmem.tempstack); + if (!stackedset) { + qh_fprintf(qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n"); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n", + qh_setsize(qhmem.tempstack)+1, stackedset, qh_setsize(stackedset)); + return stackedset; +} /* settemppop */ + +/*--------------------------------- + + qh_settemppush( set ) + push temporary set unto qhmem.tempstack (makes it temporary) + + notes: + duplicates settemp() for tracing + + design: + append set to tempstack +*/ +void qh_settemppush(setT *set) { + if (!set) { + fprintf (qhmem.ferr, "qhull error (qh_settemppush): can not push a NULL temp\n"); + qh_errexit (qhmem_ERRqhull, NULL, NULL); + } + qh_setappend(&qhmem.tempstack, set); + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n", + qh_setsize(qhmem.tempstack), set, qh_setsize(set)); +} /* settemppush */ + + +/*--------------------------------- + + qh_settruncate( set, size ) + truncate set to size elements + + notes: + set must be defined + + see: + SETtruncate_ + + design: + check size + update actual size of set +*/ +void qh_settruncate(setT *set, int size) { + + if (size < 0 || size > set->maxsize) { + qh_fprintf(qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + set->e[set->maxsize].i= size+1; /* maybe overwritten */ + set->e[size].p= NULL; +} /* settruncate */ + +/*--------------------------------- + + qh_setunique( set, elem ) + add elem to unsorted set unless it is already in set + + notes: + returns 1 if it is appended + + design: + if elem not in set + append elem to set +*/ +int qh_setunique(setT **set, void *elem) { + + if (!qh_setin(*set, elem)) { + qh_setappend(set, elem); + return 1; + } + return 0; +} /* setunique */ + +/*--------------------------------- + + qh_setzero( set, index, size ) + zero elements from index on + set actual size of set to size + + notes: + set must be defined + the set becomes an indexed set (can not use FOREACH...) + + see also: + qh_settruncate + + design: + check index and size + update actual size + zero elements starting at e[index] +*/ +void qh_setzero(setT *set, int idx, int size) { + int count; + + if (idx < 0 || idx >= size || size > set->maxsize) { + qh_fprintf(qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + set->e[set->maxsize].i= size+1; /* may be overwritten */ + count= size - idx + 1; /* +1 for NULL terminator */ + memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize); +} /* setzero */ diff --git a/extern/qhull/qset.h b/extern/qhull/qset.h new file mode 100644 index 000000000000..759b501a8017 --- /dev/null +++ b/extern/qhull/qset.h @@ -0,0 +1,488 @@ +/* --------------------------------- + + qset.h + header file for qset.c that implements set + + see qh-set.htm and qset.c + + only uses mem.c, malloc/free + + for error handling, writes message and calls + qh_errexit(qhmem_ERRqhull, NULL, NULL); + + set operations satisfy the following properties: + - sets have a max size, the actual size (if different) is stored at the end + - every set is NULL terminated + - sets may be sorted or unsorted, the caller must distinguish this + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/qset.h#4 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#ifndef qhDEFset +#define qhDEFset 1 + +#include+ +/*================= -structures- ===============*/ + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* a set is a sorted or unsorted array of pointers */ +#endif + +/*------------------------------------------ + +setT + a set or list of pointers with maximum size and actual size. + +variations: + unsorted, unique -- a list of unique pointers with NULL terminator + user guarantees uniqueness + sorted -- a sorted list of unique pointers with NULL terminator + qset.c guarantees uniqueness + unsorted -- a list of pointers terminated with NULL + indexed -- an array of pointers with NULL elements + +structure for set of n elements: + + -------------- + | maxsize + -------------- + | e[0] - a pointer, may be NULL for indexed sets + -------------- + | e[1] + + -------------- + | ... + -------------- + | e[n-1] + -------------- + | e[n] = NULL + -------------- + | ... + -------------- + | e[maxsize] - n+1 or NULL (determines actual size of set) + -------------- + +*/ + +/*-- setelemT -- internal type to allow both pointers and indices +*/ +typedef union setelemT setelemT; +union setelemT { + void *p; + int i; /* integer used for e[maxSize] */ +}; + +struct setT { + int maxsize; /* maximum number of elements (except NULL) */ + setelemT e[1]; /* array of pointers, tail is NULL */ + /* last slot (unless NULL) is actual size+1 + e[maxsize]==NULL or e[e[maxsize]-1]==NULL */ + /* this may generate a warning since e[] contains + maxsize elements */ +}; + +/*=========== -constants- =========================*/ + +/*------------------------------------- + + SETelemsize + size of a set element in bytes +*/ +#define SETelemsize ((int)sizeof(setelemT)) + + +/*=========== -macros- =========================*/ + +/*------------------------------------- + + FOREACHsetelement_(type, set, variable) + define FOREACH iterator + + declare: + assumes *variable and **variablep are declared + no space in "variable)" [DEC Alpha cc compiler] + + each iteration: + variable is set element + variablep is one beyond variable. + + to repeat an element: + variablep--; / *repeat* / + + at exit: + variable is NULL at end of loop + + example: + #define FOREACHfacet_( facets ) FOREACHsetelement_( facetT, facets, facet ) + + notes: + use FOREACHsetelement_i_() if need index or include NULLs + + WARNING: + nested loops can't use the same variable (define another FOREACH) + + needs braces if nested inside another FOREACH + this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} ) +*/ +#define FOREACHsetelement_(type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##p= (type **)&((set)->e[0].p); \ + (variable= *variable##p++);) + +/*------------------------------------------ + + FOREACHsetelement_i_(type, set, variable) + define indexed FOREACH iterator + + declare: + type *variable, variable_n, variable_i; + + each iteration: + variable is set element, may be NULL + variable_i is index, variable_n is qh_setsize() + + to repeat an element: + variable_i--; variable_n-- repeats for deleted element + + at exit: + variable==NULL and variable_i==variable_n + + example: + #define FOREACHfacet_i_( facets ) FOREACHsetelement_i_( facetT, facets, facet ) + + WARNING: + nested loops can't use the same variable (define another FOREACH) + + needs braces if nested inside another FOREACH + this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} ) +*/ +#define FOREACHsetelement_i_(type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##_i= 0, variable= (type *)((set)->e[0].p), \ + variable##_n= qh_setsize(set);\ + variable##_i < variable##_n;\ + variable= (type *)((set)->e[++variable##_i].p) ) + +/*---------------------------------------- + + FOREACHsetelementreverse_(type, set, variable)- + define FOREACH iterator in reverse order + + declare: + assumes *variable and **variablep are declared + also declare 'int variabletemp' + + each iteration: + variable is set element + + to repeat an element: + variabletemp++; / *repeat* / + + at exit: + variable is NULL + + example: + #define FOREACHvertexreverse_( vertices ) FOREACHsetelementreverse_( vertexT, vertices, vertex ) + + notes: + use FOREACHsetelementreverse12_() to reverse first two elements + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHsetelementreverse_(type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##temp= qh_setsize(set)-1, variable= qh_setlast(set);\ + variable; variable= \ + ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL)) + +/*------------------------------------- + + FOREACHsetelementreverse12_(type, set, variable)- + define FOREACH iterator with e[1] and e[0] reversed + + declare: + assumes *variable and **variablep are declared + + each iteration: + variable is set element + variablep is one after variable. + + to repeat an element: + variablep--; / *repeat* / + + at exit: + variable is NULL at end of loop + + example + #define FOREACHvertexreverse12_( vertices ) FOREACHsetelementreverse12_( vertexT, vertices, vertex ) + + notes: + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHsetelementreverse12_(type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##p= (type **)&((set)->e[1].p); \ + (variable= *variable##p); \ + variable##p == ((type **)&((set)->e[0].p))?variable##p += 2: \ + (variable##p == ((type **)&((set)->e[1].p))?variable##p--:variable##p++)) + +/*------------------------------------- + + FOREACHelem_( set )- + iterate elements in a set + + declare: + void *elem, *elemp; + + each iteration: + elem is set element + elemp is one beyond + + to repeat an element: + elemp--; / *repeat* / + + at exit: + elem == NULL at end of loop + + example: + FOREACHelem_(set) { + + notes: + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHelem_(set) FOREACHsetelement_(void, set, elem) + +/*------------------------------------- + + FOREACHset_( set )- + iterate a set of sets + + declare: + setT *set, **setp; + + each iteration: + set is set element + setp is one beyond + + to repeat an element: + setp--; / *repeat* / + + at exit: + set == NULL at end of loop + + example + FOREACHset_(sets) { + + notes: + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHset_(sets) FOREACHsetelement_(setT, sets, set) + +/*------------------------------------------- + + SETindex_( set, elem ) + return index of elem in set + + notes: + for use with FOREACH iteration + WARN64 -- Maximum set size is 2G + + example: + i= SETindex_(ridges, ridge) +*/ +#define SETindex_(set, elem) ((int)((void **)elem##p - (void **)&(set)->e[1].p)) + +/*----------------------------------------- + + SETref_( elem ) + l.h.s. for modifying the current element in a FOREACH iteration + + example: + SETref_(ridge)= anotherridge; +*/ +#define SETref_(elem) (elem##p[-1]) + +/*----------------------------------------- + + SETelem_(set, n) + return the n'th element of set + + notes: + assumes that n is valid [0..size] and that set is defined + use SETelemt_() for type cast +*/ +#define SETelem_(set, n) ((set)->e[n].p) + +/*----------------------------------------- + + SETelemt_(set, n, type) + return the n'th element of set as a type + + notes: + assumes that n is valid [0..size] and that set is defined +*/ +#define SETelemt_(set, n, type) ((type*)((set)->e[n].p)) + +/*----------------------------------------- + + SETelemaddr_(set, n, type) + return address of the n'th element of a set + + notes: + assumes that n is valid [0..size] and set is defined +*/ +#define SETelemaddr_(set, n, type) ((type **)(&((set)->e[n].p))) + +/*----------------------------------------- + + SETfirst_(set) + return first element of set + +*/ +#define SETfirst_(set) ((set)->e[0].p) + +/*----------------------------------------- + + SETfirstt_(set, type) + return first element of set as a type + +*/ +#define SETfirstt_(set, type) ((type*)((set)->e[0].p)) + +/*----------------------------------------- + + SETsecond_(set) + return second element of set + +*/ +#define SETsecond_(set) ((set)->e[1].p) + +/*----------------------------------------- + + SETsecondt_(set, type) + return second element of set as a type +*/ +#define SETsecondt_(set, type) ((type*)((set)->e[1].p)) + +/*----------------------------------------- + + SETaddr_(set, type) + return address of set's elements +*/ +#define SETaddr_(set,type) ((type **)(&((set)->e[0].p))) + +/*----------------------------------------- + + SETreturnsize_(set, size) + return size of a set + + notes: + set must be defined + use qh_setsize(set) unless speed is critical +*/ +#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize)) + +/*----------------------------------------- + + SETempty_(set) + return true(1) if set is empty + + notes: + set may be NULL +*/ +#define SETempty_(set) (!set || (SETfirst_(set) ? 0 : 1)) + +/*--------------------------------- + + SETsizeaddr_(set) + return pointer to 'actual size+1' of set (set CANNOT be NULL!!) + Its type is setelemT* for strict aliasing + All SETelemaddr_ must be cast to setelemT + + + notes: + *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL +*/ +#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize])) + +/*----------------------------------------- + + SETtruncate_(set, size) + truncate set to size + + see: + qh_settruncate() + +*/ +#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \ + set->e[size].p= NULL;} + +/*======= prototypes in alphabetical order ============*/ + +void qh_setaddsorted(setT **setp, void *elem); +void qh_setaddnth(setT **setp, int nth, void *newelem); +void qh_setappend(setT **setp, void *elem); +void qh_setappend_set(setT **setp, setT *setA); +void qh_setappend2ndlast(setT **setp, void *elem); +void qh_setcheck(setT *set, const char *tname, unsigned id); +void qh_setcompact(setT *set); +setT *qh_setcopy(setT *set, int extra); +void *qh_setdel(setT *set, void *elem); +void *qh_setdellast(setT *set); +void *qh_setdelnth(setT *set, int nth); +void *qh_setdelnthsorted(setT *set, int nth); +void *qh_setdelsorted(setT *set, void *newelem); +setT *qh_setduplicate( setT *set, int elemsize); +int qh_setequal(setT *setA, setT *setB); +int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB); +int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB); +void **qh_setendpointer(setT *set); +void qh_setfree(setT **set); +void qh_setfree2( setT **setp, int elemsize); +void qh_setfreelong(setT **set); +int qh_setin(setT *set, void *setelem); +int qh_setindex(setT *set, void *setelem); +void qh_setlarger(setT **setp); +void *qh_setlast(setT *set); +setT *qh_setnew(int size); +setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend); +void qh_setprint(FILE *fp, const char* string, setT *set); +void qh_setreplace(setT *set, void *oldelem, void *newelem); +int qh_setsize(setT *set); +setT *qh_settemp(int setsize); +void qh_settempfree(setT **set); +void qh_settempfree_all(void); +setT *qh_settemppop(void); +void qh_settemppush(setT *set); +void qh_settruncate(setT *set, int size); +int qh_setunique(setT **set, void *elem); +void qh_setzero(setT *set, int idx, int size); + + +#endif /* qhDEFset */ diff --git a/extern/qhull/random.c b/extern/qhull/random.c new file mode 100644 index 000000000000..21b15025afd6 --- /dev/null +++ b/extern/qhull/random.c @@ -0,0 +1,243 @@ +/* --------------------------------- + + random.c -- utilities + Park & Miller's minimimal standard random number generator + argc/argv conversion + + Used by rbox. Do not use 'qh' +*/ + +#include "libqhull.h" +#include+#include +#include + +#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +#pragma warning( disable : 4706) /* assignment within conditional function */ +#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */ +#endif + +/*--------------------------------- + + qh_argv_to_command( argc, argv, command, max_size ) + + build command from argc/argv + max_size is at least + + returns: + a space-delimited string of options (just as typed) + returns false if max_size is too short + + notes: + silently removes + makes option string easy to input and output + matches qh_argv_to_command_size() + + argc may be 0 +*/ +int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) { + int i, remaining; + char *s; + *command= '\0'; /* max_size > 0 */ + + if (argc) { + if ((s= strrchr( argv[0], '\\')) /* get filename w/o .exe extension */ + || (s= strrchr( argv[0], '/'))) + s++; + else + s= argv[0]; + if ((int)strlen(s) < max_size) /* WARN64 */ + strcpy(command, s); + else + goto error_argv; + if ((s= strstr(command, ".EXE")) + || (s= strstr(command, ".exe"))) + *s= '\0'; + } + for (i=1; i < argc; i++) { + s= argv[i]; + remaining= max_size - (int)strlen(command) - (int)strlen(s) - 2; /* WARN64 */ + if (!*s || strchr(s, ' ')) { + char *t= command + strlen(command); + remaining -= 2; + if (remaining < 0) { + goto error_argv; + } + *t++= ' '; + *t++= '"'; + while (*s) { + if (*s == '"') { + if (--remaining < 0) + goto error_argv; + *t++= '\\'; + } + *t++= *s++; + } + *t++= '"'; + *t= '\0'; + }else if (remaining < 0) { + goto error_argv; + }else + strcat(command, " "); + strcat(command, s); + } + return 1; + +error_argv: + return 0; +} /* argv_to_command */ + +/*--------------------------------- + +qh_argv_to_command_size( argc, argv ) + + return size to allocate for qh_argv_to_command() + +notes: + argc may be 0 + actual size is usually shorter +*/ +int qh_argv_to_command_size(int argc, char *argv[]) { + unsigned int count= 1; /* null-terminator if argc==0 */ + int i; + char *s; + + for (i=0; i 0 && strchr(argv[i], ' ')) { + count += 2; /* quote delimiters */ + for (s=argv[i]; *s; s++) { + if (*s == '"') { + count++; + } + } + } + } + return count; +} /* argv_to_command_size */ + +/*--------------------------------- + + qh_rand() + qh_srand( seed ) + generate pseudo-random number between 1 and 2^31 -2 + + notes: + For qhull and rbox, called from qh_RANDOMint(),etc. [user.h] + + From Park & Miller's minimal standard random number generator + Communications of the ACM, 31:1192-1201, 1988. + Does not use 0 or 2^31 -1 + this is silently enforced by qh_srand() + Can make 'Rn' much faster by moving qh_rand to qh_distplane +*/ + +/* Global variables and constants */ + +int qh_rand_seed= 1; /* define as global variable instead of using qh */ + +#define qh_rand_a 16807 +#define qh_rand_m 2147483647 +#define qh_rand_q 127773 /* m div a */ +#define qh_rand_r 2836 /* m mod a */ + +int qh_rand( void) { + int lo, hi, test; + int seed = qh_rand_seed; + + hi = seed / qh_rand_q; /* seed div q */ + lo = seed % qh_rand_q; /* seed mod q */ + test = qh_rand_a * lo - qh_rand_r * hi; + if (test > 0) + seed= test; + else + seed= test + qh_rand_m; + qh_rand_seed= seed; + /* seed = seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax; for testing */ + /* seed = qh_RANDOMmax; for testing */ + return seed; +} /* rand */ + +void qh_srand( int seed) { + if (seed < 1) + qh_rand_seed= 1; + else if (seed >= qh_rand_m) + qh_rand_seed= qh_rand_m - 1; + else + qh_rand_seed= seed; +} /* qh_srand */ + +/*--------------------------------- + +qh_randomfactor( scale, offset ) +return a random factor r * scale + offset + +notes: +qh.RANDOMa/b are defined in global.c +*/ +realT qh_randomfactor(realT scale, realT offset) { + realT randr; + + randr= qh_RANDOMint; + return randr * scale + offset; +} /* randomfactor */ + +/*--------------------------------- + +qh_randommatrix( buffer, dim, rows ) +generate a random dim X dim matrix in range [-1,1] +assumes buffer is [dim+1, dim] + +returns: +sets buffer to random numbers +sets rows to rows of buffer +sets row[dim] as scratch row +*/ +void qh_randommatrix(realT *buffer, int dim, realT **rows) { + int i, k; + realT **rowi, *coord, realr; + + coord= buffer; + rowi= rows; + for (i=0; i < dim; i++) { + *(rowi++)= coord; + for (k=0; k < dim; k++) { + realr= qh_RANDOMint; + *(coord++)= 2.0 * realr/(qh_RANDOMmax+1) - 1.0; + } + } + *rowi= coord; +} /* randommatrix */ + +/*--------------------------------- + + qh_strtol( s, endp) qh_strtod( s, endp) + internal versions of strtol() and strtod() + does not skip trailing spaces + notes: + some implementations of strtol()/strtod() skip trailing spaces +*/ +double qh_strtod(const char *s, char **endp) { + double result; + + result= strtod(s, endp); + if (s < (*endp) && (*endp)[-1] == ' ') + (*endp)--; + return result; +} /* strtod */ + +int qh_strtol(const char *s, char **endp) { + int result; + + result= (int) strtol(s, endp, 10); /* WARN64 */ + if (s< (*endp) && (*endp)[-1] == ' ') + (*endp)--; + return result; +} /* strtol */ diff --git a/extern/qhull/random.h b/extern/qhull/random.h new file mode 100644 index 000000000000..e1e29270419a --- /dev/null +++ b/extern/qhull/random.h @@ -0,0 +1,31 @@ +/* --------------------------------- + + random.h + header file for random routines + + see qh-geom.htm and random.c + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/random.h#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#ifndef qhDEFrandom +#define qhDEFrandom 1 + +#include "libqhull.h" + +/*============= prototypes in alphabetical order ======= */ + + +int qh_argv_to_command(int argc, char *argv[], char* command, int max_size); +int qh_argv_to_command_size(int argc, char *argv[]); +int qh_rand( void); +void qh_srand( int seed); +realT qh_randomfactor(realT scale, realT offset); +void qh_randommatrix(realT *buffer, int dim, realT **row); +int qh_strtol(const char *s, char **endp); +double qh_strtod(const char *s, char **endp); + +#endif /* qhDEFrandom */ diff --git a/extern/qhull/rboxlib.c b/extern/qhull/rboxlib.c new file mode 100644 index 000000000000..0122563f7062 --- /dev/null +++ b/extern/qhull/rboxlib.c @@ -0,0 +1,794 @@ +/*--------------------------------- + + rboxlib.c + Generate input points + + notes: + For documentation, see prompt[] of rbox.c + 50 points generated for 'rbox D4' + + WARNING: + incorrect range if qh_RANDOMmax is defined wrong (user.h) +*/ + +#include "random.h" +#include "libqhull.h" + +#include+#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER /* Microsoft Visual C++ */ +#pragma warning( disable : 4706) /* assignment within conditional expression. */ +#pragma warning( disable : 4996) /* this function (strncat,sprintf,strcpy) or variable may be unsafe. */ +#endif + +#define MAXdim 200 +#define PI 3.1415926535897932384 + +/* ------------------------------ prototypes ----------------*/ +int roundi( double a); +void out1( double a); +void out2n( double a, double b); +void out3n( double a, double b, double c); + +void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ); +void qh_free(void *mem); +void *qh_malloc(size_t size); +int qh_rand( void); +void qh_srand( int seed); + + +/* ------------------------------ globals -------------------*/ + +/* No state is carried between rbox requests */ +typedef struct rboxT rboxT; +struct rboxT { + FILE *fout; + FILE *ferr; + int isinteger; + double out_offset; + jmp_buf errexit; /* exit label for rboxpoints, defined by setjmp(), called by qh_errexit_rbox() */ +}; + + +int rbox_inuse= 0; +rboxT rbox; + +/*--------------------------------- + + qh_rboxpoints( fout, ferr, rbox_command ) + Generate points to fout according to rbox options + Report errors on ferr + + returns: + 0 (qh_ERRnone) on success + 1 (qh_ERRinput) on input error + 4 (qh_ERRmem) on memory error + 5 (qh_ERRqhull) on internal error + + notes: + To avoid stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user.c) + Rbox is not multithreaded. + + design: + Straight line code (consider defining a struct and functions): + + Parse arguments into variables + Determine the number of points + Generate the points +*/ +int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { + int i,j,k; + int gendim; + int cubesize, diamondsize, seed=0, count, apex; + int dim=3 , numpoints= 0, totpoints, addpoints=0; + int issphere=0, isaxis=0, iscdd= 0, islens= 0, isregular=0, iswidth=0, addcube=0; + int isgap=0, isspiral=0, NOcommand= 0, adddiamond=0; + int israndom=0, istime=0; + int isbox=0, issimplex=0, issimplex2=0, ismesh=0; + double width=0.0, gap=0.0, radius= 0.0; + double coord[MAXdim], offset, meshm=3.0, meshn=4.0, meshr=5.0; + double *simplex= NULL, *simplexp; + int nthroot, mult[MAXdim]; + double norm, factor, randr, rangap, lensangle= 0, lensbase= 1; + double anglediff, angle, x, y, cube= 0.0, diamond= 0.0; + double box= qh_DEFAULTbox; /* scale all numbers before output */ + double randmax= qh_RANDOMmax; + char command[200], seedbuf[200]; + char *s= command, *t, *first_point= NULL; + time_t timedata; + int exitcode; + + if (rbox_inuse) { + qh_fprintf_rbox(rbox.ferr, 6188, "rbox error: rbox in use by another process. Please lock calls to rbox.\n"); + return qh_ERRqhull; + } + rbox_inuse= True; + rbox.ferr= ferr; + rbox.fout= fout; + + exitcode= setjmp(rbox.errexit); + if (exitcode) { + /* same code for error exit and normal return */ + if (simplex) + qh_free(simplex); + rbox_inuse= False; + return exitcode; + } + + *command= '\0'; + strncat(command, rbox_command, sizeof(command)-strlen(command)-1); + + while (*s && !isspace(*s)) /* skip program name */ + s++; + while (*s) { + while (*s && isspace(*s)) + s++; + if (*s == '-') + s++; + if (!*s) + break; + if (isdigit(*s)) { + numpoints= qh_strtol(s, &s); + continue; + } + /* ============= read flags =============== */ + switch (*s++) { + case 'c': + addcube= 1; + t= s; + while (isspace(*t)) + t++; + if (*t == 'G') + cube= qh_strtod(++t, &s); + break; + case 'd': + adddiamond= 1; + t= s; + while (isspace(*t)) + t++; + if (*t == 'G') + diamond= qh_strtod(++t, &s); + break; + case 'h': + iscdd= 1; + break; + case 'l': + isspiral= 1; + break; + case 'n': + NOcommand= 1; + break; + case 'r': + isregular= 1; + break; + case 's': + issphere= 1; + break; + case 't': + istime= 1; + if (isdigit(*s)) { + seed= qh_strtol(s, &s); + israndom= 0; + }else + israndom= 1; + break; + case 'x': + issimplex= 1; + break; + case 'y': + issimplex2= 1; + break; + case 'z': + rbox.isinteger= 1; + break; + case 'B': + box= qh_strtod(s, &s); + isbox= 1; + break; + case 'D': + dim= qh_strtol(s, &s); + if (dim < 1 + || dim > MAXdim) { + qh_fprintf_rbox(rbox.ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim); + qh_errexit_rbox(qh_ERRinput); + } + break; + case 'G': + if (isdigit(*s)) + gap= qh_strtod(s, &s); + else + gap= 0.5; + isgap= 1; + break; + case 'L': + if (isdigit(*s)) + radius= qh_strtod(s, &s); + else + radius= 10; + islens= 1; + break; + case 'M': + ismesh= 1; + if (*s) + meshn= qh_strtod(s, &s); + if (*s == ',') { + ++s; + meshm= qh_strtod(s, &s); + }else + meshm= 0.0; + if (*s == ',') { + ++s; + meshr= qh_strtod(s, &s); + }else + meshr= sqrt(meshn*meshn + meshm*meshm); + if (*s && !isspace(*s)) { + qh_fprintf_rbox(rbox.ferr, 7069, "rbox warning: assuming 'M3,4,5' since mesh args are not integers or reals\n"); + meshn= 3.0, meshm=4.0, meshr=5.0; + } + break; + case 'O': + rbox.out_offset= qh_strtod(s, &s); + break; + case 'P': + if (!first_point) + first_point= s-1; + addpoints++; + while (*s && !isspace(*s)) /* read points later */ + s++; + break; + case 'W': + width= qh_strtod(s, &s); + iswidth= 1; + break; + case 'Z': + if (isdigit(*s)) + radius= qh_strtod(s, &s); + else + radius= 1.0; + isaxis= 1; + break; + default: + qh_fprintf_rbox(rbox.ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s); + qh_errexit_rbox(qh_ERRinput); + } + if (*s && !isspace(*s)) { + qh_fprintf_rbox(rbox.ferr, 7071, "rbox error: missing space between flags at %s.\n", s); + qh_errexit_rbox(qh_ERRinput); + } + } + + /* ============= defaults, constants, and sizes =============== */ + if (rbox.isinteger && !isbox) + box= qh_DEFAULTzbox; + if (addcube) { + cubesize= (int)floor(ldexp(1.0,dim)+0.5); + if (cube == 0.0) + cube= box; + }else + cubesize= 0; + if (adddiamond) { + diamondsize= 2*dim; + if (diamond == 0.0) + diamond= box; + }else + diamondsize= 0; + if (islens) { + if (isaxis) { + qh_fprintf_rbox(rbox.ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n"); + qh_errexit_rbox(qh_ERRinput); + } + if (radius <= 1.0) { + qh_fprintf_rbox(rbox.ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n", + radius); + qh_errexit_rbox(qh_ERRinput); + } + lensangle= asin(1.0/radius); + lensbase= radius * cos(lensangle); + } + + if (!numpoints) { + if (issimplex2) + ; /* ok */ + else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) { + qh_fprintf_rbox(rbox.ferr, 6192, "rbox error: missing count\n"); + qh_errexit_rbox(qh_ERRinput); + }else if (adddiamond + addcube + addpoints) + ; /* ok */ + else { + numpoints= 50; /* ./rbox D4 is the test case */ + issphere= 1; + } + } + if ((issimplex + islens + isspiral + ismesh > 1) + || (issimplex + issphere + isspiral + ismesh > 1)) { + qh_fprintf_rbox(rbox.ferr, 6193, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n"); + qh_errexit_rbox(qh_ERRinput); + } + + /* ============= print header with total points =============== */ + if (issimplex || ismesh) + totpoints= numpoints; + else if (issimplex2) + totpoints= numpoints+dim+1; + else if (isregular) { + totpoints= numpoints; + if (dim == 2) { + if (islens) + totpoints += numpoints - 2; + }else if (dim == 3) { + if (islens) + totpoints += 2 * numpoints; + else if (isgap) + totpoints += 1 + numpoints; + else + totpoints += 2; + } + }else + totpoints= numpoints + isaxis; + totpoints += cubesize + diamondsize + addpoints; + + /* ============= seed randoms =============== */ + if (istime == 0) { + for (s=command; *s; s++) { + if (issimplex2 && *s == 'y') /* make 'y' same seed as 'x' */ + i= 'x'; + else + i= *s; + seed= 11*seed + i; + } + }else if (israndom) { + seed= (int)time(&timedata); + sprintf(seedbuf, " t%d", seed); /* appends an extra t, not worth removing */ + strncat(command, seedbuf, sizeof(command)-strlen(command)-1); + t= strstr(command, " t "); + if (t) + strcpy(t+1, t+3); /* remove " t " */ + } /* else, seed explicitly set to n */ + qh_RANDOMseed_(seed); + + /* ============= print header =============== */ + + if (iscdd) + qh_fprintf_rbox(rbox.fout, 9391, "%s\nbegin\n %d %d %s\n", + NOcommand ? "" : command, + totpoints, dim+1, + rbox.isinteger ? "integer" : "real"); + else if (NOcommand) + qh_fprintf_rbox(rbox.fout, 9392, "%d\n%d\n", dim, totpoints); + else + qh_fprintf_rbox(rbox.fout, 9393, "%d %s\n%d\n", dim, command, totpoints); + + /* ============= explicit points =============== */ + if ((s= first_point)) { + while (s && *s) { /* 'P' */ + count= 0; + if (iscdd) + out1( 1.0); + while (*++s) { + out1( qh_strtod(s, &s)); + count++; + if (isspace(*s) || !*s) + break; + if (*s != ',') { + qh_fprintf_rbox(rbox.ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s); + qh_errexit_rbox(qh_ERRinput); + } + } + if (count < dim) { + for (k=dim-count; k--; ) + out1( 0.0); + }else if (count > dim) { + qh_fprintf_rbox(rbox.ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n", + count, dim, s); + qh_errexit_rbox(qh_ERRinput); + } + qh_fprintf_rbox(rbox.fout, 9394, "\n"); + while ((s= strchr(s, 'P'))) { + if (isspace(s[-1])) + break; + } + } + } + + /* ============= simplex distribution =============== */ + if (issimplex+issimplex2) { + if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) { + qh_fprintf_rbox(rbox.ferr, 6196, "rbox error: insufficient memory for simplex\n"); + qh_errexit_rbox(qh_ERRmem); /* qh_ERRmem */ + } + simplexp= simplex; + if (isregular) { + for (i=0; i randmax/2) + coord[dim-1]= -coord[dim-1]; + /* ============= project 'Wn' point toward boundary =============== */ + }else if (iswidth && !issphere) { + j= qh_RANDOMint % gendim; + if (coord[j] < 0) + coord[j]= -1.0 - coord[j] * width; + else + coord[j]= 1.0 - coord[j] * width; + } + /* ============= write point =============== */ + if (iscdd) + out1( 1.0); + for (k=0; k < dim; k++) + out1( coord[k] * box); + qh_fprintf_rbox(rbox.fout, 9399, "\n"); + } + } + + /* ============= write cube vertices =============== */ + if (addcube) { + for (j=0; j =0; k--) { + if (j & ( 1 << k)) + out1( cube); + else + out1( -cube); + } + qh_fprintf_rbox(rbox.fout, 9400, "\n"); + } + } + + /* ============= write diamond vertices =============== */ + if (adddiamond) { + for (j=0; j =0; k--) { + if (j/2 != k) + out1( 0.0); + else if (j & 0x1) + out1( diamond); + else + out1( -diamond); + } + qh_fprintf_rbox(rbox.fout, 9401, "\n"); + } + } + + if (iscdd) + qh_fprintf_rbox(rbox.fout, 9402, "end\nhull\n"); + + /* same code for error exit and normal return */ + if (simplex) + qh_free(simplex); + rbox_inuse= False; + return qh_ERRnone; +} /* rboxpoints */ + +/*------------------------------------------------ +outxxx - output functions +*/ +int roundi( double a) { + if (a < 0.0) { + if (a - 0.5 < INT_MIN) { + qh_fprintf_rbox(rbox.ferr, 6200, "rbox input error: negative coordinate %2.2g is too large. Reduce 'Bn'\n", a); + qh_errexit_rbox(qh_ERRinput); + } + return (int)(a - 0.5); + }else { + if (a + 0.5 > INT_MAX) { + qh_fprintf_rbox(rbox.ferr, 6201, "rbox input error: coordinate %2.2g is too large. Reduce 'Bn'\n", a); + qh_errexit_rbox(qh_ERRinput); + } + return (int)(a + 0.5); + } +} /* roundi */ + +void out1(double a) { + + if (rbox.isinteger) + qh_fprintf_rbox(rbox.fout, 9403, "%d ", roundi( a+rbox.out_offset)); + else + qh_fprintf_rbox(rbox.fout, 9404, qh_REAL_1, a+rbox.out_offset); +} /* out1 */ + +void out2n( double a, double b) { + + if (rbox.isinteger) + qh_fprintf_rbox(rbox.fout, 9405, "%d %d\n", roundi(a+rbox.out_offset), roundi(b+rbox.out_offset)); + else + qh_fprintf_rbox(rbox.fout, 9406, qh_REAL_2n, a+rbox.out_offset, b+rbox.out_offset); +} /* out2n */ + +void out3n( double a, double b, double c) { + + if (rbox.isinteger) + qh_fprintf_rbox(rbox.fout, 9407, "%d %d %d\n", roundi(a+rbox.out_offset), roundi(b+rbox.out_offset), roundi(c+rbox.out_offset)); + else + qh_fprintf_rbox(rbox.fout, 9408, qh_REAL_3n, a+rbox.out_offset, b+rbox.out_offset, c+rbox.out_offset); +} /* out3n */ + +void qh_errexit_rbox(int exitcode) +{ + longjmp(rbox.errexit, exitcode); +} /* rbox_errexit */ diff --git a/extern/qhull/stat.c b/extern/qhull/stat.c new file mode 100644 index 000000000000..50ed9a291568 --- /dev/null +++ b/extern/qhull/stat.c @@ -0,0 +1,713 @@ +/* --------------------------------- + + stat.c + contains all statistics that are collected for qhull + + see qh-stat.htm and stat.h + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/stat.c#3 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*============ global data structure ==========*/ + +#if qh_QHpointer +qhstatT *qh_qhstat=NULL; /* global data structure */ +#else +qhstatT qh_qhstat; /* add "={0}" if this causes a compiler error */ +#endif + +/*========== functions in alphabetic order ================*/ + +/*--------------------------------- + + qh_allstatA() + define statistics in groups of 20 + + notes: + (otherwise, 'gcc -O2' uses too much memory) + uses qhstat.next +*/ +void qh_allstatA(void) { + + /* zdef_(type,name,doc,average) */ + zzdef_(zdoc, Zdoc2, "precision statistics", -1); + zdef_(zinc, Znewvertex, NULL, -1); + zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex); + zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1); + zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1); + zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1); + zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1); + + qhstat precision= qhstat next; /* call qh_precision for each of these */ + zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1); + zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1); + zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1); + zzdef_(zinc, Zflippedfacets, "flipped facets", -1); + zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1); + zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1); + zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1); + zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1); + zzdef_(zinc, Zback0, "zero divisors during back substitute", -1); + zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1); + zzdef_(zinc, Zmultiridge, "ridges with multiple neighbors", -1); +} +void qh_allstatB(void) { + zzdef_(zdoc, Zdoc1, "summary information", -1); + zdef_(zinc, Zvertices, "number of vertices in output", -1); + zdef_(zinc, Znumfacets, "number of facets in output", -1); + zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1); + zdef_(zinc, Znowsimplicial, "number of simplicial facets that were merged", -1); + zdef_(zinc, Znumridges, "number of ridges in output", -1); + zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets); + zdef_(zmax, Zmaxridges, "maximum number of ridges", -1); + zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets); + zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1); + zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets); + zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1); + zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices); + zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1); + zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1); + zdef_(zinc, Ztotvertices, "vertices created altogether", -1); + zzdef_(zinc, Zsetplane, "facets created altogether", -1); + zdef_(zinc, Ztotridges, "ridges created altogether", -1); + zdef_(zinc, Zpostfacets, "facets before post merge", -1); + zdef_(zadd, Znummergetot, "average merges per facet(at most 511)", Znumfacets); + zdef_(zmax, Znummergemax, " maximum merges for a facet(at most 511)", -1); + zdef_(zinc, Zangle, NULL, -1); + zdef_(wadd, Wangle, "average angle(cosine) of facet normals for all ridges", Zangle); + zdef_(wmax, Wanglemax, " maximum angle(cosine) of facet normals across a ridge", -1); + zdef_(wmin, Wanglemin, " minimum angle(cosine) of facet normals across a ridge", -1); + zdef_(wadd, Wareatot, "total area of facets", -1); + zdef_(wmax, Wareamax, " maximum facet area", -1); + zdef_(wmin, Wareamin, " minimum facet area", -1); +} +void qh_allstatC(void) { + zdef_(zdoc, Zdoc9, "build hull statistics", -1); + zzdef_(zinc, Zprocessed, "points processed", -1); + zzdef_(zinc, Zretry, "retries due to precision problems", -1); + zdef_(wmax, Wretrymax, " max. random joggle", -1); + zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1); + zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed); + zdef_(zinc, Zinsidevisible, " ave. visible facets without an horizon neighbor", Zprocessed); + zdef_(zadd, Zvisfacettot, " ave. facets deleted per iteration", Zprocessed); + zdef_(zmax, Zvisfacetmax, " maximum", -1); + zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed); + zdef_(zmax, Zvisvertexmax, " maximum", -1); + zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed); + zdef_(zadd, Znewfacettot, "ave. new or merged facets per iteration", Zprocessed); + zdef_(zmax, Znewfacetmax, " maximum(includes initial simplex)", -1); + zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed); + zdef_(wadd, Wnewbalance2, " standard deviation", -1); + zdef_(wadd, Wpbalance, "average partition balance", Zpbalance); + zdef_(wadd, Wpbalance2, " standard deviation", -1); + zdef_(zinc, Zpbalance, " number of trials", -1); + zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1); + zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1); + zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1); + zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1); + zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1); + zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1); + zdef_(zinc, Zgoodfacet, "good facets found", -1); + zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1); + zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1); + zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1); + zzdef_(zinc, Zcheckpart, " ave. distance tests per check", Ztotcheck); +} +void qh_allstatD(void) { + zdef_(zinc, Zvisit, "resets of visit_id", -1); + zdef_(zinc, Zvvisit, " resets of vertex_visit", -1); + zdef_(zmax, Zvisit2max, " max visit_id/2", -1); + zdef_(zmax, Zvvisit2max, " max vertex_visit/2", -1); + + zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1); + zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1); + zdef_(zmax, Zdelvertexmax, " maximum vertices deleted per iteration", -1); + zdef_(zinc, Zfindbest, "calls to findbest", -1); + zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest); + zdef_(zmax, Zfindbestmax, " max. facets tested", -1); + zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest); + zdef_(zinc, Zfindnew, "calls to findbestnew", -1); + zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew); + zdef_(zmax, Zfindnewmax, " max. facets tested", -1); + zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew); + zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1); + zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1); + zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon); + zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1); + zdef_(zinc, Zfindjump, " ave. clearly better", Zfindhorizon); + zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1); + zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1); + zdef_(zinc, Zpartflip, " repartitioned coplanar points for flipped orientation", -1); +} +void qh_allstatE(void) { + zdef_(zinc, Zpartinside, "inside points", -1); + zdef_(zinc, Zpartnear, " inside points kept with a facet", -1); + zdef_(zinc, Zcoplanarinside, " inside points that were coplanar with a facet", -1); + zdef_(zinc, Zbestlower, "calls to findbestlower", -1); + zdef_(zinc, Zbestlowerv, " with search of vertex neighbors", -1); + zdef_(wadd, Wmaxout, "difference in max_outside at final check", -1); + zzdef_(zinc, Zpartitionall, "distance tests for initial partition", -1); + zdef_(zinc, Ztotpartition, "partitions of a point", -1); + zzdef_(zinc, Zpartition, "distance tests for partitioning", -1); + zzdef_(zinc, Zdistcheck, "distance tests for checking flipped facets", -1); + zzdef_(zinc, Zdistconvex, "distance tests for checking convexity", -1); + zdef_(zinc, Zdistgood, "distance tests for checking good point", -1); + zdef_(zinc, Zdistio, "distance tests for output", -1); + zdef_(zinc, Zdiststat, "distance tests for statistics", -1); + zdef_(zinc, Zdistplane, "total number of distance tests", -1); + zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1); + zzdef_(zinc, Zpartcoplanar, " distance tests for these partitions", -1); + zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1); +} +void qh_allstatE2(void) { + zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1); + zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1); + zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup); + zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1); + zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge); + zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1); + zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1); + + zdef_(zdoc, Zdoc6, "statistics for determining merges", -1); + zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1); + zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1); + zzdef_(zinc, Zbestdist, "distance tests for best merge", -1); + zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1); + zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1); + zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1); + zdef_(zinc, Zcoplanarcentrum, "coplanar centrums in getmergeset", -1); + zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1); +} +void qh_allstatF(void) { + zdef_(zdoc, Zdoc7, "statistics for merging", -1); + zdef_(zinc, Zpremergetot, "merge iterations", -1); + zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot); + zdef_(zadd, Zmergeinitmax, " maximum", -1); + zdef_(zadd, Zmergesettot, " ave. additional non-convex ridges per iteration", Zpremergetot); + zdef_(zadd, Zmergesetmax, " maximum additional in one pass", -1); + zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1); + zdef_(zadd, Zmergesettot2, " additional non-convex ridges", -1); + zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1); + zdef_(wmin, Wminvertex, "max distance of merged vertex below facet(or roundoff)", -1); + zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1); + zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1); + zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1); + zdef_(zinc, Zmergesimplex, "merged a simplex", -1); + zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1); + zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1); + zzdef_(zadd, Zcyclefacettot, " ave. facets per cycle", Zcyclehorizon); + zdef_(zmax, Zcyclefacetmax, " max. facets", -1); + zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1); + zdef_(zinc, Zmergenew, "new facets merged", -1); + zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1); + zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1); + zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1); + zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1); + zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1); + zdef_(zinc, Zneighbor, "merges due to redundant neighbors", -1); + zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1); +} +void qh_allstatG(void) { + zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1); + zdef_(wadd, Wacoplanartot, " average merge distance", Zacoplanar); + zdef_(wmax, Wacoplanarmax, " maximum merge distance", -1); + zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1); + zdef_(wadd, Wcoplanartot, " average merge distance", Zcoplanar); + zdef_(wmax, Wcoplanarmax, " maximum merge distance", -1); + zdef_(zinc, Zconcave, "merges due to concave facets", -1); + zdef_(wadd, Wconcavetot, " average merge distance", Zconcave); + zdef_(wmax, Wconcavemax, " maximum merge distance", -1); + zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1); + zdef_(wadd, Wavoidoldtot, " average merge distance", Zavoidold); + zdef_(wmax, Wavoidoldmax, " maximum merge distance", -1); + zdef_(zinc, Zdegen, "merges due to degenerate facets", -1); + zdef_(wadd, Wdegentot, " average merge distance", Zdegen); + zdef_(wmax, Wdegenmax, " maximum merge distance", -1); + zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1); + zdef_(wadd, Wflippedtot, " average merge distance", Zflipped); + zdef_(wmax, Wflippedmax, " maximum merge distance", -1); + zdef_(zinc, Zduplicate, "merges due to duplicated ridges", -1); + zdef_(wadd, Wduplicatetot, " average merge distance", Zduplicate); + zdef_(wmax, Wduplicatemax, " maximum merge distance", -1); +} +void qh_allstatH(void) { + zdef_(zdoc, Zdoc8, "renamed vertex statistics", -1); + zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1); + zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1); + zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1); + zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1); + zdef_(zinc, Zdupridge, " duplicate ridges detected", -1); + zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1); + zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1); + zdef_(zinc, Zdropdegen, "degenerate facets due to dropped neighbors", -1); + zdef_(zinc, Zdelfacetdup, " facets deleted because of no neighbors", -1); + zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1); + zdef_(zinc, Zremvertexdel, " deleted", -1); + zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1); + zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1); + zdef_(zinc, Zintersect, "intersections found redundant vertices", -1); + zdef_(zadd, Zintersecttot, " ave. number found per vertex", Zintersect); + zdef_(zmax, Zintersectmax, " max. found for a vertex", -1); + zdef_(zinc, Zvertexridge, NULL, -1); + zdef_(zadd, Zvertexridgetot, " ave. number of ridges per tested vertex", Zvertexridge); + zdef_(zmax, Zvertexridgemax, " max. number of ridges per tested vertex", -1); + + zdef_(zdoc, Zdoc10, "memory usage statistics(in bytes)", -1); + zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1); + zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1); + zdef_(zadd, Zmempoints, "for input points and outside and coplanar sets",-1); + zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1); +} /* allstat */ + +void qh_allstatI(void) { + qhstat vridges= qhstat next; + zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1); + zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1); + zzdef_(wadd, Wridge, " ave. distance to ridge", Zridge); + zzdef_(wmax, Wridgemax, " max. distance to ridge", -1); + zzdef_(zinc, Zridgemid, "bounded ridges", -1); + zzdef_(wadd, Wridgemid, " ave. distance of midpoint to ridge", Zridgemid); + zzdef_(wmax, Wridgemidmax, " max. distance of midpoint to ridge", -1); + zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1); + zzdef_(wadd, Wridgeok, " ave. angle to ridge", Zridgeok); + zzdef_(wmax, Wridgeokmax, " max. angle to ridge", -1); + zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1); + zzdef_(wadd, Wridge0, " ave. angle to ridge", Zridge0); + zzdef_(wmax, Wridge0max, " max. angle to ridge", -1); + + zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1); + zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1); + zdef_(zadd, Ztricoplanartot, " ave. new facets created(may be deleted)", Ztricoplanar); + zdef_(zmax, Ztricoplanarmax, " max. new facets created", -1); + zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1); + zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1); + zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1); +} /* allstat */ + +/*--------------------------------- + + qh_allstatistics() + reset printed flag for all statistics +*/ +void qh_allstatistics(void) { + int i; + + for(i=ZEND; i--; ) + qhstat printed[i]= False; +} /* allstatistics */ + +#if qh_KEEPstatistics +/*--------------------------------- + + qh_collectstatistics() + collect statistics for qh.facet_list + +*/ +void qh_collectstatistics(void) { + facetT *facet, *neighbor, **neighborp; + vertexT *vertex, **vertexp; + realT dotproduct, dist; + int sizneighbors, sizridges, sizvertices, i; + + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + zval_(Zmempoints)= qh num_points * qh normal_size + + sizeof(qhT) + sizeof(qhstatT); + zval_(Zmemfacets)= 0; + zval_(Zmemridges)= 0; + zval_(Zmemvertices)= 0; + zval_(Zangle)= 0; + wval_(Wangle)= 0.0; + zval_(Znumridges)= 0; + zval_(Znumfacets)= 0; + zval_(Znumneighbors)= 0; + zval_(Znumvertices)= 0; + zval_(Znumvneighbors)= 0; + zval_(Znummergetot)= 0; + zval_(Znummergemax)= 0; + zval_(Zvertices)= qh num_vertices - qh_setsize(qh del_vertices); + if (qh MERGING || qh APPROXhull || qh JOGGLEmax < REALmax/2) + wmax_(Wmaxoutside, qh max_outside); + if (qh MERGING) + wmin_(Wminvertex, qh min_vertex); + FORALLfacets + facet->seen= False; + if (qh DELAUNAY) { + FORALLfacets { + if (facet->upperdelaunay != qh UPPERdelaunay) + facet->seen= True; /* remove from angle statistics */ + } + } + FORALLfacets { + if (facet->visible && qh NEWfacets) + continue; + sizvertices= qh_setsize(facet->vertices); + sizneighbors= qh_setsize(facet->neighbors); + sizridges= qh_setsize(facet->ridges); + zinc_(Znumfacets); + zadd_(Znumvertices, sizvertices); + zmax_(Zmaxvertices, sizvertices); + zadd_(Znumneighbors, sizneighbors); + zmax_(Zmaxneighbors, sizneighbors); + zadd_(Znummergetot, facet->nummerge); + i= facet->nummerge; /* avoid warnings */ + zmax_(Znummergemax, i); + if (!facet->simplicial) { + if (sizvertices == qh hull_dim) { + zinc_(Znowsimplicial); + }else { + zinc_(Znonsimplicial); + } + } + if (sizridges) { + zadd_(Znumridges, sizridges); + zmax_(Zmaxridges, sizridges); + } + zadd_(Zmemfacets, sizeof(facetT) + qh normal_size + 2*sizeof(setT) + + SETelemsize * (sizneighbors + sizvertices)); + if (facet->ridges) { + zadd_(Zmemridges, + sizeof(setT) + SETelemsize * sizridges + sizridges * + (sizeof(ridgeT) + sizeof(setT) + SETelemsize * (qh hull_dim-1))/2); + } + if (facet->outsideset) + zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->outsideset)); + if (facet->coplanarset) + zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->coplanarset)); + if (facet->seen) /* Delaunay upper envelope */ + continue; + facet->seen= True; + FOREACHneighbor_(facet) { + if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge + || neighbor->seen || !facet->normal || !neighbor->normal) + continue; + dotproduct= qh_getangle(facet->normal, neighbor->normal); + zinc_(Zangle); + wadd_(Wangle, dotproduct); + wmax_(Wanglemax, dotproduct) + wmin_(Wanglemin, dotproduct) + } + if (facet->normal) { + FOREACHvertex_(facet->vertices) { + zinc_(Zdiststat); + qh_distplane(vertex->point, facet, &dist); + wmax_(Wvertexmax, dist); + wmin_(Wvertexmin, dist); + } + } + } + FORALLvertices { + if (vertex->deleted) + continue; + zadd_(Zmemvertices, sizeof(vertexT)); + if (vertex->neighbors) { + sizneighbors= qh_setsize(vertex->neighbors); + zadd_(Znumvneighbors, sizneighbors); + zmax_(Zmaxvneighbors, sizneighbors); + zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors); + } + } + qh RANDOMdist= qh old_randomdist; +} /* collectstatistics */ +#endif /* qh_KEEPstatistics */ + +/*--------------------------------- + + qh_freestatistics( ) + free memory used for statistics +*/ +void qh_freestatistics(void) { + +#if qh_QHpointer + qh_free(qh_qhstat); + qh_qhstat= NULL; +#endif +} /* freestatistics */ + +/*--------------------------------- + + qh_initstatistics( ) + allocate and initialize statistics + + notes: + uses qh_malloc() instead of qh_memalloc() since mem.c not set up yet + NOerrors -- qh_initstatistics can not use qh_errexit(). One first call, qh_memalloc is not initialized. Also invoked by QhullQh(). +*/ +void qh_initstatistics(void) { + int i; + realT realx; + int intx; + +#if qh_QHpointer + if(qh_qhstat){ /* qh_initstatistics may be called from Qhull::resetStatistics() */ + qh_free(qh_qhstat); + qh_qhstat= 0; + } + if (!(qh_qhstat= (qhstatT *)qh_malloc(sizeof(qhstatT)))) { + qh_fprintf(qhmem.ferr, 6183, "qhull error (qh_initstatistics): insufficient memory\n"); + qh_exit(qh_ERRmem); /* can not use qh_errexit() */ + } +#endif + + qhstat next= 0; + qh_allstatA(); + qh_allstatB(); + qh_allstatC(); + qh_allstatD(); + qh_allstatE(); + qh_allstatE2(); + qh_allstatF(); + qh_allstatG(); + qh_allstatH(); + qh_allstatI(); + if (qhstat next > (int)sizeof(qhstat id)) { + qh_fprintf(qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\ + qhstat.next %d should be <= sizeof(qhstat id) %d\n", qhstat next, (int)sizeof(qhstat id)); +#if 0 /* for locating error, Znumridges should be duplicated */ + for(i=0; i < ZEND; i++) { + int j; + for(j=i+1; j < ZEND; j++) { + if (qhstat id[i] == qhstat id[j]) { + qh_fprintf(qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n", + qhstat id[i], i, j); + } + } + } +#endif + qh_exit(qh_ERRqhull); /* can not use qh_errexit() */ + } + qhstat init[zinc].i= 0; + qhstat init[zadd].i= 0; + qhstat init[zmin].i= INT_MAX; + qhstat init[zmax].i= INT_MIN; + qhstat init[wadd].r= 0; + qhstat init[wmin].r= REALmax; + qhstat init[wmax].r= -REALmax; + for(i=0; i < ZEND; i++) { + if (qhstat type[i] > ZTYPEreal) { + realx= qhstat init[(unsigned char)(qhstat type[i])].r; + qhstat stats[i].r= realx; + }else if (qhstat type[i] != zdoc) { + intx= qhstat init[(unsigned char)(qhstat type[i])].i; + qhstat stats[i].i= intx; + } + } +} /* initstatistics */ + +/*--------------------------------- + + qh_newstats( ) + returns True if statistics for zdoc + + returns: + next zdoc +*/ +boolT qh_newstats(int idx, int *nextindex) { + boolT isnew= False; + int start, i; + + if (qhstat type[qhstat id[idx]] == zdoc) + start= idx+1; + else + start= idx; + for(i= start; i < qhstat next && qhstat type[qhstat id[i]] != zdoc; i++) { + if (!qh_nostatistic(qhstat id[i]) && !qhstat printed[qhstat id[i]]) + isnew= True; + } + *nextindex= i; + return isnew; +} /* newstats */ + +/*--------------------------------- + + qh_nostatistic( index ) + true if no statistic to print +*/ +boolT qh_nostatistic(int i) { + + if ((qhstat type[i] > ZTYPEreal + &&qhstat stats[i].r == qhstat init[(unsigned char)(qhstat type[i])].r) + || (qhstat type[i] < ZTYPEreal + &&qhstat stats[i].i == qhstat init[(unsigned char)(qhstat type[i])].i)) + return True; + return False; +} /* nostatistic */ + +#if qh_KEEPstatistics +/*--------------------------------- + + qh_printallstatistics( fp, string ) + print all statistics with header 'string' +*/ +void qh_printallstatistics(FILE *fp, const char *string) { + + qh_allstatistics(); + qh_collectstatistics(); + qh_printstatistics(fp, string); + qh_memstatistics(fp); +} + + +/*--------------------------------- + + qh_printstatistics( fp, string ) + print statistics to a file with header 'string' + skips statistics with qhstat.printed[] (reset with qh_allstatistics) + + see: + qh_printallstatistics() +*/ +void qh_printstatistics(FILE *fp, const char *string) { + int i, k; + realT ave; + + if (qh num_points != qh num_vertices) { + wval_(Wpbalance)= 0; + wval_(Wpbalance2)= 0; + }else + wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance), + wval_(Wnewbalance2), &ave); + qh_fprintf(fp, 9350, "\n\ +%s\n\ + qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh rbox_command, + qh qhull_command, qh_version, qh qhull_options); + qh_fprintf(fp, 9351, "\nprecision constants:\n\ + %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\ + %6.2g max. roundoff error for distance computation('En')\n\ + %6.2g max. roundoff error for angle computations\n\ + %6.2g min. distance for outside points ('Wn')\n\ + %6.2g min. distance for visible facets ('Vn')\n\ + %6.2g max. distance for coplanar facets ('Un')\n\ + %6.2g max. facet width for recomputing centrum and area\n\ +", + qh MAXabs_coord, qh DISTround, qh ANGLEround, qh MINoutside, + qh MINvisible, qh MAXcoplanar, qh WIDEfacet); + if (qh KEEPnearinside) + qh_fprintf(fp, 9352, "\ + %6.2g max. distance for near-inside points\n", qh NEARinside); + if (qh premerge_cos < REALmax/2) qh_fprintf(fp, 9353, "\ + %6.2g max. cosine for pre-merge angle\n", qh premerge_cos); + if (qh PREmerge) qh_fprintf(fp, 9354, "\ + %6.2g radius of pre-merge centrum\n", qh premerge_centrum); + if (qh postmerge_cos < REALmax/2) qh_fprintf(fp, 9355, "\ + %6.2g max. cosine for post-merge angle\n", qh postmerge_cos); + if (qh POSTmerge) qh_fprintf(fp, 9356, "\ + %6.2g radius of post-merge centrum\n", qh postmerge_centrum); + qh_fprintf(fp, 9357, "\ + %6.2g max. distance for merging two simplicial facets\n\ + %6.2g max. roundoff error for arithmetic operations\n\ + %6.2g min. denominator for divisions\n\ + zero diagonal for Gauss: ", qh ONEmerge, REALepsilon, qh MINdenom); + for(k=0; k < qh hull_dim; k++) + qh_fprintf(fp, 9358, "%6.2e ", qh NEARzero[k]); + qh_fprintf(fp, 9359, "\n\n"); + for(i=0 ; i < qhstat next; ) + qh_printstats(fp, i, &i); +} /* printstatistics */ +#endif /* qh_KEEPstatistics */ + +/*--------------------------------- + + qh_printstatlevel( fp, id ) + print level information for a statistic + + notes: + nop if id >= ZEND, printed, or same as initial value +*/ +void qh_printstatlevel(FILE *fp, int id, int start) { +#define NULLfield " " + + if (id >= ZEND || qhstat printed[id]) + return; + if (qhstat type[id] == zdoc) { + qh_fprintf(fp, 9360, "%s\n", qhstat doc[id]); + return; + } + start= 0; /* not used */ + if (qh_nostatistic(id) || !qhstat doc[id]) + return; + qhstat printed[id]= True; + if (qhstat count[id] != -1 + && qhstat stats[(unsigned char)(qhstat count[id])].i == 0) + qh_fprintf(fp, 9361, " *0 cnt*"); + else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] == -1) + qh_fprintf(fp, 9362, "%7.2g", qhstat stats[id].r); + else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] != -1) + qh_fprintf(fp, 9363, "%7.2g", qhstat stats[id].r/ qhstat stats[(unsigned char)(qhstat count[id])].i); + else if (qhstat type[id] < ZTYPEreal && qhstat count[id] == -1) + qh_fprintf(fp, 9364, "%7d", qhstat stats[id].i); + else if (qhstat type[id] < ZTYPEreal && qhstat count[id] != -1) + qh_fprintf(fp, 9365, "%7.3g", (realT) qhstat stats[id].i / qhstat stats[(unsigned char)(qhstat count[id])].i); + qh_fprintf(fp, 9366, " %s\n", qhstat doc[id]); +} /* printstatlevel */ + + +/*--------------------------------- + + qh_printstats( fp, index, nextindex ) + print statistics for a zdoc group + + returns: + next zdoc if non-null +*/ +void qh_printstats(FILE *fp, int idx, int *nextindex) { + int j, nexti; + + if (qh_newstats(idx, &nexti)) { + qh_fprintf(fp, 9367, "\n"); + for (j=idx; j-------------------------------- + + qh_stddev( num, tot, tot2, ave ) + compute the standard deviation and average from statistics + + tot2 is the sum of the squares + notes: + computes r.m.s.: + (x-ave)^2 + == x^2 - 2x tot/num + (tot/num)^2 + == tot2 - 2 tot tot/num + tot tot/num + == tot2 - tot ave +*/ +realT qh_stddev(int num, realT tot, realT tot2, realT *ave) { + realT stddev; + + *ave= tot/num; + stddev= sqrt(tot2/num - *ave * *ave); + return stddev; +} /* stddev */ + +#endif /* qh_KEEPstatistics */ + +#if !qh_KEEPstatistics +void qh_collectstatistics(void) {} +void qh_printallstatistics(FILE *fp, char *string) {}; +void qh_printstatistics(FILE *fp, char *string) {} +#endif diff --git a/extern/qhull/stat.h b/extern/qhull/stat.h new file mode 100644 index 000000000000..97d8efa585f3 --- /dev/null +++ b/extern/qhull/stat.h @@ -0,0 +1,541 @@ +/* --------------------------------- + + stat.h + contains all statistics that are collected for qhull + + see qh-stat.htm and stat.c + + Copyright (c) 1993-2012 The Geometry Center. + $Id: //main/2011/qhull/src/libqhull/stat.h#5 $$Change: 1464 $ + $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $ + + recompile qhull if you change this file + + Integer statistics are Z* while real statistics are W*. + + define maydebugx to call a routine at every statistic event + +*/ + +#ifndef qhDEFstat +#define qhDEFstat 1 + +#include "libqhull.h" + +/*--------------------------------- + + qh_KEEPstatistics + 0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval) +*/ +#ifndef qh_KEEPstatistics +#define qh_KEEPstatistics 1 +#endif + +/*--------------------------------- + + Zxxx for integers, Wxxx for reals + + notes: + be sure that all statistics are defined in stat.c + otherwise initialization may core dump + can pick up all statistics by: + grep '[zw].*_[(][ZW]' *.c >z.x + remove trailers with query">- + remove leaders with query-replace-regexp [ ^I]+ ( +*/ +#if qh_KEEPstatistics +enum statistics { /* alphabetical after Z/W */ + Zacoplanar, + Wacoplanarmax, + Wacoplanartot, + Zangle, + Wangle, + Wanglemax, + Wanglemin, + Zangletests, + Wareatot, + Wareamax, + Wareamin, + Zavoidold, + Wavoidoldmax, + Wavoidoldtot, + Zback0, + Zbestcentrum, + Zbestdist, + Zbestlower, + Zbestlowerv, + Zcentrumtests, + Zcheckpart, + Zcomputefurthest, + Zconcave, + Wconcavemax, + Wconcavetot, + Zconcaveridges, + Zconcaveridge, + Zcoplanar, + Wcoplanarmax, + Wcoplanartot, + Zcoplanarangle, + Zcoplanarcentrum, + Zcoplanarhorizon, + Zcoplanarinside, + Zcoplanarpart, + Zcoplanarridges, + Wcpu, + Zcyclefacetmax, + Zcyclefacettot, + Zcyclehorizon, + Zcyclevertex, + Zdegen, + Wdegenmax, + Wdegentot, + Zdegenvertex, + Zdelfacetdup, + Zdelridge, + Zdelvertextot, + Zdelvertexmax, + Zdetsimplex, + Zdistcheck, + Zdistconvex, + Zdistgood, + Zdistio, + Zdistplane, + Zdiststat, + Zdistvertex, + Zdistzero, + Zdoc1, + Zdoc2, + Zdoc3, + Zdoc4, + Zdoc5, + Zdoc6, + Zdoc7, + Zdoc8, + Zdoc9, + Zdoc10, + Zdoc11, + Zdoc12, + Zdropdegen, + Zdropneighbor, + Zdupflip, + Zduplicate, + Wduplicatemax, + Wduplicatetot, + Zdupridge, + Zdupsame, + Zflipped, + Wflippedmax, + Wflippedtot, + Zflippedfacets, + Zfindbest, + Zfindbestmax, + Zfindbesttot, + Zfindcoplanar, + Zfindfail, + Zfindhorizon, + Zfindhorizonmax, + Zfindhorizontot, + Zfindjump, + Zfindnew, + Zfindnewmax, + Zfindnewtot, + Zfindnewjump, + Zfindnewsharp, + Zgauss0, + Zgoodfacet, + Zhashlookup, + Zhashridge, + Zhashridgetest, + Zhashtests, + Zinsidevisible, + Zintersect, + Zintersectfail, + Zintersectmax, + Zintersectnum, + Zintersecttot, + Zmaxneighbors, + Wmaxout, + Wmaxoutside, + Zmaxridges, + Zmaxvertex, + Zmaxvertices, + Zmaxvneighbors, + Zmemfacets, + Zmempoints, + Zmemridges, + Zmemvertices, + Zmergeflipdup, + Zmergehorizon, + Zmergeinittot, + Zmergeinitmax, + Zmergeinittot2, + Zmergeintohorizon, + Zmergenew, + Zmergesettot, + Zmergesetmax, + Zmergesettot2, + Zmergesimplex, + Zmergevertex, + Wmindenom, + Wminvertex, + Zminnorm, + Zmultiridge, + Znearlysingular, + Zneighbor, + Wnewbalance, + Wnewbalance2, + Znewfacettot, + Znewfacetmax, + Znewvertex, + Wnewvertex, + Wnewvertexmax, + Znoarea, + Znonsimplicial, + Znowsimplicial, + Znotgood, + Znotgoodnew, + Znotmax, + Znumfacets, + Znummergemax, + Znummergetot, + Znumneighbors, + Znumridges, + Znumvertices, + Znumvisibility, + Znumvneighbors, + Zonehorizon, + Zpartangle, + Zpartcoplanar, + Zpartflip, + Zparthorizon, + Zpartinside, + Zpartition, + Zpartitionall, + Zpartnear, + Zpbalance, + Wpbalance, + Wpbalance2, + Zpostfacets, + Zpremergetot, + Zprocessed, + Zremvertex, + Zremvertexdel, + Zrenameall, + Zrenamepinch, + Zrenameshare, + Zretry, + Wretrymax, + Zridge, + Wridge, + Wridgemax, + Zridge0, + Wridge0, + Wridge0max, + Zridgemid, + Wridgemid, + Wridgemidmax, + Zridgeok, + Wridgeok, + Wridgeokmax, + Zsearchpoints, + Zsetplane, + Ztestvneighbor, + Ztotcheck, + Ztothorizon, + Ztotmerge, + Ztotpartcoplanar, + Ztotpartition, + Ztotridges, + Ztotvertices, + Ztotvisible, + Ztricoplanar, + Ztricoplanarmax, + Ztricoplanartot, + Ztridegen, + Ztrimirror, + Ztrinull, + Wvertexmax, + Wvertexmin, + Zvertexridge, + Zvertexridgetot, + Zvertexridgemax, + Zvertices, + Zvisfacettot, + Zvisfacetmax, + Zvisit, + Zvisit2max, + Zvisvertextot, + Zvisvertexmax, + Zvvisit, + Zvvisit2max, + Zwidefacet, + Zwidevertices, + ZEND}; + +/*--------------------------------- + + Zxxx/Wxxx statistics that remain defined if qh_KEEPstatistics=0 + + notes: + be sure to use zzdef, zzinc, etc. with these statistics (no double checking!) +*/ +#else +enum statistics { /* for zzdef etc. macros */ + Zback0, + Zbestdist, + Zcentrumtests, + Zcheckpart, + Zconcaveridges, + Zcoplanarhorizon, + Zcoplanarpart, + Zcoplanarridges, + Zcyclefacettot, + Zcyclehorizon, + Zdelvertextot, + Zdistcheck, + Zdistconvex, + Zdistzero, + Zdoc1, + Zdoc2, + Zdoc3, + Zdoc11, + Zflippedfacets, + Zgauss0, + Zminnorm, + Zmultiridge, + Znearlysingular, + Wnewvertexmax, + Znumvisibility, + Zpartcoplanar, + Zpartition, + Zpartitionall, + Zprocessed, + Zretry, + Zridge, + Wridge, + Wridgemax, + Zridge0, + Wridge0, + Wridge0max, + Zridgemid, + Wridgemid, + Wridgemidmax, + Zridgeok, + Wridgeok, + Wridgeokmax, + Zsetplane, + Ztotcheck, + Ztotmerge, + ZEND}; +#endif + +/*--------------------------------- + + ztype + the type of a statistic sets its initial value. + + notes: + The type should be the same as the macro for collecting the statistic +*/ +enum ztypes {zdoc,zinc,zadd,zmax,zmin,ZTYPEreal,wadd,wmax,wmin,ZTYPEend}; + +/*========== macros and constants =============*/ + +/*---------------------------------- + + MAYdebugx + define as maydebug() to be called frequently for error trapping +*/ +#define MAYdebugx + +/*---------------------------------- + + zzdef_, zdef_( type, name, doc, -1) + define a statistic (assumes 'qhstat.next= 0;') + + zdef_( type, name, doc, count) + define an averaged statistic + printed as name/count +*/ +#define zzdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \ + qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype +#if qh_KEEPstatistics +#define zdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \ + qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype +#else +#define zdef_(type,name,doc,count) +#endif + +/*---------------------------------- + + zzinc_( name ), zinc_( name) + increment an integer statistic +*/ +#define zzinc_(id) {MAYdebugx; qhstat stats[id].i++;} +#if qh_KEEPstatistics +#define zinc_(id) {MAYdebugx; qhstat stats[id].i++;} +#else +#define zinc_(id) {} +#endif + +/*---------------------------------- + + zzadd_( name, value ), zadd_( name, value ), wadd_( name, value ) + add value to an integer or real statistic +*/ +#define zzadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);} +#define wwadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);} +#if qh_KEEPstatistics +#define zadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);} +#define wadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);} +#else +#define zadd_(id, val) {} +#define wadd_(id, val) {} +#endif + +/*---------------------------------- + + zzval_( name ), zval_( name ), wwval_( name ) + set or return value of a statistic +*/ +#define zzval_(id) ((qhstat stats[id]).i) +#define wwval_(id) ((qhstat stats[id]).r) +#if qh_KEEPstatistics +#define zval_(id) ((qhstat stats[id]).i) +#define wval_(id) ((qhstat stats[id]).r) +#else +#define zval_(id) qhstat tempi +#define wval_(id) qhstat tempr +#endif + +/*---------------------------------- + + zmax_( id, val ), wmax_( id, value ) + maximize id with val +*/ +#define wwmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));} +#if qh_KEEPstatistics +#define zmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].i,(val));} +#define wmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));} +#else +#define zmax_(id, val) {} +#define wmax_(id, val) {} +#endif + +/*---------------------------------- + + zmin_( id, val ), wmin_( id, value ) + minimize id with val +*/ +#if qh_KEEPstatistics +#define zmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].i,(val));} +#define wmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].r,(val));} +#else +#define zmin_(id, val) {} +#define wmin_(id, val) {} +#endif + +/*================== stat.h types ==============*/ + + +/*---------------------------------- + + intrealT + union of integer and real, used for statistics +*/ +typedef union intrealT intrealT; /* union of int and realT */ +union intrealT { + int i; + realT r; +}; + +/*---------------------------------- + + qhstat + global data structure for statistics, similar to qh and qhrbox + + notes: + access to qh_qhstat is via the "qhstat" macro. There are two choices + qh_QHpointer = 1 access globals via a pointer + enables qh_saveqhull() and qh_restoreqhull() + = 0 qh_qhstat is a static data structure + only one instance of qhull() can be active at a time + default value + qh_QHpointer is defined in libqhull.h + qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h] + + allocated in stat.c using qh_malloc() +*/ +#ifndef DEFqhstatT +#define DEFqhstatT 1 +typedef struct qhstatT qhstatT; +#endif + +#if qh_QHpointer_dllimport +#define qhstat qh_qhstat-> +__declspec(dllimport) extern qhstatT *qh_qhstat; +#elif qh_QHpointer +#define qhstat qh_qhstat-> +extern qhstatT *qh_qhstat; +#elif qh_dllimport +#define qhstat qh_qhstat. +__declspec(dllimport) extern qhstatT qh_qhstat; +#else +#define qhstat qh_qhstat. +extern qhstatT qh_qhstat; +#endif +struct qhstatT { + intrealT stats[ZEND]; /* integer and real statistics */ + unsigned char id[ZEND+10]; /* id's in print order */ + const char *doc[ZEND]; /* array of documentation strings */ + short int count[ZEND]; /* -1 if none, else index of count to use */ + char type[ZEND]; /* type, see ztypes above */ + char printed[ZEND]; /* true, if statistic has been printed */ + intrealT init[ZTYPEend]; /* initial values by types, set initstatistics */ + + int next; /* next index for zdef_ */ + int precision; /* index for precision problems */ + int vridges; /* index for Voronoi ridges */ + int tempi; + realT tempr; +}; + +/*========== function prototypes ===========*/ + +void qh_allstatA(void); +void qh_allstatB(void); +void qh_allstatC(void); +void qh_allstatD(void); +void qh_allstatE(void); +void qh_allstatE2(void); +void qh_allstatF(void); +void qh_allstatG(void); +void qh_allstatH(void); +void qh_allstatI(void); +void qh_allstatistics(void); +void qh_collectstatistics(void); +void qh_freestatistics(void); +void qh_initstatistics(void); +boolT qh_newstats(int idx, int *nextindex); +boolT qh_nostatistic(int i); +void qh_printallstatistics(FILE *fp, const char *string); +void qh_printstatistics(FILE *fp, const char *string); +void qh_printstatlevel(FILE *fp, int id, int start); +void qh_printstats(FILE *fp, int idx, int *nextindex); +realT qh_stddev(int num, realT tot, realT tot2, realT *ave); + +#endif /* qhDEFstat */ diff --git a/extern/qhull/user.c b/extern/qhull/user.c new file mode 100644 index 000000000000..ce2f76410d1e --- /dev/null +++ b/extern/qhull/user.c @@ -0,0 +1,525 @@ +/*--------------------------------- + + user.c + user redefinable functions + + see user2.c for qh_fprintf, qh_malloc, qh_free + + see README.txt see COPYING.txt for copyright information. + + see libqhull.h for data structures, macros, and user-callable functions. + + see user_eg.c, unix.c, and qhull_interface.cpp for examples. + + see user.h for user-definable constants + + use qh_NOmem in mem.h to turn off memory management + use qh_NOmerge in user.h to turn off facet merging + set qh_KEEPstatistics in user.h to 0 to turn off statistics + + This is unsupported software. You're welcome to make changes, + but you're on your own if something goes wrong. Use 'Tc' to + check frequently. Usually qhull will report an error if + a data structure becomes inconsistent. If so, it also reports + the last point added to the hull, e.g., 102. You can then trace + the execution of qhull with "T4P102". + + Please report any errors that you fix to qhull@qhull.org + + call_qhull is a template for calling qhull from within your application + + if you recompile and load this module, then user.o will not be loaded + from qhull.a + + you can add additional quick allocation sizes in qh_user_memsizes + + if the other functions here are redefined to not use qh_print..., + then io.o will not be loaded from qhull.a. See user_eg.c for an + example. We recommend keeping io.o for the extra debugging + information it supplies. +*/ + +#include "qhull_a.h" + +#include+ +/*--------------------------------- + + qh_call_qhull( void ) + template for calling qhull from inside your program + remove #if 0, #endif to compile + + returns: + exit code(see qh_ERR... in libqhull.h) + all memory freed + + notes: + This can be called any number of times. + + see: + qh_call_qhull_once() + +*/ +#if 0 +{ + int dim; /* dimension of points */ + int numpoints; /* number of points */ + coordT *points; /* array of coordinates for each point */ + boolT ismalloc; /* True if qhull should free points in qh_freeqhull() or reallocation */ + char flags[]= "qhull Tv"; /* option flags for qhull, see qh_opt.htm */ + FILE *outfile= stdout; /* output from qh_produce_output() + use NULL to skip qh_produce_output() */ + FILE *errfile= stderr; /* error messages from qhull code */ + int exitcode; /* 0 if no error from qhull */ + facetT *facet; /* set by FORALLfacets */ + int curlong, totlong; /* memory remaining after qh_memfreeshort */ + +#if qh_QHpointer /* see user.h */ + if (qh_qh){ + printf ("QH6238: Qhull link error. The global variable qh_qh was not initialized\n\ + to NULL by global.c. Please compile this program with -Dqh_QHpointer_dllimport\n\ + as well as -Dqh_QHpointer, or use libqhullstatic, or use a different tool chain.\n\n"); + exit -1; + } +#endif + + /* initialize dim, numpoints, points[], ismalloc here */ + exitcode= qh_new_qhull(dim, numpoints, points, ismalloc, + flags, outfile, errfile); + if (!exitcode) { /* if no error */ + /* 'qh facet_list' contains the convex hull */ + FORALLfacets { + /* ... your code ... */ + } + } + qh_freeqhull(!qh_ALL); + qh_memfreeshort(&curlong, &totlong); + if (curlong || totlong) + qh_fprintf(errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong); +} +#endif + +/*--------------------------------- + + qh_new_qhull( dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile ) + build new qhull data structure and return exitcode (0 if no errors) + + notes: + do not modify points until finished with results. + The qhull data structure contains pointers into the points array. + do not call qhull functions before qh_new_qhull(). + The qhull data structure is not initialized until qh_new_qhull(). + + outfile may be null + qhull_cmd must start with "qhull " + projects points to a new point array for Delaunay triangulations ('d' and 'v') + transforms points into a new point array for halfspace intersection ('H') + + + To allow multiple, concurrent calls to qhull() + - set qh_QHpointer in user.h + - use qh_save_qhull and qh_restore_qhull to swap the global data structure between calls. + - use qh_freeqhull(qh_ALL) to free intermediate convex hulls + + see: + user_eg.c for an example +*/ +int qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile) { + int exitcode, hulldim; + boolT new_ismalloc; + static boolT firstcall = True; + coordT *new_points; + + if (firstcall) { + qh_meminit(errfile); + firstcall= False; + } + if (strncmp(qhull_cmd,"qhull ", (size_t)6)) { + qh_fprintf(errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n"); + qh_exit(qh_ERRinput); + } + qh_initqhull_start(NULL, outfile, errfile); + trace1((qh ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd)); + exitcode = setjmp(qh errexit); + if (!exitcode) + { + qh NOerrexit = False; + qh_initflags(qhull_cmd); + if (qh DELAUNAY) + qh PROJECTdelaunay= True; + if (qh HALFspace) { + /* points is an array of halfspaces, + the last coordinate of each halfspace is its offset */ + hulldim= dim-1; + qh_setfeasible(hulldim); + new_points= qh_sethalfspace_all(dim, numpoints, points, qh feasible_point); + new_ismalloc= True; + if (ismalloc) + qh_free(points); + }else { + hulldim= dim; + new_points= points; + new_ismalloc= ismalloc; + } + qh_init_B(new_points, numpoints, hulldim, new_ismalloc); + qh_qhull(); + qh_check_output(); + if (outfile) { + qh_produce_output(); + }else { + qh_prepare_output(); + } + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points(); + } + qh NOerrexit = True; + return exitcode; +} /* new_qhull */ + +/*--------------------------------- + + qh_errexit( exitcode, facet, ridge ) + report and exit from an error + report facet and ridge if non-NULL + reports useful information such as last point processed + set qh.FORCEoutput to print neighborhood of facet + + see: + qh_errexit2() in libqhull.c for printing 2 facets + + design: + check for error within error processing + compute qh.hulltime + print facet and ridge (if any) + report commandString, options, qh.furthest_id + print summary and statistics (including precision statistics) + if qh_ERRsingular + print help text for singular data set + exit program via long jump (if defined) or exit() +*/ +void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge) { + + if (qh ERREXITcalled) { + qh_fprintf(qh ferr, 8126, "\nqhull error while processing previous error. Exit program\n"); + qh_exit(qh_ERRqhull); + } + qh ERREXITcalled= True; + if (!qh QHULLfinished) + qh hulltime= qh_CPUclock - qh hulltime; + qh_errprint("ERRONEOUS", facet, NULL, ridge, NULL); + qh_fprintf(qh ferr, 8127, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command); + qh_fprintf(qh ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options); + if (qh furthest_id >= 0) { + qh_fprintf(qh ferr, 8129, "Last point added to hull was p%d.", qh furthest_id); + if (zzval_(Ztotmerge)) + qh_fprintf(qh ferr, 8130, " Last merge was #%d.", zzval_(Ztotmerge)); + if (qh QHULLfinished) + qh_fprintf(qh ferr, 8131, "\nQhull has finished constructing the hull."); + else if (qh POSTmerging) + qh_fprintf(qh ferr, 8132, "\nQhull has started post-merging."); + qh_fprintf(qh ferr, 8133, "\n"); + } + if (qh FORCEoutput && (qh QHULLfinished || (!facet && !ridge))) + qh_produce_output(); + else if (exitcode != qh_ERRinput) { + if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh hull_dim+1) { + qh_fprintf(qh ferr, 8134, "\nAt error exit:\n"); + qh_printsummary(qh ferr); + if (qh PRINTstatistics) { + qh_collectstatistics(); + qh_printstatistics(qh ferr, "at error exit"); + qh_memstatistics(qh ferr); + } + } + if (qh PRINTprecision) + qh_printstats(qh ferr, qhstat precision, NULL); + } + if (!exitcode) + exitcode= qh_ERRqhull; + else if (exitcode == qh_ERRsingular) + qh_printhelp_singular(qh ferr); + else if (exitcode == qh_ERRprec && !qh PREmerge) + qh_printhelp_degenerate(qh ferr); + if (qh NOerrexit) { + qh_fprintf(qh ferr, 6187, "qhull error while ending program. Exit program\n"); + qh_exit(qh_ERRqhull); + } + qh ERREXITcalled= False; + qh NOerrexit= True; + longjmp(qh errexit, exitcode); +} /* errexit */ + + +/*--------------------------------- + + qh_errprint( fp, string, atfacet, otherfacet, atridge, atvertex ) + prints out the information of facets and ridges to fp + also prints neighbors and geomview output + + notes: + except for string, any parameter may be NULL +*/ +void qh_errprint(const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) { + int i; + + if (atfacet) { + qh_fprintf(qh ferr, 8135, "%s FACET:\n", string); + qh_printfacet(qh ferr, atfacet); + } + if (otherfacet) { + qh_fprintf(qh ferr, 8136, "%s OTHER FACET:\n", string); + qh_printfacet(qh ferr, otherfacet); + } + if (atridge) { + qh_fprintf(qh ferr, 8137, "%s RIDGE:\n", string); + qh_printridge(qh ferr, atridge); + if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet) + qh_printfacet(qh ferr, atridge->top); + if (atridge->bottom + && atridge->bottom != atfacet && atridge->bottom != otherfacet) + qh_printfacet(qh ferr, atridge->bottom); + if (!atfacet) + atfacet= atridge->top; + if (!otherfacet) + otherfacet= otherfacet_(atridge, atfacet); + } + if (atvertex) { + qh_fprintf(qh ferr, 8138, "%s VERTEX:\n", string); + qh_printvertex(qh ferr, atvertex); + } + if (qh fout && qh FORCEoutput && atfacet && !qh QHULLfinished && !qh IStracing) { + qh_fprintf(qh ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n"); + for (i=0; i < qh_PRINTEND; i++) /* use fout for geomview output */ + qh_printneighborhood(qh fout, qh PRINTout[i], atfacet, otherfacet, + !qh_ALL); + } +} /* errprint */ + + +/*--------------------------------- + + qh_printfacetlist( fp, facetlist, facets, printall ) + print all fields for a facet list and/or set of facets to fp + if !printall, + only prints good facets + + notes: + also prints all vertices +*/ +void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall) { + facetT *facet, **facetp; + + qh_printbegin(qh ferr, qh_PRINTfacets, facetlist, facets, printall); + FORALLfacet_(facetlist) + qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall); + FOREACHfacet_(facets) + qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall); + qh_printend(qh ferr, qh_PRINTfacets, facetlist, facets, printall); +} /* printfacetlist */ + + +/*--------------------------------- + + qh_printhelp_degenerate( fp ) + prints descriptive message for precision error + + notes: + no message if qh_QUICKhelp +*/ +void qh_printhelp_degenerate(FILE *fp) { + + if (qh MERGEexact || qh PREmerge || qh JOGGLEmax < REALmax/2) + qh_fprintf(fp, 9368, "\n\ +A Qhull error has occurred. Qhull should have corrected the above\n\ +precision error. Please send the input and all of the output to\n\ +qhull_bug@qhull.org\n"); + else if (!qh_QUICKhelp) { + qh_fprintf(fp, 9369, "\n\ +Precision problems were detected during construction of the convex hull.\n\ +This occurs because convex hull algorithms assume that calculations are\n\ +exact, but floating-point arithmetic has roundoff errors.\n\ +\n\ +To correct for precision problems, do not use 'Q0'. By default, Qhull\n\ +selects 'C-0' or 'Qx' and merges non-convex facets. With option 'QJ',\n\ +Qhull joggles the input to prevent precision problems. See \"Imprecision\n\ +in Qhull\" (qh-impre.htm).\n\ +\n\ +If you use 'Q0', the output may include\n\ +coplanar ridges, concave ridges, and flipped facets. In 4-d and higher,\n\ +Qhull may produce a ridge with four neighbors or two facets with the same \n\ +vertices. Qhull reports these events when they occur. It stops when a\n\ +concave ridge, flipped facet, or duplicate facet occurs.\n"); +#if REALfloat + qh_fprintf(fp, 9370, "\ +\n\ +Qhull is currently using single precision arithmetic. The following\n\ +will probably remove the precision problems:\n\ + - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n"); +#endif + if (qh DELAUNAY && !qh SCALElast && qh MAXabs_coord > 1e4) + qh_fprintf(fp, 9371, "\ +\n\ +When computing the Delaunay triangulation of coordinates > 1.0,\n\ + - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n"); + if (qh DELAUNAY && !qh ATinfinity) + qh_fprintf(fp, 9372, "\ +When computing the Delaunay triangulation:\n\ + - use 'Qz' to add a point at-infinity. This reduces precision problems.\n"); + + qh_fprintf(fp, 9373, "\ +\n\ +If you need triangular output:\n\ + - use option 'Qt' to triangulate the output\n\ + - use option 'QJ' to joggle the input points and remove precision errors\n\ + - use option 'Ft'. It triangulates non-simplicial facets with added points.\n\ +\n\ +If you must use 'Q0',\n\ +try one or more of the following options. They can not guarantee an output.\n\ + - use 'QbB' to scale the input to a cube.\n\ + - use 'Po' to produce output and prevent partitioning for flipped facets\n\ + - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\ + - use 'En' to specify a maximum roundoff error less than %2.2g.\n\ + - options 'Qf', 'Qbb', and 'QR0' may also help\n", + qh DISTround); + qh_fprintf(fp, 9374, "\ +\n\ +To guarantee simplicial output:\n\ + - use option 'Qt' to triangulate the output\n\ + - use option 'QJ' to joggle the input points and remove precision errors\n\ + - use option 'Ft' to triangulate the output by adding points\n\ + - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\ +"); + } +} /* printhelp_degenerate */ + + +/*--------------------------------- + + qh_printhelp_narrowhull( minangle ) + Warn about a narrow hull + + notes: + Alternatively, reduce qh_WARNnarrow in user.h + +*/ +void qh_printhelp_narrowhull(FILE *fp, realT minangle) { + + qh_fprintf(fp, 9375, "qhull precision warning: \n\ +The initial hull is narrow (cosine of min. angle is %.16f).\n\ +Is the input lower dimensional (e.g., on a plane in 3-d)? Qhull may\n\ +produce a wide facet. Options 'QbB' (scale to unit box) or 'Qbb' (scale\n\ +last coordinate) may remove this warning. Use 'Pp' to skip this warning.\n\ +See 'Limitations' in qh-impre.htm.\n", + -minangle); /* convert from angle between normals to angle between facets */ +} /* printhelp_narrowhull */ + +/*--------------------------------- + + qh_printhelp_singular( fp ) + prints descriptive message for singular input +*/ +void qh_printhelp_singular(FILE *fp) { + facetT *facet; + vertexT *vertex, **vertexp; + realT min, max, *coord, dist; + int i,k; + + qh_fprintf(fp, 9376, "\n\ +The input to qhull appears to be less than %d dimensional, or a\n\ +computation has overflowed.\n\n\ +Qhull could not construct a clearly convex simplex from points:\n", + qh hull_dim); + qh_printvertexlist(fp, "", qh facet_list, NULL, qh_ALL); + if (!qh_QUICKhelp) + qh_fprintf(fp, 9377, "\n\ +The center point is coplanar with a facet, or a vertex is coplanar\n\ +with a neighboring facet. The maximum round off error for\n\ +computing distances is %2.2g. The center point, facets and distances\n\ +to the center point are as follows:\n\n", qh DISTround); + qh_printpointid(fp, "center point", qh hull_dim, qh interior_point, -1); + qh_fprintf(fp, 9378, "\n"); + FORALLfacets { + qh_fprintf(fp, 9379, "facet"); + FOREACHvertex_(facet->vertices) + qh_fprintf(fp, 9380, " p%d", qh_pointid(vertex->point)); + zinc_(Zdistio); + qh_distplane(qh interior_point, facet, &dist); + qh_fprintf(fp, 9381, " distance= %4.2g\n", dist); + } + if (!qh_QUICKhelp) { + if (qh HALFspace) + qh_fprintf(fp, 9382, "\n\ +These points are the dual of the given halfspaces. They indicate that\n\ +the intersection is degenerate.\n"); + qh_fprintf(fp, 9383,"\n\ +These points either have a maximum or minimum x-coordinate, or\n\ +they maximize the determinant for k coordinates. Trial points\n\ +are first selected from points that maximize a coordinate.\n"); + if (qh hull_dim >= qh_INITIALmax) + qh_fprintf(fp, 9384, "\n\ +Because of the high dimension, the min x-coordinate and max-coordinate\n\ +points are used if the determinant is non-zero. Option 'Qs' will\n\ +do a better, though much slower, job. Instead of 'Qs', you can change\n\ +the points by randomly rotating the input with 'QR0'.\n"); + } + qh_fprintf(fp, 9385, "\nThe min and max coordinates for each dimension are:\n"); + for (k=0; k < qh hull_dim; k++) { + min= REALmax; + max= -REALmin; + for (i=qh num_points, coord= qh first_point+k; i--; coord += qh hull_dim) { + maximize_(max, *coord); + minimize_(min, *coord); + } + qh_fprintf(fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min); + } + if (!qh_QUICKhelp) { + qh_fprintf(fp, 9387, "\n\ +If the input should be full dimensional, you have several options that\n\ +may determine an initial simplex:\n\ + - use 'QJ' to joggle the input and make it full dimensional\n\ + - use 'QbB' to scale the points to the unit cube\n\ + - use 'QR0' to randomly rotate the input for different maximum points\n\ + - use 'Qs' to search all points for the initial simplex\n\ + - use 'En' to specify a maximum roundoff error less than %2.2g.\n\ + - trace execution with 'T3' to see the determinant for each point.\n", + qh DISTround); +#if REALfloat + qh_fprintf(fp, 9388, "\ + - recompile qhull for realT precision(#define REALfloat 0 in libqhull.h).\n"); +#endif + qh_fprintf(fp, 9389, "\n\ +If the input is lower dimensional:\n\ + - use 'QJ' to joggle the input and make it full dimensional\n\ + - use 'Qbk:0Bk:0' to delete coordinate k from the input. You should\n\ + pick the coordinate with the least range. The hull will have the\n\ + correct topology.\n\ + - determine the flat containing the points, rotate the points\n\ + into a coordinate plane, and delete the other coordinates.\n\ + - add one or more points to make the input full dimensional.\n\ +"); + } +} /* printhelp_singular */ + +/*--------------------------------- + + qh_user_memsizes() + allocate up to 10 additional, quick allocation sizes + + notes: + increase maximum number of allocations in qh_initqhull_mem() +*/ +void qh_user_memsizes(void) { + + /* qh_memsize(size); */ +} /* user_memsizes */ diff --git a/extern/qhull/user.h b/extern/qhull/user.h new file mode 100644 index 000000000000..45b3202696a3 --- /dev/null +++ b/extern/qhull/user.h @@ -0,0 +1,855 @@ +/* --------------------------------- + + user.h + user redefinable constants + + see qh-user.htm. see COPYING for copyright information. + + before reading any code, review libqhull.h for data structure definitions and + the "qh" macro. + +Sections: + ============= qhull library constants ====================== + ============= data types and configuration macros ========== + ============= performance related constants ================ + ============= memory constants ============================= + ============= joggle constants ============================= + ============= conditional compilation ====================== + ============= -merge constants- ============================ + +Code flags -- + NOerrors -- the code does not call qh_errexit() + WARN64 -- the code may be incompatible with 64-bit pointers + +*/ + +#include+ +#ifndef qhDEFuser +#define qhDEFuser 1 + +/*============================================================*/ +/*============= qhull library constants ======================*/ +/*============================================================*/ + +/*---------------------------------- + + FILENAMElen -- max length for TI and TO filenames + +*/ + +#define qh_FILENAMElen 500 + +/*---------------------------------- + + msgcode -- Unique message codes for qh_fprintf + + If add new messages, assign these values and increment. + + def counters = [27, 1047, 2059, 3025, 4068, 5003, + 6241, 7079, 8143, 9410, 10000, 11026] + + See: qh_ERR* [libqhull.h] +*/ + +#define MSG_TRACE0 0 +#define MSG_TRACE1 1000 +#define MSG_TRACE2 2000 +#define MSG_TRACE3 3000 +#define MSG_TRACE4 4000 +#define MSG_TRACE5 5000 +#define MSG_ERROR 6000 /* errors written to qh.ferr */ +#define MSG_WARNING 7000 +#define MSG_STDERR 8000 /* log messages Written to qh.ferr */ +#define MSG_OUTPUT 9000 +#define MSG_QHULL_ERROR 10000 /* errors thrown by QhullError [QhullError.h] */ +#define MSG_FIXUP 11000 /* FIXUP QH11... */ +#define MSG_MAXLEN 3000 /* qh_printhelp_degenerate() in user.c */ + + +/*---------------------------------- + + qh_OPTIONline -- max length of an option line 'FO' +*/ +#define qh_OPTIONline 80 + +/*============================================================*/ +/*============= data types and configuration macros ==========*/ +/*============================================================*/ + +/*---------------------------------- + + realT + set the size of floating point numbers + + qh_REALdigits + maximimum number of significant digits + + qh_REAL_1, qh_REAL_2n, qh_REAL_3n + format strings for printf + + qh_REALmax, qh_REALmin + maximum and minimum (near zero) values + + qh_REALepsilon + machine roundoff. Maximum roundoff error for addition and multiplication. + + notes: + Select whether to store floating point numbers in single precision (float) + or double precision (double). + + Use 'float' to save about 8% in time and 25% in space. This is particularly + helpful if high-d where convex hulls are space limited. Using 'float' also + reduces the printed size of Qhull's output since numbers have 8 digits of + precision. + + Use 'double' when greater arithmetic precision is needed. This is needed + for Delaunay triangulations and Voronoi diagrams when you are not merging + facets. + + If 'double' gives insufficient precision, your data probably includes + degeneracies. If so you should use facet merging (done by default) + or exact arithmetic (see imprecision section of manual, qh-impre.htm). + You may also use option 'Po' to force output despite precision errors. + + You may use 'long double', but many format statements need to be changed + and you may need a 'long double' square root routine. S. Grundmann + (sg@eeiwzb.et.tu-dresden.de) has done this. He reports that the code runs + much slower with little gain in precision. + + WARNING: on some machines, int f(){realT a= REALmax;return (a == REALmax);} + returns False. Use (a > REALmax/2) instead of (a == REALmax). + + REALfloat = 1 all numbers are 'float' type + = 0 all numbers are 'double' type +*/ +#define REALfloat 0 + +#if (REALfloat == 1) +#define realT float +#define REALmax FLT_MAX +#define REALmin FLT_MIN +#define REALepsilon FLT_EPSILON +#define qh_REALdigits 8 /* maximum number of significant digits */ +#define qh_REAL_1 "%6.8g " +#define qh_REAL_2n "%6.8g %6.8g\n" +#define qh_REAL_3n "%6.8g %6.8g %6.8g\n" + +#elif (REALfloat == 0) +#define realT double +#define REALmax DBL_MAX +#define REALmin DBL_MIN +#define REALepsilon DBL_EPSILON +#define qh_REALdigits 16 /* maximum number of significant digits */ +#define qh_REAL_1 "%6.16g " +#define qh_REAL_2n "%6.16g %6.16g\n" +#define qh_REAL_3n "%6.16g %6.16g %6.16g\n" + +#else +#error unknown float option +#endif + +/*---------------------------------- + + qh_CPUclock + define the clock() function for reporting the total time spent by Qhull + returns CPU ticks as a 'long int' + qh_CPUclock is only used for reporting the total time spent by Qhull + + qh_SECticks + the number of clock ticks per second + + notes: + looks for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or assumes microseconds + to define a custom clock, set qh_CLOCKtype to 0 + + if your system does not use clock() to return CPU ticks, replace + qh_CPUclock with the corresponding function. It is converted + to 'unsigned long' to prevent wrap-around during long runs. By default, + defines clock_t as 'long' + + Set qh_CLOCKtype to + + 1 for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond + Note: may fail if more than 1 hour elapsed time + + 2 use qh_clock() with POSIX times() (see global.c) +*/ +#define qh_CLOCKtype 1 /* change to the desired number */ + +#if (qh_CLOCKtype == 1) + +#if defined(CLOCKS_PER_SECOND) +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */ +#define qh_SECticks CLOCKS_PER_SECOND + +#elif defined(CLOCKS_PER_SEC) +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */ +#define qh_SECticks CLOCKS_PER_SEC + +#elif defined(CLK_TCK) +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */ +#define qh_SECticks CLK_TCK + +#else +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */ +#define qh_SECticks 1E6 +#endif + +#elif (qh_CLOCKtype == 2) +#define qh_CPUclock qh_clock() /* return CPU clock */ +#define qh_SECticks 100 + +#else /* qh_CLOCKtype == ? */ +#error unknown clock option +#endif + +/*---------------------------------- + + qh_RANDOMtype, qh_RANDOMmax, qh_RANDOMseed + define random number generator + + qh_RANDOMint generates a random integer between 0 and qh_RANDOMmax. + qh_RANDOMseed sets the random number seed for qh_RANDOMint + + Set qh_RANDOMtype (default 5) to: + 1 for random() with 31 bits (UCB) + 2 for rand() with RAND_MAX or 15 bits (system 5) + 3 for rand() with 31 bits (Sun) + 4 for lrand48() with 31 bits (Solaris) + 5 for qh_rand() with 31 bits (included with Qhull) + + notes: + Random numbers are used by rbox to generate point sets. Random + numbers are used by Qhull to rotate the input ('QRn' option), + simulate a randomized algorithm ('Qr' option), and to simulate + roundoff errors ('Rn' option). + + Random number generators differ between systems. Most systems provide + rand() but the period varies. The period of rand() is not critical + since qhull does not normally use random numbers. + + The default generator is Park & Miller's minimal standard random + number generator [CACM 31:1195 '88]. It is included with Qhull. + + If qh_RANDOMmax is wrong, qhull will report a warning and Geomview + output will likely be invisible. +*/ +#define qh_RANDOMtype 5 /* *** change to the desired number *** */ + +#if (qh_RANDOMtype == 1) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, random()/MAX */ +#define qh_RANDOMint random() +#define qh_RANDOMseed_(seed) srandom(seed); + +#elif (qh_RANDOMtype == 2) +#ifdef RAND_MAX +#define qh_RANDOMmax ((realT)RAND_MAX) +#else +#define qh_RANDOMmax ((realT)32767) /* 15 bits (System 5) */ +#endif +#define qh_RANDOMint rand() +#define qh_RANDOMseed_(seed) srand((unsigned)seed); + +#elif (qh_RANDOMtype == 3) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, Sun */ +#define qh_RANDOMint rand() +#define qh_RANDOMseed_(seed) srand((unsigned)seed); + +#elif (qh_RANDOMtype == 4) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, lrand38()/MAX */ +#define qh_RANDOMint lrand48() +#define qh_RANDOMseed_(seed) srand48(seed); + +#elif (qh_RANDOMtype == 5) +#define qh_RANDOMmax ((realT)2147483646UL) /* 31 bits, qh_rand/MAX */ +#define qh_RANDOMint qh_rand() +#define qh_RANDOMseed_(seed) qh_srand(seed); +/* unlike rand(), never returns 0 */ + +#else +#error: unknown random option +#endif + +/*---------------------------------- + + qh_ORIENTclock + 0 for inward pointing normals by Geomview convention +*/ +#define qh_ORIENTclock 0 + + +/*============================================================*/ +/*============= joggle constants =============================*/ +/*============================================================*/ + +/*---------------------------------- + +qh_JOGGLEdefault +default qh.JOGGLEmax is qh.DISTround * qh_JOGGLEdefault + +notes: +rbox s r 100 | qhull QJ1e-15 QR0 generates 90% faults at distround 7e-16 +rbox s r 100 | qhull QJ1e-14 QR0 generates 70% faults +rbox s r 100 | qhull QJ1e-13 QR0 generates 35% faults +rbox s r 100 | qhull QJ1e-12 QR0 generates 8% faults +rbox s r 100 | qhull QJ1e-11 QR0 generates 1% faults +rbox s r 100 | qhull QJ1e-10 QR0 generates 0% faults +rbox 1000 W0 | qhull QJ1e-12 QR0 generates 86% faults +rbox 1000 W0 | qhull QJ1e-11 QR0 generates 20% faults +rbox 1000 W0 | qhull QJ1e-10 QR0 generates 2% faults +the later have about 20 points per facet, each of which may interfere + +pick a value large enough to avoid retries on most inputs +*/ +#define qh_JOGGLEdefault 30000.0 + +/*---------------------------------- + +qh_JOGGLEincrease +factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain +*/ +#define qh_JOGGLEincrease 10.0 + +/*---------------------------------- + +qh_JOGGLEretry +if ZZretry = qh_JOGGLEretry, increase qh.JOGGLEmax + +notes: +try twice at the original value in case of bad luck the first time +*/ +#define qh_JOGGLEretry 2 + +/*---------------------------------- + +qh_JOGGLEagain +every following qh_JOGGLEagain, increase qh.JOGGLEmax + +notes: +1 is OK since it's already failed qh_JOGGLEretry times +*/ +#define qh_JOGGLEagain 1 + +/*---------------------------------- + +qh_JOGGLEmaxincrease +maximum qh.JOGGLEmax due to qh_JOGGLEincrease +relative to qh.MAXwidth + +notes: +qh.joggleinput will retry at this value until qh_JOGGLEmaxretry +*/ +#define qh_JOGGLEmaxincrease 1e-2 + +/*---------------------------------- + +qh_JOGGLEmaxretry +stop after qh_JOGGLEmaxretry attempts +*/ +#define qh_JOGGLEmaxretry 100 + +/*============================================================*/ +/*============= performance related constants ================*/ +/*============================================================*/ + +/*---------------------------------- + + qh_HASHfactor + total hash slots / used hash slots. Must be at least 1.1. + + notes: + =2 for at worst 50% occupancy for qh hash_table and normally 25% occupancy +*/ +#define qh_HASHfactor 2 + +/*---------------------------------- + + qh_VERIFYdirect + with 'Tv' verify all points against all facets if op count is smaller + + notes: + if greater, calls qh_check_bestdist() instead +*/ +#define qh_VERIFYdirect 1000000 + +/*---------------------------------- + + qh_INITIALsearch + if qh_INITIALmax, search points up to this dimension +*/ +#define qh_INITIALsearch 6 + +/*---------------------------------- + + qh_INITIALmax + if dim >= qh_INITIALmax, use min/max coordinate points for initial simplex + + notes: + from points with non-zero determinants + use option 'Qs' to override (much slower) +*/ +#define qh_INITIALmax 8 + +/*============================================================*/ +/*============= memory constants =============================*/ +/*============================================================*/ + +/*---------------------------------- + + qh_MEMalign + memory alignment for qh_meminitbuffers() in global.c + + notes: + to avoid bus errors, memory allocation must consider alignment requirements. + malloc() automatically takes care of alignment. Since mem.c manages + its own memory, we need to explicitly specify alignment in + qh_meminitbuffers(). + + A safe choice is sizeof(double). sizeof(float) may be used if doubles + do not occur in data structures and pointers are the same size. Be careful + of machines (e.g., DEC Alpha) with large pointers. + + If using gcc, best alignment is + #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *)) +*/ +#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *)))) + +/*---------------------------------- + + qh_MEMbufsize + size of additional memory buffers + + notes: + used for qh_meminitbuffers() in global.c +*/ +#define qh_MEMbufsize 0x10000 /* allocate 64K memory buffers */ + +/*---------------------------------- + + qh_MEMinitbuf + size of initial memory buffer + + notes: + use for qh_meminitbuffers() in global.c +*/ +#define qh_MEMinitbuf 0x20000 /* initially allocate 128K buffer */ + +/*---------------------------------- + + qh_INFINITE + on output, indicates Voronoi center at infinity +*/ +#define qh_INFINITE -10.101 + +/*---------------------------------- + + qh_DEFAULTbox + default box size (Geomview expects 0.5) + + qh_DEFAULTbox + default box size for integer coorindate (rbox only) +*/ +#define qh_DEFAULTbox 0.5 +#define qh_DEFAULTzbox 1e6 + +/*============================================================*/ +/*============= conditional compilation ======================*/ +/*============================================================*/ + +/*---------------------------------- + + __cplusplus + defined by C++ compilers + + __MSC_VER + defined by Microsoft Visual C++ + + __MWERKS__ && __POWERPC__ + defined by Metrowerks when compiling for the Power Macintosh + + __STDC__ + defined for strict ANSI C +*/ + +/*---------------------------------- + + qh_COMPUTEfurthest + compute furthest distance to an outside point instead of storing it with the facet + =1 to compute furthest + + notes: + computing furthest saves memory but costs time + about 40% more distance tests for partitioning + removes facet->furthestdist +*/ +#define qh_COMPUTEfurthest 0 + +/*---------------------------------- + + qh_KEEPstatistics + =0 removes most of statistic gathering and reporting + + notes: + if 0, code size is reduced by about 4%. +*/ +#define qh_KEEPstatistics 1 + +/*---------------------------------- + + qh_MAXoutside + record outer plane for each facet + =1 to record facet->maxoutside + + notes: + this takes a realT per facet and slightly slows down qhull + it produces better outer planes for geomview output +*/ +#define qh_MAXoutside 1 + +/*---------------------------------- + + qh_NOmerge + disables facet merging if defined + + notes: + This saves about 10% space. + + Unless 'Q0' + qh_NOmerge sets 'QJ' to avoid precision errors + + #define qh_NOmerge + + see: + qh_NOmem in mem.c + + see user.c/user_eg.c for removing io.o +*/ + +/*---------------------------------- + + qh_NOtrace + no tracing if defined + + notes: + This saves about 5% space. + + #define qh_NOtrace +*/ + +/*---------------------------------- + + qh_QHpointer + access global data with pointer or static structure + + qh_QHpointer = 1 access globals via a pointer to allocated memory + enables qh_saveqhull() and qh_restoreqhull() + [2010, gcc] costs about 4% in time and 4% in space + [2003, msvc] costs about 8% in time and 2% in space + + = 0 qh_qh and qh_qhstat are static data structures + only one instance of qhull() can be active at a time + default value + + qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h] + It is required for msvc-2005. It is not needed for gcc. + + notes: + all global variables for qhull are in qh, qhmem, and qhstat + qh is defined in libqhull.h + qhmem is defined in mem.h + qhstat is defined in stat.h + C++ build defines qh_QHpointer [libqhullp.pro, libqhullcpp.pro] + + see: + user_eg.c for an example +*/ +#ifdef qh_QHpointer +#if qh_dllimport +#error QH6207 Qhull error: Use qh_QHpointer_dllimport instead of qh_dllimport with qh_QHpointer +#endif +#else +#define qh_QHpointer 0 +#if qh_QHpointer_dllimport +#error QH6234 Qhull error: Use qh_dllimport instead of qh_QHpointer_dllimport when qh_QHpointer is not defined +#endif +#endif +#if 0 /* sample code */ + qhT *oldqhA, *oldqhB; + + exitcode= qh_new_qhull(dim, numpoints, points, ismalloc, + flags, outfile, errfile); + /* use results from first call to qh_new_qhull */ + oldqhA= qh_save_qhull(); + exitcode= qh_new_qhull(dimB, numpointsB, pointsB, ismalloc, + flags, outfile, errfile); + /* use results from second call to qh_new_qhull */ + oldqhB= qh_save_qhull(); + qh_restore_qhull(&oldqhA); + /* use results from first call to qh_new_qhull */ + qh_freeqhull(qh_ALL); /* frees all memory used by first call */ + qh_restore_qhull(&oldqhB); + /* use results from second call to qh_new_qhull */ + qh_freeqhull(!qh_ALL); /* frees long memory used by second call */ + qh_memfreeshort(&curlong, &totlong); /* frees short memory and memory allocator */ +#endif + +/*---------------------------------- + + qh_QUICKhelp + =1 to use abbreviated help messages, e.g., for degenerate inputs +*/ +#define qh_QUICKhelp 0 + +/*============================================================*/ +/*============= -merge constants- ============================*/ +/*============================================================*/ +/* + These constants effect facet merging. You probably will not need + to modify them. They effect the performance of facet merging. +*/ + +/*---------------------------------- + + qh_DIMmergeVertex + max dimension for vertex merging (it is not effective in high-d) +*/ +#define qh_DIMmergeVertex 6 + +/*---------------------------------- + + qh_DIMreduceBuild + max dimension for vertex reduction during build (slow in high-d) +*/ +#define qh_DIMreduceBuild 5 + +/*---------------------------------- + + qh_BESTcentrum + if > 2*dim+n vertices, qh_findbestneighbor() tests centrums (faster) + else, qh_findbestneighbor() tests all vertices (much better merges) + + qh_BESTcentrum2 + if qh_BESTcentrum2 * DIM3 + BESTcentrum < #vertices tests centrums +*/ +#define qh_BESTcentrum 20 +#define qh_BESTcentrum2 2 + +/*---------------------------------- + + qh_BESTnonconvex + if > dim+n neighbors, qh_findbestneighbor() tests nonconvex ridges. + + notes: + It is needed because qh_findbestneighbor is slow for large facets +*/ +#define qh_BESTnonconvex 15 + +/*---------------------------------- + + qh_MAXnewmerges + if >n newmerges, qh_merge_nonconvex() calls qh_reducevertices_centrums. + + notes: + It is needed because postmerge can merge many facets at once +*/ +#define qh_MAXnewmerges 2 + +/*---------------------------------- + + qh_MAXnewcentrum + if <= dim+n vertices (n approximates the number of merges), + reset the centrum in qh_updatetested() and qh_mergecycle_facets() + + notes: + needed to reduce cost and because centrums may move too much if + many vertices in high-d +*/ +#define qh_MAXnewcentrum 5 + +/*---------------------------------- + + qh_COPLANARratio + for 3-d+ merging, qh.MINvisible is n*premerge_centrum + + notes: + for non-merging, it's DISTround +*/ +#define qh_COPLANARratio 3 + +/*---------------------------------- + + qh_DISToutside + When is a point clearly outside of a facet? + Stops search in qh_findbestnew or qh_partitionall + qh_findbest uses qh.MINoutside since since it is only called if no merges. + + notes: + 'Qf' always searches for best facet + if !qh.MERGING, same as qh.MINoutside. + if qh_USEfindbestnew, increase value since neighboring facets may be ill-behaved + [Note: Zdelvertextot occurs normally with interior points] + RBOX 1000 s Z1 G1e-13 t1001188774 | QHULL Tv + When there is a sharp edge, need to move points to a + clearly good facet; otherwise may be lost in another partitioning. + if too big then O(n^2) behavior for partitioning in cone + if very small then important points not processed + Needed in qh_partitionall for + RBOX 1000 s Z1 G1e-13 t1001032651 | QHULL Tv + Needed in qh_findbestnew for many instances of + RBOX 1000 s Z1 G1e-13 t | QHULL Tv + + See: + qh_DISToutside -- when is a point clearly outside of a facet + qh_SEARCHdist -- when is facet coplanar with the best facet? + qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint() +*/ +#define qh_DISToutside ((qh_USEfindbestnew ? 2 : 1) * \ + fmax_((qh MERGING ? 2 : 1)*qh MINoutside, qh max_outside)) + +/*---------------------------------- + + qh_RATIOnearinside + ratio of qh.NEARinside to qh.ONEmerge for retaining inside points for + qh_check_maxout(). + + notes: + This is overkill since do not know the correct value. + It effects whether 'Qc' reports all coplanar points + Not used for 'd' since non-extreme points are coplanar +*/ +#define qh_RATIOnearinside 5 + +/*---------------------------------- + + qh_SEARCHdist + When is a facet coplanar with the best facet? + qh_findbesthorizon: all coplanar facets of the best facet need to be searched. + + See: + qh_DISToutside -- when is a point clearly outside of a facet + qh_SEARCHdist -- when is facet coplanar with the best facet? + qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint() +*/ +#define qh_SEARCHdist ((qh_USEfindbestnew ? 2 : 1) * \ + (qh max_outside + 2 * qh DISTround + fmax_( qh MINvisible, qh MAXcoplanar))); + +/*---------------------------------- + + qh_USEfindbestnew + Always use qh_findbestnew for qh_partitionpoint, otherwise use + qh_findbestnew if merged new facet or sharpnewfacets. + + See: + qh_DISToutside -- when is a point clearly outside of a facet + qh_SEARCHdist -- when is facet coplanar with the best facet? + qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint() +*/ +#define qh_USEfindbestnew (zzval_(Ztotmerge) > 50) + +/*---------------------------------- + + qh_WIDEcoplanar + n*MAXcoplanar or n*MINvisible for a WIDEfacet + + if vertex is further than qh.WIDEfacet from the hyperplane + then its ridges are not counted in computing the area, and + the facet's centrum is frozen. + + notes: + qh.WIDEfacet= max(qh.MAXoutside,qh_WIDEcoplanar*qh.MAXcoplanar, + qh_WIDEcoplanar * qh.MINvisible); +*/ +#define qh_WIDEcoplanar 6 + +/*---------------------------------- + + qh_MAXnarrow + max. cosine in initial hull that sets qh.NARROWhull + + notes: + If qh.NARROWhull, the initial partition does not make + coplanar points. If narrow, a coplanar point can be + coplanar to two facets of opposite orientations and + distant from the exact convex hull. + + Conservative estimate. Don't actually see problems until it is -1.0 +*/ +#define qh_MAXnarrow -0.99999999 + +/*---------------------------------- + + qh_WARNnarrow + max. cosine in initial hull to warn about qh.NARROWhull + + notes: + this is a conservative estimate. + Don't actually see problems until it is -1.0. See qh-impre.htm +*/ +#define qh_WARNnarrow -0.999999999999999 + +/*---------------------------------- + + qh_ZEROdelaunay + a zero Delaunay facet occurs for input sites coplanar with their convex hull + the last normal coefficient of a zero Delaunay facet is within + qh_ZEROdelaunay * qh.ANGLEround of 0 + + notes: + qh_ZEROdelaunay does not allow for joggled input ('QJ'). + + You can avoid zero Delaunay facets by surrounding the input with a box. + + Use option 'PDk:-n' to explicitly define zero Delaunay facets + k= dimension of input sites (e.g., 3 for 3-d Delaunay triangulation) + n= the cutoff for zero Delaunay facets (e.g., 'PD3:-1e-12') +*/ +#define qh_ZEROdelaunay 2 + +#endif /* qh_DEFuser */ diff --git a/extern/qhull/usermem.c b/extern/qhull/usermem.c new file mode 100644 index 000000000000..722c0b42f871 --- /dev/null +++ b/extern/qhull/usermem.c @@ -0,0 +1,62 @@ +/* --------------------------------- + + usermem.c + qh_exit(), qh_free(), and qh_malloc() + + See README.txt. + + If you redefine one of these functions you must redefine all of them. + If you recompile and load this file, then usermem.o will not be loaded + from qhull.a or qhull.lib + + See libqhull.h for data structures, macros, and user-callable functions. + See user.c for qhull-related, redefinable functions + see user.h for user-definable constants + See userprintf.c for qh_fprintf and userprintf_rbox,c for qh_fprintf_rbox + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull.h" + +#include+ +/*--------------------------------- + + qh_exit( exitcode ) + exit program + + notes: + same as exit() +*/ +void qh_exit(int exitcode) { + exit(exitcode); +} /* exit */ + +/*--------------------------------- + +qh_free( mem ) +free memory + +notes: +same as free() +*/ +void qh_free(void *mem) { + free(mem); +} /* free */ + +/*--------------------------------- + + qh_malloc( mem ) + allocate memory + + notes: + same as malloc() +*/ +void *qh_malloc(size_t size) { + return malloc(size); +} /* malloc */ diff --git a/extern/qhull/userprintf.c b/extern/qhull/userprintf.c new file mode 100644 index 000000000000..d6c245b217b4 --- /dev/null +++ b/extern/qhull/userprintf.c @@ -0,0 +1,62 @@ +/* --------------------------------- + + userprintf.c + qh_fprintf() + + see README.txt see COPYING.txt for copyright information. + + If you recompile and load this file, then userprintf.o will not be loaded + from qhull.a or qhull.lib + + See libqhull.h for data structures, macros, and user-callable functions. + See user.c for qhull-related, redefinable functions + see user.h for user-definable constants + See usermem.c for qh_exit(), qh_free(), and qh_malloc() + see Qhull.cpp and RboxPoints.cpp for examples. + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull.h" + +#include+#include +#include + +/*--------------------------------- + + qh_fprintf(fp, msgcode, format, list of args ) + print arguments to *fp according to format + Use qh_fprintf_rbox() for rboxlib.c + + notes: + same as fprintf() + fgets() is not trapped like fprintf() + exit qh_fprintf via qh_errexit() +*/ + +void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ) { + va_list args; + + if (!fp) { + fprintf(stderr, "QH6232 Qhull internal error (userprintf.c): fp is 0. Wrong qh_fprintf called.\n"); + qh_errexit(6232, NULL, NULL); + } + va_start(args, fmt); +#if qh_QHpointer + if (qh_qh && qh ANNOTATEoutput) { +#else + if (qh ANNOTATEoutput) { +#endif + fprintf(fp, "[QH%.4d]", msgcode); + }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) { + fprintf(fp, "QH%.4d ", msgcode); + } + vfprintf(fp, fmt, args); + va_end(args); + + /* Place debugging traps here. Use with option 'Tn' */ + +} /* qh_fprintf */ diff --git a/extern/qhull/userprintf_rbox.c b/extern/qhull/userprintf_rbox.c new file mode 100644 index 000000000000..0e1a12ac3f34 --- /dev/null +++ b/extern/qhull/userprintf_rbox.c @@ -0,0 +1,52 @@ +/* --------------------------------- + + userprintf_rbox.c + qh_fprintf_rbox() + + see README.txt see COPYING.txt for copyright information. + + If you recompile and load this file, then userprintf_rbox.o will not be loaded + from qhull.a or qhull.lib + + See libqhull.h for data structures, macros, and user-callable functions. + See user.c for qhull-related, redefinable functions + see user.h for user-definable constants + See usermem.c for qh_exit(), qh_free(), and qh_malloc() + see Qhull.cpp and RboxPoints.cpp for examples. + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull.h" + +#include+#include +#include + +/*--------------------------------- + + qh_fprintf_rbox(fp, msgcode, format, list of args ) + print arguments to *fp according to format + Use qh_fprintf_rbox() for rboxlib.c + + notes: + same as fprintf() + fgets() is not trapped like fprintf() + exit qh_fprintf_rbox via qh_errexit_rbox() +*/ + +void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ) { + va_list args; + + if (!fp) { + fprintf(stderr, "QH6231 Qhull internal error (userprintf.c): fp is 0. Wrong qh_fprintf_rbox called.\n"); + qh_errexit_rbox(6231); + } + if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR) + fprintf(fp, "QH%.4d ", msgcode); + va_start(args, fmt); + vfprintf(fp, fmt, args); + va_end(args); +} /* qh_fprintf_rbox */ diff --git a/lib/matplotlib/delaunay/triangulate.py b/lib/matplotlib/delaunay/triangulate.py index b5eaf8d2739c..7de24ad78215 100644 --- a/lib/matplotlib/delaunay/triangulate.py +++ b/lib/matplotlib/delaunay/triangulate.py @@ -11,6 +11,11 @@ from matplotlib._delaunay import delaunay from .interpolate import LinearInterpolator, NNInterpolator +from matplotlib.cbook import warn_deprecated +warn_deprecated('1.4', + name='matplotlib.delaunay', + alternative='matplotlib.tri.Triangulation', + obj_type='module') __all__ = ['Triangulation', 'DuplicatePointWarning'] diff --git a/lib/matplotlib/mlab.py b/lib/matplotlib/mlab.py index 2ad5e9fc3a7b..cd39a8269014 100644 --- a/lib/matplotlib/mlab.py +++ b/lib/matplotlib/mlab.py @@ -3356,124 +3356,131 @@ def newfunc(val, mask, mval): if opened: fh.close() -def griddata(x,y,z,xi,yi,interp='nn'): - """ - ``zi = griddata(x,y,z,xi,yi)`` fits a surface of the form *z* = - *f*(*x*, *y*) to the data in the (usually) nonuniformly spaced - vectors (*x*, *y*, *z*). :func:`griddata` interpolates this - surface at the points specified by (*xi*, *yi*) to produce - *zi*. *xi* and *yi* must describe a regular grid, can be either 1D - or 2D, but must be monotonically increasing. - - A masked array is returned if any grid points are outside convex - hull defined by input data (no extrapolation is done). - - If interp keyword is set to '`nn`' (default), - uses natural neighbor interpolation based on Delaunay - triangulation. By default, this algorithm is provided by the - :mod:`matplotlib.delaunay` package, written by Robert Kern. The - triangulation algorithm in this package is known to fail on some - nearly pathological cases. For this reason, a separate toolkit - (:mod:`mpl_tookits.natgrid`) has been created that provides a more - robust algorithm fof triangulation and interpolation. This - toolkit is based on the NCAR natgrid library, which contains code - that is not redistributable under a BSD-compatible license. When - installed, this function will use the :mod:`mpl_toolkits.natgrid` - algorithm, otherwise it will use the built-in - :mod:`matplotlib.delaunay` package. - - If the interp keyword is set to '`linear`', then linear interpolation - is used instead of natural neighbor. In this case, the output grid - is assumed to be regular with a constant grid spacing in both the x and - y directions. For regular grids with nonconstant grid spacing, you - must use natural neighbor interpolation. Linear interpolation is only valid if - :mod:`matplotlib.delaunay` package is used - :mod:`mpl_tookits.natgrid` - only provides natural neighbor interpolation. - - The natgrid matplotlib toolkit can be downloaded from - http://sourceforge.net/project/showfiles.php?group_id=80706&package_id=142792 - """ - try: - from mpl_toolkits.natgrid import _natgrid, __version__ - _use_natgrid = True - except ImportError: - import matplotlib.delaunay as delaunay - from matplotlib.delaunay import __version__ - _use_natgrid = False - if not griddata._reported: - if _use_natgrid: - verbose.report('using natgrid version %s' % __version__) - else: - verbose.report('using delaunay version %s' % __version__) - griddata._reported = True + +def griddata(x, y, z, xi, yi, interp='nn'): + """Interpolates from a nonuniformly spaced grid to some other + grid. + + Fits a surface of the form z = f(`x`, `y`) to the data in the + (usually) nonuniformly spaced vectors (`x`, `y`, `z`), then + interpolates this surface at the points specified by + (`xi`, `yi`) to produce `zi`. + + Parameters + ---------- + x, y, z : 1d array_like + Coordinates of grid points to interpolate from. + xi, yi : 1d or 2d array_like + Coordinates of grid points to interpolate to. + interp : string key from {'nn', 'linear'} + Interpolation algorithm, either 'nn' for natural neighbor, or + 'linear' for linear interpolation. + + Returns + ------- + 2d float array + Array of values interpolated at (`xi`, `yi`) points. Array + will be masked is any of (`xi`, `yi`) are outside the convex + hull of (`x`, `y`). + + Notes + ----- + If `interp` is 'nn' (the default), uses natural neighbor + interpolation based on Delaunay triangulation. This option is + only available if the mpl_toolkits.natgrid module is installed. + This can be downloaded from https://github.com/matplotlib/natgrid. + The (`xi`, `yi`) grid must be regular and monotonically increasing + in this case. + + If `interp` is 'linear', linear interpolation is used via + matplotlib.tri.LinearTriInterpolator. + + Instead of using `griddata`, more flexible functionality and other + interpolation options are available using a + matplotlib.tri.Triangulation and a matplotlib.tri.TriInterpolator. + """ + # Check input arguments. + x = np.asanyarray(x, dtype=np.float64) + y = np.asanyarray(y, dtype=np.float64) + z = np.asanyarray(z, dtype=np.float64) + if x.shape != y.shape or x.shape != z.shape or x.ndim != 1: + raise ValueError("x, y and z must be equal-length 1-D arrays") + + xi = np.asanyarray(xi, dtype=np.float64) + yi = np.asanyarray(yi, dtype=np.float64) if xi.ndim != yi.ndim: - raise TypeError("inputs xi and yi must have same number of dimensions (1 or 2)") - if xi.ndim != 1 and xi.ndim != 2: - raise TypeError("inputs xi and yi must be 1D or 2D.") - if not len(x)==len(y)==len(z): - raise TypeError("inputs x,y,z must all be 1D arrays of the same length") - # remove masked points. - if hasattr(z,'mask'): - # make sure mask is not a scalar boolean array. - if z.mask.ndim: - x = x.compress(z.mask == False) - y = y.compress(z.mask == False) - z = z.compressed() - if _use_natgrid: # use natgrid toolkit if available. - if interp != 'nn': - raise ValueError("only natural neighor interpolation" - " allowed when using natgrid toolkit in griddata.") + raise ValueError("xi and yi must be arrays with the same number of " + "dimensions (1 or 2)") + if xi.ndim == 2 and xi.shape != yi.shape: + raise ValueError("if xi and yi are 2D arrays, they must have the same " + "shape") + if xi.ndim == 1: + xi, yi = np.meshgrid(xi, yi) + + if interp == 'nn': + use_nn_interpolation = True + elif interp == 'linear': + use_nn_interpolation = False + else: + raise ValueError("interp keyword must be one of 'linear' (for linear " + "interpolation) or 'nn' (for natural neighbor " + "interpolation). Default is 'nn'.") + + # Remove masked points. + mask = np.ma.getmask(z) + if not (mask is np.ma.nomask): + x = x.compress(~mask) + y = y.compress(~mask) + z = z.compressed() + + if use_nn_interpolation: + try: + from mpl_toolkits.natgrid import _natgrid + except ImportError: + raise RuntimeError("To use interp='nn' (Natural Neighbor " + "interpolation) in griddata, natgrid must be installed. " + "Either install it from http://sourceforge.net/projects/" + "matplotlib/files/matplotlib-toolkits, or use interp='linear' " + "instead.") + if xi.ndim == 2: - xi = xi[0,:] - yi = yi[:,0] - # override default natgrid internal parameters. - _natgrid.seti('ext',0) - _natgrid.setr('nul',np.nan) - # cast input arrays to doubles (this makes a copy) - x = x.astype(np.float) - y = y.astype(np.float) - z = z.astype(np.float) - xo = xi.astype(np.float) - yo = yi.astype(np.float) - if min(xo[1:]-xo[0:-1]) < 0 or min(yo[1:]-yo[0:-1]) < 0: - raise ValueError('output grid defined by xi,yi must be monotone increasing') - # allocate array for output (buffer will be overwritten by nagridd) - zo = np.empty((yo.shape[0],xo.shape[0]), np.float) - _natgrid.natgridd(x,y,z,xo,yo,zo) - else: # use Robert Kern's delaunay package from scikits (default) - if xi.ndim != yi.ndim: - raise TypeError("inputs xi and yi must have same number of dimensions (1 or 2)") - if xi.ndim != 1 and xi.ndim != 2: - raise TypeError("inputs xi and yi must be 1D or 2D.") - if xi.ndim == 1: - xi,yi = np.meshgrid(xi,yi) - # triangulate data - tri = delaunay.Triangulation(x,y) - # interpolate data - if interp == 'nn': - interp = tri.nn_interpolator(z) - zo = interp(xi,yi) - elif interp == 'linear': - # make sure grid has constant dx, dy - dx = xi[0,1:]-xi[0,0:-1] - dy = yi[1:,0]-yi[0:-1,0] - epsx = np.finfo(xi.dtype).resolution - epsy = np.finfo(yi.dtype).resolution - if dx.max()-dx.min() > epsx or dy.max()-dy.min() > epsy: - raise ValueError("output grid must have constant spacing" - " when using interp='linear'") - interp = tri.linear_interpolator(z) - zo = interp[yi.min():yi.max():complex(0,yi.shape[0]), - xi.min():xi.max():complex(0,xi.shape[1])] - else: - raise ValueError("interp keyword must be one of" - " 'linear' (for linear interpolation) or 'nn'" - " (for natural neighbor interpolation). Default is 'nn'.") - # mask points on grid outside convex hull of input data. - if np.any(np.isnan(zo)): - zo = np.ma.masked_where(np.isnan(zo),zo) - return zo -griddata._reported = False + # natgrid expects 1D xi and yi arrays. + xi = xi[0, :] + yi = yi[:, 0] + + # Override default natgrid internal parameters. + _natgrid.seti('ext', 0) + _natgrid.setr('nul', np.nan) + + if np.min(np.diff(xi)) < 0 or np.min(np.diff(yi)) < 0: + raise ValueError("Output grid defined by xi,yi must be monotone " + "increasing") + + # Allocate array for output (buffer will be overwritten by natgridd) + zi = np.empty((yi.shape[0], xi.shape[0]), np.float64) + + # Natgrid requires each array to be contiguous rather than e.g. a view + # that is a non-contiguous slice of another array. Use numpy.require + # to deal with this, which will copy if necessary. + x = np.require(x, requirements=['C']) + y = np.require(y, requirements=['C']) + z = np.require(z, requirements=['C']) + xi = np.require(xi, requirements=['C']) + yi = np.require(yi, requirements=['C']) + _natgrid.natgridd(x, y, z, xi, yi, zi) + + # Mask points on grid outside convex hull of input data. + if np.any(np.isnan(zi)): + zi = np.ma.masked_where(np.isnan(zi), zi) + return zi + else: + # Linear interpolation performed using a matplotlib.tri.Triangulation + # and a matplotlib.tri.LinearTriInterpolator. + from .tri import Triangulation, LinearTriInterpolator + triang = Triangulation(x, y) + interpolator = LinearTriInterpolator(triang, z) + return interpolator(xi, yi) + ################################################## # Linear interpolation algorithms diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.pdf b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.pdf deleted file mode 100644 index 33f10c1b4a64..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.svg b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.svg deleted file mode 100644 index bc1730e04c4a..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.svg +++ /dev/null @@ -1,1229 +0,0 @@ - - - - diff --git a/lib/matplotlib/tests/test_coding_standards.py b/lib/matplotlib/tests/test_coding_standards.py index 8dcc99a25add..e24f5eb837c5 100644 --- a/lib/matplotlib/tests/test_coding_standards.py +++ b/lib/matplotlib/tests/test_coding_standards.py @@ -34,7 +34,8 @@ 'ttconv.py', '_gtkagg.py', '_backend_gdk.py', - 'pyparsing*'] + 'pyparsing*', + '_qhull.py'] PEP8_ADDITIONAL_IGNORE = ('E121', 'E122', 'E123', 'E124', 'E125', 'E126', 'E127', 'E128') EXPECTED_BAD_FILES = ['*/matplotlib/__init__.py', diff --git a/lib/matplotlib/tests/test_mlab.py b/lib/matplotlib/tests/test_mlab.py index 3b4d9ed6de17..b6a20fafc3ea 100644 --- a/lib/matplotlib/tests/test_mlab.py +++ b/lib/matplotlib/tests/test_mlab.py @@ -8,6 +8,15 @@ import matplotlib.cbook as cbook import tempfile import unittest +from nose.tools import assert_raises +from matplotlib.testing.decorators import knownfailureif + + +try: + from mpl_toolkits.natgrid import _natgrid + HAS_NATGRID = True +except ImportError: + HAS_NATGRID = False from numpy.testing import assert_allclose, assert_array_equal @@ -2686,6 +2695,73 @@ def setUp(self): iscomplex=True, sides='default', nsides=2) +def test_griddata_linear(): + # z is a linear function of x and y. + def get_z(x, y): + return 3.0*x - y + + # Passing 1D xi and yi arrays to griddata. + x = np.asarray([0.0, 1.0, 0.0, 1.0, 0.5]) + y = np.asarray([0.0, 0.0, 1.0, 1.0, 0.5]) + z = get_z(x, y) + xi = [0.2, 0.4, 0.6, 0.8] + yi = [0.1, 0.3, 0.7, 0.9] + zi = mlab.griddata(x, y, z, xi, yi, interp='linear') + xi, yi = np.meshgrid(xi, yi) + np.testing.assert_array_almost_equal(zi, get_z(xi, yi)) + + # Passing 2D xi and yi arrays to griddata. + zi = mlab.griddata(x, y, z, xi, yi, interp='linear') + np.testing.assert_array_almost_equal(zi, get_z(xi, yi)) + + # Masking z array. + z_masked = np.ma.array(z, mask=[False, False, False, True, False]) + correct_zi_masked = np.ma.masked_where(xi + yi > 1.0, get_z(xi, yi)) + zi = mlab.griddata(x, y, z_masked, xi, yi, interp='linear') + np.testing.assert_array_almost_equal(zi, correct_zi_masked) + np.testing.assert_array_equal(np.ma.getmask(zi), + np.ma.getmask(correct_zi_masked)) + + +@knownfailureif(not HAS_NATGRID) +def test_griddata_nn(): + # z is a linear function of x and y. + def get_z(x, y): + return 3.0*x - y + + # Passing 1D xi and yi arrays to griddata. + x = np.asarray([0.0, 1.0, 0.0, 1.0, 0.5]) + y = np.asarray([0.0, 0.0, 1.0, 1.0, 0.5]) + z = get_z(x, y) + xi = [0.2, 0.4, 0.6, 0.8] + yi = [0.1, 0.3, 0.7, 0.9] + correct_zi = [[0.49999252, 1.0999978, 1.7000030, 2.3000080], + [0.29999208, 0.8999978, 1.5000029, 2.1000059], + [-0.1000099, 0.4999943, 1.0999964, 1.6999979], + [-0.3000128, 0.2999894, 0.8999913, 1.4999933]] + zi = mlab.griddata(x, y, z, xi, yi, interp='nn') + np.testing.assert_array_almost_equal(zi, correct_zi) + + # Decreasing xi or yi should raise ValueError. + assert_raises(ValueError, mlab.griddata, x, y, z, xi[::-1], yi, + interp='nn') + assert_raises(ValueError, mlab.griddata, x, y, z, xi, yi[::-1], + interp='nn') + + # Passing 2D xi and yi arrays to griddata. + xi, yi = np.meshgrid(xi, yi) + zi = mlab.griddata(x, y, z, xi, yi, interp='nn') + np.testing.assert_array_almost_equal(zi, correct_zi) + + # Masking z array. + z_masked = np.ma.array(z, mask=[False, False, False, True, False]) + correct_zi_masked = np.ma.masked_where(xi + yi > 1.0, correct_zi) + zi = mlab.griddata(x, y, z_masked, xi, yi, interp='nn') + np.testing.assert_array_almost_equal(zi, correct_zi_masked, 5) + np.testing.assert_array_equal(np.ma.getmask(zi), + np.ma.getmask(correct_zi_masked)) + + if __name__ == '__main__': import nose import sys diff --git a/lib/matplotlib/tests/test_triangulation.py b/lib/matplotlib/tests/test_triangulation.py index 78eff62fd404..a17f8c3e733a 100644 --- a/lib/matplotlib/tests/test_triangulation.py +++ b/lib/matplotlib/tests/test_triangulation.py @@ -6,111 +6,154 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.tri as mtri -import matplotlib.delaunay as mdel -from nose.tools import assert_equal +from nose.tools import assert_equal, assert_raises from numpy.testing import assert_array_equal, assert_array_almost_equal,\ assert_array_less from matplotlib.testing.decorators import image_comparison import matplotlib.cm as cm +from matplotlib.path import Path def test_delaunay(): - # No duplicate points. - x = [0, 1, 1, 0] - y = [0, 0, 1, 1] - npoints = 4 - ntriangles = 2 - nedges = 5 + # No duplicate points, regular grid. + nx = 5 + ny = 4 + x, y = np.meshgrid(np.linspace(0.0, 1.0, nx), np.linspace(0.0, 1.0, ny)) + x = x.ravel() + y = y.ravel() + npoints = nx*ny + ntriangles = 2 * (nx-1) * (ny-1) + nedges = 3*nx*ny - 2*nx - 2*ny + 1 + + # Create delaunay triangulation. + triang = mtri.Triangulation(x, y) - # Without duplicate points, mpl calls delaunay triangulation and - # does not modify it. - mpl_triang = mtri.Triangulation(x, y) - del_triang = mdel.Triangulation(x, y) + # The tests in the remainder of this function should be passed by any + # triangulation that does not contain duplicate points. # Points - floating point. - assert_array_almost_equal(mpl_triang.x, x) - assert_array_almost_equal(mpl_triang.x, del_triang.x) - assert_array_almost_equal(mpl_triang.y, y) - assert_array_almost_equal(mpl_triang.y, del_triang.y) + assert_array_almost_equal(triang.x, x) + assert_array_almost_equal(triang.y, y) # Triangles - integers. - assert_equal(len(mpl_triang.triangles), ntriangles) - assert_equal(np.min(mpl_triang.triangles), 0) - assert_equal(np.max(mpl_triang.triangles), npoints-1) - assert_array_equal(mpl_triang.triangles, del_triang.triangle_nodes) + assert_equal(len(triang.triangles), ntriangles) + assert_equal(np.min(triang.triangles), 0) + assert_equal(np.max(triang.triangles), npoints-1) # Edges - integers. - assert_equal(len(mpl_triang.edges), nedges) - assert_equal(np.min(mpl_triang.edges), 0) - assert_equal(np.max(mpl_triang.edges), npoints-1) - assert_array_equal(mpl_triang.edges, del_triang.edge_db) + assert_equal(len(triang.edges), nedges) + assert_equal(np.min(triang.edges), 0) + assert_equal(np.max(triang.edges), npoints-1) + + # Neighbors - integers. + # Check that neighbors calculated by C++ triangulation class are the same + # as those returned from delaunay routine. + neighbors = triang.neighbors + triang._neighbors = None + assert_array_equal(triang.neighbors, neighbors) + + # Is each point used in at least one triangle? + assert_array_equal(np.unique(triang.triangles), np.arange(npoints)) def test_delaunay_duplicate_points(): - # Issue 838. - import warnings - - # Index 2 is the same as index 0. - x = [0, 1, 0, 1, 0] - y = [0, 0, 0, 1, 1] - duplicate_index = 2 - npoints = 4 # Number of non-duplicate points. - nduplicates = 1 - ntriangles = 2 - nedges = 5 - - # With duplicate points, mpl calls delaunay triangulation but - # modified returned arrays. - warnings.simplefilter("ignore") # Ignore DuplicatePointWarning. - mpl_triang = mtri.Triangulation(x, y) - del_triang = mdel.Triangulation(x, y) - warnings.resetwarnings() + # x[duplicate] == x[duplicate_of] + # y[duplicate] == y[duplicate_of] + npoints = 10 + duplicate = 7 + duplicate_of = 3 + + np.random.seed(23) + x = np.random.random((npoints)) + y = np.random.random((npoints)) + x[duplicate] = x[duplicate_of] + y[duplicate] = y[duplicate_of] + + # Create delaunay triangulation. + triang = mtri.Triangulation(x, y) - # Points - floating point. - assert_equal(len(mpl_triang.x), npoints + nduplicates) - assert_equal(len(del_triang.x), npoints) - assert_array_almost_equal(mpl_triang.x, x) - assert_array_almost_equal(del_triang.x[:duplicate_index], - x[:duplicate_index]) - assert_array_almost_equal(del_triang.x[duplicate_index:], - x[duplicate_index+1:]) - - assert_equal(len(mpl_triang.y), npoints + nduplicates) - assert_equal(len(del_triang.y), npoints) - assert_array_almost_equal(mpl_triang.y, y) - assert_array_almost_equal(del_triang.y[:duplicate_index], - y[:duplicate_index]) - assert_array_almost_equal(del_triang.y[duplicate_index:], - y[duplicate_index+1:]) + # Duplicate points should be ignored, so the index of the duplicate points + # should not appear in any triangle. + assert_array_equal(np.unique(triang.triangles), + np.delete(np.arange(npoints), duplicate)) - # Triangles - integers. - assert_equal(len(mpl_triang.triangles), ntriangles) - assert_equal(np.min(mpl_triang.triangles), 0) - assert_equal(np.max(mpl_triang.triangles), npoints-1 + nduplicates) - assert_equal(len(del_triang.triangle_nodes), ntriangles) - assert_equal(np.min(del_triang.triangle_nodes), 0) - assert_equal(np.max(del_triang.triangle_nodes), npoints-1) - # Convert mpl triangle point indices to delaunay's. - converted_indices = np.where(mpl_triang.triangles > duplicate_index, - mpl_triang.triangles - nduplicates, - mpl_triang.triangles) - assert_array_equal(del_triang.triangle_nodes, converted_indices) - # Edges - integers. - assert_equal(len(mpl_triang.edges), nedges) - assert_equal(np.min(mpl_triang.edges), 0) - assert_equal(np.max(mpl_triang.edges), npoints-1 + nduplicates) - assert_equal(len(del_triang.edge_db), nedges) - assert_equal(np.min(del_triang.edge_db), 0) - assert_equal(np.max(del_triang.edge_db), npoints-1) - # Convert mpl edge point indices to delaunay's. - converted_indices = np.where(mpl_triang.edges > duplicate_index, - mpl_triang.edges - nduplicates, - mpl_triang.edges) - assert_array_equal(del_triang.edge_db, converted_indices) - - -@image_comparison(baseline_images=['tripcolor1']) +def test_delaunay_points_in_line(): + # Cannot triangulate points that are all in a straight line, but check + # that delaunay code fails gracefully. + x = np.linspace(0.0, 10.0, 11) + y = np.linspace(0.0, 10.0, 11) + assert_raises(RuntimeError, mtri.Triangulation, x, y) + + # Add an extra point not on the line and the triangulation is OK. + x = np.append(x, 2.0) + y = np.append(y, 8.0) + triang = mtri.Triangulation(x, y) + + +def test_delaunay_insufficient_points(): + # Triangulation should raise a ValueError if passed less than 3 points. + assert_raises(ValueError, mtri.Triangulation, [], []) + assert_raises(ValueError, mtri.Triangulation, [1], [5]) + assert_raises(ValueError, mtri.Triangulation, [1, 2], [5, 6]) + + # Triangulation should also raise a ValueError if passed duplicate points + # such that there are less than 3 unique points. + assert_raises(ValueError, mtri.Triangulation, [1, 2, 1], [5, 6, 5]) + assert_raises(ValueError, mtri.Triangulation, [1, 2, 2], [5, 6, 6]) + assert_raises(ValueError, mtri.Triangulation, [1, 1, 1, 2, 1, 2], + [5, 5, 5, 6, 5, 6]) + + +def test_delaunay_robust(): + # Fails when mtri.Triangulation uses matplotlib.delaunay, works when using + # qhull. + tri_points = np.array([ + [0.8660254037844384, -0.5000000000000004], + [0.7577722283113836, -0.5000000000000004], + [0.6495190528383288, -0.5000000000000003], + [0.5412658773652739, -0.5000000000000003], + [0.811898816047911, -0.40625000000000044], + [0.7036456405748561, -0.4062500000000004], + [0.5953924651018013, -0.40625000000000033]]) + test_points = np.asarray([ + [0.58, -0.46], + [0.65, -0.46], + [0.65, -0.42], + [0.7, -0.48], + [0.7, -0.44], + [0.75, -0.44], + [0.8, -0.48]]) + + # Utility function that indicates if a triangle defined by 3 points + # (xtri, ytri) contains the test point xy. Avoid calling with a point that + # lies on or very near to an edge of the triangle. + def tri_contains_point(xtri, ytri, xy): + tri_points = np.vstack((xtri, ytri)).T + return Path(tri_points).contains_point(xy) + + # Utility function that returns how many triangles of the specified + # triangulation contain the test point xy. Avoid calling with a point that + # lies on or very near to an edge of any triangle in the triangulation. + def tris_contain_point(triang, xy): + count = 0 + for tri in triang.triangles: + if tri_contains_point(triang.x[tri], triang.y[tri], xy): + count += 1 + return count + + # Using matplotlib.delaunay, an invalid triangulation is created with + # overlapping triangles; qhull is OK. + triang = mtri.Triangulation(tri_points[:, 0], tri_points[:, 1]) + for test_point in test_points: + assert_equal(tris_contain_point(triang, test_point), 1) + + # If ignore the first point of tri_points, matplotlib.delaunay throws a + # KeyError when calculating the convex hull; qhull is OK. + triang = mtri.Triangulation(tri_points[1:, 0], tri_points[1:, 1]) + + +@image_comparison(baseline_images=['tripcolor1'], extensions=['png']) def test_tripcolor(): x = np.asarray([0, 0.5, 1, 0, 0.5, 1, 0, 0.5, 1, 0.75]) y = np.asarray([0, 0, 0, 0.5, 0.5, 0.5, 1, 1, 1, 0.75]) @@ -139,6 +182,7 @@ def test_tripcolor(): def test_no_modify(): + # Test that Triangulation does not modify triangles array passed to it. triangles = np.array([[3, 2, 0], [3, 1, 0]], dtype=np.int32) points = np.array([(0, 0), (0, 1.1), (1, 0), (1, 1)]) diff --git a/lib/matplotlib/tri/triangulation.py b/lib/matplotlib/tri/triangulation.py index 88cad6d2d2c8..afcd5b0a1135 100644 --- a/lib/matplotlib/tri/triangulation.py +++ b/lib/matplotlib/tri/triangulation.py @@ -3,8 +3,8 @@ import six -import matplotlib.delaunay as delaunay import matplotlib._tri as _tri +import matplotlib._qhull as _qhull import numpy as np @@ -14,33 +14,27 @@ class Triangulation(object): ntri triangles. The triangles can either be specified by the user or automatically generated using a Delaunay triangulation. - Read-only attributes: - - *x*: array of shape (npoints). - x-coordinates of grid points. - - *y*: array of shape (npoints). - y-coordinates of grid points. - - *triangles*: integer array of shape (ntri,3). + Parameters + ---------- + x, y : array_like of shape (npoints) + Coordinates of grid points. + triangles : integer array_like of shape (ntri, 3), optional For each triangle, the indices of the three points that make - up the triangle, ordered in an anticlockwise manner. - - *mask*: optional boolean array of shape (ntri). + up the triangle, ordered in an anticlockwise manner. If not + specified, the Delaunay triangulation is calculated. + mask : boolean array_like of shape (ntri), optional Which triangles are masked out. - *edges*: integer array of shape (?,2). - All edges of non-masked triangles. Each edge is the start - point index and end point index. Each edge (start,end and - end,start) appears only once. - - *neighbors*: integer array of shape (ntri,3). - For each triangle, the indices of the three triangles that - share the same edges, or -1 if there is no such neighboring - triangle. neighbors[i,j] is the triangle that is the neighbor - to the edge from point index triangles[i,j] to point index - triangles[i,(j+1)%3]. + Attributes + ---------- + edges + neighbors + is_delaunay : bool + Whether the Triangulation is a calculated Delaunay + triangulation (where `triangles` was not specified) or not. + Notes + ----- For a Triangulation to be valid it must not have duplicate points, triangles formed from colinear points, or overlapping triangles. """ @@ -53,19 +47,13 @@ def __init__(self, x, y, triangles=None, mask=None): self.mask = None self._edges = None self._neighbors = None + self.is_delaunay = False if triangles is None: - # No triangulation specified, so use matplotlib.delaunay. - dt = delaunay.Triangulation(self.x, self.y) - self.triangles = np.asarray( - dt.to_client_point_indices(dt.triangle_nodes), dtype=np.int32) - if mask is None: - self._edges = np.asarray( - dt.to_client_point_indices(dt.edge_db), dtype=np.int32) - # Delaunay triangle_neighbors uses different edge indexing, - # so convert. - neighbors = np.asarray(dt.triangle_neighbors, dtype=np.int32) - self._neighbors = np.roll(neighbors, 1, axis=1) + # No triangulation specified, so use matplotlib._qhull to obtain + # Delaunay triangulation. + self.triangles, self._neighbors = _qhull.delaunay(x, y) + self.is_delaunay = True else: # Triangulation specified. Copy, since we may correct triangle # orientation. @@ -102,6 +90,13 @@ def calculate_plane_coefficients(self, z): @property def edges(self): + """ + Return integer array of shape (nedges,2) containing all edges of + non-masked triangles. + + Each edge is the start point index and end point index. Each + edge (start,end and end,start) appears only once. + """ if self._edges is None: self._edges = self.get_cpp_triangulation().get_edges() return self._edges @@ -184,6 +179,16 @@ def get_trifinder(self): @property def neighbors(self): + """ + Return integer array of shape (ntri,3) containing neighbor + triangles. + + For each triangle, the indices of the three triangles that + share the same edges, or -1 if there is no such neighboring + triangle. neighbors[i,j] is the triangle that is the neighbor + to the edge from point index triangles[i,j] to point index + triangles[i,(j+1)%3]. + """ if self._neighbors is None: self._neighbors = self.get_cpp_triangulation().get_neighbors() return self._neighbors diff --git a/setup.py b/setup.py index 32f4c341756c..a14e10a6da92 100644 --- a/setup.py +++ b/setup.py @@ -73,11 +73,13 @@ setupext.FreeType(), setupext.FT2Font(), setupext.Png(), + setupext.Qhull(), setupext.Image(), setupext.TTConv(), setupext.Path(), setupext.Contour(), setupext.Delaunay(), + setupext.QhullWrap(), setupext.Tri(), 'Optional subpackages', setupext.SampleData(), diff --git a/setupext.py b/setupext.py index c71c4d7dee29..617bc777c8e7 100644 --- a/setupext.py +++ b/setupext.py @@ -936,6 +936,36 @@ def get_extension(self): return ext +class Qhull(SetupPackage): + name = "qhull" + + def check(self): + self.__class__.found_external = True + try: + return self._check_for_pkg_config( + 'qhull', 'qhull/qhull_a.h', min_version='2003.1') + except CheckFailed as e: + self.__class__.found_pkgconfig = False + # Qhull may not be in the pkg-config system but may still be + # present on this system, so check if the header files can be + # found. + include_dirs = [ + os.path.join(x, 'include', 'qhull') for x in get_base_dirs()] + if has_include_file(include_dirs, 'qhull_a.h'): + return 'Using system Qhull (version unknown, no pkg-config info)' + else: + self.__class__.found_external = False + return str(e) + ' Using local copy.' + + def add_flags(self, ext): + if self.found_external: + pkg_config.setup_extension(ext, 'qhull', + default_libraries=['qhull']) + else: + ext.include_dirs.append('extern') + ext.sources.extend(glob.glob('extern/qhull/*.c')) + + class TTConv(SetupPackage): name = "ttconv" @@ -1011,6 +1041,18 @@ def get_extension(self): return ext +class QhullWrap(SetupPackage): + name = "qhull_wrap" + + def get_extension(self): + sources = ['src/qhull_wrap.c'] + ext = make_extension('matplotlib._qhull', sources, + define_macros=[('MPL_DEVNULL', os.devnull)]) + Numpy().add_flags(ext) + Qhull().add_flags(ext) + return ext + + class Tri(SetupPackage): name = "tri" diff --git a/src/qhull_wrap.c b/src/qhull_wrap.c new file mode 100644 index 000000000000..a295bc3148d3 --- /dev/null +++ b/src/qhull_wrap.c @@ -0,0 +1,363 @@ +/* + * Wrapper module for libqhull, providing Delaunay triangulation. + * + * This module's methods should not be accessed directly. To obtain a Delaunay + * triangulation, construct an instance of the matplotlib.tri.Triangulation + * class without specifying a triangles array. + */ +#include "Python.h" +#include "numpy/noprefix.h" +#include "qhull/qhull_a.h" +#include + + +#if PY_MAJOR_VERSION >= 3 +#define PY3K 1 +#else +#define PY3K 0 +#endif + +#ifndef MPL_DEVNULL +#error "MPL_DEVNULL must be defined as the OS-equivalent of /dev/null" +#endif + +#define STRINGIFY(x) STR(x) +#define STR(x) #x + + +static const char* qhull_error_msg[6] = { + "", /* 0 = qh_ERRnone */ + "input inconsistency", /* 1 = qh_ERRinput */ + "singular input data", /* 2 = qh_ERRsingular */ + "precision error", /* 3 = qh_ERRprec */ + "insufficient memory", /* 4 = qh_ERRmem */ + "internal error"}; /* 5 = qh_ERRqhull */ + + +/* Return the indices of the 3 vertices that comprise the specified facet (i.e. + * triangle). */ +static void +get_facet_vertices(const facetT* facet, int indices[3]) +{ + vertexT *vertex, **vertexp; + FOREACHvertex_(facet->vertices) + *indices++ = qh_pointid(vertex->point); +} + +/* Return the indices of the 3 triangles that are neighbors of the specified + * facet (triangle). */ +static void +get_facet_neighbours(const facetT* facet, const int* tri_indices, + int indices[3]) +{ + facetT *neighbor, **neighborp; + FOREACHneighbor_(facet) + *indices++ = (neighbor->upperdelaunay ? -1 : tri_indices[neighbor->id]); +} + +/* Return 1 if the specified points arrays contain at least 3 unique points, + * or 0 otherwise. */ +static int +at_least_3_unique_points(int npoints, const double* x, const double* y) +{ + int i; + const int unique1 = 0; /* First unique point has index 0. */ + int unique2 = 0; /* Second unique point index is 0 until set. */ + + if (npoints < 3) + return 0; + + for (i = 1; i < npoints; ++i) { + if (unique2 == 0) { + /* Looking for second unique point. */ + if (x[i] != x[unique1] || y[i] != y[unique1]) + unique2 = i; + } + else { + /* Looking for third unique point. */ + if ( (x[i] != x[unique1] || y[i] != y[unique1]) && + (x[i] != x[unique2] || y[i] != y[unique2]) ) { + /* 3 unique points found, with indices 0, unique2 and i. */ + return 1; + } + } + } + + /* Run out of points before 3 unique points found. */ + return 0; +} + +/* Delaunay implementation methyod. If hide_qhull_errors is 1 then qhull error + * messages are discarded; if it is 0 then they are written to stderr. */ +static PyObject* +delaunay_impl(int npoints, const double* x, const double* y, + int hide_qhull_errors) +{ + coordT* points = NULL; + facetT* facet; + int i, ntri, max_facet_id; + FILE* error_file = NULL; /* qhull expects a FILE* to write errors to. */ + int exitcode; /* Value returned from qh_new_qhull(). */ + int* tri_indices = NULL; /* Maps qhull facet id to triangle index. */ + int indices[3]; + int curlong, totlong; /* Memory remaining after qh_memfreeshort. */ + PyObject* tuple; /* Return tuple (triangles, neighbors). */ + const int ndim = 2; + npy_intp dims[2]; + PyArrayObject* triangles = NULL; + PyArrayObject* neighbors = NULL; + int* triangles_ptr; + int* neighbors_ptr; + + /* Allocate points. */ + points = (coordT*)malloc(npoints*ndim*sizeof(coordT)); + if (points == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Could not allocate points array in qhull.delaunay"); + goto error_before_qhull; + } + + /* Prepare points array to pass to qhull. */ + for (i = 0; i < npoints; ++i) { + points[2*i ] = x[i]; + points[2*i+1] = y[i]; + } + + /* qhull expects a FILE* to write errors to. */ + if (hide_qhull_errors) { + /* qhull errors are ignored by writing to OS-equivalent of /dev/null. + * Rather than have OS-specific code here, instead it is determined by + * setupext.py and passed in via the macro MPL_DEVNULL. */ + error_file = fopen(STRINGIFY(MPL_DEVNULL), "w"); + if (error_file == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "Could not open devnull in qhull.delaunay"); + goto error_before_qhull; + } + } + else { + /* qhull errors written to stderr. */ + error_file = stderr; + } + + /* Perform Delaunay triangulation. */ + exitcode = qh_new_qhull(ndim, npoints, points, False, + "qhull d Qt Qbb Qc Qz", NULL, error_file); + if (exitcode != qh_ERRnone) { + PyErr_Format(PyExc_RuntimeError, + "Error in qhull Delaunay triangulation calculation: %s (exitcode=%d)%s", + qhull_error_msg[exitcode], exitcode, + hide_qhull_errors ? "; use python verbose option (-v) to see original qhull error." : ""); + goto error; + } + + /* Split facets so that they only have 3 points each. */ + qh_triangulate(); + + /* Determine ntri and max_facet_id. + Note that libqhull uses macros to iterate through collections. */ + ntri = 0; + FORALLfacets { + if (!facet->upperdelaunay) + ++ntri; + } + + max_facet_id = qh facet_id - 1; + + /* Create array to map facet id to triangle index. */ + tri_indices = (int*)malloc((max_facet_id+1)*sizeof(int)); + if (tri_indices == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Could not allocate triangle map in qhull.delaunay"); + goto error; + } + + /* Allocate python arrays to return. */ + dims[0] = ntri; + dims[1] = 3; + triangles = (PyArrayObject*)PyArray_SimpleNew(ndim, dims, PyArray_INT); + if (triangles == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Could not allocate triangles array in qhull.delaunay"); + goto error; + } + + neighbors = (PyArrayObject*)PyArray_SimpleNew(ndim, dims, PyArray_INT); + if (neighbors == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Could not allocate neighbors array in qhull.delaunay"); + goto error; + } + + triangles_ptr = (int*)PyArray_DATA(triangles); + neighbors_ptr = (int*)PyArray_DATA(neighbors); + + /* Determine triangles array and set tri_indices array. */ + i = 0; + FORALLfacets { + if (!facet->upperdelaunay) { + tri_indices[facet->id] = i++; + get_facet_vertices(facet, indices); + *triangles_ptr++ = (facet->toporient ? indices[0] : indices[2]); + *triangles_ptr++ = indices[1]; + *triangles_ptr++ = (facet->toporient ? indices[2] : indices[0]); + } + else + tri_indices[facet->id] = -1; + } + + /* Determine neighbors array. */ + FORALLfacets { + if (!facet->upperdelaunay) { + get_facet_neighbours(facet, tri_indices, indices); + *neighbors_ptr++ = (facet->toporient ? indices[2] : indices[0]); + *neighbors_ptr++ = (facet->toporient ? indices[0] : indices[2]); + *neighbors_ptr++ = indices[1]; + } + } + + /* Clean up. */ + qh_freeqhull(!qh_ALL); + qh_memfreeshort(&curlong, &totlong); + if (curlong || totlong) + PyErr_WarnEx(PyExc_RuntimeWarning, + "Qhull could not free all allocated memory", 1); + if (hide_qhull_errors) + fclose(error_file); + free(tri_indices); + free(points); + + tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, (PyObject*)triangles); + PyTuple_SetItem(tuple, 1, (PyObject*)neighbors); + return tuple; + +error: + /* Clean up. */ + Py_XDECREF(triangles); + Py_XDECREF(neighbors); + qh_freeqhull(!qh_ALL); + qh_memfreeshort(&curlong, &totlong); + /* Don't bother checking curlong and totlong as raising error anyway. */ + if (hide_qhull_errors) + fclose(error_file); + free(tri_indices); + +error_before_qhull: + free(points); + + return NULL; +} + +/* Process python arguments and call Delaunay implementation method. */ +static PyObject* +delaunay(PyObject *self, PyObject *args) +{ + PyObject* xarg; + PyObject* yarg; + PyArrayObject* xarray; + PyArrayObject* yarray; + PyObject* ret; + int npoints; + const double* x; + const double* y; + + if (!PyArg_ParseTuple(args, "OO", &xarg, &yarg)) { + PyErr_SetString(PyExc_ValueError, "expecting x and y arrays"); + return NULL; + } + + xarray = (PyArrayObject*)PyArray_ContiguousFromObject(xarg, PyArray_DOUBLE, + 1, 1); + yarray = (PyArrayObject*)PyArray_ContiguousFromObject(yarg, PyArray_DOUBLE, + 1, 1); + if (xarray == 0 || yarray == 0 || + PyArray_DIM(xarray,0) != PyArray_DIM(yarray, 0)) { + Py_XDECREF(xarray); + Py_XDECREF(yarray); + PyErr_SetString(PyExc_ValueError, + "x and y must be 1D arrays of the same length"); + return NULL; + } + + npoints = PyArray_DIM(xarray, 0); + + if (npoints < 3) { + Py_XDECREF(xarray); + Py_XDECREF(yarray); + PyErr_SetString(PyExc_ValueError, + "x and y arrays must have a length of at least 3"); + return NULL; + } + + x = (const double*)PyArray_DATA(xarray); + y = (const double*)PyArray_DATA(yarray); + + if (!at_least_3_unique_points(npoints, x, y)) { + Py_XDECREF(xarray); + Py_XDECREF(yarray); + PyErr_SetString(PyExc_ValueError, + "x and y arrays must consist of at least 3 unique points"); + return NULL; + } + + ret = delaunay_impl(npoints, x, y, Py_VerboseFlag == 0); + + Py_XDECREF(xarray); + Py_XDECREF(yarray); + return ret; +} + +/* Return qhull version string for assistance in debugging. */ +static PyObject* +version() +{ + return PyBytes_FromString(qh_version); +} + +static PyMethodDef qhull_methods[] = { + {"delaunay", (PyCFunction)delaunay, METH_VARARGS, ""}, + {"version", (PyCFunction)version, METH_NOARGS, ""}, + {NULL, NULL, 0, NULL} +}; + +#if PY3K +static struct PyModuleDef qhull_module = { + PyModuleDef_HEAD_INIT, + "qhull", + "Computing Delaunay triangulations.\n", + -1, + qhull_methods, + NULL, NULL, NULL, NULL +}; + +#define ERROR_RETURN return NULL + +PyMODINIT_FUNC +PyInit__qhull(void) +#else +#define ERROR_RETURN return + +PyMODINIT_FUNC +init_qhull(void) +#endif +{ + PyObject* m; + + #if PY3K + m = PyModule_Create(&qhull_module); + #else + m = Py_InitModule3("_qhull", qhull_methods, + "Computing Delaunay triangulations.\n"); + #endif + + if (m == NULL) { + ERROR_RETURN; + } + + import_array(); + + #if PY3K + return m; + #endif +}