From 043a4de3f2742628b4e5a068ae143ef8139fec86 Mon Sep 17 00:00:00 2001 From: Tazpn <tazpn@users.sourceforge.net> Date: Fri, 15 Jun 2007 03:26:03 +0000 Subject: [PATCH] Add collision import/export support Add bhkListObj object. --- MaxNifPlugins_Readme.txt | 7 + NifCommon/NifPlugins.cpp | 3 +- NifCommon/NifQHull.cpp | 67 + NifCommon/niutils.h | 7 + NifCommon/qhull/geom.c | 1228 ++++++++ NifCommon/qhull/geom.h | 177 ++ NifCommon/qhull/geom2.c | 2160 ++++++++++++++ NifCommon/qhull/global.c | 2041 +++++++++++++ NifCommon/qhull/io.c | 4108 +++++++++++++++++++++++++++ NifCommon/qhull/io.h | 154 + NifCommon/qhull/mem.c | 450 +++ NifCommon/qhull/mem.h | 174 ++ NifCommon/qhull/merge.c | 3619 +++++++++++++++++++++++ NifCommon/qhull/merge.h | 174 ++ NifCommon/qhull/poly.c | 1180 ++++++++ NifCommon/qhull/poly.h | 291 ++ NifCommon/qhull/poly2.c | 3137 ++++++++++++++++++++ NifCommon/qhull/qconvex.c | 334 +++ NifCommon/qhull/qdelaun.c | 324 +++ NifCommon/qhull/qhalf.c | 325 +++ NifCommon/qhull/qhull.c | 1396 +++++++++ NifCommon/qhull/qhull.h | 1030 +++++++ NifCommon/qhull/qhull_a.h | 127 + NifCommon/qhull/qhull_interface.cpp | 96 + NifCommon/qhull/qset.c | 1301 +++++++++ NifCommon/qhull/qset.h | 468 +++ NifCommon/qhull/qvoronoi.c | 318 +++ NifCommon/qhull/rbox.c | 788 +++++ NifCommon/qhull/stat.c | 702 +++++ NifCommon/qhull/stat.h | 522 ++++ NifCommon/qhull/unix.c | 377 +++ NifCommon/qhull/user.c | 324 +++ NifCommon/qhull/user.h | 762 +++++ NifCommon/qhull/user_eg.c | 317 +++ NifCommon/qhull/user_eg2.c | 542 ++++ NifExport/Coll.cpp | 186 +- NifExport/Exporter.cpp | 2 +- NifExport/Exporter.h | 258 +- NifImport/ImportCollision.cpp | 404 ++- NifImport/ImportMeshAndSkin.cpp | 113 +- NifImport/NIFImport.cpp | 5 + NifImport/NIFImporter.h | 4 +- NifImport/NifDialog.cpp | 1 + NifPlugins/DllEntry.cpp | 3 + NifPlugins_VC80.sln | 1 + NifPlugins_VC80.vcproj | 1828 ++++++++++++ NifProps/NifProps.rc | 106 +- NifProps/bhkListObj.cpp | 622 ++++ NifProps/bhkRigidBodyModifer.cpp | 6 +- NifProps/resource.h | 3 + 50 files changed, 32287 insertions(+), 285 deletions(-) create mode 100644 NifCommon/NifQHull.cpp create mode 100644 NifCommon/qhull/geom.c create mode 100644 NifCommon/qhull/geom.h create mode 100644 NifCommon/qhull/geom2.c create mode 100644 NifCommon/qhull/global.c create mode 100644 NifCommon/qhull/io.c create mode 100644 NifCommon/qhull/io.h create mode 100644 NifCommon/qhull/mem.c create mode 100644 NifCommon/qhull/mem.h create mode 100644 NifCommon/qhull/merge.c create mode 100644 NifCommon/qhull/merge.h create mode 100644 NifCommon/qhull/poly.c create mode 100644 NifCommon/qhull/poly.h create mode 100644 NifCommon/qhull/poly2.c create mode 100644 NifCommon/qhull/qconvex.c create mode 100644 NifCommon/qhull/qdelaun.c create mode 100644 NifCommon/qhull/qhalf.c create mode 100644 NifCommon/qhull/qhull.c create mode 100644 NifCommon/qhull/qhull.h create mode 100644 NifCommon/qhull/qhull_a.h create mode 100644 NifCommon/qhull/qhull_interface.cpp create mode 100644 NifCommon/qhull/qset.c create mode 100644 NifCommon/qhull/qset.h create mode 100644 NifCommon/qhull/qvoronoi.c create mode 100644 NifCommon/qhull/rbox.c create mode 100644 NifCommon/qhull/stat.c create mode 100644 NifCommon/qhull/stat.h create mode 100644 NifCommon/qhull/unix.c create mode 100644 NifCommon/qhull/user.c create mode 100644 NifCommon/qhull/user.h create mode 100644 NifCommon/qhull/user_eg.c create mode 100644 NifCommon/qhull/user_eg2.c create mode 100644 NifProps/bhkListObj.cpp diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt index a126f78..7503c0e 100644 --- a/MaxNifPlugins_Readme.txt +++ b/MaxNifPlugins_Readme.txt @@ -37,9 +37,16 @@ ----- o Installer - Add nifgmax shortcut on desktop option + + o Props + - Add bhkListObject utility to hold collision objects + o Exporter - Controller parentage change in Animation export - Fix bug where child node has parent set twice resulting in error. + + o Importer + - Add limited Collision import support 0.2.11 (Experimental Release) ----- diff --git a/NifCommon/NifPlugins.cpp b/NifCommon/NifPlugins.cpp index 66d0be9..eb965ee 100644 --- a/NifCommon/NifPlugins.cpp +++ b/NifCommon/NifPlugins.cpp @@ -66,7 +66,7 @@ void npSetProp(INode *node, const TSTR &prop, float value) bool npGetProp(INode *node, const TSTR &prop, Vector3 &value, const Vector3 def) { - bool ret; + bool ret = false; if (node) { TSTR tmp; @@ -80,6 +80,7 @@ bool npGetProp(INode *node, const TSTR &prop, Vector3 &value, const Vector3 def) if (endp) value.z = _tcstod(endp, &endp); + ret = true; } else { value = def; diff --git a/NifCommon/NifQHull.cpp b/NifCommon/NifQHull.cpp new file mode 100644 index 0000000..6860e92 --- /dev/null +++ b/NifCommon/NifQHull.cpp @@ -0,0 +1,67 @@ +#include "Max.h" + +#include <qhull/qhull.h> +#include <qhull/mem.h> +#include <qhull/qset.h> +#include <qhull/geom.h> +#include <qhull/merge.h> +#include <qhull/poly.h> +#include <qhull/io.h> +#include <qhull/stat.h> + +using namespace Niflib; + +vector<Triangle> compute_convex_hull(const vector<Vector3>& verts) +{ + vector<Triangle> tris; + + int dim=3; /* dimension of points */ + int numpoints=0; /* number of points */ + coordT *points=0; /* array of coordinates for each point */ + boolT ismalloc=0; /* True if qhull should free points in qh_freeqhull() or reallocation */ + char flags[]= "qhull i Qt"; /* 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 */ + + vertexT *vertex, **vertexp; + setT *vertices; + + numpoints = verts.size(); + points = new coordT[3 * verts.size()]; + for (int i=0, n=verts.size(); i<n; ++i) { + points[i*3 + 0] = verts[i].x; + points[i*3 + 1] = verts[i].y; + points[i*3 + 2] = verts[i].z; + } + + /* 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 { + vertices = qh_facet3vertex (facet); + if (qh_setsize (vertices) == 3) { + Triangle tri; + int i = 0; + FOREACHvertex_(vertices) { + tri[i++] = qh_pointid(vertex->point); + } + tris.push_back(tri); + } + qh_settempfree(&vertices); + } + } + qh_freeqhull(!qh_ALL); + qh_memfreeshort (&curlong, &totlong); + if (curlong || totlong) + fprintf (errfile, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); + + delete[] points; + return tris; +}; diff --git a/NifCommon/niutils.h b/NifCommon/niutils.h index 59b541a..bd1cda7 100644 --- a/NifCommon/niutils.h +++ b/NifCommon/niutils.h @@ -340,6 +340,13 @@ static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = false) return m; } +static inline Niflib::Matrix33 TOMATRIX33(const Matrix3 &tm, bool invert = false){ + Niflib::Matrix33 m3(tm.GetRow(0)[0], tm.GetRow(0)[1], tm.GetRow(0)[2], + tm.GetRow(1)[0], tm.GetRow(1)[1], tm.GetRow(1)[2], + tm.GetRow(2)[0], tm.GetRow(2)[1], tm.GetRow(2)[2]); + return m3; +} + static inline Niflib::Matrix44 TOMATRIX4(const Matrix3 &tm, bool invert = false){ Niflib::Matrix33 m3(tm.GetRow(0)[0], tm.GetRow(0)[1], tm.GetRow(0)[2], tm.GetRow(1)[0], tm.GetRow(1)[1], tm.GetRow(1)[2], diff --git a/NifCommon/qhull/geom.c b/NifCommon/qhull/geom.c new file mode 100644 index 0000000..a3949bb --- /dev/null +++ b/NifCommon/qhull/geom.c @@ -0,0 +1,1228 @@ +/*<html><pre> -<a href="qh-geom.htm" + >-------------------------------</a><a name="TOP">-</a> + + geom.c + geometric routines of qhull + + see qh-geom.htm and geom.h + + copyright (c) 1993-2003 The Geometry Center + + infrequent code goes into geom2.c +*/ +#include "qhull_a.h" + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="distplane">-</a> + + 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 sortfacets) + + see: + qh_distnorm in geom2.c +*/ +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) { + fprintf (qh ferr, "qh_distplane: "); + fprintf (qh ferr, qh_REAL_1, *dist); + fprintf (qh ferr, "from p%d to f%d\n", qh_pointid(point), facet->id); + } + return; +} /* distplane */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="findbest">-</a> + + 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; + fprintf (qh ferr, "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); + fprintf(qh ferr, " testhorizon? %d noupper? %d", testhorizon, noupper); + fprintf (qh ferr, " Last point added was p%d.", qh furthest_id); + fprintf(qh ferr, " 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, "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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="findbesthorizon">-</a> + + 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 coplanarset */ + int numpartinit= *numpart, coplanarset_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); + } + coplanarset_size= 0; + facet= startfacet; + while (True) { + trace4((qh ferr, "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.coplanarset at least searchdist below */ + coplanarset_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 (!coplanarset_size++) { + SETfirst_(qh coplanarset)= nextfacet; + SETtruncate_(qh coplanarset, 1); + }else + qh_setappend (&qh coplanarset, 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 (!coplanarset_size) + break; + else if (!--coplanarset_size) { + facet= (facetT*) SETfirst_(qh coplanarset); + SETtruncate_(qh coplanarset, 0); + }else + facet= (facetT*)qh_setdellast (qh coplanarset); + } /* while True, for each facet in qh.coplanarset */ + if (!ischeckmax) { + zadd_(Zfindhorizontot, *numpart - numpartinit); + zmax_(Zfindhorizonmax, *numpart - numpartinit); + if (newbest) + zinc_(Zparthorizon); + } + trace4((qh ferr, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist)); + return bestfacet; +} /* findbesthorizon */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="findbestnew">-</a> + + 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.coplanarset. + If share visit_id with qh_findbest, coplanarset 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) + fprintf(qh ferr, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n"); + else + fprintf(qh ferr, "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; + fprintf(qh ferr, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n", + qh_pointid(point), startfacet->id, isdistoutside, distoutside); + fprintf(qh ferr, " Last point added p%d visitid %d.", qh furthest_id, visitid); + fprintf(qh ferr, " 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, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist)); + qh IStracing= oldtrace; + return bestfacet; +} /* findbestnew */ + +/* ============ hyperplane functions -- keep code together [?] ============ */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="backnormal">-</a> + + 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, "qh_backnormal: zero diagonal at column %d.\n", i)); + qh_precision ("zero diagonal on back substitution"); + } +} /* backnormal */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="gausselim">-</a> + + 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) { + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getangle">-</a> + + 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, "qh_getangle: %2.2g\n", angle)); + return(angle); +} /* getangle */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getcenter">-</a> + + 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) { + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getcentrum">-</a> + + 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, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n", + facet->id, qh_setsize(facet->vertices), dist)); + return centrum; +} /* getcentrum */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getdistance">-</a> + + 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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="normalize">-</a> + + 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="normalize2">-</a> + + 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, "qh_normalize: norm=%2.2g too small during p%d\n", + norm, qh furthest_id)); + return; + } + } + } +} /* normalize */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="projectpoint">-</a> + + 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,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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="setfacetplane">-</a> + + 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 k,i, normsize= qh normal_size, 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; + fprintf (qh ferr, "qh_setfacetplane: facet f%d created.\n", facet->id); + fprintf (qh ferr, " Last point added to hull was p%d.", qh furthest_id); + if (zzval_(Ztotmerge)) + fprintf(qh ferr, " Last merge was #%d.", zzval_(Ztotmerge)); + fprintf (qh ferr, "\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(); + } + }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_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, "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) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "qh_setfacetplane: f%d offset %2.2g normal: ", + facet->id, facet->offset); + for (k=0; k < qh hull_dim; k++) + fprintf (qh ferr, "%2.2g ", facet->normal[k]); + fprintf (qh ferr, "\n"); + } + if (facet == qh tracefacet) + qh IStracing= oldtrace; +} /* setfacetplane */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sethyperplane_det">-</a> + + 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, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id)); + zzinc_(Znearlysingular); + } +} /* sethyperplane_det */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sethyperplane_gauss">-</a> + + 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, "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, "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/NifCommon/qhull/geom.h b/NifCommon/qhull/geom.h new file mode 100644 index 0000000..99aa3a6 --- /dev/null +++ b/NifCommon/qhull/geom.h @@ -0,0 +1,177 @@ +/*<html><pre> -<a href="qh-geom.htm" + >-------------------------------</a><a name="TOP">-</a> + + geom.h + header file for geometric routines + + see qh-geom.htm and geom.c + + copyright (c) 1993-2003 The Geometry Center +*/ + +#ifndef qhDEFgeom +#define qhDEFgeom 1 + +/* ============ -macros- ======================== */ + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="fabs_">-</a> + + fabs_(a) + returns the absolute value of a +*/ +#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a )) + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="fmax_">-</a> + + fmax_(a,b) + returns the maximum value of a and b +*/ +#define fmax_( a,b ) ( ( a ) < ( b ) ? ( b ) : ( a ) ) + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="fmin_">-</a> + + fmin_(a,b) + returns the minimum value of a and b +*/ +#define fmin_( a,b ) ( ( a ) > ( b ) ? ( b ) : ( a ) ) + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="maximize_">-</a> + + maximize_(maxval, val) + set maxval to val if val is greater than maxval +*/ +#define maximize_( maxval, val ) {if (( maxval ) < ( val )) ( maxval )= ( val );} + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="minimize_">-</a> + + minimize_(minval, val) + set minval to val if val is less than minval +*/ +#define minimize_( minval, val ) {if (( minval ) > ( val )) ( minval )= ( val );} + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="det2_">-</a> + + det2_(a1, a2, + b1, b2) + + compute a 2-d determinate +*/ +#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 )) + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="det3_">-</a> + + 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 ) ) + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="dX">-</a> + + 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, char *string, realT **rows, int numrow, int numcol); +void qh_printpoints (FILE *fp, 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); +int qh_rand( void); +void qh_srand( int seed); +realT qh_randomfactor (void); +void qh_randommatrix (realT *buffer, int dim, realT **row); +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/NifCommon/qhull/geom2.c b/NifCommon/qhull/geom2.c new file mode 100644 index 0000000..0d7c93e --- /dev/null +++ b/NifCommon/qhull/geom2.c @@ -0,0 +1,2160 @@ +/*<html><pre> -<a href="qh-geom.htm" + >-------------------------------</a><a name="TOP">-</a> + + + geom2.c + infrequently used geometric routines of qhull + + see qh-geom.htm and geom.h + + copyright (c) 1993-2003 The Geometry Center + + frequently used code goes into geom.c +*/ + +#include "qhull_a.h" + +/*================== functions in alphabetic order ============*/ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="copypoints">-</a> + + qh_copypoints( points, numpoints, dimension) + return malloc'd copy of points +*/ +coordT *qh_copypoints (coordT *points, int numpoints, int dimension) { + int size; + coordT *newpoints; + + size= numpoints * dimension * sizeof(coordT); + if (!(newpoints=(coordT*)malloc(size))) { + fprintf(qh ferr, "qhull error: insufficient memory to copy %d points\n", + numpoints); + qh_errexit(qh_ERRmem, NULL, NULL); + } + memcpy ((char *)newpoints, (char *)points, size); + return newpoints; +} /* copypoints */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="crossproduct">-</a> + + 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="determinant">-</a> + + 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] + (not 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) { + fprintf (qh ferr, "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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="detjoggle">-</a> + + 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, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth)); + return joggle; +} /* detjoggle */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="detroundoff">-</a> + + 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= qsqrt (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= qsqrt (qh hull_dim) * qh MAXwidth * + qsqrt (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= qsqrt (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) { + fprintf (qh ferr, "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) + fprintf (qh ferr, "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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="detsimplex">-</a> + + 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) { + fprintf (qh ferr, "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, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n", + det, qh_pointid(apex), dim, *nearzero)); + return det; +} /* detsimplex */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="distnorm">-</a> + + 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="distround">-</a> + + 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 qsqrt(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= qsqrt (dimension) * maxabs; + minimize_( maxdistsum, maxsumabs); + maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs); + /* adds maxabs for offset */ + trace4((qh ferr, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n", + maxround, maxabs, maxsumabs, maxdistsum)); + return maxround; +} /* distround */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="divzero">-</a> + + 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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="facetarea">-</a> + + 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, (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, "qh_facetarea: f%d area %2.2g\n", facet->id, area)); + return area; +} /* facetarea */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="facetarea_simplex">-</a> + + 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) { + fprintf (qh ferr, "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, "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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="facetcenter">-</a> + + 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="findgooddist">-</a> + + 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) { + zinc_(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; + zinc_(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, "qh_findgooddist: p%d is %2.2g above good facet f%d\n", + qh_pointid(point), bestdist, bestfacet->id)); + return bestfacet; + } + trace4((qh ferr, "qh_findgooddist: no good facet for p%d above f%d\n", + qh_pointid(point), facetA->id)); + return NULL; +} /* findgooddist */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getarea">-</a> + + qh_getarea( facetlist ) + set area of all facets in facetlist + collect statistics + + 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 REPORTfreq) + fprintf (qh ferr, "computing area of each facet and volume of the convex hull\n"); + else + trace1((qh ferr, "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; + facet->f.area= area= qh_facetarea (facet); + facet->isarea= True; + 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); + } + } +} /* getarea */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="gram_schmidt">-</a> + + 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= qsqrt(norm); + wmin_(Wmindenom, norm); + if (norm == 0.0) /* either 0 or overflow due to qsqrt */ + 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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="inthresholds">-</a> + + 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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="joggleinput">-</a> + + 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 size, i, seed; + 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*)malloc(size))) { + fprintf(qh ferr, "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)) { + fprintf (qh ferr, "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, "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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="maxabsval">-</a> + + 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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="maxmin">-</a> + + 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 { + fprintf (qh ferr, "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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="maxouter">-</a> + + 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, "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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="maxsimplex">-</a> + + 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) { + fprintf (qh ferr, "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 { + fprintf (qh ferr, "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, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1)); + }else { + trace0((qh ferr, "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) { + fprintf (qh ferr, "qhull internal error (qh_maxsimplex): not enough points available\n"); + qh_errexit (qh_ERRqhull, NULL, NULL); + } + qh_setappend(simplex, maxpoint); + trace1((qh ferr, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n", + qh_pointid(maxpoint), k+1, maxdet)); + } /* k */ +} /* maxsimplex */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="minabsval">-</a> + + 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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="mindiff">-</a> + + 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 */ + + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="orientoutside">-</a> + + 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="outerinner">-</a> + + 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 + + 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 * qsqrt (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 * qsqrt (qh hull_dim); + } +} /* outerinner */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="pointdist">-</a> + + 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(qsqrt(dist)); + return dist; +} /* pointdist */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="printmatrix">-</a> + + 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, char *string, realT **rows, int numrow, int numcol) { + realT *rowp; + realT r; /*bug fix*/ + int i,k; + + fprintf (fp, "%s\n", string); + for (i= 0; i < numrow; i++) { + rowp= rows[i]; + for (k= 0; k < numcol; k++) { + r= *rowp++; + fprintf (fp, "%6.3g ", r); + } + fprintf (fp, "\n"); + } +} /* printmatrix */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="printpoints">-</a> + + 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, char *string, setT *points) { + pointT *point, **pointp; + + if (string) { + fprintf (fp, "%s", string); + FOREACHpoint_(points) + fprintf (fp, " p%d", qh_pointid(point)); + fprintf (fp, "\n"); + }else { + FOREACHpoint_(points) + fprintf (fp, " %d", qh_pointid(point)); + fprintf (fp, "\n"); + } +} /* printpoints */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="projectinput">-</a> + + 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); + 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) { + fprintf(qh ferr, "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*)malloc(newnum*newdim*sizeof(coordT)))){ + fprintf(qh ferr, "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, "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) { + fprintf(qh ferr, "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) + 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, "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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="projectpoints">-</a> + + 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) { + fprintf (qh ferr, "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<n; j++) { + if (project[j] == -1) + oldk++; + else { + newp= newpoints+newk++; + if (project[j] == +1) { + if (oldk >= 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, "qh_projectpoints: projected %d points from dim %d to dim %d\n", + numpoints, dim, newdim)); +} /* projectpoints */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="rand">-</a> + + qh_rand() + qh_srand( seed ) + generate pseudo-random number between 1 and 2^31 -2 + + notes: + from Park & Miller's minimimal 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 +*/ +int qh_rand_seed= 1; /* define as global variable instead of using qh */ + +int qh_rand( void) { +#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 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="randomfactor">-</a> + + qh_randomfactor() + return a random factor within qh.RANDOMmax of 1.0 + + notes: + qh.RANDOMa/b are defined in global.c +*/ +realT qh_randomfactor (void) { + realT randr; + + randr= qh_RANDOMint; + return randr * qh RANDOMa + qh RANDOMb; +} /* randomfactor */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="randommatrix">-</a> + + 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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="rotateinput">-</a> + + 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="rotatepoints">-</a> + + 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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="scaleinput">-</a> + + 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="scalelast">-</a> + + 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, "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) + fprintf (qh ferr, "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 + fprintf (qh ferr, "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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="scalepoints">-</a> + + 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) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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, "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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="setdelaunay">-</a> + + 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, "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 */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sethalfspace">-</a> + + 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) { + fprintf (qh ferr, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset); + for (k= dim, coordp= coords; k--; ) { + r= *coordp++; + fprintf (qh ferr, " %6.2g", r); + } + fprintf (qh ferr, "\n"); + } + return True; +LABELerroroutside: + feasiblep= feasible; + normp= normal; + fprintf(qh ferr, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: "); + for (k= dim; k--; ) + fprintf (qh ferr, qh_REAL_1, r=*(feasiblep++)); + fprintf (qh ferr, "\n halfspace: "); + for (k= dim; k--; ) + fprintf (qh ferr, qh_REAL_1, r=*(normp++)); + fprintf (qh ferr, "\n at offset: "); + fprintf (qh ferr, qh_REAL_1, *offset); + fprintf (qh ferr, " and distance: "); + fprintf (qh ferr, qh_REAL_1, dist); + fprintf (qh ferr, "\n"); + return False; +} /* sethalfspace */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sethalfspace_all">-</a> + + 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, "qh_sethalfspace_all: compute dual for halfspace intersection\n")); + newdim= dim - 1; + if (!(newpoints=(coordT*)malloc(count*newdim*sizeof(coordT)))){ + fprintf(qh ferr, "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)) { + fprintf (qh ferr, "The halfspace was at index %d\n", i); + qh_errexit (qh_ERRinput, NULL, NULL); + } + normalp= offsetp + 1; + } + return newpoints; +} /* sethalfspace_all */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sharpnewfacets">-</a> + + 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, "qh_sharpnewfacets: %d\n", issharp)); + return issharp; +} /* sharpnewfacets */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="voronoi_center">-</a> + + 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) { + fprintf (qh ferr, "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); + }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) { + fprintf (qh ferr, "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) + fprintf (qh ferr, "p%d dist %.2g, ", qh_pointid (point), + qh_pointdist (point, center, dim)); + fprintf (qh ferr, "\n"); + } + } +#endif + } + if (simplex != points) + qh_settempfree (&simplex); + return center; +} /* voronoi_center */ + diff --git a/NifCommon/qhull/global.c b/NifCommon/qhull/global.c new file mode 100644 index 0000000..ff4d4a5 --- /dev/null +++ b/NifCommon/qhull/global.c @@ -0,0 +1,2041 @@ +/*<html><pre> -<a href="qh-globa.htm" + >-------------------------------</a><a name="TOP">-</a> + + global.c + initializes all the globals of the qhull application + + see README + + see qhull.h for qh.globals and function prototypes + + see qhull_a.h for internal functions + + copyright (c) 1993-2003, The Geometry Center + */ + +#include "qhull_a.h" + +/*========= qh definition (see qhull.h) =======================*/ + +#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 + +/*-<a href ="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh_version">-</a> + + qh_version + version string by year and date + + the revision increases on code changes only + + notes: + change date: Changes.txt, Announce.txt, README.txt, + qhull.man, qhull.txt, qhull-news.html, Eudora signatures, + 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, qhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c +*/ + +char *qh_version = "2003.1 2003/12/30"; + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="appendprint">-</a> + + 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 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="checkflags">-</a> + + 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, key, opt, prevopt; + char chkkey[]= " "; + char chkopt[]= " "; + char chkopt2[]= " "; + + if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') { + fprintf(qh ferr, "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")) { + fprintf(qh ferr, "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 == '\'') { /* TO 'file name' */ + t= strchr(s, '\''); + if (!t) { + fprintf(qh ferr, "qhull error (qh_checkflags): missing the 2nd single-quote for:\n%s\n", s-1); + qh_errexit(qh_ERRinput, NULL, NULL); + } + s= t+1; + 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]= '\''; + fprintf(qh ferr, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr); + qh_errexit(qh_ERRinput, NULL, NULL); + } + } +} /* checkflags */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="clock">-</a> + + 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) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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 + fprintf (qh ferr, "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 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="freebuffers">-</a> + + qh_freebuffers() + free up global memory buffers + + notes: + must match qh_initbuffers() +*/ +void qh_freebuffers (void) { + + trace5((qh ferr, "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 coplanarset); + if (qh line) /* allocated by qh_readinput, freed if no error */ + free (qh line); + if (qh half_space) + free (qh half_space); + if (qh temp_malloc) + free (qh temp_malloc); + if (qh feasible_point) /* allocated by qh_readfeasible */ + free (qh feasible_point); + if (qh feasible_string) /* allocated by qh_initflags */ + 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) { + free(qh first_point); + qh first_point= NULL; + } + if (qh input_points && qh input_malloc) { /* set by qh_joggleinput */ + free (qh input_points); + qh input_points= NULL; + } + trace5((qh ferr, "qh_freebuffers: finished\n")); +} /* freebuffers */ + + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="freebuild">-</a> + + qh_freebuild( allmem ) + free global memory used by qh_initbuild and qh_buildhull + if !allmem, + does not free short memory (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, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n")); + if (qh del_vertices) + qh_settruncate (qh del_vertices, 0); + if (allmem) { + qh_clearcenters (qh_ASnone); + while ((vertex= qh vertex_list)) { + if (vertex->next) + qh_delvertex (vertex); + else { + qh_memfree (vertex, 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, 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, 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, sizeof(mergeT)); + qh facet_mergeset= NULL; /* temp set */ + qh degen_mergeset= NULL; /* temp set */ + qh_settempfree_all(); +} /* freebuild */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="freeqhull">-</a> + + qh_freeqhull( allmem ) + free global memory + if !allmem, + does not free short memory (freed by qh_memfreeshort) + + notes: + sets qh.NOerrexit in case caller forgets to + + design: + free global and temporary memory from qh_initbuild and qh_buildhull + free buffers + free statistics +*/ +void qh_freeqhull (boolT allmem) { + + trace1((qh ferr, "qh_freeqhull: free global memory\n")); + qh NOerrexit= True; /* no more setjmp since called at exit */ + qh_freebuild (allmem); + qh_freebuffers(); + qh_freestatistics(); +#if qh_QHpointer + free (qh_qh); + qh_qh= NULL; +#else + memset((char *)&qh_qh, 0, sizeof(qhT)); + qh NOerrexit= True; +#endif +} /* freeqhull */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="init_A">-</a> + + 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 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="init_B">-</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 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 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="init_qhull_command">-</a> + + qh_init_qhull_command( argc, argv ) + build qh.qhull_command from argc/argv + + returns: + a space-deliminated 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[]) { + int i; + char *s; + + if (argc) { + if ((s= strrchr( argv[0], '\\'))) /* Borland gives full path */ + strcpy (qh qhull_command, s+1); + else + strcpy (qh qhull_command, argv[0]); + if ((s= strstr (qh qhull_command, ".EXE")) + || (s= strstr (qh qhull_command, ".exe"))) + *s= '\0'; + } + for (i=1; i < argc; i++) { + if (strlen (qh qhull_command) + strlen(argv[i]) + 1 < sizeof(qh qhull_command)) { + strcat (qh qhull_command, " "); + strcat (qh qhull_command, argv[i]); + }else { + fprintf (qh ferr, "qhull input error: more than %d characters in command line\n", + (int)sizeof(qh qhull_command)); + exit (1); /* can not use qh_errexit */ + } + } +} /* init_qhull_command */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initflags">-</a> + + 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]) { + *qh qhull_command= '\0'; + strncat( qh qhull_command, command, sizeof( qh qhull_command)); + } + 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 != '-') + fprintf(qh ferr, "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 != '-') + fprintf(qh ferr, "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 == '-') + fprintf(qh ferr, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n"); + else if (!isdigit(*s)) + fprintf(qh ferr, "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 + fprintf (qh ferr, "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 (t-start+1, 1))) { + fprintf(qh ferr, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh_ERRmem, NULL, NULL); + } + strncpy (qh feasible_string, start, 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)) + fprintf(qh ferr, "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 != '-') + fprintf(qh ferr, "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 != '-') + fprintf(qh ferr, "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 == '-') + fprintf(qh ferr, "qhull warning: negative outside width for option 'Wn'. Ignored.\n"); + else if (!isdigit(*s)) + fprintf(qh ferr, "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--; + fprintf (qh ferr, "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)) + fprintf (qh ferr, "qhull input error: missing dimension for option 'GDn'\n"); + else { + if (qh DROPdim >= 0) + fprintf (qh ferr, "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--; + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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--; + fprintf (qh ferr, "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, "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)) + fprintf (qh ferr, "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--; + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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) + fprintf (qh ferr, "qhull warning: good point already defined for option 'QGn'. Ignored\n"); + else if (s == t) + fprintf (qh ferr, "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 != '-') + fprintf (qh ferr, "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) + fprintf (qh ferr, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n"); + else if (s == t) + fprintf (qh ferr, "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--; + fprintf (qh ferr, "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 '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 fout) + fprintf (qh ferr, "qhull warning: output file undefined (stdout). Option 'Tz' ignored.\n"); + else { + qh_option ("Tz-stdout", NULL, NULL); + qh ferr= qh fout; + qhmem.ferr= qh fout; + } + break; + case 'C': + if (!isdigit(*s)) + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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 (s[0] != ' ' || s[1] == '\"' || s[1] == '\'' ||isspace (s[1])) { + s++; + fprintf (qh ferr, "qhull warning: option 'TI' mistyped.\nUse 'TI', one space, file name, and space or end-of-line.\nDo not use quotes. Option 'FI' ignored.\n"); + }else { /* not a procedure because of qh_option (filename, NULL, NULL); */ + char filename[500], *t= filename; + + s++; + while (*s) { + if (t - filename >= sizeof (filename)-2) { + fprintf (qh ferr, "qhull error: filename for 'TI' too long.\n"); + qh_errexit (qh_ERRinput, NULL, NULL); + } + if (isspace (*s)) + break; + *(t++)= *s++; + } + *t= '\0'; + if (!freopen (filename, "r", stdin)) { + fprintf (qh ferr, "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 (s[0] != ' ' || s[1] == '\"' || isspace (s[1])) { + s++; + fprintf (qh ferr, "qhull warning: option 'TO' mistyped.\nUse 'TO', one space, file name, and space or end-of-line.\nThe file name may be enclosed in single quotes.\nDo not use double quotes. Option 'FO' ignored.\n"); + }else { /* not a procedure because of qh_option (filename, NULL, NULL); */ + char filename[500], *t= filename; + boolT isquote= False; + + s++; + if (*s == '\'') { + isquote= True; + s++; + } + while (*s) { + if (t - filename >= sizeof (filename)-2) { + fprintf (qh ferr, "qhull error: filename for 'TO' too long.\n"); + qh_errexit (qh_ERRinput, NULL, NULL); + } + if (isquote) { + if (*s == '\'') { + s++; + isquote= False; + break; + } + }else if (isspace (*s)) + break; + *(t++)= *s++; + } + *t= '\0'; + if (isquote) + fprintf (qh ferr, "qhull error: missing end quote for option 'TO'. Rest of line ignored.\n"); + else if (!freopen (filename, "w", stdout)) { + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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) + fprintf (qh ferr, "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)) + fprintf (qh ferr, "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--; + fprintf (qh ferr, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + default: + fprintf (qh ferr, "qhull warning: unknown flag %c (%x)\n", (int)s[-1], + (int)s[-1]); + break; + } + if (s-1 == prev_s && *s && !isspace(*s)) { + fprintf (qh ferr, "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 (isgeom && !qh FORCEoutput && qh PRINTout[1]) + fprintf (qh ferr, "qhull warning: additional output formats are not compatible with Geomview\n"); + /* set derived values in qh_initqhull_globals */ +} /* initflags */ + + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_buffers">-</a> + + 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 coplanarset= 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--; ) { + 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 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_globals">-</a> + + 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) + test for qh.PRINTgood (i.e., only print 'good' facets) + initialize qh.CENTERtype, qh.normal_size, + qh.center_size, qh.TRACEpoint/level, + initialize and test random numbers + check for conflicting print output options +*/ +void qh_initqhull_globals (coordT *points, int numpoints, int dim, boolT ismalloc) { + int seed, pointsneeded, extra= 0, i, randi, k; + boolT printgeom= False, printmath= False, printcoplanar= False; + realT randr; + realT factorial; + + time_t timedata; + + trace0((qh ferr, "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) + fprintf(qh ferr, "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) + fprintf(qh ferr, "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) { + fprintf (qh ferr, "qhull input error: merging not installed (qh_NOmerge + 'Qx', 'Cn' or 'An')\n"); + qh_errexit (qh_ERRinput, NULL, NULL); + } +#endif + 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 DELAUNAY && qh KEEPcoplanar && !qh KEEPinside) { + qh KEEPinside= True; + qh_option ("Qinterior-keep", NULL, NULL); + } + if (qh DELAUNAY && qh HALFspace) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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) + fprintf (qh ferr, "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) { + fprintf(qh ferr, "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++; + extra= 1; + } + } + if (qh hull_dim <= 1) { + fprintf(qh ferr, "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, "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) { + fprintf (qh ferr, "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= 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) { + fprintf (qh ferr, "\ +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/10 + || randr > qh_RANDOMmax * 5) + fprintf (qh ferr, "\ +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/2.0, qh_RANDOMmax); + qh RANDOMa= 2.0 * qh RANDOMfactor/qh_RANDOMmax; + qh RANDOMb= 1.0 - qh RANDOMfactor; + if (qh_HASHfactor < 1.1) { + fprintf(qh ferr, "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) { + fprintf(qh ferr,"qhull input error: not enough points (%d) to construct initial simplex (need %d)\n", + numpoints, pointsneeded); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh PRINTtransparent) { + if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) { + fprintf(qh ferr,"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) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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) + fprintf (qh ferr, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n"); + } + if (!qh KEEPcoplanar && !qh KEEPinside && !qh ONLYgood) { + if ((qh PRINTcoplanar && qh PRINTspheres) || printcoplanar) { + qh KEEPcoplanar = True; + qh_option ("Qcoplanar", NULL, NULL); + } + } + if (printmath && (qh hull_dim > 3 || qh VORONOI)) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "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; + } + } + 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) + fprintf (qh ferr, "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; + } +} /* initqhull_globals */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_mem">-</a> + + 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(sizeof(vertexT)); + if (qh MERGING) { + qh_memsize(sizeof(ridgeT)); + qh_memsize(sizeof(mergeT)); + } + qh_memsize(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 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_start">-</a> + + qh_initqhull_start( infile, outfile, errfile ) + start initialization of qhull + initialize statistics, stdio, default values for global variables + + see: + qh_maxmin() determines the precision constants +*/ +void qh_initqhull_start (FILE *infile, FILE *outfile, FILE *errfile) { + + qh_CPUclock; /* start the clock */ +#if qh_QHpointer + if (!(qh_qh= (qhT *)malloc (sizeof(qhT)))) { + fprintf (errfile, "qhull error (qh_initqhull_globals): insufficient memory\n"); + exit (qh_ERRmem); /* no error handler */ + } + memset((char *)qh_qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */ +#else + memset((char *)&qh_qh, 0, sizeof(qhT)); +#endif + strcat (qh qhull, "qhull"); + qh_initstatistics(); + 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 */ + qh_RANDOMseed_(1); +} /* initqhull_start */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initthresholds">-</a> + + 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 index, maxdim, k; + char *s= command; + 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)) { + fprintf(qh ferr, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n", + key, s-1); + continue; + } + index= qh_strtol (s, &s); + if (index >= qh hull_dim) { + fprintf(qh ferr, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n", + index, key, qh hull_dim); + continue; + } + if (*s == ':') { + s++; + value= qh_strtod(s, &s); + if (fabs((double)value) > 1.0) { + fprintf(qh ferr, "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[index]= value; + else + qh upper_threshold[index]= 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)) { + fprintf(qh ferr, "qhull warning: no dimension given for Qhull option %c. Ignored\n", + key); + continue; + } + index= qh_strtol (s, &s); + if (index >= maxdim) { + fprintf(qh ferr, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n", + index, 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[index]= value; + else + qh upper_bound[index]= 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 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="option">-</a> + + qh_option( option, intVal, realVal ) + add an option description to qh.qhull_options + + notes: + will be printed with statistics ('Ts') and errors + strlen(option) < 40 +*/ +void qh_option (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= strlen(buf); + qh qhull_optionlen += len; + maxlen= sizeof (qh qhull_options) - len -1; + maximize_(maxlen, 0); + if (qh qhull_optionlen >= 80 && maxlen > 0) { + qh qhull_optionlen= len; + strncat (qh qhull_options, "\n", maxlen--); + } + strncat (qh qhull_options, buf, maxlen); +} /* option */ + +#if qh_QHpointer +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="restore_qhull">-</a> + + qh_restore_qhull( oldqh ) + restores a previously saved qhull + also restores qh_qhstat and qhmem.tempstack + + 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() +*/ +void qh_restore_qhull (qhT **oldqh) { + + if (*oldqh && strcmp ((*oldqh)->qhull, "qhull")) { + fprintf (qhmem.ferr, "qhull internal error (qh_restore_qhull): %p is not a qhull data structure\n", + *oldqh); + qh_errexit (qh_ERRqhull, NULL, NULL); + } + if (qh_qh) { + fprintf (qhmem.ferr, "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) { + fprintf (qhmem.ferr, "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; + trace1((qh ferr, "qh_restore_qhull: restored qhull from %p\n", *oldqh)); +} /* restore_qhull */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="save_qhull">-</a> + + 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, "qh_save_qhull: save qhull %p\n", qh_qh)); + if (!qh_qh) { + fprintf (qhmem.ferr, "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 + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="strtol">-</a> + + 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); + if (s< (*endp) && (*endp)[-1] == ' ') + (*endp)--; + return result; +} /* strtol */ diff --git a/NifCommon/qhull/io.c b/NifCommon/qhull/io.c new file mode 100644 index 0000000..afc089b --- /dev/null +++ b/NifCommon/qhull/io.c @@ -0,0 +1,4108 @@ +/*<html><pre> -<a href="qh-io.htm" + >-------------------------------</a><a name="TOP">-</a> + + 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-2003 The Geometry Center +*/ + +#include "qhull_a.h" + +/*========= -functions in alphabetical order after qh_produce_output() =====*/ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="produce_output">-</a> + + qh_produce_output() + prints out the result of qhull in desired format + 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 i, tempsize= qh_setsize ((setT*)qhmem.tempstack), d_1; + + if (qh VORONOI) { + qh_clearcenters (qh_ASvoronoi); + qh_vertexneighbors(); + } + if (qh TRIangulate) { + 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 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_collectstatistics(); + qh_printstatistics(qh ferr, ""); + qh_memstatistics (qh ferr); + d_1= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; + fprintf(qh ferr, "\ + size in bytes: merge %d ridge %d vertex %d facet %d\n\ + normal %d ridge vertices %d facet vertices or neighbors %d\n", + sizeof(mergeT), sizeof(ridgeT), + sizeof(vertexT), sizeof(facetT), + qh normal_size, d_1, d_1 + SETelemsize); + } + if (qh_setsize ((setT*)qhmem.tempstack) != tempsize) { + fprintf (qh ferr, "qhull internal error (qh_produce_output): temporary sets not empty (%d)\n", + qh_setsize ((setT*)qhmem.tempstack)); + qh_errexit (qh_ERRqhull, NULL, NULL); + } +} /* produce_output */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="dfacet">-</a> + + 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 */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="dvertex">-</a> + + 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 */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="compare_vertexpoint">-</a> + + qh_compare_vertexpoint( p1, p2 ) + used by qsort() to order vertices by point id +*/ +int qh_compare_vertexpoint(const void *p1, const void *p2) { + vertexT *a= *((vertexT **)p1), *b= *((vertexT **)p2); + + return ((qh_pointid(a->point) > qh_pointid(b->point)?1:-1)); +} /* compare_vertexpoint */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="compare_facetarea">-</a> + + qh_compare_facetarea( p1, p2 ) + used by qsort() to order facets by area +*/ +int qh_compare_facetarea(const void *p1, const void *p2) { + facetT *a= *((facetT **)p1), *b= *((facetT **)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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="compare_facetmerge">-</a> + + 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) { + facetT *a= *((facetT **)p1), *b= *((facetT **)p2); + + return (a->nummerge - b->nummerge); +} /* compare_facetvisit */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="compare_facetvisit">-</a> + + 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) { + facetT *a= *((facetT **)p1), *b= *((facetT **)p2); + int i,j; + + if (!(i= a->visitid)) + i= - a->id; /* do not convert to int */ + if (!(j= b->visitid)) + j= - b->id; + return (i - j); +} /* compare_facetvisit */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="countfacets">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="detvnorm">-</a> + + 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 { + fprintf(qh ferr, "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, "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, "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, "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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="detvridge">-</a> + + 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), qh_setsize (centers), + sizeof (facetT *), qh_compare_facetvisit); + qh_settempfree (&tricenters); + return centers; +} /* detvridge */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="detvridge3">-</a> + + 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 (not 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) { + fprintf (stderr, "qh_detvridge3: neigbors 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="eachvoronoi">-</a> + + 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, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n", + count, qh_pointid (atvertex->point), qh_pointid (vertex->point))); + if (printvridge) { + 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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="eachvoronoi_all">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="facet2point">-</a> + + 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 */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="facetvertices">-</a> + + 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 */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="geomplanes">-</a> + + 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 * qsqrt (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 */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="markkeep">-</a> + + qh_markkeep( facetlist ) + mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea + ignores visible facets (not 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, "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, + 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, + 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 */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="markvoronoi">-</a> + + 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, "qh_markvoronoi: islower %d numcenters %d\n", islower, numcenters)); + return vertices; +} /* markvoronoi */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="order_vertexneighbors">-</a> + + 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, "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) { + fprintf (qh ferr, "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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printafacet">-</a> + + 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, int 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) { + fprintf (fp, qh_REAL_1, facet->f.area); + fprintf (fp, "\n"); + }else + fprintf (fp, "0\n"); + break; + case qh_PRINTcoplanars: + fprintf (fp, "%d", qh_setsize (facet->coplanarset)); + FOREACHpoint_(facet->coplanarset) + fprintf (fp, " %d", qh_pointid (point)); + fprintf (fp, "\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: + fprintf (fp, "%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: + fprintf (fp, "%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) { + fprintf (fp, "no normal for facet f%d\n", facet->id); + break; + } + if (qh CDDoutput) { + fprintf (fp, qh_REAL_1, -offset); + for (k=0; k < qh hull_dim; k++) + fprintf (fp, qh_REAL_1, -facet->normal[k]); + }else { + for (k=0; k < qh hull_dim; k++) + fprintf (fp, qh_REAL_1, facet->normal[k]); + fprintf (fp, qh_REAL_1, offset); + } + fprintf (fp, "\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: + fprintf (fp, "%d", qh_setsize (facet->neighbors)); + FOREACHneighbor_(facet) + fprintf (fp, " %d", + neighbor->visitid ? neighbor->visitid - 1: - neighbor->id); + fprintf (fp, "\n"); + break; + case qh_PRINTpointintersect: + if (!qh feasible_point) { + fprintf (fp, "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--; ) + fprintf (fp, qh_REAL_1, qh_INFINITE); + fprintf (fp, "\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); + fprintf (fp, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist); + } + break; + case qh_PRINTpoints: /* VORONOI only by qh_printbegin */ + if (qh CDDoutput) + fprintf (fp, "1 "); + qh_printcenter (fp, format, NULL, facet); + break; + case qh_PRINTvertices: + fprintf (fp, "%d", qh_setsize (facet->vertices)); + FOREACHvertex_(facet->vertices) + fprintf (fp, " %d", qh_pointid (vertex->point)); + fprintf (fp, "\n"); + break; + } +} /* printafacet */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printbegin">-</a> + + 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, int 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: + fprintf (fp, "%d\n", numfacets); + break; + case qh_PRINTcoplanars: + fprintf (fp, "%d\n", numfacets); + break; + case qh_PRINTcentrums: + if (qh CENTERtype == qh_ASnone) + qh_clearcenters (qh_AScentrum); + fprintf (fp, "%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)) + fprintf (qh ferr, "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))) + fprintf (qh ferr, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n"); + if (qh PRINTdim == 4 && (qh PRINTspheres)) + fprintf (qh ferr, "qhull warning: output for vertices not implemented in 4-d\n"); + if (qh PRINTdim == 4 && qh DOintersections && qh PRINTnoplanes) + fprintf (qh ferr, "qhull warning: 'Gnh' generates no output in 4-d\n"); + if (qh PRINTdim == 2) { + fprintf(fp, "{appearance {linewidth 3} LIST # %s | %s\n", + qh rbox_command, qh qhull_command); + }else if (qh PRINTdim == 3) { + fprintf(fp, "{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 */ + fprintf (fp, "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) + fprintf (fp, "4VECT %d %d 1\n", num, num); + else + fprintf (fp, "VECT %d %d 1\n", num, num); + for (i= num; i--; ) { + if (i % 20 == 0) + fprintf (fp, "\n"); + fprintf (fp, "1 "); + } + fprintf (fp, "# 1 point per line\n1 "); + for (i= num-1; i--; ) { + if (i % 20 == 0) + fprintf (fp, "\n"); + fprintf (fp, "0 "); + } + fprintf (fp, "# 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); + } + fprintf (fp, "0 1 1 1 # color of points\n"); + } + if (qh PRINTdim == 4 && !qh PRINTnoplanes) + /* 4dview loads up multiple 4OFF objects slowly */ + fprintf(fp, "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 * qsqrt (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: + fprintf (fp, "%d\n", numfacets); + break; + case qh_PRINTincidences: + if (qh VORONOI && qh PRINTprecision) + fprintf (qh ferr, "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) + fprintf(fp, "%d\n", numfacets); + else + fprintf(fp, "%d\n", numsimplicial+numridges); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh CDDoutput) + fprintf (fp, "%s | %s\nbegin\n %d %d real\n", qh rbox_command, + qh qhull_command, numfacets, qh hull_dim+1); + else + fprintf (fp, "%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) + fprintf (qh ferr, "qhull warning: output is the Delaunay triangulation\n"); + if (format == qh_PRINTmaple) { + if (qh hull_dim == 2) + fprintf(fp, "PLOT(CURVES(\n"); + else + fprintf(fp, "PLOT3D(POLYGONS(\n"); + }else + fprintf(fp, "{\n"); + qh printoutvar= 0; /* counts number of facets for notfirst */ + break; + case qh_PRINTmerges: + fprintf (fp, "%d\n", numfacets); + break; + case qh_PRINTpointintersect: + fprintf (fp, "%d\n%d\n", qh hull_dim, numfacets); + break; + case qh_PRINTneighbors: + fprintf (fp, "%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) + fprintf (fp, "%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 */ + fprintf (fp, "%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: + fprintf (fp, "%d\n", numcoplanars); + break; + case qh_PRINTpoints: + if (!qh VORONOI) + goto LABELnoformat; + if (qh CDDoutput) + fprintf (fp, "%s | %s\nbegin\n%d %d real\n", qh rbox_command, + qh qhull_command, numfacets, qh hull_dim); + else + fprintf (fp, "%d\n%d\n", qh hull_dim-1, numfacets); + break; + case qh_PRINTvertices: + fprintf (fp, "%d\n", numfacets); + break; + case qh_PRINTsummary: + default: + LABELnoformat: + fprintf (qh ferr, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n", + qh hull_dim); + qh_errexit (qh_ERRqhull, NULL, NULL); + } +} /* printbegin */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printcenter">-</a> + + 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 +*/ +void qh_printcenter (FILE *fp, int format, char *string, facetT *facet) { + int k, num; + + if (qh CENTERtype != qh_ASvoronoi && qh CENTERtype != qh_AScentrum) + return; + if (string) + fprintf (fp, string, facet->id); + 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++) + fprintf (fp, qh_REAL_1, facet->center[k]); + }else { + for (k=0; k < num; k++) + fprintf (fp, 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++) + fprintf (fp, qh_REAL_1, facet->center[k]); + } + if (format == qh_PRINTgeom && num == 2) + fprintf (fp, " 0\n"); + else + fprintf (fp, "\n"); +} /* printcenter */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printcentrum">-</a> + + 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; + } + fprintf (fp, "{appearance {-normal -edge normscale 0} "); + if (qh firstcentrum) { + qh firstcentrum= False; + fprintf (fp, "{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 + fprintf (fp, "{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); + fprintf (fp, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]); + fprintf (fp, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]); + fprintf (fp, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]); + qh_printpoint3 (fp, centrum); + fprintf (fp, "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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printend">-</a> + + qh_printend( fp, format ) + prints trailer for all output formats + + see: + qh_printbegin() and qh_printafacet() + +*/ +void qh_printend (FILE *fp, int format, facetT *facetlist, setT *facets, boolT printall) { + int num; + facetT *facet, **facetp; + + if (!qh printoutnum) + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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 + fprintf(fp, "}\n"); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh CDDoutput) + fprintf (fp, "end\n"); + break; + case qh_PRINTmaple: + fprintf(fp, "));\n"); + break; + case qh_PRINTmathematica: + fprintf(fp, "}\n"); + break; + case qh_PRINTpoints: + if (qh CDDoutput) + fprintf (fp, "end\n"); + break; + } +} /* printend */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printend4geom">-</a> + + 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) + fprintf (fp, "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) + fprintf (fp, "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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printextremes">-</a> + + 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, int 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); + fprintf (fp, "%d\n", numpoints); + FOREACHpoint_i_(points) { + if (point) + fprintf (fp, "%d\n", point_i); + } + qh_settempfree (&points); +} /* printextremes */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printextremes_2d">-</a> + + 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, int 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); + fprintf(fp, "%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) { + fprintf(qh ferr, "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; + fprintf(fp, "%d\n", qh_pointid (vertexA->point)); + } + if (vertexB->visitid != qh vertex_visit) { + vertexB->visitid= qh vertex_visit; + fprintf(fp, "%d\n", qh_pointid (vertexB->point)); + } + } + facet->visitid= qh visit_id; + facet= nextfacet; + }while (facet && facet != startfacet); +} /* printextremes_2d */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printextremes_d">-</a> + + 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, int 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; + } + fprintf (fp, "%d\n", numpoints); + FOREACHvertex_(vertices) { + if (vertex->seen) + fprintf (fp, "%d\n", qh_pointid (vertex->point)); + } + qh_settempfree (&vertices); +} /* printextremes_d */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet">-</a> + + 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 */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet2geom">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet2geom_points">-</a> + + 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; + + fprintf(fp, "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); + } + fprintf(fp, "%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); + } + fprintf(fp, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printfacet2geom_points */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet2math">-</a> + + 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, int format, int notfirst) { + pointT *point0, *point1; + realT mindist; + char *pointfmt; + + qh_facet2point (facet, &point0, &point1, &mindist); + if (notfirst) + fprintf(fp, ","); + 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"; + fprintf(fp, pointfmt, point0[0], point0[1], point1[0], point1[1]); + qh_memfree (point1, qh normal_size); + qh_memfree (point0, qh normal_size); +} /* printfacet2math */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_points">-</a> + + 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; + + fprintf(fp, "{ 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) + fprintf(fp, "0 "); + else + fprintf(fp, "%8.4g ", point[k]); + } + if (printpoints != points) + qh_memfree (point, qh normal_size); + fprintf (fp, "\n"); + } + if (printpoints != points) + qh_settempfree (&printpoints); + fprintf(fp, "%d ", n); + for(i= 0; i < n; i++) + fprintf(fp, "%d ", i); + fprintf(fp, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]); +} /* printfacet3geom_points */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_simplicial">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3math">-</a> + + 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, int format, int notfirst) { + vertexT *vertex, **vertexp; + setT *points, *vertices; + pointT *point, **pointp; + boolT firstpoint= True; + realT dist; + char *pointfmt, *endfmt; + + if (notfirst) + fprintf(fp, ",\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) { + fprintf(fp, "["); + pointfmt= "[%16.8f, %16.8f, %16.8f]"; + endfmt= "]"; + }else { + fprintf(fp, "Polygon[{"); + pointfmt= "{%16.8f, %16.8f, %16.8f}"; + endfmt= "}]"; + } + FOREACHpoint_(points) { + if (firstpoint) + firstpoint= False; + else + fprintf(fp, ",\n"); + fprintf(fp, pointfmt, point[0], point[1], point[2]); + } + FOREACHpoint_(points) + qh_memfree (point, qh normal_size); + qh_settempfree(&points); + qh_settempfree(&vertices); + fprintf(fp, endfmt); +} /* printfacet3math */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3vertex">-</a> + + 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, int format) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facet3vertex (facet); + if (format == qh_PRINToff) + fprintf (fp, "%d ", qh_setsize (vertices)); + FOREACHvertex_(vertices) + fprintf (fp, "%d ", qh_pointid(vertex->point)); + fprintf (fp, "\n"); + qh_settempfree(&vertices); +} /* printfacet3vertex */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a> + + 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) + fprintf(fp, "OFF 3 1 1 # f%d\n", facet->id); + else { + qh printoutvar++; + fprintf (fp, "# 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) + fprintf(fp, "%8.4g ", point[k]); + } + fprintf (fp, "\n"); + qh_memfree (point, qh normal_size); + } + if (qh DROPdim >= 0) + fprintf(fp, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + } +} /* printfacet4geom_nonsimplicial */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet4geom_simplicial">-</a> + + 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) + fprintf(fp, "OFF 3 1 1 # ridge between f%d f%d\n", + facet->id, neighbor->id); + else { + qh printoutvar++; + fprintf (fp, "# 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) + fprintf(fp, "%8.4g ", vertex->point[k]); + } + fprintf (fp, "\n"); + } + if (qh DROPdim >= 0) + fprintf(fp, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + qh_setfree(&vertices); + } +} /* printfacet4geom_simplicial */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a> + + 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, int format) { + vertexT *vertex, **vertexp; + ridgeT *ridge, **ridgep; + + if (facet->visible && qh NEWfacets) + return; + FOREACHridge_(facet->ridges) { + if (format == qh_PRINTtriangles) + fprintf(fp, "%d ", qh hull_dim); + fprintf(fp, "%d ", id); + if ((ridge->top == facet) ^ qh_ORIENTclock) { + FOREACHvertex_(ridge->vertices) + fprintf(fp, "%d ", qh_pointid(vertex->point)); + }else { + FOREACHvertexreverse12_(ridge->vertices) + fprintf(fp, "%d ", qh_pointid(vertex->point)); + } + fprintf(fp, "\n"); + } +} /* printfacetNvertex_nonsimplicial */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a> + + 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, int format) { + vertexT *vertex, **vertexp; + + if (format == qh_PRINToff || format == qh_PRINTtriangles) + fprintf (fp, "%d ", qh_setsize (facet->vertices)); + if ((facet->toporient ^ qh_ORIENTclock) + || (qh hull_dim > 2 && !facet->simplicial)) { + FOREACHvertex_(facet->vertices) + fprintf(fp, "%d ", qh_pointid(vertex->point)); + }else { + FOREACHvertexreverse12_(facet->vertices) + fprintf(fp, "%d ", qh_pointid(vertex->point)); + } + fprintf(fp, "\n"); +} /* printfacetNvertex_simplicial */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacetheader">-</a> + + qh_printfacetheader( fp, facet ) + prints header fields of a facet to fp + + notes: + for 'f' output and debugging +*/ +void qh_printfacetheader(FILE *fp, facetT *facet) { + pointT *point, **pointp, *furthest; + facetT *neighbor, **neighborp; + realT dist; + + if (facet == qh_MERGEridge) { + fprintf (fp, " MERGEridge\n"); + return; + }else if (facet == qh_DUPLICATEridge) { + fprintf (fp, " DUPLICATEridge\n"); + return; + }else if (!facet) { + fprintf (fp, " NULLfacet\n"); + return; + } + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + fprintf(fp, "- f%d\n", facet->id); + fprintf(fp, " - flags:"); + if (facet->toporient) + fprintf(fp, " top"); + else + fprintf(fp, " bottom"); + if (facet->simplicial) + fprintf(fp, " simplicial"); + if (facet->tricoplanar) + fprintf(fp, " tricoplanar"); + if (facet->upperdelaunay) + fprintf(fp, " upperDelaunay"); + if (facet->visible) + fprintf(fp, " visible"); + if (facet->newfacet) + fprintf(fp, " new"); + if (facet->tested) + fprintf(fp, " tested"); + if (!facet->good) + fprintf(fp, " notG"); + if (facet->seen) + fprintf(fp, " seen"); + if (facet->coplanar) + fprintf(fp, " coplanar"); + if (facet->mergehorizon) + fprintf(fp, " mergehorizon"); + if (facet->keepcentrum) + fprintf(fp, " keepcentrum"); + if (facet->dupridge) + fprintf(fp, " dupridge"); + if (facet->mergeridge && !facet->mergeridge2) + fprintf(fp, " mergeridge1"); + if (facet->mergeridge2) + fprintf(fp, " mergeridge2"); + if (facet->newmerge) + fprintf(fp, " newmerge"); + if (facet->flipped) + fprintf(fp, " flipped"); + if (facet->notfurthest) + fprintf(fp, " notfurthest"); + if (facet->degenerate) + fprintf(fp, " degenerate"); + if (facet->redundant) + fprintf(fp, " redundant"); + fprintf(fp, "\n"); + if (facet->isarea) + fprintf(fp, " - area: %2.2g\n", facet->f.area); + else if (qh NEWfacets && facet->visible && facet->f.replace) + fprintf(fp, " - replacement: f%d\n", facet->f.replace->id); + else if (facet->newfacet) { + if (facet->f.samecycle && facet->f.samecycle != facet) + fprintf(fp, " - shares same visible/horizon as f%d\n", facet->f.samecycle->id); + }else if (facet->tricoplanar /* !isarea */) { + if (facet->f.triowner) + fprintf(fp, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id); + }else if (facet->f.newcycle) + fprintf(fp, " - was horizon to f%d\n", facet->f.newcycle->id); + if (facet->nummerge) + fprintf(fp, " - merges: %d\n", facet->nummerge); + qh_printpointid(fp, " - normal: ", qh hull_dim, facet->normal, -1); + fprintf(fp, " - 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) + fprintf(fp, " - maxoutside: %10.7g\n", facet->maxoutside); +#endif + if (!SETempty_(facet->outsideset)) { + furthest= (pointT*)qh_setlast(facet->outsideset); + if (qh_setsize (facet->outsideset) < 6) { + fprintf(fp, " - 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 { + fprintf(fp, " - outside set: %d points.", qh_setsize(facet->outsideset)); + qh_printpoint(fp, " Furthest", furthest); + } +#if !qh_COMPUTEfurthest + fprintf(fp, " - furthest distance= %2.2g\n", facet->furthestdist); +#endif + } + if (!SETempty_(facet->coplanarset)) { + furthest= (pointT*)qh_setlast(facet->coplanarset); + if (qh_setsize (facet->coplanarset) < 6) { + fprintf(fp, " - 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 { + fprintf(fp, " - coplanar set: %d points.", qh_setsize(facet->coplanarset)); + qh_printpoint(fp, " Furthest", furthest); + } + zinc_(Zdistio); + qh_distplane (furthest, facet, &dist); + fprintf(fp, " furthest distance= %2.2g\n", dist); + } + qh_printvertices (fp, " - vertices:", facet->vertices); + fprintf(fp, " - neighboring facets: "); + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) + fprintf(fp, " MERGE"); + else if (neighbor == qh_DUPLICATEridge) + fprintf(fp, " DUP"); + else + fprintf(fp, " f%d", neighbor->id); + } + fprintf(fp, "\n"); + qh RANDOMdist= qh old_randomdist; +} /* printfacetheader */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacetridges">-</a> + + qh_printfacetridges( fp, facet ) + prints ridges of a facet to fp + + notes: + ridges printed in neighbor order + assumes the ridges exist + for 'f' output +*/ +void qh_printfacetridges(FILE *fp, facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int numridges= 0; + + + if (facet->visible && qh NEWfacets) { + fprintf(fp, " - ridges (ids may be garbage):"); + FOREACHridge_(facet->ridges) + fprintf(fp, " r%d", ridge->id); + fprintf(fp, "\n"); + }else { + fprintf(fp, " - 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)) { + fprintf (fp, " - all ridges:"); + FOREACHridge_(facet->ridges) + fprintf (fp, " r%d", ridge->id); + fprintf (fp, "\n"); + } + FOREACHridge_(facet->ridges) { + if (!ridge->seen) + qh_printridge(fp, ridge); + } + } +} /* printfacetridges */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacets">-</a> + + 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, int 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)) + fprintf (qh ferr, "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); + fprintf (fp, "%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) + fprintf(fp, "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) + fprintf (fp, "%s | %s\n", qh rbox_command, qh qhull_command); + else if (format == qh_PRINTsize) { + fprintf (fp, "0\n2 "); + fprintf (fp, qh_REAL_1, qh totarea); + fprintf (fp, qh_REAL_1, qh totvol); + fprintf (fp, "\n"); + }else if (format == qh_PRINTsummary) { + qh_countfacets (facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + vertices= qh_facetvertices (facetlist, facets, printall); + fprintf (fp, "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); + fprintf (fp, 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 */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printhelp_degenerate">-</a> + + 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) + fprintf(fp, "\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) { + fprintf(fp, "\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 + fprintf (fp, "\ +\n\ +Qhull is currently using single precision arithmetic. The following\n\ +will probably remove the precision problems:\n\ + - recompile qhull for double precision (#define REALfloat 0 in user.h).\n"); +#endif + if (qh DELAUNAY && !qh SCALElast && qh MAXabs_coord > 1e4) + fprintf( fp, "\ +\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) + fprintf( fp, "\ +When computing the Delaunay triangulation:\n\ + - use 'Qz' to add a point at-infinity. This reduces precision problems.\n"); + + fprintf(fp, "\ +\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); + fprintf(fp, "\ +\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 */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printhelp_singular">-</a> + + 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; + + fprintf(fp, "\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) + fprintf(fp, "\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); + fprintf (fp, "\n"); + FORALLfacets { + fprintf (fp, "facet"); + FOREACHvertex_(facet->vertices) + fprintf (fp, " p%d", qh_pointid(vertex->point)); + zinc_(Zdistio); + qh_distplane(qh interior_point, facet, &dist); + fprintf (fp, " distance= %4.2g\n", dist); + } + if (!qh_QUICKhelp) { + if (qh HALFspace) + fprintf (fp, "\n\ +These points are the dual of the given halfspaces. They indicate that\n\ +the intersection is degenerate.\n"); + fprintf (fp,"\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) + fprintf (fp, "\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"); + } + fprintf (fp, "\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); + } + fprintf (fp, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min); + } + if (!qh_QUICKhelp) { + fprintf (fp, "\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 + fprintf (fp, "\ + - recompile qhull for double precision (#define REALfloat 0 in qhull.h).\n"); +#endif + fprintf (fp, "\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\ +"); + if (qh DELAUNAY && !qh ATinfinity) + fprintf (fp, "\n\n\ +This is a Delaunay triangulation and the input is co-circular or co-spherical:\n\ + - use 'Qz' to add a point \"at infinity\" (i.e., above the paraboloid)\n\ + - or use 'QJ' to joggle the input and avoid co-circular data\n"); + } +} /* printhelp_singular */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printhyperplaneintersection">-</a> + + 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) + fprintf(fp, "VECT 1 %d 1 %d 1 ", i, i); + else if (qh hull_dim == 4 && qh DROPdim >= 0) + fprintf(fp, "OFF 3 1 1 "); + else + qh printoutvar++; + fprintf (fp, "# 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); + fprintf(fp, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]); + }else + fprintf(fp, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]); + if (nearzero1+nearzero2) + fprintf (fp, "p%d (coplanar facets)\n", qh_pointid (vertex->point)); + else + fprintf (fp, "projected p%d\n", qh_pointid (vertex->point)); + } + if (qh hull_dim == 3) + fprintf(fp, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); + else if (qh hull_dim == 4 && qh DROPdim >= 0) + fprintf(fp, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printhyperplaneintersection */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printline3geom">-</a> + + 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)) { + fprintf (fp, "VECT 1 2 1 2 1\n"); + for (k= 0; k < 3; k++) + fprintf (fp, "%8.4g ", pB[k]); + fprintf (fp, " # p%d\n", qh_pointid (pointB)); + }else + fprintf (fp, "VECT 1 1 1 1 1\n"); + for (k=0; k < 3; k++) + fprintf (fp, "%8.4g ", pA[k]); + fprintf (fp, " # p%d\n", qh_pointid (pointA)); + fprintf (fp, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]); +} + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printneighborhood">-</a> + + 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, int 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpoint">-</a> + + 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) +*/ +void qh_printpoint(FILE *fp, char *string, pointT *point) { + int id= qh_pointid( point); + + qh_printpointid( fp, string, qh hull_dim, point, id); +} /* printpoint */ + +void qh_printpointid(FILE *fp, char *string, int dim, pointT *point, int id) { + int k; + realT r; /*bug fix*/ + + if (!point) + return; + if (string) { + fputs (string, fp); + if (id != -1) + fprintf(fp, " p%d: ", id); + } + for(k= dim; k--; ) { + r= *point++; + if (string) + fprintf(fp, " %8.4g", r); + else + fprintf(fp, qh_REAL_1, r); + } + fprintf(fp, "\n"); +} /* printpointid */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpoint3">-</a> + + 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++) + fprintf (fp, "%8.4g ", p[k]); + fprintf (fp, " # p%d\n", qh_pointid (point)); +} /* printpoint3 */ + +/*---------------------------------------- +-printpoints- print pointids for a set of points starting at index + see geom.c +*/ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpoints_out">-</a> + + 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, int 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) + fprintf (fp, "%s | %s\nbegin\n%d %d real\n", qh rbox_command, + qh qhull_command, numpoints, qh hull_dim + 1); + else + fprintf (fp, "%d\n%d\n", qh hull_dim, numpoints); + FOREACHpoint_i_(points) { + if (point) { + if (qh CDDoutput) + fprintf (fp, "1 "); + qh_printpoint (fp, NULL, point); + } + } + if (qh CDDoutput) + fprintf (fp, "end\n"); + qh_settempfree (&points); +} /* printpoints_out */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpointvect">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpointvect2">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printridge">-</a> + + qh_printridge( fp, ridge ) + prints the information in a ridge + + notes: + for qh_printfacetridges() +*/ +void qh_printridge(FILE *fp, ridgeT *ridge) { + + fprintf(fp, " - r%d", ridge->id); + if (ridge->tested) + fprintf (fp, " tested"); + if (ridge->nonconvex) + fprintf (fp, " nonconvex"); + fprintf (fp, "\n"); + qh_printvertices (fp, " vertices:", ridge->vertices); + if (ridge->top && ridge->bottom) + fprintf(fp, " between f%d and f%d\n", + ridge->top->id, ridge->bottom->id); +} /* printridge */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printspheres">-</a> + + 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++; + fprintf (fp, "{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) { + fprintf(fp, "%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); + fprintf (fp, "1\n"); + } + fprintf (fp, "}}}\n"); +} /* printspheres */ + + +/*---------------------------------------------- +-printsummary- + see qhull.c +*/ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvdiagram">-</a> + + 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, int 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 { + fprintf(qh ferr, "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); + fprintf (fp, "%d\n", totcount); + totcount= qh_printvdiagram2 (fp, printvridge, vertices, innerouter, True /* inorder*/); + qh_settempfree (&vertices); +#if 0 /* for testing qh_eachvoronoi_all */ + fprintf (fp, "\n"); + totcount= qh_eachvoronoi_all(fp, printvridge, qh UPPERdelaunay, innerouter, True /* inorder*/); + fprintf (fp, "%d\n", totcount); +#endif +} /* printvdiagram */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvdiagram2">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvertex">-</a> + + qh_printvertex( fp, vertex ) + prints the information in a vertex +*/ +void qh_printvertex(FILE *fp, vertexT *vertex) { + pointT *point; + int k, count= 0; + facetT *neighbor, **neighborp; + realT r; /*bug fix*/ + + if (!vertex) { + fprintf (fp, " NULLvertex\n"); + return; + } + fprintf(fp, "- p%d (v%d):", qh_pointid(vertex->point), vertex->id); + point= vertex->point; + if (point) { + for(k= qh hull_dim; k--; ) { + r= *point++; + fprintf(fp, " %5.2g", r); + } + } + if (vertex->deleted) + fprintf(fp, " deleted"); + if (vertex->delridge) + fprintf (fp, " ridgedeleted"); + fprintf(fp, "\n"); + if (vertex->neighbors) { + fprintf(fp, " neighbors:"); + FOREACHneighbor_(vertex) { + if (++count % 100 == 0) + fprintf (fp, "\n "); + fprintf(fp, " f%d", neighbor->id); + } + fprintf(fp, "\n"); + } +} /* printvertex */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvertexlist">-</a> + + 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, char* string, facetT *facetlist, + setT *facets, boolT printall) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facetvertices (facetlist, facets, printall); + fputs (string, fp); + FOREACHvertex_(vertices) + qh_printvertex(fp, vertex); + qh_settempfree (&vertices); +} /* printvertexlist */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvertices">-</a> + + qh_printvertices( fp, string, vertices ) + prints vertices in a set +*/ +void qh_printvertices(FILE *fp, char* string, setT *vertices) { + vertexT *vertex, **vertexp; + + fputs (string, fp); + FOREACHvertex_(vertices) + fprintf (fp, " p%d (v%d)", qh_pointid(vertex->point), vertex->id); + fprintf(fp, "\n"); +} /* printvertices */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvneighbors">-</a> + + 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 */ + fprintf (fp, "%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); + fprintf (fp, "%d", numneighbors); + if (qh hull_dim == 3) + qh_order_vertexneighbors (vertex); + else if (qh hull_dim >= 4) + qsort (SETaddr_(vertex->neighbors, facetT), numneighbors, + sizeof (facetT *), qh_compare_facetvisit); + FOREACHneighbor_(vertex) + fprintf (fp, " %d", + neighbor->visitid ? neighbor->visitid - 1 : - neighbor->id); + fprintf (fp, "\n"); + }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT))) + fprintf (fp, "1 %d\n", + facet->visitid ? facet->visitid - 1 : - facet->id); + else + fprintf (fp, "0\n"); + } + qh_settempfree (&coplanar_points); + qh_settempfree (&vertex_points); + qh_settempfree (&vertices); +} /* printvneighbors */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvoronoi">-</a> + + 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, int 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) + fprintf (fp, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n", + numcenters, numvertices); + else + fprintf (fp, "%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--; ) + fprintf (fp, qh_REAL_1, 0.0); + fprintf (fp, " 0 # infinity not used\n"); + }else { + for (k= qh hull_dim-1; k--; ) + fprintf (fp, qh_REAL_1, qh_INFINITE); + fprintf (fp, "\n"); + } + FORALLfacet_(facetlist) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + fprintf (fp, "# %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) + fprintf (fp, "# %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, vertexT), + 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) { + fprintf (fp, "%d", numneighbors); + if (vertex) { + FOREACHneighbor_(vertex) { + if (neighbor->visitid && neighbor->visitid < numfacets) + fprintf (fp, " %d", neighbor->visitid); + } + } + fprintf (fp, " # p%d (v%d)\n", vertex_i, vertex->id); + }else + fprintf (fp, " # p%d is coplanar or isolated\n", vertex_i); + }else { + if (numinf) + numneighbors++; + fprintf (fp, "%d", numneighbors); + if (vertex) { + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) { + if (numinf) { + numinf= 0; + fprintf (fp, " %d", neighbor->visitid); + } + }else if (neighbor->visitid < numfacets) + fprintf (fp, " %d", neighbor->visitid); + } + } + fprintf (fp, "\n"); + } + } + if (format == qh_PRINTgeom) + fprintf (fp, "}\n"); + qh_settempfree (&vertices); +} /* printvoronoi */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvnorm">-</a> + + 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 + + see: + qh_printvdiagram() + qh_eachvoronoi() +*/ +void qh_printvnorm (FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + pointT *normal; + realT offset; + int k; + + normal= qh_detvnorm (vertex, vertexA, centers, &offset); + fprintf (fp, "%d %d %d ", + 2+qh hull_dim, qh_pointid (vertex->point), qh_pointid (vertexA->point)); + for (k= 0; k< qh hull_dim-1; k++) + fprintf (fp, qh_REAL_1, normal[k]); + fprintf (fp, qh_REAL_1, offset); + fprintf (fp, "\n"); +} /* printvnorm */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvridge">-</a> + + 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 +*/ +void qh_printvridge (FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + facetT *facet, **facetp; + + fprintf (fp, "%d %d %d", qh_setsize (centers)+2, + qh_pointid (vertex->point), qh_pointid (vertexA->point)); + FOREACHfacet_(centers) + fprintf (fp, " %d", facet->visitid); + fprintf (fp, "\n"); +} /* printvridge */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="projectdim3">-</a> + + 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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="readfeasible">-</a> + + qh_readfeasible( dim, remainder ) + read feasible point from remainder string 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, char *remainder) { + boolT isfirst= True; + int linecount= 0, tokcount= 0; + char *s, *t, firstline[qh_MAXfirst+1]; + coordT *coords, value; + + if (!qh HALFspace) { + fprintf (qh ferr, "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) + fprintf (qh ferr, "qhull input warning: feasible point (dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n"); + if (!(qh feasible_point= (coordT*)malloc (dim* sizeof(coordT)))) { + fprintf(qh ferr, "qhull error: insufficient memory for feasible point\n"); + qh_errexit(qh_ERRmem, NULL, NULL); + } + coords= qh feasible_point; + while ((s= (isfirst ? remainder : 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) { + fprintf (qh ferr, "qhull input error: coordinates for feasible point do not finish out the line: %s\n", + s); + qh_errexit (qh_ERRinput, NULL, NULL); + } + return linecount; + } + } + } + fprintf (qh ferr, "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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="readpoints">-</a> + + 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: this routine needs rewriting +*/ +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, *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", 5) || !memcmp (firstline, "BEGIN", 5)) + break; + else if (!*qh rbox_command) + strncat(qh rbox_command, s, sizeof (qh rbox_command)-1); + } + if (!s) { + fprintf (qh ferr, "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", 5) || !memcmp (s, "BEGIN", 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) { + fprintf(qh ferr, "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) { + fprintf(qh ferr,"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) { + fprintf(qh ferr,"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) { + fprintf(qh ferr,"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*) 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*)malloc ((qh maxline+1) * sizeof (char)); + *ismalloc= True; /* use malloc since memory not setup */ + coords= points= qh temp_malloc= + (coordT*)malloc((*numpoints)*(*dimension)*sizeof(coordT)); + if (!coords || !qh line || (qh HALFspace && !qh half_space)) { + fprintf(qh ferr, "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", 3) || !memcmp (s, "END", 3)) { + if (qh CDDinput ) + break; + else if (wasbegin) + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "The halfspace was on line %d\n", linecount); + if (wasbegin) + fprintf (qh ferr, "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) { + fprintf(qh ferr, "qhull input error: line %d contained more than %d characters\n", + linecount, (int) (s - qh line)); + qh_errexit(qh_ERRinput, NULL, NULL); + } + isfirst= False; + } + if (tokcount != maxcount) { + newnum= fmin_(numinput, tokcount/diminput); + fprintf(qh ferr,"\ +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) + fprintf(qh ferr, ", line %d is the first comment", firsttext); + if (firstshort) + fprintf(qh ferr, ", line %d is the first short\nline", firstshort); + if (firstlong) + fprintf(qh ferr, ", line %d is the first long line", firstlong); + fprintf(qh ferr, ". 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")) + fprintf (qh ferr, "\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"); + } + free (qh line); + qh line= NULL; + if (qh half_space) { + free (qh half_space); + qh half_space= NULL; + } + qh temp_malloc= NULL; + trace1((qh ferr,"qh_readpoints: read in %d %d-dimensional points\n", + numinput, diminput)); + return(points); +} /* readpoints */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="setfeasible">-</a> + + 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)) { + fprintf(qh ferr, "\ +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*)malloc (dim* sizeof(coordT)))) { + fprintf(qh ferr, "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) { + fprintf (qh ferr, "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 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="skipfacet">-</a> + + 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 */ + diff --git a/NifCommon/qhull/io.h b/NifCommon/qhull/io.h new file mode 100644 index 0000000..818df1c --- /dev/null +++ b/NifCommon/qhull/io.h @@ -0,0 +1,154 @@ +/*<html><pre> -<a href="qh-io.htm" + >-------------------------------</a><a name="TOP">-</a> + + io.h + declarations of Input/Output functions + + see README, qhull.h and io.c + + copyright (c) 1993-2003, The Geometry Center +*/ + +#ifndef qhDEFio +#define qhDEFio 1 + +/*============ constants and flags ==================*/ + +/*-<a href="qh-io.htm#TOC" + >--------------------------------</a><a name="qh_MAXfirst">-</a> + + qh_MAXfirst + maximum length of first two lines of stdin +*/ +#define qh_MAXfirst 200 + +/*-<a href="qh-io.htm#TOC" + >--------------------------------</a><a name="qh_MINradius">-</a> + + qh_MINradius + min radius for Gp and Gv, fraction of maxcoord +*/ +#define qh_MINradius 0.02 + +/*-<a href="qh-io.htm#TOC" + >--------------------------------</a><a name="qh_GEOMepsilon">-</a> + + qh_GEOMepsilon + adjust outer planes for 'lines closer' and geomview roundoff. + This prevents bleed through. +*/ +#define qh_GEOMepsilon 2e-3 + +/*-<a href="qh-io.htm#TOC" + >--------------------------------</a><a name="qh_WHITESPACE">-</a> + + qh_WHITESPACE + possible values of white space +*/ +#define qh_WHITESPACE " \n\t\v\r\f" + + +/*-<a href="qh-io.htm#TOC" + >--------------------------------</a><a name="RIDGE">-</a> + + qh_RIDGE + to select which ridges to print in qh_eachvoronoi +*/ +typedef enum +{ + qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter +} +qh_RIDGE; + +/*-<a href="qh-io.htm#TOC" + >--------------------------------</a><a name="printvridgeT">-</a> + + printvridgeT + prints results of qh_printvdiagram + + see: + <a href="io.c#printvridge">qh_printvridge</a> 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_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_printafacet(FILE *fp, int format, facetT *facet, boolT printall); +void qh_printbegin (FILE *fp, int format, facetT *facetlist, setT *facets, boolT printall); +void qh_printcenter (FILE *fp, int format, char *string, facetT *facet); +void qh_printcentrum (FILE *fp, facetT *facet, realT radius); +void qh_printend (FILE *fp, int 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, int printall); +void qh_printextremes_2d (FILE *fp, facetT *facetlist, setT *facets, int printall); +void qh_printextremes_d (FILE *fp, facetT *facetlist, setT *facets, int printall); +void qh_printfacet(FILE *fp, facetT *facet); +void qh_printfacet2math(FILE *fp, facetT *facet, int 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, int 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, int 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, int format); +void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, int format); +void qh_printfacetheader(FILE *fp, facetT *facet); +void qh_printfacetridges(FILE *fp, facetT *facet); +void qh_printfacets(FILE *fp, int format, facetT *facetlist, setT *facets, boolT printall); +void qh_printhelp_degenerate(FILE *fp); +void qh_printhelp_singular(FILE *fp); +void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2, + setT *vertices, realT color[3]); +void qh_printneighborhood (FILE *fp, int format, facetT *facetA, facetT *facetB, boolT printall); +void qh_printline3geom (FILE *fp, pointT *pointA, pointT *pointB, realT color[3]); +void qh_printpoint(FILE *fp, char *string, pointT *point); +void qh_printpointid(FILE *fp, 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, int 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, int 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, char* string, facetT *facetlist, + setT *facets, boolT printall); +void qh_printvertices (FILE *fp, char* string, setT *vertices); +void qh_printvneighbors (FILE *fp, facetT* facetlist, setT *facets, boolT printall); +void qh_printvoronoi (FILE *fp, int 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_projectdim3 (pointT *source, pointT *destination); +int qh_readfeasible (int dim, char *remainder); +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc); +void qh_setfeasible (int dim); +boolT qh_skipfacet(facetT *facet); + +#endif /* qhDEFio */ diff --git a/NifCommon/qhull/mem.c b/NifCommon/qhull/mem.c new file mode 100644 index 0000000..c640c8a --- /dev/null +++ b/NifCommon/qhull/mem.c @@ -0,0 +1,450 @@ +/*<html><pre> -<a href="qh-mem.htm" + >-------------------------------</a><a name="TOP">-</a> + + 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(sizeof(facetT)); + qh_memsize(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-2003 The Geometry Center +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "mem.h" + +#ifndef qhDEFqhull +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +void qh_errexit(int exitcode, facetT *, ridgeT *); +#endif + +/*============ -global data structure ============== + see mem.h for definition +*/ + +qhmemT qhmem= {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 ======== */ + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="intcompare">-</a> + + qh_intcompare( i, j ) + used by qsort and bsearch to compare two integers +*/ +static int qh_intcompare(const void *i, const void *j) { + return(*((int *)i) - *((int *)j)); +} /* intcompare */ + + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="memalloc">-</a> + + 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 malloc() +*/ +void *qh_memalloc(int insize) { + void **freelistp, *newbuffer; + int index, size; + int outsize, bufsize; + void *object; + + if ((unsigned) insize <= (unsigned) qhmem.LASTsize) { + index= qhmem.indextable[insize]; + freelistp= qhmem.freelists+index; + if ((object= *freelistp)) { + qhmem.cntquick++; + *freelistp= *((void **)*freelistp); /* replace freelist with next object */ + return (object); + }else { + outsize= qhmem.sizetable[index]; + qhmem.cntshort++; + if (outsize > qhmem .freesize) { + if (!qhmem.curbuffer) + bufsize= qhmem.BUFinit; + else + bufsize= qhmem.BUFsize; + qhmem.totshort += bufsize; + if (!(newbuffer= malloc(bufsize))) { + fprintf(qhmem.ferr, "qhull error (qh_memalloc): insufficient memory\n"); + 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; + } + object= qhmem.freemem; + qhmem.freemem= (void *)((char *)qhmem.freemem + outsize); + qhmem.freesize -= outsize; + return object; + } + }else { /* long allocation */ + if (!qhmem.indextable) { + fprintf (qhmem.ferr, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n"); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + outsize= insize; + qhmem .cntlong++; + qhmem .curlong++; + qhmem .totlong += outsize; + if (qhmem.maxlong < qhmem.totlong) + qhmem.maxlong= qhmem.totlong; + if (!(object= malloc(outsize))) { + fprintf(qhmem.ferr, "qhull error (qh_memalloc): insufficient memory\n"); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (qhmem.IStracing >= 5) + fprintf (qhmem.ferr, "qh_memalloc long: %d bytes at %p\n", outsize, object); + } + return (object); +} /* memalloc */ + + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="memfree">-</a> + + qh_memfree( object, size ) + 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 free(object) +*/ +void qh_memfree(void *object, int size) { + void **freelistp; + + if (!object) + return; + if (size <= qhmem.LASTsize) { + qhmem .freeshort++; + freelistp= qhmem.freelists + qhmem.indextable[size]; + *((void **)object)= *freelistp; + *freelistp= object; + }else { + qhmem .freelong++; + qhmem .totlong -= size; + free (object); + if (qhmem.IStracing >= 5) + fprintf (qhmem.ferr, "qh_memfree long: %d bytes at %p\n", size, object); + } +} /* memfree */ + + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="memfreeshort">-</a> + + qh_memfreeshort( curlong, totlong ) + frees up all short and qhmem memory allocations + + returns: + number and size of current long allocations +*/ +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); + free(buffer); + } + qhmem.curbuffer= NULL; + if (qhmem .LASTsize) { + free (qhmem .indextable); + free (qhmem .freelists); + free (qhmem .sizetable); + } + ferr= qhmem.ferr; + memset((char *)&qhmem, 0, sizeof qhmem); /* every field is 0, FALSE, NULL */ + qhmem.ferr= ferr; +} /* memfreeshort */ + + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="meminit">-</a> + + 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)) { + fprintf (ferr, "qhull internal error (qh_meminit): sizeof(void*) < sizeof(int). qset.c will not work\n"); + exit (1); /* can not use qh_errexit() */ + } +} /* meminit */ + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="meminitbuffers">-</a> + + 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) { + fprintf (qhmem.ferr, "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 (numsizes, sizeof(int)); + qhmem.freelists= (void **) calloc (numsizes, sizeof(void *)); + if (!qhmem.sizetable || !qhmem.freelists) { + fprintf(qhmem.ferr, "qhull error (qh_meminit): insufficient memory\n"); + qh_errexit (qhmem_ERRmem, NULL, NULL); + } + if (qhmem.IStracing >= 1) + fprintf (qhmem.ferr, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment); +} /* meminitbuffers */ + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="memsetup">-</a> + + qh_memsetup() + set up memory after running memsize() +*/ +void qh_memsetup (void) { + int k,i; + + qsort(qhmem.sizetable, qhmem.TABLEsize, sizeof(int), qh_intcompare); + qhmem.LASTsize= qhmem.sizetable[qhmem.TABLEsize-1]; + if (qhmem .LASTsize >= qhmem .BUFsize || qhmem.LASTsize >= qhmem .BUFinit) { + fprintf (qhmem.ferr, "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 *)malloc((qhmem.LASTsize+1) * sizeof(int)))) { + fprintf(qhmem.ferr, "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 */ + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="memsize">-</a> + + qh_memsize( size ) + define a free list for this size +*/ +void qh_memsize(int size) { + int k; + + if (qhmem .LASTsize) { + fprintf (qhmem .ferr, "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 + fprintf(qhmem.ferr, "qhull warning (memsize): free list table has room for only %d sizes\n", qhmem.NUMsizes); +} /* memsize */ + + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="memstatistics">-</a> + + qh_memstatistics( fp ) + print out memory statistics + + notes: + does not account for wasted memory at the end of each block +*/ +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; + } + fprintf (fp, "\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 long memory allocated (except for input)\n\ +%7d bytes of long memory in use (in %d pieces)\n\ +%7d bytes per memory buffer (initially %d bytes)\n", + qhmem .cntquick, qhmem.cntshort, qhmem.cntlong, + qhmem .freeshort, qhmem.freelong, + qhmem .totshort - qhmem .freesize - totfree, + totfree, + qhmem .maxlong, qhmem .totlong, qhmem .cntlong - qhmem .freelong, + qhmem .BUFsize, qhmem .BUFinit); + if (qhmem.cntlarger) { + fprintf (fp, "%7d calls to qh_setlarger\n%7.2g average copy size\n", + qhmem.cntlarger, ((float) qhmem.totlarger)/ qhmem.cntlarger); + fprintf (fp, " freelists (bytes->count):"); + } + for (i=0; i < qhmem.TABLEsize; i++) { + count=0; + for (object= qhmem .freelists[i]; object; object= *((void **)object)) + count++; + fprintf (fp, " %d->%d", qhmem.sizetable[i], count); + } + fprintf (fp, "\n\n"); +} /* memstatistics */ + + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="NOmem">-</a> + + qh_NOmem + turn off quick-fit memory allocation + + notes: + uses malloc() and free() instead +*/ +#else /* qh_NOmem */ + +void *qh_memalloc(int insize) { + void *object; + + if (!(object= malloc(insize))) { + fprintf(qhmem.ferr, "qhull error (qh_memalloc): insufficient memory\n"); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (qhmem.IStracing >= 5) + fprintf (qhmem.ferr, "qh_memalloc long: %d bytes at %p\n", insize, object); + return object; +} + +void qh_memfree(void *object, int size) { + + if (!object) + return; + free (object); + if (qhmem.IStracing >= 5) + fprintf (qhmem.ferr, "qh_memfree long: %d bytes at %p\n", size, object); +} + +void qh_memfreeshort (int *curlong, int *totlong) { + + memset((char *)&qhmem, 0, sizeof qhmem); /* every field is 0, FALSE, NULL */ + *curlong= 0; + *totlong= 0; +} + +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)) { + fprintf (ferr, "qhull internal error (qh_meminit): sizeof(void*) < sizeof(int). qset.c will not work\n"); + 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) { + +} + +#endif /* qh_NOmem */ diff --git a/NifCommon/qhull/mem.h b/NifCommon/qhull/mem.h new file mode 100644 index 0000000..cf45e2f --- /dev/null +++ b/NifCommon/qhull/mem.h @@ -0,0 +1,174 @@ +/*<html><pre> -<a href="qh-mem.htm" + >-------------------------------</a><a name="TOP">-</a> + + 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-2003, The Geometry Center +*/ + +#ifndef qhDEFmem +#define qhDEFmem + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="NOmem">-</a> + + 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 +*/ + +/*------------------------------------------- + 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 <a href="user.h#MEMalign">qh_MEMalign</a> in user.h for qhull's alignment +*/ + +#define qhmem_ERRmem 4 /* matches qh_ERRmem in qhull.h */ +#define qhmem_ERRqhull 5 /* matches qh_ERRqhull in qhull.h */ + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="ptr_intT">-</a> + + ptr_intT + for casting a void* to an integer-type + + notes: + On 64-bit machines, a pointer may be larger than an 'int'. + qh_meminit() checks that 'long' holds a 'void*' +*/ +typedef unsigned long ptr_intT; + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="qhmemT">-</a> + + 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; + +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 free memory in bytes */ + void *tempstack; /* stack of temporary memory, managed by users */ + FILE *ferr; /* file for reporting errors */ + int IStracing; /* =5 if tracing memory allocations */ + int cntquick; /* count of quick allocations */ + /* remove statistics doesn't effect speed */ + int cntshort; /* count of short allocations */ + int cntlong; /* count of long allocations */ + int curlong; /* current count of inuse, long allocations */ + int freeshort; /* count of short memfrees */ + int freelong; /* count of long memfrees */ + int totshort; /* total size of short allocations */ + int totlong; /* total size of long allocations */ + int maxlong; /* maximum totlong */ + int cntlarger; /* count of setlarger's */ + int totlarger; /* total copied by setlarger */ +}; + + +/*==================== -macros ====================*/ + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="memalloc_">-</a> + + qh_memalloc_(size, object, type) + returns object of size bytes + assumes size<=qhmem.LASTsize and void **freelistp is a temp +*/ + +#ifdef qh_NOmem +#define qh_memalloc_(size, freelistp, object, type) {\ + object= (type*)qh_memalloc (size); } +#else /* !qh_NOmem */ + +#define qh_memalloc_(size, freelistp, object, type) {\ + freelistp= qhmem.freelists + qhmem.indextable[size];\ + if ((object= (type*)*freelistp)) {\ + qhmem.cntquick++; \ + *freelistp= *((void **)*freelistp);\ + }else object= (type*)qh_memalloc (size);} +#endif + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="memfree_">-</a> + + qh_memfree_(object, size) + free up an object + + notes: + object may be NULL + assumes size<=qhmem.LASTsize and void **freelistp is a temp +*/ +#ifdef qh_NOmem +#define qh_memfree_(object, size, freelistp) {\ + qh_memfree (object, size); } +#else /* !qh_NOmem */ + +#define qh_memfree_(object, size, freelistp) {\ + if (object) { \ + qhmem .freeshort++;\ + freelistp= qhmem.freelists + qhmem.indextable[size];\ + *((void **)object)= *freelistp;\ + *freelistp= object;}} +#endif + +/*=============== prototypes in alphabetical order ============*/ + +void *qh_memalloc(int insize); +void qh_memfree (void *object, int size); +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); + +#endif /* qhDEFmem */ diff --git a/NifCommon/qhull/merge.c b/NifCommon/qhull/merge.c new file mode 100644 index 0000000..457bbe5 --- /dev/null +++ b/NifCommon/qhull/merge.c @@ -0,0 +1,3619 @@ +/*<html><pre> -<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + 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 qhull.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-2003 The Geometry Center +*/ + +#include "qhull_a.h" + +#ifndef qh_NOmerge + +/*===== functions (alphabetical after premerge and postmerge) ======*/ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="premerge">-</a> + + 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, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="postmerge">-</a> + + 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 (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"); + fprintf (qh ferr, "\n%s with 'C%.2g' and 'A%.2g'\n", + reason, maxcentrum, maxangle); + } + trace2((qh ferr, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="all_merges">-</a> + + 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, "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, 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, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="appendmergeset">-</a> + + 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_(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) { + fprintf(qh ferr, "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)) { + fprintf(qh ferr, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="basevertices">-</a> + + 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, "qh_basevertices: found %d vertices\n", + qh_setsize (vertices))); + return vertices; +} /* basevertices */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="checkconnect">-</a> + + 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; + fprintf(qh ferr, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="checkzero">-</a> + + 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, "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, "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, "qh_checkzero: facet f%d needs pre-merging\n", + facet->id)); + return False; + + LABELnonconvex: + trace2((qh ferr, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="compareangle">-</a> + + qh_compareangle( angle1, angle2 ) + used by qsort() to order merges by angle +*/ +int qh_compareangle(const void *p1, const void *p2) { + mergeT *a= *((mergeT **)p1), *b= *((mergeT **)p2); + + return ((a->angle > b->angle) ? 1 : -1); +} /* compareangle */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="comparemerge">-</a> + + qh_comparemerge( merge1, merge2 ) + used by qsort() to order merges +*/ +int qh_comparemerge(const void *p1, const void *p2) { + mergeT *a= *((mergeT **)p1), *b= *((mergeT **)p2); + + return (a->type - b->type); +} /* comparemerge */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="comparevisit">-</a> + + qh_comparevisit( vertex1, vertex2 ) + used by qsort() to order vertices by their visitid +*/ +int qh_comparevisit (const void *p1, const void *p2) { + vertexT *a= *((vertexT **)p1), *b= *((vertexT **)p2); + + return (a->visitid - b->visitid); +} /* comparevisit */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="copynonconvex">-</a> + + 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, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n", + atridge->id, ridge->id)); + break; + } + } +} /* copynonconvex */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="degen_redundant_facet">-</a> + + 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, "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, "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, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id)); + } +} /* degen_redundant_facet */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="degen_redundant_neighbors">-</a> + + 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, "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, "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, "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, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id)); + } + } +} /* degen_redundant_neighbors */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="find_newvertex">-</a> + + 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) { + fprintf (qh ferr, "qh_find_newvertex: find new vertex for v%d from ", + oldvertex->id); + FOREACHvertex_(vertices) + fprintf (qh ferr, "v%d ", vertex->id); + FOREACHridge_(ridges) + fprintf (qh ferr, "r%d ", ridge->id); + fprintf (qh ferr, "\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 += qh_setsize (ridges); + if (!qh_setsize (vertices)) { + trace4((qh ferr, "qh_find_newvertex: vertices not in ridges for v%d\n", + oldvertex->id)); + return NULL; + } + qsort (SETaddr_(vertices, vertexT), 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, "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, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="findbest_test">-</a> + + 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 + maxdist= dist; + }else + dist= qh_getdistance (facet, neighbor, &mindist, &maxdist); + if (dist < *distp) { + *bestfacet= neighbor; + *mindistp= mindist; + *maxdistp= maxdist; + *distp= dist; + } +} /* findbest_test */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="findbestneighbor">-</a> + + 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) { + fprintf (qh ferr, "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, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="flippedmerges">-</a> + + 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, "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, "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, sizeof(mergeT)); + else + qh_setappend (&qh facet_mergeset, merge); + } + qh_settempfree (&othermerges); + if (nummerge) + *wasmerge= True; + trace1((qh ferr, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge)); +} /* flippedmerges */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="forcedmerges">-</a> + + 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, "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)) { + fprintf (qh ferr, "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, "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, sizeof(mergeT)); + else + qh_setappend (&qh facet_mergeset, merge); + } + qh_settempfree (&othermerges); + if (nummerge) + *wasmerge= True; + trace1((qh ferr, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n", + nummerge, numflip)); +} /* forcedmerges */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="getmergeset">-</a> + + 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, "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), nummerges,sizeof(mergeT *),qh_compareangle); + else + qsort(SETaddr_(qh facet_mergeset, mergeT), nummerges,sizeof(mergeT *),qh_comparemerge); + if (qh POSTmerging) { + zadd_(Zmergesettot2, nummerges); + }else { + zadd_(Zmergesettot, nummerges); + zmax_(Zmergesetmax, nummerges); + } + trace2((qh ferr, "qh_getmergeset: %d merges found\n", nummerges)); +} /* getmergeset */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="getmergeset_initial">-</a> + + 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), nummerges,sizeof(mergeT *),qh_compareangle); + else + qsort(SETaddr_(qh facet_mergeset, mergeT), nummerges,sizeof(mergeT *),qh_comparemerge); + if (qh POSTmerging) { + zadd_(Zmergeinittot2, nummerges); + }else { + zadd_(Zmergeinittot, nummerges); + zmax_(Zmergeinitmax, nummerges); + } + trace2((qh ferr, "qh_getmergeset_initial: %d merges found\n", nummerges)); +} /* getmergeset_initial */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="hashridge">-</a> + + 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= (int)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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="hashridge_find">-</a> + + 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= (int)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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="makeridges">-</a> + + 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, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mark_dupridges">-</a> + + 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, "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, "qh_mark_dupridges: found %d duplicated ridges\n", + nummerge)); +} /* mark_dupridges */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="maydropneighbor">-</a> + + 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, "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, "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, "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, "qh_maydropneighbors: f%d is degenerate.\n", facet->id)); + } +} /* maydropneighbor */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="merge_degenredundant">-</a> + + 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, 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) { + fprintf (qh ferr, "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, "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, "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, "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, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="merge_nonconvex">-</a> + + 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, "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, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle">-</a> + + 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) { + fprintf (qh ferr, "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, "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; + fprintf (qh ferr, "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) { + fprintf (qh ferr, " same cycle:"); + FORALLsame_cycle_(samecycle) + fprintf(qh ferr, " f%d", same->id); + fprintf (qh ferr, "\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) { + fprintf (qh ferr, "qh_mergecycle: end of trace facet\n"); + qh IStracing= tracerestore; + } +} /* mergecycle */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_all">-</a> + + 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, "qh_mergecycle_all: begin\n")); + for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) { + if (facet->normal) + continue; + if (!facet->mergehorizon) { + fprintf (qh ferr, "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= nummerge; + zzinc_(Zcyclehorizon); + total += facets; + zzadd_(Zcyclefacettot, facets); + zmax_(Zcyclefacetmax, facets); + } + cycles++; + } + if (cycles) + *wasmerge= True; + trace1((qh ferr, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles)); +} /* mergecycle_all */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_facets">-</a> + + 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, "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, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_facets */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_neighbors">-</a> + + 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, "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, "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, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n", + delneighbors, newneighbors)); +} /* mergecycle_neighbors */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_ridges">-</a> + + 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, "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, "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 { + fprintf (qh ferr, "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, sizeof(ridgeT), freelistp); + numold++; + }else if (neighbor->visitid == samevisitid) { + qh_setdel (neighbor->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree_(ridge, 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, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n", + numold, numnew)); +} /* mergecycle_ridges */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_vneighbors">-</a> + + 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, "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, "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, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_vneighbors */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergefacet">-</a> + + 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 + 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) { + fprintf (qh ferr, "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; + fprintf (qh ferr, "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; + fprintf (qh ferr, "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; + } + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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) { + fprintf(qh ferr, "\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) + fprintf(qh ferr, "Option 'Qx' may avoid this problem.\n"); + qh_errexit(qh_ERRinput, 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= 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) { + fprintf (qh ferr, "qh_mergefacet: end of wide tracing\n"); + qh IStracing= tracerestore; + } +} /* mergefacet */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergefacet2d">-</a> + + 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, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergeneighbors">-</a> + + 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, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergeridges">-</a> + + 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, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergesimplex">-</a> + + 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, "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, "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, "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, 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, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n", + facet1->id, getid_(apex), facet2->id)); +} /* mergesimplex */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergevertex_del">-</a> + + 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, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergevertex_neighbors">-</a> + + 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, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n", + facet1->id, facet2->id)); + if (qh tracevertex) { + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergevertices">-</a> + + 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)) { + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="neighbor_intersections">-</a> + + 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, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n", + qh_setsize (intersect), vertex->id)); + return intersect; +} /* neighbor_intersections */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="newvertices">-</a> + + 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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="reducevertices">-</a> + + 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, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n", + numshare, numrename, degenredun)); + return degenredun; +} /* reducevertices */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="redundant_vertex">-</a> + + 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, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="remove_extravertices">-</a> + + 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, "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, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id)); + }else + trace3((qh ferr, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="rename_sharedvertex">-</a> + + 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) { + fprintf (qh ferr, "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, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="renameridgevertex">-</a> + + 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, "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, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="renamevertex">-</a> + + 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) + fprintf (qh ferr, "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) + fprintf (qh ferr, "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) + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="test_appendmerge">-</a> + + 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, "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, "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, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="test_vneighbors">-</a> + + 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, "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, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n", + nummerges)); + return (nummerges > 0); +} /* test_vneighbors */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="tracemerge">-</a> + + 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)) { + fprintf (qh ferr, "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) + fprintf (qh ferr, "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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="tracemerging">-</a> + + 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); + fprintf (qh ferr, "\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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="updatetested">-</a> + + 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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="vertexridges">-</a> + + 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, "qh_vertexridges: found %d ridges for v%d\n", + size, vertex->id)); + } + return ridges; +} /* vertexridges */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="vertexridges_facet">-</a> + + 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 */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="willdelete">-</a> + + 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 (char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { +} +boolT qh_checkzero (boolT testall) { + } +#endif /* qh_NOmerge */ + diff --git a/NifCommon/qhull/merge.h b/NifCommon/qhull/merge.h new file mode 100644 index 0000000..3c4ec20 --- /dev/null +++ b/NifCommon/qhull/merge.h @@ -0,0 +1,174 @@ +/*<html><pre> -<a href="qh-merge.htm" + >-------------------------------</a><a name="TOP">-</a> + + merge.h + header file for merge.c + + see qh-merge.htm and merge.c + + copyright (c) 1993-2003, The Geometry Center +*/ + +#ifndef qhDEFmerge +#define qhDEFmerge 1 + + +/*============ -constants- ==============*/ + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="qh_ANGLEredundant">-</a> + + qh_ANGLEredundant + indicates redundant merge in mergeT->angle +*/ +#define qh_ANGLEredundant 6.0 + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="qh_ANGLEdegen">-</a> + + qh_ANGLEdegen + indicates degenerate facet in mergeT->angle +*/ +#define qh_ANGLEdegen 5.0 + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="qh_ANGLEconcave">-</a> + + 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 + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="MRG">-</a> + + 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 (not enough neighbors) facet1 == facet2 */ + MRGredundant, /* redundant facet (vertex subset) */ + /* merge_degenredundant assumes degen < redundant */ + MRGmirror, /* mirror facet from qh_triangulate */ + ENDmrg +} mergeType; + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="qh_MERGEapex">-</a> + + qh_MERGEapex + flag for qh_mergefacet() to indicate an apex merge +*/ +#define qh_MERGEapex True + +/*============ -structures- ====================*/ + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="mergeT">-</a> + + 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- =========================*/ + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="FOREACHmerge_">-</a> + + 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 <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#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 (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/NifCommon/qhull/poly.c b/NifCommon/qhull/poly.c new file mode 100644 index 0000000..7f5e4b8 --- /dev/null +++ b/NifCommon/qhull/poly.c @@ -0,0 +1,1180 @@ +/*<html><pre> -<a href="qh-poly.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly.c + implements polygons and simplices + + see qh-poly.htm, poly.h and qhull.h + + infrequent code is in poly2.c + (all but top 50 and their callers 12/3/95) + + copyright (c) 1993-2003, The Geometry Center +*/ + +#include "qhull_a.h" + +/*======== functions in alphabetical order ==========*/ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="appendfacet">-</a> + + 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, "qh_appendfacet: append f%d to facet_list\n", facet->id)); +} /* appendfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="appendvertex">-</a> + + 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, "qh_appendvertex: append v%d to vertex_list\n", vertex->id)); +} /* appendvertex */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="attachnewfacets">-</a> + + 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, "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, sizeof(ridgeT)); + } + } + SETfirst_(visible->ridges)= NULL; + } + SETfirst_(visible->neighbors)= NULL; + } + trace1((qh ferr, "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 { + fprintf (qh ferr, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkflipped">-</a> + + 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, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="delfacet">-</a> + + 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, "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, sizeof(facetT), freelistp); +} /* delfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="deletevisible">-</a> + + 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, "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) { + fprintf (qh ferr, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="facetintersect">-</a> + + 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) { + fprintf (qh ferr, "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, "qh_facetintersect: f%d skip %d matches f%d skip %d\n", + facetA->id, *skipA, facetB->id, *skipB)); + return(intersect); +} /* facetintersect */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="gethash">-</a> + + qh_gethash( hashsize, set, size, firstindex, skipelem ) + return hashvalue for a set with firstindex and skipelem + + notes: + 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 +*/ +unsigned qh_gethash (int hashsize, setT *set, int size, int firstindex, void *skipelem) { + void **elemp= SETelemaddr_(set, firstindex, void); + ptr_intT hash = 0, elem; + int i; + + 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; + } + hash %= (ptr_intT) hashsize; + /* hash= 0; for debugging purposes */ + return hash; +} /* gethash */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="makenewfacet">-</a> + + 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= toporient; + if (horizon) + qh_setappend(&(newfacet->neighbors), horizon); + qh_appendfacet(newfacet); + return(newfacet); +} /* makenewfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="makenewplanes">-</a> + + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="makenew_nonsimplicial">-</a> + + 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, 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) { + fprintf (qh ferr, "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, sizeof(ridgeT)); + }else { + qh_setappend(&(newfacet->ridges), ridge); + if (toporient) + ridge->top= newfacet; + else + ridge->bottom= newfacet; + } + trace4((qh ferr, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="makenew_simplicial">-</a> + + 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, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="matchneighbor">-</a> + + 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= (int)qh_gethash (hashsize, newfacet->vertices, qh hull_dim, 1, + SETelem_(newfacet->vertices, newskip)); + trace4((qh ferr, "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"); + fprintf (qh ferr, "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 == (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, "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"); + fprintf (qh ferr, "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, "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, "qh_matchneighbor: no match for f%d skip %d at hash %d\n", + newfacet->id, newskip, hash)); +} /* matchneighbor */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="matchnewfacets">-</a> + + 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, "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; newskip<qh hull_dim; newskip++) /* furthest/horizon already matched */ + qh_matchneighbor (newfacet, newskip, hashsize, &hashcount); +#if 0 /* use the following to trap hashcount errors */ + { + int count= 0, k; + facetT *facet, *neighbor; + + count= 0; + FORALLfacet_(qh newfacet_list) { /* newfacet already in use */ + for (k=1; k < qh hull_dim; k++) { + neighbor= SETelemt_(facet->neighbors, k, facetT); + if (!neighbor || neighbor == qh_DUPLICATEridge) + count++; + } + if (facet == newfacet) + break; + } + if (count != hashcount) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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++; + } + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="matchvertices">-</a> + + 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); + *same= !(((ptr_intT)skipA & 0x1) ^ ((ptr_intT)*skipB & 0x1)); + trace4((qh ferr, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="newfacet">-</a> + + 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_(sizeof(facetT), freelistp, facet, facetT); + memset ((char *)facet, 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, "qh_newfacet: created facet f%d\n", facet->id)); + return (facet); +} /* newfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="newridge">-</a> + + qh_newridge() + return a new ridge +*/ +ridgeT *qh_newridge(void) { + ridgeT *ridge; + void **freelistp; /* used !qh_NOmem */ + + qh_memalloc_(sizeof(ridgeT), freelistp, ridge, ridgeT); + memset ((char *)ridge, 0, sizeof(ridgeT)); + zinc_(Ztotridges); + if (qh ridge_id == 0xFFFFFF) { + fprintf(qh ferr, "\ +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, "qh_newridge: created ridge r%d\n", ridge->id)); + return (ridge); +} /* newridge */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="pointid">-</a> + + 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: + if point not in point array + the code does a comparison of unrelated pointers. +*/ +int qh_pointid (pointT *point) { + long offset, id; + + if (!point) + id= -3; + else if (point == qh interior_point) + id= -2; + else if (point >= qh first_point + && point < qh first_point + qh num_points * qh hull_dim) { + offset= point - qh first_point; + id= offset / qh hull_dim; + }else if ((id= qh_setindex (qh other_points, point)) != -1) + id += qh num_points; + else + id= -1; + return (int) id; +} /* pointid */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="removefacet">-</a> + + 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, "qh_removefacet: remove f%d from facet_list\n", facet->id)); +} /* removefacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="removevertex">-</a> + + 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, "qh_removevertex: remove v%d from vertex_list\n", vertex->id)); +} /* removevertex */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="updatevertices">-</a> + + 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, "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, "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, "qh_updatevertices: delete vertex p%d (v%d) in f%d\n", + qh_pointid(vertex->point), vertex->id, visible->id)); + } + } + } + } +} /* updatevertices */ + + + diff --git a/NifCommon/qhull/poly.h b/NifCommon/qhull/poly.h new file mode 100644 index 0000000..320944f --- /dev/null +++ b/NifCommon/qhull/poly.h @@ -0,0 +1,291 @@ +/*<html><pre> -<a href="qh-poly.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly.h + header file for poly.c and poly2.c + + see qh-poly.htm, qhull.h and poly.c + + copyright (c) 1993-2003, The Geometry Center +*/ + +#ifndef qhDEFpoly +#define qhDEFpoly 1 + +/*=============== constants ========================== */ + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="ALGORITHMfault">-</a> + + ALGORITHMfault + use as argument to checkconvex() to report errors during buildhull +*/ +#define qh_ALGORITHMfault 0 + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="DATAfault">-</a> + + DATAfault + use as argument to checkconvex() to report errors during initialhull +*/ +#define qh_DATAfault 1 + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="DUPLICATEridge">-</a> + + 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 + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="MERGEridge">-</a> + + 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- =========================*/ + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLfacet_">-</a> + + 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 ) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLnew_facets">-</a> + + 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 ) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLvertex_">-</a> + + 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 ) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLvisible_facets">-</a> + + 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) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLsame_">-</a> + + 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) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLsame_cycle_">-</a> + + 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)) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHneighborA_">-</a> + + 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: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHneighborA_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighborA) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHvisible_">-</a> + + FOREACHvisible_( facets ) { ... } + assign 'visible' to each facet in facets + + notes: + uses 'facetT *facet, *facetp;' + see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHnewfacet_">-</a> + + FOREACHnewfacet_( facets ) { ... } + assign 'newfacet' to each facet in facets + + notes: + uses 'facetT *newfacet, *newfacetp;' + see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHvertexA_">-</a> + + FOREACHvertexA_( vertices ) { ... } + assign 'vertexA' to each vertex in vertices + + notes: + uses 'vertexT *vertexA, *vertexAp;' + see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHvertexreverse12_">-</a> + + FOREACHvertexreverse12_( vertices ) { ... } + assign 'vertex' to each vertex in vertices + reverse order of first two vertices + + notes: + uses 'vertexT *vertex, *vertexp;' + see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#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); +unsigned 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, unsigned 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/NifCommon/qhull/poly2.c b/NifCommon/qhull/poly2.c new file mode 100644 index 0000000..d00d8dc --- /dev/null +++ b/NifCommon/qhull/poly2.c @@ -0,0 +1,3137 @@ +/*<html><pre> -<a href="qh-poly.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly2.c + implements polygons and simplices + + see qh-poly.htm, poly.h and qhull.h + + frequently used code is in poly.c + + copyright (c) 1993-2003, The Geometry Center +*/ + +#include "qhull_a.h" + +/*======== functions in alphabetical order ==========*/ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="addhash">-</a> + + 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, unsigned 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_bestdist">-</a> + + 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, "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, "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) + fprintf (qh ferr, "\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; + fprintf(qh ferr, "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) + fprintf(qh ferr, "\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) { + fprintf( qh ferr, "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, "qh_check_bestdist: max distance outside %2.2g\n", maxdist)); +} /* check_bestdist */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_maxout">-</a> + + 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, "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, "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) + fprintf (qh ferr, "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; + zinc_(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)) + fprintf (qh ferr, "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, "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 + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_output">-</a> + + 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 */ + + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_point">-</a> + + 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; + } + fprintf(qh ferr, "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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_points">-</a> + + 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, "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 * qh num_points; + else + total= (float) qh num_facets * qh num_points; + if (total >= qh_VERIFYdirect && !qh maxoutdone) { + if (!qh_QUICKhelp && qh SKIPcheckmax && qh MERGING) + fprintf (qh ferr, "\n\ +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) + fprintf (qh ferr, "\n\ +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) + fprintf (qh ferr, "\n\ +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) + fprintf (qh ferr, "\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 + fprintf (qh ferr, "\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) { + fprintf( qh ferr, "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) { + fprintf( qh ferr, "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, "qh_check_points: max distance outside %2.2g\n", maxdist)); + } +} /* check_points */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkconvex">-</a> + + 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, "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"); + fprintf (qh ferr, "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"); + fprintf (qh ferr, "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"); + fprintf (qh ferr, "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"); + fprintf (qh ferr, "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, "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; + fprintf (qh ferr, "qhull note: 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"); + fprintf (qh ferr, "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"); + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkfacet">-</a> + + 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) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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)) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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)) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkflipped_all">-</a> + + 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)) { + fprintf(qh ferr, "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) { + fprintf (qh ferr, "\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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkpolygon">-</a> + + 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, "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 + ) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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 += numfacets; + if (facetlist == qh facet_list) { + if (numfacets != qh num_facets - qh num_visible) { + fprintf(qh ferr, "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) { + fprintf(qh ferr, "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)) { + fprintf(qh ferr, "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) { + fprintf (qh ferr, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n", + numvertices, numfacets); + waserror= True; + } + if (qh hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) { + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkvertex">-</a> + + 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) { + fprintf (qh ferr, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point); + waserror= True; + } + if (vertex->id >= qh vertex_id) { + fprintf (qh ferr, "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)) { + fprintf (qh ferr, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="clearcenters">-</a> + + 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 (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, "qh_clearcenters: switched to center type %d\n", type)); +} /* clearcenters */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="createsimplex">-</a> + + 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= 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, "qh_createsimplex: created simplex\n")); +} /* createsimplex */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="delridge">-</a> + + 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, sizeof(ridgeT), freelistp); +} /* delridge */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="delvertex">-</a> + + 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, sizeof(vertexT)); +} /* delvertex */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="facet3vertex">-</a> + + 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) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findbestfacet">-</a> + + 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 <a href="../html/qh-in.htm#findfacet">locate a facet with qh_findbestfacet()</a> + + 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: + <a href="geom.c#findbest">qh_findbest</a> +*/ +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, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n", + bestfacet->id, *bestdist, *isoutside, totpart)); + return bestfacet; +} /* findbestfacet */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findbestlower">-</a> + + 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) { + fprintf(qh ferr, "\n\ +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, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n", + bestfacet->id, bestdist, upperfacet->id, qh_pointid(point))); + return bestfacet; +} /* findbestlower */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findfacet_all">-</a> + + 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 +*/ +facetT *qh_findfacet_all (pointT *point, realT *bestdist, boolT *isoutside, + int *numpart) { + facetT *bestfacet= NULL, *facet; + realT dist; + int totpart= 0; + + *bestdist= REALmin; + *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, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n", + getid_(bestfacet), *bestdist, *isoutside, totpart)); + return bestfacet; +} /* findfacet_all */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findgood">-</a> + + 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, "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, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findgood_all">-</a> + + 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) { + fprintf (qh ferr, "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) + fprintf (qh ferr, "qhull warning: point p%d is not a vertex ('QV%d').\n", + qh GOODvertex-1, qh GOODvertex-1); + else + fprintf (qh ferr, "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, "qh_findgood_all: f%d is closest (%2.2g) to thresholds\n", + bestfacet->id, bestangle)); + return; + } + } + qh num_good= numgood; + trace0((qh ferr, "qh_findgood_all: %d good facets remain out of %d facets\n", + numgood, startgood)); +} /* findgood_all */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="furthestnext">-</a> + + 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, "qh_furthestnext: made f%d next facet (dist %.2g)\n", + bestfacet->id, bestdist)); + } +} /* furthestnext */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="furthestout">-</a> + + 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, "qh_furthestout: p%d is furthest outside point of f%d\n", + qh_pointid (point), facet->id)); +} /* furthestout */ + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="infiniteloop">-</a> + + qh_infiniteloop( facet ) + report infinite loop error due to facet +*/ +void qh_infiniteloop (facetT *facet) { + + fprintf (qh ferr, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n"); + qh_errexit (qh_ERRqhull, facet, NULL); +} /* qh_infiniteloop */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="initbuild">-</a> + + 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)))) { + fprintf (qh ferr, "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) + fprintf (qh ferr, "\nTrace level %d for %s | %s\n", + qh IStracing ? qh IStracing : qh TRACElevel, qh rbox_command, qh qhull_command); + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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))) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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, "qh_initbuild: initial hull created and points partitioned\n")); +} /* initbuild */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="initialhull">-</a> + + 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 ^= True; + } + FORALLfacets + qh_setfacetplane(facet); + FORALLfacets { + if (!qh_checkflipped (facet, NULL, qh_ALL)) {/* due to axis-parallel facet */ + trace1((qh ferr, "qh_initialhull: initial orientation incorrect. Correct all facets\n")); + facet->flipped= False; + FORALLfacets { + facet->toporient ^= True; + qh_orientoutside (facet); + } + break; + } + } + FORALLfacets { + if (!qh_checkflipped (facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */ + qh_precision ("initial facet is coplanar with interior point"); + fprintf (qh ferr, "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) + fprintf (qh ferr, "qhull precision warning: \n\ +The initial hull is narrow (cosine of min. angle is %.16f).\n\ +A coplanar point may lead to a wide facet. Options 'QbB' (scale to unit box)\n\ +or 'Qbb' (scale last coordinate) may remove this warning. Use 'Pp' to skip\n\ +this warning. See 'Limitations' in qh-impre.htm.\n", + -minangle); /* convert from angle between normals to angle between facets */ + } + 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) { + fprintf(qh ferr, "qh_initialhull: simplex constructed, interior point:"); + for (k=0; k < qh hull_dim; k++) + fprintf (qh ferr, " %6.4g", qh interior_point[k]); + fprintf (qh ferr, "\n"); + } +#endif +} /* initialhull */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="initialvertices">-</a> + + 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 index, 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); + index= (int)floor(qh num_points * randr); + while (qh_setin (simplex, qh_point (index))) { + index++; /* in case qh_RANDOMint always returns the same value */ + index= index < qh num_points ? index : 0; + } + qh_setappend (&simplex, qh_point (index)); + } + }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++; + } + } + } + index= 0; + while (k != dim && (point= qh_point (index++))) { + 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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="isvertex">-</a> + + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="makenewfacets">-</a> + + 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, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="matchduplicates">-</a> + + 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= (int)qh_gethash (hashsize, atfacet->vertices, qh hull_dim, 1, + SETelem_(atfacet->vertices, atskip)); + trace2((qh ferr, "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 == (newfacet->toporient ^ facet->toporient)); + if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) { + if (!makematch) { + fprintf (qh ferr, "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, "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, "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) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="nearcoplanar">-</a> + + qh_nearcoplanar() + for all facets, remove near-inside points from facet->coplanarset</li> + 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 * qsqrt (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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="nearvertex">-</a> + + 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) { + fprintf(qh ferr, "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= (vertexT*)SETfirst_(facet->vertices); + 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= qsqrt (bestdist); + trace3((qh ferr, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n", + bestvertex->id, *bestdistp, facet->id, qh_pointid(point))); + return bestvertex; +} /* nearvertex */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="newhashtable">-</a> + + 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 ((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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="newvertex">-</a> + + qh_newvertex( point ) + returns a new vertex for point +*/ +vertexT *qh_newvertex(pointT *point) { + vertexT *vertex; + + zinc_(Ztotvertices); + vertex= (vertexT *)qh_memalloc(sizeof(vertexT)); + memset ((char *) vertex, 0, sizeof (vertexT)); + if (qh vertex_id == 0xFFFFFF) { + fprintf(qh ferr, "qhull input error: more than %d vertices. ID field overflows and two vertices\n\ +may have the same identifier. Vertices not sorted correctly.\n", 0xFFFFFF); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh vertex_id == qh tracevertex_id) + qh tracevertex= vertex; + vertex->id= qh vertex_id++; + vertex->point= point; + trace4((qh ferr, "qh_newvertex: vertex p%d (v%d) created\n", qh_pointid(vertex->point), + vertex->id)); + return (vertex); +} /* newvertex */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="nextridge3d">-</a> + + qh_nextridge3d( atridge, facet, vertex ) + return next ridge and vertex for a 3d facet + + notes: + in qh_ORIENTclock order + this is a O(n^2) implementation to trace all ridges + be sure to stop on any 2nd visit + + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="outcoplanar">-</a> + + 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, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="point">-</a> + + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="point_add">-</a> + + 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) + fprintf (qh ferr, "qhull internal warning (point_add): unknown point %p id %d\n", + point, id); + else if (id >= size) { + fprintf (qh ferr, "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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="pointfacet">-</a> + + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="pointvertex">-</a> + + 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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="prependfacet">-</a> + + 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, "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 */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="printhashtable">-</a> + + 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; + fprintf (fp, "hash %d f%d ", facet_i, facet->id); + FOREACHvertex_(facet->vertices) + fprintf (fp, "v%d ", vertex->id); + fprintf (fp, "\n neighbors:"); + FOREACHneighbor_i_(facet) { + if (neighbor == qh_MERGEridge) + id= -3; + else if (neighbor == qh_DUPLICATEridge) + id= -2; + else + id= getid_(neighbor); + fprintf (fp, " %d", id); + } + fprintf (fp, "\n"); + } + } +} /* printhashtable */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="printlists">-</a> + + 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; + + fprintf (qh ferr, "qh_printlists: facets:"); + FORALLfacets { + if (++count % 100 == 0) + fprintf (qh ferr, "\n "); + fprintf (qh ferr, " %d", facet->id); + } + fprintf (qh ferr, "\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) + fprintf (qh ferr, "\n "); + fprintf (qh ferr, " %d", vertex->id); + } + fprintf (qh ferr, "\n"); +} /* printlists */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="resetlists">-</a> + + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="setvoronoi_all">-</a> + + 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 + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="triangulate">-</a> + + qh_triangulate() + triangulate non-simplicial facets on qh.facet_list, + if qh.CENTERtype=qh_ASvoronoi, sets Voronoi centers of non-simplicial facets + + 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; + + trace1((qh ferr, "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, "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) { + fprintf( qh ferr, "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, "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, sizeof(mergeT)); + if (mergetype == MRGmirror) { + zinc_(Ztrimirror); + qh_triangulate_mirror (facet1, facet2); + } + } + qh_settempfree(&qh degen_mergeset); + trace2((qh ferr, "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, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list))); + trace2((qh ferr, "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, "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, "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) { + fprintf( qh ferr, "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, "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); +} /* triangulate */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="triangulate_facet">-</a> + + 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, "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= (vertexT*)SETfirst_(facetA->vertices); + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="triangulate_link">-</a> + + 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, "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) { + fprintf( qh ferr, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="triangulate_mirror">-</a> + + 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, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="triangulate_null">-</a> + + 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, "qh_triangulate_null: delete null facet f%d\n", facetA->id)); + neighbor= (facetT*)SETfirst_(facetA->neighbors); + otherfacet= (facetT*)SETsecond_(facetA->neighbors); + qh_triangulate_link (facetA, neighbor, facetA, otherfacet); + qh_willdelete (facetA, NULL); +} /* triangulate_null */ + +#else /* qh_NOmerge */ +void qh_triangulate (void) { +} +#endif /* qh_NOmerge */ + + /*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="vertexintersect">-</a> + + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="vertexintersect_new">-</a> + + 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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="vertexneighbors">-</a> + + 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, "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 */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="vertexsubset">-</a> + + 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/NifCommon/qhull/qconvex.c b/NifCommon/qhull/qconvex.c new file mode 100644 index 0000000..825e2c8 --- /dev/null +++ b/NifCommon/qhull/qconvex.c @@ -0,0 +1,334 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + qconvex.c + compute convex hulls using qhull + + see unix.c for full interface + + copyright (c) 1993-2003, The Geometry Center +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include "qhull.h" +#include "mem.h" +#include "qset.h" + +#if __MWERKS__ && __POWERPC__ +#include <SIOUX.h> +#include <Files.h> +#include <console.h> +#include <Desk.h> + +#elif __cplusplus +extern "C" { + int isatty (int); +} + +#elif _MSC_VER +#include <io.h> +#define isatty _isatty + +#else +int isatty (int); /* returns 1 if stdin is a tty + if "Undefined symbol" this can be deleted along with call in main() */ +#endif + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt">-</a> + + qh_prompt + long prompt for qconvex + + notes: + restricted version of qhull.c + + see: + concise prompt below +*/ + +/* duplicated in qconvex.htm */ +char hidden_options[]=" d v H Qbb Qf Qg Qm Qr Qu Qv Qx Qz TR E V Fp Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 "; + +char qh_prompta[]= "\n\ +qconvex- compute the convex hull\n\ + http://www.qhull.org %s\n\ +\n\ +input (stdin):\n\ + first lines: dimension and number of points (or vice-versa).\n\ + other lines: point coordinates, best if one point per line\n\ + comments: start with a non-numeric character\n\ +\n\ +options:\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ + Qc - keep coplanar points with nearest facet\n\ + Qi - keep interior points with nearest facet\n\ +\n\ +Qhull control options:\n\ + Qbk:n - scale coord k so that low bound is n\n\ + QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\ + QbB - scale input to unit cube centered at the origin\n\ + Qbk:0Bk:0 - remove k-th coordinate from input\n\ + QJn - randomly joggle input in range [-n,n]\n\ + QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\ +%s%s%s%s"; /* split up qh_prompt for Visual C++ */ +char qh_promptb[]= "\ + Qs - search all points for the initial simplex\n\ + QGn - good facet if visible from point n, -n for not visible\n\ + QVn - good facet if it includes point n, -n if not\n\ +\n\ +"; +char qh_promptc[]= "\ +Trace options:\n\ + T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\ + Tc - check frequently during execution\n\ + Ts - print statistics\n\ + Tv - verify result: structure, convexity, and point inclusion\n\ + Tz - send all output to stdout\n\ + TFn - report summary when n or more facets created\n\ + TI file - input data from file, no spaces or single quotes\n\ + TO file - output results to file, may be enclosed in single quotes\n\ + TPn - turn on tracing when point n added to hull\n\ + TMn - turn on tracing at merge n\n\ + TWn - trace merge facets when width > n\n\ + TVn - stop qhull after adding point n, -n for before (see TCn)\n\ + TCn - stop qhull after building cone for point n (see TVn)\n\ +\n\ +Precision options:\n\ + Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\ + An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\ + C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\ + Rn - randomly perturb computations by a factor of [1-n,1+n]\n\ + Un - max distance below plane for a new, coplanar point\n\ + Wn - min facet width for outside point (before roundoff)\n\ +\n\ +Output formats (may be combined; if none, produces a summary to stdout):\n\ + f - facet dump\n\ + G - Geomview output (see below)\n\ + i - vertices incident to each facet\n\ + m - Mathematica output (2-d and 3-d)\n\ + n - normals with offsets\n\ + o - OFF file format (dim, points and facets; Voronoi regions)\n\ + p - point coordinates \n\ + s - summary (stderr)\n\ +\n\ +"; +char qh_promptd[]= "\ +More formats:\n\ + Fa - area for each facet\n\ + FA - compute total area and volume for option 's'\n\ + Fc - count plus coplanar points for each facet\n\ + use 'Qc' (default) for coplanar and 'Qi' for interior\n\ + FC - centrum for each facet\n\ + Fd - use cdd format for input (homogeneous with offset first)\n\ + FD - use cdd format for numeric output (offset first)\n\ + FF - facet dump without ridges\n\ + Fi - inner plane for each facet\n\ + FI - ID for each facet\n\ + Fm - merge count for each facet (511 max)\n\ + Fn - count plus neighboring facets for each facet\n\ + FN - count plus neighboring facets for each point\n\ + Fo - outer plane (or max_outside) for each facet\n\ + FO - options and precision constants\n\ + FP - nearest vertex for each coplanar point\n\ + FQ - command used for qconvex\n\ + Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\ + for output: #vertices, #facets,\n\ + #coplanar points, #non-simplicial facets\n\ + #real (2), max outer plane, min vertex\n\ + FS - sizes: #int (0) \n\ + #real(2) tot area, tot volume\n\ + Ft - triangulation with centrums for non-simplicial facets (OFF format)\n\ + Fv - count plus vertices for each facet\n\ + FV - average of vertices (a feasible point for 'H')\n\ + Fx - extreme points (in order for 2-d)\n\ +\n\ +"; +char qh_prompte[]= "\ +Geomview output (2-d, 3-d, and 4-d)\n\ + Ga - all points as dots\n\ + Gp - coplanar points and vertices as radii\n\ + Gv - vertices as spheres\n\ + Gi - inner planes only\n\ + Gn - no planes\n\ + Go - outer planes only\n\ + Gc - centrums\n\ + Gh - hyperplane intersections\n\ + Gr - ridges\n\ + GDn - drop dimension n in 3-d and 4-d output\n\ +\n\ +Print options:\n\ + PAn - keep n largest facets by area\n\ + Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\ + PDk:n - drop facet if normal[k] >= n\n\ + Pg - print good facets (needs 'QGn' or 'QVn')\n\ + PFn - keep facets whose area is at least n\n\ + PG - print neighbors of good facets\n\ + PMn - keep n facets with most merges\n\ + Po - force output. If error, output neighborhood of facet\n\ + Pp - do not report precision problems\n\ +\n\ + . - list of all options\n\ + - - one line descriptions of all options\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt2">-</a> + + qh_prompt2 + synopsis for qhull +*/ +char qh_prompt2[]= "\n\ +qconvex- compute the convex hull. Qhull %s\n\ + input (stdin): dimension, number of points, point coordinates\n\ + comments start with a non-numeric character\n\ +\n\ +options (qconvex.htm):\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ + Tv - verify result: structure, convexity, and point inclusion\n\ + . - concise list of all options\n\ + - - one-line description of all options\n\ +\n\ +output options (subset):\n\ + s - summary of results (default)\n\ + i - vertices incident to each facet\n\ + n - normals with offsets\n\ + p - vertex coordinates (includes coplanar points if 'Qc')\n\ + Fx - extreme points (convex hull vertices)\n\ + FA - compute total area and volume\n\ + o - OFF format (dim, n, points, facets)\n\ + G - Geomview output (2-d, 3-d, and 4-d)\n\ + m - Mathematica output (2-d and 3-d)\n\ + QVn - print facets that include point n, -n if not\n\ + TO file- output results to file, may be enclosed in single quotes\n\ +\n\ +examples:\n\ + rbox c D2 | qconvex s n rbox c D2 | qconvex i\n\ + rbox c D2 | qconvex o rbox 1000 s | qconvex s Tv FA\n\ + rbox c d D2 | qconvex s Qc Fx rbox y 1000 W0 | qconvex s n\n\ + rbox y 1000 W0 | qconvex s QJ rbox d G1 D12 | qconvex QR0 FA Pp\n\ + rbox c D7 | qconvex FA TF1000\n\ +\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt3">-</a> + + qh_prompt3 + concise prompt for qhull +*/ +char qh_prompt3[]= "\n\ +Qhull %s.\n\ +Except for 'F.' and 'PG', upper-case options take an argument.\n\ +\n\ + incidences mathematica normals OFF_format points\n\ + summary facet_dump\n\ +\n\ + Farea FArea_total Fcoplanars FCentrums Fd_cdd_in\n\ + FD_cdd_out FFacet_xridge Finner FIDs Fmerges\n\ + Fneighbors FNeigh_vertex Fouter FOptions FPoint_near\n\ + FQhull Fsummary FSize Fvertices FVertex_ave\n\ + Fxtremes FMaple\n\ +\n\ + Gvertices Gpoints Gall_points Gno_planes Ginner\n\ + Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\ +\n\ + PArea_keep Pdrop d0:0D0 PFacet_area_keep Pgood PGood_neighbors\n\ + PMerge_keep Poutput_forced Pprecision_not\n\ +\n\ + QbBound 0:0.5 QbB_scale_box Qcoplanar QGood_point Qinterior\n\ + QJoggle Qrandom QRotate Qsearch_1st Qtriangulate\n\ + QVertex_good\n\ +\n\ + T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\ + TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\ + TWide_trace TVertex_stop TCone_stop\n\ +\n\ + Angle_max Centrum_size Random_dist Ucoplanar_max Wide_outside\n\ +"; + +/*-<a href="qh-qhull.htm" + >-------------------------------</a><a name="main">-</a> + + main( argc, argv ) + processes the command line, calls qhull() to do the work, and exits + + design: + initializes data structures + reads points + finishes initialization + computes convex hull and other structures + checks the result + writes the output + frees memory +*/ +int main(int argc, char *argv[]) { + int curlong, totlong; /* used !qh_NOmem */ + int exitcode, numpoints, dim; + coordT *points; + boolT ismalloc; + +#if __MWERKS__ && __POWERPC__ + char inBuf[BUFSIZ], outBuf[BUFSIZ], errBuf[BUFSIZ]; + SIOUXSettings.showstatusline= false; + SIOUXSettings.tabspaces= 1; + SIOUXSettings.rows= 40; + if (setvbuf (stdin, inBuf, _IOFBF, sizeof(inBuf)) < 0 /* w/o, SIOUX I/O is slow*/ + || setvbuf (stdout, outBuf, _IOFBF, sizeof(outBuf)) < 0 + || (stdout != stderr && setvbuf (stderr, errBuf, _IOFBF, sizeof(errBuf)) < 0)) + fprintf (stderr, "qhull internal warning (main): could not change stdio to fully buffered.\n"); + argc= ccommand(&argv); +#endif + + if ((argc == 1) && isatty( 0 /*stdin*/)) { + fprintf(stdout, qh_prompt2, qh_version); + exit(qh_ERRnone); + } + if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox, + qh_promptb, qh_promptc, qh_promptd, qh_prompte); + exit(qh_ERRnone); + } + if (argc >1 && *argv[1] == '.' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompt3, qh_version); + exit(qh_ERRnone); + } + qh_init_A (stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */ + exitcode= setjmp (qh errexit); /* simple statement for CRAY J916 */ + if (!exitcode) { + qh_checkflags (qh qhull_command, hidden_options); + qh_initflags (qh qhull_command); + points= qh_readpoints (&numpoints, &dim, &ismalloc); + if (dim >= 5) { + qh_option ("Qxact_merge", NULL, NULL); + qh MERGEexact= True; /* 'Qx' always */ + } + qh_init_B (points, numpoints, dim, ismalloc); + qh_qhull(); + qh_check_output(); + qh_produce_output(); + if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points(); + exitcode= qh_ERRnone; + } + qh NOerrexit= True; /* no more setjmp */ +#ifdef qh_NOmem + qh_freeqhull( True); +#else + qh_freeqhull( False); + qh_memfreeshort (&curlong, &totlong); + if (curlong || totlong) + fprintf (stderr, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); +#endif + return exitcode; +} /* main */ + diff --git a/NifCommon/qhull/qdelaun.c b/NifCommon/qhull/qdelaun.c new file mode 100644 index 0000000..52f7e8c --- /dev/null +++ b/NifCommon/qhull/qdelaun.c @@ -0,0 +1,324 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + qdelaun.c + compute Delaunay triangulations and furthest-point Delaunay + triangulations using qhull + + see unix.c for full interface + + copyright (c) 1993-2003, The Geometry Center +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include "qhull.h" +#include "mem.h" +#include "qset.h" + +#if __MWERKS__ && __POWERPC__ +#include <SIOUX.h> +#include <Files.h> +#include <console.h> +#include <Desk.h> + +#elif __cplusplus +extern "C" { + int isatty (int); +} + +#elif _MSC_VER +#include <io.h> +#define isatty _isatty + +#else +int isatty (int); /* returns 1 if stdin is a tty + if "Undefined symbol" this can be deleted along with call in main() */ +#endif + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt">-</a> + + qh_prompt + long prompt for qhull + + notes: + restricted version of qhull.c + + see: + concise prompt below +*/ + +/* duplicated in qdelau_f.htm and qdelaun.htm */ +char hidden_options[]=" d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Ft Fp FV Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 "; + +char qh_prompta[]= "\n\ +qdelaunay- compute the Delaunay triangulation\n\ + http://www.qhull.org %s\n\ +\n\ +input (stdin):\n\ + first lines: dimension and number of points (or vice-versa).\n\ + other lines: point coordinates, best if one point per line\n\ + comments: start with a non-numeric character\n\ +\n\ +options:\n\ + Qu - compute furthest-site Delaunay triangulation\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ +\n\ +Qhull control options:\n\ + QJn - randomly joggle input in range [-n,n]\n\ +%s%s%s%s"; /* split up qh_prompt for Visual C++ */ +char qh_promptb[]= "\ + Qs - search all points for the initial simplex\n\ + Qz - add point-at-infinity to Delaunay triangulation\n\ + QGn - print Delaunay region if visible from point n, -n if not\n\ + QVn - print Delaunay regions that include point n, -n if not\n\ +\n\ +"; +char qh_promptc[]= "\ +Trace options:\n\ + T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\ + Tc - check frequently during execution\n\ + Ts - print statistics\n\ + Tv - verify result: structure, convexity, and in-circle test\n\ + Tz - send all output to stdout\n\ + TFn - report summary when n or more facets created\n\ + TI file - input data from file, no spaces or single quotes\n\ + TO file - output results to file, may be enclosed in single quotes\n\ + TPn - turn on tracing when point n added to hull\n\ + TMn - turn on tracing at merge n\n\ + TWn - trace merge facets when width > n\n\ + TVn - stop qhull after adding point n, -n for before (see TCn)\n\ + TCn - stop qhull after building cone for point n (see TVn)\n\ +\n\ +Precision options:\n\ + Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\ + An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\ + C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\ + Rn - randomly perturb computations by a factor of [1-n,1+n]\n\ + Wn - min facet width for outside point (before roundoff)\n\ +\n\ +Output formats (may be combined; if none, produces a summary to stdout):\n\ + f - facet dump\n\ + G - Geomview output (see below)\n\ + i - vertices incident to each Delaunay region\n\ + m - Mathematica output (2-d only, lifted to a paraboloid)\n\ + o - OFF format (dim, points, and facets as a paraboloid)\n\ + p - point coordinates (lifted to a paraboloid)\n\ + s - summary (stderr)\n\ +\n\ +"; +char qh_promptd[]= "\ +More formats:\n\ + Fa - area for each Delaunay region\n\ + FA - compute total area for option 's'\n\ + Fc - count plus coincident points for each Delaunay region\n\ + Fd - use cdd format for input (homogeneous with offset first)\n\ + FD - use cdd format for numeric output (offset first)\n\ + FF - facet dump without ridges\n\ + FI - ID of each Delaunay region\n\ + Fm - merge count for each Delaunay region (511 max)\n\ + FM - Maple output (2-d only, lifted to a paraboloid)\n\ + Fn - count plus neighboring region for each Delaunay region\n\ + FN - count plus neighboring region for each point\n\ + FO - options and precision constants\n\ + FP - nearest point and distance for each coincident point\n\ + FQ - command used for qdelaunay\n\ + Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\ + for output: #vertices, #Delaunay regions,\n\ + #coincident points, #non-simplicial regions\n\ + #real (2), max outer plane, min vertex\n\ + FS - sizes: #int (0)\n\ + #real(2) tot area, 0\n\ + Fv - count plus vertices for each Delaunay region\n\ + Fx - extreme points of Delaunay triangulation (on convex hull)\n\ +\n\ +"; +char qh_prompte[]= "\ +Geomview options (2-d and 3-d)\n\ + Ga - all points as dots\n\ + Gp - coplanar points and vertices as radii\n\ + Gv - vertices as spheres\n\ + Gi - inner planes only\n\ + Gn - no planes\n\ + Go - outer planes only\n\ + Gc - centrums\n\ + Gh - hyperplane intersections\n\ + Gr - ridges\n\ + GDn - drop dimension n in 3-d and 4-d output\n\ + Gt - transparent outer ridges to view 3-d Delaunay\n\ +\n\ +Print options:\n\ + PAn - keep n largest Delaunay regions by area\n\ + Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\ + PDk:n - drop facet if normal[k] >= n\n\ + Pg - print good Delaunay regions (needs 'QGn' or 'QVn')\n\ + PFn - keep Delaunay regions whose area is at least n\n\ + PG - print neighbors of good regions (needs 'QGn' or 'QVn')\n\ + PMn - keep n Delaunay regions with most merges\n\ + Po - force output. If error, output neighborhood of facet\n\ + Pp - do not report precision problems\n\ +\n\ + . - list of all options\n\ + - - one line descriptions of all options\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt2">-</a> + + qh_prompt2 + synopsis for qhull +*/ +char qh_prompt2[]= "\n\ +qdelaunay- compute the Delaunay triangulation. Qhull %s\n\ + input (stdin): dimension, number of points, point coordinates\n\ + comments start with a non-numeric character\n\ +\n\ +options (qdelaun.htm):\n\ + Qu - furthest-site Delaunay triangulation\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ + Tv - verify result: structure, convexity, and in-circle test\n\ + . - concise list of all options\n\ + - - one-line description of all options\n\ +\n\ +output options (subset):\n\ + s - summary of results (default)\n\ + i - vertices incident to each Delaunay region\n\ + Fx - extreme points (vertices of the convex hull)\n\ + o - OFF format (shows the points lifted to a paraboloid)\n\ + G - Geomview output (2-d and 3-d points lifted to a paraboloid)\n\ + m - Mathematica output (2-d inputs lifted to a paraboloid)\n\ + QVn - print Delaunay regions that include point n, -n if not\n\ + TO file- output results to file, may be enclosed in single quotes\n\ +\n\ +examples:\n\ + rbox c P0 D2 | qdelaunay s o rbox c P0 D2 | qdelaunay i\n\ + rbox c P0 D2 | qdelaunay Fv rbox c P0 D2 | qdelaunay s Qu Fv\n\ + rbox c G1 d D2 | qdelaunay s i rbox c G1 d D2 | qdelaunay Qt\n\ + rbox M3,4 z 100 D2 | qdelaunay s rbox M3,4 z 100 D2 | qdelaunay s Qt\n\ +\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt3">-</a> + + qh_prompt3 + concise prompt for qhull +*/ +char qh_prompt3[]= "\n\ +Qhull %s.\n\ +Except for 'F.' and 'PG', upper-case options take an argument.\n\ +\n\ + incidences mathematica OFF_format points_lifted summary\n\ + facet_dump\n\ +\n\ + Farea FArea_total Fcoincident Fd_cdd_in FD_cdd_out\n\ + FF_dump_xridge FIDs Fmerges Fneighbors FNeigh_vertex\n\ + FOptions FPoint_near FQdelaun Fsummary FSize\n\ + Fvertices Fxtremes FMaple\n\ +\n\ + Gvertices Gpoints Gall_points Gno_planes Ginner\n\ + Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\ + Gtransparent\n\ +\n\ + PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\ + PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\ +\n\ + QGood_point QJoggle Qsearch_1st Qtriangulate QupperDelaunay\n\ + QVertex_good Qzinfinite\n\ +\n\ + T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\ + TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\ + TWide_trace TVertex_stop TCone_stop\n\ +\n\ + Angle_max Centrum_size Random_dist Wide_outside\n\ +"; + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="main">-</a> + + main( argc, argv ) + processes the command line, calls qhull() to do the work, and exits + + design: + initializes data structures + reads points + finishes initialization + computes convex hull and other structures + checks the result + writes the output + frees memory +*/ +int main(int argc, char *argv[]) { + int curlong, totlong; /* used !qh_NOmem */ + int exitcode, numpoints, dim; + coordT *points; + boolT ismalloc; + +#if __MWERKS__ && __POWERPC__ + char inBuf[BUFSIZ], outBuf[BUFSIZ], errBuf[BUFSIZ]; + SIOUXSettings.showstatusline= false; + SIOUXSettings.tabspaces= 1; + SIOUXSettings.rows= 40; + if (setvbuf (stdin, inBuf, _IOFBF, sizeof(inBuf)) < 0 /* w/o, SIOUX I/O is slow*/ + || setvbuf (stdout, outBuf, _IOFBF, sizeof(outBuf)) < 0 + || (stdout != stderr && setvbuf (stderr, errBuf, _IOFBF, sizeof(errBuf)) < 0)) + fprintf (stderr, "qhull internal warning (main): could not change stdio to fully buffered.\n"); + argc= ccommand(&argv); +#endif + + if ((argc == 1) && isatty( 0 /*stdin*/)) { + fprintf(stdout, qh_prompt2, qh_version); + exit(qh_ERRnone); + } + if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompta, qh_version, + qh_promptb, qh_promptc, qh_promptd, qh_prompte); + exit(qh_ERRnone); + } + if (argc >1 && *argv[1] == '.' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompt3, qh_version); + exit(qh_ERRnone); + } + qh_init_A (stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */ + exitcode= setjmp (qh errexit); /* simple statement for CRAY J916 */ + if (!exitcode) { + qh_option ("delaunay Qbbound-last", NULL, NULL); + qh DELAUNAY= True; /* 'd' */ + qh SCALElast= True; /* 'Qbb' */ + qh KEEPcoplanar= True; /* 'Qc', to keep coplanars in 'p' */ + qh_checkflags (qh qhull_command, hidden_options); + qh_initflags (qh qhull_command); + points= qh_readpoints (&numpoints, &dim, &ismalloc); + if (dim >= 5) { + qh_option ("Qxact_merge", NULL, NULL); + qh MERGEexact= True; /* 'Qx' always */ + } + qh_init_B (points, numpoints, dim, ismalloc); + qh_qhull(); + qh_check_output(); + qh_produce_output(); + if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points(); + exitcode= qh_ERRnone; + } + qh NOerrexit= True; /* no more setjmp */ +#ifdef qh_NOmem + qh_freeqhull( True); +#else + qh_freeqhull( False); + qh_memfreeshort (&curlong, &totlong); + if (curlong || totlong) + fprintf (stderr, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); +#endif + return exitcode; +} /* main */ + diff --git a/NifCommon/qhull/qhalf.c b/NifCommon/qhull/qhalf.c new file mode 100644 index 0000000..4464d54 --- /dev/null +++ b/NifCommon/qhull/qhalf.c @@ -0,0 +1,325 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + qhalf.c + compute the intersection of halfspaces about a point + + see unix.c for full interface + + copyright (c) 1993-2003, The Geometry Center +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include "qhull.h" +#include "mem.h" +#include "qset.h" + +#if __MWERKS__ && __POWERPC__ +#include <SIOUX.h> +#include <Files.h> +#include <console.h> +#include <Desk.h> + +#elif __cplusplus +extern "C" { + int isatty (int); +} + +#elif _MSC_VER +#include <io.h> +#define isatty _isatty + +#else +int isatty (int); /* returns 1 if stdin is a tty + if "Undefined symbol" this can be deleted along with call in main() */ +#endif + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt">-</a> + + qh_prompt + long prompt for qhull + + notes: + restricted version of qhull.c + + see: + concise prompt below +*/ + +/* duplicated in qhalf.htm */ +char hidden_options[]=" d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 "; + +char qh_prompta[]= "\n\ +qhalf- compute the intersection of halfspaces about a point\n\ + http://www.qhull.org %s\n\ +\n\ +input (stdin):\n\ + optional interior point: dimension, 1, coordinates\n\ + first lines: dimension+1 and number of halfspaces\n\ + other lines: halfspace coefficients followed by offset\n\ + comments: start with a non-numeric character\n\ +\n\ +options:\n\ + Hn,n - specify coordinates of interior point\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ + Qc - keep coplanar halfspaces\n\ + Qi - keep other redundant halfspaces\n\ +\n\ +Qhull control options:\n\ + QJn - randomly joggle input in range [-n,n]\n\ +%s%s%s%s"; /* split up qh_prompt for Visual C++ */ +char qh_promptb[]= "\ + Qbk:0Bk:0 - remove k-th coordinate from input\n\ + Qs - search all halfspaces for the initial simplex\n\ + QGn - print intersection if visible to halfspace n, -n for not\n\ + QVn - print intersections for halfspace n, -n if not\n\ +\n\ +"; +char qh_promptc[]= "\ +Trace options:\n\ + T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\ + Tc - check frequently during execution\n\ + Ts - print statistics\n\ + Tv - verify result: structure, convexity, and redundancy\n\ + Tz - send all output to stdout\n\ + TFn - report summary when n or more facets created\n\ + TI file - input data from file, no spaces or single quotes\n\ + TO file - output results to file, may be enclosed in single quotes\n\ + TPn - turn on tracing when halfspace n added to intersection\n\ + TMn - turn on tracing at merge n\n\ + TWn - trace merge facets when width > n\n\ + TVn - stop qhull after adding halfspace n, -n for before (see TCn)\n\ + TCn - stop qhull after building cone for halfspace n (see TVn)\n\ +\n\ +Precision options:\n\ + Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\ + An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\ + C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\ + Rn - randomly perturb computations by a factor of [1-n,1+n]\n\ + Un - max distance below plane for a new, coplanar halfspace\n\ + Wn - min facet width for outside halfspace (before roundoff)\n\ +\n\ +Output formats (may be combined; if none, produces a summary to stdout):\n\ + f - facet dump\n\ + G - Geomview output (dual convex hull)\n\ + i - non-redundant halfspaces incident to each intersection\n\ + m - Mathematica output (dual convex hull)\n\ + o - OFF format (dual convex hull: dimension, points, and facets)\n\ + p - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')\n\ + s - summary (stderr)\n\ +\n\ +"; +char qh_promptd[]= "\ +More formats:\n\ + Fc - count plus redundant halfspaces for each intersection\n\ + - Qc (default) for coplanar and Qi for other redundant\n\ + Fd - use cdd format for input (homogeneous with offset first)\n\ + FF - facet dump without ridges\n\ + FI - ID of each intersection\n\ + Fm - merge count for each intersection (511 max)\n\ + FM - Maple output (dual convex hull)\n\ + Fn - count plus neighboring intersections for each intersection\n\ + FN - count plus intersections for each non-redundant halfspace\n\ + FO - options and precision constants\n\ + Fp - dim, count, and intersection coordinates\n\ + FP - nearest halfspace and distance for each redundant halfspace\n\ + FQ - command used for qhalf\n\ + Fs - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections\n\ + for output: #non-redundant, #intersections, #coplanar\n\ + halfspaces, #non-simplicial intersections\n\ + #real (2), max outer plane, min vertex\n\ + Fv - count plus non-redundant halfspaces for each intersection\n\ + Fx - non-redundant halfspaces\n\ +\n\ +"; +char qh_prompte[]= "\ +Geomview output (2-d, 3-d and 4-d; dual convex hull)\n\ + Ga - all points (i.e., transformed halfspaces) as dots\n\ + Gp - coplanar points and vertices as radii\n\ + Gv - vertices (i.e., non-redundant halfspaces) as spheres\n\ + Gi - inner planes (i.e., halfspace intersections) only\n\ + Gn - no planes\n\ + Go - outer planes only\n\ + Gc - centrums\n\ + Gh - hyperplane intersections\n\ + Gr - ridges\n\ + GDn - drop dimension n in 3-d and 4-d output\n\ +\n\ +Print options:\n\ + PAn - keep n largest facets (i.e., intersections) by area\n\ + Pdk:n- drop facet if normal[k] <= n (default 0.0)\n\ + PDk:n- drop facet if normal[k] >= n\n\ + Pg - print good facets (needs 'QGn' or 'QVn')\n\ + PFn - keep facets whose area is at least n\n\ + PG - print neighbors of good facets\n\ + PMn - keep n facets with most merges\n\ + Po - force output. If error, output neighborhood of facet\n\ + Pp - do not report precision problems\n\ +\n\ + . - list of all options\n\ + - - one line descriptions of all options\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt2">-</a> + + qh_prompt2 + synopsis for qhull +*/ +char qh_prompt2[]= "\n\ +qhalf- halfspace intersection about a point. Qhull %s\n\ + input (stdin): [dim, 1, interior point], dim+1, n, coefficients+offset\n\ + comments start with a non-numeric character\n\ +\n\ +options (qhalf.htm):\n\ + Hn,n - specify coordinates of interior point\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ + Tv - verify result: structure, convexity, and redundancy\n\ + . - concise list of all options\n\ + - - one-line description of all options\n\ +\n\ +output options (subset):\n\ + s - summary of results (default)\n\ + Fp - intersection coordinates\n\ + Fv - non-redundant halfspaces incident to each intersection\n\ + Fx - non-redundant halfspaces\n\ + o - OFF file format (dual convex hull)\n\ + G - Geomview output (dual convex hull)\n\ + m - Mathematica output (dual convex hull)\n\ + QVn - print intersections for halfspace n, -n if not\n\ + TO file - output results to file, may be enclosed in single quotes\n\ +\n\ +examples:\n\ + rbox d | qconvex FQ n | qhalf s H0,0,0 Fp\n\ + rbox c | qconvex FQ FV n | qhalf s i\n\ + rbox c | qconvex FQ FV n | qhalf s o\n\ +\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt3">-</a> + + qh_prompt3 + concise prompt for qhull +*/ +char qh_prompt3[]= "\n\ +Qhull %s.\n\ +Except for 'F.' and 'PG', upper_case options take an argument.\n\ +\n\ + incidences Geomview mathematica OFF_format point_dual\n\ + summary facet_dump\n\ +\n\ + Fc_redundant Fd_cdd_in FF_dump_xridge FIDs Fmerges\n\ + Fneighbors FN_intersect FOptions Fp_coordinates FP_nearest\n\ + FQhalf Fsummary Fv_halfspace FMaple Fx_non_redundant\n\ +\n\ + Gvertices Gpoints Gall_points Gno_planes Ginner\n\ + Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\ +\n\ + PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\ + PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\ +\n\ + Qbk:0Bk:0_drop Qcoplanar QG_half_good Qi_redundant QJoggle\n\ + Qsearch_1st Qtriangulate QVertex_good\n\ +\n\ + T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\ + TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\ + TWide_trace TVertex_stop TCone_stop\n\ +\n\ + Angle_max Centrum_size Random_dist Ucoplanar_max Wide_outside\n\ +"; + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="main">-</a> + + main( argc, argv ) + processes the command line, calls qhull() to do the work, and exits + + design: + initializes data structures + reads points + finishes initialization + computes convex hull and other structures + checks the result + writes the output + frees memory +*/ +int main(int argc, char *argv[]) { + int curlong, totlong; /* used !qh_NOmem */ + int exitcode, numpoints, dim; + coordT *points; + boolT ismalloc; + +#if __MWERKS__ && __POWERPC__ + char inBuf[BUFSIZ], outBuf[BUFSIZ], errBuf[BUFSIZ]; + SIOUXSettings.showstatusline= false; + SIOUXSettings.tabspaces= 1; + SIOUXSettings.rows= 40; + if (setvbuf (stdin, inBuf, _IOFBF, sizeof(inBuf)) < 0 /* w/o, SIOUX I/O is slow*/ + || setvbuf (stdout, outBuf, _IOFBF, sizeof(outBuf)) < 0 + || (stdout != stderr && setvbuf (stderr, errBuf, _IOFBF, sizeof(errBuf)) < 0)) + fprintf (stderr, "qhull internal warning (main): could not change stdio to fully buffered.\n"); + argc= ccommand(&argv); +#endif + + if ((argc == 1) && isatty( 0 /*stdin*/)) { + fprintf(stdout, qh_prompt2, qh_version); + exit(qh_ERRnone); + } + if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompta, qh_version, + qh_promptb, qh_promptc, qh_promptd, qh_prompte); + exit(qh_ERRnone); + } + if (argc >1 && *argv[1] == '.' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompt3, qh_version); + exit(qh_ERRnone); + } + qh_init_A (stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */ + exitcode= setjmp (qh errexit); /* simple statement for CRAY J916 */ + if (!exitcode) { + qh_option ("Halfspace", NULL, NULL); + qh HALFspace= True; /* 'H' */ + qh_checkflags (qh qhull_command, hidden_options); + qh_initflags (qh qhull_command); + if (qh SCALEinput) { + fprintf(qh ferr, "\ +qhull error: options 'Qbk:n' and 'QBk:n' are not used with qhalf.\n\ + Use 'Qbk:0Bk:0 to drop dimension k.\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + points= qh_readpoints (&numpoints, &dim, &ismalloc); + if (dim >= 5) { + qh_option ("Qxact_merge", NULL, NULL); + qh MERGEexact= True; /* 'Qx' always */ + } + qh_init_B (points, numpoints, dim, ismalloc); + qh_qhull(); + qh_check_output(); + qh_produce_output(); + if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points(); + exitcode= qh_ERRnone; + } + qh NOerrexit= True; /* no more setjmp */ +#ifdef qh_NOmem + qh_freeqhull( True); +#else + qh_freeqhull( False); + qh_memfreeshort (&curlong, &totlong); + if (curlong || totlong) + fprintf (stderr, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); +#endif + return exitcode; +} /* main */ + diff --git a/NifCommon/qhull/qhull.c b/NifCommon/qhull/qhull.c new file mode 100644 index 0000000..65c509a --- /dev/null +++ b/NifCommon/qhull/qhull.c @@ -0,0 +1,1396 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + qhull.c + Quickhull algorithm for convex hulls + + qhull() and top-level routines + + see qh-qhull.htm, qhull.h, unix.c + + see qhull_a.h for internal functions + + copyright (c) 1993-2003 The Geometry Center +*/ + +#include "qhull_a.h" + +/*============= functions in alphabetic order after qhull() =======*/ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="qhull">-</a> + + 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, "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); + fprintf (qh ferr, "\nTesting all coplanar points.\n"); + } + qh_check_maxout(); + } + if (qh KEEPnearinside && !qh maxoutdone) + qh_nearcoplanar(); + } + if (qh_setsize ((setT*)qhmem.tempstack) != 0) { + fprintf (qh ferr, "qhull internal error (qh_qhull): temporary sets not empty (%d)\n", + qh_setsize ((setT*)qhmem.tempstack)); + qh_errexit (qh_ERRqhull, NULL, NULL); + } + qh hulltime= qh_CPUclock - qh hulltime; + qh QHULLfinished= True; + trace1((qh ferr, "qh_qhull: algorithm completed\n")); +} /* qhull */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="addpoint">-</a> + + 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: + check point in qh.first_point/.num_points + 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) { + fprintf (qh ferr, "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, "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 */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="build_withrestart">-</a> + + 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); + qh ERREXITcalled= False; + qh STOPcone= True; /* if break, prevents normal output */ + } + if (!qh RERUN && qh JOGGLEmax < REALmax/2) { + if (qh build_cnt > qh_JOGGLEmaxretry) { + fprintf(qh ferr, "\n\ +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= False; + qh_freebuild (True); /* first call is a nop */ + qh build_cnt++; + if (!qh qhull_optionsiz) + qh qhull_optionsiz= strlen (qh qhull_options); + else { + qh qhull_options [qh qhull_optionsiz]= '\0'; + qh qhull_optionlen= 80; + } + 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 */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="buildhull">-</a> + + 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, "qh_buildhull: start build hull\n")); + FORALLfacets { + if (facet->visible || facet->newfacet) { + fprintf (qh ferr, "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) { + fprintf (qh ferr, "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,"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) { + fprintf (qh ferr, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh num_outside); + qh_errexit (qh_ERRqhull, NULL, NULL); + } + trace1((qh ferr, "qh_buildhull: completed the hull construction\n")); +} /* buildhull */ + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="buildtracing">-</a> + + 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= qh_CPUclock - qh hulltime; + cpu /= qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + fprintf (qh ferr, "\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= qh_CPUclock - qh hulltime; + cpu /= qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + zinc_(Zdistio); + qh_distplane (furthest, facet, &dist); + fprintf (qh ferr, "\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= qh_CPUclock - qh hulltime; + cpu /= qh_SECticks; + qh_distplane (furthest, facet, &dist); + fprintf (qh ferr, "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); + } + if (qh visit_id > (unsigned) INT_MAX) { + qh visit_id= 0; + FORALLfacets + facet->visitid= qh visit_id; + } + if (qh vertex_visit > (unsigned) INT_MAX) { + qh vertex_visit= 0; + FORALLvertices + vertex->visitid= qh vertex_visit; + } + qh furthest_id= furthestid; + qh RANDOMdist= qh old_randomdist; +} /* buildtracing */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="errexit2">-</a> + + 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 */ + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="findhorizon">-</a> + + 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,"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) { + fprintf (qh ferr, "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, "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"); + fprintf(qh ferr, "qhull precision error (qh_findhorizon): empty horizon\n\ +Point 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, "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 */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="nextfurthest">-</a> + + 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, index; + 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); + index= (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 > index) { + *visible= facet; + return ((pointT*)qh_setdelnth (facet->outsideset, index)); + }else + index -= size; + } + } + fprintf (qh ferr, "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, index+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 */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="partitionall">-</a> + + qh_partitionall( vertices, points, numpoints ) + partitions all points in points/numpoints to the outsidesets of facets + vertices= vertices in qh.facet_list (not 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, "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 */ + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="partitioncoplanar">-</a> + + 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, 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, "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, "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, "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) { + fprintf (qh ferr, "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, "qh_partitioncoplanar: point p%d is coplanar with facet f%d (or inside) dist %2.2g\n", + qh_pointid(point), bestfacet->id, bestdist)); +} /* partitioncoplanar */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="partitionpoint">-</a> + + 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, "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, "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, "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 */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="partitionvisible">-</a> + + 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 (not 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) { + fprintf (qh ferr, "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,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar)); +} /* partitionvisible */ + + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="precision">-</a> + + qh_precision( reason ) + restart on precision errors if not merging and if 'QJn' +*/ +void qh_precision (char *reason) { + + if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) { + if (qh JOGGLEmax < REALmax/2) { + trace0((qh ferr, "qh_precision: qhull restart because of %s\n", reason)); + longjmp(qh restartexit, qh_ERRprec); + } + } +} /* qh_precision */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="printsummary">-</a> + + 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; + 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) + fprintf (fp, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error."); + 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) + fprintf (fp, "\n\ +Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + else + fprintf (fp, "\n\ +Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + fprintf(fp, " Number of Voronoi regions%s: %d\n", + qh ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + fprintf(fp, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + fprintf(fp, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + fprintf(fp, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + fprintf(fp, " Number of%s Voronoi vertices: %d\n", + goodused ? " 'good'" : "", qh num_good); + if (nonsimplicial) + fprintf(fp, " Number of%s non-simplicial Voronoi vertices: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh DELAUNAY) { + if (qh UPPERdelaunay) + fprintf (fp, "\n\ +Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + else + fprintf (fp, "\n\ +Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + fprintf(fp, " Number of input sites%s: %d\n", + qh ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + fprintf(fp, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + fprintf(fp, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + fprintf(fp, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + fprintf(fp, " Number of%s Delaunay regions: %d\n", + goodused ? " 'good'" : "", qh num_good); + if (nonsimplicial) + fprintf(fp, " Number of%s non-simplicial Delaunay regions: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh HALFspace) { + fprintf (fp, "\n\ +Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + fprintf(fp, " Number of halfspaces: %d\n", size); + fprintf(fp, " 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"; + fprintf(fp, " Number of %s halfspaces: %d\n", s, numcoplanars); + } + fprintf(fp, " Number of intersection points: %d\n", qh num_facets - qh num_visible); + if (goodused) + fprintf(fp, " Number of 'good' intersection points: %d\n", qh num_good); + if (nonsimplicial) + fprintf(fp, " Number of%s non-simplicial intersection points: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else { + fprintf (fp, "\n\ +Convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + fprintf(fp, " 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"; + fprintf(fp, " Number of %s points: %d\n", s, numcoplanars); + } + fprintf(fp, " Number of facets: %d\n", qh num_facets - qh num_visible); + if (goodused) + fprintf(fp, " Number of 'good' facets: %d\n", qh num_good); + if (nonsimplicial) + fprintf(fp, " Number of%s non-simplicial facets: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + } + if (numtricoplanars) + fprintf(fp, " Number of triangulated facets: %d\n", numtricoplanars); + fprintf(fp, "\nStatistics for: %s | %s", + qh rbox_command, qh qhull_command); + if (qh ROTATErandom != INT_MIN) + fprintf(fp, " QR%d\n\n", qh ROTATErandom); + else + fprintf(fp, "\n\n"); + fprintf(fp, " Number of points processed: %d\n", zzval_(Zprocessed)); + fprintf(fp, " Number of hyperplanes created: %d\n", zzval_(Zsetplane)); + if (qh DELAUNAY) + fprintf(fp, " Number of facets in hull: %d\n", qh num_facets - qh num_visible); + fprintf(fp, " 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; + fprintf(fp, " average new facet balance: %2.2g\n", + wval_(Wnewbalance)/zval_(Zprocessed)); + stddev= qh_stddev (zval_(Zprocessed), wval_(Wnewbalance), + wval_(Wnewbalance2), &ave); + fprintf(fp, " new facet standard deviation: %2.2g\n", stddev); + fprintf(fp, " average partition balance: %2.2g\n", + wval_(Wpbalance)/zval_(Zpbalance)); + stddev= qh_stddev (zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + fprintf(fp, " partition standard deviation: %2.2g\n", stddev); + } +#endif + if (nummerged) { + fprintf(fp," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+ + zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+ + zzval_(Zdistzero)); + fprintf(fp," Number of distance tests for checking: %d\n",zzval_(Zcheckpart)); + fprintf(fp," Number of merged facets: %d\n", nummerged); + } + if (!qh RANDOMoutside && qh QHULLfinished) { + cpu= qh hulltime; + cpu /= qh_SECticks; + wval_(Wcpu)= cpu; + fprintf (fp, " CPU seconds to compute hull (after input): %2.4g\n", cpu); + } + if (qh RERUN) { + if (!qh PREmerge && !qh MERGEexact) + fprintf(fp, " 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)) + fprintf(fp, " After %d retries, input joggled by: %2.2g\n", + zzval_(Zretry), qh JOGGLEmax); + else + fprintf(fp, " Input joggled by: %2.2g\n", qh JOGGLEmax); + } + if (qh totarea != 0.0) + fprintf(fp, " %s facet area: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh totarea); + if (qh totvol != 0.0) + fprintf(fp, " %s volume: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh totvol); + if (qh MERGING) { + qh_outerinner (NULL, &outerplane, &innerplane); + if (outerplane > 2 * qh DISTround) { + fprintf(fp, " 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) + fprintf (fp, " (%.1fx)\n", ratio); + else + fprintf (fp, "\n"); + } + if (innerplane < -2 * qh DISTround) { + fprintf(fp, " 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) + fprintf (fp, " (%.1fx)\n", ratio); + else + fprintf (fp, "\n"); + } + } + fprintf(fp, "\n"); +} /* printsummary */ + + diff --git a/NifCommon/qhull/qhull.h b/NifCommon/qhull/qhull.h new file mode 100644 index 0000000..95afe0f --- /dev/null +++ b/NifCommon/qhull/qhull.h @@ -0,0 +1,1030 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + qhull.h + user-level header file for using qhull.a library + + see qh-qhull.htm, qhull_a.h + + copyright (c) 1993-2003, The Geometry Center + + 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 qhull.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 qhull.h + + recompile qhull if you change this file +*/ + +#ifndef qhDEFqhull +#define qhDEFqhull 1 + +/*=========================== -included files ==============*/ + +#include <setjmp.h> +#include <float.h> +#include <time.h> + +#if __MWERKS__ && __POWERPC__ +#include <SIOUX.h> +#include <Files.h> +#include <Desk.h> +#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 qhull.h +#endif +#endif +#endif + +#include "user.h" /* user defineable constants */ + +/*============ constants and basic types ====================*/ + +extern char *qh_version; /* defined in global.c */ + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="coordT">-</a> + + coordT + coordinates and coefficients are stored as realT (i.e., double) + + notes: + 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 + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="pointT">-</a> + + pointT + a point is an array of DIM3 coordinates +*/ +#define pointT coordT + +/*-<a href="qh-qhull.htm#TOC" + >--------------------------------</a><a name="flagT">-</a> + + flagT + Boolean flag as a bit +*/ +#define flagT unsigned int + +/*-<a href="qh-qhull.htm#TOC" + >--------------------------------</a><a name="boolT">-</a> + + boolT + boolean value, either True or False + + notes: + needed for portability +*/ +#define boolT unsigned int +#ifdef False +#undef False +#endif +#ifdef True +#undef True +#endif +#define False 0 +#define True 1 + +/*-<a href="qh-qhull.htm#TOC" + >--------------------------------</a><a name="CENTERtype">-</a> + + qh_CENTER + to distinguish facet->center +*/ +typedef enum +{ + qh_ASnone = 0, qh_ASvoronoi, qh_AScentrum +} +qh_CENTER; + +/*-<a href="qh-qhull.htm#TOC" + >--------------------------------</a><a name="qh_PRINT">-</a> + + 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; + +/*-<a href="qh-qhull.htm#TOC" + >--------------------------------</a><a name="qh_ALL">-</a> + + 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 */ + +/*-<a href="qh-qhull.htm#TOC" + >--------------------------------</a><a name="qh_ERR">-</a> + + qh_ERR + Qhull exit codes, for indicating errors +*/ +#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 */ + +/* ============ -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 + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="facetT">-</a> + + 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 defines ridge */ + 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) */ +}; + + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="ridgeT">-</a> + + ridgeT + defines a ridge + + notes: + a ridge is DIM3-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 */ + 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 */ +}; + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="vertexT">-</a> + + 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; /* for use with qh vertex_visit */ + unsigned id:24; /* unique identifier, =>room for 8 flags */ + flagT seen:1; /* used to perform operations only once */ + flagT seen2:1; /* another seen flag */ + 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 */ +}; + +/*======= -global variables -qh ============================*/ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh">-</a> + + qh + all global variables for qhull are in qh, qhmem, and qhstat + + notes: + qhmem is defined in mem.h and qhstat is defined in stat.h + Access to qh_qh is via the "qh" macro. See qh_QHpointer in user.h +*/ +typedef struct qhT qhT; +#if qh_QHpointer +#define qh qh_qh-> +extern qhT *qh_qh; /* allocated in global.c */ +#else +#define qh qh_qh. +extern qhT qh_qh; +#endif + +struct qhT { + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh-const">-</a> + + 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 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 (not 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 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 */ + 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 before qh_initbuild */ + 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 */ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh-prec">-</a> + + 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 */ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh-intern">-</a> + + qh internal constants + internal constants for Qhull +*/ + char qhull[sizeof("qhull")]; /* for checking ownership */ + void *old_stat; /* pointer to saved qh_qhstat, qh_save_qhull */ + 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) */ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh-lists">-</a> + + 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; /* ID of next, new ridge from newridge() */ + unsigned vertex_id; /* ID of next, new vertex from newvertex() */ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh-var">-</a> + + 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 */ + 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 */ + realT totvol; /* 'FA': total volume computed by qh_getarea */ + unsigned int visit_id; /* unique ID for searching neighborhoods, */ + unsigned int vertex_visit; /* unique ID for searching vertices */ + boolT ZEROall_ok; /* True if qh_checkzero always succeeds */ + boolT WAScoplanar; /* True if qh_partitioncoplanar (qh_check_maxout) */ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh-set">-</a> + + 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 (first is qh interior_point) */ + setT *del_vertices; /* vertices to partition and delete with visible + facets. Have deleted set for checkfacet */ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh-buf">-</a> + + 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 */ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh-static">-</a> + + 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 errexit (prevents duplicate calls */ + boolT firstcentrum; /* for qh_printcentrum */ + 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 */ + boolT old_randomdist; /* save RANDOMdist when io, tracing, or statistics */ + int ridgeoutnum; /* number of ridges in 4OFF output */ + void *old_qhstat; /* for saving qh_qhstat in save_qhull() */ + setT *old_tempstack; /* for saving qhmem.tempstack in save_qhull */ + setT *coplanarset; /* set of coplanar facets for searching qh_findbesthorizon() */ +}; + +/*=========== -macros- =========================*/ + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="otherfacet_">-</a> + + otherfacet_(ridge, facet) + return neighboring facet for a ridge in facet +*/ +#define otherfacet_(ridge, facet) \ + (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="getid_">-</a> + + getid_(p) + return ID for facet, ridge, or vertex + return MAXINT if NULL (-1 causes type conversion error ) +*/ +#define getid_(p) ((p) ? (p)->id : -1) + +/*============== FORALL macros ===================*/ + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLfacets">-</a> + + 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) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLpoints">-</a> + + 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) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLpoint_">-</a> + + 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) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLvertices">-</a> + + 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) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHfacet_">-</a> + + FOREACHfacet_( facets ) { ... } + assign 'facet' to each facet in facets + + declare: + facetT *facet, **facetp; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHneighbor_">-</a> + + 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: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHneighbor_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighbor) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHpoint_">-</a> + + FOREACHpoint_( points ) { ... } + assign 'point' to each point in points set + + declare: + pointT *point, **pointp; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHpoint_(points) FOREACHsetelement_(pointT, points, point) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHridge_">-</a> + + FOREACHridge_( ridges ) { ... } + assign 'ridge' to each ridge in ridges set + + declare: + ridgeT *ridge, **ridgep; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHridge_(ridges) FOREACHsetelement_(ridgeT, ridges, ridge) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHvertex_">-</a> + + FOREACHvertex_( vertices ) { ... } + assign 'vertex' to each vertex in vertices set + + declare: + vertexT *vertex, **vertexp; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHfacet_i_">-</a> + + FOREACHfacet_i_( facets ) { ... } + assign 'facet' and 'facet_i' for each facet in facets set + + declare: + facetT *facet; + int facet_n, facet_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHfacet_i_(facets) FOREACHsetelement_i_(facetT, facets, facet) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHneighbor_i_">-</a> + + 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: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHneighbor_i_(facet) FOREACHsetelement_i_(facetT, facet->neighbors, neighbor) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHpoint_i_">-</a> + + FOREACHpoint_i_( points ) { ... } + assign 'point' and 'point_i' for each point in points set + + declare: + pointT *point; + int point_n, point_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHpoint_i_(points) FOREACHsetelement_i_(pointT, points, point) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHridge_i_">-</a> + + FOREACHridge_i_( ridges ) { ... } + assign 'ridge' and 'ridge_i' for each ridge in ridges set + + declare: + ridgeT *ridge; + int ridge_n, ridge_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHridge_i_(ridges) FOREACHsetelement_i_(ridgeT, ridges, ridge) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHvertex_i_">-</a> + + FOREACHvertex_i_( vertices ) { ... } + assign 'vertex' and 'vertex_i' for each vertex in vertices set + + declare: + vertexT *vertex; + int vertex_n, vertex_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> + */ +#define FOREACHvertex_i_(vertices) FOREACHsetelement_i_(vertexT, vertices,vertex) + +/********* -qhull.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(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_user_memsizes (void); + +/***** -geom.c/geom2.c prototypes (duplicated from geom.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_freebuffers (void); +void qh_freeqhull (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_start (FILE *infile, FILE *outfile, FILE *errfile); +void qh_initthresholds (char *command); +void qh_option (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, int 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*/); + +/********* -stat.c prototypes (duplicated from stat.h) **********************/ + +void qh_collectstatistics (void); +void qh_printallstatistics (FILE *fp, char *string); + +#endif /* qhDEFqhull */ diff --git a/NifCommon/qhull/qhull_a.h b/NifCommon/qhull/qhull_a.h new file mode 100644 index 0000000..746db6b --- /dev/null +++ b/NifCommon/qhull/qhull_a.h @@ -0,0 +1,127 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + qhull_a.h + all header files for compiling qhull + + see qh-qhull.htm + + see qhull.h for user-level definitions + + see user.h for user-defineable constants + + defines internal functions for qhull.c global.c + + copyright (c) 1993-2003, The Geometry Center + + 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 +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#include <string.h> +#include <math.h> +#include <float.h> /* some compilers will not need float.h */ +#include <limits.h> +#include <time.h> +#include <ctype.h> +/*** uncomment here and qset.c + if string.h does not define memcpy() +#include <memory.h> +*/ +#include "qhull.h" +#include "mem.h" +#include "qset.h" +#include "geom.h" +#include "merge.h" +#include "poly.h" +#include "io.h" +#include "stat.h" + +#if qh_CLOCKtype == 2 /* defined in user.h from qhull.h */ +#include <sys/types.h> +#include <sys/times.h> +#include <unistd.h> +#endif + +#ifdef _MSC_VER /* Microsoft Visual C++ */ +#pragma warning( disable : 4056) /* float constant expression. Looks like a compiler bug */ +#pragma warning( disable : 4146) /* unary minus applied to unsigned type */ +#pragma warning( disable : 4244) /* conversion from 'unsigned long' to 'real' */ +#pragma warning( disable : 4305) /* conversion from 'const double' to 'float' */ +#endif + +/* ======= -macros- =========== */ + +/*-<a href="qh-qhull.htm#TOC" + >--------------------------------</a><a name="traceN">-</a> + + traceN((fp.ferr, "format\n", vars)); + calls fprintf if qh.IStracing >= N + + notes: + removing tracing reduces code size but doesn't change execution speed +*/ +#ifndef qh_NOtrace +#define trace0(args) {if (qh IStracing) fprintf args;} +#define trace1(args) {if (qh IStracing >= 1) fprintf args;} +#define trace2(args) {if (qh IStracing >= 2) fprintf args;} +#define trace3(args) {if (qh IStracing >= 3) fprintf args;} +#define trace4(args) {if (qh IStracing >= 4) fprintf args;} +#define trace5(args) {if (qh IStracing >= 5) 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 */ + +/***** -qhull.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 (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); +int qh_strtol (const char *s, char **endp); +double qh_strtod (const char *s, char **endp); + +/***** -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); + +#define qsqrt(x) sqrt(double(x)) + +#endif /* qhDEFqhulla */ diff --git a/NifCommon/qhull/qhull_interface.cpp b/NifCommon/qhull/qhull_interface.cpp new file mode 100644 index 0000000..5072737 --- /dev/null +++ b/NifCommon/qhull/qhull_interface.cpp @@ -0,0 +1,96 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> +*/ + +#include <iostream.h> +#include <conio.h> + +//--- Include qhull, so it works from with in a C++ source file +//--- +//--- In MVC one cannot just do: +//--- +//--- extern "C" +//--- { +//--- #include "qhull_a.h" +//--- } +//--- +//--- Because qhull_a.h includes math.h, which can not appear +//--- inside a extern "C" declaration. +//--- +//--- Maybe that why Numerical recipes in C avoid this problem, by removing +//--- standard include headers from its header files and add them in the +//--- respective source files instead. +//--- +//--- [K. Erleben] + +#if defined(__cplusplus) +extern "C" +{ +#endif +#include <stdio.h> +#include <stdlib.h> +#include <qhull/qhull.h> +#include <qhull/mem.h> +#include <qhull/qset.h> +#include <qhull/geom.h> +#include <qhull/merge.h> +#include <qhull/poly.h> +#include <qhull/io.h> +#include <qhull/stat.h> +#if defined(__cplusplus) +} +#endif + +/*********************************************************************/ +/* */ +/* */ +/* */ +/* */ +/*********************************************************************/ + +void compute_convex_hull(void) +{ + 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 */ + + /* 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) + fprintf (errfile, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); +}; + +/*********************************************************************/ +/* */ +/* */ +/* */ +/* */ +/*********************************************************************/ + +void main() +{ + cout << "Hello world" << endl; + + cout << "Press any key..." << endl; + + while(!_kbhit()); + +}; diff --git a/NifCommon/qhull/qset.c b/NifCommon/qhull/qset.c new file mode 100644 index 0000000..e615077 --- /dev/null +++ b/NifCommon/qhull/qset.c @@ -0,0 +1,1301 @@ +/*<html><pre> -<a href="qh-set.htm" + >-------------------------------</a><a name="TOP">-</a> + + qset.c + implements set manipulations needed for quickhull + + see qh-set.htm and qset.h + + copyright (c) 1993-2003 The Geometry Center +*/ + +#include <stdio.h> +#include <string.h> +/*** uncomment here and qhull_a.h + if string.h does not define memcpy() +#include <memory.h> +*/ +#include "qset.h" +#include "mem.h" + +#ifndef qhDEFqhull +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +void qh_errexit(int exitcode, facetT *, ridgeT *); +#endif + +/*=============== internal macros ===========================*/ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="SETsizeaddr_">-</a> + + SETsizeaddr_(set) + return pointer to actual size+1 of set (set CANNOT be NULL!!) + + notes: + *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL +*/ +#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize].i)) + +/*============ functions in alphabetical order ===================*/ + +/*-<a href="qh-set.htm#TOC" + >--------------------------------<a name="setaddnth">-</a> + + 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 *sizep, oldsize, i; + void **oldp, **newp; + + if (!*setp || !*(sizep= SETsizeaddr_(*setp))) { + qh_setlarger(setp); + sizep= SETsizeaddr_(*setp); + } + oldsize= *sizep - 1; + if (nth < 0 || nth > oldsize) { + fprintf (qhmem.ferr, "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)++; + oldp= SETelemaddr_(*setp, oldsize, void); /* NULL */ + newp= oldp+1; + for (i= oldsize-nth+1; i--; ) /* move at least NULL */ + *(newp--)= *(oldp--); /* may overwrite *sizep */ + *newp= newelem; +} /* setaddnth */ + + +/*-<a href="qh-set.htm#TOC" + >--------------------------------<a name="setaddsorted">-</a> + + 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setappend">-</a> + + 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) { + int *sizep; + void **endp; + + if (!newelem) + return; + if (!*setp || !*(sizep= SETsizeaddr_(*setp))) { + qh_setlarger(setp); + sizep= SETsizeaddr_(*setp); + } + *(endp= &((*setp)->e[(*sizep)++ - 1].p))= newelem; + *(++endp)= NULL; +} /* setappend */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setappend_set">-</a> + + 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 *sizep, sizeA, size; + setT *oldset; + + if (!setA) + return; + SETreturnsize_(setA, sizeA); + if (!*setp) + *setp= qh_setnew (sizeA); + sizep= SETsizeaddr_(*setp); + if (!(size= *sizep)) + size= (*setp)->maxsize; + else + size--; + if (size + sizeA > (*setp)->maxsize) { + oldset= *setp; + *setp= qh_setcopy (oldset, sizeA); + qh_setfree (&oldset); + sizep= SETsizeaddr_(*setp); + } + *sizep= size+sizeA+1; /* memcpy may overwrite */ + if (sizeA > 0) + memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), SETelemsize *(sizeA+1)); +} /* setappend_set */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setappend2ndlast">-</a> + + 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) { + int *sizep; + void **endp, **lastp; + + if (!*setp || !*(sizep= SETsizeaddr_(*setp))) { + qh_setlarger(setp); + sizep= SETsizeaddr_(*setp); + } + endp= SETelemaddr_(*setp, (*sizep)++ -1, void); /* NULL */ + lastp= endp-1; + *(endp++)= *lastp; + *endp= NULL; /* may overwrite *sizep */ + *lastp= newelem; +} /* setappend2ndlast */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setcheck">-</a> + + 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, char *tname, int id) { + int maxsize, size; + int waserr= 0; + + if (!set) + return; + SETreturnsize_(set, size); + maxsize= set->maxsize; + if (size > maxsize || !maxsize) { + fprintf (qhmem.ferr, "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) { + fprintf (qhmem.ferr, "qhull internal error (qh_setcheck): %s%d (size %d max %d) is not null terminated.\n", + tname, id, maxsize, size-1); + waserr= 1; + } + if (waserr) { + qh_setprint (qhmem.ferr, "ERRONEOUS", set); + qh_errexit (qhmem_ERRqhull, NULL, NULL); + } +} /* setcheck */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setcompact">-</a> + + 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, destp-firstp); +} /* setcompact */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setcopy">-</a> + + 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)= size+1; /* memcpy may overwrite */ + memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), SETelemsize *(size+1)); + return (newset); +} /* setcopy */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setdel">-</a> + + 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) { + void **elemp, **lastp; + int *sizep; + + if (!set) + return NULL; + elemp= SETaddr_(set, void); + while (*elemp != oldelem && *elemp) + elemp++; + if (*elemp) { + sizep= SETsizeaddr_(set); + if (!(*sizep)--) /* if was a full set */ + *sizep= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + lastp= SETelemaddr_(set, *sizep-1, void); + *elemp= *lastp; /* may overwrite itself */ + *lastp= NULL; + return oldelem; + } + return NULL; +} /* setdel */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setdellast">-</a> + + 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; + int *sizep; + void *returnvalue; + + if (!set || !(set->e[0].p)) + return NULL; + sizep= SETsizeaddr_(set); + if ((setsize= *sizep)) { + returnvalue= set->e[setsize - 2].p; + set->e[setsize - 2].p= NULL; + (*sizep)--; + }else { + maxsize= set->maxsize; + returnvalue= set->e[maxsize - 1].p; + set->e[maxsize - 1].p= NULL; + *sizep= maxsize; + } + return returnvalue; +} /* setdellast */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setdelnth">-</a> + + 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 **elemp, **lastp, *elem; + int *sizep; + + + elemp= SETelemaddr_(set, nth, void); + sizep= SETsizeaddr_(set); + if (!(*sizep)--) /* if was a full set */ + *sizep= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + if (nth < 0 || nth >= *sizep) { + fprintf (qhmem.ferr, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint (qhmem.ferr, "", set); + qh_errexit (qhmem_ERRqhull, NULL, NULL); + } + lastp= SETelemaddr_(set, *sizep-1, void); + elem= *elemp; + *elemp= *lastp; /* may overwrite itself */ + *lastp= NULL; + return elem; +} /* setdelnth */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setdelnthsorted">-</a> + + 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 **newp, **oldp, *elem; + int *sizep; + + sizep= SETsizeaddr_(set); + if (nth < 0 || (*sizep && nth >= *sizep-1) || nth >= set->maxsize) { + fprintf (qhmem.ferr, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint (qhmem.ferr, "", set); + qh_errexit (qhmem_ERRqhull, NULL, NULL); + } + newp= SETelemaddr_(set, nth, void); + elem= *newp; + oldp= newp+1; + while ((*(newp++)= *(oldp++))) + ; /* copy remaining elements and NULL */ + if (!(*sizep)--) /* if was a full set */ + *sizep= set->maxsize; /* *sizep= (max size-1)+ 1 */ + return elem; +} /* setdelnthsorted */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setdelsorted">-</a> + + 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) { + void **newp, **oldp; + int *sizep; + + if (!set) + return NULL; + newp= SETaddr_(set, void); + while(*newp != oldelem && *newp) + newp++; + if (*newp) { + oldp= newp+1; + while ((*(newp++)= *(oldp++))) + ; /* copy remaining elements */ + sizep= SETsizeaddr_(set); + if (!(*sizep)--) /* if was a full set */ + *sizep= set->maxsize; /* *sizep= (max size-1)+ 1 */ + return oldelem; + } + return NULL; +} /* setdelsorted */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setduplicate">-</a> + + 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, elemsize); + qh_setappend (&newSet, newElem); + } + return newSet; +} /* setduplicate */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setequal">-</a> + + 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, sizeB; + + SETreturnsize_(setA, sizeA); + 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setequal_except">-</a> + + 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setequal_skip">-</a> + + 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setfree">-</a> + + 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setfree2">-</a> + + 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 */ + + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setfreelong">-</a> + + 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setin">-</a> + + 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setindex">-</a> + + 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. + + design: + checks maxsize + scans set for atelem +*/ +int qh_setindex(setT *set, void *atelem) { + void **elem; + int size, i; + + 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setlarger">-</a> + + 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, *sizep; + setT *newset, *set, **setp, *oldset; + void **oldp, **newp; + + if (*oldsetp) { + oldset= *oldsetp; + SETreturnsize_(oldset, size); + qhmem.cntlarger++; + qhmem.totlarger += size+1; + newset= qh_setnew(2 * size); + oldp= SETaddr_(oldset, void); + newp= SETaddr_(newset, void); + memcpy((char *)newp, (char *)oldp, (size+1) * SETelemsize); + sizep= SETsizeaddr_(newset); + *sizep= size+1; + FOREACHset_((setT *)qhmem.tempstack) { + if (set == oldset) + *(setp-1)= newset; + } + qh_setfree(oldsetp); + }else + newset= qh_setnew(3); + *oldsetp= newset; +} /* setlarger */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setlast">-</a> + + 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); + if (!size) + return SETelem_(set, set->maxsize - 1); + else if (size > 1) + return SETelem_(set, size - 2); + } + return NULL; +} /* setlast */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setnew">-</a> + + qh_setnew( setsize ) + creates and allocates space for a set + + notes: + setsize means the number of elements (NOT 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 ((unsigned) size <= (unsigned) 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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setnew_delnthsorted">-</a> + + 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) { + fprintf (qhmem.ferr, "qhull internal error (qh_setaddnth): 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, 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, tailsize * SETelemsize); + newp += tailsize; + } + *newp= NULL; + return(newset); +} /* setnew_delnthsorted */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setprint">-</a> + + qh_setprint( fp, string, set ) + print set elements to fp with identifying string + + notes: + never errors +*/ +void qh_setprint(FILE *fp, char* string, setT *set) { + int size, k; + + if (!set) + fprintf (fp, "%s set is null\n", string); + else { + SETreturnsize_(set, size); + fprintf (fp, "%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++) + fprintf(fp, " %p", set->e[k].p); + fprintf(fp, "\n"); + } +} /* setprint */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setreplace">-</a> + + 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 { + fprintf (qhmem.ferr, "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 */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setsize">-</a> + + qh_setsize( set ) + returns the size of a set + + notes: + errors if set's maxsize is incorrect + same as SETreturnsize_(set) + + design: + determine actual size of set from maxsize +*/ +int qh_setsize(setT *set) { + int size, *sizep; + + if (!set) + return (0); + sizep= SETsizeaddr_(set); + if ((size= *sizep)) { + size--; + if (size > set->maxsize) { + fprintf (qhmem.ferr, "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 */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settemp">-</a> + + 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 ((setT **)&qhmem.tempstack, newset); + if (qhmem.IStracing >= 5) + fprintf (qhmem.ferr, "qh_settemp: temp set %p of %d elements, depth %d\n", + newset, newset->maxsize, qh_setsize ((setT*)qhmem.tempstack)); + return newset; +} /* settemp */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settempfree">-</a> + + 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); + fprintf (qhmem.ferr, "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((setT*)qhmem.tempstack)+1, + stackedset, qh_setsize(stackedset)); + qh_errexit (qhmem_ERRqhull, NULL, NULL); + } + qh_setfree (set); +} /* settempfree */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settempfree_all">-</a> + + 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_((setT *)qhmem.tempstack) + qh_setfree(&set); + qh_setfree((setT **)&qhmem.tempstack); +} /* settempfree_all */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settemppop">-</a> + + 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((setT *)qhmem.tempstack); + if (!stackedset) { + fprintf (qhmem.ferr, "qhull internal error (qh_settemppop): pop from empty temporary stack\n"); + qh_errexit (qhmem_ERRqhull, NULL, NULL); + } + if (qhmem.IStracing >= 5) + fprintf (qhmem.ferr, "qh_settemppop: depth %d temp set %p of %d elements\n", + qh_setsize((setT*)qhmem.tempstack)+1, stackedset, qh_setsize(stackedset)); + return stackedset; +} /* settemppop */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settemppush">-</a> + + 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) { + + qh_setappend ((setT**)&qhmem.tempstack, set); + if (qhmem.IStracing >= 5) + fprintf (qhmem.ferr, "qh_settemppush: depth %d temp set %p of %d elements\n", + qh_setsize((setT*)qhmem.tempstack), set, qh_setsize(set)); +} /* settemppush */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settruncate">-</a> + + 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) { + fprintf (qhmem.ferr, "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 */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setunique">-</a> + + 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 */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setzero">-</a> + + 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 index, int size) { + int count; + + if (index < 0 || index >= size || size > set->maxsize) { + fprintf (qhmem.ferr, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", index, size); + qh_setprint (qhmem.ferr, "", set); + qh_errexit (qhmem_ERRqhull, NULL, NULL); + } + set->e[set->maxsize].i= size+1; /* may be overwritten */ + count= size - index + 1; /* +1 for NULL terminator */ + memset ((char *)SETelemaddr_(set, index, void), 0, count * SETelemsize); +} /* setzero */ + + diff --git a/NifCommon/qhull/qset.h b/NifCommon/qhull/qset.h new file mode 100644 index 0000000..7a0f577 --- /dev/null +++ b/NifCommon/qhull/qset.h @@ -0,0 +1,468 @@ +/*<html><pre> -<a href="qh-set.htm" + >-------------------------------</a><a name="TOP">-</a> + + 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-2003, The Geometry Center +*/ + +#ifndef qhDEFset +#define qhDEFset 1 + +/*================= -structures- ===============*/ + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* a set is a sorted or unsorted array of pointers */ +#endif + +/*-<a href="qh-set.htm#TOC" +>----------------------------------------</a><a name="setT">-</a> + +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- =========================*/ + +/*-<a href="qh-set.htm#TOC" + >-----------------------------------</a><a name="SETelemsize">-</a> + + SETelemsize + size of a set element in bytes +*/ +#define SETelemsize sizeof(setelemT) + + +/*=========== -macros- =========================*/ + +/*-<a href="qh-set.htm#TOC" + >-----------------------------------</a><a name="FOREACHsetelement_">-</a> + + 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++);) + +/*-<a href="qh-set.htm#TOC" + >----------------------------------------</a><a name="FOREACHsetelement_i_">-</a> + + 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) ) + +/*-<a href="qh-set.htm#TOC" + >--------------------------------------</a><a name="FOREACHsetelementreverse_">-</a> + + 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)) + +/*-<a href="qh-set.htm#TOC" + >-----------------------------------</a><a name="FOREACHsetelementreverse12_">-</a> + + 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++)) + +/*-<a href="qh-set.htm#TOC" + >-----------------------------------</a><a name="FOREACHelem_">-</a> + + 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) + +/*-<a href="qh-set.htm#TOC" + >-----------------------------------</a><a name="FOREACHset_">-</a> + + 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) + +/*-<a href="qh-set.htm#TOC" + >-----------------------------------------</a><a name="SETindex_">-</a> + + SETindex_( set, elem ) + return index of elem in set + + notes: + for use with FOREACH iteration + + example: + i= SETindex_(ridges, ridge) +*/ +#define SETindex_(set, elem) ((void **)elem##p - (void **)&(set)->e[1].p) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETref_">-</a> + + SETref_( elem ) + l.h.s. for modifying the current element in a FOREACH iteration + + example: + SETref_(ridge)= anotherridge; +*/ +#define SETref_(elem) (elem##p[-1]) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETelem_">-</a> + + 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) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETelemt_">-</a> + + 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)) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETelemaddr_">-</a> + + 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))) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETfirst_">-</a> + + SETfirst_(set) + return first element of set + +*/ +#define SETfirst_(set) ((set)->e[0].p) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETfirstt_">-</a> + + SETfirstt_(set, type) + return first element of set as a type + +*/ +#define SETfirstt_(set, type) ((type*)((set)->e[0].p)) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETsecond_">-</a> + + SETsecond_(set) + return second element of set + +*/ +#define SETsecond_(set) ((set)->e[1].p) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETsecondt_">-</a> + + SETsecondt_(set, type) + return second element of set as a type +*/ +#define SETsecondt_(set, type) ((type*)((set)->e[1].p)) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETaddr_">-</a> + + SETaddr_(set, type) + return address of set's elements +*/ +#define SETaddr_(set,type) ((type **)(&((set)->e[0].p))) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETreturnsize_">-</a> + + 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)) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETempty_">-</a> + + SETempty_(set) + return true (1) if set is empty + + notes: + set may be NULL +*/ +#define SETempty_(set) (!set || (SETfirst_(set) ? 0:1)) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETtruncate_">-</a> + + SETtruncate_(set) + return first element of set + + 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, char *tname, int 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_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, 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 index, int size); + + +#endif /* qhDEFset */ diff --git a/NifCommon/qhull/qvoronoi.c b/NifCommon/qhull/qvoronoi.c new file mode 100644 index 0000000..8070787 --- /dev/null +++ b/NifCommon/qhull/qvoronoi.c @@ -0,0 +1,318 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + qvoronoi.c + compute Voronoi diagrams and furthest-point Voronoi + diagrams using qhull + + see unix.c for full interface + + copyright (c) 1993-2003, The Geometry Center +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include "qhull.h" +#include "mem.h" +#include "qset.h" + +#if __MWERKS__ && __POWERPC__ +#include <SIOUX.h> +#include <Files.h> +#include <console.h> +#include <Desk.h> + +#elif __cplusplus +extern "C" { + int isatty (int); +} + +#elif _MSC_VER +#include <io.h> +#define isatty _isatty + +#else +int isatty (int); /* returns 1 if stdin is a tty + if "Undefined symbol" this can be deleted along with call in main() */ +#endif + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt">-</a> + + qh_prompt + long prompt for qhull + + notes: + restricted version of qhull.c + + see: + concise prompt below +*/ + +/* duplicated in qvoron_f.htm and qvoronoi.htm */ +char hidden_options[]=" d n m v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Pv Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 "; + +char qh_prompta[]= "\n\ +qvoronoi- compute the Voronoi diagram\n\ + http://www.qhull.org %s\n\ +\n\ +input (stdin):\n\ + first lines: dimension and number of points (or vice-versa).\n\ + other lines: point coordinates, best if one point per line\n\ + comments: start with a non-numeric character\n\ +\n\ +options:\n\ + Qu - compute furthest-site Voronoi diagram\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ +\n\ +Qhull control options:\n\ + Qz - add point-at-infinity to Voronoi diagram\n\ + QJn - randomly joggle input in range [-n,n]\n\ +%s%s%s%s"; /* split up qh_prompt for Visual C++ */ +char qh_promptb[]= "\ + Qs - search all points for the initial simplex\n\ + QGn - Voronoi vertices if visible from point n, -n if not\n\ + QVn - Voronoi vertices for input point n, -n if not\n\ +\n\ +"; +char qh_promptc[]= "\ +Trace options:\n\ + T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\ + Tc - check frequently during execution\n\ + Ts - statistics\n\ + Tv - verify result: structure, convexity, and in-circle test\n\ + Tz - send all output to stdout\n\ + TFn - report summary when n or more facets created\n\ + TI file - input data from file, no spaces or single quotes\n\ + TO file - output results to file, may be enclosed in single quotes\n\ + TPn - turn on tracing when point n added to hull\n\ + TMn - turn on tracing at merge n\n\ + TWn - trace merge facets when width > n\n\ + TVn - stop qhull after adding point n, -n for before (see TCn)\n\ + TCn - stop qhull after building cone for point n (see TVn)\n\ +\n\ +Precision options:\n\ + Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\ + An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\ + C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\ + Rn - randomly perturb computations by a factor of [1-n,1+n]\n\ + Wn - min facet width for non-coincident point (before roundoff)\n\ +\n\ +Output formats (may be combined; if none, produces a summary to stdout):\n\ + s - summary to stderr\n\ + p - Voronoi vertices\n\ + o - OFF format (dim, Voronoi vertices, and Voronoi regions)\n\ + i - Delaunay regions (use 'Pp' to avoid warning)\n\ + f - facet dump\n\ +\n\ +"; +char qh_promptd[]= "\ +More formats:\n\ + Fc - count plus coincident points (by Voronoi vertex)\n\ + Fd - use cdd format for input (homogeneous with offset first)\n\ + FD - use cdd format for output (offset first)\n\ + FF - facet dump without ridges\n\ + Fi - separating hyperplanes for bounded Voronoi regions\n\ + FI - ID for each Voronoi vertex\n\ + Fm - merge count for each Voronoi vertex (511 max)\n\ + Fn - count plus neighboring Voronoi vertices for each Voronoi vertex\n\ + FN - count and Voronoi vertices for each Voronoi region\n\ + Fo - separating hyperplanes for unbounded Voronoi regions\n\ + FO - options and precision constants\n\ + FP - nearest point and distance for each coincident point\n\ + FQ - command used for qvoronoi\n\ + Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\ + for output: #Voronoi regions, #Voronoi vertices,\n\ + #coincident points, #non-simplicial regions\n\ + #real (2), max outer plane and min vertex\n\ + Fv - Voronoi diagram as Voronoi vertices between adjacent input sites\n\ + Fx - extreme points of Delaunay triangulation (on convex hull)\n\ +\n\ +"; +char qh_prompte[]= "\ +Geomview options (2-d only)\n\ + Ga - all points as dots\n\ + Gp - coplanar points and vertices as radii\n\ + Gv - vertices as spheres\n\ + Gi - inner planes only\n\ + Gn - no planes\n\ + Go - outer planes only\n\ + Gc - centrums\n\ + Gh - hyperplane intersections\n\ + Gr - ridges\n\ + GDn - drop dimension n in 3-d and 4-d output\n\ +\n\ +Print options:\n\ + PAn - keep n largest Voronoi vertices by 'area'\n\ + Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\ + PDk:n - drop facet if normal[k] >= n\n\ + Pg - print good Voronoi vertices (needs 'QGn' or 'QVn')\n\ + PFn - keep Voronoi vertices whose 'area' is at least n\n\ + PG - print neighbors of good Voronoi vertices\n\ + PMn - keep n Voronoi vertices with most merges\n\ + Po - force output. If error, output neighborhood of facet\n\ + Pp - do not report precision problems\n\ +\n\ + . - list of all options\n\ + - - one line descriptions of all options\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt2">-</a> + + qh_prompt2 + synopsis for qhull +*/ +char qh_prompt2[]= "\n\ +qvoronoi- compute the Voronoi diagram. Qhull %s\n\ + input (stdin): dimension, number of points, point coordinates\n\ + comments start with a non-numeric character\n\ +\n\ +options (qvoronoi.htm):\n\ + Qu - compute furthest-site Voronoi diagram\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ + Tv - verify result: structure, convexity, and in-circle test\n\ + . - concise list of all options\n\ + - - one-line description of all options\n\ +\n\ +output options (subset):\n\ + s - summary of results (default)\n\ + p - Voronoi vertices\n\ + o - OFF file format (dim, Voronoi vertices, and Voronoi regions)\n\ + FN - count and Voronoi vertices for each Voronoi region\n\ + Fv - Voronoi diagram as Voronoi vertices between adjacent input sites\n\ + Fi - separating hyperplanes for bounded regions, 'Fo' for unbounded\n\ + G - Geomview output (2-d only)\n\ + QVn - Voronoi vertices for input point n, -n if not\n\ + TO file- output results to file, may be enclosed in single quotes\n\ +\n\ +examples:\n\ +rbox c P0 D2 | qvoronoi s o rbox c P0 D2 | qvoronoi Fi\n\ +rbox c P0 D2 | qvoronoi Fo rbox c P0 D2 | qvoronoi Fv\n\ +rbox c P0 D2 | qvoronoi s Qu Fv rbox c P0 D2 | qvoronoi Qu Fo\n\ +rbox c G1 d D2 | qvoronoi s p rbox c G1 d D2 | qvoronoi QJ s p\n\ +rbox c P0 D2 | qvoronoi s Fv QV0\n\ +\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt3">-</a> + + qh_prompt3 + concise prompt for qhull +*/ +char qh_prompt3[]= "\n\ +Qhull %s.\n\ +Except for 'F.' and 'PG', upper-case options take an argument.\n\ +\n\ + OFF_format p_vertices i_delaunay summary facet_dump\n\ +\n\ + Fcoincident Fd_cdd_in FD_cdd_out FF-dump-xridge Fi_bounded\n\ + Fxtremes Fmerges Fneighbors FNeigh_region FOptions\n\ + Fo_unbounded FPoint_near FQvoronoi Fsummary Fvoronoi\n\ + FIDs\n\ +\n\ + Gvertices Gpoints Gall_points Gno_planes Ginner\n\ + Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\ +\n\ + PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\ + PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\ +\n\ + QG_vertex_good QJoggle Qsearch_1st Qtriangulate Qupper_voronoi\n\ + QV_point_good Qzinfinite\n\ +\n\ + T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\ + TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\ + TWide_trace TVertex_stop TCone_stop\n\ +\n\ + Angle_max Centrum_size Random_dist Wide_outside\n\ +"; + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="main">-</a> + + main( argc, argv ) + processes the command line, calls qhull() to do the work, and exits + + design: + initializes data structures + reads points + finishes initialization + computes convex hull and other structures + checks the result + writes the output + frees memory +*/ +int main(int argc, char *argv[]) { + int curlong, totlong; /* used !qh_NOmem */ + int exitcode, numpoints, dim; + coordT *points; + boolT ismalloc; + +#if __MWERKS__ && __POWERPC__ + char inBuf[BUFSIZ], outBuf[BUFSIZ], errBuf[BUFSIZ]; + SIOUXSettings.showstatusline= false; + SIOUXSettings.tabspaces= 1; + SIOUXSettings.rows= 40; + if (setvbuf (stdin, inBuf, _IOFBF, sizeof(inBuf)) < 0 /* w/o, SIOUX I/O is slow*/ + || setvbuf (stdout, outBuf, _IOFBF, sizeof(outBuf)) < 0 + || (stdout != stderr && setvbuf (stderr, errBuf, _IOFBF, sizeof(errBuf)) < 0)) + fprintf (stderr, "qhull internal warning (main): could not change stdio to fully buffered.\n"); + argc= ccommand(&argv); +#endif + + if ((argc == 1) && isatty( 0 /*stdin*/)) { + fprintf(stdout, qh_prompt2, qh_version); + exit(qh_ERRnone); + } + if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompta, qh_version, + qh_promptb, qh_promptc, qh_promptd, qh_prompte); + exit(qh_ERRnone); + } + if (argc >1 && *argv[1] == '.' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompt3, qh_version); + exit(qh_ERRnone); + } + qh_init_A (stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */ + exitcode= setjmp (qh errexit); /* simple statement for CRAY J916 */ + if (!exitcode) { + qh_option ("voronoi _bbound-last _coplanar-keep", NULL, NULL); + qh DELAUNAY= True; /* 'v' */ + qh VORONOI= True; + qh SCALElast= True; /* 'Qbb' */ + qh_checkflags (qh qhull_command, hidden_options); + qh_initflags (qh qhull_command); + points= qh_readpoints (&numpoints, &dim, &ismalloc); + if (dim >= 5) { + qh_option ("_merge-exact", NULL, NULL); + qh MERGEexact= True; /* 'Qx' always */ + } + qh_init_B (points, numpoints, dim, ismalloc); + qh_qhull(); + qh_check_output(); + qh_produce_output(); + if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points(); + exitcode= qh_ERRnone; + } + qh NOerrexit= True; /* no more setjmp */ +#ifdef qh_NOmem + qh_freeqhull( True); +#else + qh_freeqhull( False); + qh_memfreeshort (&curlong, &totlong); + if (curlong || totlong) + fprintf (stderr, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); +#endif + return exitcode; +} /* main */ + diff --git a/NifCommon/qhull/rbox.c b/NifCommon/qhull/rbox.c new file mode 100644 index 0000000..b8a3116 --- /dev/null +++ b/NifCommon/qhull/rbox.c @@ -0,0 +1,788 @@ +/*<html><pre> -<a href="index.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + rbox.c + Generate input points for qhull. + + notes: + 50 points generated for 'rbox D4' + + This code needs a full rewrite. It needs separate procedures for each + distribution with common, helper procedures. + + WARNING: + incorrect range if qh_RANDOMmax is defined wrong (user.h) +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <limits.h> +#include <time.h> + +#include "user.h" +#if __MWERKS__ && __POWERPC__ +#include <SIOUX.h> +#include <Files.h> +#include <console.h> +#include <Desk.h> +#endif + +#ifdef _MSC_VER /* Microsoft Visual C++ */ +#pragma warning( disable : 4244) /* conversion from double to int */ +#endif + +#define MINVALUE 0.8 +#define MAXdim 200 +#define PI 3.1415926535897932384 +#define DEFAULTzbox 1e6 + +char prompt[]= "\n\ +-rbox- generate various point distributions. Default is random in cube.\n\ +\n\ +args (any order, space separated): Version: 2001/06/24\n\ + 3000 number of random points in cube, lens, spiral, sphere or grid\n\ + D3 dimension 3-d\n\ + c add a unit cube to the output ('c G2.0' sets size)\n\ + d add a unit diamond to the output ('d G2.0' sets size)\n\ + l generate a regular 3-d spiral\n\ + r generate a regular polygon, ('r s Z1 G0.1' makes a cone)\n\ + s generate cospherical points\n\ + x generate random points in simplex, may use 'r' or 'Wn'\n\ + y same as 'x', plus simplex\n\ + Pn,m,r add point [n,m,r] first, pads with 0\n\ +\n\ + Ln lens distribution of radius n. Also 's', 'r', 'G', 'W'.\n\ + Mn,m,r lattice (Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...\n\ + '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}. Try 'M3,4 z'.\n\ + W0.1 random distribution within 0.1 of the cube's or sphere's surface\n\ + Z0.5 s random points in a 0.5 disk projected to a sphere\n\ + Z0.5 s G0.6 same as Z0.5 within a 0.6 gap\n\ +\n\ + Bn bounding box coordinates, default %2.2g\n\ + h output as homogeneous coordinates for cdd\n\ + n remove command line from the first line of output\n\ + On offset coordinates by n\n\ + t use time as the random number seed (default is command line)\n\ + tn use n as the random number seed\n\ + z print integer coordinates, default 'Bn' is %2.2g\n\ +"; + +/* ------------------------------ prototypes ----------------*/ +int roundi( double a); +void out1( double a); +void out2n( double a, double b); +void out3n( double a, double b, double c); +int qh_rand( void); +void qh_srand( int seed); + + +/* ------------------------------ globals -------------------*/ + + FILE *fp; + int isinteger= 0; + double out_offset= 0.0; + + +/*-------------------------------------------- +-rbox- main procedure of rbox application +*/ +int main(int argc, char **argv) { + 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, 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, *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], *s, seedbuf[200]; + time_t timedata; + +#if __MWERKS__ && __POWERPC__ + char inBuf[BUFSIZ], outBuf[BUFSIZ], errBuf[BUFSIZ]; + SIOUXSettings.showstatusline= False; + SIOUXSettings.tabspaces= 1; + SIOUXSettings.rows= 40; + if (setvbuf (stdin, inBuf, _IOFBF, sizeof(inBuf)) < 0 /* w/o, SIOUX I/O is slow*/ + || setvbuf (stdout, outBuf, _IOFBF, sizeof(outBuf)) < 0 + || (stdout != stderr && setvbuf (stderr, errBuf, _IOFBF, sizeof(errBuf)) < 0)) + fprintf ( stderr, "qhull internal warning (main): could not change stdio to fully buffered.\n"); + argc= ccommand(&argv); +#endif + if (argc == 1) { + printf (prompt, box, DEFAULTzbox); + exit(1); + } + if ((s = strrchr( argv[0], '\\'))) /* Borland gives full path */ + strcpy (command, s+1); + else + strcpy (command, argv[0]); + if ((s= strstr (command, ".EXE")) + || (s= strstr (command, ".exe"))) + *s= '\0'; + /* ============= read flags =============== */ + for (i=1; i < argc; i++) { + if (strlen (command) + strlen(argv[i]) + 1 < sizeof(command) ) { + strcat (command, " "); + strcat (command, argv[i]); + } + if (isdigit (argv[i][0])) { + numpoints= atoi (argv[i]); + continue; + } + if (argv[i][0] == '-') + (argv[i])++; + switch (argv[i][0]) { + case 'c': + addcube= 1; + if (i+1 < argc && argv[i+1][0] == 'G') + cube= (double) atof (&argv[++i][1]); + break; + case 'd': + adddiamond= 1; + if (i+1 < argc && argv[i+1][0] == 'G') + diamond= (double) atof (&argv[++i][1]); + 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 (argv[i][1])) + seed= atoi (&argv[i][1]); + else { + seed= time (&timedata); + sprintf (seedbuf, "%d", seed); + strcat (command, seedbuf); + } + break; + case 'x': + issimplex= 1; + break; + case 'y': + issimplex2= 1; + break; + case 'z': + isinteger= 1; + break; + case 'B': + box= (double) atof (&argv[i][1]); + isbox= 1; + break; + case 'D': + dim= atoi (&argv[i][1]); + if (dim < 1 + || dim > MAXdim) { + fprintf (stderr, "rbox error: dim %d too large or too small\n", dim); + exit (1); + } + break; + case 'G': + if (argv[i][1]) + gap= (double) atof (&argv[i][1]); + else + gap= 0.5; + isgap= 1; + break; + case 'L': + if (argv[i][1]) + radius= (double) atof (&argv[i][1]); + else + radius= 10; + islens= 1; + break; + case 'M': + ismesh= 1; + s= argv[i]+1; + if (*s) + meshn= strtod (s, &s); + if (*s == ',') + meshm= strtod (++s, &s); + else + meshm= 0.0; + if (*s == ',') + meshr= strtod (++s, &s); + else + meshr= sqrt (meshn*meshn + meshm*meshm); + if (*s) { + fprintf (stderr, "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': + out_offset= (double) atof (&argv[i][1]); + break; + case 'P': + addpoints++; + break; + case 'W': + width= (double) atof (&argv[i][1]); + iswidth= 1; + break; + case 'Z': + if (argv[i][1]) + radius= (double) atof (&argv[i][1]); + else + radius= 1.0; + isaxis= 1; + break; + default: + fprintf (stderr, "rbox warning: unknown flag %s.\nExecute 'rbox' without arguments for documentation.\n", argv[i]); + } + } + /* ============= defaults, constants, and sizes =============== */ + if (isinteger && !isbox) + box= DEFAULTzbox; + if (addcube) { + cubesize= 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) { + fprintf (stderr, "rbox error: can not combine 'Ln' with 'Zn'\n"); + exit(1); + } + if (radius <= 1.0) { + fprintf (stderr, "rbox error: lens radius %.2g should be greater than 1.0\n", + radius); + exit(1); + } + lensangle= asin (1.0/radius); + lensbase= radius * cos (lensangle); + } + if (!numpoints) { + if (issimplex2) + ; /* ok */ + else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) { + fprintf (stderr, "rbox error: missing count\n"); + exit(1); + }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)) { + fprintf (stderr, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n"); + exit(1); + } + fp= stdout; + /* ============= 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; + if (iscdd) + fprintf(fp, "%s\nbegin\n %d %d %s\n", + NOcommand ? "" : command, + totpoints, dim+1, + isinteger ? "integer" : "real"); + else if (NOcommand) + fprintf(fp, "%d\n%d\n", dim, totpoints); + else + fprintf(fp, "%d %s\n%d\n", dim, command, totpoints); + /* ============= 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, seed explicitly set to n or to time */ + qh_RANDOMseed_(seed); + /* ============= explicit points =============== */ + for (i=1; i < argc; i++) { + if (argv[i][0] == 'P') { + s= argv[i]+1; + count= 0; + if (iscdd) + out1( 1.0); + while (*s) { + out1( strtod (s, &s)); + count++; + if (*s) { + if (*s++ != ',') { + fprintf (stderr, "rbox error: missing comma after coordinate in %s\n\n", argv[i]); + exit (1); + } + } + } + if (count < dim) { + for (k= dim-count; k--; ) + out1( 0.0); + }else if (count > dim) { + fprintf (stderr, "rbox error: %d coordinates instead of %d coordinates in %s\n\n", + count, dim, argv[i]); + exit (1); + } + fprintf (fp, "\n"); + } + } + /* ============= simplex distribution =============== */ + if (issimplex+issimplex2) { + if (!(simplex= malloc( dim * (dim+1) * sizeof(double)))) { + fprintf (stderr, "insufficient memory for simplex\n"); + exit(0); + } + simplexp= simplex; + if (isregular) { + for (i= 0; i<dim; i++) { + for (k= 0; k<dim; k++) + *(simplexp++)= i==k ? 1.0 : 0.0; + } + for (k= 0; k<dim; k++) + *(simplexp++)= -1.0; + }else { + for (i= 0; i<dim+1; i++) { + for (k= 0; k<dim; k++) { + randr= qh_RANDOMint; + *(simplexp++)= 2.0 * randr/randmax - 1.0; + } + } + } + if (issimplex2) { + simplexp= simplex; + for (i= 0; i<dim+1; i++) { + if (iscdd) + out1( 1.0); + for (k= 0; k<dim; k++) + out1( *(simplexp++) * box); + fprintf (fp, "\n"); + } + } + for (j= 0; j<numpoints; j++) { + if (iswidth) + apex= qh_RANDOMint % (dim+1); + else + apex= -1; + for (k= 0; k<dim; k++) + coord[k]= 0.0; + norm= 0.0; + for (i= 0; i<dim+1; i++) { + randr= qh_RANDOMint; + factor= randr/randmax; + if (i == apex) + factor *= width; + norm += factor; + for (k= 0; k<dim; k++) { + simplexp= simplex + i*dim + k; + coord[k] += factor * (*simplexp); + } + } + for (k= 0; k<dim; k++) + coord[k] /= norm; + if (iscdd) + out1( 1.0); + for (k=0; k < dim; k++) + out1( coord[k] * box); + fprintf (fp, "\n"); + } + isregular= 0; /* continue with isbox */ + numpoints= 0; + } + /* ============= mesh distribution =============== */ + if (ismesh) { + nthroot= pow (numpoints, 1.0/dim) + 0.99999; + for (k= dim; k--; ) + mult[k]= 0; + for (i= 0; i < numpoints; i++) { + for (k= 0; k < dim; k++) { + if (k == 0) + out1( mult[0] * meshn + mult[1] * (-meshm)); + else if (k == 1) + out1( mult[0] * meshm + mult[1] * meshn); + else + out1( mult[k] * meshr ); + } + fprintf (fp, "\n"); + for (k= 0; k < dim; k++) { + if (++mult[k] < nthroot) + break; + mult[k]= 0; + } + } + } + + /* ============= regular points for 's' =============== */ + else if (isregular && !islens) { + if (dim != 2 && dim != 3) { + fprintf(stderr, "rbox error: regular points can be used only in 2-d and 3-d\n\n"); + exit(1); + } + if (!isaxis || radius == 0.0) { + isaxis= 1; + radius= 1.0; + } + if (dim == 3) { + if (iscdd) + out1( 1.0); + out3n( 0.0, 0.0, -box); + if (!isgap) { + if (iscdd) + out1( 1.0); + out3n( 0.0, 0.0, box); + } + } + angle= 0.0; + anglediff= 2.0 * PI/numpoints; + for (i=0; i < numpoints; i++) { + angle += anglediff; + x= radius * cos (angle); + y= radius * sin (angle); + if (dim == 2) { + if (iscdd) + out1( 1.0); + out2n( x*box, y*box); + }else { + norm= sqrt (1.0 + x*x + y*y); + if (iscdd) + out1( 1.0); + out3n( box*x/norm, box*y/norm, box/norm); + if (isgap) { + x *= 1-gap; + y *= 1-gap; + norm= sqrt (1.0 + x*x + y*y); + if (iscdd) + out1( 1.0); + out3n( box*x/norm, box*y/norm, box/norm); + } + } + } + } + /* ============= regular points for 'r Ln D2' =============== */ + else if (isregular && islens && dim == 2) { + double cos_0; + + angle= lensangle; + anglediff= 2 * lensangle/(numpoints - 1); + cos_0= cos (lensangle); + for (i=0; i < numpoints; i++, angle -= anglediff) { + x= radius * sin (angle); + y= radius * (cos (angle) - cos_0); + if (iscdd) + out1( 1.0); + out2n( x*box, y*box); + if (i != 0 && i != numpoints - 1) { + if (iscdd) + out1( 1.0); + out2n( x*box, -y*box); + } + } + } + /* ============= regular points for 'r Ln D3' =============== */ + else if (isregular && islens && dim != 2) { + if (dim != 3) { + fprintf(stderr, "rbox error: regular points can be used only in 2-d and 3-d\n\n"); + exit(1); + } + angle= 0.0; + anglediff= 2* PI/numpoints; + if (!isgap) { + isgap= 1; + gap= 0.5; + } + offset= sqrt (radius * radius - (1-gap)*(1-gap)) - lensbase; + for (i=0; i < numpoints; i++, angle += anglediff) { + x= cos (angle); + y= sin (angle); + if (iscdd) + out1( 1.0); + out3n( box*x, box*y, 0); + x *= 1-gap; + y *= 1-gap; + if (iscdd) + out1( 1.0); + out3n( box*x, box*y, box * offset); + if (iscdd) + out1( 1.0); + out3n( box*x, box*y, -box * offset); + } + } + /* ============= apex of 'Zn' distribution + gendim =============== */ + else { + if (isaxis) { + gendim= dim-1; + if (iscdd) + out1( 1.0); + for (j=0; j < gendim; j++) + out1( 0.0); + out1( -box); + fprintf (fp, "\n"); + }else if (islens) + gendim= dim-1; + else + gendim= dim; + /* ============= generate random point in unit cube =============== */ + for (i=0; i < numpoints; i++) { + norm= 0.0; + for (j=0; j < gendim; j++) { + randr= qh_RANDOMint; + coord[j]= 2.0 * randr/randmax - 1.0; + norm += coord[j] * coord[j]; + } + norm= sqrt (norm); + /* ============= dim-1 point of 'Zn' distribution ========== */ + if (isaxis) { + if (!isgap) { + isgap= 1; + gap= 1.0; + } + randr= qh_RANDOMint; + rangap= 1.0 - gap * randr/randmax; + factor= radius * rangap / norm; + for (j=0; j<gendim; j++) + coord[j]= factor * coord[j]; + /* ============= dim-1 point of 'Ln s' distribution =========== */ + }else if (islens && issphere) { + if (!isgap) { + isgap= 1; + gap= 1.0; + } + randr= qh_RANDOMint; + rangap= 1.0 - gap * randr/randmax; + factor= rangap / norm; + for (j=0; j<gendim; j++) + coord[j]= factor * coord[j]; + /* ============= dim-1 point of 'Ln' distribution ========== */ + }else if (islens && !issphere) { + if (!isgap) { + isgap= 1; + gap= 1.0; + } + j= qh_RANDOMint % gendim; + if (coord[j] < 0) + coord[j]= -1.0 - coord[j] * gap; + else + coord[j]= 1.0 - coord[j] * gap; + /* ============= point of 'l' distribution =============== */ + }else if (isspiral) { + if (dim != 3) { + fprintf(stderr, "rbox error: spiral distribution is available only in 3d\n\n"); + exit(1); + } + coord[0]= cos(2*PI*i/(numpoints - 1)); + coord[1]= sin(2*PI*i/(numpoints - 1)); + coord[2]= 2.0*(double)i/(double)(numpoints-1) - 1.0; + /* ============= point of 's' distribution =============== */ + }else if (issphere) { + factor= 1.0/norm; + if (iswidth) { + randr= qh_RANDOMint; + factor *= 1.0 - width * randr/randmax; + } + for (j=0; j<dim; j++) + coord[j]= factor * coord[j]; + } + /* ============= project 'Zn s' point in to sphere =============== */ + if (isaxis && issphere) { + coord[dim-1]= 1.0; + norm= 1.0; + for (j=0; j<gendim; j++) + norm += coord[j] * coord[j]; + norm= sqrt (norm); + for (j=0; j<dim; j++) + coord[j]= coord[j] / norm; + if (iswidth) { + randr= qh_RANDOMint; + coord[dim-1] *= 1 - width * randr/randmax; + } + /* ============= project 'Zn' point onto cube =============== */ + }else if (isaxis && !issphere) { /* not very interesting */ + randr= qh_RANDOMint; + coord[dim-1]= 2.0 * randr/randmax - 1.0; + /* ============= project 'Ln' point out to sphere =============== */ + }else if (islens) { + coord[dim-1]= lensbase; + for (j=0, norm= 0; j<dim; j++) + norm += coord[j] * coord[j]; + norm= sqrt (norm); + for (j=0; j<dim; j++) + coord[j]= coord[j] * radius/ norm; + coord[dim-1] -= lensbase; + if (iswidth) { + randr= qh_RANDOMint; + coord[dim-1] *= 1 - width * randr/randmax; + } + if (qh_RANDOMint > 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); + fprintf (fp, "\n"); + } + } + /* ============= write cube vertices =============== */ + if (addcube) { + for (j=0; j<cubesize; j++) { + if (iscdd) + out1( 1.0); + for (k=dim-1; k>=0; k--) { + if (j & ( 1 << k)) + out1( cube); + else + out1( -cube); + } + fprintf (fp, "\n"); + } + } + /* ============= write diamond vertices =============== */ + if (adddiamond) { + for (j=0; j<diamondsize; j++) { + if (iscdd) + out1( 1.0); + for (k=dim-1; k>=0; k--) { + if (j/2 != k) + out1( 0.0); + else if (j & 0x1) + out1( diamond); + else + out1( -diamond); + } + fprintf (fp, "\n"); + } + } + if (iscdd) + fprintf (fp, "end\nhull\n"); + return 0; + } /* rbox */ + +/*------------------------------------------------ +-outxxx - output functions +*/ +int roundi( double a) { + if (a < 0.0) { + if (a - 0.5 < INT_MIN) { + fprintf(stderr, "rbox input error: coordinate %2.2g is too large. Reduce 'Bn'\n", a); + exit (1); + } + return a - 0.5; + }else { + if (a + 0.5 > INT_MAX) { + fprintf(stderr, "rbox input error: coordinate %2.2g is too large. Reduce 'Bn'\n", a); + exit (1); + } + return a + 0.5; + } +} /* roundi */ + +void out1(double a) { + + if (isinteger) + fprintf(fp, "%d ", roundi( a+out_offset)); + else + fprintf(fp, qh_REAL_1, a+out_offset); +} /* out1 */ + +void out2n( double a, double b) { + + if (isinteger) + fprintf(fp, "%d %d\n", roundi(a+out_offset), roundi(b+out_offset)); + else + fprintf(fp, qh_REAL_2n, a+out_offset, b+out_offset); +} /* out2n */ + +void out3n( double a, double b, double c) { + + if (isinteger) + fprintf(fp, "%d %d %d\n", roundi(a+out_offset), roundi(b+out_offset), roundi(c+out_offset)); + else + fprintf(fp, qh_REAL_3n, a+out_offset, b+out_offset, c+out_offset); +} /* out3n */ + +/*------------------------------------------------- +-rand & srand- generate pseudo-random number between 1 and 2^31 -2 + from Park & Miller's minimimal standard random number generator + Communications of the ACM, 31:1192-1201, 1988. +notes: + does not use 0 or 2^31 -1 + this is silently enforced by qh_srand() + copied from geom2.c +*/ +static int seed = 1; /* global static */ + +int qh_rand( void) { +#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 lo, hi, test; + + 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; + return seed; +} /* rand */ + +void qh_srand( int newseed) { + if (newseed < 1) + seed= 1; + else if (newseed >= qh_rand_m) + seed= qh_rand_m - 1; + else + seed= newseed; +} /* qh_srand */ + diff --git a/NifCommon/qhull/stat.c b/NifCommon/qhull/stat.c new file mode 100644 index 0000000..fb31582 --- /dev/null +++ b/NifCommon/qhull/stat.c @@ -0,0 +1,702 @@ +/*<html><pre> -<a href="qh-stat.htm" + >-------------------------------</a><a name="TOP">-</a> + + stat.c + contains all statistics that are collected for qhull + + see qh-stat.htm and stat.h + + copyright (c) 1993-2003, The Geometry Center +*/ + +#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 ================*/ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="allstatA">-</a> + + 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 (not 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 (not above max_outside)", -1); + zdef_(zinc, Znotgood, "points ignored (not 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); + zdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1); + zzdef_(zinc, Zcheckpart, " ave. distance tests per check", Ztotcheck); +} +void qh_allstatD(void) { + 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 */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="allstatistics">-</a> + + 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 +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="collectstatistics">-</a> + + 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 */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="freestatistics">-</a> + + qh_freestatistics( ) + free memory used for statistics +*/ +void qh_freestatistics (void) { + +#if qh_QHpointer + free (qh_qhstat); + qh_qhstat= NULL; +#endif +} /* freestatistics */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="initstatistics">-</a> + + qh_initstatistics( ) + allocate and initialize statistics + + notes: + uses malloc() instead of qh_memalloc() since mem.c not set up yet +*/ +void qh_initstatistics (void) { + int i; + realT realx; + int intx; + +#if qh_QHpointer + if (!(qh_qhstat= (qhstatT *)malloc (sizeof(qhstatT)))) { + fprintf (qhmem.ferr, "qhull error (qh_initstatistics): insufficient memory\n"); + exit (1); /* 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 > sizeof(qhstat id)) { + fprintf (qhmem.ferr, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\ + qhstat.next %d should be <= sizeof(qhstat id) %d\n", qhstat next, 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]) { + fprintf (qhmem.ferr, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n", + qhstat id[i], i, j); + } + } + } +#endif + exit (1); /* 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 */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="newstats">-</a> + + qh_newstats( ) + returns True if statistics for zdoc + + returns: + next zdoc +*/ +boolT qh_newstats (int index, int *nextindex) { + boolT isnew= False; + int start, i; + + if (qhstat type[qhstat id[index]] == zdoc) + start= index+1; + else + start= index; + 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 */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="nostatistic">-</a> + + 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 +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="printallstatistics">-</a> + + qh_printallstatistics( fp, string ) + print all statistics with header 'string' +*/ +void qh_printallstatistics (FILE *fp, char *string) { + + qh_allstatistics(); + qh_collectstatistics(); + qh_printstatistics (fp, string); + qh_memstatistics (fp); +} + + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="printstatistics">-</a> + + 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, 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); + fprintf (fp, "\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); + fprintf (fp, "\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) + fprintf(fp, "\ + %6.2g max. distance for near-inside points\n", qh NEARinside); + if (qh premerge_cos < REALmax/2) fprintf (fp, "\ + %6.2g max. cosine for pre-merge angle\n", qh premerge_cos); + if (qh PREmerge) fprintf (fp, "\ + %6.2g radius of pre-merge centrum\n", qh premerge_centrum); + if (qh postmerge_cos < REALmax/2) fprintf (fp, "\ + %6.2g max. cosine for post-merge angle\n", qh postmerge_cos); + if (qh POSTmerge) fprintf (fp, "\ + %6.2g radius of post-merge centrum\n", qh postmerge_centrum); + fprintf (fp, "\ + %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++) + fprintf (fp, "%6.2e ", qh NEARzero[k]); + fprintf (fp, "\n\n"); + for (i=0 ; i < qhstat next; ) + qh_printstats (fp, i, &i); +} /* printstatistics */ +#endif /* qh_KEEPstatistics */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="printstatlevel">-</a> + + 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) { + fprintf (fp, "%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) + fprintf (fp, " *0 cnt*"); + else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] == -1) + fprintf (fp, "%7.2g", qhstat stats[id].r); + else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] != -1) + fprintf (fp, "%7.2g", qhstat stats[id].r/ qhstat stats[(unsigned char)(qhstat count[id])].i); + else if (qhstat type[id] < ZTYPEreal && qhstat count[id] == -1) + fprintf (fp, "%7d", qhstat stats[id].i); + else if (qhstat type[id] < ZTYPEreal && qhstat count[id] != -1) + fprintf (fp, "%7.3g", (realT) qhstat stats[id].i / qhstat stats[(unsigned char)(qhstat count[id])].i); + fprintf (fp, " %s\n", qhstat doc[id]); +} /* printstatlevel */ + + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="printstats">-</a> + + qh_printstats( fp, index, nextindex ) + print statistics for a zdoc group + + returns: + next zdoc if non-null +*/ +void qh_printstats (FILE *fp, int index, int *nextindex) { + int j, nexti; + + if (qh_newstats (index, &nexti)) { + fprintf (fp, "\n"); + for (j=index; j<nexti; j++) + qh_printstatlevel (fp, qhstat id[j], 0); + } + if (nextindex) + *nextindex= nexti; +} /* printstats */ + +#if qh_KEEPstatistics + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="stddev">-</a> + + 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/NifCommon/qhull/stat.h b/NifCommon/qhull/stat.h new file mode 100644 index 0000000..a667f75 --- /dev/null +++ b/NifCommon/qhull/stat.h @@ -0,0 +1,522 @@ + /*<html><pre> -<a href="qh-stat.htm" + >-------------------------------</a><a name="TOP">-</a> + + stat.h + contains all statistics that are collected for qhull + + see qh-stat.htm and stat.c + + copyright (c) 1993-2003, The Geometry Center + + 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 + + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="KEEPstatistics">-</a> + + qh_KEEPstatistics + 0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval) +*/ +#ifndef qh_KEEPstatistics +#define qh_KEEPstatistics 1 +#endif + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="statistics">-</a> + + 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">-</a> + 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, + Zvisvertextot, + Zvisvertexmax, + Zwidefacet, + Zwidevertices, + ZEND}; + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="ZZstat">-</a> + + 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, + Ztotmerge, + ZEND}; +#endif + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="ztype">-</a> + + 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 =============*/ + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="MAYdebugx">-</a> + + MAYdebugx + define as maydebug() to be called frequently for error trapping +*/ +#define MAYdebugx + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zdef_">-</a> + + 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 + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zinc_">-</a> + + 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 + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zadd_">-</a> + + 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 + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zval_">-</a> + + 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 + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zmax_">-</a> + + 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 + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zmin_">-</a> + + 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 ==============*/ + + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="intrealT">-</a> + + intrealT + union of integer and real, used for statistics +*/ +typedef union intrealT intrealT; /* union of int and realT */ +union intrealT { + int i; + realT r; +}; + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="qhstat">-</a> + + qhstat + global data structure for statistics + + 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 qhull.h + + allocated in stat.c +*/ +typedef struct qhstatT qhstatT; +#if qh_QHpointer +#define qhstat qh_qhstat-> +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 */ + 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 index, int *nextindex); +boolT qh_nostatistic (int i); +void qh_printallstatistics (FILE *fp, char *string); +void qh_printstatistics (FILE *fp, char *string); +void qh_printstatlevel (FILE *fp, int id, int start); +void qh_printstats (FILE *fp, int index, int *nextindex); +realT qh_stddev (int num, realT tot, realT tot2, realT *ave); + +#endif /* qhDEFstat */ diff --git a/NifCommon/qhull/unix.c b/NifCommon/qhull/unix.c new file mode 100644 index 0000000..6175081 --- /dev/null +++ b/NifCommon/qhull/unix.c @@ -0,0 +1,377 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + unix.c + command line interface to qhull + includes SIOUX interface for Macintoshes + + see qh-qhull.htm + + copyright (c) 1993-2003, The Geometry Center +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include "qhull.h" +#include "mem.h" +#include "qset.h" + +#if __MWERKS__ && __POWERPC__ +#include <SIOUX.h> +#include <Files.h> +#include <console.h> +#include <Desk.h> + +#elif __cplusplus +extern "C" { + int isatty (int); +} + +#elif _MSC_VER +#include <io.h> +#define isatty _isatty + +#else +int isatty (int); /* returns 1 if stdin is a tty + if "Undefined symbol" this can be deleted along with call in main() */ +#endif + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt">-</a> + + qh_prompt + long prompt for qhull + + see: + concise prompt below +*/ +char qh_prompta[]= "\n\ +qhull- compute convex hulls and related structures.\n\ + http://www.qhull.org %s\n\ +\n\ +input (stdin):\n\ + first lines: dimension and number of points (or vice-versa).\n\ + other lines: point coordinates, best if one point per line\n\ + comments: start with a non-numeric character\n\ + halfspaces: use dim plus one and put offset after coefficients.\n\ + May be preceeded by a single interior point ('H').\n\ +\n\ +options:\n\ + d - Delaunay triangulation by lifting points to a paraboloid\n\ + d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\ + v - Voronoi diagram (dual of the Delaunay triangulation)\n\ + v Qu - furthest-site Voronoi diagram\n\ + Hn,n,... - halfspace intersection about point [n,n,0,...]\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ + Qc - keep coplanar points with nearest facet\n\ + Qi - keep interior points with nearest facet\n\ +\n\ +Qhull control options:\n\ + Qbk:n - scale coord k so that low bound is n\n\ + QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\ + QbB - scale input to unit cube centered at the origin\n\ + Qbb - scale last coordinate to [0,m] for Delaunay triangulations\n\ + Qbk:0Bk:0 - remove k-th coordinate from input\n\ + QJn - randomly joggle input in range [-n,n]\n\ + QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\ +%s%s%s%s"; /* split up qh_prompt for Visual C++ */ +char qh_promptb[]= "\ + Qf - partition point to furthest outside facet\n\ + Qg - only build good facets (needs 'QGn', 'QVn', or 'PdD')\n\ + Qm - only process points that would increase max_outside\n\ + Qr - process random outside points instead of furthest ones\n\ + Qs - search all points for the initial simplex\n\ + Qu - for 'd' or 'v', compute upper hull without point at-infinity\n\ + returns furthest-site Delaunay triangulation\n\ + Qv - test vertex neighbors for convexity\n\ + Qx - exact pre-merges (skips coplanar and angle-coplanar facets)\n\ + Qz - add point-at-infinity to Delaunay triangulation\n\ + QGn - good facet if visible from point n, -n for not visible\n\ + QVn - good facet if it includes point n, -n if not\n\ + Q0 - turn off default premerge with 'C-0'/'Qx'\n\ + Q1 - sort merges by type instead of angle\n\ + Q2 - merge all non-convex at once instead of independent sets\n\ + Q3 - do not merge redundant vertices\n\ + Q4 - avoid old->new merges\n\ + Q5 - do not correct outer planes at end of qhull\n\ + Q6 - do not pre-merge concave or coplanar facets\n\ + Q7 - depth-first processing instead of breadth-first\n\ + Q8 - do not process near-inside points\n\ + Q9 - process furthest of furthest points\n\ + Q10 - no special processing for narrow distributions\n\ + Q11 - copy normals and recompute centrums for tricoplanar facets\n\ +\n\ +"; +char qh_promptc[]= "\ +Topts- Trace options:\n\ + T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\ + Tc - check frequently during execution\n\ + Ts - print statistics\n\ + Tv - verify result: structure, convexity, and point inclusion\n\ + Tz - send all output to stdout\n\ + TFn - report summary when n or more facets created\n\ + TI file - input data from file, no spaces or single quotes\n\ + TO file - output results to file, may be enclosed in single quotes\n\ + TPn - turn on tracing when point n added to hull\n\ + TMn - turn on tracing at merge n\n\ + TWn - trace merge facets when width > n\n\ + TRn - rerun qhull n times. Use with 'QJn'\n\ + TVn - stop qhull after adding point n, -n for before (see TCn)\n\ + TCn - stop qhull after building cone for point n (see TVn)\n\ +\n\ +Precision options:\n\ + Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\ + An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\ + C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\ + En - max roundoff error for distance computation\n\ + Rn - randomly perturb computations by a factor of [1-n,1+n]\n\ + Vn - min distance above plane for a visible facet (default 3C-n or En)\n\ + Un - max distance below plane for a new, coplanar point (default Vn)\n\ + Wn - min facet width for outside point (before roundoff, default 2Vn)\n\ +\n\ +Output formats (may be combined; if none, produces a summary to stdout):\n\ + f - facet dump\n\ + G - Geomview output (see below)\n\ + i - vertices incident to each facet\n\ + m - Mathematica output (2-d and 3-d)\n\ + o - OFF format (dim, points and facets; Voronoi regions)\n\ + n - normals with offsets\n\ + p - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')\n\ + s - summary (stderr)\n\ +\n\ +"; +char qh_promptd[]= "\ +More formats:\n\ + Fa - area for each facet\n\ + FA - compute total area and volume for option 's'\n\ + Fc - count plus coplanar points for each facet\n\ + use 'Qc' (default) for coplanar and 'Qi' for interior\n\ + FC - centrum or Voronoi center for each facet\n\ + Fd - use cdd format for input (homogeneous with offset first)\n\ + FD - use cdd format for numeric output (offset first)\n\ + FF - facet dump without ridges\n\ + Fi - inner plane for each facet\n\ + for 'v', separating hyperplanes for bounded Voronoi regions\n\ + FI - ID of each facet\n\ + Fm - merge count for each facet (511 max)\n\ + FM - Maple output (2-d and 3-d)\n\ + Fn - count plus neighboring facets for each facet\n\ + FN - count plus neighboring facets for each point\n\ + Fo - outer plane (or max_outside) for each facet\n\ + for 'v', separating hyperplanes for unbounded Voronoi regions\n\ + FO - options and precision constants\n\ + Fp - dim, count, and intersection coordinates (halfspace only)\n\ + FP - nearest vertex and distance for each coplanar point\n\ + FQ - command used for qhull\n\ + Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\ + output: #vertices, #facets, #coplanars, #nonsimplicial\n\ + #real (2), max outer plane, min vertex\n\ + FS - sizes: #int (0)\n\ + #real(2) tot area, tot volume\n\ + Ft - triangulation with centrums for non-simplicial facets (OFF format)\n\ + Fv - count plus vertices for each facet\n\ + for 'v', Voronoi diagram as Voronoi vertices for pairs of sites\n\ + FV - average of vertices (a feasible point for 'H')\n\ + Fx - extreme points (in order for 2-d)\n\ +\n\ +"; +char qh_prompte[]= "\ +Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)\n\ + Ga - all points as dots\n\ + Gp - coplanar points and vertices as radii\n\ + Gv - vertices as spheres\n\ + Gi - inner planes only\n\ + Gn - no planes\n\ + Go - outer planes only\n\ + Gc - centrums\n\ + Gh - hyperplane intersections\n\ + Gr - ridges\n\ + GDn - drop dimension n in 3-d and 4-d output\n\ + Gt - for 3-d 'd', transparent outer ridges\n\ +\n\ +Print options:\n\ + PAn - keep n largest facets by area\n\ + Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\ + PDk:n - drop facet if normal[k] >= n\n\ + Pg - print good facets (needs 'QGn' or 'QVn')\n\ + PFn - keep facets whose area is at least n\n\ + PG - print neighbors of good facets\n\ + PMn - keep n facets with most merges\n\ + Po - force output. If error, output neighborhood of facet\n\ + Pp - do not report precision problems\n\ +\n\ + . - list of all options\n\ + - - one line descriptions of all options\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt2">-</a> + + qh_prompt2 + synopsis for qhull +*/ +char qh_prompt2[]= "\n\ +qhull- compute convex hulls and related structures. Qhull %s\n\ + input (stdin): dimension, n, point coordinates\n\ + comments start with a non-numeric character\n\ + halfspace: use dim+1 and put offsets after coefficients\n\ +\n\ +options (qh-quick.htm):\n\ + d - Delaunay triangulation by lifting points to a paraboloid\n\ + d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\ + v - Voronoi diagram as the dual of the Delaunay triangulation\n\ + v Qu - furthest-site Voronoi diagram\n\ + H1,1 - Halfspace intersection about [1,1,0,...] via polar duality\n\ + Qt - triangulated output\n\ + QJ - joggled input instead of merged facets\n\ + Tv - verify result: structure, convexity, and point inclusion\n\ + . - concise list of all options\n\ + - - one-line description of all options\n\ +\n\ +Output options (subset):\n\ + s - summary of results (default)\n\ + i - vertices incident to each facet\n\ + n - normals with offsets\n\ + p - vertex coordinates (if 'Qc', includes coplanar points)\n\ + if 'v', Voronoi vertices\n\ + Fp - halfspace intersections\n\ + Fx - extreme points (convex hull vertices)\n\ + FA - compute total area and volume\n\ + o - OFF format (if 'v', outputs Voronoi regions)\n\ + G - Geomview output (2-d, 3-d and 4-d)\n\ + m - Mathematica output (2-d and 3-d)\n\ + QVn - print facets that include point n, -n if not\n\ + TO file- output results to file, may be enclosed in single quotes\n\ +\n\ +examples:\n\ + rbox c d D2 | qhull Qc s f Fx | more rbox 1000 s | qhull Tv s FA\n\ + rbox 10 D2 | qhull d QJ s i TO result rbox 10 D2 | qhull v Qbb Qt p\n\ + rbox 10 D2 | qhull d Qu QJ m rbox 10 D2 | qhull v Qu QJ o\n\ + rbox c | qhull n rbox c | qhull FV n | qhull H Fp\n\ + rbox d D12 | qhull QR0 FA rbox c D7 | qhull FA TF1000\n\ + rbox y 1000 W0 | qhull rbox 10 | qhull v QJ o Fv\n\ +\n\ +"; +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="prompt3">-</a> + + qh_prompt3 + concise prompt for qhull +*/ +char qh_prompt3[]= "\n\ +Qhull %s.\n\ +Except for 'F.' and 'PG', upper-case options take an argument.\n\ +\n\ + delaunay voronoi Geomview Halfspace facet_dump\n\ + incidences mathematica normals OFF_format points\n\ + summary\n\ +\n\ + Farea FArea-total Fcoplanars FCentrums Fd-cdd-in\n\ + FD-cdd-out FF-dump-xridge Finner FIDs Fmerges\n\ + Fneighbors FNeigh-vertex Fouter FOptions Fpoint-intersect\n\ + FPoint_near FQhull Fsummary FSize Ftriangles\n\ + Fvertices Fvoronoi FVertex-ave Fxtremes FMaple\n\ +\n\ + Gvertices Gpoints Gall_points Gno_planes Ginner\n\ + Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\ + Gtransparent\n\ +\n\ + PArea-keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\ + PGood_neighbors PMerge-keep Poutput_forced Pprecision_not\n\ +\n\ + QbBound 0:0.5 Qbk:0Bk:0_drop QbB-scale-box Qbb-scale-last Qcoplanar\n\ + Qfurthest Qgood_only QGood_point Qinterior Qmax_out\n\ + QJoggle Qrandom QRotate Qsearch_1st Qtriangulate\n\ + QupperDelaunay QVertex_good Qvneighbors Qxact_merge Qzinfinite\n\ +\n\ + Q0_no_premerge Q1_no_angle Q2_no_independ Q3_no_redundant Q4_no_old\n\ + Q5_no_check_out Q6_no_concave Q7_depth_first Q8_no_near_in Q9_pick_furthest\n\ + Q10_no_narrow Q11_trinormals\n\ +\n\ + T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\ + TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\ + TRerun TWide_trace TVertex_stop TCone_stop\n\ +\n\ + Angle_max Centrum_size Error_round Random_dist Visible_min\n\ + Ucoplanar_max Wide_outside\n\ +"; + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="main">-</a> + + main( argc, argv ) + processes the command line, calls qhull() to do the work, and exits + + design: + initializes data structures + reads points + finishes initialization + computes convex hull and other structures + checks the result + writes the output + frees memory +*/ +int main(int argc, char *argv[]) { + int curlong, totlong; /* used !qh_NOmem */ + int exitcode, numpoints, dim; + coordT *points; + boolT ismalloc; + +#if __MWERKS__ && __POWERPC__ + char inBuf[BUFSIZ], outBuf[BUFSIZ], errBuf[BUFSIZ]; + SIOUXSettings.showstatusline= false; + SIOUXSettings.tabspaces= 1; + SIOUXSettings.rows= 40; + if (setvbuf (stdin, inBuf, _IOFBF, sizeof(inBuf)) < 0 /* w/o, SIOUX I/O is slow*/ + || setvbuf (stdout, outBuf, _IOFBF, sizeof(outBuf)) < 0 + || (stdout != stderr && setvbuf (stderr, errBuf, _IOFBF, sizeof(errBuf)) < 0)) + fprintf (stderr, "qhull internal warning (main): could not change stdio to fully buffered.\n"); + argc= ccommand(&argv); +#endif + + if ((argc == 1) && isatty( 0 /*stdin*/)) { + fprintf(stdout, qh_prompt2, qh_version); + exit(qh_ERRnone); + } + if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox, + qh_promptb, qh_promptc, qh_promptd, qh_prompte); + exit(qh_ERRnone); + } + if (argc >1 && *argv[1] == '.' && !*(argv[1]+1)) { + fprintf(stdout, qh_prompt3, qh_version); + exit(qh_ERRnone); + } + qh_init_A (stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */ + exitcode= setjmp (qh errexit); /* simple statement for CRAY J916 */ + if (!exitcode) { + qh_initflags (qh qhull_command); + points= qh_readpoints (&numpoints, &dim, &ismalloc); + qh_init_B (points, numpoints, dim, ismalloc); + qh_qhull(); + qh_check_output(); + qh_produce_output(); + if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points(); + exitcode= qh_ERRnone; + } + qh NOerrexit= True; /* no more setjmp */ +#ifdef qh_NOmem + qh_freeqhull( True); +#else + qh_freeqhull( False); + qh_memfreeshort (&curlong, &totlong); + if (curlong || totlong) + fprintf (stderr, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); +#endif + return exitcode; +} /* main */ + diff --git a/NifCommon/qhull/user.c b/NifCommon/qhull/user.c new file mode 100644 index 0000000..18d65fc --- /dev/null +++ b/NifCommon/qhull/user.c @@ -0,0 +1,324 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> + + user.c + user redefinable functions + + see README.txt see COPYING.txt for copyright information. + + see qhull.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" + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="call_qhull">-</a> + + 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 qhull.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 */ + + /* 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) + fprintf (errfile, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong); +} +#endif + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="new_qhull">-</a> + + 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 ", 6)) { + fprintf (errfile, "qh_new_qhull: start qhull_cmd argument with \"qhull \"\n"); + exit(1); + } + qh_initqhull_start (NULL, outfile, errfile); + trace1(( qh ferr, "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) + 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(); + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points(); + } + qh NOerrexit = True; + return exitcode; +} /* new_qhull */ + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="errexit">-</a> + + 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 qhull.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) { + fprintf (qh ferr, "\nqhull error while processing previous error. Exit program\n"); + exit(1); + } + qh ERREXITcalled= True; + if (!qh QHULLfinished) + qh hulltime= qh_CPUclock - qh hulltime; + qh_errprint("ERRONEOUS", facet, NULL, ridge, NULL); + fprintf (qh ferr, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command); + fprintf(qh ferr, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options); + if (qh furthest_id >= 0) { + fprintf(qh ferr, "Last point added to hull was p%d.", qh furthest_id); + if (zzval_(Ztotmerge)) + fprintf(qh ferr, " Last merge was #%d.", zzval_(Ztotmerge)); + if (qh QHULLfinished) + fprintf(qh ferr, "\nQhull has finished constructing the hull."); + else if (qh POSTmerging) + fprintf(qh ferr, "\nQhull has started post-merging."); + fprintf (qh ferr, "\n"); + } + if (qh FORCEoutput && (qh QHULLfinished || (!facet && !ridge))) + qh_produce_output(); + else { + if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh hull_dim+1) { + fprintf (qh ferr, "\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) { + fprintf (qh ferr, "qhull error while ending program. Exit program\n"); + exit(1); + } + qh NOerrexit= True; + longjmp(qh errexit, exitcode); +} /* errexit */ + + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="errprint">-</a> + + 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(char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) { + int i; + + if (atfacet) { + fprintf(qh ferr, "%s FACET:\n", string); + qh_printfacet(qh ferr, atfacet); + } + if (otherfacet) { + fprintf(qh ferr, "%s OTHER FACET:\n", string); + qh_printfacet(qh ferr, otherfacet); + } + if (atridge) { + fprintf(qh ferr, "%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) { + fprintf(qh ferr, "%s VERTEX:\n", string); + qh_printvertex (qh ferr, atvertex); + } + if (qh fout && qh FORCEoutput && atfacet && !qh QHULLfinished && !qh IStracing) { + fprintf(qh ferr, "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 */ + + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="printfacetlist">-</a> + + 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 */ + + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="user_memsizes">-</a> + + 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/NifCommon/qhull/user.h b/NifCommon/qhull/user.h new file mode 100644 index 0000000..ebf59d5 --- /dev/null +++ b/NifCommon/qhull/user.h @@ -0,0 +1,762 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> + + user.h + user redefinable constants + + see qh-user.htm. see COPYING for copyright information. + + before reading any code, review qhull.h for data structure definitions and + the "qh" macro. +*/ + +#ifndef qhDEFuser +#define qhDEFuser 1 + +/*============= data types and configuration macros ==========*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="realT">-</a> + + 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 + help 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="CPUclock">-</a> + + 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. + + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="RANDOM">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="ORIENTclock">-</a> + + qh_ORIENTclock + 0 for inward pointing normals by Geomview convention +*/ +#define qh_ORIENTclock 0 + + +/*========= performance related constants =========*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="HASHfactor">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="VERIFYdirect">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="INITIALsearch">-</a> + + qh_INITIALsearch + if qh_INITIALmax, search points up to this dimension +*/ +#define qh_INITIALsearch 6 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="INITIALmax">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="JOGGLEdefault">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="JOGGLEincrease">-</a> + + qh_JOGGLEincrease + factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain +*/ +#define qh_JOGGLEincrease 10.0 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="JOGGLEretry">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="JOGGLEagain">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="JOGGLEmaxincrease">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="JOGGLEmaxretry">-</a> + + qh_JOGGLEmaxretry + stop after qh_JOGGLEmaxretry attempts +*/ +#define qh_JOGGLEmaxretry 100 + +/*========= memory constants =========*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="MEMalign">-</a> + + 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 fmax_(sizeof(realT), sizeof(void *)) + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="MEMbufsize">-</a> + + qh_MEMbufsize + size of additional memory buffers + + notes: + used for qh_meminitbuffers() in global.c +*/ +#define qh_MEMbufsize 0x10000 /* allocate 64K memory buffers */ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="MEMinitbuf">-</a> + + qh_MEMinitbuf + size of initial memory buffer + + notes: + use for qh_meminitbuffers() in global.c +*/ +#define qh_MEMinitbuf 0x20000 /* initially allocate 128K buffer */ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="INFINITE">-</a> + + qh_INFINITE + on output, indicates Voronoi center at infinity +*/ +#define qh_INFINITE -10.101 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="DEFAULTbox">-</a> + + qh_DEFAULTbox + default box size (Geomview expects 0.5) +*/ +#define qh_DEFAULTbox 0.5 + +/*======= conditional compilation ============================*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="compiler">-</a> + + __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 +*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="COMPUTEfurthest">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="KEEPstatistics">-</a> + + qh_KEEPstatistics + =0 removes most of statistic gathering and reporting + + notes: + if 0, code size is reduced by about 4%. +*/ +#define qh_KEEPstatistics 1 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="MAXoutside">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="NOmerge">-</a> + + 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: + <a href="mem.h#NOmem">qh_NOmem</a> in mem.c + + see user.c/user_eg.c for removing io.o +*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="NOtrace">-</a> + + qh_NOtrace + no tracing if defined + + notes: + This saves about 5% space. + + #define qh_NOtrace +*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="QHpointer">-</a> + + 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() + 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 + + notes: + all global variables for qhull are in qh, qhmem, and qhstat + qh is defined in qhull.h + qhmem is defined in mem.h + qhstat is defined in stat.h + + see: + user_eg.c for an example +*/ +#define qh_QHpointer 0 +#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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="QUICKhelp">-</a> + + 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 these. They effect the performance of facet merging. +*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="DIMmergeVertex">-</a> + + qh_DIMmergeVertex + max dimension for vertex merging (it is not effective in high-d) +*/ +#define qh_DIMmergeVertex 6 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="DIMreduceBuild">-</a> + + qh_DIMreduceBuild + max dimension for vertex reduction during build (slow in high-d) +*/ +#define qh_DIMreduceBuild 5 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="BESTcentrum">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="BESTnonconvex">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="MAXnewmerges">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="MAXnewcentrum">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="COPLANARratio">-</a> + + qh_COPLANARratio + for 3-d+ merging, qh.MINvisible is n*premerge_centrum + + notes: + for non-merging, it's DISTround +*/ +#define qh_COPLANARratio 3 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="DISToutside">-</a> + + 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)) + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="RATIOnearinside">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="SEARCHdist">-</a> + + 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))); + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="USEfindbestnew">-</a> + + 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) + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="WIDEcoplanar">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="MAXnarrow">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="WARNnarrow">-</a> + + 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 + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="ZEROdelaunay">-</a> + + 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/NifCommon/qhull/user_eg.c b/NifCommon/qhull/user_eg.c new file mode 100644 index 0000000..55b4c57 --- /dev/null +++ b/NifCommon/qhull/user_eg.c @@ -0,0 +1,317 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> + + user_eg.c + sample code for calling qhull() from an application + + call with: + + user_eg "cube/diamond options" "delaunay options" "halfspace options" + + for example: + + user_eg # return summaries + + user_eg "n" "o" "Fp" # return normals, OFF, points + + user_eg "n Qt" "o" "Fp" # triangulated cube + + user_eg "QR0 p" "QR0 v p" "QR0 Fp" # rotate input and return points + # 'v' returns Voronoi + # transform is rotated for halfspaces + + main() makes three runs of qhull. + + 1) compute the convex hull of a cube + + 2a) compute the Delaunay triangulation of random points + + 2b) find the Delaunay triangle closest to a point. + + 3) compute the halfspace intersection of a diamond + + notes: + + For another example, see main() in unix.c and user_eg2.c. + These examples, call qh_qhull() directly. They allow + tighter control on the code loaded with Qhull. + + For a simple C++ example, see qhull_interface.cpp + + Summaries are sent to stderr if other output formats are used + + compiled by 'make user_eg' + + see qhull.h for data structures, macros, and user-callable functions. +*/ + +#include "qhull_a.h" + +/*------------------------------------------------- +-internal function prototypes +*/ +void print_summary (void); +void makecube (coordT *points, int numpoints, int dim); +void makeDelaunay (coordT *points, int numpoints, int dim, int seed); +void findDelaunay (int dim); +void makehalf (coordT *points, int numpoints, int dim); + +/*------------------------------------------------- +-print_summary() +*/ +void print_summary (void) { + facetT *facet; + int k; + + printf ("\n%d vertices and %d facets with normals:\n", + qh num_vertices, qh num_facets); + FORALLfacets { + for (k=0; k < qh hull_dim; k++) + printf ("%6.2g ", facet->normal[k]); + printf ("\n"); + } +} + +/*-------------------------------------------------- +-makecube- set points to vertices of cube + points is numpoints X dim +*/ +void makecube (coordT *points, int numpoints, int dim) { + int j,k; + coordT *point; + + for (j=0; j<numpoints; j++) { + point= points + j*dim; + for (k=dim; k--; ) { + if (j & ( 1 << k)) + point[k]= 1.0; + else + point[k]= -1.0; + } + } +} /*.makecube.*/ + +/*-------------------------------------------------- +-makeDelaunay- set points for dim Delaunay triangulation of random points + points is numpoints X dim. +notes: + makeDelaunay() in user_eg2.c uses qh_setdelaunay() to project points in place. +*/ +void makeDelaunay (coordT *points, int numpoints, int dim, int seed) { + int j,k; + coordT *point, realr; + + printf ("seed: %d\n", seed); + qh_RANDOMseed_( seed); + for (j=0; j<numpoints; j++) { + point= points + j*dim; + for (k= 0; k < dim; k++) { + realr= qh_RANDOMint; + point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0; + } + } +} /*.makeDelaunay.*/ + +/*-------------------------------------------------- +-findDelaunay- find Delaunay triangle for [0.5,0.5,...] + assumes dim < 100 +notes: + calls qh_setdelaunay() to project the point to a parabaloid +warning: + This is not implemented for tricoplanar facets ('Qt'), + See <a href="../html/qh-in.htm#findfacet">locate a facet with qh_findbestfacet()</a> +*/ +void findDelaunay (int dim) { + int k; + coordT point[ 100]; + boolT isoutside; + realT bestdist; + facetT *facet; + vertexT *vertex, **vertexp; + + for (k= 0; k < dim; k++) + point[k]= 0.5; + qh_setdelaunay (dim+1, 1, point); + facet= qh_findbestfacet (point, qh_ALL, &bestdist, &isoutside); + if (facet->tricoplanar) { + fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n", + facet->id); + qh_errexit (qh_ERRqhull, facet, NULL); + } + FOREACHvertex_(facet->vertices) { + for (k=0; k < dim; k++) + printf ("%5.2f ", vertex->point[k]); + printf ("\n"); + } +} /*.findDelaunay.*/ + +/*-------------------------------------------------- +-makehalf- set points to halfspaces for a (dim)-dimensional diamond + points is numpoints X dim+1 + + each halfspace consists of dim coefficients followed by an offset +*/ +void makehalf (coordT *points, int numpoints, int dim) { + int j,k; + coordT *point; + + for (j=0; j<numpoints; j++) { + point= points + j*(dim+1); + point[dim]= -1.0; /* offset */ + for (k=dim; k--; ) { + if (j & ( 1 << k)) + point[k]= 1.0; + else + point[k]= -1.0; + } + } +} /*.makehalf.*/ + +#define DIM 3 /* dimension of points, must be < 31 for SIZEcube */ +#define SIZEcube (1<<DIM) +#define SIZEdiamond (2*DIM) +#define TOTpoints (SIZEcube + SIZEdiamond) + +/*-------------------------------------------------- +-main- derived from call_qhull in user.c + + see program header + + this contains three runs of Qhull for convex hull, Delaunay + triangulation or Voronoi vertices, and halfspace intersection + +*/ +int main (int argc, char *argv[]) { + int dim= DIM; /* dimension of points */ + int numpoints; /* number of points */ + coordT points[(DIM+1)*TOTpoints]; /* array of coordinates for each point */ + coordT *rows[TOTpoints]; + boolT ismalloc= False; /* True if qhull should free points in qh_freeqhull() or reallocation */ + char flags[250]; /* 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 */ + int i; + + printf ("This is the output from user_eg.c\n\n\ +It shows how qhull() may be called from an application. It is not part\n\ +of qhull itself. If it appears accidently, please remove user_eg.c from\n\ +your project.\n\n"); + + /* + Run 1: convex hull + */ + printf( "\ncompute convex hull of cube after rotating input\n"); + sprintf (flags, "qhull s Tcv %s", argc >= 2 ? argv[1] : ""); + numpoints= SIZEcube; + makecube (points, numpoints, DIM); + for (i=numpoints; i--; ) + rows[i]= points+dim*i; + qh_printmatrix (outfile, "input", rows, numpoints, dim); + exitcode= qh_new_qhull (dim, numpoints, points, ismalloc, + flags, outfile, errfile); + if (!exitcode) { /* if no error */ + /* 'qh facet_list' contains the convex hull */ + print_summary(); + FORALLfacets { + /* ... your code ... */ + } + } + qh_freeqhull(!qh_ALL); /* free long memory */ + qh_memfreeshort (&curlong, &totlong); /* free short memory and memory allocator */ + if (curlong || totlong) + fprintf (errfile, "qhull internal warning (user_eg, #1): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong); + + /* + Run 2: Delaunay triangulation + */ + + printf( "\ncompute %d-d Delaunay triangulation\n", dim); + sprintf (flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : ""); + numpoints= SIZEcube; + makeDelaunay (points, numpoints, dim, time(NULL)); + for (i=numpoints; i--; ) + rows[i]= points+dim*i; + qh_printmatrix (outfile, "input", rows, numpoints, dim); + exitcode= qh_new_qhull (dim, numpoints, points, ismalloc, + flags, outfile, errfile); + if (!exitcode) { /* if no error */ + /* 'qh facet_list' contains the convex hull */ + /* If you want a Voronoi diagram ('v') and do not request output (i.e., outfile=NULL), + call qh_setvoronoi_all() after qh_new_qhull(). */ + print_summary(); + FORALLfacets { + /* ... your code ... */ + } + printf( "\nfind %d-d Delaunay triangle closest to [0.5, 0.5, ...]\n", dim); + exitcode= setjmp (qh errexit); + if (!exitcode) { + /* Trap Qhull errors in findDelaunay(). Without the setjmp(), Qhull + will exit() after reporting an error */ + qh NOerrexit= False; + findDelaunay (DIM); + } + qh NOerrexit= True; + } +#if qh_QHpointer /* see user.h */ + { + qhT *oldqhA, *oldqhB; + coordT pointsB[DIM*TOTpoints]; /* array of coordinates for each point */ + + + printf( "\nsave first triangulation and compute a new triangulation\n"); + oldqhA= qh_save_qhull(); + sprintf (flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : ""); + numpoints= SIZEcube; + makeDelaunay (pointsB, numpoints, dim, time(NULL)+1); + for (i=numpoints; i--; ) + rows[i]= pointsB+dim*i; + qh_printmatrix (outfile, "input", rows, numpoints, dim); + exitcode= qh_new_qhull (dim, numpoints, pointsB, ismalloc, + flags, outfile, errfile); + if (!exitcode) + print_summary(); + printf( "\nsave second triangulation and restore first one\n"); + oldqhB= qh_save_qhull(); + qh_restore_qhull (&oldqhA); + print_summary(); + printf( "\nfree first triangulation and restore second one.\n"); + qh_freeqhull (qh_ALL); /* free short and long memory used by first call */ + /* do not use qh_memfreeshort */ + qh_restore_qhull (&oldqhB); + print_summary(); + } +#endif + qh_freeqhull(!qh_ALL); /* free long memory */ + qh_memfreeshort (&curlong, &totlong); /* free short memory and memory allocator */ + if (curlong || totlong) + fprintf (errfile, "qhull internal warning (user_eg, #2): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong); + + /* + Run 3: halfspace intersection about the origin + */ + printf( "\ncompute halfspace intersection about the origin for a diamond\n"); + sprintf (flags, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "Fp"); + numpoints= SIZEcube; + makehalf (points, numpoints, dim); + for (i=numpoints; i--; ) + rows[i]= points+(dim+1)*i; + qh_printmatrix (outfile, "input as halfspace coefficients + offsets", rows, numpoints, dim+1); + /* use qh_sethalfspace_all to transform the halfspaces yourself. + If so, set 'qh feasible_point and do not use option 'Hn,...' [it would retransform the halfspaces] + */ + exitcode= qh_new_qhull (dim+1, numpoints, points, ismalloc, + flags, outfile, errfile); + if (!exitcode) + print_summary(); + qh_freeqhull (!qh_ALL); + qh_memfreeshort (&curlong, &totlong); + if (curlong || totlong) /* could also check previous runs */ + fprintf (stderr, "qhull internal warning (user_eg, #3): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); + return exitcode; +} /* main */ + diff --git a/NifCommon/qhull/user_eg2.c b/NifCommon/qhull/user_eg2.c new file mode 100644 index 0000000..5bd012e --- /dev/null +++ b/NifCommon/qhull/user_eg2.c @@ -0,0 +1,542 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + user_eg2.c + + sample code for calling qhull() from an application. + + See user_eg.c for a simpler method using qh_new_qhull(). + The method used here and in unix.c gives you additional + control over Qhull. + + call with: + + user_eg2 "triangulated cube/diamond options" "delaunay options" "halfspace options" + + for example: + + user_eg2 # return summaries + + user_eg2 "n" "o" "Fp" # return normals, OFF, points + + user_eg2 "QR0 p" "QR0 v p" "QR0 Fp" # rotate input and return points + # 'v' returns Voronoi + # transform is rotated for halfspaces + + main() makes three runs of qhull. + + 1) compute the convex hull of a cube, and incrementally add a diamond + + 2a) compute the Delaunay triangulation of random points, and add points. + + 2b) find the Delaunay triangle closest to a point. + + 3) compute the halfspace intersection of a diamond, and add a cube + + notes: + + summaries are sent to stderr if other output formats are used + + derived from unix.c and compiled by 'make user_eg2' + + see qhull.h for data structures, macros, and user-callable functions. + + If you want to control all output to stdio and input to stdin, + set the #if below to "1" and delete all lines that contain "io.c". + This prevents the loading of io.o. Qhull will + still write to 'qh ferr' (stderr) for error reporting and tracing. + + Defining #if 1, also prevents user.o from being loaded. +*/ + +#include "qhull_a.h" + +/*------------------------------------------------- +-internal function prototypes +*/ +void print_summary (void); +void makecube (coordT *points, int numpoints, int dim); +void adddiamond (coordT *points, int numpoints, int numnew, int dim); +void makeDelaunay (coordT *points, int numpoints, int dim); +void addDelaunay (coordT *points, int numpoints, int numnew, int dim); +void findDelaunay (int dim); +void makehalf (coordT *points, int numpoints, int dim); +void addhalf (coordT *points, int numpoints, int numnew, int dim, coordT *feasible); + +/*------------------------------------------------- +-print_summary() +*/ +void print_summary (void) { + facetT *facet; + int k; + + printf ("\n%d vertices and %d facets with normals:\n", + qh num_vertices, qh num_facets); + FORALLfacets { + for (k=0; k < qh hull_dim; k++) + printf ("%6.2g ", facet->normal[k]); + printf ("\n"); + } +} + +/*-------------------------------------------------- +-makecube- set points to vertices of cube + points is numpoints X dim +*/ +void makecube (coordT *points, int numpoints, int dim) { + int j,k; + coordT *point; + + for (j=0; j<numpoints; j++) { + point= points + j*dim; + for (k=dim; k--; ) { + if (j & ( 1 << k)) + point[k]= 1.0; + else + point[k]= -1.0; + } + } +} /*.makecube.*/ + +/*-------------------------------------------------- +-adddiamond- add diamond to convex hull + points is numpoints+numnew X dim. + +notes: + qh_addpoint() does not make a copy of the point coordinates. + + For inside points and some outside points, qh_findbestfacet performs + an exhaustive search for a visible facet. Algorithms that retain + previously constructed hulls should be faster for on-line construction + of the convex hull. +*/ +void adddiamond (coordT *points, int numpoints, int numnew, int dim) { + int j,k; + coordT *point; + facetT *facet; + boolT isoutside; + realT bestdist; + + for (j= 0; j < numnew ; j++) { + point= points + (numpoints+j)*dim; + if (points == qh first_point) /* in case of 'QRn' */ + qh num_points= numpoints+j+1; + /* qh num_points sets the size of the points array. You may + allocate the points elsewhere. If so, qh_addpoint records + the point's address in qh other_points + */ + for (k=dim; k--; ) { + if (j/2 == k) + point[k]= (j & 1) ? 2.0 : -2.0; + else + point[k]= 0.0; + } + facet= qh_findbestfacet (point, !qh_ALL, &bestdist, &isoutside); + if (isoutside) { + if (!qh_addpoint (point, facet, False)) + break; /* user requested an early exit with 'TVn' or 'TCn' */ + } + printf ("%d vertices and %d facets\n", + qh num_vertices, qh num_facets); + /* qh_produce_output(); */ + } + if (qh DOcheckmax) + qh_check_maxout(); + else if (qh KEEPnearinside) + qh_nearcoplanar(); +} /*.adddiamond.*/ + +/*-------------------------------------------------- +-makeDelaunay- set points for dim-1 Delaunay triangulation of random points + points is numpoints X dim. Each point is projected to a paraboloid. +*/ +void makeDelaunay (coordT *points, int numpoints, int dim) { + int j,k, seed; + coordT *point, realr; + + seed= time(NULL); + printf ("seed: %d\n", seed); + qh_RANDOMseed_( seed); + for (j=0; j<numpoints; j++) { + point= points + j*dim; + for (k= 0; k < dim-1; k++) { + realr= qh_RANDOMint; + point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0; + } + } + qh_setdelaunay (dim, numpoints, points); +} /*.makeDelaunay.*/ + +/*-------------------------------------------------- +-addDelaunay- add points to dim-1 Delaunay triangulation + points is numpoints+numnew X dim. Each point is projected to a paraboloid. +notes: + qh_addpoint() does not make a copy of the point coordinates. + + Since qh_addpoint() is not given a visible facet, it performs a directed + search of all facets. Algorithms that retain previously + constructed hulls may be faster. +*/ +void addDelaunay (coordT *points, int numpoints, int numnew, int dim) { + int j,k; + coordT *point, realr; + facetT *facet; + realT bestdist; + boolT isoutside; + + for (j= 0; j < numnew ; j++) { + point= points + (numpoints+j)*dim; + if (points == qh first_point) /* in case of 'QRn' */ + qh num_points= numpoints+j+1; + /* qh num_points sets the size of the points array. You may + allocate the point elsewhere. If so, qh_addpoint records + the point's address in qh other_points + */ + for (k= 0; k < dim-1; k++) { + realr= qh_RANDOMint; + point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0; + } + qh_setdelaunay (dim, 1, point); + facet= qh_findbestfacet (point, !qh_ALL, &bestdist, &isoutside); + if (isoutside) { + if (!qh_addpoint (point, facet, False)) + break; /* user requested an early exit with 'TVn' or 'TCn' */ + } + qh_printpoint (stdout, "added point", point); + printf ("%d points, %d extra points, %d vertices, and %d facets in total\n", + qh num_points, qh_setsize (qh other_points), + qh num_vertices, qh num_facets); + + /* qh_produce_output(); */ + } + if (qh DOcheckmax) + qh_check_maxout(); + else if (qh KEEPnearinside) + qh_nearcoplanar(); +} /*.addDelaunay.*/ + +/*-------------------------------------------------- +-findDelaunay- find Delaunay triangle for [0.5,0.5,...] + assumes dim < 100 +notes: + calls qh_setdelaunay() to project the point to a parabaloid +warning: + This is not implemented for tricoplanar facets ('Qt'), + See <a href="../html/qh-in.htm#findfacet">locate a facet with qh_findbestfacet()</a> +*/ +void findDelaunay (int dim) { + int k; + coordT point[ 100]; + boolT isoutside; + realT bestdist; + facetT *facet; + vertexT *vertex, **vertexp; + + for (k= 0; k < dim-1; k++) + point[k]= 0.5; + qh_setdelaunay (dim, 1, point); + facet= qh_findbestfacet (point, qh_ALL, &bestdist, &isoutside); + if (facet->tricoplanar) { + fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n", + facet->id); + qh_errexit (qh_ERRqhull, facet, NULL); + } + FOREACHvertex_(facet->vertices) { + for (k=0; k < dim-1; k++) + printf ("%5.2f ", vertex->point[k]); + printf ("\n"); + } +} /*.findDelaunay.*/ + +/*-------------------------------------------------- +-makehalf- set points to halfspaces for a (dim)-d diamond + points is numpoints X dim+1 + + each halfspace consists of dim coefficients followed by an offset +*/ +void makehalf (coordT *points, int numpoints, int dim) { + int j,k; + coordT *point; + + for (j=0; j<numpoints; j++) { + point= points + j*(dim+1); + point[dim]= -1.0; /* offset */ + for (k=dim; k--; ) { + if (j & ( 1 << k)) + point[k]= 1.0; + else + point[k]= -1.0; + } + } +} /*.makehalf.*/ + +/*-------------------------------------------------- +-addhalf- add halfspaces for a (dim)-d cube to the intersection + points is numpoints+numnew X dim+1 +notes: + assumes dim < 100. + + For makehalf(), points is the initial set of halfspaces with offsets. + It is transformed by qh_sethalfspace_all into a + (dim)-d set of newpoints. Qhull computed the convex hull of newpoints - + this is equivalent to the halfspace intersection of the + orginal halfspaces. + + For addhalf(), the remainder of points stores the transforms of + the added halfspaces. Qhull computes the convex hull of newpoints + and the added points. qh_addpoint() does not make a copy of these points. + + Since halfspace intersection is equivalent to a convex hull, + qh_findbestfacet may perform an exhaustive search + for a visible facet. Algorithms that retain previously constructed + intersections should be faster for on-line construction. +*/ +void addhalf (coordT *points, int numpoints, int numnew, int dim, coordT *feasible) { + int j,k; + coordT *point, normal[100], offset, *next; + facetT *facet; + boolT isoutside; + realT bestdist; + + for (j= 0; j < numnew ; j++) { + offset= -1.0; + for (k=dim; k--; ) { + if (j/2 == k) { + normal[k]= sqrt (dim); /* to normalize as in makehalf */ + if (j & 1) + normal[k]= -normal[k]; + }else + normal[k]= 0.0; + } + point= points + (numpoints+j)* (dim+1); /* does not use point[dim] */ + qh_sethalfspace (dim, point, &next, normal, &offset, feasible); + facet= qh_findbestfacet (point, !qh_ALL, &bestdist, &isoutside); + if (isoutside) { + if (!qh_addpoint (point, facet, False)) + break; /* user requested an early exit with 'TVn' or 'TCn' */ + } + qh_printpoint (stdout, "added offset -1 and normal", normal); + printf ("%d points, %d extra points, %d vertices, and %d facets in total\n", + qh num_points, qh_setsize (qh other_points), + qh num_vertices, qh num_facets); + /* qh_produce_output(); */ + } + if (qh DOcheckmax) + qh_check_maxout(); + else if (qh KEEPnearinside) + qh_nearcoplanar(); +} /*.addhalf.*/ + +#define DIM 3 /* dimension of points, must be < 31 for SIZEcube */ +#define SIZEcube (1<<DIM) +#define SIZEdiamond (2*DIM) +#define TOTpoints (SIZEcube + SIZEdiamond) + +/*-------------------------------------------------- +-main- derived from call_qhull in user.c + + see program header + + this contains three runs of Qhull for convex hull, Delaunay + triangulation or Voronoi vertices, and halfspace intersection + +*/ +int main (int argc, char *argv[]) { + boolT ismalloc; + int curlong, totlong, exitcode; + char options [2000]; + + printf ("This is the output from user_eg2.c\n\n\ +It shows how qhull() may be called from an application. It is not part\n\ +of qhull itself. If it appears accidently, please remove user_eg2.c from\n\ +your project.\n\n"); + ismalloc= False; /* True if qh_freeqhull should 'free(array)' */ + /* + Run 1: convex hull + */ + qh_init_A (stdin, stdout, stderr, 0, NULL); + exitcode= setjmp (qh errexit); + if (!exitcode) { + coordT array[TOTpoints][DIM]; + + strcat (qh rbox_command, "user_eg cube"); + sprintf (options, "qhull s Tcv Q11 %s ", argc >= 2 ? argv[1] : ""); + qh_initflags (options); + printf( "\ncompute triangulated convex hull of cube after rotating input\n"); + makecube (array[0], SIZEcube, DIM); + qh_init_B (array[0], SIZEcube, DIM, ismalloc); + qh_qhull(); + qh_check_output(); + qh_triangulate(); /* requires option 'Q11' if want to add points */ + print_summary (); + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points (); + printf( "\nadd points in a diamond\n"); + adddiamond (array[0], SIZEcube, SIZEdiamond, DIM); + qh_check_output(); + print_summary (); + qh_produce_output(); /* delete this line to help avoid io.c */ + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points (); + } + qh NOerrexit= True; + qh_freeqhull (!qh_ALL); + qh_memfreeshort (&curlong, &totlong); + /* + Run 2: Delaunay triangulation + */ + qh_init_A (stdin, stdout, stderr, 0, NULL); + exitcode= setjmp (qh errexit); + if (!exitcode) { + coordT array[TOTpoints][DIM]; + + strcat (qh rbox_command, "user_eg Delaunay"); + sprintf (options, "qhull s d Tcv %s", argc >= 3 ? argv[2] : ""); + qh_initflags (options); + printf( "\ncompute %d-d Delaunay triangulation\n", DIM-1); + makeDelaunay (array[0], SIZEcube, DIM); + /* Instead of makeDelaunay with qh_setdelaunay, you may + produce a 2-d array of points, set DIM to 2, and set + qh PROJECTdelaunay to True. qh_init_B will call + qh_projectinput to project the points to the paraboloid + and add a point "at-infinity". + */ + qh_init_B (array[0], SIZEcube, DIM, ismalloc); + qh_qhull(); + /* If you want Voronoi ('v') without qh_produce_output(), call + qh_setvoronoi_all() after qh_qhull() */ + qh_check_output(); + print_summary (); + qh_produce_output(); /* delete this line to help avoid io.c */ + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points (); + printf( "\nadd points to triangulation\n"); + addDelaunay (array[0], SIZEcube, SIZEdiamond, DIM); + qh_check_output(); + qh_produce_output(); /* delete this line to help avoid io.c */ + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points (); + printf( "\nfind Delaunay triangle closest to [0.5, 0.5, ...]\n"); + findDelaunay (DIM); + } + qh NOerrexit= True; + qh_freeqhull (!qh_ALL); + qh_memfreeshort (&curlong, &totlong); + /* + Run 3: halfspace intersection + */ + qh_init_A (stdin, stdout, stderr, 0, NULL); + exitcode= setjmp (qh errexit); + if (!exitcode) { + coordT array[TOTpoints][DIM+1]; /* +1 for halfspace offset */ + pointT *points; + + strcat (qh rbox_command, "user_eg halfspaces"); + sprintf (options, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : ""); + qh_initflags (options); + printf( "\ncompute halfspace intersection about the origin for a diamond\n"); + makehalf (array[0], SIZEcube, DIM); + qh_setfeasible (DIM); /* from io.c, sets qh feasible_point from 'Hn,n' */ + /* you may malloc and set qh feasible_point directly. It is only used for + option 'Fp' */ + points= qh_sethalfspace_all ( DIM+1, SIZEcube, array[0], qh feasible_point); + qh_init_B (points, SIZEcube, DIM, True); /* qh_freeqhull frees points */ + qh_qhull(); + qh_check_output(); + qh_produce_output(); /* delete this line to help avoid io.c */ + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points (); + printf( "\nadd halfspaces for cube to intersection\n"); + addhalf (array[0], SIZEcube, SIZEdiamond, DIM, qh feasible_point); + qh_check_output(); + qh_produce_output(); /* delete this line to help avoid io.c */ + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points (); + } + qh NOerrexit= True; + qh NOerrexit= True; + qh_freeqhull (!qh_ALL); + qh_memfreeshort (&curlong, &totlong); + if (curlong || totlong) /* could also check previous runs */ + fprintf (stderr, "qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", + totlong, curlong); + return exitcode; +} /* main */ + +#if 1 /* use 1 to prevent loading of io.o and user.o */ +/*------------------------------------------- +-errexit- return exitcode to system after an error + assumes exitcode non-zero + prints useful information + see qh_errexit2() in qhull.c for 2 facets +*/ +void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge) { + + if (qh ERREXITcalled) { + fprintf (qh ferr, "qhull error while processing previous error. Exit program\n"); + exit(1); + } + qh ERREXITcalled= True; + if (!qh QHULLfinished) + qh hulltime= (unsigned)clock() - qh hulltime; + fprintf (qh ferr, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command); + fprintf(qh ferr, "Options selected:\n%s\n", qh qhull_options); + if (qh furthest_id >= 0) { + fprintf(qh ferr, "\nLast point added to hull was p%d", qh furthest_id); + if (zzval_(Ztotmerge)) + fprintf(qh ferr, " Last merge was #%d.", zzval_(Ztotmerge)); + if (qh QHULLfinished) + fprintf(qh ferr, "\nQhull has finished constructing the hull."); + else if (qh POSTmerging) + fprintf(qh ferr, "\nQhull has started post-merging"); + fprintf(qh ferr, "\n\n"); + } + if (qh NOerrexit) { + fprintf (qh ferr, "qhull error while ending program. Exit program\n"); + exit(1); + } + if (!exitcode) + exitcode= qh_ERRqhull; + qh NOerrexit= True; + longjmp(qh errexit, exitcode); +} /* errexit */ + + +/*------------------------------------------- +-errprint- prints out the information of the erroneous object + any parameter may be NULL, also prints neighbors and geomview output +*/ +void qh_errprint(char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) { + + fprintf (qh ferr, "%s facets f%d f%d ridge r%d vertex v%d\n", + string, getid_(atfacet), getid_(otherfacet), getid_(atridge), + getid_(atvertex)); +} /* errprint */ + + +void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall) { + facetT *facet, **facetp; + + /* remove these calls to help avoid io.c */ + qh_printbegin (qh ferr, qh_PRINTfacets, facetlist, facets, printall);/*io.c*/ + FORALLfacet_(facetlist) /*io.c*/ + qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall); /*io.c*/ + FOREACHfacet_(facets) /*io.c*/ + qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall); /*io.c*/ + qh_printend (qh ferr, qh_PRINTfacets, facetlist, facets, printall); /*io.c*/ + + FORALLfacet_(facetlist) + fprintf( qh ferr, "facet f%d\n", facet->id); +} /* printfacetlist */ + + + +/*----------------------------------------- +-user_memsizes- allocate up to 10 additional, quick allocation sizes +*/ +void qh_user_memsizes (void) { + + /* qh_memsize (size); */ +} /* user_memsizes */ + +#endif diff --git a/NifExport/Coll.cpp b/NifExport/Coll.cpp index a362328..83b33f9 100755 --- a/NifExport/Coll.cpp +++ b/NifExport/Coll.cpp @@ -1,6 +1,6 @@ #include "pch.h" #include "../NifProps/bhkRigidBodyInterface.h" - +#include "obj/bhkListShape.h" #ifdef _DEBUG #include <assert.h> #include <crtdbg.h> @@ -11,6 +11,7 @@ static Class_ID SCUBA_CLASS_ID(0x6d3d77ac, 0x79c939a9); static Class_ID BHKRIGIDBODYMODIFIER_CLASS_ID(0x398fd801, 0x303e44e5); +extern Class_ID BHKLISTOBJECT_CLASS_ID; enum { @@ -251,6 +252,9 @@ bool Exporter::makeCollisionHierarchy(NiNodeRef &parent, INode *node, TimeValue Exporter::Result Exporter::exportCollision(NiNodeRef &parent, INode *node) { + if (isHandled(node)) + return Exporter::Skip; + ProgressUpdate(Collision, FormatText("'%s' Collision", node->GetName())); // marked as collision? @@ -263,30 +267,35 @@ Exporter::Result Exporter::exportCollision(NiNodeRef &parent, INode *node) NiNodeRef newParent; if (coll) { + markAsHandled(node); + newParent = nodeParent; // always have collision one level up? TimeValue t = 0; Matrix3 tm = getTransform(node, t, local); - bhkShapeRef shape = makeCollisionShape(node, tm); + bhkRigidBodyRef body = makeCollisionBody(node); - body->SetShape(DynamicCast<bhkShape>(shape)); + bhkShapeRef shape = makeCollisionShape(node, tm, body); + if (shape) + { + body->SetShape(DynamicCast<bhkShape>(shape)); - Matrix44 rm4 = TOMATRIX4(tm, false); - Vector3 trans; Matrix33 rm; float scale; - rm4.Decompose(trans, rm, scale); + Matrix44 rm4 = TOMATRIX4(tm, false); + Vector3 trans; Matrix33 rm; float scale; + rm4.Decompose(trans, rm, scale); - QuaternionXYZW q = TOQUATXYZW(rm.AsQuaternion()); - body->SetRotation(q); - body->SetTranslation(trans / Exporter::bhkScaleFactor); + QuaternionXYZW q = TOQUATXYZW(rm.AsQuaternion()); + body->SetRotation(q); + body->SetTranslation(trans / Exporter::bhkScaleFactor); - bhkCollisionObjectRef co = new bhkCollisionObject(); - co->SetBody(DynamicCast<NiObject>(body)); - - //co->SetTarget(newParent); + bhkCollisionObjectRef co = new bhkCollisionObject(); + co->SetBody(DynamicCast<NiObject>(body)); - // link - newParent->SetCollisionObject(DynamicCast<NiCollisionObject>(co)); + //co->SetTarget(newParent); + // link + newParent->SetCollisionObject(DynamicCast<NiCollisionObject>(co)); + } } else if (isCollisionGroup(node) && !mFlattenHierarchy) { newParent = makeNode(nodeParent, node); } else { @@ -306,11 +315,35 @@ Exporter::Result Exporter::exportCollision(NiNodeRef &parent, INode *node) bhkRigidBodyRef Exporter::makeCollisionBody(INode *node) { // get data from node - int lyr, mtl, msys, qtype; - float mass, lindamp, angdamp, frict, maxlinvel, maxangvel, resti, pendepth; - Vector3 center; - - if (npIsCollision(node)) + int lyr = NP_DEFAULT_HVK_LAYER; + int mtl = NP_DEFAULT_HVK_MATERIAL; + int msys = NP_DEFAULT_HVK_MOTION_SYSTEM; + int qtype = NP_DEFAULT_HVK_QUALITY_TYPE; + float mass = NP_DEFAULT_HVK_MASS; + float lindamp = NP_DEFAULT_HVK_LINEAR_DAMPING; + float angdamp = NP_DEFAULT_HVK_ANGULAR_DAMPING; + float frict = NP_DEFAULT_HVK_FRICTION; + float maxlinvel = NP_DEFAULT_HVK_MAX_LINEAR_VELOCITY; + float maxangvel = NP_DEFAULT_HVK_MAX_ANGULAR_VELOCITY; + float resti = NP_DEFAULT_HVK_RESTITUTION; + float pendepth = NP_DEFAULT_HVK_PENETRATION_DEPTH; + Vector3 center(0,0,0); + + if (bhkRigidBodyInterface *irb = (bhkRigidBodyInterface *)node->GetObjectRef()->GetInterface(BHKRIGIDBODYINTERFACE_DESC)) + { + mass = irb->GetMass(0); + frict = irb->GetFriction(0); + resti = irb->GetRestitution(0); + lyr = irb->GetLayer(0); + msys = irb->GetMotionSystem(0); + qtype = irb->GetQualityType(0); + lindamp = irb->GetLinearDamping(0); + angdamp = irb->GetAngularDamping(0); + maxlinvel = irb->GetMaxLinearVelocity(0); + pendepth = irb->GetPenetrationDepth(0); + maxangvel = irb->GetMaxAngularVelocity(0); + } + else if (npIsCollision(node)) { // Handle compatibility npGetProp(node, NP_HVK_MASS_OLD, mass, NP_DEFAULT_HVK_EMPTY); @@ -372,7 +405,7 @@ bhkRigidBodyRef Exporter::makeCollisionBody(INode *node) return body; } -bhkShapeRef Exporter::makeCollisionShape(INode *node, Matrix3& tm) +bhkShapeRef Exporter::makeCollisionShape(INode *node, Matrix3& tm, bhkRigidBodyRef body) { bhkShapeRef shape; @@ -386,6 +419,8 @@ bhkShapeRef Exporter::makeCollisionShape(INode *node, Matrix3& tm) shape = makeSphereShape(node, os.obj, tm); else if (os.obj->SuperClassID() == GEOMOBJECT_CLASS_ID) shape = makeTriStripsShape(node, tm); + else if (os.obj->ClassID() == BHKLISTOBJECT_CLASS_ID) + shape = makeListShape(node, tm, body); return shape; } @@ -505,10 +540,15 @@ bhkShapeRef Exporter::makeTriStripsShape(INode *node, Matrix3& tm) data->SetVertices(verts); data->SetNormals(vnorms); + int lyr = OL_STATIC; + npGetProp(node, NP_HVK_LAYER, lyr, NP_DEFAULT_HVK_LAYER); + // setup shape bhkNiTriStripsShapeRef shape = StaticCast<bhkNiTriStripsShape>(bhkNiTriStripsShape::Create()); shape->SetNumStripsData(1); shape->SetStripsData(0, data); + shape->SetNumDataLayers(1); + shape->SetOblivionLayer(0, OblivionLayer(lyr)); int mtl; npGetProp(node, NP_HVK_MATERIAL, mtl, NP_DEFAULT_HVK_MATERIAL); @@ -542,12 +582,28 @@ Exporter::Result Exporter::scanForCollision(INode *node) // Check self to see if is one of our bhkXXXObject classes if (Object* obj = node->GetObjectRef()) { - if (obj->SuperClassID() == HELPER_CLASS_ID && - obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB()) { - mCollisionNodes.insert(node); - } + if (obj->ClassID() == BHKLISTOBJECT_CLASS_ID) + { + mCollisionNodes.insert(node); + + const int PB_MESHLIST = 1; + IParamBlock2* pblock2 = obj->GetParamBlockByID(0); + int nBlocks = pblock2->Count(PB_MESHLIST); + for (int i = 0;i < pblock2->Count(PB_MESHLIST); i++) { + INode *tnode = NULL; + pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i); + if (tnode != NULL) { + mCollisionNodes.insert(tnode); + markAsHandled(tnode); // dont process collision since the list will + } + } + } + else if (obj->SuperClassID() == HELPER_CLASS_ID && + obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB()) + { + mCollisionNodes.insert(node); + } } - if (npIsCollision(node)) { mCollisionNodes.insert(node); @@ -559,7 +615,85 @@ Exporter::Result Exporter::scanForCollision(INode *node) return Exporter::Ok; } +bool Exporter::isHandled(INode *node) +{ + return (mHandledNodes.find(node) != mHandledNodes.end()); +} + +bool Exporter::markAsHandled(INode* node) +{ + mHandledNodes.insert(node); + return true; +} + bool Exporter::isCollision(INode *node) { return (mCollisionNodes.find(node) != mCollisionNodes.end()); +} + +bhkShapeRef Exporter::makeListShape(INode *node, Matrix3& tm, bhkRigidBodyRef body) +{ + const int PB_MATERIAL = 0; + const int PB_MESHLIST = 1; + IParamBlock2* pblock2 = node->GetObjectRef()->GetParamBlockByID(0); + int nBlocks = pblock2->Count(PB_MESHLIST); + if (nBlocks > 0) + { + if (bhkRigidBodyInterface *irb = (bhkRigidBodyInterface *)node->GetObjectRef()->GetInterface(BHKRIGIDBODYINTERFACE_DESC)) + { + int mass = irb->GetMass(0); + float frict = irb->GetFriction(0); + float resti = irb->GetRestitution(0); + int lyr = irb->GetLayer(0); + int msys = irb->GetMotionSystem(0); + int qtype = irb->GetQualityType(0); + float lindamp = irb->GetLinearDamping(0); + float angdamp = irb->GetAngularDamping(0); + float maxlinvel = irb->GetMaxLinearVelocity(0); + float maxangvel = irb->GetMaxAngularVelocity(0); + float pendepth = irb->GetPenetrationDepth(0); + + body->SetLayer(OblivionLayer(lyr)); + body->SetLayerCopy(OblivionLayer(lyr)); + body->SetMotionSystem(MotionSystem(msys)); + body->SetQualityType(MotionQuality(qtype)); + body->SetMass(mass); + body->SetLinearDamping(lindamp); + body->SetAngularDamping(angdamp); + body->SetFriction(frict); + body->SetRestitution(resti); + body->SetMaxLinearVelocity(maxlinvel); + body->SetMaxAngularVelocity(maxangvel); + body->SetPenetrationDepth(pendepth); + } + + bhkListShapeRef shape = new bhkListShape(); + + int mtl = pblock2->GetInt(PB_MATERIAL, 0, 0); + shape->SetMaterial(HavokMaterial(mtl)); + + vector<bhkShapeRef> shapes; + + for (int i = 0; i < nBlocks; i++) { + INode *tnode = NULL; + pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i); + if (tnode != NULL) + { + bhkShapeRef subshape = makeCollisionShape(tnode, tm, body); + if (subshape) + shapes.push_back(subshape); + } + } + shape->SetSubShapes(shapes); + + if (shapes.size() == 1) // ignore the list when only one object is present + { + return shapes[0]; + } + else if (!shapes.empty()) + { + return bhkShapeRef(shape); + } + } + return bhkShapeRef(); } \ No newline at end of file diff --git a/NifExport/Exporter.cpp b/NifExport/Exporter.cpp index 19b3963..6ad3be8 100755 --- a/NifExport/Exporter.cpp +++ b/NifExport/Exporter.cpp @@ -233,7 +233,7 @@ Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node) ProgressUpdate(Geometry, FormatText("'%s' Geometry", nodeName.data())); // Abort if is a collision node or is hidden and we are not exporting hidden - if (coll || (node->IsHidden() && !mExportHidden)) + if (coll || (node->IsHidden() && !mExportHidden) || isHandled(node)) return Skip; bool local = !mFlattenHierarchy; diff --git a/NifExport/Exporter.h b/NifExport/Exporter.h index aaf1df0..12534e9 100755 --- a/NifExport/Exporter.h +++ b/NifExport/Exporter.h @@ -18,7 +18,7 @@ class Exporter { public: - + enum Result { Error, @@ -27,33 +27,33 @@ public: Skip }; - enum ExportType - { - NIF_WO_ANIM = 0, - NIF_WO_KF = 1, - SINGLE_KF_WITH_NIF = 2, - SINGLE_KF_WO_NIF = 3, - MULTI_KF_WITH_NIF = 4, - MULTI_KF_WO_NIF = 5, - NIF_WITH_MGR = 6, - }; + enum ExportType + { + NIF_WO_ANIM = 0, + NIF_WO_KF = 1, + SINGLE_KF_WITH_NIF = 2, + SINGLE_KF_WO_NIF = 3, + MULTI_KF_WITH_NIF = 4, + MULTI_KF_WO_NIF = 5, + NIF_WITH_MGR = 6, + }; - // Callback for post-processing instructions - struct NiCallback - { - NiCallback() {}; - virtual ~NiCallback() {}; - virtual Result execute() = 0; - }; + // Callback for post-processing instructions + struct NiCallback + { + NiCallback() {}; + virtual ~NiCallback() {}; + virtual Result execute() = 0; + }; - class CancelExporterException{}; + class CancelExporterException{}; /* exporter version */ static int mVersion; /* export options, static for dialog usage. */ - static bool mUseRegistry; + static bool mUseRegistry; static bool mSelectedOnly; static bool mTriStrips; static bool mExportHidden; @@ -63,38 +63,38 @@ public: static bool mVertexColors; static float mWeldThresh; static bool mExportCollision; - static bool mRemapIndices; - static bool mExportExtraNodes; - static bool mExportSkin; - static bool mUserPropBuffer; - static bool mFlattenHierarchy; - static bool mRemoveUnreferencedBones; - static bool mSortNodesToEnd; - static string mGameName; - static string mNifVersion; - static int mNifVersionInt; - static int mNifUserVersion; - static bool mSkeletonOnly; - static bool mExportCameras; - static bool mGenerateBoneCollision; - static bool mExportTransforms; - static float mDefaultPriority; - static ExportType mExportType; - static bool mMultiplePartitions; - static int mBonesPerVertex; - static int mBonesPerPartition; - static bool mUseTimeTags; - static bool mAutoDetect; - static bool mAllowAccum; - static string mCreatorName; - static bool mCollapseTransforms; - static bool mFixNormals; - static bool mTangentAndBinormalExtraData; - static stringlist mRotate90Degrees; - static bool mSupportPrnStrings; - static bool mSuppressPrompts; - static bool mUseAlternateStripper; - static float bhkScaleFactor; + static bool mRemapIndices; + static bool mExportExtraNodes; + static bool mExportSkin; + static bool mUserPropBuffer; + static bool mFlattenHierarchy; + static bool mRemoveUnreferencedBones; + static bool mSortNodesToEnd; + static string mGameName; + static string mNifVersion; + static int mNifVersionInt; + static int mNifUserVersion; + static bool mSkeletonOnly; + static bool mExportCameras; + static bool mGenerateBoneCollision; + static bool mExportTransforms; + static float mDefaultPriority; + static ExportType mExportType; + static bool mMultiplePartitions; + static int mBonesPerVertex; + static int mBonesPerPartition; + static bool mUseTimeTags; + static bool mAutoDetect; + static bool mAllowAccum; + static string mCreatorName; + static bool mCollapseTransforms; + static bool mFixNormals; + static bool mTangentAndBinormalExtraData; + static stringlist mRotate90Degrees; + static bool mSupportPrnStrings; + static bool mSuppressPrompts; + static bool mUseAlternateStripper; + static float bhkScaleFactor; Exporter(Interface *i, AppSettings *appSettings); @@ -109,18 +109,18 @@ public: static void writeConfig(INode *node); // writes config to registry static void writeConfig(Interface *i); - // writes config to registry - static void writeKfConfig(Interface *i); + // writes config to registry + static void writeKfConfig(Interface *i); // reads config from root node static void readConfig(INode *node); // reads config from registry static void readConfig(Interface *i); - // reads config from registry - static void readKfConfig(Interface *i); + // reads config from registry + static void readKfConfig(Interface *i); - static AppSettings * exportAppSettings(); - static AppSettings * importAppSettings(string fname); + static AppSettings * exportAppSettings(); + static AppSettings * importAppSettings(string fname); public: typedef vector<unsigned short> TriStrip; @@ -134,32 +134,35 @@ public: Triangles faces; vector<TexCoord> uvs; vector<Color4> vcolors; - vector<int> vidx; - TriStrips strips; + vector<int> vidx; + TriStrips strips; }; // maps face groups to material ID typedef std::map<int, FaceGroup> FaceGroups; - typedef std::set<INode*> INodeMap; - typedef std::map<string, NiNodeRef> NodeMap; - typedef std::list<NiCallback*> CallbackList; - typedef std::list<Ref<NiNode> > NodeList; - + typedef std::set<INode*> INodeMap; + typedef std::map<string, NiNodeRef> NodeMap; + typedef std::list<NiCallback*> CallbackList; + typedef std::list<Ref<NiNode> > NodeList; + Interface *mI; NiNodeRef mNiRoot; - AppSettings *mAppSettings; - NodeMap mNodeMap; - INodeMap mCollisionNodes; - INode* mSceneCollisionNode; - CallbackList mPostExportCallbacks; - bool mIsBethesda; - Box3 mBoundingBox; - NodeList mAnimationRoots; - - Result exportNodes(NiNodeRef &root, INode *node); + AppSettings *mAppSettings; + NodeMap mNodeMap; + INodeMap mCollisionNodes; + INodeMap mHandledNodes; + INode* mSceneCollisionNode; + CallbackList mPostExportCallbacks; + bool mIsBethesda; + Box3 mBoundingBox; + NodeList mAnimationRoots; + + Result exportNodes(NiNodeRef &root, INode *node); Result exportCollision(NiNodeRef &root, INode *node); - Result scanForCollision(INode *node); - bool isCollision(INode *node); + Result scanForCollision(INode *node); + bool isCollision(INode *node); + bool isHandled(INode *node); + bool markAsHandled(INode* node); /* utility functions */ Mtl *getMaterial(INode *node, int subMtl); @@ -167,20 +170,20 @@ public: void convertMatrix(Matrix33 &dst, const Matrix3 &src); void nodeTransform(Matrix33 &rot, Vector3 &trans, INode *node, TimeValue t, bool local=true); void nodeTransform(QuaternionXYZW &rot, Vector3 &trans, INode *node, TimeValue t, bool local=true); - Matrix3 getTransform(INode *node, TimeValue t, bool local=true); + Matrix3 getTransform(INode *node, TimeValue t, bool local=true); Point3 getVertexNormal(Mesh* mesh, int faceNo, RVertex* rv); bool equal(const Vector3 &a, const Point3 &b, float thresh); - BitmapTex *getTexture(Mtl *mtl); - BitmapTex *getTexture(Mtl *mtl, int i); + BitmapTex *getTexture(Mtl *mtl); + BitmapTex *getTexture(Mtl *mtl, int i); void getTextureMatrix(Matrix3 &mat, Mtl *mtl); - NiNodeRef makeNode(NiNodeRef &parent, INode *maxNode, bool local=true); - NiNodeRef getNode(const string& name); + NiNodeRef makeNode(NiNodeRef &parent, INode *maxNode, bool local=true); + NiNodeRef getNode(const string& name); // returns true if the node contains collision objects bool isCollisionGroup(INode *maxNode, bool root=true); // returns true if the node contains meshes bool isMeshGroup(INode *maxNode, bool root=true); - void CalcBoundingBox(INode *node, Box3& box, int all=1); - void CalcBoundingSphere(INode *node, Point3 center, float& radius, int all=1); + void CalcBoundingBox(INode *node, Box3& box, int all=1); + void CalcBoundingSphere(INode *node, Point3 center, float& radius, int all=1); /* tristrips */ void strippify(TriStrips &strips, vector<Vector3> &verts, vector<Vector3> &norms, const Triangles &tris); @@ -203,64 +206,65 @@ public: /* texture & material */ // creates NiTexturingProperty + NiSourceTexture void makeTexture(NiAVObjectRef &parent, Mtl *mtl); - bool makeTextureDesc(BitmapTex *bmTex, Niflib::TexDesc &td); + bool makeTextureDesc(BitmapTex *bmTex, Niflib::TexDesc &td); // creates a NiMaterialProperty void makeMaterial(NiAVObjectRef &parent, Mtl *mtl); - bool exportCiv4Shader(NiAVObjectRef parent, Mtl* mtl); + bool exportCiv4Shader(NiAVObjectRef parent, Mtl* mtl); /* havok & collision */ int addVertex(vector<Vector3> &verts, vector<Vector3> &vnorms, const Point3 &pt, const Point3 &norm); void addFace(Triangles &tris, vector<Vector3> &verts, vector<Vector3> &vnorms, - int face, const int vi[3], Mesh *mesh, Matrix3& tm); + int face, const int vi[3], Mesh *mesh, Matrix3& tm); bool makeCollisionHierarchy(NiNodeRef &parent, INode *node, TimeValue t); - + /* creates a bhkRigidBody */ bhkRigidBodyRef makeCollisionBody(INode *node); /* creates a collision shape from a node */ - bhkShapeRef makeCollisionShape(INode *node, Matrix3& tm); + bhkShapeRef makeCollisionShape(INode *node, Matrix3& tm, bhkRigidBodyRef body); bhkShapeRef makeTriStripsShape(INode *node, Matrix3& tm); bhkShapeRef makeBoxShape(INode *node, Object *obj, Matrix3& tm); bhkShapeRef makeSphereShape(INode *node, Object *obj, Matrix3& tm); bhkShapeRef makeCapsuleShape(INode *node, Object *obj, Matrix3& tm); - - /* skin export */ - bool makeSkin(NiTriBasedGeomRef shape, INode *node, FaceGroup &grp, TimeValue t); - bool exportSkin(); - - /* animation export */ - Result doAnimExport(Ref<NiControllerSequence> root); - Result doAnimExport(Ref<NiControllerManager> ctrl, INode *node); - bool isNodeTracked(INode *node); - bool isNodeKeyed(INode *node); - Ref<NiTimeController> CreateController(INode *node, Interval range); - static void InitializeTimeController(Ref<NiTimeController> ctrl, NiNodeRef parent); - - /* misc export */ - bool exportUPB(NiNodeRef &root, INode *node); - bool removeUnreferencedBones(NiNodeRef node); - void sortNodes(NiNodeRef node); - NiNodeRef exportBone(NiNodeRef parent, INode *node); - Result exportLight(NiNodeRef root, INode *node, GenLight* light); - void getChildNodes(INode *node, vector<NiNodeRef>&list); - bool exportPrn(NiNodeRef &root, INode *node); - NiNodeRef createAccumNode(NiNodeRef parent, INode *node); - int countNodes(INode *node); - bool isSkeletonRoot(INode *node); - void ApplyAllSkinOffsets( NiAVObjectRef & root ); - - /* Progress Bar stuff */ - enum ProgressSection - { - Geometry, - Animation, - Collision, - Skin, - ProgressSectionCount - }; - int progressCounters[ProgressSectionCount]; - int progressMax[ProgressSectionCount]; - void ProgressUpdate( ProgressSection section, const TCHAR *s ); + bhkShapeRef makeListShape(INode *node, Matrix3& tm, bhkRigidBodyRef body); + + /* skin export */ + bool makeSkin(NiTriBasedGeomRef shape, INode *node, FaceGroup &grp, TimeValue t); + bool exportSkin(); + + /* animation export */ + Result doAnimExport(Ref<NiControllerSequence> root); + Result doAnimExport(Ref<NiControllerManager> ctrl, INode *node); + bool isNodeTracked(INode *node); + bool isNodeKeyed(INode *node); + Ref<NiTimeController> CreateController(INode *node, Interval range); + static void InitializeTimeController(Ref<NiTimeController> ctrl, NiNodeRef parent); + + /* misc export */ + bool exportUPB(NiNodeRef &root, INode *node); + bool removeUnreferencedBones(NiNodeRef node); + void sortNodes(NiNodeRef node); + NiNodeRef exportBone(NiNodeRef parent, INode *node); + Result exportLight(NiNodeRef root, INode *node, GenLight* light); + void getChildNodes(INode *node, vector<NiNodeRef>&list); + bool exportPrn(NiNodeRef &root, INode *node); + NiNodeRef createAccumNode(NiNodeRef parent, INode *node); + int countNodes(INode *node); + bool isSkeletonRoot(INode *node); + void ApplyAllSkinOffsets( NiAVObjectRef & root ); + + /* Progress Bar stuff */ + enum ProgressSection + { + Geometry, + Animation, + Collision, + Skin, + ProgressSectionCount + }; + int progressCounters[ProgressSectionCount]; + int progressMax[ProgressSectionCount]; + void ProgressUpdate( ProgressSection section, const TCHAR *s ); }; #endif diff --git a/NifImport/ImportCollision.cpp b/NifImport/ImportCollision.cpp index 22f17b3..f5f6604 100644 --- a/NifImport/ImportCollision.cpp +++ b/NifImport/ImportCollision.cpp @@ -19,10 +19,16 @@ HISTORY: #include "obj\bhkSphereShape.h" #include "obj\bhkCapsuleShape.h" #include "obj\bhkConvexVerticesShape.h" +#include "obj\bhkMoppBvTreeShape.h" +#include "obj\bhkPackedNiTriStripsShape.h" +#include "obj\hkPackedNiTriStripsData.h" +#include "obj\bhkListShape.h" +#include "..\NifProps\bhkRigidBodyInterface.h" #include "NifPlugins.h" using namespace Niflib; +extern Class_ID BHKLISTOBJECT_CLASS_ID; static Class_ID SCUBA_CLASS_ID(0x6d3d77ac, 0x79c939a9); enum { @@ -37,12 +43,28 @@ struct CollisionImport NifImporter ∋ + void AddShape(INode *rbody, INode *shapeNode); + bool ImportRigidBody(bhkRigidBodyRef rb, INode* node); + INode *CreateRigidBody(bhkRigidBodyRef body); + + bool ImportBase(bhkRigidBodyRef body, bhkShapeRef shape, INode* parent, INode *shapeNode); + bool ImportShape(INode *rbody, bhkRigidBodyRef body, bhkShapeRef shape, INode* parent); + bool ImportBox(INode *rbody, bhkRigidBodyRef body, bhkBoxShapeRef shape, INode *parent); + bool ImportSphere(INode *rbody, bhkRigidBodyRef body, bhkSphereShapeRef shape, INode *parent); + bool ImportCapsule(INode *rbody, bhkRigidBodyRef body, bhkCapsuleShapeRef shape, INode *parent); + bool ImportConvexVertices(INode *rbody, bhkRigidBodyRef body, bhkConvexVerticesShapeRef shape, INode *parent); + bool ImportTriStripsShape(INode *rbody, bhkRigidBodyRef body, bhkNiTriStripsShapeRef shape, INode *parent); + bool ImportMoppBvTreeShape(INode *rbody, bhkRigidBodyRef body, bhkMoppBvTreeShapeRef shape, INode *parent); + bool ImportPackedNiTriStripsShape(INode *rbody, bhkRigidBodyRef body, bhkPackedNiTriStripsShapeRef shape, INode *parent); + bool ImportListShape(INode *rbody, bhkRigidBodyRef body, bhkListShapeRef shape, INode *parent); - bool ImportShape(bhkRigidBodyRef body, bhkShapeRef shape, INode* parent); - INode* ImportSphere(bhkRigidBodyRef body, bhkSphereShapeRef shape, INode *parent); - INode* ImportCapsule(bhkRigidBodyRef body, bhkCapsuleShapeRef shape, INode *parent); - INode* ImportConvexVertices(bhkRigidBodyRef body, bhkConvexVerticesShapeRef shape, INode *parent); + INode* ImportCollisionMesh( + const vector<Vector3>& verts, + const vector<Triangle>& tris, + const vector<Vector3>& norms, + INode *parent + ); }; bool NifImporter::ImportCollision(NiNodeRef node) @@ -50,22 +72,32 @@ bool NifImporter::ImportCollision(NiNodeRef node) bool ok = false; if (!enableCollision) return false; -#ifdef USE_UNSUPPORTED_CODE // Currently only support the Oblivion bhk basic objects NiCollisionObjectRef collObj = node->GetCollisionObject(); if (collObj) { - CollisionImport ci(*this); - if (INode *inode = FindINode(gi, collObj->GetTarget())) - { - NiObjectRef body = collObj->GetBody(); - if (body->IsDerivedType(bhkRigidBody::TYPE)) - { - ci.ImportRigidBody(bhkRigidBodyRef(body), inode); - } - } + NiObjectRef body = collObj->GetBody(); + if (body->IsDerivedType(bhkRigidBody::TYPE)) + { + bhkRigidBodyRef rbody = DynamicCast<bhkRigidBody>(body); + + if (bhkShapeRef shape = rbody->GetShape()) { + INode *node = NULL; + NiNodeRef target = collObj->GetTarget(); + if (ignoreRootNode || strmatch(target->GetName(), "Scene Root")) + node = gi->GetRootNode(); + else + node = FindINode(gi, target); + + CollisionImport ci(*this); + INode *body = ci.CreateRigidBody(rbody); + if (!ci.ImportShape(body, rbody, shape, node)) + { + body->Delete(0, 1); + } + } + } } -#endif return ok; } @@ -104,51 +136,189 @@ bool CollisionImport::ImportRigidBody(bhkRigidBodyRef body, INode* node) npSetProp(node, NP_HVK_PENETRATION_DEPTH, pendepth); npSetProp(node, NP_HVK_CENTER, center); - if (bhkShapeRef shape = body->GetShape()) { - ImportShape(body, shape, node); - } + npSetCollision(node, true); return true; } -bool CollisionImport::ImportShape(bhkRigidBodyRef body, bhkShapeRef shape, INode* parent) +INode* CollisionImport::CreateRigidBody(bhkRigidBodyRef body) { - INode *shapeNode = NULL; - if (shape->IsDerivedType(bhkCapsuleShape::TYPE)) - { - shapeNode = ImportCapsule(body, bhkCapsuleShapeRef(shape), parent); - } - else if (shape->IsDerivedType(bhkSphereShape::TYPE)) - { - shapeNode = ImportSphere(body, bhkSphereShapeRef(shape), parent); - } - else if (shape->IsDerivedType(bhkConvexVerticesShape::TYPE)) - { - shapeNode = ImportConvexVertices(body, bhkConvexVerticesShapeRef(shape), parent); - } + INode *rbody = NULL; + if (body == NULL) + return rbody; - // Now do common post processing for the node - if (shapeNode != NULL) - { - TSTR name = "bhk"; - name.Append(parent->GetName()); - shapeNode->SetName(name); - - Point3 pos = TOPOINT3(body->GetTranslation()) * ni.bhkScaleFactor; - Quat rot = TOQUAT(body->GetRotation()); - PosRotScaleNode(shapeNode, pos, rot, ni.bhkScaleFactor, prsDefault); - - shapeNode->SetPrimaryVisibility(FALSE); - shapeNode->SetSecondaryVisibility(FALSE); - shapeNode->BoneAsLine(TRUE); - shapeNode->SetRenderable(FALSE); - shapeNode->XRayMtl(TRUE); - parent->AttachChild(shapeNode); - return true; - } - return false; + OblivionLayer lyr = body->GetLayer(); + //body->GetLayerCopy(lyr); + MotionSystem msys = body->GetMotionSystem(); + MotionQuality qtype = body->GetQualityType(); + float mass = body->GetMass(); + float lindamp = body->GetLinearDamping(); + float angdamp = body->GetAngularDamping(); + float frict = body->GetFriction(); + float resti = body->GetRestitution(); + float maxlinvel = body->GetMaxLinearVelocity(); + float maxangvel = body->GetMaxAngularVelocity(); + float pendepth = body->GetPenetrationDepth(); + Vector3 center = body->GetCenter(); + + SimpleObject2* listObj = (SimpleObject2*)ni.gi->CreateInstance(HELPER_CLASS_ID, BHKLISTOBJECT_CLASS_ID); + if (listObj != NULL) + { + if (bhkRigidBodyInterface *irb = (bhkRigidBodyInterface *)listObj->GetInterface(BHKRIGIDBODYINTERFACE_DESC)) + { + body->SetLayer(lyr); + //body->SetLayerCopy(lyr); + body->SetMotionSystem(msys); + body->SetQualityType(qtype); + body->SetMass(mass); + body->SetLinearDamping(lindamp); + body->SetAngularDamping(angdamp); + body->SetFriction(frict); + body->SetRestitution(resti); + body->SetMaxLinearVelocity(maxlinvel); + body->SetMaxAngularVelocity(maxangvel); + body->SetPenetrationDepth(pendepth); + body->SetCenter(center); + } + + if (INode *n = ni.gi->CreateObjectNode(listObj)) { + Point3 startPos(0.0,0.0,0.0); + Quat q; q.Identity(); + PosRotScaleNode(n, startPos, q, 1.0f, prsPos); + + rbody = n; + } + } + + //npSetCollision(node, true); + return rbody; +} + +void CollisionImport::AddShape(INode *rbody, INode *shapeNode) +{ + const int PB_MESHLIST = 1; + + if (IParamBlock2* pblock2 = rbody->GetObjectRef()->GetParamBlockByID(0)) + { + int nBlocks = pblock2->Count(PB_MESHLIST); + pblock2->SetCount(PB_MESHLIST, nBlocks+1); + pblock2->SetValue(PB_MESHLIST, 0, shapeNode, nBlocks); + } +} + +bool CollisionImport::ImportBase(bhkRigidBodyRef body, bhkShapeRef shape, INode* parent, INode *shapeNode) +{ + // Now do common post processing for the node + if (shapeNode != NULL) + { + shapeNode->SetName( TSTR(shape->GetType().GetTypeName().c_str()) ); + + //ImportRigidBody(body, shapeNode); + + Point3 pos = TOPOINT3(body->GetTranslation()) / ni.bhkScaleFactor; + Quat rot = TOQUAT(body->GetRotation()); + PosRotScaleNode(shapeNode, pos, rot, 1.0, prsDefault); + + + // Wireframe Red color + StdMat2 *collMat = NewDefaultStdMat(); + collMat->SetDiffuse(Color(1.0f, 0.0f, 0.0f), 0); + collMat->SetWire(TRUE); + collMat->SetFaceted(TRUE); + ni.gi->GetMaterialLibrary().Add(collMat); + shapeNode->SetMtl(collMat); + + shapeNode->SetPrimaryVisibility(FALSE); + shapeNode->SetSecondaryVisibility(FALSE); + shapeNode->BoneAsLine(TRUE); + shapeNode->SetRenderable(FALSE); + //shapeNode->XRayMtl(TRUE); + shapeNode->SetWireColor( RGB(255,0,0) ); + parent->AttachChild(shapeNode); + return true; + } + return false; +} + +bool CollisionImport::ImportShape(INode *rbody, bhkRigidBodyRef body, bhkShapeRef shape, INode* parent) +{ + bool ok = false; + if (shape->IsDerivedType(bhkBoxShape::TYPE)) + { + ok |= ImportBox(rbody, body, bhkBoxShapeRef(shape), parent); + } + else if (shape->IsDerivedType(bhkCapsuleShape::TYPE)) + { + ok |= ImportCapsule(rbody, body, bhkCapsuleShapeRef(shape), parent); + } + else if (shape->IsDerivedType(bhkSphereShape::TYPE)) + { + ok |= ImportSphere(rbody, body, bhkSphereShapeRef(shape), parent); + } + else if (shape->IsDerivedType(bhkConvexVerticesShape::TYPE)) + { + ok |= ImportConvexVertices(rbody, body, bhkConvexVerticesShapeRef(shape), parent); + } + else if (shape->IsDerivedType(bhkNiTriStripsShape::TYPE)) + { + ok |= ImportTriStripsShape(rbody, body, bhkNiTriStripsShapeRef(shape), parent); + } + else if (shape->IsDerivedType(bhkMoppBvTreeShape::TYPE)) + { + ok |= ImportMoppBvTreeShape(rbody, body, bhkMoppBvTreeShapeRef(shape), parent); + } + else if (shape->IsDerivedType(bhkPackedNiTriStripsShape::TYPE)) + { + ok |= ImportPackedNiTriStripsShape(rbody, body, bhkPackedNiTriStripsShapeRef(shape), parent); + } + else if (shape->IsDerivedType(bhkListShape::TYPE)) + { + ok |= ImportListShape(rbody, body, bhkListShapeRef(shape), parent); + } + return ok; +} + +INode *CollisionImport::ImportCollisionMesh( + const vector<Vector3>& verts, + const vector<Triangle>& tris, + const vector<Vector3>& norms, + INode *parent + ) +{ + INode *returnNode = NULL; + if ( ImpNode *node = ni.i->CreateNode() ) + { + TriObject *triObject = CreateNewTriObject(); + node->Reference(triObject); + + Mesh& mesh = triObject->GetMesh(); + INode *tnode = node->GetINode(); + + // Vertex info + { + int nVertices = verts.size(); + mesh.setNumVerts(nVertices); + for (int i=0; i < nVertices; ++i){ + Vector3 v = verts[i] * ni.bhkScaleFactor; + mesh.verts[i].Set(v.x, v.y, v.z); + } + } + + // Triangles and texture vertices + ni.SetTriangles(mesh, tris); + ni.SetNormals(mesh, tris, norms); + + ni.i->AddNodeToScene(node); + + returnNode = node->GetINode(); + returnNode->EvalWorldState(0); + + if (parent != NULL) + parent->AttachChild(tnode, 1); + } + return returnNode; } -INode* CollisionImport::ImportSphere(bhkRigidBodyRef body, bhkSphereShapeRef shape, INode *parent) +bool CollisionImport::ImportSphere(INode *rbody, bhkRigidBodyRef body, bhkSphereShapeRef shape, INode *parent) { if (SimpleObject *ob = (SimpleObject *)ni.gi->CreateInstance(GEOMOBJECT_CLASS_ID, Class_ID(SPHERE_CLASS_ID, 0))) { float radius = shape->GetRadius(); @@ -161,13 +331,20 @@ INode* CollisionImport::ImportSphere(bhkRigidBodyRef body, bhkSphereShapeRef sha // Need to "Affect Pivot Only" and "Center to Object" first n->CenterPivot(0, FALSE); #endif - return n; + ImportBase(body, shape, parent, n); + AddShape(rbody, n); + return true; } } - return NULL; + return false; +} + +bool CollisionImport::ImportBox(INode *rbody, bhkRigidBodyRef body, bhkBoxShapeRef shape, INode *parent) +{ + return false; } -INode* CollisionImport::ImportCapsule(bhkRigidBodyRef body, bhkCapsuleShapeRef shape, INode *parent) +bool CollisionImport::ImportCapsule(INode *rbody, bhkRigidBodyRef body, bhkCapsuleShapeRef shape, INode *parent) { if (SimpleObject *ob = (SimpleObject *)ni.gi->CreateInstance(GEOMOBJECT_CLASS_ID, SCUBA_CLASS_ID)) { float radius = shape->GetRadius(); @@ -190,13 +367,120 @@ INode* CollisionImport::ImportCapsule(bhkRigidBodyRef body, bhkCapsuleShapeRef s // Need to reposition the Capsule so that caps are rotated correctly for pts given - return n; + ImportBase(body, shape, parent, n); + AddShape(rbody, n); + return true; } } - return NULL; + return true; +} + +extern vector<Triangle> compute_convex_hull(const vector<Vector3>& verts); + +bool CollisionImport::ImportConvexVertices(INode *rbody, bhkRigidBodyRef body, bhkConvexVerticesShapeRef shape, INode *parent) +{ + INode *returnNode = NULL; + vector<Vector3> verts = shape->GetVertices(); + //vector<Triangle> tris = shape->GetTriangles(); + vector<Vector3> norms = shape->GetNormals(); + vector<Triangle> tris = compute_convex_hull(verts); + returnNode = ImportCollisionMesh(verts, tris, norms, parent); + + ImportBase(body, shape, parent, returnNode); + AddShape(rbody, returnNode); + return true; +} + +bool CollisionImport::ImportTriStripsShape(INode *rbody, bhkRigidBodyRef body, bhkNiTriStripsShapeRef shape, INode *parent) +{ + if (shape->GetNumStripsData() != 1) + return NULL; + + if ( ImpNode *node = ni.i->CreateNode() ) + { + TriObject *triObject = CreateNewTriObject(); + node->Reference(triObject); + + //string name = shape->GetName(); + //node->SetName(name.c_str()); + + INode *inode = node->GetINode(); + + // Texture + Mesh& mesh = triObject->GetMesh(); + NiTriStripsDataRef triShapeData = shape->GetStripsData(0); + if (triShapeData == NULL) + return false; + + // Temporary shape + NiTriStripsRef triShape = new NiTriStrips(); + Point3 pos = TOPOINT3(body->GetTranslation()) / ni.bhkScaleFactor; + triShape->SetLocalTranslation( TOVECTOR3(pos) ); + + Quat rot = TOQUAT(body->GetRotation()); + Matrix3 mat; + rot.MakeMatrix(mat, false); + triShape->SetLocalRotation( TOMATRIX33(mat, false) ); + + + vector<Triangle> tris = triShapeData->GetTriangles(); + ni.ImportMesh(node, triObject, triShape, triShapeData, tris); + + ImportBase(body, shape, parent, inode); + AddShape(rbody, inode); + return true; + } + return false; } -INode* CollisionImport::ImportConvexVertices(bhkRigidBodyRef body, bhkConvexVerticesShapeRef shape, INode *parent) +bool CollisionImport::ImportMoppBvTreeShape(INode *rbody, bhkRigidBodyRef body, bhkMoppBvTreeShapeRef shape, INode *parent) { - return NULL; + bool retval = ImportShape(rbody, body, shape->GetShape(), parent); + //if (shapes.Count() > 0) { + // for (int i=0, n=shapes.Count(); i<n; ++i) + // { + // npSetProp(shapes[i], NP_HVK_MATERIAL, shape->GetMaterial()); + // } + //} + return retval; +} + +bool CollisionImport::ImportPackedNiTriStripsShape(INode *rbody, bhkRigidBodyRef body, bhkPackedNiTriStripsShapeRef shape, INode *parent) +{ + if (hkPackedNiTriStripsDataRef data = shape->GetData()) + { + vector<Vector3> verts = data->GetVertices(); + vector<Triangle> tris = data->GetTriangles(); + vector<Vector3> norms = data->GetNormals(); + + INode *inode = ImportCollisionMesh(verts, tris, norms, parent); + ImportBase(body, shape, parent, inode); + AddShape(rbody, inode); + return true; + } + + return false; +} + +bool CollisionImport::ImportListShape(INode *rbody, bhkRigidBodyRef body, bhkListShapeRef shape, INode *parent) +{ + bool ok = false; + HavokMaterial material = shape->GetMaterial(); + + const int PB_MATERIAL = 0; + if (IParamBlock2* pblock2 = rbody->GetObjectRef()->GetParamBlockByID(0)) + { + pblock2->SetValue(PB_MATERIAL, 0, material, 0); + } + + vector<Ref<bhkShape > > bhkshapes = shape->GetSubShapes(); + for (int i = 0, n = bhkshapes.size(); i<n; ++i) { + ok |= ImportShape(rbody, body, bhkshapes[i], parent); + //if (shapes.Count() > 0) { + // for (int i=0, n=shapes.Count(); i<n; ++i) { + // npSetProp(shapes[i], NP_HVK_MATERIAL, shape->GetMaterial()); + // } + //} + } + return ok; } \ No newline at end of file diff --git a/NifImport/ImportMeshAndSkin.cpp b/NifImport/ImportMeshAndSkin.cpp index 5f0780b..321c323 100644 --- a/NifImport/ImportMeshAndSkin.cpp +++ b/NifImport/ImportMeshAndSkin.cpp @@ -96,60 +96,8 @@ bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triG } } // Triangles and texture vertices - SetTrangles(mesh, tris); - - // Normals - { - mesh.checkNormals(TRUE); - vector<Vector3> n = triGeomData->GetNormals(); - if (n.size() > 0) - { - bool needNormals = false; - for (int i=0; i<n.size(); i++){ - Vector3 v = n[i]; - Point3 norm(v.x, v.y, v.z); - if (norm != mesh.getNormal(i)) { - needNormals = true; - break; - } - } - if (needNormals) - { -#if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 5 - mesh.SpecifyNormals(); - MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals (); - if (NULL != specNorms) - { - specNorms->ClearAndFree(); - specNorms->SetNumFaces(tris.size()); - specNorms->SetNumNormals(n.size()); - - Point3* norms = specNorms->GetNormalArray(); - for (int i=0; i<n.size(); i++){ - Vector3 v = n[i]; - norms[i] = Point3(v.x, v.y, v.z); - } - MeshNormalFace* pFaces = specNorms->GetFaceArray(); - for (int i=0; i<tris.size(); i++){ - Triangle& tri = tris[i]; - pFaces[i].SpecifyNormalID(0, tri.v1); - pFaces[i].SpecifyNormalID(1, tri.v2); - pFaces[i].SpecifyNormalID(2, tri.v3); - } -#if VERSION_3DSMAX > ((7000<<16)+(15<<8)+0) // Version 7+ - specNorms->SetAllExplicit(true); -#else - for (int i=0; i<specNorms->GetNumNormals(); ++i) { - specNorms->SetNormalExplicit(i, true); - } -#endif - specNorms->CheckNormals(); - } -#endif - } - } - } - + SetTriangles(mesh, tris); + SetNormals(mesh, tris, triGeomData->GetNormals() ); vector<Color4> cv = triGeomData->GetColors(); ImportVertexColor(tnode, o, tris, cv, 0); @@ -164,8 +112,6 @@ bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triG mesh.RemoveDegenerateFaces(); if (removeIllegalFaces) mesh.RemoveIllegalFaces(); - if (enableAutoSmooth) - mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); if (enableSkinSupport) ImportSkin(node, triGeom); @@ -188,13 +134,13 @@ bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triG return true; } -void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v) +void NifImporter::SetTriangles(Mesh& mesh, const vector<Triangle>& v) { int n = v.size(); mesh.setNumFaces(n); mesh.setNumTVFaces(n); for (int i=0; i<n; ++i) { - Triangle& t = v[i]; + const Triangle& t = v[i]; Face& f = mesh.faces[i]; f.setVerts(t.v1, t.v2, t.v3); f.Show(); @@ -204,6 +150,57 @@ void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v) } } +void NifImporter::SetNormals(Mesh& mesh, const vector<Niflib::Triangle>& tris, const vector<Niflib::Vector3>& n) +{ + mesh.checkNormals(TRUE); + if (n.size() > 0) + { + bool needNormals = false; + for (int i=0; i<n.size(); i++){ + Vector3 v = n[i]; + Point3 norm(v.x, v.y, v.z); + if (norm != mesh.getNormal(i)) { + needNormals = true; + break; + } + } + if (needNormals) + { +#if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 5 + mesh.SpecifyNormals(); + MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals (); + if (NULL != specNorms) + { + specNorms->ClearAndFree(); + specNorms->SetNumFaces(tris.size()); + specNorms->SetNumNormals(n.size()); + + Point3* norms = specNorms->GetNormalArray(); + for (int i=0; i<n.size(); i++){ + Vector3 v = n[i]; + norms[i] = Point3(v.x, v.y, v.z); + } + MeshNormalFace* pFaces = specNorms->GetFaceArray(); + for (int i=0; i<tris.size(); i++){ + const Triangle& tri = tris[i]; + pFaces[i].SpecifyNormalID(0, tri.v1); + pFaces[i].SpecifyNormalID(1, tri.v2); + pFaces[i].SpecifyNormalID(2, tri.v3); + } +#if VERSION_3DSMAX > ((7000<<16)+(15<<8)+0) // Version 7+ + specNorms->SetAllExplicit(true); +#else + for (int i=0; i<specNorms->GetNumNormals(); ++i) { + specNorms->SetNormalExplicit(i, true); + } +#endif + specNorms->CheckNormals(); + } +#endif + } + } +} + bool NifImporter::ImportMesh(NiTriShapeRef triShape) { bool ok = true; diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp index 628e43c..e56b2ea 100644 --- a/NifImport/NIFImport.cpp +++ b/NifImport/NIFImport.cpp @@ -300,6 +300,11 @@ bool NifImporter::DoImport() ok = ImportMeshes(rootNode); + // Import Havok Collision Data surrounding node + if (enableCollision) { + ImportCollision(rootNode); + } + if (importSkeleton && removeUnusedImportedBones){ vector<string> importedNodes = GetNamesOfNodes(nodes); sort(importedBones.begin(), importedBones.end()); diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h index 5fcd6a1..6931ab6 100644 --- a/NifImport/NIFImporter.h +++ b/NifImport/NIFImporter.h @@ -109,7 +109,9 @@ public: bool ImportUPB(INode *node, Niflib::NiNodeRef block); - void SetTrangles(Mesh& mesh, vector<Niflib::Triangle>& v); + void SetTriangles(Mesh& mesh, const vector<Niflib::Triangle>& v); + void SetNormals(Mesh& mesh, const vector<Niflib::Triangle>& t, const vector<Niflib::Vector3>& v); + bool ImportMesh(Niflib::NiTriShapeRef triShape); bool ImportMesh(Niflib::NiTriStripsRef triStrips); bool ImportMultipleGeometry(Niflib::NiNodeRef parent, vector<Niflib::NiTriBasedGeomRef>& glist); diff --git a/NifImport/NifDialog.cpp b/NifImport/NifDialog.cpp index be115ce..fd7781b 100644 --- a/NifImport/NifDialog.cpp +++ b/NifImport/NifDialog.cpp @@ -43,6 +43,7 @@ static INT_PTR CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM CheckDlgButton(hWnd, IDC_CHK_SKIN, imp->enableSkinSupport); CheckDlgButton(hWnd, IDC_CHK_VCOLORS, imp->vertexColorMode); CheckDlgButton(hWnd, IDC_CHK_COLL, imp->enableCollision); + EnableWindow(GetDlgItem(hWnd, IDC_CHK_COLL), TRUE); CheckDlgButton(hWnd, IDC_CHK_ANIMATION, imp->enableAnimations); CheckDlgButton(hWnd, IDC_CHK_LIGHTS, imp->enableLights); CheckDlgButton(hWnd, IDC_CHK_CAMERA, imp->enableCameras); diff --git a/NifPlugins/DllEntry.cpp b/NifPlugins/DllEntry.cpp index 031207b..ebec13f 100644 --- a/NifPlugins/DllEntry.cpp +++ b/NifPlugins/DllEntry.cpp @@ -21,6 +21,8 @@ extern ClassDesc2* GetbhkCapsuleDesc(); extern ClassDesc2* GetbhkRigidBodyModifierDesc(); extern ClassDesc2* GetbhkBoxDesc(); extern ClassDesc* GetDDSLibClassDesc(); +extern ClassDesc2* GetbhkListObjDesc(); + enum ClassDescType { @@ -85,6 +87,7 @@ void InitializeLibSettings() if ( GetIniValue<bool>("NifProps", "Enable", true, iniName) ) { classDescEnabled[CD_Props] = true; classDescriptions[nClasses++] = GetNifPropsDesc(); + classDescriptions[nClasses++] = GetbhkListObjDesc(); #ifdef USE_UNSUPPORTED_CODE classDescriptions[nClasses++] = GetbhkRigidBodyModifierDesc(); classDescriptions[nClasses++] = GetbhkSphereDesc(); diff --git a/NifPlugins_VC80.sln b/NifPlugins_VC80.sln index f036cd8..6cc5564 100644 --- a/NifPlugins_VC80.sln +++ b/NifPlugins_VC80.sln @@ -6,6 +6,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject MaxNifPlugins_Readme.txt = MaxNifPlugins_Readme.txt MaxNifTools.ini = MaxNifTools.ini + MaxNifTools.iss = MaxNifTools.iss ..\docsys\nif.xml = ..\docsys\nif.xml NifPlugins_Development_Readme.txt = NifPlugins_Development_Readme.txt EndProjectSection diff --git a/NifPlugins_VC80.vcproj b/NifPlugins_VC80.vcproj index a61ae49..1ae10ba 100644 --- a/NifPlugins_VC80.vcproj +++ b/NifPlugins_VC80.vcproj @@ -3377,6 +3377,10 @@ RelativePath=".\NifProps\bhkCapsuleObj.cpp" > </File> + <File + RelativePath=".\NifProps\bhkListObj.cpp" + > + </File> <File RelativePath=".\NifProps\bhkRigidBodyInterface.cpp" > @@ -5105,6 +5109,10 @@ RelativePath=".\NifCommon\NifPlugins.cpp" > </File> + <File + RelativePath=".\NifCommon\NifQHull.cpp" + > + </File> <File RelativePath=".\NifCommon\niutils.cpp" > @@ -5114,6 +5122,1826 @@ Name="Resource Files" > </Filter> + <Filter + Name="qhull" + > + <File + RelativePath=".\NifCommon\qhull\geom.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\geom.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\geom2.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\global.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\io.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\io.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\mem.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\mem.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\merge.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\merge.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\poly.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\poly.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\poly2.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\qhull.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\qhull.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\qhull_a.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\qset.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\qset.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\stat.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\stat.h" + > + </File> + <File + RelativePath=".\NifCommon\qhull\user.c" + > + <FileConfiguration + Name="Release - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 6|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 7|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 8|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 5|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 9|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - gmax|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Debug - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release - Max 4.2|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\NifCommon\qhull\user.h" + > + </File> + </Filter> </Filter> <Filter Name="NifPlugins" diff --git a/NifProps/NifProps.rc b/NifProps/NifProps.rc index 97cd66f..fe76e91 100755 --- a/NifProps/NifProps.rc +++ b/NifProps/NifProps.rc @@ -218,10 +218,12 @@ BEGIN CONTROL "Proxy Mesh",IDC_RDO_PROXY_MESH,"Button",BS_AUTORADIOBUTTON,9,58,80,10 END -IDD_BOXPARAM1 DIALOGEX 0, 0, 107, 71 +IDD_BOXPARAM1 DIALOGEX 0, 0, 107, 69 STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN + COMBOBOX IDC_CB_MATERIAL,12,13,83,157,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Material",IDC_LBL_MATERIAL,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,12,4,83,8 CONTROL "",IDC_LENGTHEDIT,"CustEdit",WS_TABSTOP,50,29,36,10 CONTROL "",IDC_WIDTHEDIT,"CustEdit",WS_TABSTOP,50,42,36,10 CONTROL "",IDC_HEIGHTEDIT,"CustEdit",WS_TABSTOP,50,55,36,10 @@ -231,8 +233,6 @@ BEGIN RTEXT "Length:",IDC_STATIC,24,29,25,8 RTEXT "Height:",IDC_STATIC,25,55,24,8 RTEXT "Width:",IDC_STATIC,26,42,23,8 - COMBOBOX IDC_CB_MATERIAL,12,13,83,157,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "Material",IDC_LBL_MATERIAL,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,12,4,83,8 END IDD_RB_MOD_PANEL1 DIALOGEX 0, 0, 107, 15 @@ -266,6 +266,18 @@ BEGIN LTEXT "Collision Meshes:",IDC_STATIC,7,2,77,8 END +IDD_LISTPARAM DIALOGEX 0, 0, 107, 133 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + COMBOBOX IDC_CB_MATERIAL,12,13,83,157,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Material",IDC_LBL_MATERIAL,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,12,4,83,8 + LISTBOX IDC_LIST1,7,39,94,71,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + CONTROL "Add",IDC_ADD,"CustButton",WS_TABSTOP,7,113,47,12 + CONTROL "Remove",IDC_REMOVE,"CustButton",WS_TABSTOP,57,113,46,12 + LTEXT "Collision Meshes:",IDC_STATIC,9,28,77,8 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -308,7 +320,13 @@ BEGIN IDD_BOXPARAM1, DIALOG BEGIN RIGHTMARGIN, 106 - BOTTOMMARGIN, 46 + BOTTOMMARGIN, 67 + END + + IDD_LISTPARAM, DIALOG + BEGIN + RIGHTMARGIN, 106 + BOTTOMMARGIN, 131 END END #endif // APSTUDIO_INVOKED @@ -404,10 +422,10 @@ BEGIN 0x7053, 0x6c65, 0x006c, IDC_CB_LAYER, 0x403, 6, 0 0x6942, 0x6570, 0x0064, - IDC_CB_LAYER, 0x403, 5, 0 -0x7254, 0x6565, "\000" - IDC_CB_LAYER, 0x403, 5, 0 -0x7250, 0x706f, "\000" + IDC_CB_LAYER, 0x403, 6, 0 +0x7254, 0x6565, 0x0073, + IDC_CB_LAYER, 0x403, 6, 0 +0x7250, 0x706f, 0x0073, IDC_CB_LAYER, 0x403, 6, 0 0x6157, 0x6574, 0x0072, IDC_CB_LAYER, 0x403, 8, 0 @@ -832,6 +850,76 @@ BEGIN 0 END +IDD_LISTPARAM DLGINIT +BEGIN + IDC_CB_MATERIAL, 0x403, 6, 0 +0x7453, 0x6e6f, 0x0065, + IDC_CB_MATERIAL, 0x403, 6, 0 +0x6c43, 0x746f, 0x0068, + IDC_CB_MATERIAL, 0x403, 5, 0 +0x6944, 0x7472, "\000" + IDC_CB_MATERIAL, 0x403, 6, 0 +0x6c47, 0x7361, 0x0073, + IDC_CB_MATERIAL, 0x403, 6, 0 +0x7247, 0x7361, 0x0073, + IDC_CB_MATERIAL, 0x403, 6, 0 +0x654d, 0x6174, 0x006c, + IDC_CB_MATERIAL, 0x403, 8, 0 +0x724f, 0x6167, 0x696e, 0x0063, + IDC_CB_MATERIAL, 0x403, 5, 0 +0x6b53, 0x6e69, "\000" + IDC_CB_MATERIAL, 0x403, 6, 0 +0x6157, 0x6574, 0x0072, + IDC_CB_MATERIAL, 0x403, 5, 0 +0x6f57, 0x646f, "\000" + IDC_CB_MATERIAL, 0x403, 12, 0 +0x6548, 0x7661, 0x2079, 0x7453, 0x6e6f, 0x0065, + IDC_CB_MATERIAL, 0x403, 12, 0 +0x6548, 0x7661, 0x2079, 0x654d, 0x6174, 0x006c, + IDC_CB_MATERIAL, 0x403, 11, 0 +0x6548, 0x7661, 0x2079, 0x6f57, 0x646f, "\000" + IDC_CB_MATERIAL, 0x403, 6, 0 +0x6843, 0x6961, 0x006e, + IDC_CB_MATERIAL, 0x403, 5, 0 +0x6e53, 0x776f, "\000" + IDC_CB_MATERIAL, 0x403, 13, 0 +0x7453, 0x6e6f, 0x2065, 0x7453, 0x6961, 0x7372, "\000" + IDC_CB_MATERIAL, 0x403, 13, 0 +0x6c43, 0x746f, 0x2068, 0x7453, 0x6961, 0x7372, "\000" + IDC_CB_MATERIAL, 0x403, 12, 0 +0x6944, 0x7472, 0x5320, 0x6174, 0x7269, 0x0073, + IDC_CB_MATERIAL, 0x403, 13, 0 +0x6c47, 0x7361, 0x2073, 0x7453, 0x6961, 0x7372, "\000" + IDC_CB_MATERIAL, 0x403, 13, 0 +0x7247, 0x7361, 0x2073, 0x7453, 0x6961, 0x7372, "\000" + IDC_CB_MATERIAL, 0x403, 13, 0 +0x654d, 0x6174, 0x206c, 0x7453, 0x6961, 0x7372, "\000" + IDC_CB_MATERIAL, 0x403, 15, 0 +0x724f, 0x6167, 0x696e, 0x2063, 0x7453, 0x6961, 0x7372, "\000" + IDC_CB_MATERIAL, 0x403, 12, 0 +0x6b53, 0x6e69, 0x5320, 0x6174, 0x7269, 0x0073, + IDC_CB_MATERIAL, 0x403, 13, 0 +0x6157, 0x6574, 0x2072, 0x7453, 0x6961, 0x7372, "\000" + IDC_CB_MATERIAL, 0x403, 12, 0 +0x6f57, 0x646f, 0x5320, 0x6174, 0x7269, 0x0073, + IDC_CB_MATERIAL, 0x403, 19, 0 +0x6548, 0x7661, 0x2079, 0x7453, 0x6e6f, 0x2065, 0x7453, 0x6961, 0x7372, +"\000" + IDC_CB_MATERIAL, 0x403, 19, 0 +0x6548, 0x7661, 0x2079, 0x654d, 0x6174, 0x206c, 0x7453, 0x6961, 0x7372, +"\000" + IDC_CB_MATERIAL, 0x403, 18, 0 +0x6548, 0x7661, 0x2079, 0x6f57, 0x646f, 0x5320, 0x6174, 0x7269, 0x0073, + + IDC_CB_MATERIAL, 0x403, 13, 0 +0x6843, 0x6961, 0x206e, 0x7453, 0x6961, 0x7372, "\000" + IDC_CB_MATERIAL, 0x403, 12, 0 +0x6e53, 0x776f, 0x5320, 0x6174, 0x7269, 0x0073, + IDC_CB_MATERIAL, 0x403, 9, 0 +0x6c45, 0x7665, 0x7461, 0x726f, "\000" + 0 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -903,6 +991,8 @@ BEGIN IDS_RB_MOD_PANEL2 "Sphere" IDS_RB_MOD_PANEL3 "Capsule" IDS_RB_MOD_PANEL4 "Proxy Mesh" + IDS_RB_LIST "bhkListShape" + IDS_RB_LIST_CLASS "bhkListShape" END #endif // English (U.S.) resources diff --git a/NifProps/bhkListObj.cpp b/NifProps/bhkListObj.cpp new file mode 100644 index 0000000..a572cce --- /dev/null +++ b/NifProps/bhkListObj.cpp @@ -0,0 +1,622 @@ +/********************************************************************** +*< +FILE: bhkListObj.cpp + +DESCRIPTION: Collision List Object Implementation + +CREATED BY: tazpn (Theo) + +HISTORY: + V1.0 - Derived from 3ds max prim box example + +*> Copyright (c) 2006, All Rights Reserved. +**********************************************************************/ +#pragma warning( disable:4800 ) +#include <max.h> +#include "MAX_Mem.h" +#include <map> +#include "NifProps.h" +#include "iparamm.h" +#include "Simpobj.h" +#include "surf_api.h" +#include "notify.h" +#include "macroRec.h" +#include "bhkRigidBodyInterface.h" +#include "NifGui.h" +#include "NifStrings.h" + +#ifndef _countof +#define _countof(x) (sizeof(x)/sizeof((x)[0])) +#endif + +Class_ID BHKLISTOBJECT_CLASS_ID = Class_ID(0x236508a2, BHKRIGIDBODYCLASS_DESC.PartB()); +class ListPickObjectMode; + +class bhkListValidatorClass : public PBValidator +{ +public: + class bhkListObject *mod; +private: + BOOL Validate(PB2Value &v) + { + INode *node = (INode*) v.r; + if (node->TestForLoop(FOREVER,(ReferenceMaker *) mod)!=REF_SUCCEED) return FALSE; + + ObjectState os = node->EvalWorldState(0); + //Allow only tri object derived objects + if (os.obj->CanConvertToType(triObjectClassID) && os.obj->SuperClassID() != SHAPE_CLASS_ID) { + return TRUE; + } + // and out objects which support the RigidBodyInterface + //if (os.obj->SuperClassID() == HELPER_CLASS_ID && NULL != GetInterface(BHKRIGIDBODYINTERFACE_DESC)) { + // return TRUE; + //} + if (os.obj->SuperClassID() == HELPER_CLASS_ID && os.obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB() ) { + return TRUE; + } + return FALSE; + }; +}; + +class bhkListObject : public SimpleObject2, public bhkRigidBodyIfcHelper +{ +public: + // Class vars + IParamMap2 *pmapParam; + IObjParam *ip; + ListPickObjectMode *pickObMode; + IParamMap2 *pbvParams[1]; //proxy + Interface *mIP; + bhkListValidatorClass validator; + ICustButton *iPickButton; + + bhkListObject(BOOL loading); + ~bhkListObject(); + + // From Object + int CanConvertToType(Class_ID obtype); + Object* ConvertToType(TimeValue t, Class_ID obtype); + void GetCollapseTypes(Tab<Class_ID> &clist,Tab<TSTR*> &nlist); + + CreateMouseCallBack* GetCreateMouseCallBack(); + void BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev); + void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next); + RefTargetHandle Clone(RemapDir& remap); + TCHAR *GetObjectName() { return GetString(IDS_RB_LIST); } + + int NumParamBlocks() { return 1; } // return number of ParamBlocks in this instance + IParamBlock2* GetParamBlock(int i) { return pblock2; } // return i'th ParamBlock + IParamBlock2* GetParamBlockByID(BlockID id) { return (pblock2->ID() == id) ? pblock2 : NULL; } // return id'd ParamBlock + + // Animatable methods + void DeleteThis() {delete this;} + Class_ID ClassID() { return BHKLISTOBJECT_CLASS_ID; } + SClass_ID SuperClassID() { return HELPER_CLASS_ID; } + + // From SimpleObject + void BuildMesh(TimeValue t); + BOOL OKtoDisplay(TimeValue t); + void InvalidateUI(); + + int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags); + //int HitTest(TimeValue t, INode *inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt); + + void UpdateUI(); + + BaseInterface *GetInterface(Interface_ID id) { + if (id == BHKRIGIDBODYINTERFACE_DESC) + return this; + return SimpleObject2::GetInterface(id); + } +}; + +//--- ClassDescriptor and class vars --------------------------------- + +// The class descriptor for box +class bhkListObjClassDesc : public ClassDesc2 +{ +public: + bhkListObjClassDesc(); + int IsPublic() { return 1; } + void * Create(BOOL loading = FALSE) { + return new bhkListObject(loading); + } + const TCHAR * ClassName() { return GetString(IDS_RB_LIST_CLASS); } + SClass_ID SuperClassID() { return HELPER_CLASS_ID; } + Class_ID ClassID() { return BHKLISTOBJECT_CLASS_ID; } + const TCHAR* Category() { return "NifTools"; } + + const TCHAR* InternalName() { return _T("bhkListShape"); } // returns fixed parsable name (scripter-visible name) + HINSTANCE HInstance() { return hInstance; } // returns owning module handle +}; + +extern ClassDesc2* GetbhkListObjDesc(); + +// in prim.cpp - The dll instance handle +extern HINSTANCE hInstance; + +//--- Parameter map/block descriptors ------------------------------- + +// Parameter and ParamBlock IDs +enum { list_params, bv_mesh, }; // pblock2 ID +enum +{ + PB_MATERIAL, + PB_MESHLIST, +}; + +enum { list_params_panel, }; + +static ParamBlockDesc2 param_blk ( + list_params, _T("parameters"), 0, NULL, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, 0, + //rollout + 1, + list_params, IDD_LISTPARAM, IDS_PARAMS, 0, 0, NULL, + + // params + PB_MATERIAL, _T("material"), TYPE_INT, P_ANIMATABLE, IDS_DS_MATERIAL, + p_default, NP_DEFAULT_HVK_MATERIAL, + end, + + PB_MESHLIST, _T("meshList"), TYPE_INODE_TAB, 0, P_AUTO_UI|P_VARIABLE_SIZE, IDS_MESHLIST, + p_ui, list_params, TYPE_NODELISTBOX, IDC_LIST1,IDC_ADD,0,IDC_REMOVE, + end, + + end + ); + +// bug in pb desc? forces us to use this rather than in inline version +static bhkListObjClassDesc listDesc; +extern ClassDesc2* GetbhkListObjDesc() { return &listDesc; } +bhkListObjClassDesc::bhkListObjClassDesc() { + param_blk.SetClassDesc(this); +} + + +class ListPickObjectMode : + public PickModeCallback, + public PickNodeCallback { +public: + bhkListObject *mod; + + BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags); + BOOL Pick(IObjParam *ip,ViewExp *vpt); + void EnterMode(IObjParam *ip); + void ExitMode(IObjParam *ip); + BOOL RightClick(IObjParam *ip,ViewExp *vpt) {return TRUE;} + BOOL Filter(INode *node); + PickNodeCallback *GetFilter() {return this;} +}; + +//--- ListPickObjectMode ------------------------------------------------ + +BOOL ListPickObjectMode::Filter(INode *node) +{ + if (node) { + node->BeginDependencyTest(); + mod->NotifyDependents(FOREVER,0,REFMSG_TEST_DEPENDENCY); + if (node->EndDependencyTest()) { + return FALSE; + } + ////added code for looptest + //if (node->TestForLoop(FOREVER,(ReferenceMaker *) mod)!=REF_SUCCEED) + // return FALSE; + + for (int i = 0;i < mod->pblock2->Count(PB_MESHLIST); i++) { + INode *tnode = NULL; + mod->pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i); + if (node == tnode) + return FALSE; + } + + ObjectState os = node->EvalWorldState(0); + //added code such that lines are not selected + if ( (os.obj->IsSubClassOf(triObjectClassID) || os.obj->CanConvertToType(triObjectClassID)) + && (os.obj->SuperClassID() != SHAPE_CLASS_ID) + ) + { + return TRUE; + } + if (os.obj->SuperClassID() == HELPER_CLASS_ID && os.obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB() ) { + return TRUE; + } + } + return FALSE; +} + +BOOL ListPickObjectMode::HitTest( + IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags) +{ + INode *node = mod->mIP->PickNode(hWnd,m, this); //added "this" argument such that the Filter above is used + return node?TRUE:FALSE; +} + +BOOL ListPickObjectMode::Pick(IObjParam *ip,ViewExp *vpt) +{ + BOOL rv = FALSE; + if (INode *node = vpt->GetClosestHit()) { + theHold.Begin(); + ObjectState os = node->EvalWorldState(0); + if (os.obj->CanConvertToType(triObjectClassID)) { + mod->pblock2->Append(PB_MESHLIST,1,&node,1); + rv = TRUE; + } + theHold.Accept(GetString(IDS_ADD_MESH)); + } + return rv; +} + +void ListPickObjectMode::EnterMode(IObjParam *ip) +{mod->iPickButton->SetCheck(TRUE);} + +void ListPickObjectMode::ExitMode(IObjParam *ip) +{mod->iPickButton->SetCheck(FALSE);} + +static ListPickObjectMode thePickMode; + + + +class ListParamDlgProc : public ParamMap2UserDlgProc { +public: + bhkListObject *so; + HWND thishWnd; + NpComboBox mCbMaterial; + + ListParamDlgProc(bhkListObject *s) {so=s;thishWnd=NULL;} + INT_PTR DlgProc(TimeValue t,IParamMap2 *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam); + void Update(TimeValue t); + void DeleteThis() {delete this;} + + //--- ParamDlgProc -------------------------------- + void TurnSpinner(HWND hWnd,int SpinNum,BOOL ison) + { + ISpinnerControl *spin2 = GetISpinner(GetDlgItem(hWnd,SpinNum)); + if (ison) spin2->Enable();else spin2->Disable(); + ReleaseISpinner(spin2); + }; + +}; + +void ListParamDlgProc::Update(TimeValue t) +{ + if (!thishWnd) + return; + return; +} + +INT_PTR ListParamDlgProc::DlgProc(TimeValue t,IParamMap2 *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) +{ + thishWnd=hWnd; + switch (msg) + { + case WM_INITDIALOG: + { + mCbMaterial.init(GetDlgItem(hWnd, IDC_CB_MATERIAL)); + for (const char **str = NpHvkMaterialNames; *str; ++str) + mCbMaterial.add(*str); + + int sel = NP_DEFAULT_HVK_MATERIAL; + Interval valid; + so->pblock2->GetValue( PB_MATERIAL, 0, sel, valid); + mCbMaterial.select( sel ); + + Update(t); + break; + } + case WM_DESTROY: + if (so && so->iPickButton != NULL) { + ReleaseICustButton(so->iPickButton); + so->iPickButton = NULL; + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_CB_MATERIAL: + if (HIWORD(wParam)==CBN_SELCHANGE) { + so->pblock2->SetValue( PB_MATERIAL, 0, mCbMaterial.selection() ); + } + break; + } + break; + } + return FALSE; +} +//--- List methods ------------------------------- + + +bhkListObject::bhkListObject(BOOL loading) +{ + pmapParam = NULL; + ip = NULL; + pickObMode = NULL; + pbvParams[0] = NULL; + mIP = NULL; + iPickButton = NULL; + validator.mod = this; + + SetAFlag(A_PLUGIN1); + listDesc.MakeAutoParamBlocks(this); + assert(pblock2); +} + +bhkListObject::~bhkListObject() +{ + param_blk.SetUserDlgProc(); + if (pmapParam) { + pmapParam = NULL; + } +} + +void bhkListObject::BeginEditParams(IObjParam *ip,ULONG flags,Animatable *prev) +{ + SimpleObject::BeginEditParams(ip,flags,prev); + mIP = ip; + + //if (pmapParam == NULL) { + // pmapParam = CreateCPParamMap2( + // 0, + // pblock2, + // GetCOREInterface(), + // hInstance, + // MAKEINTRESOURCE(IDD_LISTPARAM), + // GetString(IDS_PARAMS), + // 0); + //} + listDesc.BeginEditParams(ip,this,flags,prev); + param_blk.SetUserDlgProc(new ListParamDlgProc(this)); + pmapParam = pblock2->GetMap(list_params); + + BeginEditRBParams(ip, flags, prev); + + //pmapParam->GetIRollup()->Hide(1); + + + this->ip = ip; + + //if(pmapParam) { + // // A callback for the type in. + // pmapParam->SetUserDlgProc(new ListParamDlgProc(this)); + //} + +} + +void bhkListObject::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next ) +{ + param_blk.SetUserDlgProc(); + + SimpleObject::EndEditParams(ip,flags,next); + this->ip = NULL; + pmapParam = NULL; + //if (pmapParam && flags&END_EDIT_REMOVEUI ) { + // DestroyCPParamMap2 (pmapParam); + // pmapParam = NULL; + //} + + if (iPickButton != NULL) { + ReleaseICustButton(iPickButton); + iPickButton = NULL; + } + + // tear down the appropriate auto-rollouts + listDesc.EndEditParams(ip, this, flags, next); + EndEditRBParams(ip, flags, next); + mIP = NULL; +} + +void bhkListObject::UpdateUI() +{ + if (ip == NULL) + return; + ListParamDlgProc* dlg = static_cast<ListParamDlgProc*>(pmapParam->GetUserDlgProc()); + dlg->Update(ip->GetTime()); +} + +enum +{ + POSX = 0, // right + POSY = 1, // back + POSZ = 2, // top + NEGX = 3, // left + NEGY = 4, // front + NEGZ = 5, // bottom +}; + +void bhkListObject::BuildMesh(TimeValue t) +{ + extern void BuildBox(Mesh&mesh, float l, float w, float h); + + ivalid = FOREVER; + BuildBox(mesh, 10.0f, 10.0f, 10.0f); +} + +Object* bhkListObject::ConvertToType(TimeValue t, Class_ID obtype) +{ + return 0; + //return SimpleObject::ConvertToType(t,obtype); +} + +int bhkListObject::CanConvertToType(Class_ID obtype) +{ + return 0; +} + + +void bhkListObject::GetCollapseTypes(Tab<Class_ID> &clist,Tab<TSTR*> &nlist) +{ + Object::GetCollapseTypes(clist, nlist); +} + +class ListObjCreateCallBack: public CreateMouseCallBack { + bhkListObject *ob; + Point3 p0,p1; + IPoint2 sp0, sp1; + BOOL square; +public: + int proc( ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat ); + void SetObj(bhkListObject *obj) { ob = obj; } +}; + +int ListObjCreateCallBack::proc(ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat ) { + Point3 d; + if (msg == MOUSE_FREEMOVE) + { + vpt->SnapPreview(m,m,NULL, SNAP_IN_3D); + } + + else if (msg==MOUSE_POINT||msg==MOUSE_MOVE) { + switch(point) { + case 0: + // Find the node and plug in the wire color + { + ULONG handle; + ob->NotifyDependents(FOREVER, (PartID)&handle, REFMSG_GET_NODE_HANDLE); + INode *node = GetCOREInterface()->GetINodeByHandle(handle); + if (node) node->SetWireColor(RGB(255, 0, 0)); + } + + sp0 = m; + ob->suspendSnap = TRUE; + p0 = vpt->SnapPoint(m,m,NULL,SNAP_IN_3D); + p1 = p0 + Point3(.01,.01,.01); + mat.SetTrans(float(.5)*(p0+p1)); + + ob->pmapParam->Invalidate(); + + if (msg==MOUSE_POINT) + { + ob->suspendSnap = FALSE; + return CREATE_STOP; + } + break; + } + } + else + if (msg == MOUSE_ABORT) { + return CREATE_ABORT; + } + + return TRUE; +} + +static ListObjCreateCallBack listCreateCB; + +CreateMouseCallBack* bhkListObject::GetCreateMouseCallBack() +{ + listCreateCB.SetObj(this); + return(&listCreateCB); +} + + +BOOL bhkListObject::OKtoDisplay(TimeValue t) +{ + return TRUE; +} + +void bhkListObject::InvalidateUI() +{ + param_blk.InvalidateUI(pblock2->LastNotifyParamID()); + if (pmapParam) pmapParam->Invalidate(); +} + +RefTargetHandle bhkListObject::Clone(RemapDir& remap) +{ + bhkListObject* newob = new bhkListObject(FALSE); + newob->ReplaceReference(0,remap.CloneRef(pblock2)); + newob->ivalid.SetEmpty(); + BaseClone(this, newob, remap); + return(newob); +} + +int bhkListObject::Display(TimeValue t, INode* inode, ViewExp *vpt, int flags) +{ + Matrix3 m; + Color color = Color(inode->GetWireColor()); + GraphicsWindow *gw = vpt->getGW(); + Material *mtl = gw->getMaterial(); + m = inode->GetObjectTM(t); + gw->setTransform(m); + DWORD rlim = gw->getRndLimits(); + + DWORD newrlim = GW_WIREFRAME|GW_Z_BUFFER; +#if VERSION_3DSMAX >= ((5000<<16)+(15<<8)+0) // Version 5+ + newrlim |= GW_EDGES_ONLY; +#endif + gw->setRndLimits(newrlim); + + if (inode->Selected()) + gw->setColor( LINE_COLOR, GetSelColor()); + else if(!inode->IsFrozen() && !inode->Dependent()) + gw->setColor( LINE_COLOR, color); + + Matrix3 m3(true); + + float size = 20.0f; + Point3 pts[5]; + // X + pts[0] = Point3(-size, 0.0f, 0.0f); pts[1] = Point3(size, 0.0f, 0.0f); + vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL); + + // Y + pts[0] = Point3(0.0f, -size, 0.0f); pts[1] = Point3(0.0f, size, 0.0f); + vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL); + + // Z + pts[0] = Point3(0.0f, 0.0f, -size); pts[1] = Point3(0.0f, 0.0f, size); + vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL); + + //UpdateMesh(t); + //mesh.render( gw, mtl, NULL, COMP_ALL); + gw->setRndLimits(rlim); + return 0; +} + +#if 0 +int bhkListObject::HitTest(TimeValue t, INode *inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt) +{ + Matrix3 tm(1); + HitRegion hitRegion; + DWORD savedLimits; + Point3 pt(0,0,0); + + vpt->getGW()->setTransform(tm); + GraphicsWindow *gw = vpt->getGW(); + Material *mtl = gw->getMaterial(); + + tm = inode->GetObjectTM(t); + MakeHitRegion(hitRegion, type, crossing, 4, p); + + gw->setRndLimits(((savedLimits = gw->getRndLimits())|GW_PICK)&~GW_ILLUM); + gw->setHitRegion(&hitRegion); + gw->clearHitCode(); + +// DrawAndHit(t, inode, vpt); + float size = 20.0f; + Point3 pts[5]; + // X + pts[0] = Point3(-size, 0.0f, 0.0f); pts[1] = Point3(size, 0.0f, 0.0f); + vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL); + + // Y + pts[0] = Point3(0.0f, -size, 0.0f); pts[1] = Point3(0.0f, size, 0.0f); + vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL); + + // Z + pts[0] = Point3(0.0f, 0.0f, -size); pts[1] = Point3(0.0f, 0.0f, size); + vpt->getGW()->polyline(2, pts, NULL, NULL, FALSE, NULL); + + + gw->setRndLimits(savedLimits); + + // CAL-08/27/03: This doesn't make sense. It shouldn't do this. (Defect #468271) + // This will always select this helper when there's an intersection on the bounding box and the selection window. + // TODO: There's still a problem with window selection. We need to check if it hits all components in DrawAndHit. + /* + if((hitRegion.type != POINT_RGN) && !hitRegion.crossing) + return TRUE; + */ + + return gw->checkHitCode(); +} +#endif \ No newline at end of file diff --git a/NifProps/bhkRigidBodyModifer.cpp b/NifProps/bhkRigidBodyModifer.cpp index 45a5e81..c15b62d 100644 --- a/NifProps/bhkRigidBodyModifer.cpp +++ b/NifProps/bhkRigidBodyModifer.cpp @@ -101,9 +101,9 @@ private: return TRUE; } // and out objects which support the RigidBodyInterface - //if (os.obj->SuperClassID() == HELPER_CLASS_ID && NULL != GetInterface(BHKRIGIDBODYINTERFACE_DESC)) { - // return TRUE; - //} + if (os.obj->SuperClassID() == HELPER_CLASS_ID && NULL != GetInterface(BHKRIGIDBODYINTERFACE_DESC)) { + return TRUE; + } if (os.obj->SuperClassID() == HELPER_CLASS_ID && os.obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB() ) { return TRUE; } diff --git a/NifProps/resource.h b/NifProps/resource.h index 16b569e..ee8c2e5 100755 --- a/NifProps/resource.h +++ b/NifProps/resource.h @@ -77,6 +77,7 @@ #define IDD_RB_MOD_PANEL4 11011 #define IDC_LBL_CENTER 11012 #define IDS_DS_MASS 11012 +#define IDD_LISTPARAM 11012 #define IDC_LBL_POS2 11013 #define IDS_DS_FRICTION 11013 #define IDS_DS_RESTITUTION 11014 @@ -117,6 +118,8 @@ #define IDS_RB_MOD_PANEL2 11049 #define IDS_RB_MOD_PANEL3 11050 #define IDS_RB_MOD_PANEL4 11051 +#define IDS_RB_LIST 11052 +#define IDS_RB_LIST_CLASS 11053 #define IDC_ED_CENTER_X 11490 #define IDC_SP_CENTER_X 11491 #define IDC_ED_CENTER_Y 11492 -- GitLab