vertexenum (empty) → 0.1.0.0
raw patch · 42 files changed
+28167/−0 lines, 42 filesdep +basedep +containersdep +hmatrix-glpksetup-changed
Dependencies added: base, containers, hmatrix-glpk, tasty, tasty-hunit, vector-space, vertexenum
Files
- C/geom2_r.c +2096/−0
- C/geom_r.c +1234/−0
- C/geom_r.h +184/−0
- C/global_r.c +2100/−0
- C/halfspaces.c +75/−0
- C/io_r.c +4062/−0
- C/io_r.h +167/−0
- C/libqhull_r.c +1403/−0
- C/libqhull_r.h +1134/−0
- C/mem_r.c +562/−0
- C/mem_r.h +234/−0
- C/merge_r.c +3627/−0
- C/merge_r.h +186/−0
- C/poly2_r.c +3222/−0
- C/poly_r.c +1205/−0
- C/poly_r.h +303/−0
- C/qhull_ra.h +158/−0
- C/qset_r.c +1340/−0
- C/qset_r.h +502/−0
- C/random_r.c +247/−0
- C/random_r.h +41/−0
- C/stat_r.c +682/−0
- C/stat_r.h +533/−0
- C/user_r.c +527/−0
- C/user_r.h +882/−0
- C/usermem_r.c +94/−0
- C/userprintf_r.c +65/−0
- C/utils.c +91/−0
- C/utils.h +20/−0
- CHANGELOG.md +5/−0
- LICENSE +674/−0
- README.md +46/−0
- Setup.hs +2/−0
- src/Geometry/VertexEnum.hs +25/−0
- src/Geometry/VertexEnum/CVertexEnum.hs +16/−0
- src/Geometry/VertexEnum/Constraint.hs +35/−0
- src/Geometry/VertexEnum/Internal.hs +87/−0
- src/Geometry/VertexEnum/LinearCombination.hs +76/−0
- src/Geometry/VertexEnum/VertexEnum.hs +87/−0
- tests/Approx.hs +11/−0
- tests/Main.hs +40/−0
- vertexenum.cabal +87/−0
+ C/geom2_r.c view
@@ -0,0 +1,2096 @@+/*<html><pre> -<a href="qh-geom_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + + geom2_r.c + infrequently used geometric routines of qhull + + see qh-geom_r.htm and geom_r.h + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/geom2_r.c#6 $$Change: 2065 $ + $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $ + + frequently used code goes into geom_r.c +*/ + +#include "qhull_ra.h" + +/*================== functions in alphabetic order ============*/ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="copypoints">-</a> + + qh_copypoints(qh, points, numpoints, dimension) + return qh_malloc'd copy of points + + notes: + qh_free the returned points to avoid a memory leak +*/ +coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension) { + int size; + coordT *newpoints; + + size= numpoints * dimension * (int)sizeof(coordT); + if (!(newpoints= (coordT*)qh_malloc((size_t)size))) { + qh_fprintf(qh, qh->ferr, 6004, "qhull error: insufficient memory to copy %d points\n", + numpoints); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */ + return newpoints; +} /* copypoints */ + +/*-<a href="qh-geom_r.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_r.htm#TOC" + >-------------------------------</a><a name="determinant">-</a> + + qh_determinant(qh, rows, dim, nearzero ) + compute signed determinant of a square matrix + uses qh.NEARzero to test for degenerate matrices + + returns: + determinant + overwrites rows and the matrix + if dim == 2 or 3 + nearzero iff determinant < qh->NEARzero[dim-1] + (!quite correct, not critical) + if dim >= 4 + nearzero iff diagonal[k] < qh->NEARzero[k] +*/ +realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero) { + realT det=0; + int i; + boolT sign= False; + + *nearzero= False; + if (dim < 2) { + qh_fprintf(qh, qh->ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n"); + qh_errexit(qh, 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) < 10*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) < 10*qh->NEARzero[2]) /* what should this be? det 5.5e-12 was flat for qh_maxsimplex of qdelaunay 0,0 27,27 -36,36 -9,63 */ + *nearzero= True; + }else { + qh_gausselim(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="detjoggle">-</a> + + qh_detjoggle(qh, 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(qhT *qh, 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_(qh, 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, qh->hull_dim, maxabs, sumabs); + joggle= distround * qh_JOGGLEdefault; + maximize_(joggle, REALepsilon * qh_JOGGLEdefault); + trace2((qh, qh->ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth)); + return joggle; +} /* detjoggle */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="detroundoff">-</a> + + qh_detroundoff(qh) + 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(qhT *qh) { + + qh_option(qh, "_max-width", NULL, &qh->MAXwidth); + if (!qh->SETroundoff) { + qh->DISTround= qh_distround(qh, qh->hull_dim, qh->MAXabs_coord, qh->MAXsumcoord); + if (qh->RANDOMdist) + qh->DISTround += qh->RANDOMfactor * qh->MAXabs_coord; + qh_option(qh, "Error-roundoff", NULL, &qh->DISTround); + } + qh->MINdenom= qh->MINdenom_1 * qh->MAXabs_coord; + qh->MINdenom_1_2= sqrt(qh->MINdenom_1 * qh->hull_dim) ; /* if will be normalized */ + qh->MINdenom_2= qh->MINdenom_1_2 * qh->MAXabs_coord; + /* for inner product */ + qh->ANGLEround= 1.01 * qh->hull_dim * REALepsilon; + if (qh->RANDOMdist) + qh->ANGLEround += qh->RANDOMfactor; + if (qh->premerge_cos < REALmax/2) { + qh->premerge_cos -= qh->ANGLEround; + if (qh->RANDOMdist) + qh_option(qh, "Angle-premerge-with-random", NULL, &qh->premerge_cos); + } + if (qh->postmerge_cos < REALmax/2) { + qh->postmerge_cos -= qh->ANGLEround; + if (qh->RANDOMdist) + qh_option(qh, "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(qh, "Centrum-premerge-with-random", NULL, &qh->premerge_centrum); + if (qh->RANDOMdist && qh->POSTmerge) + qh_option(qh, "Centrum-postmerge-with-random", NULL, &qh->postmerge_centrum); + { /* compute ONEmerge, max vertex offset for merging simplicial facets */ + realT maxangle= 1.0, maxrho; + + minimize_(maxangle, qh->premerge_cos); + minimize_(maxangle, qh->postmerge_cos); + /* max diameter * sin theta + DISTround for vertex to its hyperplane */ + qh->ONEmerge= sqrt((realT)qh->hull_dim) * qh->MAXwidth * + sqrt(1.0 - maxangle * maxangle) + qh->DISTround; + maxrho= qh->hull_dim * qh->premerge_centrum + qh->DISTround; + maximize_(qh->ONEmerge, maxrho); + maxrho= qh->hull_dim * qh->postmerge_centrum + qh->DISTround; + maximize_(qh->ONEmerge, maxrho); + if (qh->MERGING) + qh_option(qh, "_one-merge", NULL, &qh->ONEmerge); + } + qh->NEARinside= qh->ONEmerge * qh_RATIOnearinside; /* only used if qh->KEEPnearinside */ + if (qh->JOGGLEmax < REALmax/2 && (qh->KEEPcoplanar || qh->KEEPinside)) { + realT maxdist; /* adjust qh.NEARinside for joggle */ + qh->KEEPnearinside= True; + maxdist= sqrt((realT)qh->hull_dim) * qh->JOGGLEmax + qh->DISTround; + maxdist= 2*maxdist; /* vertex and coplanar point can joggle in opposite directions */ + maximize_(qh->NEARinside, maxdist); /* must agree with qh_nearcoplanar() */ + } + if (qh->KEEPnearinside) + qh_option(qh, "_near-inside", NULL, &qh->NEARinside); + if (qh->JOGGLEmax < qh->DISTround) { + qh_fprintf(qh, qh->ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n", + qh->JOGGLEmax, qh->DISTround); + qh_errexit(qh, 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(qh, "Visible-distance", NULL, &qh->MINvisible); + } + if (qh->MAXcoplanar > REALmax/2) { + qh->MAXcoplanar= qh->MINvisible; + qh_option(qh, "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(qh, "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(qh, "_wide-facet", NULL, &qh->WIDEfacet); + if (qh->MINvisible > qh->MINoutside + 3 * REALepsilon + && !qh->BESToutside && !qh->FORCEoutput) + qh_fprintf(qh, qh->ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g. Flipped facets are likely.\n", + qh->MINvisible, qh->MINoutside); + qh->max_vertex= qh->DISTround; + qh->min_vertex= -qh->DISTround; + /* numeric constants reported in printsummary */ +} /* detroundoff */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="detsimplex">-</a> + + qh_detsimplex(qh, 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(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero) { + pointT *coorda, *coordp, *gmcoord, *point, **pointp; + coordT **rows; + int k, i=0; + realT det; + + zinc_(Zdetsimplex); + gmcoord= qh->gm_matrix; + rows= qh->gm_row; + FOREACHpoint_(points) { + if (i == dim) + break; + rows[i++]= gmcoord; + coordp= point; + coorda= apex; + for (k=dim; k--; ) + *(gmcoord++)= *coordp++ - *coorda++; + } + if (i < dim) { + qh_fprintf(qh, qh->ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n", + i, dim); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + det= qh_determinant(qh, rows, dim, nearzero); + trace2((qh, qh->ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n", + det, qh_pointid(qh, apex), dim, *nearzero)); + return det; +} /* detsimplex */ + +/*-<a href="qh-geom_r.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_r.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_r.htm#TOC" + >-------------------------------</a><a name="distround">-</a> + + qh_distround(qh, 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 Golub & van Loan, 1983, Lemma 3.2-1, "Rounding Errors" + use sqrt(dim) since one vector is normalized + or use maxsumabs since one vector is < 1 +*/ +realT qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs) { + realT maxdistsum, maxround; + + maxdistsum= sqrt((realT)dimension) * maxabs; + minimize_( maxdistsum, maxsumabs); + maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs); + /* adds maxabs for offset */ + trace4((qh, qh->ferr, 4008, "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_r.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_r.htm#TOC" + >-------------------------------</a><a name="facetarea">-</a> + + qh_facetarea(qh, 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(qhT *qh, 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, 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(qh, facet); + FOREACHridge_(facet->ridges) + area += qh_facetarea_simplex(qh, qh->hull_dim, centrum, ridge->vertices, + NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset); + if (qh->CENTERtype != qh_AScentrum) + qh_memfree(qh, centrum, qh->normal_size); + } + if (facet->upperdelaunay && qh->DELAUNAY) + area= -area; /* the normal should be [0,...,1] */ + trace4((qh, qh->ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area)); + return area; +} /* facetarea */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="facetarea_simplex">-</a> + + qh_facetarea_simplex(qh, 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(qhT *qh, int dim, coordT *apex, setT *vertices, + vertexT *notvertex, boolT toporient, coordT *normal, realT *offset) { + pointT *coorda, *coordp, *gmcoord; + coordT **rows, *normalp; + int k, i=0; + realT area, dist; + vertexT *vertex, **vertexp; + boolT nearzero; + + gmcoord= qh->gm_matrix; + rows= qh->gm_row; + FOREACHvertex_(vertices) { + if (vertex == notvertex) + continue; + rows[i++]= gmcoord; + coorda= apex; + coordp= vertex->point; + normalp= normal; + if (notvertex) { + for (k=dim; k--; ) + *(gmcoord++)= *coordp++ - *coorda++; + }else { + dist= *offset; + for (k=dim; k--; ) + dist += *coordp++ * *normalp++; + if (dist < -qh->WIDEfacet) { + zinc_(Znoarea); + return 0.0; + } + coordp= vertex->point; + normalp= normal; + for (k=dim; k--; ) + *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++; + } + } + if (i != dim-1) { + qh_fprintf(qh, qh->ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n", + i, dim); + qh_errexit(qh, 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(qh, rows, dim, &nearzero); + if (toporient) + area= -area; + area *= qh->AREAfactor; + trace4((qh, qh->ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n", + area, qh_pointid(qh, apex), toporient, nearzero)); + return area; +} /* facetarea_simplex */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="facetcenter">-</a> + + qh_facetcenter(qh, 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(qhT *qh, setT *vertices) { + setT *points= qh_settemp(qh, qh_setsize(qh, vertices)); + vertexT *vertex, **vertexp; + pointT *center; + + FOREACHvertex_(vertices) + qh_setappend(qh, &points, vertex->point); + center= qh_voronoi_center(qh, qh->hull_dim-1, points); + qh_settempfree(qh, &points); + return center; +} /* facetcenter */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="findgooddist">-</a> + + qh_findgooddist(qh, 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(qhT *qh, pointT *point, facetT *facetA, realT *distp, + facetT **facetlist) { + realT bestdist= -REALmax, dist; + facetT *neighbor, **neighborp, *bestfacet=NULL, *facet; + boolT goodseen= False; + + if (facetA->good) { + zzinc_(Zcheckpart); /* calls from check_bestdist occur after print stats */ + qh_distplane(qh, point, facetA, &bestdist); + bestfacet= facetA; + goodseen= True; + } + qh_removefacet(qh, facetA); + qh_appendfacet(qh, facetA); + *facetlist= facetA; + facetA->visitid= ++qh->visit_id; + FORALLfacet_(*facetlist) { + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh->visit_id) + continue; + neighbor->visitid= qh->visit_id; + if (goodseen && !neighbor->good) + continue; + zzinc_(Zcheckpart); + qh_distplane(qh, point, neighbor, &dist); + if (dist > 0) { + qh_removefacet(qh, neighbor); + qh_appendfacet(qh, neighbor); + if (neighbor->good) { + goodseen= True; + if (dist > bestdist) { + bestdist= dist; + bestfacet= neighbor; + } + } + } + } + } + if (bestfacet) { + *distp= bestdist; + trace2((qh, qh->ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n", + qh_pointid(qh, point), bestdist, bestfacet->id)); + return bestfacet; + } + trace4((qh, qh->ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n", + qh_pointid(qh, point), facetA->id)); + return NULL; +} /* findgooddist */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getarea">-</a> + + qh_getarea(qh, facetlist ) + set area of all facets in facetlist + collect statistics + nop if hasAreaVolume + + returns: + sets qh->totarea/totvol to total area and volume of convex hull + for Delaunay triangulation, computes projected area of the lower or upper hull + ignores upper hull if qh->ATinfinity + + notes: + could compute outer volume by expanding facet area by rays from interior + the following attempt at perpendicular projection underestimated badly: + qh.totoutvol += (-dist + facet->maxoutside + qh->DISTround) + * area/ qh->hull_dim; + design: + for each facet on facetlist + compute facet->area + update qh.totarea and qh.totvol +*/ +void qh_getarea(qhT *qh, facetT *facetlist) { + realT area; + realT dist; + facetT *facet; + + if (qh->hasAreaVolume) + return; + if (qh->REPORTfreq) + qh_fprintf(qh, qh->ferr, 8020, "computing area of each facet and volume of the convex hull\n"); + else + trace1((qh, qh->ferr, 1001, "qh_getarea: computing volume and area for each facet\n")); + qh->totarea= qh->totvol= 0.0; + FORALLfacet_(facetlist) { + if (!facet->normal) + continue; + if (facet->upperdelaunay && qh->ATinfinity) + continue; + if (!facet->isarea) { + facet->f.area= qh_facetarea(qh, facet); + facet->isarea= True; + } + area= facet->f.area; + if (qh->DELAUNAY) { + if (facet->upperdelaunay == qh->UPPERdelaunay) + qh->totarea += area; + }else { + qh->totarea += area; + qh_distplane(qh, qh->interior_point, facet, &dist); + qh->totvol += -dist * area/ qh->hull_dim; + } + if (qh->PRINTstatistics) { + wadd_(Wareatot, area); + wmax_(Wareamax, area); + wmin_(Wareamin, area); + } + } + qh->hasAreaVolume= True; +} /* getarea */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="gram_schmidt">-</a> + + qh_gram_schmidt(qh, dim, row ) + implements Gram-Schmidt orthogonalization by rows + + returns: + false if zero norm + overwrites rows[dim][dim] + + notes: + see Golub & van Loan, 1983, Algorithm 6.2-2, "Modified Gram-Schmidt" + 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(qhT *qh, int dim, realT **row) { + realT *rowi, *rowj, norm; + int i, j, k; + + for (i=0; i < dim; i++) { + rowi= row[i]; + for (norm= 0.0, k= dim; k--; rowi++) + norm += *rowi * *rowi; + norm= sqrt(norm); + wmin_(Wmindenom, norm); + if (norm == 0.0) /* either 0 or overflow due to sqrt */ + return False; + for (k=dim; k--; ) + *(--rowi) /= norm; + for (j=i+1; j < dim; j++) { + rowj= row[j]; + for (norm= 0.0, k=dim; k--; ) + norm += *rowi++ * *rowj++; + for (k=dim; k--; ) + *(--rowj) -= *(--rowi) * norm; + } + } + return True; +} /* gram_schmidt */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="inthresholds">-</a> + + qh_inthresholds(qh, 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(qhT *qh, 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_r.htm#TOC" + >-------------------------------</a><a name="joggleinput">-</a> + + qh_joggleinput(qh) + 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(qhT *qh) { + int i, seed, size; + coordT *coordp, *inputp; + realT randr, randa, randb; + + if (!qh->input_points) { /* first call */ + qh->input_points= qh->first_point; + qh->input_malloc= qh->POINTSmalloc; + size= qh->num_points * qh->hull_dim * sizeof(coordT); + if (!(qh->first_point=(coordT*)qh_malloc((size_t)size))) { + qh_fprintf(qh, qh->ferr, 6009, "qhull error: insufficient memory to joggle %d points\n", + qh->num_points); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + qh->POINTSmalloc= True; + if (qh->JOGGLEmax == 0.0) { + qh->JOGGLEmax= qh_detjoggle(qh, qh->input_points, qh->num_points, qh->hull_dim); + qh_option(qh, "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(qh, "QJoggle", NULL, &qh->JOGGLEmax); + } + if (qh->build_cnt > 1 && qh->JOGGLEmax > fmax_(qh->MAXwidth/4, 0.1)) { + qh_fprintf(qh, qh->ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n", + qh->JOGGLEmax); + qh_errexit(qh, 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(qh, "_joggle-seed", &seed, NULL); + trace0((qh, qh->ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n", + qh->JOGGLEmax, seed)); + inputp= qh->input_points; + coordp= qh->first_point; + randa= 2.0 * qh->JOGGLEmax/qh_RANDOMmax; + randb= -qh->JOGGLEmax; + size= qh->num_points * qh->hull_dim; + for (i=size; i--; ) { + randr= qh_RANDOMint; + *(coordp++)= *(inputp++) + (randr * randa + randb); + } + if (qh->DELAUNAY) { + qh->last_low= qh->last_high= qh->last_newhigh= REALmax; + qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point); + } +} /* joggleinput */ + +/*-<a href="qh-geom_r.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_r.htm#TOC" + >-------------------------------</a><a name="maxmin">-</a> + + qh_maxmin(qh, 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(qhT *qh, pointT *points, int numpoints, int dimension) { + int k; + realT maxcoord, temp; + pointT *minimum, *maximum, *point, *pointtemp; + setT *set; + + qh->max_outside= 0.0; + qh->MAXabs_coord= 0.0; + qh->MAXwidth= -REALmax; + qh->MAXsumcoord= 0.0; + qh->min_vertex= 0.0; + qh->WAScoplanar= False; + if (qh->ZEROcentrum) + qh->ZEROall_ok= True; + if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax + && REALmax > 0.0 && -REALmax < 0.0) + ; /* all ok */ + else { + qh_fprintf(qh, qh->ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\ +REALepsilon %g REALmin %g REALmax %g -REALmax %g\n", + REALepsilon, REALmin, REALmax, -REALmax); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + set= qh_settemp(qh, 2*dimension); + for (k=0; k < dimension; k++) { + if (points == qh->GOODpointp) + minimum= maximum= points + dimension; + else + minimum= maximum= points; + FORALLpoint_(qh, 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(qh, &set, maximum); + qh_setappend(qh, &set, minimum); + /* calculation of qh NEARzero is based on Golub & van Loan, 1983, + Eq. 4.4-13 for "Gaussian elimination with complete pivoting". + Golub & van Loan say that 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, qh->ferr, "qh_maxmin: found the max and min points(by dim):", set); + return(set); +} /* maxmin */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="maxouter">-</a> + + qh_maxouter(qh) + 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(qhT *qh) { + realT dist; + + dist= fmax_(qh->max_outside, qh->DISTround); + dist += qh->DISTround; + trace4((qh, qh->ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh->max_outside)); + return dist; +} /* maxouter */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="maxsimplex">-</a> + + qh_maxsimplex(qh, 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(qhT *qh, 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(qh, *simplex); + if (sizinit < 2) { + if (qh_setsize(qh, maxpoints) >= 2) { + FOREACHpoint_(maxpoints) { + if (maxcoord < point[0]) { + maxcoord= point[0]; + maxx= point; + } + if (mincoord > point[0]) { + mincoord= point[0]; + minx= point; + } + } + }else { + FORALLpoint_(qh, 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(qh, simplex, minx); + if (qh_setsize(qh, *simplex) < 2) + qh_setunique(qh, simplex, maxx); + sizinit= qh_setsize(qh, *simplex); + if (sizinit < 2) { + qh_precision(qh, "input has same x coordinate"); + if (zzval_(Zsetplane) > qh->hull_dim+1) { + qh_fprintf(qh, qh->ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n", + qh_setsize(qh, maxpoints)+numpoints); + qh_errexit(qh, qh_ERRprec, NULL, NULL); + }else { + qh_fprintf(qh, qh->ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh->hull_dim); + qh_errexit(qh, 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(qh, point, *simplex, k, &nearzero); + if ((det= fabs_(det)) > maxdet) { + maxdet= det; + maxpoint= point; + maxnearzero= nearzero; + } + } + } + if (!maxpoint || maxnearzero) { + zinc_(Zsearchpoints); + if (!maxpoint) { + trace0((qh, qh->ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1)); + }else { + trace0((qh, qh->ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n", + k+1, qh_pointid(qh, maxpoint), maxdet)); + } + FORALLpoint_(qh, points, numpoints) { + if (point == qh->GOODpointp) + continue; + if (!qh_setin(*simplex, point)) { + det= qh_detsimplex(qh, point, *simplex, k, &nearzero); + if ((det= fabs_(det)) > maxdet) { + maxdet= det; + maxpoint= point; + maxnearzero= nearzero; + } + } + } + } /* !maxpoint */ + if (!maxpoint) { + qh_fprintf(qh, qh->ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_setappend(qh, simplex, maxpoint); + trace1((qh, qh->ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n", + qh_pointid(qh, maxpoint), k+1, maxdet)); + } /* k */ +} /* maxsimplex */ + +/*-<a href="qh-geom_r.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_r.htm#TOC" + >-------------------------------</a><a name="mindiff">-</a> + + qh_mindif(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="orientoutside">-</a> + + qh_orientoutside(qh, facet ) + make facet outside oriented via qh.interior_point + + returns: + True if facet reversed orientation. +*/ +boolT qh_orientoutside(qhT *qh, facetT *facet) { + int k; + realT dist; + + qh_distplane(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="outerinner">-</a> + + qh_outerinner(qh, 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), qh_check_bestdist(), qh_check_points() + + notes: + outerplaner or innerplane may be NULL + facet is const + Does not error (QhullFacet) + + includes qh.DISTround for actual points + adds another qh.DISTround if testing with floating point arithmetic +*/ +void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) { + realT dist, mindist; + vertexT *vertex, **vertexp; + + if (outerplane) { + if (!qh_MAXoutside || !facet || !qh->maxoutdone) { + *outerplane= qh_maxouter(qh); /* includes qh.DISTround */ + }else { /* qh_MAXoutside ... */ +#if qh_MAXoutside + *outerplane= facet->maxoutside + qh->DISTround; +#endif + + } + if (qh->JOGGLEmax < REALmax/2) + *outerplane += qh->JOGGLEmax * sqrt((realT)qh->hull_dim); + } + if (innerplane) { + if (facet) { + mindist= REALmax; + FOREACHvertex_(facet->vertices) { + zinc_(Zdistio); + qh_distplane(qh, vertex->point, facet, &dist); + minimize_(mindist, dist); + } + *innerplane= mindist - qh->DISTround; + }else + *innerplane= qh->min_vertex - qh->DISTround; + if (qh->JOGGLEmax < REALmax/2) + *innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim); + } +} /* outerinner */ + +/*-<a href="qh-geom_r.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(sqrt(dist)); + return dist; +} /* pointdist */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="printmatrix">-</a> + + qh_printmatrix(qh, fp, string, rows, numrow, numcol ) + print matrix to fp given by row vectors + print string as header + qh may be NULL if fp is defined + + notes: + print a vector by qh_printmatrix(qh, fp, "", &vect, 1, len) +*/ +void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol) { + realT *rowp; + realT r; /*bug fix*/ + int i,k; + + qh_fprintf(qh, fp, 9001, "%s\n", string); + for (i=0; i < numrow; i++) { + rowp= rows[i]; + for (k=0; k < numcol; k++) { + r= *rowp++; + qh_fprintf(qh, fp, 9002, "%6.3g ", r); + } + qh_fprintf(qh, fp, 9003, "\n"); + } +} /* printmatrix */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="printpoints">-</a> + + qh_printpoints(qh, fp, string, points ) + print pointids to fp for a set of points + if string, prints string and 'p' point ids +*/ +void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points) { + pointT *point, **pointp; + + if (string) { + qh_fprintf(qh, fp, 9004, "%s", string); + FOREACHpoint_(points) + qh_fprintf(qh, fp, 9005, " p%d", qh_pointid(qh, point)); + qh_fprintf(qh, fp, 9006, "\n"); + }else { + FOREACHpoint_(points) + qh_fprintf(qh, fp, 9007, " %d", qh_pointid(qh, point)); + qh_fprintf(qh, fp, 9008, "\n"); + } +} /* printpoints */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="projectinput">-</a> + + qh_projectinput(qh) + 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(qhT *qh) { + int k,i; + int newdim= qh->input_dim, newnum= qh->num_points; + signed char *project; + int projectsize= (qh->input_dim+1)*sizeof(*project); + pointT *newpoints, *coord, *infinity; + realT paraboloid, maxboloid= 0; + + project= (signed char*)qh_memalloc(qh, projectsize); + memset((char*)project, 0, (size_t)projectsize); + for (k=0; k < qh->input_dim; k++) { /* skip Delaunay bound */ + if (qh->lower_bound[k] == 0 && qh->upper_bound[k] == 0) { + project[k]= -1; + newdim--; + } + } + if (qh->DELAUNAY) { + project[k]= 1; + newdim++; + if (qh->ATinfinity) + newnum++; + } + if (newdim != qh->hull_dim) { + qh_memfree(qh, project, projectsize); + qh_fprintf(qh, qh->ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh->hull_dim); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (!(newpoints= qh->temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){ + qh_memfree(qh, project, projectsize); + qh_fprintf(qh, qh->ferr, 6016, "qhull error: insufficient memory to project %d points\n", + qh->num_points); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + /* qh_projectpoints throws error if mismatched dimensions */ + qh_projectpoints(qh, project, qh->input_dim+1, qh->first_point, + qh->num_points, qh->input_dim, newpoints, newdim); + trace1((qh, qh->ferr, 1003, "qh_projectinput: updating lower and upper_bound\n")); + qh_projectpoints(qh, project, qh->input_dim+1, qh->lower_bound, + 1, qh->input_dim+1, qh->lower_bound, newdim+1); + qh_projectpoints(qh, project, qh->input_dim+1, qh->upper_bound, + 1, qh->input_dim+1, qh->upper_bound, newdim+1); + if (qh->HALFspace) { + if (!qh->feasible_point) { + qh_memfree(qh, project, projectsize); + qh_fprintf(qh, qh->ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_projectpoints(qh, project, qh->input_dim, qh->feasible_point, + 1, qh->input_dim, qh->feasible_point, newdim); + } + qh_memfree(qh, project, projectsize); + if (qh->POINTSmalloc) + qh_free(qh->first_point); + qh->first_point= newpoints; + qh->POINTSmalloc= True; + qh->temp_malloc= NULL; + 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, qh->ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n")); + }else if (qh->DELAUNAY) /* !qh->ATinfinity */ + qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point); +} /* projectinput */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="projectpoints">-</a> + + qh_projectpoints(qh, 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(qhT *qh, signed char *project, int n, realT *points, + int numpoints, int dim, realT *newpoints, int newdim) { + int testdim= dim, oldk=0, newk=0, i,j=0,k; + realT *newp, *oldp; + + for (k=0; k < n; k++) + testdim += project[k]; + if (testdim != newdim) { + qh_fprintf(qh, qh->ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n", + newdim, testdim); + qh_errexit(qh, 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, qh->ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n", + numpoints, dim, newdim)); +} /* projectpoints */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="rotateinput">-</a> + + qh_rotateinput(qh, 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(qhT *qh, realT **rows) { + + if (!qh->POINTSmalloc) { + qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim); + qh->POINTSmalloc= True; + } + qh_rotatepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, rows); +} /* rotateinput */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="rotatepoints">-</a> + + qh_rotatepoints(qh, 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(qhT *qh, 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, 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_r.htm#TOC" + >-------------------------------</a><a name="scaleinput">-</a> + + qh_scaleinput(qh) + 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(qhT *qh) { + + if (!qh->POINTSmalloc) { + qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim); + qh->POINTSmalloc= True; + } + qh_scalepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, + qh->lower_bound, qh->upper_bound); +} /* scaleinput */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="scalelast">-</a> + + qh_scalelast(qh, 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(qhT *qh, coordT *points, int numpoints, int dim, coordT low, + coordT high, coordT newhigh) { + realT scale, shift; + coordT *coord; + int i; + boolT nearzero= False; + + trace4((qh, qh->ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n", + low, high, newhigh)); + qh->last_low= low; + qh->last_high= high; + qh->last_newhigh= newhigh; + scale= qh_divzero(newhigh, high - low, + qh->MINdenom_1, &nearzero); + if (nearzero) { + if (qh->DELAUNAY) + qh_fprintf(qh, qh->ferr, 6019, "qhull input error: can not scale last coordinate. Input is cocircular\n or cospherical. Use option 'Qz' to add a point at infinity.\n"); + else + qh_fprintf(qh, qh->ferr, 6020, "qhull input error: can not scale last coordinate. New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n", + newhigh, low, high, high-low); + qh_errexit(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="scalepoints">-</a> + + qh_scalepoints(qh, 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(qhT *qh, pointT *points, int numpoints, int dim, + realT *newlows, realT *newhighs) { + int i,k; + realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord; + boolT nearzero= False; + + for (k=0; k < dim; k++) { + newhigh= newhighs[k]; + newlow= newlows[k]; + if (newhigh > REALmax/2 && newlow < -REALmax/2) + continue; + low= REALmax; + high= -REALmax; + for (i=numpoints, coord=points+k; i--; coord += dim) { + minimize_(low, *coord); + maximize_(high, *coord); + } + if (newhigh > REALmax/2) + newhigh= high; + if (newlow < -REALmax/2) + newlow= low; + if (qh->DELAUNAY && k == dim-1 && newhigh < newlow) { + qh_fprintf(qh, qh->ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n", + k, k, newhigh, newlow); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + scale= qh_divzero(newhigh - newlow, high - low, + qh->MINdenom_1, &nearzero); + if (nearzero) { + qh_fprintf(qh, qh->ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n", + k, newlow, newhigh, low, high); + qh_errexit(qh, 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, qh->ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n", + k, low, high, newlow, newhigh, numpoints, scale, shift)); + } +} /* scalepoints */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="setdelaunay">-</a> + + qh_setdelaunay(qh, 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, 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(qhT *qh, int dim, int count, pointT *points) { + int i, k; + coordT *coordp, coord; + realT paraboloid; + + trace0((qh, qh->ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count)); + coordp= points; + for (i=0; i < count; i++) { + coord= *coordp++; + paraboloid= coord*coord; + for (k=dim-2; k--; ) { + coord= *coordp++; + paraboloid += coord*coord; + } + *coordp++ = paraboloid; + } + if (qh->last_low < REALmax/2) + qh_scalelast(qh, points, count, dim, qh->last_low, qh->last_high, qh->last_newhigh); +} /* setdelaunay */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sethalfspace">-</a> + + qh_sethalfspace(qh, dim, coords, nextp, normal, offset, feasible ) + set point to dual of halfspace relative to feasible point + halfspace is normal coefficients and offset. + + returns: + false and prints error if feasible point is outside of hull + overwrites coordinates for point at dim coords + nextp= next point (coords) + does not call qh_errexit + + design: + compute distance from feasible point to halfspace + divide each normal coefficient by -dist +*/ +boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp, + coordT *normal, coordT *offset, coordT *feasible) { + coordT *normp= normal, *feasiblep= feasible, *coordp= coords; + realT dist; + realT r; /*bug fix*/ + int k; + boolT zerodiv; + + dist= *offset; + for (k=dim; k--; ) + dist += *(normp++) * *(feasiblep++); + if (dist > 0) + goto LABELerroroutside; + normp= normal; + if (dist < -qh->MINdenom) { + for (k=dim; k--; ) + *(coordp++)= *(normp++) / -dist; + }else { + for (k=dim; k--; ) { + *(coordp++)= qh_divzero(*(normp++), -dist, qh->MINdenom_1, &zerodiv); + if (zerodiv) + goto LABELerroroutside; + } + } + *nextp= coordp; + if (qh->IStracing >= 4) { + qh_fprintf(qh, qh->ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset); + for (k=dim, coordp=coords; k--; ) { + r= *coordp++; + qh_fprintf(qh, qh->ferr, 8022, " %6.2g", r); + } + qh_fprintf(qh, qh->ferr, 8023, "\n"); + } + return True; +LABELerroroutside: + feasiblep= feasible; + normp= normal; + qh_fprintf(qh, qh->ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: "); + for (k=dim; k--; ) + qh_fprintf(qh, qh->ferr, 8024, qh_REAL_1, r=*(feasiblep++)); + qh_fprintf(qh, qh->ferr, 8025, "\n halfspace: "); + for (k=dim; k--; ) + qh_fprintf(qh, qh->ferr, 8026, qh_REAL_1, r=*(normp++)); + qh_fprintf(qh, qh->ferr, 8027, "\n at offset: "); + qh_fprintf(qh, qh->ferr, 8028, qh_REAL_1, *offset); + qh_fprintf(qh, qh->ferr, 8029, " and distance: "); + qh_fprintf(qh, qh->ferr, 8030, qh_REAL_1, dist); + qh_fprintf(qh, qh->ferr, 8031, "\n"); + return False; +} /* sethalfspace */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sethalfspace_all">-</a> + + qh_sethalfspace_all(qh, 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 + feasible is a point inside all halfspaces (http://www.qhull.org/html/qhalf.htm#notes) + + returns: + malloc'd array of count X dim-1 points + + notes: + call before qh_init_B or qh_initqhull_globals + free memory when done + unused/untested code: please email bradb@shore.net if this works ok for you + if using option 'Fp', qh->feasible_point must be set (e.g., to 'feasible') + qh->feasible_point is a malloc'd array that is freed by qh_freebuffers. + + design: + see qh_sethalfspace +*/ +coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible) { + int i, newdim; + pointT *newpoints; + coordT *coordp, *normalp, *offsetp; + + trace0((qh, qh->ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n")); + newdim= dim - 1; + if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){ + qh_fprintf(qh, qh->ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n", + count); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + coordp= newpoints; + normalp= halfspaces; + for (i=0; i < count; i++) { + offsetp= normalp + newdim; + if (!qh_sethalfspace(qh, newdim, coordp, &coordp, normalp, offsetp, feasible)) { + qh_free(newpoints); /* feasible is not inside halfspace as reported by qh_sethalfspace */ + qh_fprintf(qh, qh->ferr, 8032, "The halfspace was at index %d\n", i); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + normalp= offsetp + 1; + } + return newpoints; +} /* sethalfspace_all */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sharpnewfacets">-</a> + + qh_sharpnewfacets(qh) + + 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(qhT *qh) { + facetT *facet; + boolT issharp = False; + int *quadrant, k; + + quadrant= (int*)qh_memalloc(qh, 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(qh, quadrant, qh->hull_dim * sizeof(int)); + trace3((qh, qh->ferr, 3001, "qh_sharpnewfacets: %d\n", issharp)); + return issharp; +} /* sharpnewfacets */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="voronoi_center">-</a> + + qh_voronoi_center(qh, 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 (qh_memalloc) + if non-simplicial, + returns center for max simplex of points + + notes: + only called by qh_facetcenter + 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(qhT *qh, int dim, setT *points) { + pointT *point, **pointp, *point0; + pointT *center= (pointT*)qh_memalloc(qh, qh->center_size); + setT *simplex; + int i, j, k, size= qh_setsize(qh, points); + coordT *gmcoord; + realT *diffp, sum2, *sum2row, *sum2p, det, factor; + boolT nearzero, infinite; + + if (size == dim+1) + simplex= points; + else if (size < dim+1) { + qh_memfree(qh, center, qh->center_size); + qh_fprintf(qh, qh->ferr, 6025, "qhull internal error (qh_voronoi_center):\n need at least %d points to construct a Voronoi center\n", + dim+1); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + simplex= points; /* never executed -- avoids warning */ + }else { + simplex= qh_settemp(qh, dim+1); + qh_maxsimplex(qh, 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, 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, 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, qh->gm_row, dim, &nearzero)*factor + point0[i]; + } +#ifndef qh_NOtrace + if (qh->IStracing >= 3) { + qh_fprintf(qh, qh->ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor); + qh_printmatrix(qh, qh->ferr, "center:", ¢er, 1, dim); + if (qh->IStracing >= 5) { + qh_printpoints(qh, qh->ferr, "points", simplex); + FOREACHpoint_(simplex) + qh_fprintf(qh, qh->ferr, 8034, "p%d dist %.2g, ", qh_pointid(qh, point), + qh_pointdist(point, center, dim)); + qh_fprintf(qh, qh->ferr, 8035, "\n"); + } + } +#endif + } + if (simplex != points) + qh_settempfree(qh, &simplex); + return center; +} /* voronoi_center */ +
+ C/geom_r.c view
@@ -0,0 +1,1234 @@+/*<html><pre> -<a href="qh-geom_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + geom_r.c + geometric routines of qhull + + see qh-geom_r.htm and geom_r.h + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/geom_r.c#2 $$Change: 1995 $ + $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $ + + infrequent code goes into geom2_r.c +*/ + +#include "qhull_ra.h" + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="distplane">-</a> + + qh_distplane(qh, point, facet, dist ) + return distance from point to facet + + returns: + dist + if qh.RANDOMdist, joggles result + + notes: + dist > 0 if point is above facet (i.e., outside) + does not error (for qh_sortfacets, qh_outerinner) + + see: + qh_distnorm in geom2_r.c + qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies +*/ +void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist) { + coordT *normal= facet->normal, *coordp, randr; + int k; + + switch (qh->hull_dim){ + case 2: + *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1]; + break; + case 3: + *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2]; + break; + case 4: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]; + break; + case 5: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]; + break; + case 6: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]; + break; + case 7: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]; + break; + case 8: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7]; + break; + default: + *dist= facet->offset; + coordp= point; + for (k=qh->hull_dim; k--; ) + *dist += *coordp++ * *normal++; + break; + } + zinc_(Zdistplane); + if (!qh->RANDOMdist && qh->IStracing < 4) + return; + if (qh->RANDOMdist) { + randr= qh_RANDOMint; + *dist += (2.0 * randr / qh_RANDOMmax - 1.0) * + qh->RANDOMfactor * qh->MAXabs_coord; + } + if (qh->IStracing >= 4) { + qh_fprintf(qh, qh->ferr, 8001, "qh_distplane: "); + qh_fprintf(qh, qh->ferr, 8002, qh_REAL_1, *dist); + qh_fprintf(qh, qh->ferr, 8003, "from p%d to f%d\n", qh_pointid(qh, point), facet->id); + } + return; +} /* distplane */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="findbest">-</a> + + qh_findbest(qh, 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(qhT *qh, 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(qh, point))) { + if (qh->TRACElevel > qh->IStracing) + qh->IStracing= qh->TRACElevel; + qh_fprintf(qh, qh->ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n", + qh_pointid(qh, point), startfacet->id, isnewfacets, bestoutside, qh->MINoutside); + qh_fprintf(qh, qh->ferr, 8005, " testhorizon? %d noupper? %d", testhorizon, noupper); + qh_fprintf(qh, qh->ferr, 8006, " Last point added was p%d.", qh->furthest_id); + qh_fprintf(qh, qh->ferr, 8007, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh->max_outside); + } + if (isoutside) + *isoutside= True; + if (!startfacet->flipped) { /* test startfacet */ + *numpart= 1; + qh_distplane(qh, 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, qh->ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n", + facet->id, bestdist, getid_(bestfacet))); + lastfacet= facet; + FOREACHneighbor_(facet) { + if (!neighbor->newfacet && isnewfacets) + continue; + if (neighbor->visitid == visitid) + continue; + neighbor->visitid= visitid; + if (!neighbor->flipped) { /* code duplicated above */ + (*numpart)++; + qh_distplane(qh, 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(qh, 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(qh)) { + /* seldom used, qh_findbestnew will retest all facets */ + zinc_(Zfindnewsharp); + bestfacet= qh_findbestnew(qh, 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(qh, lastfacet, point, &bestdist, numpart); + if (testhorizon) + bestfacet= qh_findbesthorizon(qh, !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_r.htm#TOC" + >-------------------------------</a><a name="findbesthorizon">-</a> + + qh_findbesthorizon(qh, 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(qhT *qh, boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) { + facetT *bestfacet= startfacet; + realT dist; + facetT *neighbor, **neighborp, *facet; + facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */ + int numpartinit= *numpart, coplanarfacetset_size; + unsigned int visitid= ++qh->visit_id; + boolT newbest= False; /* for tracing */ + realT minsearch, searchdist; /* skip facets that are too far from point */ + + if (!ischeckmax) { + zinc_(Zfindhorizon); + }else { +#if qh_MAXoutside + if ((!qh->ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside) + startfacet->maxoutside= *bestdist; +#endif + } + searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */ + minsearch= *bestdist - searchdist; + if (ischeckmax) { + /* Always check coplanar facets. Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */ + minimize_(minsearch, -searchdist); + } + coplanarfacetset_size= 0; + facet= startfacet; + while (True) { + trace4((qh, qh->ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n", + facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper, + minsearch, searchdist)); + FOREACHneighbor_(facet) { + if (neighbor->visitid == visitid) + continue; + neighbor->visitid= visitid; + if (!neighbor->flipped) { + qh_distplane(qh, point, neighbor, &dist); + (*numpart)++; + if (dist > *bestdist) { + if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh->MINoutside)) { + bestfacet= neighbor; + *bestdist= dist; + newbest= True; + if (!ischeckmax) { + minsearch= dist - searchdist; + if (dist > *bestdist + searchdist) { + zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */ + coplanarfacetset_size= 0; + } + } + } + }else if (dist < minsearch) + continue; /* if ischeckmax, dist can't be positive */ +#if qh_MAXoutside + if (ischeckmax && dist > neighbor->maxoutside) + neighbor->maxoutside= dist; +#endif + } /* end of !flipped */ + if (nextfacet) { + if (!coplanarfacetset_size++) { + SETfirst_(qh->coplanarfacetset)= nextfacet; + SETtruncate_(qh->coplanarfacetset, 1); + }else + qh_setappend(qh, &qh->coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv + and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */ + } + nextfacet= neighbor; + } /* end of EACHneighbor */ + facet= nextfacet; + if (facet) + nextfacet= NULL; + else if (!coplanarfacetset_size) + break; + else if (!--coplanarfacetset_size) { + facet= SETfirstt_(qh->coplanarfacetset, facetT); + SETtruncate_(qh->coplanarfacetset, 0); + }else + facet= (facetT*)qh_setdellast(qh->coplanarfacetset); + } /* while True, for each facet in qh.coplanarfacetset */ + if (!ischeckmax) { + zadd_(Zfindhorizontot, *numpart - numpartinit); + zmax_(Zfindhorizonmax, *numpart - numpartinit); + if (newbest) + zinc_(Zparthorizon); + } + trace4((qh, qh->ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist)); + return bestfacet; +} /* findbesthorizon */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="findbestnew">-</a> + + qh_findbestnew(qh, point, startfacet, dist, isoutside, numpart ) + find best newfacet for point + searches all of qh.newfacet_list starting at startfacet + searches horizon facets of coplanar best newfacets + searches all facets if startfacet == qh.facet_list + returns: + best new or horizon facet that is not upperdelaunay + early out if isoutside and not 'Qf' + dist is distance to facet + isoutside is true if point is outside of facet + numpart is number of distance tests + + notes: + Always used for merged new facets (see qh_USEfindbestnew) + Avoids upperdelaunay facet unless (isoutside and outside) + + Uses qh.visit_id, qh.coplanarfacetset. + If share visit_id with qh_findbest, coplanarfacetset is incorrect. + + If merging (testhorizon), searches horizon facets of coplanar best facets because + a point maybe coplanar to the bestfacet, below its horizon facet, + and above a horizon facet of a coplanar newfacet. For example, + rbox 1000 s Z1 G1e-13 | qhull + rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc + + qh_findbestnew() used if + qh_sharpnewfacets -- newfacets contains a sharp angle + if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew) + + see also: + qh_partitionall() and qh_findbest() + + design: + for each new facet starting from startfacet + test distance from point to facet + return facet if clearly outside + unless upperdelaunay and a lowerdelaunay exists + update best facet + test horizon facets +*/ +facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet, + realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) { + realT bestdist= -REALmax/2; + facetT *bestfacet= NULL, *facet; + int oldtrace= qh->IStracing, i; + unsigned int visitid= ++qh->visit_id; + realT distoutside= 0.0; + boolT isdistoutside; /* True if distoutside is defined */ + boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */ + + if (!startfacet) { + if (qh->MERGING) + qh_fprintf(qh, qh->ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n"); + else + qh_fprintf(qh, qh->ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n", + qh->furthest_id); + qh_errexit(qh, 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(qh, point))) { + if (qh->TRACElevel > qh->IStracing) + qh->IStracing= qh->TRACElevel; + qh_fprintf(qh, qh->ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n", + qh_pointid(qh, point), startfacet->id, isdistoutside, distoutside); + qh_fprintf(qh, qh->ferr, 8009, " Last point added p%d visitid %d.", qh->furthest_id, visitid); + qh_fprintf(qh, qh->ferr, 8010, " Last merge was #%d.\n", zzval_(Ztotmerge)); + } + /* visit all new facets starting with startfacet, maybe qh->facet_list */ + for (i=0, facet=startfacet; i < 2; i++, facet= qh->newfacet_list) { + FORALLfacet_(facet) { + if (facet == startfacet && i) + break; + facet->visitid= visitid; + if (!facet->flipped) { + qh_distplane(qh, 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) /* testhorizon is always True. Keep the same code as qh_findbest */ + bestfacet= qh_findbesthorizon(qh, !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, qh->ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist)); + qh->IStracing= oldtrace; + return bestfacet; +} /* findbestnew */ + +/* ============ hyperplane functions -- keep code together [?] ============ */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="backnormal">-</a> + + qh_backnormal(qh, 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, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting" + + 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(qhT *qh, 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, qh->ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i)); + qh_precision(qh, "zero diagonal on back substitution"); + } +} /* backnormal */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="gausselim">-</a> + + qh_gausselim(qh, 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(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) { + realT *ai, *ak, *rowp, *pivotrow; + realT n, pivot, pivot_abs= 0.0, temp; + int i, j, k, pivoti, flip=0; + + *nearzero= False; + for (k=0; k < numrow; k++) { + pivot_abs= fabs_((rows[k])[k]); + pivoti= k; + for (i=k+1; i < numrow; i++) { + if ((temp= fabs_((rows[i])[k])) > pivot_abs) { + pivot_abs= temp; + pivoti= i; + } + } + if (pivoti != k) { + rowp= rows[pivoti]; + rows[pivoti]= rows[k]; + rows[k]= rowp; + *sign ^= 1; + flip ^= 1; + } + if (pivot_abs <= qh->NEARzero[k]) { + *nearzero= True; + if (pivot_abs == 0.0) { /* remainder of column == 0 */ + if (qh->IStracing >= 4) { + qh_fprintf(qh, qh->ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh->DISTround); + qh_printmatrix(qh, qh->ferr, "Matrix:", rows, numrow, numcol); + } + zzinc_(Zgauss0); + qh_precision(qh, "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, qh->ferr, "qh_gausselem: result", rows, numrow, numcol); +} /* gausselim */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getangle">-</a> + + qh_getangle(qh, 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(qhT *qh, 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, qh->ferr, 4006, "qh_getangle: %2.2g\n", angle)); + return(angle); +} /* getangle */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getcenter">-</a> + + qh_getcenter(qh, vertices ) + returns arithmetic center of a set of vertices as a new point + + notes: + allocates point array for center +*/ +pointT *qh_getcenter(qhT *qh, setT *vertices) { + int k; + pointT *center, *coord; + vertexT *vertex, **vertexp; + int count= qh_setsize(qh, vertices); + + if (count < 2) { + qh_fprintf(qh, qh->ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + center= (pointT *)qh_memalloc(qh, 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; /* count>=2 by QH6003 */ + } + return(center); +} /* getcenter */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getcentrum">-</a> + + qh_getcentrum(qh, facet ) + returns the centrum for a facet as a new point + + notes: + allocates the centrum +*/ +pointT *qh_getcentrum(qhT *qh, facetT *facet) { + realT dist; + pointT *centrum, *point; + + point= qh_getcenter(qh, facet->vertices); + zzinc_(Zcentrumtests); + qh_distplane(qh, point, facet, &dist); + centrum= qh_projectpoint(qh, point, facet, dist); + qh_memfree(qh, point, qh->normal_size); + trace4((qh, qh->ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n", + facet->id, qh_setsize(qh, facet->vertices), dist)); + return centrum; +} /* getcentrum */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getdistance">-</a> + + qh_getdistance(qh, 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(qhT *qh, 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(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="normalize">-</a> + + qh_normalize(qh, normal, dim, toporient ) + normalize a vector and report if too small + does not use min norm + + see: + qh_normalize2 +*/ +void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient) { + qh_normalize2(qh, normal, dim, toporient, NULL, NULL); +} /* normalize */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="normalize2">-</a> + + qh_normalize2(qh, 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(qhT *qh, 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, qh->ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n", + norm, qh->furthest_id)); + return; + } + } + } +} /* normalize */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="projectpoint">-</a> + + qh_projectpoint(qh, 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(qhT *qh, pointT *point, facetT *facet, realT dist) { + pointT *newpoint, *np, *normal; + int normsize= qh->normal_size; + int k; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="setfacetplane">-</a> + + qh_setfacetplane(qh, 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(qhT *qh, facetT *facet) { + pointT *point; + vertexT *vertex, **vertexp; + int normsize= qh->normal_size; + int k,i, oldtrace= 0; + realT dist; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + coordT *coord, *gmcoord; + pointT *point0= SETfirstt_(facet->vertices, vertexT)->point; + boolT nearzero= False; + + zzinc_(Zsetplane); + if (!facet->normal) + qh_memalloc_(qh, normsize, freelistp, facet->normal, coordT); + if (facet == qh->tracefacet) { + oldtrace= qh->IStracing; + qh->IStracing= 5; + qh_fprintf(qh, qh->ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id); + qh_fprintf(qh, qh->ferr, 8013, " Last point added to hull was p%d.", qh->furthest_id); + if (zzval_(Ztotmerge)) + qh_fprintf(qh, qh->ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge)); + qh_fprintf(qh, qh->ferr, 8015, "\n\nCurrent summary is:\n"); + qh_printsummary(qh, qh->ferr); + } + if (qh->hull_dim <= 4) { + i= 0; + if (qh->RANDOMdist) { + gmcoord= qh->gm_matrix; + FOREACHvertex_(facet->vertices) { + qh->gm_row[i++]= gmcoord; + coord= vertex->point; + for (k=qh->hull_dim; k--; ) + *(gmcoord++)= *coord++ * qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb); + } + }else { + FOREACHvertex_(facet->vertices) + qh->gm_row[i++]= vertex->point; + } + qh_sethyperplane_det(qh, 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, qh->RANDOMa, qh->RANDOMb); + } + } + qh_sethyperplane_gauss(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient, + facet->normal, &facet->offset, &nearzero); + if (nearzero) { + if (qh_orientoutside(qh, facet)) { + trace0((qh, qh->ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh->furthest_id)); + /* this is part of using Gaussian Elimination. For example in 5-d + 1 1 1 1 0 + 1 1 1 1 1 + 0 0 0 1 0 + 0 1 0 0 0 + 1 0 0 0 0 + norm= 0.38 0.38 -0.76 0.38 0 + has a determinate of 1, but g.e. after subtracting pt. 0 has + 0's in the diagonal, even with full pivoting. It does work + if you subtract pt. 4 instead. */ + } + } + } + facet->upperdelaunay= False; + if (qh->DELAUNAY) { + if (qh->UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */ + if (facet->normal[qh->hull_dim -1] >= qh->ANGLEround * qh_ZEROdelaunay) + facet->upperdelaunay= True; + }else { + if (facet->normal[qh->hull_dim -1] > -qh->ANGLEround * qh_ZEROdelaunay) + facet->upperdelaunay= True; + } + } + if (qh->PRINTstatistics || qh->IStracing || qh->TRACElevel || qh->JOGGLEmax < REALmax) { + qh->old_randomdist= qh->RANDOMdist; + qh->RANDOMdist= False; + FOREACHvertex_(facet->vertices) { + if (vertex->point != point0) { + boolT istrace= False; + zinc_(Zdiststat); + qh_distplane(qh, 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(qh) */ + if (dist > qh->TRACEdist) + istrace= True; + } + }else if (-dist > qh->TRACEdist) + istrace= True; + if (istrace) { + qh_fprintf(qh, qh->ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n", + qh_pointid(qh, vertex->point), vertex->id, dist, facet->id, qh->furthest_id); + qh_errprint(qh, "DISTANT", facet, NULL, NULL, NULL); + } + } + } + qh->RANDOMdist= qh->old_randomdist; + } + if (qh->IStracing >= 3) { + qh_fprintf(qh, qh->ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ", + facet->id, facet->offset); + for (k=0; k < qh->hull_dim; k++) + qh_fprintf(qh, qh->ferr, 8018, "%2.2g ", facet->normal[k]); + qh_fprintf(qh, qh->ferr, 8019, "\n"); + } + if (facet == qh->tracefacet) + qh->IStracing= oldtrace; +} /* setfacetplane */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sethyperplane_det">-</a> + + qh_sethyperplane_det(qh, 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(qhT *qh, 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(qh, 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(qh, 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(qh, 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, qh->ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh->furthest_id)); + zzinc_(Znearlysingular); + } +} /* sethyperplane_det */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sethyperplane_gauss">-</a> + + qh_sethyperplane_gauss(qh, 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(qhT *qh, 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(qh, rows, dim-1, dim, &sign, nearzero); + for (k=dim-1; k--; ) { + if ((rows[k])[k] < 0) + sign ^= 1; + } + if (*nearzero) { + zzinc_(Znearlysingular); + trace0((qh, qh->ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh->furthest_id)); + qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2); + }else { + qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2); + if (nearzero2) { + zzinc_(Znearlysingular); + trace0((qh, qh->ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh->furthest_id)); + } + } + if (nearzero2) + *nearzero= True; + qh_normalize2(qh, normal, dim, True, NULL, NULL); + pointcoord= point0; + normalcoef= normal; + *offset= -(*pointcoord++ * *normalcoef++); + for (k=dim-1; k--; ) + *offset -= *pointcoord++ * *normalcoef++; +} /* sethyperplane_gauss */ + + +
+ C/geom_r.h view
@@ -0,0 +1,184 @@+/*<html><pre> -<a href="qh-geom_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + geom_r.h + header file for geometric routines + + see qh-geom_r.htm and geom_r.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/geom_r.h#3 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ +*/ + +#ifndef qhDEFgeom +#define qhDEFgeom 1 + +#include "libqhull_r.h" + +/* ============ -macros- ======================== */ + +/*-<a href="qh-geom_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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 ======= */ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero); +void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist); +facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet, + boolT bestoutside, boolT isnewfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart); +facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT *point, + facetT *startfacet, boolT noupper, realT *bestdist, int *numpart); +facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet, realT *dist, + boolT bestoutside, boolT *isoutside, int *numpart); +void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero); +realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2); +pointT *qh_getcenter(qhT *qh, setT *vertices); +pointT *qh_getcentrum(qhT *qh, facetT *facet); +realT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist); +void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient); +void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient, + realT *minnorm, boolT *ismin); +pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist); + +void qh_setfacetplane(qhT *qh, facetT *newfacets); +void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0, + boolT toporient, coordT *normal, realT *offset, boolT *nearzero); +void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0, + boolT toporient, coordT *normal, coordT *offset, boolT *nearzero); +boolT qh_sharpnewfacets(qhT *qh); + +/*========= infrequently used code in geom2_r.c =============*/ + +coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension); +void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]); +realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero); +realT qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension); +void qh_detroundoff(qhT *qh); +realT qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero); +realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp); +realT qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs); +realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv); +realT qh_facetarea(qhT *qh, facetT *facet); +realT qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices, + vertexT *notvertex, boolT toporient, coordT *normal, realT *offset); +pointT *qh_facetcenter(qhT *qh, setT *vertices); +facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp, facetT **facetlist); +void qh_getarea(qhT *qh, facetT *facetlist); +boolT qh_gram_schmidt(qhT *qh, int dim, realT **rows); +boolT qh_inthresholds(qhT *qh, coordT *normal, realT *angle); +void qh_joggleinput(qhT *qh); +realT *qh_maxabsval(realT *normal, int dim); +setT *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension); +realT qh_maxouter(qhT *qh); +void qh_maxsimplex(qhT *qh, 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(qhT *qh, facetT *facet); +void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane); +coordT qh_pointdist(pointT *point1, pointT *point2, int dim); +void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol); +void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points); +void qh_projectinput(qhT *qh); +void qh_projectpoints(qhT *qh, signed char *project, int n, realT *points, + int numpoints, int dim, realT *newpoints, int newdim); +void qh_rotateinput(qhT *qh, realT **rows); +void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **rows); +void qh_scaleinput(qhT *qh); +void qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low, + coordT high, coordT newhigh); +void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim, + realT *newlows, realT *newhighs); +boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp, + coordT *normal, coordT *offset, coordT *feasible); +coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible); +pointT *qh_voronoi_center(qhT *qh, int dim, setT *points); + +#ifdef __cplusplus +} /* extern "C"*/ +#endif + +#endif /* qhDEFgeom */ + + +
+ C/global_r.c view
@@ -0,0 +1,2100 @@+ +/*<html><pre> -<a href="qh-globa_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + global_r.c + initializes all the globals of the qhull application + + see README + + see libqhull_r.h for qh.globals and function prototypes + + see qhull_ra.h for internal functions + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/global_r.c#16 $$Change: 2066 $ + $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $ + */ + +#include "qhull_ra.h" + +/*========= qh->definition -- globals defined in libqhull_r.h =======================*/ + +/*-<a href ="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh_version">-</a> + + qh_version + version string by year and date + qh_version2 for Unix users and -V + + the revision increases on code changes only + + notes: + change date: Changes.txt, Announce.txt, index.htm, README.txt, + qhull-news.html, Eudora signatures, CMakeLists.txt + change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt, CMakeLists.txt + check that CmakeLists @version is the same as qh_version2 + change year: Copying.txt + check download size + recompile user_eg_r.c, rbox_r.c, libqhull_r.c, qconvex_r.c, qdelaun_r.c qvoronoi_r.c, qhalf_r.c, testqset_r.c +*/ + +const char qh_version[]= "2015.2.r 2016/01/18"; +const char qh_version2[]= "qhull_r 7.2.0 (2015.2.r 2016/01/18)"; + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="appendprint">-</a> + + qh_appendprint(qh, printFormat ) + append printFormat to qh.PRINTout unless already defined +*/ +void qh_appendprint(qhT *qh, 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_r.htm#TOC" + >-------------------------------</a><a name="checkflags">-</a> + + qh_checkflags(qh, commandStr, hiddenFlags ) + errors if commandStr contains hiddenFlags + hiddenFlags starts and ends with a space and is space delimited (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(qhT *qh, char *command, char *hiddenflags) { + char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */ + char key, opt, prevopt; + char chkkey[]= " "; + char chkopt[]= " "; + char chkopt2[]= " "; + boolT waserr= False; + + if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') { + qh_fprintf(qh, qh->ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (strpbrk(hiddenflags, ",\n\r\t")) { + qh_fprintf(qh, qh->ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + while (*s && !isspace(*s)) /* skip program name */ + s++; + while (*s) { + while (*s && isspace(*s)) + s++; + if (*s == '-') + s++; + if (!*s) + break; + key = *s++; + chkerr = NULL; + if (key == 'T' && (*s == 'I' || *s == 'O')) { /* TI or TO 'file name' */ + s= qh_skipfilename(qh, ++s); + continue; + } + chkkey[1]= key; + if (strstr(hiddenflags, chkkey)) { + chkerr= chkkey; + }else if (isupper(key)) { + opt= ' '; + prevopt= ' '; + chkopt[1]= key; + chkopt2[1]= key; + while (!chkerr && *s && !isspace(*s)) { + opt= *s++; + if (isalpha(opt)) { + chkopt[2]= opt; + if (strstr(hiddenflags, chkopt)) + chkerr= chkopt; + if (prevopt != ' ') { + chkopt2[2]= prevopt; + chkopt2[3]= opt; + if (strstr(hiddenflags, chkopt2)) + chkerr= chkopt2; + } + }else if (key == 'Q' && isdigit(opt) && prevopt != 'b' + && (prevopt == ' ' || islower(prevopt))) { + chkopt[2]= opt; + if (strstr(hiddenflags, chkopt)) + chkerr= chkopt; + }else { + qh_strtod(s-1, &t); + if (s < t) + s= t; + } + prevopt= opt; + } + } + if (chkerr) { + *chkerr= '\''; + chkerr[strlen(chkerr)-1]= '\''; + qh_fprintf(qh, qh->ferr, 6029, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr); + waserr= True; + } + } + if (waserr) + qh_errexit(qh, qh_ERRinput, NULL, NULL); +} /* checkflags */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="qh_clear_outputflags">-</a> + + qh_clear_outputflags(qh) + Clear output flags for QhullPoints +*/ +void qh_clear_outputflags(qhT *qh) { + int i,k; + + qh->ANNOTATEoutput= False; + qh->DOintersections= False; + qh->DROPdim= -1; + qh->FORCEoutput= False; + qh->GETarea= False; + qh->GOODpoint= 0; + qh->GOODpointp= NULL; + qh->GOODthreshold= False; + qh->GOODvertex= 0; + qh->GOODvertexp= NULL; + qh->IStracing= 0; + qh->KEEParea= False; + qh->KEEPmerge= False; + qh->KEEPminArea= REALmax; + qh->PRINTcentrums= False; + qh->PRINTcoplanar= False; + qh->PRINTdots= False; + qh->PRINTgood= False; + qh->PRINTinner= False; + qh->PRINTneighbors= False; + qh->PRINTnoplanes= False; + qh->PRINToptions1st= False; + qh->PRINTouter= False; + qh->PRINTprecision= True; + qh->PRINTridges= False; + qh->PRINTspheres= False; + qh->PRINTstatistics= False; + qh->PRINTsummary= False; + qh->PRINTtransparent= False; + qh->SPLITthresholds= False; + qh->TRACElevel= 0; + qh->TRInormals= False; + qh->USEstdout= False; + qh->VERIFYoutput= False; + for (k=qh->input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */ + qh->lower_threshold[k]= -REALmax; + qh->upper_threshold[k]= REALmax; + qh->lower_bound[k]= -REALmax; + qh->upper_bound[k]= REALmax; + } + + for (i=0; i < qh_PRINTEND; i++) { + qh->PRINTout[i]= qh_PRINTnone; + } + + if (!qh->qhull_commandsiz2) + qh->qhull_commandsiz2= (int)strlen(qh->qhull_command); /* WARN64 */ + else { + qh->qhull_command[qh->qhull_commandsiz2]= '\0'; + } + if (!qh->qhull_optionsiz2) + qh->qhull_optionsiz2= (int)strlen(qh->qhull_options); /* WARN64 */ + else { + qh->qhull_options[qh->qhull_optionsiz2]= '\0'; + qh->qhull_optionlen= qh_OPTIONline; /* start a new line */ + } +} /* clear_outputflags */ + +/*-<a href="qh-globa_r.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(qhT *qh) { + +#if (qh_CLOCKtype == 2) + struct tms time; + static long clktck; /* initialized first call and never updated */ + double ratio, cpu; + unsigned long ticks; + + if (!clktck) { + if ((clktck= sysconf(_SC_CLK_TCK)) < 0) { + qh_fprintf(qh, qh->ferr, 6030, "qhull internal error (qh_clock): sysconf() failed. Use qh_CLOCKtype 1 in user.h\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + } + if (times(&time) == -1) { + qh_fprintf(qh, qh->ferr, 6031, "qhull internal error (qh_clock): times() failed. Use qh_CLOCKtype 1 in user.h\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + ratio= qh_SECticks / (double)clktck; + ticks= time.tms_utime * ratio; + return ticks; +#else + qh_fprintf(qh, qh->ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* never returns */ + return 0; +#endif +} /* clock */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="freebuffers">-</a> + + qh_freebuffers() + free up global memory buffers + + notes: + must match qh_initbuffers() +*/ +void qh_freebuffers(qhT *qh) { + + trace5((qh, qh->ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n")); + /* allocated by qh_initqhull_buffers */ + qh_memfree(qh, qh->NEARzero, qh->hull_dim * sizeof(realT)); + qh_memfree(qh, qh->lower_threshold, (qh->input_dim+1) * sizeof(realT)); + qh_memfree(qh, qh->upper_threshold, (qh->input_dim+1) * sizeof(realT)); + qh_memfree(qh, qh->lower_bound, (qh->input_dim+1) * sizeof(realT)); + qh_memfree(qh, qh->upper_bound, (qh->input_dim+1) * sizeof(realT)); + qh_memfree(qh, qh->gm_matrix, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT)); + qh_memfree(qh, 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, &qh->other_points); + qh_setfree(qh, &qh->del_vertices); + qh_setfree(qh, &qh->coplanarfacetset); + if (qh->line) /* allocated by qh_readinput, freed if no error */ + qh_free(qh->line); + if (qh->half_space) + qh_free(qh->half_space); + if (qh->temp_malloc) + qh_free(qh->temp_malloc); + if (qh->feasible_point) /* allocated by qh_readfeasible */ + qh_free(qh->feasible_point); + if (qh->feasible_string) /* allocated by qh_initflags */ + qh_free(qh->feasible_string); + qh->line= qh->feasible_string= NULL; + qh->half_space= qh->feasible_point= qh->temp_malloc= NULL; + /* usually allocated by qh_readinput */ + if (qh->first_point && qh->POINTSmalloc) { + qh_free(qh->first_point); + qh->first_point= NULL; + } + if (qh->input_points && qh->input_malloc) { /* set by qh_joggleinput */ + qh_free(qh->input_points); + qh->input_points= NULL; + } + trace5((qh, qh->ferr, 5002, "qh_freebuffers: finished\n")); +} /* freebuffers */ + + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="freebuild">-</a> + + qh_freebuild(qh, allmem ) + free global memory used by qh_initbuild and qh_buildhull + if !allmem, + does not free short memory (e.g., facetT, freed by qh_memfreeshort) + + design: + free centrums + free each vertex + mark unattached ridges + for each facet + free ridges + free outside set, coplanar set, neighbor set, ridge set, vertex set + free facet + free hash table + free interior point + free merge set + free temporary sets +*/ +void qh_freebuild(qhT *qh, boolT allmem) { + facetT *facet; + vertexT *vertex; + ridgeT *ridge, **ridgep; + mergeT *merge, **mergep; + + trace1((qh, qh->ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n")); + if (qh->del_vertices) + qh_settruncate(qh, qh->del_vertices, 0); + if (allmem) { + while ((vertex= qh->vertex_list)) { + if (vertex->next) + qh_delvertex(qh, vertex); + else { + qh_memfree(qh, vertex, (int)sizeof(vertexT)); + qh->newvertex_list= qh->vertex_list= NULL; + } + } + }else if (qh->VERTEXneighbors) { + FORALLvertices + qh_setfreelong(qh, &(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(qh, &(ridge->vertices)); + qh_memfree(qh, ridge, (int)sizeof(ridgeT)); + }else + ridge->seen= True; + } + qh_setfree(qh, &(facet->outsideset)); + qh_setfree(qh, &(facet->coplanarset)); + qh_setfree(qh, &(facet->neighbors)); + qh_setfree(qh, &(facet->ridges)); + qh_setfree(qh, &(facet->vertices)); + if (facet->next) + qh_delfacet(qh, facet); + else { + qh_memfree(qh, facet, (int)sizeof(facetT)); + qh->visible_list= qh->newfacet_list= qh->facet_list= NULL; + } + } + }else { + FORALLfacets { + qh_setfreelong(qh, &(facet->outsideset)); + qh_setfreelong(qh, &(facet->coplanarset)); + if (!facet->simplicial) { + qh_setfreelong(qh, &(facet->neighbors)); + qh_setfreelong(qh, &(facet->ridges)); + qh_setfreelong(qh, &(facet->vertices)); + } + } + } + qh_setfree(qh, &(qh->hash_table)); + qh_memfree(qh, qh->interior_point, qh->normal_size); + qh->interior_point= NULL; + FOREACHmerge_(qh->facet_mergeset) /* usually empty */ + qh_memfree(qh, merge, (int)sizeof(mergeT)); + qh->facet_mergeset= NULL; /* temp set */ + qh->degen_mergeset= NULL; /* temp set */ + qh_settempfree_all(qh); +} /* freebuild */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="freeqhull">-</a> + + qh_freeqhull(qh, allmem ) + + free global memory and set qhT to 0 + if !allmem, + does not free short memory (freed by qh_memfreeshort unless qh_NOmem) + +notes: + sets qh.NOerrexit in case caller forgets to + Does not throw errors + +see: + see qh_initqhull_start2() + For libqhull_r, qhstatT is part of qhT + +design: + free global and temporary memory from qh_initbuild and qh_buildhull + free buffers +*/ +void qh_freeqhull(qhT *qh, boolT allmem) { + + qh->NOerrexit= True; /* no more setjmp since called at exit and ~QhullQh */ + trace1((qh, qh->ferr, 1006, "qh_freeqhull: free global memory\n")); + qh_freebuild(qh, allmem); + qh_freebuffers(qh); + /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */ + memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT)); + qh->NOerrexit= True; +} /* freeqhull2 */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="init_A">-</a> + + qh_init_A(qh, 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(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) { + qh_meminit(qh, errfile); + qh_initqhull_start(qh, infile, outfile, errfile); + qh_init_qhull_command(qh, argc, argv); +} /* init_A */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="init_B">-</a> + + qh_init_B(qh, points, numpoints, dim, ismalloc ) + initialize globals for points array + + points has numpoints dim-dimensional points + points[0] is the first coordinate of the first point + points[1] is the second coordinate of the first point + points[dim] is the first coordinate of the second point + + ismalloc=True + Qhull will call qh_free(points) on exit or input transformation + ismalloc=False + Qhull will allocate a new point array if needed for input transformation + + qh.qhull_command + is the option string. + It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags + + returns: + if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay) + projects the input to a new point array + + if qh.DELAUNAY, + qh.hull_dim is increased by one + if qh.ATinfinity, + qh_projectinput adds point-at-infinity for Delaunay tri. + + if qh.SCALEinput + changes the upper and lower bounds of the input, see qh_scaleinput(qh) + + 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(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) { + qh_initqhull_globals(qh, points, numpoints, dim, ismalloc); + if (qh->qhmem.LASTsize == 0) + qh_initqhull_mem(qh); + /* mem_r.c and qset_r.c are initialized */ + qh_initqhull_buffers(qh); + qh_initthresholds(qh, qh->qhull_command); + if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay)) + qh_projectinput(qh); + if (qh->SCALEinput) + qh_scaleinput(qh); + if (qh->ROTATErandom >= 0) { + qh_randommatrix(qh, 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, qh->hull_dim, qh->gm_row); + qh_rotateinput(qh, qh->gm_row); + } +} /* init_B */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="init_qhull_command">-</a> + + qh_init_qhull_command(qh, argc, argv ) + build qh.qhull_command from argc/argv + Calls qh_exit if qhull_command is too short + + returns: + a space-delimited string of options (just as typed) + + notes: + makes option string easy to input and output + + argc/argv may be 0/NULL +*/ +void qh_init_qhull_command(qhT *qh, int argc, char *argv[]) { + + if (!qh_argv_to_command(argc, argv, qh->qhull_command, (int)sizeof(qh->qhull_command))){ + /* Assumes qh.ferr is defined. */ + qh_fprintf(qh, qh->ferr, 6033, "qhull input error: more than %d characters in command line.\n", + (int)sizeof(qh->qhull_command)); + qh_exit(qh_ERRinput); /* error reported, can not use qh_errexit */ + } +} /* init_qhull_command */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initflags">-</a> + + qh_initflags(qh, commandStr ) + set flags and initialized constants from commandStr + calls qh_exit() if qh->NOerrexit + + 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_r.c for documentation + + design: + for each space-delimited option group + if top-level option + check syntax + append appropriate option to option string + set appropriate global variable or append printFormat to print options + else + for each sub-option + check syntax + append appropriate option to option string + set appropriate global variable or append printFormat to print options +*/ +void qh_initflags(qhT *qh, char *command) { + int k, i, lastproject; + char *s= command, *t, *prev_s, *start, key; + boolT isgeom= False, wasproject; + realT r; + + if(qh->NOerrexit){ + qh_fprintf(qh, qh->ferr, 6245, "qhull initflags error: qh.NOerrexit was not cleared before calling qh_initflags(). It should be cleared after setjmp(). Exit qhull."); + qh_exit(6245); + } + if (command <= &qh->qhull_command[0] || command > &qh->qhull_command[0] + sizeof(qh->qhull_command)) { + if (command != &qh->qhull_command[0]) { + *qh->qhull_command= '\0'; + strncat(qh->qhull_command, command, sizeof(qh->qhull_command)-strlen(qh->qhull_command)-1); + } + while (*s && !isspace(*s)) /* skip program name */ + s++; + } + while (*s) { + while (*s && isspace(*s)) + s++; + if (*s == '-') + s++; + if (!*s) + break; + prev_s= s; + switch (*s++) { + case 'd': + qh_option(qh, "delaunay", NULL, NULL); + qh->DELAUNAY= True; + break; + case 'f': + qh_option(qh, "facets", NULL, NULL); + qh_appendprint(qh, qh_PRINTfacets); + break; + case 'i': + qh_option(qh, "incidence", NULL, NULL); + qh_appendprint(qh, qh_PRINTincidences); + break; + case 'm': + qh_option(qh, "mathematica", NULL, NULL); + qh_appendprint(qh, qh_PRINTmathematica); + break; + case 'n': + qh_option(qh, "normals", NULL, NULL); + qh_appendprint(qh, qh_PRINTnormals); + break; + case 'o': + qh_option(qh, "offFile", NULL, NULL); + qh_appendprint(qh, qh_PRINToff); + break; + case 'p': + qh_option(qh, "points", NULL, NULL); + qh_appendprint(qh, qh_PRINTpoints); + break; + case 's': + qh_option(qh, "summary", NULL, NULL); + qh->PRINTsummary= True; + break; + case 'v': + qh_option(qh, "voronoi", NULL, NULL); + qh->VORONOI= True; + qh->DELAUNAY= True; + break; + case 'A': + if (!isdigit(*s) && *s != '.' && *s != '-') + qh_fprintf(qh, qh->ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'. Ignored.\n"); + else { + if (*s == '-') { + qh->premerge_cos= -qh_strtod(s, &s); + qh_option(qh, "Angle-premerge-", NULL, &qh->premerge_cos); + qh->PREmerge= True; + }else { + qh->postmerge_cos= qh_strtod(s, &s); + qh_option(qh, "Angle-postmerge", NULL, &qh->postmerge_cos); + qh->POSTmerge= True; + } + qh->MERGING= True; + } + break; + case 'C': + if (!isdigit(*s) && *s != '.' && *s != '-') + qh_fprintf(qh, qh->ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'. Ignored.\n"); + else { + if (*s == '-') { + qh->premerge_centrum= -qh_strtod(s, &s); + qh_option(qh, "Centrum-premerge-", NULL, &qh->premerge_centrum); + qh->PREmerge= True; + }else { + qh->postmerge_centrum= qh_strtod(s, &s); + qh_option(qh, "Centrum-postmerge", NULL, &qh->postmerge_centrum); + qh->POSTmerge= True; + } + qh->MERGING= True; + } + break; + case 'E': + if (*s == '-') + qh_fprintf(qh, qh->ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n"); + else if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'. Ignored.\n"); + else { + qh->DISTround= qh_strtod(s, &s); + qh_option(qh, "Distance-roundoff", NULL, &qh->DISTround); + qh->SETroundoff= True; + } + break; + case 'H': + start= s; + qh->HALFspace= True; + qh_strtod(s, &t); + while (t > s) { + if (*t && !isspace(*t)) { + if (*t == ',') + t++; + else + qh_fprintf(qh, qh->ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n"); + } + s= t; + qh_strtod(s, &t); + } + if (start < t) { + if (!(qh->feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) { + qh_fprintf(qh, qh->ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + strncpy(qh->feasible_string, start, (size_t)(t-start)); + qh_option(qh, "Halfspace-about", NULL, NULL); + qh_option(qh, qh->feasible_string, NULL, NULL); + }else + qh_option(qh, "Halfspace", NULL, NULL); + break; + case 'R': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'. Ignored\n"); + else { + qh->RANDOMfactor= qh_strtod(s, &s); + qh_option(qh, "Random_perturb", NULL, &qh->RANDOMfactor); + qh->RANDOMdist= True; + } + break; + case 'V': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh, qh->ferr, 7008, "qhull warning: missing visible distance for option 'Vn'. Ignored\n"); + else { + qh->MINvisible= qh_strtod(s, &s); + qh_option(qh, "Visible", NULL, &qh->MINvisible); + } + break; + case 'U': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh, qh->ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'. Ignored\n"); + else { + qh->MAXcoplanar= qh_strtod(s, &s); + qh_option(qh, "U-coplanar", NULL, &qh->MAXcoplanar); + } + break; + case 'W': + if (*s == '-') + qh_fprintf(qh, qh->ferr, 7010, "qhull warning: negative outside width for option 'Wn'. Ignored.\n"); + else if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7011, "qhull warning: missing outside width for option 'Wn'. Ignored\n"); + else { + qh->MINoutside= qh_strtod(s, &s); + qh_option(qh, "W-outside", NULL, &qh->MINoutside); + qh->APPROXhull= True; + } + break; + /************ sub menus ***************/ + case 'F': + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option(qh, "Farea", NULL, NULL); + qh_appendprint(qh, qh_PRINTarea); + qh->GETarea= True; + break; + case 'A': + qh_option(qh, "FArea-total", NULL, NULL); + qh->GETarea= True; + break; + case 'c': + qh_option(qh, "Fcoplanars", NULL, NULL); + qh_appendprint(qh, qh_PRINTcoplanars); + break; + case 'C': + qh_option(qh, "FCentrums", NULL, NULL); + qh_appendprint(qh, qh_PRINTcentrums); + break; + case 'd': + qh_option(qh, "Fd-cdd-in", NULL, NULL); + qh->CDDinput= True; + break; + case 'D': + qh_option(qh, "FD-cdd-out", NULL, NULL); + qh->CDDoutput= True; + break; + case 'F': + qh_option(qh, "FFacets-xridge", NULL, NULL); + qh_appendprint(qh, qh_PRINTfacets_xridge); + break; + case 'i': + qh_option(qh, "Finner", NULL, NULL); + qh_appendprint(qh, qh_PRINTinner); + break; + case 'I': + qh_option(qh, "FIDs", NULL, NULL); + qh_appendprint(qh, qh_PRINTids); + break; + case 'm': + qh_option(qh, "Fmerges", NULL, NULL); + qh_appendprint(qh, qh_PRINTmerges); + break; + case 'M': + qh_option(qh, "FMaple", NULL, NULL); + qh_appendprint(qh, qh_PRINTmaple); + break; + case 'n': + qh_option(qh, "Fneighbors", NULL, NULL); + qh_appendprint(qh, qh_PRINTneighbors); + break; + case 'N': + qh_option(qh, "FNeighbors-vertex", NULL, NULL); + qh_appendprint(qh, qh_PRINTvneighbors); + break; + case 'o': + qh_option(qh, "Fouter", NULL, NULL); + qh_appendprint(qh, qh_PRINTouter); + break; + case 'O': + if (qh->PRINToptions1st) { + qh_option(qh, "FOptions", NULL, NULL); + qh_appendprint(qh, qh_PRINToptions); + }else + qh->PRINToptions1st= True; + break; + case 'p': + qh_option(qh, "Fpoint-intersect", NULL, NULL); + qh_appendprint(qh, qh_PRINTpointintersect); + break; + case 'P': + qh_option(qh, "FPoint-nearest", NULL, NULL); + qh_appendprint(qh, qh_PRINTpointnearest); + break; + case 'Q': + qh_option(qh, "FQhull", NULL, NULL); + qh_appendprint(qh, qh_PRINTqhull); + break; + case 's': + qh_option(qh, "Fsummary", NULL, NULL); + qh_appendprint(qh, qh_PRINTsummary); + break; + case 'S': + qh_option(qh, "FSize", NULL, NULL); + qh_appendprint(qh, qh_PRINTsize); + qh->GETarea= True; + break; + case 't': + qh_option(qh, "Ftriangles", NULL, NULL); + qh_appendprint(qh, qh_PRINTtriangles); + break; + case 'v': + /* option set in qh_initqhull_globals */ + qh_appendprint(qh, qh_PRINTvertices); + break; + case 'V': + qh_option(qh, "FVertex-average", NULL, NULL); + qh_appendprint(qh, qh_PRINTaverage); + break; + case 'x': + qh_option(qh, "Fxtremes", NULL, NULL); + qh_appendprint(qh, qh_PRINTextremes); + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'G': + isgeom= True; + qh_appendprint(qh, qh_PRINTgeom); + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option(qh, "Gall-points", NULL, NULL); + qh->PRINTdots= True; + break; + case 'c': + qh_option(qh, "Gcentrums", NULL, NULL); + qh->PRINTcentrums= True; + break; + case 'h': + qh_option(qh, "Gintersections", NULL, NULL); + qh->DOintersections= True; + break; + case 'i': + qh_option(qh, "Ginner", NULL, NULL); + qh->PRINTinner= True; + break; + case 'n': + qh_option(qh, "Gno-planes", NULL, NULL); + qh->PRINTnoplanes= True; + break; + case 'o': + qh_option(qh, "Gouter", NULL, NULL); + qh->PRINTouter= True; + break; + case 'p': + qh_option(qh, "Gpoints", NULL, NULL); + qh->PRINTcoplanar= True; + break; + case 'r': + qh_option(qh, "Gridges", NULL, NULL); + qh->PRINTridges= True; + break; + case 't': + qh_option(qh, "Gtransparent", NULL, NULL); + qh->PRINTtransparent= True; + break; + case 'v': + qh_option(qh, "Gvertices", NULL, NULL); + qh->PRINTspheres= True; + break; + case 'D': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n"); + else { + if (qh->DROPdim >= 0) + qh_fprintf(qh, qh->ferr, 7013, "qhull warning: can only drop one dimension. Previous 'GD%d' ignored\n", + qh->DROPdim); + qh->DROPdim= qh_strtol(s, &s); + qh_option(qh, "GDrop-dim", &qh->DROPdim, NULL); + } + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'P': + while (*s && !isspace(*s)) { + switch (*s++) { + case 'd': case 'D': /* see qh_initthresholds() */ + key= s[-1]; + i= qh_strtol(s, &s); + r= 0; + if (*s == ':') { + s++; + r= qh_strtod(s, &s); + } + if (key == 'd') + qh_option(qh, "Pdrop-facets-dim-less", &i, &r); + else + qh_option(qh, "PDrop-facets-dim-more", &i, &r); + break; + case 'g': + qh_option(qh, "Pgood-facets", NULL, NULL); + qh->PRINTgood= True; + break; + case 'G': + qh_option(qh, "PGood-facet-neighbors", NULL, NULL); + qh->PRINTneighbors= True; + break; + case 'o': + qh_option(qh, "Poutput-forced", NULL, NULL); + qh->FORCEoutput= True; + break; + case 'p': + qh_option(qh, "Pprecision-ignore", NULL, NULL); + qh->PRINTprecision= False; + break; + case 'A': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n"); + else { + qh->KEEParea= qh_strtol(s, &s); + qh_option(qh, "PArea-keep", &qh->KEEParea, NULL); + qh->GETarea= True; + } + break; + case 'F': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n"); + else { + qh->KEEPminArea= qh_strtod(s, &s); + qh_option(qh, "PFacet-area-keep", NULL, &qh->KEEPminArea); + qh->GETarea= True; + } + break; + case 'M': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n"); + else { + qh->KEEPmerge= qh_strtol(s, &s); + qh_option(qh, "PMerge-keep", &qh->KEEPmerge, NULL); + } + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'Q': + lastproject= -1; + while (*s && !isspace(*s)) { + switch (*s++) { + case 'b': case 'B': /* handled by qh_initthresholds */ + key= s[-1]; + if (key == 'b' && *s == 'B') { + s++; + r= qh_DEFAULTbox; + qh->SCALEinput= True; + qh_option(qh, "QbBound-unit-box", NULL, &r); + break; + } + if (key == 'b' && *s == 'b') { + s++; + qh->SCALElast= True; + qh_option(qh, "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, qh->ferr, 2004, "qh_initflags: project dimension %d\n", k)); + qh_option(qh, "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(qh, "Qbound-dim-low", &k, &r); + }else { + qh->SCALEinput= True; + if (r == 0.0) + r= qh_DEFAULTbox; + qh_option(qh, "QBound-dim-high", &k, &r); + } + } + break; + case 'c': + qh_option(qh, "Qcoplanar-keep", NULL, NULL); + qh->KEEPcoplanar= True; + break; + case 'f': + qh_option(qh, "Qfurthest-outside", NULL, NULL); + qh->BESToutside= True; + break; + case 'g': + qh_option(qh, "Qgood-facets-only", NULL, NULL); + qh->ONLYgood= True; + break; + case 'i': + qh_option(qh, "Qinterior-keep", NULL, NULL); + qh->KEEPinside= True; + break; + case 'm': + qh_option(qh, "Qmax-outside-only", NULL, NULL); + qh->ONLYmax= True; + break; + case 'r': + qh_option(qh, "Qrandom-outside", NULL, NULL); + qh->RANDOMoutside= True; + break; + case 's': + qh_option(qh, "Qsearch-initial-simplex", NULL, NULL); + qh->ALLpoints= True; + break; + case 't': + qh_option(qh, "Qtriangulate", NULL, NULL); + qh->TRIangulate= True; + break; + case 'T': + qh_option(qh, "QTestPoints", NULL, NULL); + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n"); + else { + qh->TESTpoints= qh_strtol(s, &s); + qh_option(qh, "QTestPoints", &qh->TESTpoints, NULL); + } + break; + case 'u': + qh_option(qh, "QupperDelaunay", NULL, NULL); + qh->UPPERdelaunay= True; + break; + case 'v': + qh_option(qh, "Qvertex-neighbors-convex", NULL, NULL); + qh->TESTvneighbors= True; + break; + case 'x': + qh_option(qh, "Qxact-merge", NULL, NULL); + qh->MERGEexact= True; + break; + case 'z': + qh_option(qh, "Qz-infinity-point", NULL, NULL); + qh->ATinfinity= True; + break; + case '0': + qh_option(qh, "Q0-no-premerge", NULL, NULL); + qh->NOpremerge= True; + break; + case '1': + if (!isdigit(*s)) { + qh_option(qh, "Q1-no-angle-sort", NULL, NULL); + qh->ANGLEmerge= False; + break; + } + switch (*s++) { + case '0': + qh_option(qh, "Q10-no-narrow", NULL, NULL); + qh->NOnarrow= True; + break; + case '1': + qh_option(qh, "Q11-trinormals Qtriangulate", NULL, NULL); + qh->TRInormals= True; + qh->TRIangulate= True; + break; + case '2': + qh_option(qh, "Q12-no-wide-dup", NULL, NULL); + qh->NOwide= True; + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + break; + case '2': + qh_option(qh, "Q2-no-merge-independent", NULL, NULL); + qh->MERGEindependent= False; + goto LABELcheckdigit; + break; /* no warnings */ + case '3': + qh_option(qh, "Q3-no-merge-vertices", NULL, NULL); + qh->MERGEvertices= False; + LABELcheckdigit: + if (isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit. '%c' skipped.\n", + *s++); + break; + case '4': + qh_option(qh, "Q4-avoid-old-into-new", NULL, NULL); + qh->AVOIDold= True; + break; + case '5': + qh_option(qh, "Q5-no-check-outer", NULL, NULL); + qh->SKIPcheckmax= True; + break; + case '6': + qh_option(qh, "Q6-no-concave-merge", NULL, NULL); + qh->SKIPconvex= True; + break; + case '7': + qh_option(qh, "Q7-no-breadth-first", NULL, NULL); + qh->VIRTUALmemory= True; + break; + case '8': + qh_option(qh, "Q8-no-near-inside", NULL, NULL); + qh->NOnearinside= True; + break; + case '9': + qh_option(qh, "Q9-pick-furthest", NULL, NULL); + qh->PICKfurthest= True; + break; + case 'G': + i= qh_strtol(s, &t); + if (qh->GOODpoint) + qh_fprintf(qh, qh->ferr, 7018, "qhull warning: good point already defined for option 'QGn'. Ignored\n"); + else if (s == t) + qh_fprintf(qh, qh->ferr, 7019, "qhull warning: missing good point id for option 'QGn'. Ignored\n"); + else if (i < 0 || *s == '-') { + qh->GOODpoint= i-1; + qh_option(qh, "QGood-if-dont-see-point", &i, NULL); + }else { + qh->GOODpoint= i+1; + qh_option(qh, "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(qh, "QJoggle", NULL, &qh->JOGGLEmax); + } + break; + case 'R': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh, qh->ferr, 7020, "qhull warning: missing random seed for option 'QRn'. Ignored\n"); + else { + qh->ROTATErandom= i= qh_strtol(s, &s); + if (i > 0) + qh_option(qh, "QRotate-id", &i, NULL ); + else if (i < -1) + qh_option(qh, "QRandom-seed", &i, NULL ); + } + break; + case 'V': + i= qh_strtol(s, &t); + if (qh->GOODvertex) + qh_fprintf(qh, qh->ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n"); + else if (s == t) + qh_fprintf(qh, qh->ferr, 7022, "qhull warning: no good point id given for option 'QVn'. Ignored\n"); + else if (i < 0) { + qh->GOODvertex= i - 1; + qh_option(qh, "QV-good-facets-not-point", &i, NULL); + }else { + qh_option(qh, "QV-good-facets-point", &i, NULL); + qh->GOODvertex= i + 1; + } + s= t; + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'T': + while (*s && !isspace(*s)) { + if (isdigit(*s) || *s == '-') + qh->IStracing= qh_strtol(s, &s); + else switch (*s++) { + case 'a': + qh_option(qh, "Tannotate-output", NULL, NULL); + qh->ANNOTATEoutput= True; + break; + case 'c': + qh_option(qh, "Tcheck-frequently", NULL, NULL); + qh->CHECKfrequently= True; + break; + case 's': + qh_option(qh, "Tstatistics", NULL, NULL); + qh->PRINTstatistics= True; + break; + case 'v': + qh_option(qh, "Tverify", NULL, NULL); + qh->VERIFYoutput= True; + break; + case 'z': + if (qh->ferr == qh_FILEstderr) { + /* The C++ interface captures the output in qh_fprint_qhull() */ + qh_option(qh, "Tz-stdout", NULL, NULL); + qh->USEstdout= True; + }else if (!qh->fout) + qh_fprintf(qh, qh->ferr, 7024, "qhull warning: output file undefined(stdout). Option 'Tz' ignored.\n"); + else { + qh_option(qh, "Tz-stdout", NULL, NULL); + qh->USEstdout= True; + qh->ferr= qh->fout; + qh->qhmem.ferr= qh->fout; + } + break; + case 'C': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'. Ignored\n"); + else { + i= qh_strtol(s, &s); + qh_option(qh, "TCone-stop", &i, NULL); + qh->STOPcone= i + 1; + } + break; + case 'F': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'. Ignored\n"); + else { + qh->REPORTfreq= qh_strtol(s, &s); + qh_option(qh, "TFacet-log", &qh->REPORTfreq, NULL); + qh->REPORTfreq2= qh->REPORTfreq/2; /* for tracemerging() */ + } + break; + case 'I': + if (!isspace(*s)) + qh_fprintf(qh, qh->ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s); + while (isspace(*s)) + s++; + t= qh_skipfilename(qh, s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + s= t; + if (!freopen(filename, "r", stdin)) { + qh_fprintf(qh, qh->ferr, 6041, "qhull error: could not open file \"%s\".", filename); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else { + qh_option(qh, "TInput-file", NULL, NULL); + qh_option(qh, filename, NULL, NULL); + } + } + break; + case 'O': + if (!isspace(*s)) + qh_fprintf(qh, qh->ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s); + while (isspace(*s)) + s++; + t= qh_skipfilename(qh, s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + s= t; + if (!qh->fout) { + qh_fprintf(qh, qh->ferr, 6266, "qhull input warning: qh.fout was not set by caller. Cannot use option 'TO' to redirect output. Ignoring option 'TO'\n"); + }else if (!freopen(filename, "w", qh->fout)) { + qh_fprintf(qh, qh->ferr, 6044, "qhull error: could not open file \"%s\".", filename); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else { + qh_option(qh, "TOutput-file", NULL, NULL); + qh_option(qh, filename, NULL, NULL); + } + } + break; + case 'P': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7029, "qhull warning: missing point id for trace option 'TPn'. Ignored\n"); + else { + qh->TRACEpoint= qh_strtol(s, &s); + qh_option(qh, "Trace-point", &qh->TRACEpoint, NULL); + } + break; + case 'M': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'. Ignored\n"); + else { + qh->TRACEmerge= qh_strtol(s, &s); + qh_option(qh, "Trace-merge", &qh->TRACEmerge, NULL); + } + break; + case 'R': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'. Ignored\n"); + else { + qh->RERUN= qh_strtol(s, &s); + qh_option(qh, "TRerun", &qh->RERUN, NULL); + } + break; + case 'V': + i= qh_strtol(s, &t); + if (s == t) + qh_fprintf(qh, qh->ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'. Ignored\n"); + else if (i < 0) { + qh->STOPpoint= i - 1; + qh_option(qh, "TV-stop-before-point", &i, NULL); + }else { + qh->STOPpoint= i + 1; + qh_option(qh, "TV-stop-after-point", &i, NULL); + } + s= t; + break; + case 'W': + if (!isdigit(*s)) + qh_fprintf(qh, qh->ferr, 7033, "qhull warning: missing max width for trace option 'TWn'. Ignored\n"); + else { + qh->TRACEdist= (realT) qh_strtod(s, &s); + qh_option(qh, "TWide-trace", NULL, &qh->TRACEdist); + } + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + default: + qh_fprintf(qh, qh->ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1], + (int)s[-1]); + break; + } + if (s-1 == prev_s && *s && !isspace(*s)) { + qh_fprintf(qh, qh->ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n", + (int)*prev_s, (int)*prev_s); + while (*s && !isspace(*s)) + s++; + } + } + if (qh->STOPcone && qh->JOGGLEmax < REALmax/2) + qh_fprintf(qh, qh->ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n"); + if (isgeom && !qh->FORCEoutput && qh->PRINTout[1]) + qh_fprintf(qh, qh->ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n"); + /* set derived values in qh_initqhull_globals */ +} /* initflags */ + + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_buffers">-</a> + + qh_initqhull_buffers(qh) + initialize global memory buffers + + notes: + must match qh_freebuffers() +*/ +void qh_initqhull_buffers(qhT *qh) { + int k; + + qh->TEMPsize= (qh->qhmem.LASTsize - sizeof(setT))/SETelemsize; + if (qh->TEMPsize <= 0 || qh->TEMPsize > qh->qhmem.LASTsize) + qh->TEMPsize= 8; /* e.g., if qh_NOmem */ + qh->other_points= qh_setnew(qh, qh->TEMPsize); + qh->del_vertices= qh_setnew(qh, qh->TEMPsize); + qh->coplanarfacetset= qh_setnew(qh, qh->TEMPsize); + qh->NEARzero= (realT *)qh_memalloc(qh, qh->hull_dim * sizeof(realT)); + qh->lower_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT)); + qh->upper_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT)); + qh->lower_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT)); + qh->upper_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT)); + for (k=qh->input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */ + 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, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT)); + qh->gm_row= (coordT **)qh_memalloc(qh, (qh->hull_dim+1) * sizeof(coordT *)); +} /* initqhull_buffers */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_globals">-</a> + + qh_initqhull_globals(qh, points, numpoints, dim, ismalloc ) + initialize globals + if ismalloc + points were malloc'd and qhull should free at end + + returns: + sets qh.first_point, num_points, input_dim, hull_dim and others + seeds random number generator (seed=1 if tracing) + modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput) + adjust user flags as needed + also checks DIM3 dependencies and constants + + notes: + do not use qh_point() since an input transformation may move them elsewhere + + see: + qh_initqhull_start() sets default values for non-zero globals + + design: + initialize points array from input arguments + test for qh.ZEROcentrum + (i.e., use opposite vertex instead of cetrum for convexity testing) + initialize qh.CENTERtype, qh.normal_size, + qh.center_size, qh.TRACEpoint/level, + initialize and test random numbers + qh_initqhull_outputflags() -- adjust and test output flags +*/ +void qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) { + int seed, pointsneeded, extra= 0, i, randi, k; + realT randr; + realT factorial; + + time_t timedata; + + trace0((qh, qh->ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh->rbox_command, + qh->qhull_command)); + qh->POINTSmalloc= ismalloc; + qh->first_point= points; + qh->num_points= numpoints; + qh->hull_dim= qh->input_dim= dim; + if (!qh->NOpremerge && !qh->MERGEexact && !qh->PREmerge && qh->JOGGLEmax > REALmax/2) { + qh->MERGING= True; + if (qh->hull_dim <= 4) { + qh->PREmerge= True; + qh_option(qh, "_pre-merge", NULL, NULL); + }else { + qh->MERGEexact= True; + qh_option(qh, "Qxact_merge", NULL, NULL); + } + }else if (qh->MERGEexact) + qh->MERGING= True; + if (!qh->NOpremerge && qh->JOGGLEmax > REALmax/2) { +#ifdef qh_NOmerge + qh->JOGGLEmax= 0.0; +#endif + } + if (qh->TRIangulate && qh->JOGGLEmax < REALmax/2 && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output. Triangulated output('Qt') does nothing.\n"); + if (qh->JOGGLEmax < REALmax/2 && qh->DELAUNAY && !qh->SCALEinput && !qh->SCALElast) { + qh->SCALElast= True; + qh_option(qh, "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(qh, "_zero-centrum", NULL, NULL); + } + if (qh->JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n", + REALepsilon); +#ifdef qh_NOmerge + if (qh->MERGING) { + qh_fprintf(qh, qh->ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } +#endif + if (qh->DELAUNAY && qh->KEEPcoplanar && !qh->KEEPinside) { + qh->KEEPinside= True; + qh_option(qh, "Qinterior-keep", NULL, NULL); + } + if (qh->DELAUNAY && qh->HALFspace) { + qh_fprintf(qh, qh->ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (!qh->DELAUNAY && (qh->UPPERdelaunay || qh->ATinfinity)) { + qh_fprintf(qh, qh->ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->UPPERdelaunay && qh->ATinfinity) { + qh_fprintf(qh, qh->ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->SCALElast && !qh->DELAUNAY && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n"); + qh->DOcheckmax= (!qh->SKIPcheckmax && qh->MERGING ); + qh->KEEPnearinside= (qh->DOcheckmax && !(qh->KEEPinside && qh->KEEPcoplanar) + && !qh->NOnearinside); + if (qh->MERGING) + qh->CENTERtype= qh_AScentrum; + else if (qh->VORONOI) + qh->CENTERtype= qh_ASvoronoi; + if (qh->TESTvneighbors && !qh->MERGING) { + qh_fprintf(qh, qh->ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n"); + qh_errexit(qh, qh_ERRinput, NULL ,NULL); + } + if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay)) { + qh->hull_dim -= qh->PROJECTinput; + if (qh->DELAUNAY) { + qh->hull_dim++; + if (qh->ATinfinity) + extra= 1; + } + } + if (qh->hull_dim <= 1) { + qh_fprintf(qh, qh->ferr, 6050, "qhull error: dimension %d must be > 1\n", qh->hull_dim); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + for (k=2, factorial=1.0; k < qh->hull_dim; k++) + factorial *= k; + qh->AREAfactor= 1.0 / factorial; + trace2((qh, qh->ferr, 2005, "qh_initqhull_globals: initialize globals. dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n", + dim, numpoints, ismalloc, qh->PROJECTinput, qh->hull_dim)); + qh->normal_size= qh->hull_dim * sizeof(coordT); + qh->center_size= qh->normal_size - sizeof(coordT); + pointsneeded= qh->hull_dim+1; + if (qh->hull_dim > qh_DIMmergeVertex) { + qh->MERGEvertices= False; + qh_option(qh, "Q3-no-merge-vertices-dim-high", NULL, NULL); + } + if (qh->GOODpoint) + pointsneeded++; +#ifdef qh_NOtrace + if (qh->IStracing) { + qh_fprintf(qh, qh->ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)"); + qh_errexit(qh, 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 != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) { + qh->TRACElevel= (qh->IStracing? qh->IStracing : 3); + qh->IStracing= 0; + } + if (qh->ROTATErandom == 0 || qh->ROTATErandom == -1) { + seed= (int)time(&timedata); + if (qh->ROTATErandom == -1) { + seed= -seed; + qh_option(qh, "QRandom-seed", &seed, NULL ); + }else + qh_option(qh, "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_(qh, seed); + randr= 0.0; + for (i=1000; i--; ) { + randi= qh_RANDOMint; + randr += randi; + if (randi > qh_RANDOMmax) { + qh_fprintf(qh, qh->ferr, 8036, "\ +qhull configuration error (qh_RANDOMmax in user.h):\n\ + random integer %d > qh_RANDOMmax(qh, %.8g)\n", + randi, qh_RANDOMmax); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + } + qh_RANDOMseed_(qh, seed); + randr = randr/1000; + if (randr < qh_RANDOMmax * 0.1 + || randr > qh_RANDOMmax * 0.9) + qh_fprintf(qh, qh->ferr, 8037, "\ +qhull configuration warning (qh_RANDOMmax in user.h):\n\ + average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\ + Is qh_RANDOMmax (%.2g) wrong?\n", + randr, qh_RANDOMmax * 0.5, qh_RANDOMmax); + qh->RANDOMa= 2.0 * qh->RANDOMfactor/qh_RANDOMmax; + qh->RANDOMb= 1.0 - qh->RANDOMfactor; + if (qh_HASHfactor < 1.1) { + qh_fprintf(qh, qh->ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1. Qhull uses linear hash probing\n", + qh_HASHfactor); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (numpoints+extra < pointsneeded) { + qh_fprintf(qh, qh->ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n", + numpoints, pointsneeded); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + qh_initqhull_outputflags(qh); +} /* initqhull_globals */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_mem">-</a> + + qh_initqhull_mem(qh, ) + initialize mem_r.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(qh) to add up to 10 additional sizes for quick allocation + (see numsizes below) + + returns: + mem_r.c already for qh_memalloc/qh_memfree (errors if called beforehand) + + notes: + qh_produceoutput() prints memsizes + +*/ +void qh_initqhull_mem(qhT *qh) { + int numsizes; + int i; + + numsizes= 8+10; + qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, numsizes, + qh_MEMbufsize, qh_MEMinitbuf); + qh_memsize(qh, (int)sizeof(vertexT)); + if (qh->MERGING) { + qh_memsize(qh, (int)sizeof(ridgeT)); + qh_memsize(qh, (int)sizeof(mergeT)); + } + qh_memsize(qh, (int)sizeof(facetT)); + i= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize; /* ridge.vertices */ + qh_memsize(qh, i); + qh_memsize(qh, qh->normal_size); /* normal */ + i += SETelemsize; /* facet.vertices, .ridges, .neighbors */ + qh_memsize(qh, i); + qh_user_memsizes(qh); + qh_memsetup(qh); +} /* initqhull_mem */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_outputflags">-</a> + + qh_initqhull_outputflags + initialize flags concerned with output + + returns: + adjust user flags as needed + + see: + qh_clear_outputflags() resets the flags + + design: + test for qh.PRINTgood (i.e., only print 'good' facets) + check for conflicting print output options +*/ +void qh_initqhull_outputflags(qhT *qh) { + boolT printgeom= False, printmath= False, printcoplanar= False; + int i; + + trace3((qh, qh->ferr, 3024, "qh_initqhull_outputflags: %s\n", qh->qhull_command)); + if (!(qh->PRINTgood || qh->PRINTneighbors)) { + if (qh->KEEParea || qh->KEEPminArea < REALmax/2 || qh->KEEPmerge || qh->DELAUNAY + || (!qh->ONLYgood && (qh->GOODvertex || qh->GOODpoint))) { + qh->PRINTgood= True; + qh_option(qh, "Pgood", NULL, NULL); + } + } + if (qh->PRINTtransparent) { + if (qh->hull_dim != 4 || !qh->DELAUNAY || qh->VORONOI || qh->DROPdim >= 0) { + qh_fprintf(qh, qh->ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + qh->DROPdim = 3; + qh->PRINTridges = True; + } + for (i=qh_PRINTEND; i--; ) { + if (qh->PRINTout[i] == qh_PRINTgeom) + printgeom= True; + else if (qh->PRINTout[i] == qh_PRINTmathematica || qh->PRINTout[i] == qh_PRINTmaple) + printmath= True; + else if (qh->PRINTout[i] == qh_PRINTcoplanars) + printcoplanar= True; + else if (qh->PRINTout[i] == qh_PRINTpointnearest) + printcoplanar= True; + else if (qh->PRINTout[i] == qh_PRINTpointintersect && !qh->HALFspace) { + qh_fprintf(qh, qh->ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else if (qh->PRINTout[i] == qh_PRINTtriangles && (qh->HALFspace || qh->VORONOI)) { + qh_fprintf(qh, qh->ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else if (qh->PRINTout[i] == qh_PRINTcentrums && qh->VORONOI) { + qh_fprintf(qh, qh->ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else if (qh->PRINTout[i] == qh_PRINTvertices) { + if (qh->VORONOI) + qh_option(qh, "Fvoronoi", NULL, NULL); + else + qh_option(qh, "Fvertices", NULL, NULL); + } + } + if (printcoplanar && qh->DELAUNAY && qh->JOGGLEmax < REALmax/2) { + if (qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n"); + } + if (printmath && (qh->hull_dim > 3 || qh->VORONOI)) { + qh_fprintf(qh, qh->ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (printgeom) { + if (qh->hull_dim > 4) { + qh_fprintf(qh, qh->ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->PRINTnoplanes && !(qh->PRINTcoplanar + qh->PRINTcentrums + + qh->PRINTdots + qh->PRINTspheres + qh->DOintersections + qh->PRINTridges)) { + qh_fprintf(qh, qh->ferr, 6058, "qhull input error: no output specified for Geomview\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->VORONOI && (qh->hull_dim > 3 || qh->DROPdim >= 0)) { + qh_fprintf(qh, qh->ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + /* can not warn about furthest-site Geomview output: no lower_threshold */ + if (qh->hull_dim == 4 && qh->DROPdim == -1 && + (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) { + qh_fprintf(qh, qh->ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\ +available for 4-d output(ignored). Could use 'GDn' instead.\n"); + qh->PRINTcoplanar= qh->PRINTspheres= qh->PRINTcentrums= False; + } + } + if (!qh->KEEPcoplanar && !qh->KEEPinside && !qh->ONLYgood) { + if ((qh->PRINTcoplanar && qh->PRINTspheres) || printcoplanar) { + if (qh->QHULLfinished) { + qh_fprintf(qh, qh->ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n"); + }else { + qh->KEEPcoplanar = True; + qh_option(qh, "Qcoplanar", NULL, NULL); + } + } + } + qh->PRINTdim= qh->hull_dim; + if (qh->DROPdim >=0) { /* after Geomview checks */ + if (qh->DROPdim < qh->hull_dim) { + qh->PRINTdim--; + if (!printgeom || qh->hull_dim < 3) + qh_fprintf(qh, qh->ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh->DROPdim); + }else + qh->DROPdim= -1; + }else if (qh->VORONOI) { + qh->DROPdim= qh->hull_dim-1; + qh->PRINTdim= qh->hull_dim-1; + } +} /* qh_initqhull_outputflags */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_start">-</a> + + qh_initqhull_start(qh, infile, outfile, errfile ) + allocate memory if needed and call qh_initqhull_start2() +*/ +void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) { + + qh_initstatistics(qh); + qh_initqhull_start2(qh, infile, outfile, errfile); +} /* initqhull_start */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_start2">-</a> + + qh_initqhull_start2(qh, infile, outfile, errfile ) + start initialization of qhull + initialize statistics, stdio, default values for global variables + assumes qh is allocated + notes: + report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized + see: + qh_maxmin() determines the precision constants + qh_freeqhull() +*/ +void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) { + time_t timedata; + int seed; + + qh_CPUclock; /* start the clock(for qh_clock). One-shot. */ + /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */ + memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT)); /* every field is 0, FALSE, NULL */ + qh->NOerrexit= True; + qh->ANGLEmerge= True; + qh->DROPdim= -1; + qh->ferr= errfile; + qh->fin= infile; + qh->fout= outfile; + qh->furthest_id= qh_IDunknown; + qh->JOGGLEmax= REALmax; + qh->KEEPminArea = REALmax; + qh->last_low= REALmax; + qh->last_high= REALmax; + qh->last_newhigh= REALmax; + qh->last_random= 1; + 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= qh_IDunknown; /* recompile or use 'TPn' */ + qh->tracefacet_id= UINT_MAX; /* recompile to trace a facet */ + qh->tracevertex_id= UINT_MAX; /* recompile to trace a vertex */ + seed= (int)time(&timedata); + qh_RANDOMseed_(qh, seed); + qh->run_id= qh_RANDOMint; + if(!qh->run_id) + qh->run_id++; /* guarantee non-zero */ + qh_option(qh, "run-id", &qh->run_id, NULL); + strcat(qh->qhull, "qhull"); +} /* initqhull_start2 */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initthresholds">-</a> + + qh_initthresholds(qh, 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(qhT *qh, char *command) { + realT value; + int idx, maxdim, k; + char *s= command; /* non-const due to strtol */ + char key; + + maxdim= qh->input_dim; + if (qh->DELAUNAY && (qh->PROJECTdelaunay || qh->PROJECTinput)) + maxdim++; + while (*s) { + if (*s == '-') + s++; + if (*s == 'P') { + s++; + while (*s && !isspace(key= *s++)) { + if (key == 'd' || key == 'D') { + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n", + key, s-1); + continue; + } + idx= qh_strtol(s, &s); + if (idx >= qh->hull_dim) { + qh_fprintf(qh, qh->ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n", + idx, key, qh->hull_dim); + continue; + } + if (*s == ':') { + s++; + value= qh_strtod(s, &s); + if (fabs((double)value) > 1.0) { + qh_fprintf(qh, qh->ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1. Ignored\n", + value, key); + continue; + } + }else + value= 0.0; + if (key == 'd') + qh->lower_threshold[idx]= value; + else + qh->upper_threshold[idx]= value; + } + } + }else if (*s == 'Q') { + s++; + while (*s && !isspace(key= *s++)) { + if (key == 'b' && *s == 'B') { + s++; + for (k=maxdim; k--; ) { + qh->lower_bound[k]= -qh_DEFAULTbox; + qh->upper_bound[k]= qh_DEFAULTbox; + } + }else if (key == 'b' && *s == 'b') + s++; + else if (key == 'b' || key == 'B') { + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7047, "qhull warning: no dimension given for Qhull option %c. Ignored\n", + key); + continue; + } + idx= qh_strtol(s, &s); + if (idx >= maxdim) { + qh_fprintf(qh, qh->ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n", + idx, key, maxdim); + continue; + } + if (*s == ':') { + s++; + value= qh_strtod(s, &s); + }else if (key == 'b') + value= -qh_DEFAULTbox; + else + value= qh_DEFAULTbox; + if (key == 'b') + qh->lower_bound[idx]= value; + else + qh->upper_bound[idx]= value; + } + } + }else { + while (*s && !isspace(*s)) + s++; + } + while (isspace(*s)) + s++; + } + for (k=qh->hull_dim; k--; ) { + if (qh->lower_threshold[k] > -REALmax/2) { + qh->GOODthreshold= True; + if (qh->upper_threshold[k] < REALmax/2) { + qh->SPLITthresholds= True; + qh->GOODthreshold= False; + break; + } + }else if (qh->upper_threshold[k] < REALmax/2) + qh->GOODthreshold= True; + } +} /* initthresholds */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="lib_check">-</a> + + qh_lib_check( qhullLibraryType, qhTsize, vertexTsize, ridgeTsize, facetTsize, setTsize, qhmemTsize ) + Report error if library does not agree with caller + + notes: + NOerrors -- qh_lib_check can not call qh_errexit() +*/ +void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize) { + boolT iserror= False; + +#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) /* user_r.h */ + // _CrtSetBreakAlloc(744); /* Break at memalloc {744}, or 'watch' _crtBreakAlloc */ + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR ); +#endif + + if (qhullLibraryType==QHULL_NON_REENTRANT) { /* 0 */ + qh_fprintf_stderr(6257, "qh_lib_check: Incorrect qhull library called. Caller uses non-reentrant Qhull with a static qhT. Library is reentrant.\n"); + iserror= True; + }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */ + qh_fprintf_stderr(6258, "qh_lib_check: Incorrect qhull library called. Caller uses non-reentrant Qhull with a dynamic qhT via qh_QHpointer. Library is reentrant.\n"); + iserror= True; + }else if (qhullLibraryType!=QHULL_REENTRANT) { /* 2 */ + qh_fprintf_stderr(6262, "qh_lib_check: Expecting qhullLibraryType QHULL_NON_REENTRANT(0), QHULL_QH_POINTER(1), or QHULL_REENTRANT(2). Got %d\n", qhullLibraryType); + iserror= True; + } + if (qhTsize != sizeof(qhT)) { + qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called. Size of qhT for caller is %d, but for library is %d.\n", qhTsize, sizeof(qhT)); + iserror= True; + } + if (vertexTsize != sizeof(vertexT)) { + qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called. Size of vertexT for caller is %d, but for library is %d.\n", vertexTsize, sizeof(vertexT)); + iserror= True; + } + if (ridgeTsize != sizeof(ridgeT)) { + qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called. Size of ridgeT for caller is %d, but for library is %d.\n", ridgeTsize, sizeof(ridgeT)); + iserror= True; + } + if (facetTsize != sizeof(facetT)) { + qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called. Size of facetT for caller is %d, but for library is %d.\n", facetTsize, sizeof(facetT)); + iserror= True; + } + if (setTsize && setTsize != sizeof(setT)) { + qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called. Size of setT for caller is %d, but for library is %d.\n", setTsize, sizeof(setT)); + iserror= True; + } + if (qhmemTsize && qhmemTsize != sizeof(qhmemT)) { + qh_fprintf_stderr(6254, "qh_lib_check: Incorrect qhull library called. Size of qhmemT for caller is %d, but for library is %d.\n", qhmemTsize, sizeof(qhmemT)); + iserror= True; + } + if (iserror) { + qh_fprintf_stderr(6259, "qh_lib_check: Cannot continue. Library '%s' is reentrant (e.g., qhull_r.so)\n", qh_version2); + qh_exit(qh_ERRqhull); /* can not use qh_errexit() */ + } +} /* lib_check */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="option">-</a> + + qh_option(qh, option, intVal, realVal ) + add an option description to qh.qhull_options + + notes: + NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2] + will be printed with statistics ('Ts') and errors + strlen(option) < 40 +*/ +void qh_option(qhT *qh, const char *option, int *i, realT *r) { + char buf[200]; + int len, maxlen; + + sprintf(buf, " %s", option); + if (i) + sprintf(buf+strlen(buf), " %d", *i); + if (r) + sprintf(buf+strlen(buf), " %2.2g", *r); + len= (int)strlen(buf); /* WARN64 */ + qh->qhull_optionlen += len; + maxlen= sizeof(qh->qhull_options) - len -1; + maximize_(maxlen, 0); + if (qh->qhull_optionlen >= qh_OPTIONline && maxlen > 0) { + qh->qhull_optionlen= len; + strncat(qh->qhull_options, "\n", (size_t)(maxlen--)); + } + strncat(qh->qhull_options, buf, (size_t)maxlen); +} /* option */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="zero">-</a> + + qh_zero( qh, errfile ) + Initialize and zero Qhull's memory for qh_new_qhull() + + notes: + Not needed in global.c because static variables are initialized to zero +*/ +void qh_zero(qhT *qh, FILE *errfile) { + memset((char *)qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */ + qh->NOerrexit= True; + qh_meminit(qh, errfile); +} /* zero */ +
+ C/halfspaces.c view
@@ -0,0 +1,75 @@+#define qh_QHimport +#include "qhull_ra.h" + +double** intersections( + double* halfspaces, + double* ipoint, + unsigned dim, + unsigned n, + unsigned* nintersections, + unsigned* exitcode, + unsigned print +) { + char opts[250]; + snprintf(opts, sizeof(opts), "qhull s Fp H H"); + for(unsigned i = 0; i < dim; i++) { + int integerPart = ipoint[i]; // Get integer part. + double fracPart = fabs(ipoint[i] - integerPart); // Get fractional part. + int fractPart_e8 = trunc(fracPart * 1e8); // Turn it into integer. + double fracPart2 = fracPart - (double)fractPart_e8 / 1.0e8; + int fractPart_e16 = trunc(fracPart2 * 1e16); + snprintf( + opts + strlen(opts), sizeof(opts) - strlen(opts), + "%s%d.%d%d", i == 0 ? "" : ",", integerPart, fractPart_e8, fractPart_e16 + ); + } + // printf("%s", opts); printf("\n"); + + qhT qh_qh; /* Qhull's data structure */ + qhT* qh= &qh_qh; + QHULL_LIB_CHECK + qh_meminit(qh, stderr); + boolT ismalloc = False; /* True if qhull should free points + in qh_freeqhull() or reallocation */ + FILE *errfile = NULL; + FILE* outfile = print ? stdout : NULL; + qh_zero(qh, errfile); + *exitcode = qh_new_qhull( + qh, dim+1, n, halfspaces, ismalloc, opts, outfile, errfile + ); + // printf("exitcode: %u\n", *exitcode); + + double** out; + if(!(*exitcode)) { + *nintersections = qh->num_facets; + out = malloc(*nintersections * sizeof(double*)); + facetT *facet; + unsigned i_facet = 0; + FORALLfacets { + if(facet->offset != 0) { + out[i_facet] = malloc(dim * sizeof(double)); + for(unsigned i = 0; i < dim; i++){ + out[i_facet][i] = - facet->normal[i] / facet->offset + + qh->feasible_point[i]; // = interiorpoint ? yes + } + i_facet++; + } else { + (*nintersections)--; + } + } + + } + + /* Do cleanup regardless of whether there is an error */ + int curlong, totlong; + qh_freeqhull(qh, !qh_ALL); /* free long memory */ + qh_memfreeshort(qh, &curlong, &totlong); /* free short memory + and memory allocator */ + if(*exitcode) { + free(out); + return 0; + } else { + return out; + } + +}
+ C/io_r.c view
@@ -0,0 +1,4062 @@+/*<html><pre> -<a href="qh-io_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + io_r.c + Input/Output routines of qhull application + + see qh-io_r.htm and io_r.h + + see user_r.c for qh_errprint and qh_printfacetlist + + unix_r.c calls qh_readpoints and qh_produce_output + + unix_r.c and user_r.c are the only callers of io_r.c functions + This allows the user to avoid loading io_r.o from qhull.a + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/io_r.c#4 $$Change: 2064 $ + $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*========= -functions in alphabetical order after qh_produce_output(qh) =====*/ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="produce_output">-</a> + + qh_produce_output(qh) + qh_produce_output2(qh) + prints out the result of qhull in desired format + qh_produce_output2(qh) does not call qh_prepare_output(qh) + 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(qhT *qh) { + int tempsize= qh_setsize(qh, qh->qhmem.tempstack); + + qh_prepare_output(qh); + qh_produce_output2(qh); + if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) { + qh_fprintf(qh, qh->ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n", + qh_setsize(qh, qh->qhmem.tempstack)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +} /* produce_output */ + + +void qh_produce_output2(qhT *qh) { + int i, tempsize= qh_setsize(qh, qh->qhmem.tempstack), d_1; + + if (qh->PRINTsummary) + qh_printsummary(qh, qh->ferr); + else if (qh->PRINTout[0] == qh_PRINTnone) + qh_printsummary(qh, qh->fout); + for (i=0; i < qh_PRINTEND; i++) + qh_printfacets(qh, qh->fout, qh->PRINTout[i], qh->facet_list, NULL, !qh_ALL); + qh_allstatistics(qh); + if (qh->PRINTprecision && !qh->MERGING && (qh->JOGGLEmax > REALmax/2 || qh->RERUN)) + qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL); + if (qh->VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0)) + qh_printstats(qh, qh->ferr, qh->qhstat.vridges, NULL); + if (qh->PRINTstatistics) { + qh_printstatistics(qh, qh->ferr, ""); + qh_memstatistics(qh, qh->ferr); + d_1= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize; + qh_fprintf(qh, qh->ferr, 8040, "\ + size in bytes: merge %d ridge %d vertex %d facet %d\n\ + normal %d ridge vertices %d facet vertices or neighbors %d\n", + (int)sizeof(mergeT), (int)sizeof(ridgeT), + (int)sizeof(vertexT), (int)sizeof(facetT), + qh->normal_size, d_1, d_1 + SETelemsize); + } + if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) { + qh_fprintf(qh, qh->ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n", + qh_setsize(qh, qh->qhmem.tempstack)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +} /* produce_output2 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="dfacet">-</a> + + qh_dfacet(qh, id ) + print facet by id, for debugging + +*/ +void qh_dfacet(qhT *qh, unsigned id) { + facetT *facet; + + FORALLfacets { + if (facet->id == id) { + qh_printfacet(qh, qh->fout, facet); + break; + } + } +} /* dfacet */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="dvertex">-</a> + + qh_dvertex(qh, id ) + print vertex by id, for debugging +*/ +void qh_dvertex(qhT *qh, unsigned id) { + vertexT *vertex; + + FORALLvertices { + if (vertex->id == id) { + qh_printvertex(qh, qh->fout, vertex); + break; + } + } +} /* dvertex */ + + +/*-<a href="qh-io_r.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) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); + + if (!a->isarea) + return -1; + if (!b->isarea) + return 1; + if (a->f.area > b->f.area) + return 1; + else if (a->f.area == b->f.area) + return 0; + return -1; +} /* compare_facetarea */ + +/*-<a href="qh-io_r.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) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); + + return(a->nummerge - b->nummerge); +} /* compare_facetvisit */ + +/*-<a href="qh-io_r.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) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); + int i,j; + + if (!(i= a->visitid)) + i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */ + if (!(j= b->visitid)) + j= 0 - b->id; + return(i - j); +} /* compare_facetvisit */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="compare_vertexpoint">-</a> + + qh_compare_vertexpoint( p1, p2 ) + used by qsort() to order vertices by point id + + Not usable in qhulllib_r since qh_pointid depends on qh + + int qh_compare_vertexpoint(const void *p1, const void *p2) { + const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); + + return((qh_pointid(qh, a->point) > qh_pointid(qh, b->point)?1:-1)); +}*/ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="copyfilename">-</a> + + qh_copyfilename(qh, dest, size, source, length ) + copy filename identified by qh_skipfilename() + + notes: + see qh_skipfilename() for syntax +*/ +void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length) { + char c= *source; + + if (length > size + 1) { + qh_fprintf(qh, qh->ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + strncpy(filename, source, length); + filename[length]= '\0'; + if (c == '\'' || c == '"') { + char *s= filename + 1; + char *t= filename; + while (*s) { + if (*s == c) { + if (s[-1] == '\\') + t[-1]= c; + }else + *t++= *s; + s++; + } + *t= '\0'; + } +} /* copyfilename */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="countfacets">-</a> + + qh_countfacets(qh, 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(qhT *qh, 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(qh, facet))) + facet->visitid= 0; + else { + facet->visitid= ++numfacets; + totneighbors += qh_setsize(qh, facet->neighbors); + if (facet->simplicial) { + numsimplicial++; + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else + numridges += qh_setsize(qh, facet->ridges); + if (facet->coplanarset) + numcoplanars += qh_setsize(qh, facet->coplanarset); + } + } + + FOREACHfacet_(facets) { + if ((facet->visible && qh->NEWfacets) + || (!printall && qh_skipfacet(qh, facet))) + facet->visitid= 0; + else { + facet->visitid= ++numfacets; + totneighbors += qh_setsize(qh, facet->neighbors); + if (facet->simplicial){ + numsimplicial++; + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else + numridges += qh_setsize(qh, facet->ridges); + if (facet->coplanarset) + numcoplanars += qh_setsize(qh, facet->coplanarset); + } + } + qh->visit_id += numfacets+1; + *numfacetsp= numfacets; + *numsimplicialp= numsimplicial; + *totneighborsp= totneighbors; + *numridgesp= numridges; + *numcoplanarsp= numcoplanars; + *numtricoplanarsp= numtricoplanars; +} /* countfacets */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="detvnorm">-</a> + + qh_detvnorm(qh, 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_r.c so that code for 'v Tv' can be removed by removing io_r.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(qhT *qh, 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, 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(qh, facet->vertices); + qh_setappend(qh, &points, facet->center); + } + } + if (numcenters > dim) { + simplex= qh_settemp(qh, qh->TEMPsize); + qh_setappend(qh, &simplex, vertex->point); + if (unbounded) + qh_setappend(qh, &simplex, midpoint); + qh_maxsimplex(qh, dim, points, NULL, 0, &simplex); + qh_setdelnth(qh, simplex, 0); + }else if (numcenters == dim) { + if (unbounded) + qh_setappend(qh, &points, midpoint); + simplex= points; + }else { + qh_fprintf(qh, qh->ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + i= 0; + gmcoord= qh->gm_matrix; + point0= SETfirstt_(simplex, pointT); + FOREACHpoint_(simplex) { + if (qh->IStracing >= 4) + qh_printmatrix(qh, 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(qh, 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(qh, vertex->point); + pointidA= qh_pointid(qh, 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, qh->ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n", + pointid, pointidA, dist)); + for (k=0; k < dim; k++) + midpoint[k]= vertexA->point[k] - vertex->point[k]; /* overwrites midpoint! */ + qh_normalize(qh, 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, qh->ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n", + pointid, pointidA, angle, nearzero)); + if (nearzero) { + zzinc_(Zridge0); + wwmax_(Wridge0max, angle); + wwadd_(Wridge0, angle); + }else { + zzinc_(Zridgeok) + wwmax_(Wridgeokmax, angle); + wwadd_(Wridgeok, angle); + } + } + if (simplex != points) { + FOREACHpoint_i_(qh, 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, qh->ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n", + pointid, pointidA, facet->visitid, dist)); + } + } + } + } + *offsetp= offset; + if (simplex != points) + qh_settempfree(qh, &simplex); + qh_settempfree(qh, &points); + return normal; +} /* detvnorm */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="detvridge">-</a> + + qh_detvridge(qh, 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(qhT *qh, vertexT *vertex) { + setT *centers= qh_settemp(qh, qh->TEMPsize); + setT *tricenters= qh_settemp(qh, qh->TEMPsize); + facetT *neighbor, **neighborp; + boolT firstinf= True; + + FOREACHneighbor_(vertex) { + if (neighbor->seen) { + if (neighbor->visitid) { + if (!neighbor->tricoplanar || qh_setunique(qh, &tricenters, neighbor->center)) + qh_setappend(qh, ¢ers, neighbor); + }else if (firstinf) { + firstinf= False; + qh_setappend(qh, ¢ers, neighbor); + } + } + } + qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(qh, centers), + sizeof(facetT *), qh_compare_facetvisit); + qh_settempfree(qh, &tricenters); + return centers; +} /* detvridge */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="detvridge3">-</a> + + qh_detvridge3(qh, atvertex, vertex ) + determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex + include one vertex-at-infinite for !neighbor->visitid + assumes all facet->seen2= True + + returns: + temporary set of centers (facets, i.e., Voronoi vertices) + listed in adjacency order (!oriented) + all facet->seen2= True + + design: + mark all neighbors of atvertex + for each adjacent neighbor of both atvertex and vertex + if neighbor selected + add neighbor to set of Voronoi vertices +*/ +setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex) { + setT *centers= qh_settemp(qh, qh->TEMPsize); + setT *tricenters= qh_settemp(qh, 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(qh, &tricenters, facet->center)) + qh_setappend(qh, ¢ers, facet); + }else if (firstinf) { + firstinf= False; + qh_setappend(qh, ¢ers, facet); + } + } + FOREACHneighbor_(facet) { + if (!neighbor->seen2) { + if (qh_setin(vertex->neighbors, neighbor)) + break; + else + neighbor->seen2= True; + } + } + facet= neighbor; + } + if (qh->CHECKfrequently) { + FOREACHneighbor_(vertex) { + if (!neighbor->seen2) { + qh_fprintf(qh, qh->ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n", + qh_pointid(qh, vertex->point), neighbor->id); + qh_errexit(qh, qh_ERRqhull, neighbor, NULL); + } + } + } + FOREACHneighbor_(atvertex) + neighbor->seen2= True; + qh_settempfree(qh, &tricenters); + return centers; +} /* detvridge3 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="eachvoronoi">-</a> + + qh_eachvoronoi(qh, 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(qhT *qh, 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, 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(qh, tricenters, 0); + FOREACHneighborA_(vertex) { + if (neighborA->seen) { + if (neighborA->visitid) { + if (!neighborA->tricoplanar || qh_setunique(qh, &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, qh->ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n", + count, qh_pointid(qh, atvertex->point), qh_pointid(qh, vertex->point))); + if (printvridge && fp) { + if (inorder && qh->hull_dim == 3+1) /* 3-d Voronoi diagram */ + centers= qh_detvridge3(qh, atvertex, vertex); + else + centers= qh_detvridge(qh, vertex); + (*printvridge)(qh, fp, atvertex, vertex, centers, unbounded); + qh_settempfree(qh, ¢ers); + } + } + } + } + } + } + FOREACHneighbor_(atvertex) + neighbor->seen= False; + qh_settempfree(qh, &tricenters); + return totridges; +} /* eachvoronoi */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="eachvoronoi_all">-</a> + + qh_eachvoronoi_all(qh, 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(qhT *qh, 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, qh_ASvoronoi); + qh_vertexneighbors(qh); + 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(qh, vertex->point)+1 != qh->GOODvertex) + continue; + totridges += qh_eachvoronoi(qh, fp, printvridge, vertex, + !qh_ALL, innerouter, inorder); + } + return totridges; +} /* eachvoronoi_all */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="facet2point">-</a> + + qh_facet2point(qh, 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(qhT *qh, 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(qh, vertex0->point, facet, &dist); + *mindist= dist; + *point0= qh_projectpoint(qh, vertex0->point, facet, dist); + qh_distplane(qh, vertex1->point, facet, &dist); + minimize_(*mindist, dist); + *point1= qh_projectpoint(qh, vertex1->point, facet, dist); +} /* facet2point */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="facetvertices">-</a> + + qh_facetvertices(qh, 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(qhT *qh, 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, qh->num_vertices); + FORALLvertices { + vertex->visitid= qh->vertex_visit; + qh_setappend(qh, &vertices, vertex); + } + }else { + vertices= qh_settemp(qh, qh->TEMPsize); + FORALLfacet_(facetlist) { + if (!allfacets && qh_skipfacet(qh, facet)) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + qh_setappend(qh, &vertices, vertex); + } + } + } + } + FOREACHfacet_(facets) { + if (!allfacets && qh_skipfacet(qh, facet)) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + qh_setappend(qh, &vertices, vertex); + } + } + } + return vertices; +} /* facetvertices */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="geomplanes">-</a> + + qh_geomplanes(qh, 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(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) { + realT radius; + + if (qh->MERGING || qh->JOGGLEmax < REALmax/2) { + qh_outerinner(qh, facet, outerplane, innerplane); + radius= qh->PRINTradius; + if (qh->JOGGLEmax < REALmax/2) + radius -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim); /* already accounted for in qh_outerinner() */ + *outerplane += radius; + *innerplane -= radius; + if (qh->PRINTcoplanar || qh->PRINTspheres) { + *outerplane += qh->MAXabs_coord * qh_GEOMepsilon; + *innerplane -= qh->MAXabs_coord * qh_GEOMepsilon; + } + }else + *innerplane= *outerplane= 0; +} /* geomplanes */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="markkeep">-</a> + + qh_markkeep(qh, facetlist ) + mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea + ignores visible facets (!part of convex hull) + + returns: + may clear facet->good + recomputes qh.num_good + + design: + get set of good facets + if qh.KEEParea + sort facets by area + clear facet->good for all but n largest facets + if qh.KEEPmerge + sort facets by merge count + clear facet->good for all but n most merged facets + if qh.KEEPminarea + clear facet->good if area too small + update qh.num_good +*/ +void qh_markkeep(qhT *qh, facetT *facetlist) { + facetT *facet, **facetp; + setT *facets= qh_settemp(qh, qh->num_facets); + int size, count; + + trace2((qh, qh->ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n", + qh->KEEParea, qh->KEEPmerge, qh->KEEPminArea)); + FORALLfacet_(facetlist) { + if (!facet->visible && facet->good) + qh_setappend(qh, &facets, facet); + } + size= qh_setsize(qh, facets); + if (qh->KEEParea) { + qsort(SETaddr_(facets, facetT), (size_t)size, + sizeof(facetT *), qh_compare_facetarea); + if ((count= size - qh->KEEParea) > 0) { + FOREACHfacet_(facets) { + facet->good= False; + if (--count == 0) + break; + } + } + } + if (qh->KEEPmerge) { + qsort(SETaddr_(facets, facetT), (size_t)size, + sizeof(facetT *), qh_compare_facetmerge); + if ((count= size - qh->KEEPmerge) > 0) { + FOREACHfacet_(facets) { + facet->good= False; + if (--count == 0) + break; + } + } + } + if (qh->KEEPminArea < REALmax/2) { + FOREACHfacet_(facets) { + if (!facet->isarea || facet->f.area < qh->KEEPminArea) + facet->good= False; + } + } + qh_settempfree(qh, &facets); + count= 0; + FORALLfacet_(facetlist) { + if (facet->good) + count++; + } + qh->num_good= count; +} /* markkeep */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="markvoronoi">-</a> + + qh_markvoronoi(qh, 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(qhT *qh, 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, qh_ASvoronoi); /* in case, qh_printvdiagram2 called by user */ + qh_vertexneighbors(qh); + vertices= qh_pointvertex(qh); + 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(qh, facet)) { + if (!facet->upperdelaunay) { + isLower= True; + break; + } + } + } + FOREACHfacet_(facets) { + if (printall || !qh_skipfacet(qh, 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(qh, facet)) + facet->visitid= numcenters++; + } + FOREACHfacet_(facets) { + if (printall || !qh_skipfacet(qh, facet)) + facet->visitid= numcenters++; + } + *isLowerp= isLower; + *numcentersp= numcenters; + trace2((qh, qh->ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters)); + return vertices; +} /* markvoronoi */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="order_vertexneighbors">-</a> + + qh_order_vertexneighbors(qh, 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(qhT *qh, vertexT *vertex) { + setT *newset; + facetT *facet, *neighbor, **neighborp; + + trace4((qh, qh->ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id)); + newset= qh_settemp(qh, qh_setsize(qh, vertex->neighbors)); + facet= (facetT*)qh_setdellast(vertex->neighbors); + qh_setappend(qh, &newset, facet); + while (qh_setsize(qh, vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (qh_setin(facet->neighbors, neighbor)) { + qh_setdel(vertex->neighbors, neighbor); + qh_setappend(qh, &newset, neighbor); + facet= neighbor; + break; + } + } + if (!neighbor) { + qh_fprintf(qh, qh->ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n", + vertex->id, facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + } + qh_setfree(qh, &vertex->neighbors); + qh_settemppop(qh); + vertex->neighbors= newset; +} /* order_vertexneighbors */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="prepare_output">-</a> + + qh_prepare_output(qh, ) + prepare for qh_produce_output2(qh) according to + qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds + does not reset facet->good + + notes + except for PRINTstatistics, no-op if previously called with same options +*/ +void qh_prepare_output(qhT *qh) { + if (qh->VORONOI) { + qh_clearcenters(qh, qh_ASvoronoi); /* must be before qh_triangulate */ + qh_vertexneighbors(qh); + } + if (qh->TRIangulate && !qh->hasTriangulation) { + qh_triangulate(qh); + if (qh->VERIFYoutput && !qh->CHECKfrequently) + qh_checkpolygon(qh, qh->facet_list); + } + qh_findgood_all(qh, qh->facet_list); + if (qh->GETarea) + qh_getarea(qh, qh->facet_list); + if (qh->KEEParea || qh->KEEPmerge || qh->KEEPminArea < REALmax/2) + qh_markkeep(qh, qh->facet_list); + if (qh->PRINTstatistics) + qh_collectstatistics(qh); +} + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printafacet">-</a> + + qh_printafacet(qh, 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(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall) { + realT color[4], offset, dist, outerplane, innerplane; + boolT zerodiv; + coordT *point, *normp, *coordp, **pointp, *feasiblep; + int k; + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + + if (!printall && qh_skipfacet(qh, facet)) + return; + if (facet->visible && qh->NEWfacets && format != qh_PRINTfacets) + return; + qh->printoutnum++; + switch (format) { + case qh_PRINTarea: + if (facet->isarea) { + qh_fprintf(qh, fp, 9009, qh_REAL_1, facet->f.area); + qh_fprintf(qh, fp, 9010, "\n"); + }else + qh_fprintf(qh, fp, 9011, "0\n"); + break; + case qh_PRINTcoplanars: + qh_fprintf(qh, fp, 9012, "%d", qh_setsize(qh, facet->coplanarset)); + FOREACHpoint_(facet->coplanarset) + qh_fprintf(qh, fp, 9013, " %d", qh_pointid(qh, point)); + qh_fprintf(qh, fp, 9014, "\n"); + break; + case qh_PRINTcentrums: + qh_printcenter(qh, fp, format, NULL, facet); + break; + case qh_PRINTfacets: + qh_printfacet(qh, fp, facet); + break; + case qh_PRINTfacets_xridge: + qh_printfacetheader(qh, 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(qh, color, color); + if (qh->PRINTdim != qh->hull_dim) + qh_normalize2(qh, color, 3, True, NULL, NULL); + if (qh->hull_dim <= 2) + qh_printfacet2geom(qh, fp, facet, color); + else if (qh->hull_dim == 3) { + if (facet->simplicial) + qh_printfacet3geom_simplicial(qh, fp, facet, color); + else + qh_printfacet3geom_nonsimplicial(qh, fp, facet, color); + }else { + if (facet->simplicial) + qh_printfacet4geom_simplicial(qh, fp, facet, color); + else + qh_printfacet4geom_nonsimplicial(qh, fp, facet, color); + } + break; + case qh_PRINTids: + qh_fprintf(qh, fp, 9015, "%d\n", facet->id); + break; + case qh_PRINTincidences: + case qh_PRINToff: + case qh_PRINTtriangles: + if (qh->hull_dim == 3 && format != qh_PRINTtriangles) + qh_printfacet3vertex(qh, fp, facet, format); + else if (facet->simplicial || qh->hull_dim == 2 || format == qh_PRINToff) + qh_printfacetNvertex_simplicial(qh, fp, facet, format); + else + qh_printfacetNvertex_nonsimplicial(qh, fp, facet, qh->printoutvar++, format); + break; + case qh_PRINTinner: + qh_outerinner(qh, facet, NULL, &innerplane); + offset= facet->offset - innerplane; + goto LABELprintnorm; + break; /* prevent warning */ + case qh_PRINTmerges: + qh_fprintf(qh, fp, 9016, "%d\n", facet->nummerge); + break; + case qh_PRINTnormals: + offset= facet->offset; + goto LABELprintnorm; + break; /* prevent warning */ + case qh_PRINTouter: + qh_outerinner(qh, facet, &outerplane, NULL); + offset= facet->offset - outerplane; + LABELprintnorm: + if (!facet->normal) { + qh_fprintf(qh, fp, 9017, "no normal for facet f%d\n", facet->id); + break; + } + if (qh->CDDoutput) { + qh_fprintf(qh, fp, 9018, qh_REAL_1, -offset); + for (k=0; k < qh->hull_dim; k++) + qh_fprintf(qh, fp, 9019, qh_REAL_1, -facet->normal[k]); + }else { + for (k=0; k < qh->hull_dim; k++) + qh_fprintf(qh, fp, 9020, qh_REAL_1, facet->normal[k]); + qh_fprintf(qh, fp, 9021, qh_REAL_1, offset); + } + qh_fprintf(qh, fp, 9022, "\n"); + break; + case qh_PRINTmathematica: /* either 2 or 3-d by qh_printbegin */ + case qh_PRINTmaple: + if (qh->hull_dim == 2) + qh_printfacet2math(qh, fp, facet, format, qh->printoutvar++); + else + qh_printfacet3math(qh, fp, facet, format, qh->printoutvar++); + break; + case qh_PRINTneighbors: + qh_fprintf(qh, fp, 9023, "%d", qh_setsize(qh, facet->neighbors)); + FOREACHneighbor_(facet) + qh_fprintf(qh, fp, 9024, " %d", + neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id); + qh_fprintf(qh, fp, 9025, "\n"); + break; + case qh_PRINTpointintersect: + if (!qh->feasible_point) { + qh_fprintf(qh, qh->ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh->feasible_point\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (facet->offset > 0) + goto LABELprintinfinite; + point= coordp= (coordT*)qh_memalloc(qh, 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(qh, point, qh->normal_size); + goto LABELprintinfinite; + } + } + } + qh_printpoint(qh, fp, NULL, point); + qh_memfree(qh, point, qh->normal_size); + break; + LABELprintinfinite: + for (k=qh->hull_dim; k--; ) + qh_fprintf(qh, fp, 9026, qh_REAL_1, qh_INFINITE); + qh_fprintf(qh, fp, 9027, "\n"); + break; + case qh_PRINTpointnearest: + FOREACHpoint_(facet->coplanarset) { + int id, id2; + vertex= qh_nearvertex(qh, facet, point, &dist); + id= qh_pointid(qh, vertex->point); + id2= qh_pointid(qh, point); + qh_fprintf(qh, fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist); + } + break; + case qh_PRINTpoints: /* VORONOI only by qh_printbegin */ + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9029, "1 "); + qh_printcenter(qh, fp, format, NULL, facet); + break; + case qh_PRINTvertices: + qh_fprintf(qh, fp, 9030, "%d", qh_setsize(qh, facet->vertices)); + FOREACHvertex_(facet->vertices) + qh_fprintf(qh, fp, 9031, " %d", qh_pointid(qh, vertex->point)); + qh_fprintf(qh, fp, 9032, "\n"); + break; + default: + break; + } +} /* printafacet */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printbegin">-</a> + + qh_printbegin(qh, ) + 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(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars; + int i, num; + facetT *facet, **facetp; + vertexT *vertex, **vertexp; + setT *vertices; + pointT *point, **pointp, *pointtemp; + + qh->printoutnum= 0; + qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + switch (format) { + case qh_PRINTnone: + break; + case qh_PRINTarea: + qh_fprintf(qh, fp, 9033, "%d\n", numfacets); + break; + case qh_PRINTcoplanars: + qh_fprintf(qh, fp, 9034, "%d\n", numfacets); + break; + case qh_PRINTcentrums: + if (qh->CENTERtype == qh_ASnone) + qh_clearcenters(qh, qh_AScentrum); + qh_fprintf(qh, fp, 9035, "%d\n%d\n", qh->hull_dim, numfacets); + break; + case qh_PRINTfacets: + case qh_PRINTfacets_xridge: + if (facetlist) + qh_printvertexlist(qh, fp, "Vertices and facets:\n", facetlist, facets, printall); + break; + case qh_PRINTgeom: + if (qh->hull_dim > 4) /* qh_initqhull_globals also checks */ + goto LABELnoformat; + if (qh->VORONOI && qh->hull_dim > 3) /* PRINTdim == DROPdim == hull_dim-1 */ + goto LABELnoformat; + if (qh->hull_dim == 2 && (qh->PRINTridges || qh->DOintersections)) + qh_fprintf(qh, qh->ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n"); + if (qh->hull_dim == 4 && (qh->PRINTinner || qh->PRINTouter || + (qh->PRINTdim == 4 && qh->PRINTcentrums))) + qh_fprintf(qh, qh->ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n"); + if (qh->PRINTdim == 4 && (qh->PRINTspheres)) + qh_fprintf(qh, qh->ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n"); + if (qh->PRINTdim == 4 && qh->DOintersections && qh->PRINTnoplanes) + qh_fprintf(qh, qh->ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n"); + if (qh->PRINTdim == 2) { + qh_fprintf(qh, fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n", + qh->rbox_command, qh->qhull_command); + }else if (qh->PRINTdim == 3) { + qh_fprintf(qh, fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n", + qh->rbox_command, qh->qhull_command); + }else if (qh->PRINTdim == 4) { + qh->visit_id++; + num= 0; + FORALLfacet_(facetlist) /* get number of ridges to be printed */ + qh_printend4geom(qh, NULL, facet, &num, printall); + FOREACHfacet_(facets) + qh_printend4geom(qh, NULL, facet, &num, printall); + qh->ridgeoutnum= num; + qh->printoutvar= 0; /* counts number of ridges in output */ + qh_fprintf(qh, fp, 9038, "LIST # %s | %s\n", qh->rbox_command, qh->qhull_command); + } + + if (qh->PRINTdots) { + qh->printoutnum++; + num= qh->num_points + qh_setsize(qh, qh->other_points); + if (qh->DELAUNAY && qh->ATinfinity) + num--; + if (qh->PRINTdim == 4) + qh_fprintf(qh, fp, 9039, "4VECT %d %d 1\n", num, num); + else + qh_fprintf(qh, fp, 9040, "VECT %d %d 1\n", num, num); + + for (i=num; i--; ) { + if (i % 20 == 0) + qh_fprintf(qh, fp, 9041, "\n"); + qh_fprintf(qh, fp, 9042, "1 "); + } + qh_fprintf(qh, fp, 9043, "# 1 point per line\n1 "); + for (i=num-1; i--; ) { /* num at least 3 for D2 */ + if (i % 20 == 0) + qh_fprintf(qh, fp, 9044, "\n"); + qh_fprintf(qh, fp, 9045, "0 "); + } + qh_fprintf(qh, fp, 9046, "# 1 color for all\n"); + FORALLpoints { + if (!qh->DELAUNAY || !qh->ATinfinity || qh_pointid(qh, point) != qh->num_points-1) { + if (qh->PRINTdim == 4) + qh_printpoint(qh, fp, NULL, point); + else + qh_printpoint3(qh, fp, point); + } + } + FOREACHpoint_(qh->other_points) { + if (qh->PRINTdim == 4) + qh_printpoint(qh, fp, NULL, point); + else + qh_printpoint3(qh, fp, point); + } + qh_fprintf(qh, fp, 9047, "0 1 1 1 # color of points\n"); + } + + if (qh->PRINTdim == 4 && !qh->PRINTnoplanes) + /* 4dview loads up multiple 4OFF objects slowly */ + qh_fprintf(qh, fp, 9048, "4OFF %d %d 1\n", 3*qh->ridgeoutnum, qh->ridgeoutnum); + qh->PRINTcradius= 2 * qh->DISTround; /* include test DISTround */ + if (qh->PREmerge) { + maximize_(qh->PRINTcradius, qh->premerge_centrum + qh->DISTround); + }else if (qh->POSTmerge) + maximize_(qh->PRINTcradius, qh->postmerge_centrum + qh->DISTround); + qh->PRINTradius= qh->PRINTcradius; + if (qh->PRINTspheres + qh->PRINTcoplanar) + maximize_(qh->PRINTradius, qh->MAXabs_coord * qh_MINradius); + if (qh->premerge_cos < REALmax/2) { + maximize_(qh->PRINTradius, (1- qh->premerge_cos) * qh->MAXabs_coord); + }else if (!qh->PREmerge && qh->POSTmerge && qh->postmerge_cos < REALmax/2) { + maximize_(qh->PRINTradius, (1- qh->postmerge_cos) * qh->MAXabs_coord); + } + maximize_(qh->PRINTradius, qh->MINvisible); + if (qh->JOGGLEmax < REALmax/2) + qh->PRINTradius += qh->JOGGLEmax * sqrt((realT)qh->hull_dim); + if (qh->PRINTdim != 4 && + (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) { + vertices= qh_facetvertices(qh, facetlist, facets, printall); + if (qh->PRINTspheres && qh->PRINTdim <= 3) + qh_printspheres(qh, fp, vertices, qh->PRINTradius); + if (qh->PRINTcoplanar || qh->PRINTcentrums) { + qh->firstcentrum= True; + if (qh->PRINTcoplanar&& !qh->PRINTspheres) { + FOREACHvertex_(vertices) + qh_printpointvect2(qh, fp, vertex->point, NULL, qh->interior_point, qh->PRINTradius); + } + FORALLfacet_(facetlist) { + if (!printall && qh_skipfacet(qh, facet)) + continue; + if (!facet->normal) + continue; + if (qh->PRINTcentrums && qh->PRINTdim <= 3) + qh_printcentrum(qh, fp, facet, qh->PRINTcradius); + if (!qh->PRINTcoplanar) + continue; + FOREACHpoint_(facet->coplanarset) + qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius); + FOREACHpoint_(facet->outsideset) + qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius); + } + FOREACHfacet_(facets) { + if (!printall && qh_skipfacet(qh, facet)) + continue; + if (!facet->normal) + continue; + if (qh->PRINTcentrums && qh->PRINTdim <= 3) + qh_printcentrum(qh, fp, facet, qh->PRINTcradius); + if (!qh->PRINTcoplanar) + continue; + FOREACHpoint_(facet->coplanarset) + qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius); + FOREACHpoint_(facet->outsideset) + qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius); + } + } + qh_settempfree(qh, &vertices); + } + qh->visit_id++; /* for printing hyperplane intersections */ + break; + case qh_PRINTids: + qh_fprintf(qh, fp, 9049, "%d\n", numfacets); + break; + case qh_PRINTincidences: + if (qh->VORONOI && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7053, "qhull warning: writing Delaunay. Use 'p' or 'o' for Voronoi centers\n"); + qh->printoutvar= qh->vertex_id; /* centrum id for non-simplicial facets */ + if (qh->hull_dim <= 3) + qh_fprintf(qh, fp, 9050, "%d\n", numfacets); + else + qh_fprintf(qh, fp, 9051, "%d\n", numsimplicial+numridges); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9052, "%s | %s\nbegin\n %d %d real\n", qh->rbox_command, + qh->qhull_command, numfacets, qh->hull_dim+1); + else + qh_fprintf(qh, fp, 9053, "%d\n%d\n", qh->hull_dim+1, numfacets); + break; + case qh_PRINTmathematica: + case qh_PRINTmaple: + if (qh->hull_dim > 3) /* qh_initbuffers also checks */ + goto LABELnoformat; + if (qh->VORONOI) + qh_fprintf(qh, qh->ferr, 7054, "qhull warning: output is the Delaunay triangulation\n"); + if (format == qh_PRINTmaple) { + if (qh->hull_dim == 2) + qh_fprintf(qh, fp, 9054, "PLOT(CURVES(\n"); + else + qh_fprintf(qh, fp, 9055, "PLOT3D(POLYGONS(\n"); + }else + qh_fprintf(qh, fp, 9056, "{\n"); + qh->printoutvar= 0; /* counts number of facets for notfirst */ + break; + case qh_PRINTmerges: + qh_fprintf(qh, fp, 9057, "%d\n", numfacets); + break; + case qh_PRINTpointintersect: + qh_fprintf(qh, fp, 9058, "%d\n%d\n", qh->hull_dim, numfacets); + break; + case qh_PRINTneighbors: + qh_fprintf(qh, fp, 9059, "%d\n", numfacets); + break; + case qh_PRINToff: + case qh_PRINTtriangles: + if (qh->VORONOI) + goto LABELnoformat; + num = qh->hull_dim; + if (format == qh_PRINToff || qh->hull_dim == 2) + qh_fprintf(qh, fp, 9060, "%d\n%d %d %d\n", num, + qh->num_points+qh_setsize(qh, qh->other_points), numfacets, totneighbors/2); + else { /* qh_PRINTtriangles */ + qh->printoutvar= qh->num_points+qh_setsize(qh, qh->other_points); /* first centrum */ + if (qh->DELAUNAY) + num--; /* drop last dimension */ + qh_fprintf(qh, fp, 9061, "%d\n%d %d %d\n", num, qh->printoutvar + + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2); + } + FORALLpoints + qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown); + FOREACHpoint_(qh->other_points) + qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown); + if (format == qh_PRINTtriangles && qh->hull_dim > 2) { + FORALLfacets { + if (!facet->simplicial && facet->visitid) + qh_printcenter(qh, qh->fout, format, NULL, facet); + } + } + break; + case qh_PRINTpointnearest: + qh_fprintf(qh, fp, 9062, "%d\n", numcoplanars); + break; + case qh_PRINTpoints: + if (!qh->VORONOI) + goto LABELnoformat; + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command, + qh->qhull_command, numfacets, qh->hull_dim); + else + qh_fprintf(qh, fp, 9064, "%d\n%d\n", qh->hull_dim-1, numfacets); + break; + case qh_PRINTvertices: + qh_fprintf(qh, fp, 9065, "%d\n", numfacets); + break; + case qh_PRINTsummary: + default: + LABELnoformat: + qh_fprintf(qh, qh->ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n", + qh->hull_dim); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +} /* printbegin */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printcenter">-</a> + + qh_printcenter(qh, fp, string, facet ) + print facet->center as centrum or Voronoi center + string may be NULL. Don't include '%' codes. + nop if qh->CENTERtype neither CENTERvoronoi nor CENTERcentrum + if upper envelope of Delaunay triangulation and point at-infinity + prints qh_INFINITE instead; + + notes: + defines facet->center if needed + if format=PRINTgeom, adds a 0 if would otherwise be 2-d + Same as QhullFacet::printCenter +*/ +void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet) { + int k, num; + + if (qh->CENTERtype != qh_ASvoronoi && qh->CENTERtype != qh_AScentrum) + return; + if (string) + qh_fprintf(qh, fp, 9066, string); + if (qh->CENTERtype == qh_ASvoronoi) { + num= qh->hull_dim-1; + if (!facet->normal || !facet->upperdelaunay || !qh->ATinfinity) { + if (!facet->center) + facet->center= qh_facetcenter(qh, facet->vertices); + for (k=0; k < num; k++) + qh_fprintf(qh, fp, 9067, qh_REAL_1, facet->center[k]); + }else { + for (k=0; k < num; k++) + qh_fprintf(qh, fp, 9068, qh_REAL_1, qh_INFINITE); + } + }else /* qh->CENTERtype == qh_AScentrum */ { + num= qh->hull_dim; + if (format == qh_PRINTtriangles && qh->DELAUNAY) + num--; + if (!facet->center) + facet->center= qh_getcentrum(qh, facet); + for (k=0; k < num; k++) + qh_fprintf(qh, fp, 9069, qh_REAL_1, facet->center[k]); + } + if (format == qh_PRINTgeom && num == 2) + qh_fprintf(qh, fp, 9070, " 0\n"); + else + qh_fprintf(qh, fp, 9071, "\n"); +} /* printcenter */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printcentrum">-</a> + + qh_printcentrum(qh, 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(qhT *qh, 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(qh, facet); + centrum= facet->center; + }else { + centrum= qh_getcentrum(qh, facet); + tempcentrum= True; + } + qh_fprintf(qh, fp, 9072, "{appearance {-normal -edge normscale 0} "); + if (qh->firstcentrum) { + qh->firstcentrum= False; + qh_fprintf(qh, fp, 9073, "{INST geom { define centrum CQUAD # f%d\n\ +-0.3 -0.3 0.0001 0 0 1 1\n\ + 0.3 -0.3 0.0001 0 0 1 1\n\ + 0.3 0.3 0.0001 0 0 1 1\n\ +-0.3 0.3 0.0001 0 0 1 1 } transform { \n", facet->id); + }else + qh_fprintf(qh, fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id); + apex= SETfirstt_(facet->vertices, vertexT); + qh_distplane(qh, apex->point, facet, &dist); + projpt= qh_projectpoint(qh, 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(qh, xaxis, xaxis); + qh_projectdim3(qh, normal, normal); + qh_normalize2(qh, normal, qh->PRINTdim, True, NULL, NULL); + } + qh_crossproduct(3, xaxis, normal, yaxis); + qh_fprintf(qh, fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]); + qh_fprintf(qh, fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]); + qh_fprintf(qh, fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]); + qh_printpoint3(qh, fp, centrum); + qh_fprintf(qh, fp, 9078, "1 }}}\n"); + qh_memfree(qh, projpt, qh->normal_size); + qh_printpointvect(qh, fp, centrum, facet->normal, NULL, radius, green); + if (tempcentrum) + qh_memfree(qh, centrum, qh->normal_size); +} /* printcentrum */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printend">-</a> + + qh_printend(qh, fp, format ) + prints trailer for all output formats + + see: + qh_printbegin() and qh_printafacet() + +*/ +void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int num; + facetT *facet, **facetp; + + if (!qh->printoutnum) + qh_fprintf(qh, qh->ferr, 7055, "qhull warning: no facets printed\n"); + switch (format) { + case qh_PRINTgeom: + if (qh->hull_dim == 4 && qh->DROPdim < 0 && !qh->PRINTnoplanes) { + qh->visit_id++; + num= 0; + FORALLfacet_(facetlist) + qh_printend4geom(qh, fp, facet,&num, printall); + FOREACHfacet_(facets) + qh_printend4geom(qh, fp, facet, &num, printall); + if (num != qh->ridgeoutnum || qh->printoutvar != qh->ridgeoutnum) { + qh_fprintf(qh, qh->ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh->ridgeoutnum, qh->printoutvar, num); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + }else + qh_fprintf(qh, fp, 9079, "}\n"); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9080, "end\n"); + break; + case qh_PRINTmaple: + qh_fprintf(qh, fp, 9081, "));\n"); + break; + case qh_PRINTmathematica: + qh_fprintf(qh, fp, 9082, "}\n"); + break; + case qh_PRINTpoints: + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9083, "end\n"); + break; + default: + break; + } +} /* printend */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printend4geom">-</a> + + qh_printend4geom(qh, 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(qhT *qh, 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(qh, facet)) + return; + if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets)) + return; + if (!facet->normal) + return; + if (fp) { + for (i=0; i < 3; i++) { + color[i]= (facet->normal[i]+1.0)/2.0; + maximize_(color[i], -1.0); + minimize_(color[i], +1.0); + } + } + facet->visitid= qh->visit_id; + if (facet->simplicial) { + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh->visit_id) { + if (fp) + qh_fprintf(qh, fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n", + 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2], + facet->id, neighbor->id); + num++; + } + } + }else { + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid != qh->visit_id) { + if (fp) + qh_fprintf(qh, fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n", + 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2], + ridge->id, facet->id, neighbor->id); + num++; + } + } + } + *nump= num; +} /* printend4geom */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printextremes">-</a> + + qh_printextremes(qh, 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(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices, *points; + pointT *point; + vertexT *vertex, **vertexp; + int id; + int numpoints=0, point_i, point_n; + int allpoints= qh->num_points + qh_setsize(qh, qh->other_points); + + points= qh_settemp(qh, allpoints); + qh_setzero(qh, points, 0, allpoints); + vertices= qh_facetvertices(qh, facetlist, facets, printall); + FOREACHvertex_(vertices) { + id= qh_pointid(qh, vertex->point); + if (id >= 0) { + SETelem_(points, id)= vertex->point; + numpoints++; + } + } + qh_settempfree(qh, &vertices); + qh_fprintf(qh, fp, 9086, "%d\n", numpoints); + FOREACHpoint_i_(qh, points) { + if (point) + qh_fprintf(qh, fp, 9087, "%d\n", point_i); + } + qh_settempfree(qh, &points); +} /* printextremes */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printextremes_2d">-</a> + + qh_printextremes_2d(qh, 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(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars; + setT *vertices; + facetT *facet, *startfacet, *nextfacet; + vertexT *vertexA, *vertexB; + + qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh->visit_id */ + vertices= qh_facetvertices(qh, facetlist, facets, printall); + qh_fprintf(qh, fp, 9088, "%d\n", qh_setsize(qh, vertices)); + qh_settempfree(qh, &vertices); + if (!numfacets) + return; + facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT); + qh->vertex_visit++; + qh->visit_id++; + do { + if (facet->toporient ^ qh_ORIENTclock) { + vertexA= SETfirstt_(facet->vertices, vertexT); + vertexB= SETsecondt_(facet->vertices, vertexT); + nextfacet= SETfirstt_(facet->neighbors, facetT); + }else { + vertexA= SETsecondt_(facet->vertices, vertexT); + vertexB= SETfirstt_(facet->vertices, vertexT); + nextfacet= SETsecondt_(facet->neighbors, facetT); + } + if (facet->visitid == qh->visit_id) { + qh_fprintf(qh, qh->ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n", + facet->id, nextfacet->id); + qh_errexit2(qh, qh_ERRqhull, facet, nextfacet); + } + if (facet->visitid) { + if (vertexA->visitid != qh->vertex_visit) { + vertexA->visitid= qh->vertex_visit; + qh_fprintf(qh, fp, 9089, "%d\n", qh_pointid(qh, vertexA->point)); + } + if (vertexB->visitid != qh->vertex_visit) { + vertexB->visitid= qh->vertex_visit; + qh_fprintf(qh, fp, 9090, "%d\n", qh_pointid(qh, vertexB->point)); + } + } + facet->visitid= qh->visit_id; + facet= nextfacet; + }while (facet && facet != startfacet); +} /* printextremes_2d */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printextremes_d">-</a> + + qh_printextremes_d(qh, 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(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices; + vertexT *vertex, **vertexp; + boolT upperseen, lowerseen; + facetT *neighbor, **neighborp; + int numpoints=0; + + vertices= qh_facetvertices(qh, facetlist, facets, printall); + qh_vertexneighbors(qh); + FOREACHvertex_(vertices) { + upperseen= lowerseen= False; + FOREACHneighbor_(vertex) { + if (neighbor->upperdelaunay) + upperseen= True; + else + lowerseen= True; + } + if (upperseen && lowerseen) { + vertex->seen= True; + numpoints++; + }else + vertex->seen= False; + } + qh_fprintf(qh, fp, 9091, "%d\n", numpoints); + FOREACHvertex_(vertices) { + if (vertex->seen) + qh_fprintf(qh, fp, 9092, "%d\n", qh_pointid(qh, vertex->point)); + } + qh_settempfree(qh, &vertices); +} /* printextremes_d */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet">-</a> + + qh_printfacet(qh, fp, facet ) + prints all fields of a facet to fp + + notes: + ridges printed in neighbor order +*/ +void qh_printfacet(qhT *qh, FILE *fp, facetT *facet) { + + qh_printfacetheader(qh, fp, facet); + if (facet->ridges) + qh_printfacetridges(qh, fp, facet); +} /* printfacet */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet2geom">-</a> + + qh_printfacet2geom(qh, fp, facet, color ) + print facet as part of a 2-d VECT for Geomview + + notes: + assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon + mindist is calculated within io_r.c. maxoutside is calculated elsewhere + so a DISTround error may have occurred. +*/ +void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]) { + pointT *point0, *point1; + realT mindist, innerplane, outerplane; + int k; + + qh_facet2point(qh, facet, &point0, &point1, &mindist); + qh_geomplanes(qh, facet, &outerplane, &innerplane); + if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner)) + qh_printfacet2geom_points(qh, 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(qh, fp, point0, point1, facet, innerplane, color); + } + qh_memfree(qh, point1, qh->normal_size); + qh_memfree(qh, point0, qh->normal_size); +} /* printfacet2geom */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet2geom_points">-</a> + + qh_printfacet2geom_points(qh, 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(qhT *qh, FILE *fp, pointT *point1, pointT *point2, + facetT *facet, realT offset, realT color[3]) { + pointT *p1= point1, *p2= point2; + + qh_fprintf(qh, fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id); + if (offset != 0.0) { + p1= qh_projectpoint(qh, p1, facet, -offset); + p2= qh_projectpoint(qh, p2, facet, -offset); + } + qh_fprintf(qh, fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n", + p1[0], p1[1], 0.0, p2[0], p2[1], 0.0); + if (offset != 0.0) { + qh_memfree(qh, p1, qh->normal_size); + qh_memfree(qh, p2, qh->normal_size); + } + qh_fprintf(qh, fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printfacet2geom_points */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet2math">-</a> + + qh_printfacet2math(qh, 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(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) { + pointT *point0, *point1; + realT mindist; + const char *pointfmt; + + qh_facet2point(qh, facet, &point0, &point1, &mindist); + if (notfirst) + qh_fprintf(qh, fp, 9096, ","); + if (format == qh_PRINTmaple) + pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n"; + else + pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n"; + qh_fprintf(qh, fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]); + qh_memfree(qh, point1, qh->normal_size); + qh_memfree(qh, point0, qh->normal_size); +} /* printfacet2math */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a> + + qh_printfacet3geom_nonsimplicial(qh, 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(qhT *qh, 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(qh, facet, &outerplane, &innerplane); + vertices= qh_facet3vertex(qh, facet); /* oriented */ + cntvertices= qh_setsize(qh, vertices); + projectedpoints= qh_settemp(qh, cntvertices); + FOREACHvertex_(vertices) { + zinc_(Zdistio); + qh_distplane(qh, vertex->point, facet, &dist); + projpt= qh_projectpoint(qh, vertex->point, facet, dist); + qh_setappend(qh, &projectedpoints, projpt); + } + if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner)) + qh_printfacet3geom_points(qh, 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(qh, fp, projectedpoints, facet, innerplane, color); + } + FOREACHpoint_(projectedpoints) + qh_memfree(qh, point, qh->normal_size); + qh_settempfree(qh, &projectedpoints); + qh_settempfree(qh, &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(qh, fp, facet, neighbor, ridge->vertices, black); + if (qh->PRINTridges) { + vertexA= SETfirstt_(ridge->vertices, vertexT); + vertexB= SETsecondt_(ridge->vertices, vertexT); + qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green); + } + } + } + } +} /* printfacet3geom_nonsimplicial */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_points">-</a> + + qh_printfacet3geom_points(qh, 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(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) { + int k, n= qh_setsize(qh, points), i; + pointT *point, **pointp; + setT *printpoints; + + qh_fprintf(qh, fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id); + if (offset != 0.0) { + printpoints= qh_settemp(qh, n); + FOREACHpoint_(points) + qh_setappend(qh, &printpoints, qh_projectpoint(qh, point, facet, -offset)); + }else + printpoints= points; + FOREACHpoint_(printpoints) { + for (k=0; k < qh->hull_dim; k++) { + if (k == qh->DROPdim) + qh_fprintf(qh, fp, 9099, "0 "); + else + qh_fprintf(qh, fp, 9100, "%8.4g ", point[k]); + } + if (printpoints != points) + qh_memfree(qh, point, qh->normal_size); + qh_fprintf(qh, fp, 9101, "\n"); + } + if (printpoints != points) + qh_settempfree(qh, &printpoints); + qh_fprintf(qh, fp, 9102, "%d ", n); + for (i=0; i < n; i++) + qh_fprintf(qh, fp, 9103, "%d ", i); + qh_fprintf(qh, fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]); +} /* printfacet3geom_points */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_simplicial">-</a> + + qh_printfacet3geom_simplicial(qh, ) + 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_r.c with roundoff covered by qh_GEOMepsilon + innerplane may be off by qh->DISTround. Maxoutside is calculated elsewhere + so a DISTround error may have occurred. +*/ +void qh_printfacet3geom_simplicial(qhT *qh, 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(qh, facet, &outerplane, &innerplane); + vertices= qh_facet3vertex(qh, facet); + points= qh_settemp(qh, qh->TEMPsize); + FOREACHvertex_(vertices) + qh_setappend(qh, &points, vertex->point); + if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner)) + qh_printfacet3geom_points(qh, 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(qh, fp, points, facet, innerplane, color); + } + qh_settempfree(qh, &points); + qh_settempfree(qh, &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(qh, facet->vertices, qh->hull_dim, + SETindex_(facet->neighbors, neighbor), 0); + if (qh->DOintersections) + qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, black); + if (qh->PRINTridges) { + vertexA= SETfirstt_(vertices, vertexT); + vertexB= SETsecondt_(vertices, vertexT); + qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green); + } + qh_setfree(qh, &vertices); + } + } + } +} /* printfacet3geom_simplicial */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3math">-</a> + + qh_printfacet3math(qh, 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(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) { + vertexT *vertex, **vertexp; + setT *points, *vertices; + pointT *point, **pointp; + boolT firstpoint= True; + realT dist; + const char *pointfmt, *endfmt; + + if (notfirst) + qh_fprintf(qh, fp, 9105, ",\n"); + vertices= qh_facet3vertex(qh, facet); + points= qh_settemp(qh, qh_setsize(qh, vertices)); + FOREACHvertex_(vertices) { + zinc_(Zdistio); + qh_distplane(qh, vertex->point, facet, &dist); + point= qh_projectpoint(qh, vertex->point, facet, dist); + qh_setappend(qh, &points, point); + } + if (format == qh_PRINTmaple) { + qh_fprintf(qh, fp, 9106, "["); + pointfmt= "[%16.8f, %16.8f, %16.8f]"; + endfmt= "]"; + }else { + qh_fprintf(qh, fp, 9107, "Polygon[{"); + pointfmt= "{%16.8f, %16.8f, %16.8f}"; + endfmt= "}]"; + } + FOREACHpoint_(points) { + if (firstpoint) + firstpoint= False; + else + qh_fprintf(qh, fp, 9108, ",\n"); + qh_fprintf(qh, fp, 9109, pointfmt, point[0], point[1], point[2]); + } + FOREACHpoint_(points) + qh_memfree(qh, point, qh->normal_size); + qh_settempfree(qh, &points); + qh_settempfree(qh, &vertices); + qh_fprintf(qh, fp, 9110, "%s", endfmt); +} /* printfacet3math */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3vertex">-</a> + + qh_printfacet3vertex(qh, 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(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facet3vertex(qh, facet); + if (format == qh_PRINToff) + qh_fprintf(qh, fp, 9111, "%d ", qh_setsize(qh, vertices)); + FOREACHvertex_(vertices) + qh_fprintf(qh, fp, 9112, "%d ", qh_pointid(qh, vertex->point)); + qh_fprintf(qh, fp, 9113, "\n"); + qh_settempfree(qh, &vertices); +} /* printfacet3vertex */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a> + + qh_printfacet4geom_nonsimplicial(qh, ) + 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(qhT *qh, 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(qh, fp, facet, neighbor, ridge->vertices, color); + else { + if (qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9114, "OFF 3 1 1 # f%d\n", facet->id); + else { + qh->printoutvar++; + qh_fprintf(qh, fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id); + } + FOREACHvertex_(ridge->vertices) { + zinc_(Zdistio); + qh_distplane(qh, vertex->point,facet, &dist); + point=qh_projectpoint(qh, vertex->point,facet, dist); + for (k=0; k < qh->hull_dim; k++) { + if (k != qh->DROPdim) + qh_fprintf(qh, fp, 9116, "%8.4g ", point[k]); + } + qh_fprintf(qh, fp, 9117, "\n"); + qh_memfree(qh, point, qh->normal_size); + } + if (qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + } +} /* printfacet4geom_nonsimplicial */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet4geom_simplicial">-</a> + + qh_printfacet4geom_simplicial(qh, 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(qhT *qh, 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(qh, facet->vertices, qh->hull_dim, + SETindex_(facet->neighbors, neighbor), 0); + if (qh->DOintersections) + qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, color); + else { + if (qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n", + facet->id, neighbor->id); + else { + qh->printoutvar++; + qh_fprintf(qh, fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id); + } + FOREACHvertex_(vertices) { + for (k=0; k < qh->hull_dim; k++) { + if (k != qh->DROPdim) + qh_fprintf(qh, fp, 9121, "%8.4g ", vertex->point[k]); + } + qh_fprintf(qh, fp, 9122, "\n"); + } + if (qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + qh_setfree(qh, &vertices); + } +} /* printfacet4geom_simplicial */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a> + + qh_printfacetNvertex_nonsimplicial(qh, fp, facet, id, format ) + print vertices for an N-d non-simplicial facet + triangulates each ridge to the id +*/ +void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format) { + vertexT *vertex, **vertexp; + ridgeT *ridge, **ridgep; + + if (facet->visible && qh->NEWfacets) + return; + FOREACHridge_(facet->ridges) { + if (format == qh_PRINTtriangles) + qh_fprintf(qh, fp, 9124, "%d ", qh->hull_dim); + qh_fprintf(qh, fp, 9125, "%d ", id); + if ((ridge->top == facet) ^ qh_ORIENTclock) { + FOREACHvertex_(ridge->vertices) + qh_fprintf(qh, fp, 9126, "%d ", qh_pointid(qh, vertex->point)); + }else { + FOREACHvertexreverse12_(ridge->vertices) + qh_fprintf(qh, fp, 9127, "%d ", qh_pointid(qh, vertex->point)); + } + qh_fprintf(qh, fp, 9128, "\n"); + } +} /* printfacetNvertex_nonsimplicial */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a> + + qh_printfacetNvertex_simplicial(qh, 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(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) { + vertexT *vertex, **vertexp; + + if (format == qh_PRINToff || format == qh_PRINTtriangles) + qh_fprintf(qh, fp, 9129, "%d ", qh_setsize(qh, facet->vertices)); + if ((facet->toporient ^ qh_ORIENTclock) + || (qh->hull_dim > 2 && !facet->simplicial)) { + FOREACHvertex_(facet->vertices) + qh_fprintf(qh, fp, 9130, "%d ", qh_pointid(qh, vertex->point)); + }else { + FOREACHvertexreverse12_(facet->vertices) + qh_fprintf(qh, fp, 9131, "%d ", qh_pointid(qh, vertex->point)); + } + qh_fprintf(qh, fp, 9132, "\n"); +} /* printfacetNvertex_simplicial */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacetheader">-</a> + + qh_printfacetheader(qh, fp, facet ) + prints header fields of a facet to fp + + notes: + for 'f' output and debugging + Same as QhullFacet::printHeader() +*/ +void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet) { + pointT *point, **pointp, *furthest; + facetT *neighbor, **neighborp; + realT dist; + + if (facet == qh_MERGEridge) { + qh_fprintf(qh, fp, 9133, " MERGEridge\n"); + return; + }else if (facet == qh_DUPLICATEridge) { + qh_fprintf(qh, fp, 9134, " DUPLICATEridge\n"); + return; + }else if (!facet) { + qh_fprintf(qh, fp, 9135, " NULLfacet\n"); + return; + } + qh->old_randomdist= qh->RANDOMdist; + qh->RANDOMdist= False; + qh_fprintf(qh, fp, 9136, "- f%d\n", facet->id); + qh_fprintf(qh, fp, 9137, " - flags:"); + if (facet->toporient) + qh_fprintf(qh, fp, 9138, " top"); + else + qh_fprintf(qh, fp, 9139, " bottom"); + if (facet->simplicial) + qh_fprintf(qh, fp, 9140, " simplicial"); + if (facet->tricoplanar) + qh_fprintf(qh, fp, 9141, " tricoplanar"); + if (facet->upperdelaunay) + qh_fprintf(qh, fp, 9142, " upperDelaunay"); + if (facet->visible) + qh_fprintf(qh, fp, 9143, " visible"); + if (facet->newfacet) + qh_fprintf(qh, fp, 9144, " new"); + if (facet->tested) + qh_fprintf(qh, fp, 9145, " tested"); + if (!facet->good) + qh_fprintf(qh, fp, 9146, " notG"); + if (facet->seen) + qh_fprintf(qh, fp, 9147, " seen"); + if (facet->coplanar) + qh_fprintf(qh, fp, 9148, " coplanar"); + if (facet->mergehorizon) + qh_fprintf(qh, fp, 9149, " mergehorizon"); + if (facet->keepcentrum) + qh_fprintf(qh, fp, 9150, " keepcentrum"); + if (facet->dupridge) + qh_fprintf(qh, fp, 9151, " dupridge"); + if (facet->mergeridge && !facet->mergeridge2) + qh_fprintf(qh, fp, 9152, " mergeridge1"); + if (facet->mergeridge2) + qh_fprintf(qh, fp, 9153, " mergeridge2"); + if (facet->newmerge) + qh_fprintf(qh, fp, 9154, " newmerge"); + if (facet->flipped) + qh_fprintf(qh, fp, 9155, " flipped"); + if (facet->notfurthest) + qh_fprintf(qh, fp, 9156, " notfurthest"); + if (facet->degenerate) + qh_fprintf(qh, fp, 9157, " degenerate"); + if (facet->redundant) + qh_fprintf(qh, fp, 9158, " redundant"); + qh_fprintf(qh, fp, 9159, "\n"); + if (facet->isarea) + qh_fprintf(qh, fp, 9160, " - area: %2.2g\n", facet->f.area); + else if (qh->NEWfacets && facet->visible && facet->f.replace) + qh_fprintf(qh, fp, 9161, " - replacement: f%d\n", facet->f.replace->id); + else if (facet->newfacet) { + if (facet->f.samecycle && facet->f.samecycle != facet) + qh_fprintf(qh, fp, 9162, " - shares same visible/horizon as f%d\n", facet->f.samecycle->id); + }else if (facet->tricoplanar /* !isarea */) { + if (facet->f.triowner) + qh_fprintf(qh, fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id); + }else if (facet->f.newcycle) + qh_fprintf(qh, fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id); + if (facet->nummerge) + qh_fprintf(qh, fp, 9165, " - merges: %d\n", facet->nummerge); + qh_printpointid(qh, fp, " - normal: ", qh->hull_dim, facet->normal, qh_IDunknown); + qh_fprintf(qh, fp, 9166, " - offset: %10.7g\n", facet->offset); + if (qh->CENTERtype == qh_ASvoronoi || facet->center) + qh_printcenter(qh, fp, qh_PRINTfacets, " - center: ", facet); +#if qh_MAXoutside + if (facet->maxoutside > qh->DISTround) + qh_fprintf(qh, fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside); +#endif + if (!SETempty_(facet->outsideset)) { + furthest= (pointT*)qh_setlast(facet->outsideset); + if (qh_setsize(qh, facet->outsideset) < 6) { + qh_fprintf(qh, fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(qh, furthest)); + FOREACHpoint_(facet->outsideset) + qh_printpoint(qh, fp, " ", point); + }else if (qh_setsize(qh, facet->outsideset) < 21) { + qh_printpoints(qh, fp, " - outside set:", facet->outsideset); + }else { + qh_fprintf(qh, fp, 9169, " - outside set: %d points.", qh_setsize(qh, facet->outsideset)); + qh_printpoint(qh, fp, " Furthest", furthest); + } +#if !qh_COMPUTEfurthest + qh_fprintf(qh, fp, 9170, " - furthest distance= %2.2g\n", facet->furthestdist); +#endif + } + if (!SETempty_(facet->coplanarset)) { + furthest= (pointT*)qh_setlast(facet->coplanarset); + if (qh_setsize(qh, facet->coplanarset) < 6) { + qh_fprintf(qh, fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(qh, furthest)); + FOREACHpoint_(facet->coplanarset) + qh_printpoint(qh, fp, " ", point); + }else if (qh_setsize(qh, facet->coplanarset) < 21) { + qh_printpoints(qh, fp, " - coplanar set:", facet->coplanarset); + }else { + qh_fprintf(qh, fp, 9172, " - coplanar set: %d points.", qh_setsize(qh, facet->coplanarset)); + qh_printpoint(qh, fp, " Furthest", furthest); + } + zinc_(Zdistio); + qh_distplane(qh, furthest, facet, &dist); + qh_fprintf(qh, fp, 9173, " furthest distance= %2.2g\n", dist); + } + qh_printvertices(qh, fp, " - vertices:", facet->vertices); + qh_fprintf(qh, fp, 9174, " - neighboring facets:"); + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) + qh_fprintf(qh, fp, 9175, " MERGE"); + else if (neighbor == qh_DUPLICATEridge) + qh_fprintf(qh, fp, 9176, " DUP"); + else + qh_fprintf(qh, fp, 9177, " f%d", neighbor->id); + } + qh_fprintf(qh, fp, 9178, "\n"); + qh->RANDOMdist= qh->old_randomdist; +} /* printfacetheader */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacetridges">-</a> + + qh_printfacetridges(qh, fp, facet ) + prints ridges of a facet to fp + + notes: + ridges printed in neighbor order + assumes the ridges exist + for 'f' output + same as QhullFacet::printRidges +*/ +void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int numridges= 0; + + + if (facet->visible && qh->NEWfacets) { + qh_fprintf(qh, fp, 9179, " - ridges(ids may be garbage):"); + FOREACHridge_(facet->ridges) + qh_fprintf(qh, fp, 9180, " r%d", ridge->id); + qh_fprintf(qh, fp, 9181, "\n"); + }else { + qh_fprintf(qh, fp, 9182, " - ridges:\n"); + FOREACHridge_(facet->ridges) + ridge->seen= False; + if (qh->hull_dim == 3) { + ridge= SETfirstt_(facet->ridges, ridgeT); + while (ridge && !ridge->seen) { + ridge->seen= True; + qh_printridge(qh, 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(qh, fp, ridge); + numridges++; + } + } + } + } + if (numridges != qh_setsize(qh, facet->ridges)) { + qh_fprintf(qh, fp, 9183, " - all ridges:"); + FOREACHridge_(facet->ridges) + qh_fprintf(qh, fp, 9184, " r%d", ridge->id); + qh_fprintf(qh, fp, 9185, "\n"); + } + FOREACHridge_(facet->ridges) { + if (!ridge->seen) + qh_printridge(qh, fp, ridge); + } + } +} /* printfacetridges */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacets">-</a> + + qh_printfacets(qh, 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(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars; + facetT *facet, **facetp; + setT *vertices; + coordT *center; + realT outerplane, innerplane; + + qh->old_randomdist= qh->RANDOMdist; + qh->RANDOMdist= False; + if (qh->CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff)) + qh_fprintf(qh, qh->ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n"); + if (format == qh_PRINTnone) + ; /* print nothing */ + else if (format == qh_PRINTaverage) { + vertices= qh_facetvertices(qh, facetlist, facets, printall); + center= qh_getcenter(qh, vertices); + qh_fprintf(qh, fp, 9186, "%d 1\n", qh->hull_dim); + qh_printpointid(qh, fp, NULL, qh->hull_dim, center, qh_IDunknown); + qh_memfree(qh, center, qh->normal_size); + qh_settempfree(qh, &vertices); + }else if (format == qh_PRINTextremes) { + if (qh->DELAUNAY) + qh_printextremes_d(qh, fp, facetlist, facets, printall); + else if (qh->hull_dim == 2) + qh_printextremes_2d(qh, fp, facetlist, facets, printall); + else + qh_printextremes(qh, fp, facetlist, facets, printall); + }else if (format == qh_PRINToptions) + qh_fprintf(qh, fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options); + else if (format == qh_PRINTpoints && !qh->VORONOI) + qh_printpoints_out(qh, fp, facetlist, facets, printall); + else if (format == qh_PRINTqhull) + qh_fprintf(qh, fp, 9188, "%s | %s\n", qh->rbox_command, qh->qhull_command); + else if (format == qh_PRINTsize) { + qh_fprintf(qh, fp, 9189, "0\n2 "); + qh_fprintf(qh, fp, 9190, qh_REAL_1, qh->totarea); + qh_fprintf(qh, fp, 9191, qh_REAL_1, qh->totvol); + qh_fprintf(qh, fp, 9192, "\n"); + }else if (format == qh_PRINTsummary) { + qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + vertices= qh_facetvertices(qh, facetlist, facets, printall); + qh_fprintf(qh, fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh->hull_dim, + qh->num_points + qh_setsize(qh, qh->other_points), + qh->num_vertices, qh->num_facets - qh->num_visible, + qh_setsize(qh, vertices), numfacets, numcoplanars, + numfacets - numsimplicial, zzval_(Zdelvertextot), + numtricoplanars); + qh_settempfree(qh, &vertices); + qh_outerinner(qh, NULL, &outerplane, &innerplane); + qh_fprintf(qh, fp, 9194, qh_REAL_2n, outerplane, innerplane); + }else if (format == qh_PRINTvneighbors) + qh_printvneighbors(qh, fp, facetlist, facets, printall); + else if (qh->VORONOI && format == qh_PRINToff) + qh_printvoronoi(qh, fp, format, facetlist, facets, printall); + else if (qh->VORONOI && format == qh_PRINTgeom) { + qh_printbegin(qh, fp, format, facetlist, facets, printall); + qh_printvoronoi(qh, fp, format, facetlist, facets, printall); + qh_printend(qh, fp, format, facetlist, facets, printall); + }else if (qh->VORONOI + && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter)) + qh_printvdiagram(qh, fp, format, facetlist, facets, printall); + else { + qh_printbegin(qh, fp, format, facetlist, facets, printall); + FORALLfacet_(facetlist) + qh_printafacet(qh, fp, format, facet, printall); + FOREACHfacet_(facets) + qh_printafacet(qh, fp, format, facet, printall); + qh_printend(qh, fp, format, facetlist, facets, printall); + } + qh->RANDOMdist= qh->old_randomdist; +} /* printfacets */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printhyperplaneintersection">-</a> + + qh_printhyperplaneintersection(qh, 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(qhT *qh, 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(qh, facet1->normal, facet2->normal); + denominator= 1 - costheta * costheta; + i= qh_setsize(qh, vertices); + if (qh->hull_dim == 3) + qh_fprintf(qh, fp, 9195, "VECT 1 %d 1 %d 1 ", i, i); + else if (qh->hull_dim == 4 && qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9196, "OFF 3 1 1 "); + else + qh->printoutvar++; + qh_fprintf(qh, fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id); + mindenom= 1 / (10.0 * qh->MAXabs_coord); + FOREACHvertex_(vertices) { + zadd_(Zdistio, 2); + qh_distplane(qh, vertex->point, facet1, &dist1); + qh_distplane(qh, 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(qh, p, p); + qh_fprintf(qh, fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]); + }else + qh_fprintf(qh, fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]); + if (nearzero1+nearzero2) + qh_fprintf(qh, fp, 9200, "p%d(coplanar facets)\n", qh_pointid(qh, vertex->point)); + else + qh_fprintf(qh, fp, 9201, "projected p%d\n", qh_pointid(qh, vertex->point)); + } + if (qh->hull_dim == 3) + qh_fprintf(qh, fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); + else if (qh->hull_dim == 4 && qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printhyperplaneintersection */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printline3geom">-</a> + + qh_printline3geom(qh, 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(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) { + int k; + realT pA[4], pB[4]; + + qh_projectdim3(qh, pointA, pA); + qh_projectdim3(qh, pointB, pB); + if ((fabs(pA[0] - pB[0]) > 1e-3) || + (fabs(pA[1] - pB[1]) > 1e-3) || + (fabs(pA[2] - pB[2]) > 1e-3)) { + qh_fprintf(qh, fp, 9204, "VECT 1 2 1 2 1\n"); + for (k=0; k < 3; k++) + qh_fprintf(qh, fp, 9205, "%8.4g ", pB[k]); + qh_fprintf(qh, fp, 9206, " # p%d\n", qh_pointid(qh, pointB)); + }else + qh_fprintf(qh, fp, 9207, "VECT 1 1 1 1 1\n"); + for (k=0; k < 3; k++) + qh_fprintf(qh, fp, 9208, "%8.4g ", pA[k]); + qh_fprintf(qh, fp, 9209, " # p%d\n", qh_pointid(qh, pointA)); + qh_fprintf(qh, fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]); +} + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printneighborhood">-</a> + + qh_printneighborhood(qh, fp, format, facetA, facetB, printall ) + print neighborhood of one or two facets + + notes: + calls qh_findgood_all() + bumps qh.visit_id +*/ +void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) { + facetT *neighbor, **neighborp, *facet; + setT *facets; + + if (format == qh_PRINTnone) + return; + qh_findgood_all(qh, qh->facet_list); + if (facetA == facetB) + facetB= NULL; + facets= qh_settemp(qh, 2*(qh_setsize(qh, 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(qh, &facets, facet); + } + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh->visit_id) + continue; + neighbor->visitid= qh->visit_id; + if (printall || !qh_skipfacet(qh, neighbor)) + qh_setappend(qh, &facets, neighbor); + } + } + qh_printfacets(qh, fp, format, NULL, facets, printall); + qh_settempfree(qh, &facets); +} /* printneighborhood */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpoint">-</a> + + qh_printpoint(qh, fp, string, point ) + qh_printpointid(qh, 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=qh_IDunknown(-1) or qh_IDnone(-3) + + notes: + nop if point is NULL + Same as QhullPoint's printPoint +*/ +void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point) { + int id= qh_pointid(qh, point); + + qh_printpointid(qh, fp, string, qh->hull_dim, point, id); +} /* printpoint */ + +void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id) { + int k; + realT r; /*bug fix*/ + + if (!point) + return; + if (string) { + qh_fprintf(qh, fp, 9211, "%s", string); + if (id != qh_IDunknown && id != qh_IDnone) + qh_fprintf(qh, fp, 9212, " p%d: ", id); + } + for (k=dim; k--; ) { + r= *point++; + if (string) + qh_fprintf(qh, fp, 9213, " %8.4g", r); + else + qh_fprintf(qh, fp, 9214, qh_REAL_1, r); + } + qh_fprintf(qh, fp, 9215, "\n"); +} /* printpointid */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpoint3">-</a> + + qh_printpoint3(qh, fp, point ) + prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates +*/ +void qh_printpoint3(qhT *qh, FILE *fp, pointT *point) { + int k; + realT p[4]; + + qh_projectdim3(qh, point, p); + for (k=0; k < 3; k++) + qh_fprintf(qh, fp, 9216, "%8.4g ", p[k]); + qh_fprintf(qh, fp, 9217, " # p%d\n", qh_pointid(qh, point)); +} /* printpoint3 */ + +/*---------------------------------------- +-printpoints- print pointids for a set of points starting at index + see geom_r.c +*/ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpoints_out">-</a> + + qh_printpoints_out(qh, 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(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + int allpoints= qh->num_points + qh_setsize(qh, 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(qh, allpoints); + qh_setzero(qh, points, 0, allpoints); + vertices= qh_facetvertices(qh, facetlist, facets, printall); + FOREACHvertex_(vertices) { + id= qh_pointid(qh, vertex->point); + if (id >= 0) + SETelem_(points, id)= vertex->point; + } + if (qh->KEEPinside || qh->KEEPcoplanar || qh->KEEPnearinside) { + FORALLfacet_(facetlist) { + if (!printall && qh_skipfacet(qh, facet)) + continue; + FOREACHpoint_(facet->coplanarset) { + id= qh_pointid(qh, point); + if (id >= 0) + SETelem_(points, id)= point; + } + } + FOREACHfacet_(facets) { + if (!printall && qh_skipfacet(qh, facet)) + continue; + FOREACHpoint_(facet->coplanarset) { + id= qh_pointid(qh, point); + if (id >= 0) + SETelem_(points, id)= point; + } + } + } + qh_settempfree(qh, &vertices); + FOREACHpoint_i_(qh, points) { + if (point) + numpoints++; + } + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command, + qh->qhull_command, numpoints, qh->hull_dim + 1); + else + qh_fprintf(qh, fp, 9219, "%d\n%d\n", qh->hull_dim, numpoints); + FOREACHpoint_i_(qh, points) { + if (point) { + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9220, "1 "); + qh_printpoint(qh, fp, NULL, point); + } + } + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9221, "end\n"); + qh_settempfree(qh, &points); +} /* printpoints_out */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpointvect">-</a> + + qh_printpointvect(qh, 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(qhT *qh, 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(qh, diff, qh->hull_dim, True, NULL, NULL); + for (k=qh->hull_dim; k--; ) + pointA[k]= point[k]+diff[k] * radius; + qh_printline3geom(qh, fp, point, pointA, color); +} /* printpointvect */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpointvect2">-</a> + + qh_printpointvect2(qh, 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(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) { + realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0}; + + qh_printpointvect(qh, fp, point, normal, center, radius, red); + qh_printpointvect(qh, fp, point, normal, center, -radius, yellow); +} /* printpointvect2 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printridge">-</a> + + qh_printridge(qh, fp, ridge ) + prints the information in a ridge + + notes: + for qh_printfacetridges() + same as operator<< [QhullRidge.cpp] +*/ +void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge) { + + qh_fprintf(qh, fp, 9222, " - r%d", ridge->id); + if (ridge->tested) + qh_fprintf(qh, fp, 9223, " tested"); + if (ridge->nonconvex) + qh_fprintf(qh, fp, 9224, " nonconvex"); + qh_fprintf(qh, fp, 9225, "\n"); + qh_printvertices(qh, fp, " vertices:", ridge->vertices); + if (ridge->top && ridge->bottom) + qh_fprintf(qh, fp, 9226, " between f%d and f%d\n", + ridge->top->id, ridge->bottom->id); +} /* printridge */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printspheres">-</a> + + qh_printspheres(qh, fp, vertices, radius ) + prints 3-d vertices as OFF spheres + + notes: + inflated octahedron from Stuart Levy earth/mksphere2 +*/ +void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius) { + vertexT *vertex, **vertexp; + + qh->printoutnum++; + qh_fprintf(qh, fp, 9227, "{appearance {-edge -normal normscale 0} {\n\ +INST geom {define vsphere OFF\n\ +18 32 48\n\ +\n\ +0 0 1\n\ +1 0 0\n\ +0 1 0\n\ +-1 0 0\n\ +0 -1 0\n\ +0 0 -1\n\ +0.707107 0 0.707107\n\ +0 -0.707107 0.707107\n\ +0.707107 -0.707107 0\n\ +-0.707107 0 0.707107\n\ +-0.707107 -0.707107 0\n\ +0 0.707107 0.707107\n\ +-0.707107 0.707107 0\n\ +0.707107 0.707107 0\n\ +0.707107 0 -0.707107\n\ +0 0.707107 -0.707107\n\ +-0.707107 0 -0.707107\n\ +0 -0.707107 -0.707107\n\ +\n\ +3 0 6 11\n\ +3 0 7 6 \n\ +3 0 9 7 \n\ +3 0 11 9\n\ +3 1 6 8 \n\ +3 1 8 14\n\ +3 1 13 6\n\ +3 1 14 13\n\ +3 2 11 13\n\ +3 2 12 11\n\ +3 2 13 15\n\ +3 2 15 12\n\ +3 3 9 12\n\ +3 3 10 9\n\ +3 3 12 16\n\ +3 3 16 10\n\ +3 4 7 10\n\ +3 4 8 7\n\ +3 4 10 17\n\ +3 4 17 8\n\ +3 5 14 17\n\ +3 5 15 14\n\ +3 5 16 15\n\ +3 5 17 16\n\ +3 6 13 11\n\ +3 7 8 6\n\ +3 9 10 7\n\ +3 11 12 9\n\ +3 14 8 17\n\ +3 15 13 14\n\ +3 16 12 15\n\ +3 17 10 16\n} transforms { TLIST\n"); + FOREACHvertex_(vertices) { + qh_fprintf(qh, fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n", + radius, vertex->id, radius, radius); + qh_printpoint3(qh, fp, vertex->point); + qh_fprintf(qh, fp, 9229, "1\n"); + } + qh_fprintf(qh, fp, 9230, "}}}\n"); +} /* printspheres */ + + +/*---------------------------------------------- +-printsummary- + see libqhull_r.c +*/ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvdiagram">-</a> + + qh_printvdiagram(qh, 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(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices; + int totcount, numcenters; + boolT isLower; + qh_RIDGE innerouter= qh_RIDGEall; + printvridgeT printvridge= NULL; + + if (format == qh_PRINTvertices) { + innerouter= qh_RIDGEall; + printvridge= qh_printvridge; + }else if (format == qh_PRINTinner) { + innerouter= qh_RIDGEinner; + printvridge= qh_printvnorm; + }else if (format == qh_PRINTouter) { + innerouter= qh_RIDGEouter; + printvridge= qh_printvnorm; + }else { + qh_fprintf(qh, qh->ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters); + totcount= qh_printvdiagram2(qh, NULL, NULL, vertices, innerouter, False); + qh_fprintf(qh, fp, 9231, "%d\n", totcount); + totcount= qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, True /* inorder*/); + qh_settempfree(qh, &vertices); +#if 0 /* for testing qh_eachvoronoi_all */ + qh_fprintf(qh, fp, 9232, "\n"); + totcount= qh_eachvoronoi_all(qh, fp, printvridge, qh->UPPERdelaunay, innerouter, True /* inorder*/); + qh_fprintf(qh, fp, 9233, "%d\n", totcount); +#endif +} /* printvdiagram */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvdiagram2">-</a> + + qh_printvdiagram2(qh, 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(qhT *qh, 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_(qh, vertices) { + if (vertex) { + if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex) + continue; + totcount += qh_eachvoronoi(qh, fp, printvridge, vertex, !qh_ALL, innerouter, inorder); + } + } + return totcount; +} /* printvdiagram2 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvertex">-</a> + + qh_printvertex(qh, fp, vertex ) + prints the information in a vertex + Duplicated as operator<< [QhullVertex.cpp] +*/ +void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex) { + pointT *point; + int k, count= 0; + facetT *neighbor, **neighborp; + realT r; /*bug fix*/ + + if (!vertex) { + qh_fprintf(qh, fp, 9234, " NULLvertex\n"); + return; + } + qh_fprintf(qh, fp, 9235, "- p%d(v%d):", qh_pointid(qh, vertex->point), vertex->id); + point= vertex->point; + if (point) { + for (k=qh->hull_dim; k--; ) { + r= *point++; + qh_fprintf(qh, fp, 9236, " %5.2g", r); + } + } + if (vertex->deleted) + qh_fprintf(qh, fp, 9237, " deleted"); + if (vertex->delridge) + qh_fprintf(qh, fp, 9238, " ridgedeleted"); + qh_fprintf(qh, fp, 9239, "\n"); + if (vertex->neighbors) { + qh_fprintf(qh, fp, 9240, " neighbors:"); + FOREACHneighbor_(vertex) { + if (++count % 100 == 0) + qh_fprintf(qh, fp, 9241, "\n "); + qh_fprintf(qh, fp, 9242, " f%d", neighbor->id); + } + qh_fprintf(qh, fp, 9243, "\n"); + } +} /* printvertex */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvertexlist">-</a> + + qh_printvertexlist(qh, fp, string, facetlist, facets, printall ) + prints vertices used by a facetlist or facet set + tests qh_skipfacet() if !printall +*/ +void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist, + setT *facets, boolT printall) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facetvertices(qh, facetlist, facets, printall); + qh_fprintf(qh, fp, 9244, "%s", string); + FOREACHvertex_(vertices) + qh_printvertex(qh, fp, vertex); + qh_settempfree(qh, &vertices); +} /* printvertexlist */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvertices">-</a> + + qh_printvertices(qh, fp, string, vertices ) + prints vertices in a set + duplicated as printVertexSet [QhullVertex.cpp] +*/ +void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices) { + vertexT *vertex, **vertexp; + + qh_fprintf(qh, fp, 9245, "%s", string); + FOREACHvertex_(vertices) + qh_fprintf(qh, fp, 9246, " p%d(v%d)", qh_pointid(qh, vertex->point), vertex->id); + qh_fprintf(qh, fp, 9247, "\n"); +} /* printvertices */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvneighbors">-</a> + + qh_printvneighbors(qh, 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(qhT *qh, 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, qh->other_points); + vertexT *vertex, **vertexp; + int vertex_i, vertex_n; + facetT *facet, **facetp, *neighbor, **neighborp; + pointT *point, **pointp; + + qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* sets facet->visitid */ + qh_fprintf(qh, fp, 9248, "%d\n", numpoints); + qh_vertexneighbors(qh); + vertices= qh_facetvertices(qh, facetlist, facets, printall); + vertex_points= qh_settemp(qh, numpoints); + coplanar_points= qh_settemp(qh, numpoints); + qh_setzero(qh, vertex_points, 0, numpoints); + qh_setzero(qh, coplanar_points, 0, numpoints); + FOREACHvertex_(vertices) + qh_point_add(qh, vertex_points, vertex->point, vertex); + FORALLfacet_(facetlist) { + FOREACHpoint_(facet->coplanarset) + qh_point_add(qh, coplanar_points, point, facet); + } + FOREACHfacet_(facets) { + FOREACHpoint_(facet->coplanarset) + qh_point_add(qh, coplanar_points, point, facet); + } + FOREACHvertex_i_(qh, vertex_points) { + if (vertex) { + numneighbors= qh_setsize(qh, vertex->neighbors); + qh_fprintf(qh, fp, 9249, "%d", numneighbors); + if (qh->hull_dim == 3) + qh_order_vertexneighbors(qh, vertex); + else if (qh->hull_dim >= 4) + qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors, + sizeof(facetT *), qh_compare_facetvisit); + FOREACHneighbor_(vertex) + qh_fprintf(qh, fp, 9250, " %d", + neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id); + qh_fprintf(qh, fp, 9251, "\n"); + }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT))) + qh_fprintf(qh, fp, 9252, "1 %d\n", + facet->visitid ? facet->visitid - 1 : 0 - facet->id); + else + qh_fprintf(qh, fp, 9253, "0\n"); + } + qh_settempfree(qh, &coplanar_points); + qh_settempfree(qh, &vertex_points); + qh_settempfree(qh, &vertices); +} /* printvneighbors */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvoronoi">-</a> + + qh_printvoronoi(qh, 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(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n; + facetT *facet, **facetp, *neighbor, **neighborp; + setT *vertices; + vertexT *vertex; + boolT isLower; + unsigned int numfacets= (unsigned int) qh->num_facets; + + vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters); + FOREACHvertex_i_(qh, vertices) { + if (vertex) { + numvertices++; + numneighbors = numinf = 0; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) + numinf= 1; + else if (neighbor->visitid < numfacets) + numneighbors++; + } + if (numinf && !numneighbors) { + SETelem_(vertices, vertex_i)= NULL; + numvertices--; + } + } + } + if (format == qh_PRINTgeom) + qh_fprintf(qh, fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n", + numcenters, numvertices); + else + qh_fprintf(qh, fp, 9255, "%d\n%d %d 1\n", qh->hull_dim-1, numcenters, qh_setsize(qh, vertices)); + if (format == qh_PRINTgeom) { + for (k=qh->hull_dim-1; k--; ) + qh_fprintf(qh, fp, 9256, qh_REAL_1, 0.0); + qh_fprintf(qh, fp, 9257, " 0 # infinity not used\n"); + }else { + for (k=qh->hull_dim-1; k--; ) + qh_fprintf(qh, fp, 9258, qh_REAL_1, qh_INFINITE); + qh_fprintf(qh, fp, 9259, "\n"); + } + FORALLfacet_(facetlist) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + qh_fprintf(qh, fp, 9260, "# %d f%d\n", vid++, facet->id); + qh_printcenter(qh, fp, format, NULL, facet); + } + } + FOREACHfacet_(facets) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + qh_fprintf(qh, fp, 9261, "# %d f%d\n", vid++, facet->id); + qh_printcenter(qh, fp, format, NULL, facet); + } + } + FOREACHvertex_i_(qh, vertices) { + numneighbors= 0; + numinf=0; + if (vertex) { + if (qh->hull_dim == 3) + qh_order_vertexneighbors(qh, vertex); + else if (qh->hull_dim >= 4) + qsort(SETaddr_(vertex->neighbors, facetT), + (size_t)qh_setsize(qh, vertex->neighbors), + sizeof(facetT *), qh_compare_facetvisit); + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) + numinf= 1; + else if (neighbor->visitid < numfacets) + numneighbors++; + } + } + if (format == qh_PRINTgeom) { + if (vertex) { + qh_fprintf(qh, fp, 9262, "%d", numneighbors); + FOREACHneighbor_(vertex) { + if (neighbor->visitid && neighbor->visitid < numfacets) + qh_fprintf(qh, fp, 9263, " %d", neighbor->visitid); + } + qh_fprintf(qh, fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id); + }else + qh_fprintf(qh, fp, 9265, " # p%d is coplanar or isolated\n", vertex_i); + }else { + if (numinf) + numneighbors++; + qh_fprintf(qh, fp, 9266, "%d", numneighbors); + if (vertex) { + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) { + if (numinf) { + numinf= 0; + qh_fprintf(qh, fp, 9267, " %d", neighbor->visitid); + } + }else if (neighbor->visitid < numfacets) + qh_fprintf(qh, fp, 9268, " %d", neighbor->visitid); + } + } + qh_fprintf(qh, fp, 9269, "\n"); + } + } + if (format == qh_PRINTgeom) + qh_fprintf(qh, fp, 9270, "}\n"); + qh_settempfree(qh, &vertices); +} /* printvoronoi */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvnorm">-</a> + + qh_printvnorm(qh, fp, vertex, vertexA, centers, unbounded ) + print one separating plane of the Voronoi diagram for a pair of input sites + unbounded==True if centers includes vertex-at-infinity + + assumes: + qh_ASvoronoi and qh_vertexneighbors() already set + + note: + parameter unbounded is UNUSED by this callback + + see: + qh_printvdiagram() + qh_eachvoronoi() +*/ +void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + pointT *normal; + realT offset; + int k; + QHULL_UNUSED(unbounded); + + normal= qh_detvnorm(qh, vertex, vertexA, centers, &offset); + qh_fprintf(qh, fp, 9271, "%d %d %d ", + 2+qh->hull_dim, qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point)); + for (k=0; k< qh->hull_dim-1; k++) + qh_fprintf(qh, fp, 9272, qh_REAL_1, normal[k]); + qh_fprintf(qh, fp, 9273, qh_REAL_1, offset); + qh_fprintf(qh, fp, 9274, "\n"); +} /* printvnorm */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvridge">-</a> + + qh_printvridge(qh, fp, vertex, vertexA, centers, unbounded ) + print one ridge of the Voronoi diagram for a pair of input sites + unbounded==True if centers includes vertex-at-infinity + + see: + qh_printvdiagram() + + notes: + the user may use a different function + parameter unbounded is UNUSED +*/ +void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + facetT *facet, **facetp; + QHULL_UNUSED(unbounded); + + qh_fprintf(qh, fp, 9275, "%d %d %d", qh_setsize(qh, centers)+2, + qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point)); + FOREACHfacet_(centers) + qh_fprintf(qh, fp, 9276, " %d", facet->visitid); + qh_fprintf(qh, fp, 9277, "\n"); +} /* printvridge */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="projectdim3">-</a> + + qh_projectdim3(qh, 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(qhT *qh, 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_r.htm#TOC" + >-------------------------------</a><a name="readfeasible">-</a> + + qh_readfeasible(qh, dim, curline ) + read feasible point from current line and qh.fin + + returns: + number of lines read from qh.fin + sets qh.feasible_point with malloc'd coordinates + + notes: + checks for qh.HALFspace + assumes dim > 1 + + see: + qh_setfeasible +*/ +int qh_readfeasible(qhT *qh, int dim, const char *curline) { + boolT isfirst= True; + int linecount= 0, tokcount= 0; + const char *s; + char *t, firstline[qh_MAXfirst+1]; + coordT *coords, value; + + if (!qh->HALFspace) { + qh_fprintf(qh, qh->ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->feasible_string) + qh_fprintf(qh, qh->ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n"); + if (!(qh->feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) { + qh_fprintf(qh, qh->ferr, 6071, "qhull error: insufficient memory for feasible point\n"); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + coords= qh->feasible_point; + while ((s= (isfirst ? curline : fgets(firstline, qh_MAXfirst, qh->fin)))) { + if (isfirst) + isfirst= False; + else + linecount++; + while (*s) { + while (isspace(*s)) + s++; + value= qh_strtod(s, &t); + if (s == t) + break; + s= t; + *(coords++)= value; + if (++tokcount == dim) { + while (isspace(*s)) + s++; + qh_strtod(s, &t); + if (s != t) { + qh_fprintf(qh, qh->ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n", + s); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + return linecount; + } + } + } + qh_fprintf(qh, qh->ferr, 6073, "qhull input error: only %d coordinates. Could not read %d-d feasible point.\n", + tokcount, dim); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + return 0; +} /* readfeasible */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="readpoints">-</a> + + qh_readpoints(qh, numpoints, dimension, ismalloc ) + read points from qh.fin into qh.first_point, qh.num_points + qh.fin is lines of coordinates, one per vertex, first line number of points + if 'rbox D4', + gives message + if qh.ATinfinity, + adds point-at-infinity for Delaunay triangulations + + returns: + number of points, array of point coordinates, dimension, ismalloc True + if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid + and clears qh.PROJECTdelaunay + if qh.HALFspace, reads optional feasible point, reads halfspaces, + converts to dual. + + for feasible point in "cdd format" in 3-d: + 3 1 + coordinates + comments + begin + n 4 real/integer + ... + end + + notes: + dimension will change in qh_initqhull_globals if qh.PROJECTinput + uses malloc() since qh_mem not initialized + FIXUP QH11012: qh_readpoints needs rewriting, too long +*/ +coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc) { + coordT *points, *coords, *infinity= NULL; + realT paraboloid, maxboloid= -REALmax, value; + realT *coordp= NULL, *offsetp= NULL, *normalp= NULL; + char *s= 0, *t, firstline[qh_MAXfirst+1]; + int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi; + int firsttext=0, firstshort=0, firstlong=0, firstpoint=0; + int tokcount= 0, linecount=0, maxcount, coordcount=0; + boolT islong, isfirst= True, wasbegin= False; + boolT isdelaunay= qh->DELAUNAY && !qh->PROJECTinput; + + if (qh->CDDinput) { + while ((s= fgets(firstline, qh_MAXfirst, qh->fin))) { + linecount++; + if (qh->HALFspace && linecount == 1 && isdigit(*s)) { + dimfeasible= qh_strtol(s, &s); + while (isspace(*s)) + s++; + if (qh_strtol(s, &s) == 1) + linecount += qh_readfeasible(qh, dimfeasible, s); + else + dimfeasible= 0; + }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5)) + break; + else if (!*qh->rbox_command) + strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1); + } + if (!s) { + qh_fprintf(qh, qh->ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + } + while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh->fin))) { + linecount++; + if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5)) + wasbegin= True; + while (*s) { + while (isspace(*s)) + s++; + if (!*s) + break; + if (!isdigit(*s)) { + if (!*qh->rbox_command) { + strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1); + firsttext= linecount; + } + break; + } + if (!diminput) + diminput= qh_strtol(s, &s); + else { + numinput= qh_strtol(s, &s); + if (numinput == 1 && diminput >= 2 && qh->HALFspace && !qh->CDDinput) { + linecount += qh_readfeasible(qh, diminput, s); /* checks if ok */ + dimfeasible= diminput; + diminput= numinput= 0; + }else + break; + } + } + } + if (!s) { + qh_fprintf(qh, qh->ferr, 6075, "qhull input error: short input file. Did not find dimension and number of points\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (diminput > numinput) { + tempi= diminput; /* exchange dim and n, e.g., for cdd input format */ + diminput= numinput; + numinput= tempi; + } + if (diminput < 2) { + qh_fprintf(qh, qh->ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n", + diminput); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (isdelaunay) { + qh->PROJECTdelaunay= False; + if (qh->CDDinput) + *dimension= diminput; + else + *dimension= diminput+1; + *numpoints= numinput; + if (qh->ATinfinity) + (*numpoints)++; + }else if (qh->HALFspace) { + *dimension= diminput - 1; + *numpoints= numinput; + if (diminput < 3) { + qh_fprintf(qh, qh->ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n", + diminput); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (dimfeasible) { + if (dimfeasible != *dimension) { + qh_fprintf(qh, qh->ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n", + dimfeasible, diminput); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + }else + qh_setfeasible(qh, *dimension); + }else { + if (qh->CDDinput) + *dimension= diminput-1; + else + *dimension= diminput; + *numpoints= numinput; + } + qh->normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */ + if (qh->HALFspace) { + qh->half_space= coordp= (coordT*)qh_malloc(qh->normal_size + sizeof(coordT)); + if (qh->CDDinput) { + offsetp= qh->half_space; + normalp= offsetp + 1; + }else { + normalp= qh->half_space; + offsetp= normalp + *dimension; + } + } + qh->maxline= diminput * (qh_REALdigits + 5); + maximize_(qh->maxline, 500); + qh->line= (char*)qh_malloc((qh->maxline+1) * sizeof(char)); + *ismalloc= True; /* use malloc since memory not setup */ + coords= points= qh->temp_malloc= /* numinput and diminput >=2 by QH6220 */ + (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT)); + if (!coords || !qh->line || (qh->HALFspace && !qh->half_space)) { + qh_fprintf(qh, qh->ferr, 6076, "qhull error: insufficient memory to read %d points\n", + numinput); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + if (isdelaunay && qh->ATinfinity) { + infinity= points + numinput * (*dimension); + for (k= (*dimension) - 1; k--; ) + infinity[k]= 0.0; + } + maxcount= numinput * diminput; + paraboloid= 0.0; + while ((s= (isfirst ? s : fgets(qh->line, qh->maxline, qh->fin)))) { + if (!isfirst) { + linecount++; + if (*s == 'e' || *s == 'E') { + if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) { + if (qh->CDDinput ) + break; + else if (wasbegin) + qh_fprintf(qh, qh->ferr, 7058, "qhull input warning: the input appears to be in cdd format. If so, use 'Fd'\n"); + } + } + } + islong= False; + while (*s) { + while (isspace(*s)) + s++; + value= qh_strtod(s, &t); + if (s == t) { + if (!*qh->rbox_command) + strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1); + if (*s && !firsttext) + firsttext= linecount; + if (!islong && !firstshort && coordcount) + firstshort= linecount; + break; + } + if (!firstpoint) + firstpoint= linecount; + s= t; + if (++tokcount > maxcount) + continue; + if (qh->HALFspace) { + if (qh->CDDinput) + *(coordp++)= -value; /* both coefficients and offset */ + else + *(coordp++)= value; + }else { + *(coords++)= value; + if (qh->CDDinput && !coordcount) { + if (value != 1.0) { + qh_fprintf(qh, qh->ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n", + linecount); + qh_errexit(qh, 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(qh, *dimension, coords, &coords, normalp, offsetp, qh->feasible_point)) { + qh_fprintf(qh, qh->ferr, 8048, "The halfspace was on line %d\n", linecount); + if (wasbegin) + qh_fprintf(qh, qh->ferr, 8049, "The input appears to be in cdd format. If so, you should use option 'Fd'\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + coordp= qh->half_space; + } + while (isspace(*s)) + s++; + if (*s) { + islong= True; + if (!firstlong) + firstlong= linecount; + } + } + } + if (!islong && !firstshort && coordcount) + firstshort= linecount; + if (!isfirst && s - qh->line >= qh->maxline) { + qh_fprintf(qh, qh->ferr, 6078, "qhull input error: line %d contained more than %d characters\n", + linecount, (int) (s - qh->line)); /* WARN64 */ + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + isfirst= False; + } + if (tokcount != maxcount) { + newnum= fmin_(numinput, tokcount/diminput); + qh_fprintf(qh, qh->ferr, 7073,"\ +qhull warning: instead of %d %d-dimensional points, input contains\n\ +%d points and %d extra coordinates. Line %d is the first\npoint", + numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint); + if (firsttext) + qh_fprintf(qh, qh->ferr, 8051, ", line %d is the first comment", firsttext); + if (firstshort) + qh_fprintf(qh, qh->ferr, 8052, ", line %d is the first short\nline", firstshort); + if (firstlong) + qh_fprintf(qh, qh->ferr, 8053, ", line %d is the first long line", firstlong); + qh_fprintf(qh, qh->ferr, 8054, ". Continue with %d points.\n", newnum); + numinput= newnum; + if (isdelaunay && qh->ATinfinity) { + for (k= tokcount % diminput; k--; ) + infinity[k] -= *(--coords); + *numpoints= newnum+1; + }else { + coords -= tokcount % diminput; + *numpoints= newnum; + } + } + if (isdelaunay && qh->ATinfinity) { + for (k= (*dimension) -1; k--; ) + infinity[k] /= numinput; + if (coords == infinity) + coords += (*dimension) -1; + else { + for (k=0; k < (*dimension) -1; k++) + *(coords++)= infinity[k]; + } + *(coords++)= maxboloid * 1.1; + } + if (qh->rbox_command[0]) { + qh->rbox_command[strlen(qh->rbox_command)-1]= '\0'; + if (!strcmp(qh->rbox_command, "./rbox D4")) + qh_fprintf(qh, qh->ferr, 8055, "\n\ +This is the qhull test case. If any errors or core dumps occur,\n\ +recompile qhull with 'make new'. If errors still occur, there is\n\ +an incompatibility. You should try a different compiler. You can also\n\ +change the choices in user.h. If you discover the source of the problem,\n\ +please send mail to qhull_bug@qhull.org.\n\ +\n\ +Type 'qhull' for a short list of options.\n"); + } + qh_free(qh->line); + qh->line= NULL; + if (qh->half_space) { + qh_free(qh->half_space); + qh->half_space= NULL; + } + qh->temp_malloc= NULL; + trace1((qh, qh->ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n", + numinput, diminput)); + return(points); +} /* readpoints */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="setfeasible">-</a> + + qh_setfeasible(qh, dim ) + set qh.feasible_point 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() + called only once from qh_new_qhull, otherwise leaks memory +*/ +void qh_setfeasible(qhT *qh, int dim) { + int tokcount= 0; + char *s; + coordT *coords, value; + + if (!(s= qh->feasible_string)) { + qh_fprintf(qh, qh->ferr, 6223, "\ +qhull input error: halfspace intersection needs a feasible point.\n\ +Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (!(qh->feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) { + qh_fprintf(qh, qh->ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + coords= qh->feasible_point; + while (*s) { + value= qh_strtod(s, &s); + if (++tokcount > dim) { + qh_fprintf(qh, qh->ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n", + qh->feasible_string, dim); + break; + } + *(coords++)= value; + if (*s) + s++; + } + while (++tokcount <= dim) + *(coords++)= 0.0; +} /* setfeasible */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="skipfacet">-</a> + + qh_skipfacet(qh, 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(qhT *qh, 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(qh, facet->normal, NULL)); +} /* skipfacet */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="skipfilename">-</a> + + qh_skipfilename(qh, string ) + returns pointer to character after filename + + notes: + skips leading spaces + ends with spacing or eol + if starts with ' or " ends with the same, skipping \' or \" + For qhull, qh_argv_to_command() only uses double quotes +*/ +char *qh_skipfilename(qhT *qh, char *filename) { + char *s= filename; /* non-const due to return */ + char c; + + while (*s && isspace(*s)) + s++; + c= *s++; + if (c == '\0') { + qh_fprintf(qh, qh->ferr, 6204, "qhull input error: filename expected, none found.\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (c == '\'' || c == '"') { + while (*s !=c || s[-1] == '\\') { + if (!*s) { + qh_fprintf(qh, qh->ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + s++; + } + s++; + } + else while (*s && !isspace(*s)) + s++; + return s; +} /* skipfilename */ +
+ C/io_r.h view
@@ -0,0 +1,167 @@+/*<html><pre> -<a href="qh-io_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + io_r.h + declarations of Input/Output functions + + see README, libqhull_r.h and io_r.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/io_r.h#3 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ +*/ + +#ifndef qhDEFio +#define qhDEFio 1 + +#include "libqhull_r.h" + +/*============ constants and flags ==================*/ + +/*-<a href="qh-io_r.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_r.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_r.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_r.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_r.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_r.htm#TOC" + >--------------------------------</a><a name="printvridgeT">-</a> + + printvridgeT + prints results of qh_printvdiagram + + see: + <a href="io_r.c#printvridge">qh_printvridge</a> for an example +*/ +typedef void (*printvridgeT)(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); + +/*============== -prototypes in alphabetical order =========*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_dfacet(qhT *qh, unsigned id); +void qh_dvertex(qhT *qh, 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 useable since it depends on qh */ +void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length); +void qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall, + int *numfacetsp, int *numsimplicialp, int *totneighborsp, + int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp); +pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp); +setT *qh_detvridge(qhT *qh, vertexT *vertex); +setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex); +int qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder); +int qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder); +void qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist); +setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets); +void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane); +void qh_markkeep(qhT *qh, facetT *facetlist); +setT *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp); +void qh_order_vertexneighbors(qhT *qh, vertexT *vertex); +void qh_prepare_output(qhT *qh); +void qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall); +void qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet); +void qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius); +void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *num, boolT printall); +void qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printfacet(qhT *qh, FILE *fp, facetT *facet); +void qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst); +void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2, + facetT *facet, realT offset, realT color[3]); +void qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst); +void qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]); +void qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format); +void qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format); +void qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format); +void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet); +void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet); +void qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2, + setT *vertices, realT color[3]); +void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); +void qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]); +void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point); +void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id); +void qh_printpoint3(qhT *qh, FILE *fp, pointT *point); +void qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]); +void qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius); +void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge); +void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius); +void qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +int qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder); +void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex); +void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist, + setT *facets, boolT printall); +void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices); +void qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall); +void qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); +void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); +void qh_produce_output(qhT *qh); +void qh_produce_output2(qhT *qh); +void qh_projectdim3(qhT *qh, pointT *source, pointT *destination); +int qh_readfeasible(qhT *qh, int dim, const char *curline); +coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc); +void qh_setfeasible(qhT *qh, int dim); +boolT qh_skipfacet(qhT *qh, facetT *facet); +char *qh_skipfilename(qhT *qh, char *filename); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFio */
+ C/libqhull_r.c view
@@ -0,0 +1,1403 @@+/*<html><pre> -<a href="qh-qhull_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + libqhull_r.c + Quickhull algorithm for convex hulls + + qhull() and top-level routines + + see qh-qhull_r.htm, libqhull.h, unix_r.c + + see qhull_ra.h for internal functions + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.c#2 $$Change: 2047 $ + $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*============= functions in alphabetic order after qhull() =======*/ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="qhull">-</a> + + qh_qhull(qh) + 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(qhT *qh) { + int numoutside; + + qh->hulltime= qh_CPUclock; + if (qh->RERUN || qh->JOGGLEmax < REALmax/2) + qh_build_withrestart(qh); + else { + qh_initbuild(qh); + qh_buildhull(qh); + } + if (!qh->STOPpoint && !qh->STOPcone) { + if (qh->ZEROall_ok && !qh->TESTvneighbors && qh->MERGEexact) + qh_checkzero(qh, qh_ALL); + if (qh->ZEROall_ok && !qh->TESTvneighbors && !qh->WAScoplanar) { + trace2((qh, qh->ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points. Post-merging and check of maxout not needed.\n")); + qh->DOcheckmax= False; + }else { + if (qh->MERGEexact || (qh->hull_dim > qh_DIMreduceBuild && qh->PREmerge)) + qh_postmerge(qh, "First post-merge", qh->premerge_centrum, qh->premerge_cos, + (qh->POSTmerge ? False : qh->TESTvneighbors)); + else if (!qh->POSTmerge && qh->TESTvneighbors) + qh_postmerge(qh, "For testing vertex neighbors", qh->premerge_centrum, + qh->premerge_cos, True); + if (qh->POSTmerge) + qh_postmerge(qh, "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(qh /*qh.visible_list*/, !qh_ALL, &numoutside); + qh->findbestnew= False; + qh_deletevisible(qh /*qh.visible_list*/); + qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + } + } + if (qh->DOcheckmax){ + if (qh->REPORTfreq) { + qh_buildtracing(qh, NULL, NULL); + qh_fprintf(qh, qh->ferr, 8115, "\nTesting all coplanar points.\n"); + } + qh_check_maxout(qh); + } + if (qh->KEEPnearinside && !qh->maxoutdone) + qh_nearcoplanar(qh); + } + if (qh_setsize(qh, qh->qhmem.tempstack) != 0) { + qh_fprintf(qh, qh->ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n", + qh_setsize(qh, qh->qhmem.tempstack)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->hulltime= qh_CPUclock - qh->hulltime; + qh->QHULLfinished= True; + trace1((qh, qh->ferr, 1036, "Qhull: algorithm completed\n")); +} /* qhull */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="addpoint">-</a> + + qh_addpoint(qh, furthest, facet, checkdist ) + add point (usually furthest point) above facet to hull + if checkdist, + check that point is above facet. + if point is not outside of the hull, uses qh_partitioncoplanar() + assumes that facet is defined by qh_findbestfacet() + else if facet specified, + assumes that point is above facet (major damage if below) + for Delaunay triangulations, + Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates. + + returns: + returns False if user requested an early termination + qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside) + if unknown point, adds a pointer to qh.other_points + do not deallocate the point's coordinates + + notes: + assumes point is near its best facet and not at a local minimum of a lens + distributions. Use qh_findbestfacet to avoid this case. + uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets + + see also: + qh_triangulate() -- triangulate non-simplicial facets + + design: + add point to other_points if needed + if checkdist + if point not above facet + partition coplanar point + exit + exit if pre STOPpoint requested + find horizon and visible facets for point + make new facets for point to horizon + make hyperplanes for point + compute balance statistics + match neighboring new facets + update vertex neighbors and delete interior vertices + exit if STOPcone requested + merge non-convex new facets + if merge found, many merges, or 'Qf' + use qh_findbestnew() instead of qh_findbest() + partition outside points from visible facets + delete visible facets + check polyhedron if requested + exit if post STOPpoint requested + reset working lists of facets and vertices +*/ +boolT qh_addpoint(qhT *qh, 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(qh, furthest) == qh_IDunknown) + qh_setappend(qh, &qh->other_points, furthest); + if (!facet) { + qh_fprintf(qh, qh->ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (checkdist) { + facet= qh_findbest(qh, 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(qh, furthest, facet, &dist); + return True; + } + } + qh_buildtracing(qh, furthest, facet); + if (qh->STOPpoint < 0 && qh->furthest_id == -qh->STOPpoint-1) { + facet->notfurthest= True; + return False; + } + qh_findhorizon(qh, 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(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + return True; + } + zzinc_(Zprocessed); + firstnew= qh->facet_id; + vertex= qh_makenewfacets(qh, furthest /*visible_list, attaches if !ONLYgood */); + qh_makenewplanes(qh /* 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, qh->newfacet_list, goodhorizon) && !qh->GOODclosest) { + FORALLnew_facets + qh_delfacet(qh, newfacet); + qh_delvertex(qh, vertex); + qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + zinc_(Znotgoodnew); + facet->notfurthest= True; + return True; + } + if (qh->ONLYgood) + qh_attachnewfacets(qh /*visible_list*/); + qh_matchnewfacets(qh); + qh_updatevertices(qh); + 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(qh, 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(qh /*qh.visible_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 /*qh.visible_list*/); + zmax_(Zmaxvertex, qh->num_vertices); + qh->NEWfacets= False; + if (qh->IStracing >= 4) { + if (qh->num_facets < 2000) + qh_printlists(qh); + qh_printfacetlist(qh, qh->newfacet_list, NULL, True); + qh_checkpolygon(qh, qh->facet_list); + }else if (qh->CHECKfrequently) { + if (qh->num_facets < 50) + qh_checkpolygon(qh, qh->facet_list); + else + qh_checkpolygon(qh, qh->newfacet_list); + } + if (qh->STOPpoint > 0 && qh->furthest_id == qh->STOPpoint-1) + return False; + qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + /* qh_triangulate(qh); to test qh.TRInormals */ + trace2((qh, qh->ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n", + qh_pointid(qh, furthest), numnew, newbalance, pbalance)); + return True; +} /* addpoint */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="build_withrestart">-</a> + + qh_build_withrestart(qh) + allow restarts due to qh.JOGGLEmax while calling qh_buildhull() + qh_errexit always undoes qh_build_withrestart() + qh.FIRSTpoint/qh.NUMpoints is point array + it may be moved by qh_joggleinput(qh) +*/ +void qh_build_withrestart(qhT *qh) { + int restart; + + qh->ALLOWrestart= True; + while (True) { + restart= setjmp(qh->restartexit); /* simple statement for CRAY J916 */ + if (restart) { /* only from qh_precision() */ + zzinc_(Zretry); + wmax_(Wretrymax, qh->JOGGLEmax); + /* QH7078 warns about using 'TCn' with 'QJn' */ + qh->STOPcone= qh_IDunknown; /* if break from joggle, prevents normal output */ + } + if (!qh->RERUN && qh->JOGGLEmax < REALmax/2) { + if (qh->build_cnt > qh_JOGGLEmaxretry) { + qh_fprintf(qh, qh->ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\ + with joggled input. Increase joggle above 'QJ%2.2g'\n\ + or modify qh_JOGGLE... parameters in user.h\n", + qh->build_cnt, qh->JOGGLEmax); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (qh->build_cnt && !restart) + break; + }else if (qh->build_cnt && qh->build_cnt >= qh->RERUN) + break; + qh->STOPcone= 0; + qh_freebuild(qh, True); /* first call is a nop */ + qh->build_cnt++; + if (!qh->qhull_optionsiz) + qh->qhull_optionsiz= (int)strlen(qh->qhull_options); /* WARN64 */ + else { + qh->qhull_options [qh->qhull_optionsiz]= '\0'; + qh->qhull_optionlen= qh_OPTIONline; /* starts a new line */ + } + qh_option(qh, "_run", &qh->build_cnt, NULL); + if (qh->build_cnt == qh->RERUN) { + qh->IStracing= qh->TRACElastrun; /* duplicated from qh_initqhull_globals */ + if (qh->TRACEpoint != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) { + qh->TRACElevel= (qh->IStracing? qh->IStracing : 3); + qh->IStracing= 0; + } + qh->qhmem.IStracing= qh->IStracing; + } + if (qh->JOGGLEmax < REALmax/2) + qh_joggleinput(qh); + qh_initbuild(qh); + qh_buildhull(qh); + if (qh->JOGGLEmax < REALmax/2 && !qh->MERGING) + qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault); + } + qh->ALLOWrestart= False; +} /* qh_build_withrestart */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="buildhull">-</a> + + qh_buildhull(qh) + 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(qhT *qh) { + facetT *facet; + pointT *furthest; + vertexT *vertex; + int id; + + trace1((qh, qh->ferr, 1037, "qh_buildhull: start build hull\n")); + FORALLfacets { + if (facet->visible || facet->newfacet) { + qh_fprintf(qh, qh->ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n", + facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + } + FORALLvertices { + if (vertex->newlist) { + qh_fprintf(qh, qh->ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n", + vertex->id); + qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + id= qh_pointid(qh, 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, qh->ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id)); + return; + } + } + qh->facet_next= qh->facet_list; /* advance facet when processed */ + while ((furthest= qh_nextfurthest(qh, &facet))) { + qh->num_outside--; /* if ONLYmax, furthest may not be outside */ + if (!qh_addpoint(qh, furthest, facet, qh->ONLYmax)) + break; + } + if (qh->NARROWhull) /* move points from outsideset to coplanarset */ + qh_outcoplanar(qh /* facet_list */ ); + if (qh->num_outside && !furthest) { + qh_fprintf(qh, qh->ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh->num_outside); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + trace1((qh, qh->ferr, 1039, "qh_buildhull: completed the hull construction\n")); +} /* buildhull */ + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="buildtracing">-</a> + + qh_buildtracing(qh, 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(qhT *qh, pointT *furthest, facetT *facet) { + realT dist= 0; + float cpu; + int total, furthestid; + time_t timedata; + struct tm *tp; + vertexT *vertex; + + qh->old_randomdist= qh->RANDOMdist; + qh->RANDOMdist= False; + if (!furthest) { + time(&timedata); + tp= localtime(&timedata); + cpu= (float)qh_CPUclock - (float)qh->hulltime; + cpu /= (float)qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + qh_fprintf(qh, qh->ferr, 8118, "\n\ +At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\ + The current hull contains %d facets and %d vertices. Last point was p%d\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1, + total, qh->num_facets, qh->num_vertices, qh->furthest_id); + return; + } + furthestid= qh_pointid(qh, furthest); + if (qh->TRACEpoint == furthestid) { + qh->IStracing= qh->TRACElevel; + qh->qhmem.IStracing= qh->TRACElevel; + }else if (qh->TRACEpoint != qh_IDunknown && qh->TRACEdist < REALmax/2) { + qh->IStracing= 0; + qh->qhmem.IStracing= 0; + } + if (qh->REPORTfreq && (qh->facet_id-1 > qh->lastreport+qh->REPORTfreq)) { + qh->lastreport= qh->facet_id-1; + time(&timedata); + tp= localtime(&timedata); + cpu= (float)qh_CPUclock - (float)qh->hulltime; + cpu /= (float)qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + zinc_(Zdistio); + qh_distplane(qh, furthest, facet, &dist); + qh_fprintf(qh, qh->ferr, 8119, "\n\ +At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\ + The current hull contains %d facets and %d vertices. There are %d\n\ + outside points. Next is point p%d(v%d), %2.2g above f%d.\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1, + total, qh->num_facets, qh->num_vertices, qh->num_outside+1, + furthestid, qh->vertex_id, dist, getid_(facet)); + }else if (qh->IStracing >=1) { + cpu= (float)qh_CPUclock - (float)qh->hulltime; + cpu /= (float)qh_SECticks; + qh_distplane(qh, furthest, facet, &dist); + qh_fprintf(qh, qh->ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs. Previous was p%d.\n", + furthestid, qh->vertex_id, qh->num_facets, dist, + getid_(facet), qh->num_outside+1, cpu, qh->furthest_id); + } + zmax_(Zvisit2max, (int)qh->visit_id/2); + if (qh->visit_id > (unsigned) INT_MAX) { /* 31 bits */ + zinc_(Zvisit); + qh->visit_id= 0; + FORALLfacets + facet->visitid= 0; + } + zmax_(Zvvisit2max, (int)qh->vertex_visit/2); + if (qh->vertex_visit > (unsigned) INT_MAX) { /* 31 bits */ + zinc_(Zvvisit); + qh->vertex_visit= 0; + FORALLvertices + vertex->visitid= 0; + } + qh->furthest_id= furthestid; + qh->RANDOMdist= qh->old_randomdist; +} /* buildtracing */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="errexit2">-</a> + + qh_errexit2(qh, 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(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet) { + + qh_errprint(qh, "ERRONEOUS", facet, otherfacet, NULL, NULL); + qh_errexit(qh, exitcode, NULL, NULL); +} /* errexit2 */ + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="findhorizon">-</a> + + qh_findhorizon(qh, 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(qhT *qh, pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) { + facetT *neighbor, **neighborp, *visible; + int numhorizon= 0, coplanar= 0; + realT dist; + + trace1((qh, qh->ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(qh, point),facet->id)); + *goodvisible= *goodhorizon= 0; + zinc_(Ztotvisible); + qh_removefacet(qh, facet); /* visible_list at end of qh->facet_list */ + qh_appendfacet(qh, 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(qh, "visible", facet, NULL, NULL, NULL); + qh->visit_id++; + FORALLvisible_facets { + if (visible->tricoplanar && !qh->TRInormals) { + qh_fprintf(qh, qh->ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh, 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(qh, point, neighbor, &dist); + if (dist > qh->MINvisible) { + zinc_(Ztotvisible); + qh_removefacet(qh, neighbor); /* append to end of qh->visible_list */ + qh_appendfacet(qh, neighbor); + neighbor->visible= True; + neighbor->f.replace= NULL; + qh->num_visible++; + if (neighbor->good) + (*goodvisible)++; + if (qh->IStracing >=4) + qh_errprint(qh, "visible", neighbor, NULL, NULL, NULL); + }else { + if (dist > - qh->MAXcoplanar) { + neighbor->coplanar= True; + zzinc_(Zcoplanarhorizon); + qh_precision(qh, "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, qh->ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh->MINvisible(%2.7g)\n", + qh_pointid(qh, point), neighbor->id, dist, qh->MINvisible)); + }else + neighbor->coplanar= False; + zinc_(Ztothorizon); + numhorizon++; + if (neighbor->good) + (*goodhorizon)++; + if (qh->IStracing >=4) + qh_errprint(qh, "horizon", neighbor, NULL, NULL, NULL); + } + } + } + if (!numhorizon) { + qh_precision(qh, "empty horizon"); + qh_fprintf(qh, qh->ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\ +QhullPoint p%d was above all facets.\n", qh_pointid(qh, point)); + qh_printfacetlist(qh, qh->facet_list, NULL, True); + qh_errexit(qh, qh_ERRprec, NULL, NULL); + } + trace1((qh, qh->ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n", + numhorizon, *goodhorizon, qh->num_visible, *goodvisible, coplanar)); + if (qh->IStracing >= 4 && qh->num_facets < 50) + qh_printlists(qh); +} /* findhorizon */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="nextfurthest">-</a> + + qh_nextfurthest(qh, 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(qhT *qh, facetT **visible) { + facetT *facet; + int size, idx; + realT randr, dist; + pointT *furthest; + + while ((facet= qh->facet_next) != qh->facet_tail) { + if (!facet->outsideset) { + qh->facet_next= facet->next; + continue; + } + SETreturnsize_(facet->outsideset, size); + if (!size) { + qh_setfree(qh, &facet->outsideset); + qh->facet_next= facet->next; + continue; + } + if (qh->NARROWhull) { + if (facet->notfurthest) + qh_furthestout(qh, facet); + furthest= (pointT*)qh_setlast(facet->outsideset); +#if qh_COMPUTEfurthest + qh_distplane(qh, 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 /* 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(qh, facet->outsideset); + } + } + randr= qh_RANDOMint; + randr= randr/(qh_RANDOMmax+1); + idx= (int)floor((qh->num_outside - outcoplanar) * randr); + FORALLfacet_(qh->facet_next) { + if (facet->outsideset) { + SETreturnsize_(facet->outsideset, size); + if (!size) + qh_setfree(qh, &facet->outsideset); + else if (size > idx) { + *visible= facet; + return((pointT*)qh_setdelnth(qh, facet->outsideset, idx)); + }else + idx -= size; + } + } + qh_fprintf(qh, qh->ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n", + qh->num_outside, idx+1, randr); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + }else { /* VIRTUALmemory */ + facet= qh->facet_tail->previous; + if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) { + if (facet->outsideset) + qh_setfree(qh, &facet->outsideset); + qh_removefacet(qh, facet); + qh_prependfacet(qh, facet, &qh->facet_list); + continue; + } + *visible= facet; + return furthest; + } + } + return NULL; +} /* nextfurthest */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="partitionall">-</a> + + qh_partitionall(qh, vertices, points, numpoints ) + partitions all points in points/numpoints to the outsidesets of facets + vertices= vertices in qh.facet_list(!partitioned) + + returns: + builds facet->outsideset + does not partition qh.GOODpoint + if qh.ONLYgood && !qh.MERGING, + does not partition qh.GOODvertex + + notes: + faster if qh.facet_list sorted by anticipated size of outside set + + design: + initialize pointset with all points + remove vertices from pointset + remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint) + for all facets + for all remaining points in pointset + compute distance from point to facet + if point is outside facet + remove point from pointset (by not reappending) + update bestpoint + append point or old bestpoint to facet's outside set + append bestpoint to facet's outside set (furthest) + for all points remaining in pointset + partition point into facets' outside sets and coplanar sets +*/ +void qh_partitionall(qhT *qh, 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, qh->ferr, 1042, "qh_partitionall: partition all points into outside sets\n")); + pointset= qh_settemp(qh, numpoints); + qh->num_outside= 0; + pointp= SETaddr_(pointset, pointT); + for (i=numpoints, point= points; i--; point += qh->hull_dim) + *(pointp++)= point; + qh_settruncate(qh, pointset, numpoints); + FOREACHvertex_(vertices) { + if ((id= qh_pointid(qh, vertex->point)) >= 0) + SETelem_(pointset, id)= NULL; + } + id= qh_pointid(qh, 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, 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(qh, size); + bestpoint= NULL; + point_end= 0; + FOREACHpoint_i_(qh, pointset) { + if (point) { + zzinc_(Zpartitionall); + qh_distplane(qh, 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(qh, &facet->outsideset, bestpoint); + bestpoint= point; + bestdist= dist; + }else + qh_setappend(qh, &facet->outsideset, point); + } + } + } + if (bestpoint) { + qh_setappend(qh, &facet->outsideset, bestpoint); +#if !qh_COMPUTEfurthest + facet->furthestdist= bestdist; +#endif + }else + qh_setfree(qh, &facet->outsideset); + qh_settruncate(qh, 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_(qh, pointset) { + if (point) + qh_partitionpoint(qh, point, qh->facet_list); + } + qh->findbestnew= False; + } + zzadd_(Zpartitionall, zzval_(Zpartition)); + zzval_(Zpartition)= 0; + qh_settempfree(qh, &pointset); + if (qh->IStracing >= 4) + qh_printfacetlist(qh, qh->facet_list, NULL, True); +} /* partitionall */ + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="partitioncoplanar">-</a> + + qh_partitioncoplanar(qh, 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(qhT *qh, pointT *point, facetT *facet, realT *dist) { + facetT *bestfacet; + pointT *oldfurthest; + realT bestdist, dist2= 0, angle; + int numpart= 0, oldfindbest; + boolT isoutside; + + qh->WAScoplanar= True; + if (!dist) { + if (qh->findbestnew) + bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh_ALL, &isoutside, &numpart); + else + bestfacet= qh_findbest(qh, 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, qh->ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n", + qh_pointid(qh, point), bestfacet->id, bestdist, qh->findbestnew)); + return; + } + }else if (bestdist < -qh->MAXcoplanar) { + trace4((qh, qh->ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n", + qh_pointid(qh, 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(qh, 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, qh->ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d. It is above flipped facet f%d dist %2.2g\n", + qh_pointid(qh, point), facet->id, bestfacet->id, bestdist)); + oldfindbest= qh->findbestnew; + qh->findbestnew= False; + qh_partitionpoint(qh, point, bestfacet); + qh->findbestnew= oldfindbest; + return; + } + } + qh->max_outside= bestdist; + if (bestdist > qh->TRACEdist) { + qh_fprintf(qh, qh->ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n", + qh_pointid(qh, point), facet->id, bestdist, bestfacet->id, qh->furthest_id); + qh_errprint(qh, "DISTANT", facet, bestfacet, NULL, NULL); + } + } + if (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside) { + oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset); + if (oldfurthest) { + zinc_(Zcomputefurthest); + qh_distplane(qh, oldfurthest, bestfacet, &dist2); + } + if (!oldfurthest || dist2 < bestdist) + qh_setappend(qh, &bestfacet->coplanarset, point); + else + qh_setappend2ndlast(qh, &bestfacet->coplanarset, point); + } + trace4((qh, qh->ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n", + qh_pointid(qh, point), bestfacet->id, bestdist)); +} /* partitioncoplanar */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="partitionpoint">-</a> + + qh_partitionpoint(qh, 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(qhT *qh, pointT *point, facetT *facet) { + realT bestdist; + boolT isoutside; + facetT *bestfacet; + int numpart; +#if qh_COMPUTEfurthest + realT dist; +#endif + + if (qh->findbestnew) + bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh->BESToutside, &isoutside, &numpart); + else + bestfacet= qh_findbest(qh, 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(qh, "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(qh, &(bestfacet->outsideset), point); + if (!bestfacet->newfacet) { + qh_removefacet(qh, bestfacet); /* make sure it's after qh->facet_next */ + qh_appendfacet(qh, bestfacet); + } +#if !qh_COMPUTEfurthest + bestfacet->furthestdist= bestdist; +#endif + }else { +#if qh_COMPUTEfurthest + zinc_(Zcomputefurthest); + qh_distplane(qh, oldfurthest, bestfacet, &dist); + if (dist < bestdist) + qh_setappend(qh, &(bestfacet->outsideset), point); + else + qh_setappend2ndlast(qh, &(bestfacet->outsideset), point); +#else + if (bestfacet->furthestdist < bestdist) { + qh_setappend(qh, &(bestfacet->outsideset), point); + bestfacet->furthestdist= bestdist; + }else + qh_setappend2ndlast(qh, &(bestfacet->outsideset), point); +#endif + } + qh->num_outside++; + trace4((qh, qh->ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n", + qh_pointid(qh, 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(qh, "nearly incident point"); + if ((qh->KEEPcoplanar + qh->KEEPnearinside) || bestdist > qh->max_outside) + qh_partitioncoplanar(qh, point, bestfacet, &bestdist); + else { + trace4((qh, qh->ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n", + qh_pointid(qh, point), bestfacet->id)); + } + }else if (qh->KEEPnearinside && bestdist > -qh->NEARinside) { + zinc_(Zpartnear); + qh_partitioncoplanar(qh, point, bestfacet, &bestdist); + }else { + zinc_(Zpartinside); + trace4((qh, qh->ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n", + qh_pointid(qh, point), bestfacet->id, bestdist)); + if (qh->KEEPinside) + qh_partitioncoplanar(qh, point, bestfacet, &bestdist); + } +} /* partitionpoint */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="partitionvisible">-</a> + + qh_partitionvisible(qh, allpoints, numoutside ) + partitions points in visible facets to qh.newfacet_list + qh.visible_list= visible facets + for visible facets + 1st neighbor (if any) points to a horizon facet or a new facet + if allpoints(!used), + repartitions coplanar points + + returns: + updates outside sets and coplanar sets of qh.newfacet_list + updates qh.num_outside (count of outside points) + + notes: + qh.findbest_notsharp should be clear (extra work if set) + + design: + for all visible facets with outside set or coplanar set + select a newfacet for visible facet + if outside set + partition outside set into new facets + if coplanar set and keeping coplanar/near-inside/inside points + if allpoints + partition coplanar set into new facets, may be assigned outside + else + partition coplanar set into coplanar sets of new facets + for each deleted vertex + if allpoints + partition vertex into new facets, may be assigned outside + else + partition vertex into coplanar sets of new facets +*/ +void qh_partitionvisible(qhT *qh /*qh.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(qh, visible); + } + if (!newfacet) + newfacet= qh->newfacet_list; + if (newfacet == qh->facet_tail) { + qh_fprintf(qh, qh->ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n"); + qh_errexit(qh, qh_ERRprec, NULL, NULL); + } + if (visible->outsideset) { + size= qh_setsize(qh, visible->outsideset); + *numoutside += size; + qh->num_outside -= size; + FOREACHpoint_(visible->outsideset) + qh_partitionpoint(qh, point, newfacet); + } + if (visible->coplanarset && (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside)) { + size= qh_setsize(qh, visible->coplanarset); + coplanar += size; + FOREACHpoint_(visible->coplanarset) { + if (allpoints) /* not used */ + qh_partitionpoint(qh, point, newfacet); + else + qh_partitioncoplanar(qh, point, newfacet, NULL); + } + } + } + FOREACHvertex_(qh->del_vertices) { + if (vertex->point) { + if (allpoints) /* not used */ + qh_partitionpoint(qh, vertex->point, qh->newfacet_list); + else + qh_partitioncoplanar(qh, vertex->point, qh->newfacet_list, NULL); + } + } + trace1((qh, qh->ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar)); +} /* partitionvisible */ + + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="precision">-</a> + + qh_precision(qh, reason ) + restart on precision errors if not merging and if 'QJn' +*/ +void qh_precision(qhT *qh, const char *reason) { + + if (qh->ALLOWrestart && !qh->PREmerge && !qh->MERGEexact) { + if (qh->JOGGLEmax < REALmax/2) { + trace0((qh, qh->ferr, 26, "qh_precision: qhull restart because of %s\n", reason)); + /* May be called repeatedly if qh->ALLOWrestart */ + longjmp(qh->restartexit, qh_ERRprec); + } + } +} /* qh_precision */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="printsummary">-</a> + + qh_printsummary(qh, fp ) + prints summary to fp + + notes: + not in io_r.c so that user_eg.c can prevent io_r.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(qhT *qh, FILE *fp) { + realT ratio, outerplane, innerplane; + float cpu; + int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0; + int goodused; + facetT *facet; + const char *s; + int numdel= zzval_(Zdelvertextot); + int numtricoplanars= 0; + + size= qh->num_points + qh_setsize(qh, qh->other_points); + numvertices= qh->num_vertices - qh_setsize(qh, qh->del_vertices); + id= qh_pointid(qh, qh->GOODpointp); + FORALLfacets { + if (facet->coplanarset) + numcoplanars += qh_setsize(qh, facet->coplanarset); + if (facet->good) { + if (facet->simplicial) { + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else if (qh_setsize(qh, facet->vertices) != qh->hull_dim) + nonsimplicial++; + } + } + if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id) + size--; + if (qh->STOPcone || qh->STOPpoint) + qh_fprintf(qh, fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'."); + if (qh->UPPERdelaunay) + goodused= qh->GOODvertex + qh->GOODpoint + qh->SPLITthresholds; + else if (qh->DELAUNAY) + goodused= qh->GOODvertex + qh->GOODpoint + qh->GOODthreshold; + else + goodused= qh->num_good; + nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + if (qh->VORONOI) { + if (qh->UPPERdelaunay) + qh_fprintf(qh, fp, 9289, "\n\ +Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + else + qh_fprintf(qh, fp, 9290, "\n\ +Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + qh_fprintf(qh, fp, 9291, " Number of Voronoi regions%s: %d\n", + qh->ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + qh_fprintf(qh, fp, 9292, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + qh_fprintf(qh, fp, 9293, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + qh_fprintf(qh, fp, 9294, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + qh_fprintf(qh, fp, 9295, " Number of%s Voronoi vertices: %d\n", + goodused ? " 'good'" : "", qh->num_good); + if (nonsimplicial) + qh_fprintf(qh, fp, 9296, " Number of%s non-simplicial Voronoi vertices: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh->DELAUNAY) { + if (qh->UPPERdelaunay) + qh_fprintf(qh, fp, 9297, "\n\ +Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + else + qh_fprintf(qh, fp, 9298, "\n\ +Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + qh_fprintf(qh, fp, 9299, " Number of input sites%s: %d\n", + qh->ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + qh_fprintf(qh, fp, 9300, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + qh_fprintf(qh, fp, 9301, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + qh_fprintf(qh, fp, 9302, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + qh_fprintf(qh, fp, 9303, " Number of%s Delaunay regions: %d\n", + goodused ? " 'good'" : "", qh->num_good); + if (nonsimplicial) + qh_fprintf(qh, fp, 9304, " Number of%s non-simplicial Delaunay regions: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh->HALFspace) { + qh_fprintf(qh, fp, 9305, "\n\ +Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + qh_fprintf(qh, fp, 9306, " Number of halfspaces: %d\n", size); + qh_fprintf(qh, fp, 9307, " Number of non-redundant halfspaces: %d\n", numvertices); + if (numcoplanars) { + if (qh->KEEPinside && qh->KEEPcoplanar) + s= "similar and redundant"; + else if (qh->KEEPinside) + s= "redundant"; + else + s= "similar"; + qh_fprintf(qh, fp, 9308, " Number of %s halfspaces: %d\n", s, numcoplanars); + } + qh_fprintf(qh, fp, 9309, " Number of intersection points: %d\n", qh->num_facets - qh->num_visible); + if (goodused) + qh_fprintf(qh, fp, 9310, " Number of 'good' intersection points: %d\n", qh->num_good); + if (nonsimplicial) + qh_fprintf(qh, fp, 9311, " Number of%s non-simplicial intersection points: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else { + qh_fprintf(qh, fp, 9312, "\n\ +Convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + qh_fprintf(qh, fp, 9313, " Number of vertices: %d\n", numvertices); + if (numcoplanars) { + if (qh->KEEPinside && qh->KEEPcoplanar) + s= "coplanar and interior"; + else if (qh->KEEPinside) + s= "interior"; + else + s= "coplanar"; + qh_fprintf(qh, fp, 9314, " Number of %s points: %d\n", s, numcoplanars); + } + qh_fprintf(qh, fp, 9315, " Number of facets: %d\n", qh->num_facets - qh->num_visible); + if (goodused) + qh_fprintf(qh, fp, 9316, " Number of 'good' facets: %d\n", qh->num_good); + if (nonsimplicial) + qh_fprintf(qh, fp, 9317, " Number of%s non-simplicial facets: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + } + if (numtricoplanars) + qh_fprintf(qh, fp, 9318, " Number of triangulated facets: %d\n", numtricoplanars); + qh_fprintf(qh, fp, 9319, "\nStatistics for: %s | %s", + qh->rbox_command, qh->qhull_command); + if (qh->ROTATErandom != INT_MIN) + qh_fprintf(qh, fp, 9320, " QR%d\n\n", qh->ROTATErandom); + else + qh_fprintf(qh, fp, 9321, "\n\n"); + qh_fprintf(qh, fp, 9322, " Number of points processed: %d\n", zzval_(Zprocessed)); + qh_fprintf(qh, fp, 9323, " Number of hyperplanes created: %d\n", zzval_(Zsetplane)); + if (qh->DELAUNAY) + qh_fprintf(qh, fp, 9324, " Number of facets in hull: %d\n", qh->num_facets - qh->num_visible); + qh_fprintf(qh, fp, 9325, " Number of distance tests for qhull: %d\n", zzval_(Zpartition)+ + zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar)); +#if 0 /* NOTE: must print before printstatistics() */ + {realT stddev, ave; + qh_fprintf(qh, fp, 9326, " average new facet balance: %2.2g\n", + wval_(Wnewbalance)/zval_(Zprocessed)); + stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance), + wval_(Wnewbalance2), &ave); + qh_fprintf(qh, fp, 9327, " new facet standard deviation: %2.2g\n", stddev); + qh_fprintf(qh, fp, 9328, " average partition balance: %2.2g\n", + wval_(Wpbalance)/zval_(Zpbalance)); + stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + qh_fprintf(qh, fp, 9329, " partition standard deviation: %2.2g\n", stddev); + } +#endif + if (nummerged) { + qh_fprintf(qh, fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+ + zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+ + zzval_(Zdistzero)); + qh_fprintf(qh, fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart)); + qh_fprintf(qh, fp, 9332," Number of merged facets: %d\n", nummerged); + } + if (!qh->RANDOMoutside && qh->QHULLfinished) { + cpu= (float)qh->hulltime; + cpu /= (float)qh_SECticks; + wval_(Wcpu)= cpu; + qh_fprintf(qh, fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu); + } + if (qh->RERUN) { + if (!qh->PREmerge && !qh->MERGEexact) + qh_fprintf(qh, fp, 9334, " Percentage of runs with precision errors: %4.1f\n", + zzval_(Zretry)*100.0/qh->build_cnt); /* careful of order */ + }else if (qh->JOGGLEmax < REALmax/2) { + if (zzval_(Zretry)) + qh_fprintf(qh, fp, 9335, " After %d retries, input joggled by: %2.2g\n", + zzval_(Zretry), qh->JOGGLEmax); + else + qh_fprintf(qh, fp, 9336, " Input joggled by: %2.2g\n", qh->JOGGLEmax); + } + if (qh->totarea != 0.0) + qh_fprintf(qh, fp, 9337, " %s facet area: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totarea); + if (qh->totvol != 0.0) + qh_fprintf(qh, fp, 9338, " %s volume: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totvol); + if (qh->MERGING) { + qh_outerinner(qh, NULL, &outerplane, &innerplane); + if (outerplane > 2 * qh->DISTround) { + qh_fprintf(qh, fp, 9339, " Maximum distance of %spoint above facet: %2.2g", + (qh->QHULLfinished ? "" : "merged "), outerplane); + ratio= outerplane/(qh->ONEmerge + qh->DISTround); + /* don't report ratio if MINoutside is large */ + if (ratio > 0.05 && 2* qh->ONEmerge > qh->MINoutside && qh->JOGGLEmax > REALmax/2) + qh_fprintf(qh, fp, 9340, " (%.1fx)\n", ratio); + else + qh_fprintf(qh, fp, 9341, "\n"); + } + if (innerplane < -2 * qh->DISTround) { + qh_fprintf(qh, fp, 9342, " Maximum distance of %svertex below facet: %2.2g", + (qh->QHULLfinished ? "" : "merged "), innerplane); + ratio= -innerplane/(qh->ONEmerge+qh->DISTround); + if (ratio > 0.05 && qh->JOGGLEmax > REALmax/2) + qh_fprintf(qh, fp, 9343, " (%.1fx)\n", ratio); + else + qh_fprintf(qh, fp, 9344, "\n"); + } + } + qh_fprintf(qh, fp, 9345, "\n"); +} /* printsummary */ + +
+ C/libqhull_r.h view
@@ -0,0 +1,1134 @@+/*<html><pre> -<a href="qh-qhull_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + libqhull_r.h + user-level header file for using qhull.a library + + see qh-qhull_r.htm, qhull_ra.h + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.h#8 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ + + includes function prototypes for libqhull_r.c, geom_r.c, global_r.c, io_r.c, user.c + + use mem_r.h for mem_r.c + use qset_r.h for qset_r.c + + see unix_r.c for an example of using libqhull_r.h + + recompile qhull if you change this file +*/ + +#ifndef qhDEFlibqhull +#define qhDEFlibqhull 1 + +/*=========================== -included files ==============*/ + +/* user_r.h first for QHULL_CRTDBG */ +#include "user_r.h" /* user definable constants (e.g., realT). */ + +#include "mem_r.h" /* Needed for qhT in libqhull_r.h */ +#include "qset_r.h" /* Needed for QHULL_LIB_CHECK */ +/* include stat_r.h after defining boolT. statT needed for qhT in libqhull_r.h */ + +#include <setjmp.h> +#include <float.h> +#include <time.h> +#include <stdio.h> + +#ifndef __STDC__ +#ifndef __cplusplus +#if !_MSC_VER +#error Neither __STDC__ nor __cplusplus is defined. Please use strict ANSI C or C++ to compile +#error Qhull. You may need to turn off compiler extensions in your project configuration. If +#error your compiler is a standard C compiler, you can delete this warning from libqhull_r.h +#endif +#endif +#endif + +/*============ constants and basic types ====================*/ + +extern const char qh_version[]; /* defined in global_r.c */ +extern const char qh_version2[]; /* defined in global_r.c */ + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="coordT">-</a> + + coordT + coordinates and coefficients are stored as realT (i.e., double) + + notes: + Qhull works well if realT is 'float'. If so joggle (QJ) is not effective. + + Could use 'float' for data and 'double' for calculations (realT vs. coordT) + This requires many type casts, and adjusted error bounds. + Also C compilers may do expressions in double anyway. +*/ +#define coordT realT + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="pointT">-</a> + + pointT + a point is an array of coordinates, usually qh.hull_dim + qh_pointid returns + qh_IDnone if point==0 or qh is undefined + qh_IDinterior for qh.interior_point + qh_IDunknown if point is neither in qh.first_point... nor qh.other_points + + notes: + qh.STOPcone and qh.STOPpoint assume that qh_IDunknown==-1 (other negative numbers indicate points) + qh_IDunknown is also returned by getid_() for unknown facet, ridge, or vertex +*/ +#define pointT coordT +typedef enum +{ + qh_IDnone = -3, qh_IDinterior = -2, qh_IDunknown = -1 +} +qh_pointT; + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="flagT">-</a> + + flagT + Boolean flag as a bit +*/ +#define flagT unsigned int + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="boolT">-</a> + + boolT + boolean value, either True or False + + notes: + needed for portability + Use qh_False/qh_True as synonyms +*/ +#define boolT unsigned int +#ifdef False +#undef False +#endif +#ifdef True +#undef True +#endif +#define False 0 +#define True 1 +#define qh_False 0 +#define qh_True 1 + +#include "stat_r.h" /* needs boolT */ + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="CENTERtype">-</a> + + qh_CENTER + to distinguish facet->center +*/ +typedef enum +{ + qh_ASnone = 0, /* If not MERGING and not VORONOI */ + qh_ASvoronoi, /* Set by qh_clearcenters on qh_prepare_output, or if not MERGING and VORONOI */ + qh_AScentrum /* If MERGING (assumed during merging) */ +} +qh_CENTER; + +/*-<a href="qh-qhull_r.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 qhT 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_r.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_r.htm#TOC" + >--------------------------------</a><a name="qh_ERR">-</a> + + qh_ERR + Qhull exit codes, for indicating errors + See: MSG_ERROR and MSG_WARNING [user.h] +*/ +#define qh_ERRnone 0 /* no error occurred during qhull */ +#define qh_ERRinput 1 /* input inconsistency */ +#define qh_ERRsingular 2 /* singular input data */ +#define qh_ERRprec 3 /* precision error */ +#define qh_ERRmem 4 /* insufficient memory, matches mem_r.h */ +#define qh_ERRqhull 5 /* internal error detected, matches mem_r.h */ + +/*-<a href="qh-qhull_r.htm#TOC" +>--------------------------------</a><a name="qh_FILEstderr">-</a> + +qh_FILEstderr +Fake stderr to distinguish error output from normal output +For C++ interface. Must redefine qh_fprintf_qhull +*/ +#define qh_FILEstderr ((FILE*)1) + +/* ============ -structures- ==================== + each of the following structures is defined by a typedef + all realT and coordT fields occur at the beginning of a structure + (otherwise space may be wasted due to alignment) + define all flags together and pack into 32-bit number + + DEFqhT and DEFsetT are likewise defined in + mem_r.h, qset_r.h, and stat_r.h. + +*/ + +typedef struct vertexT vertexT; +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; + +#ifndef DEFqhT +#define DEFqhT 1 +typedef struct qhT qhT; /* defined below */ +#endif + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* defined in qset_r.h */ +#endif + +/*-<a href="qh-poly_r.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 or Voronoi center for output + 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_r.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; /* set according to qh.CENTERtype */ + /* qh_ASnone: no center (not MERGING) */ + /* qh_AScentrum: centrum for testing convexity (qh_getcentrum) */ + /* assumed qh_AScentrum while merging */ + /* qh_ASvoronoi: Voronoi center (qh_facetcenter) */ + /* after constructing the hull, it may be changed (qh_clearcenter) */ + /* if tricoplanar and !keepcentrum, shared with a neighbor */ + facetT *previous; /* previous facet in the facet_list */ + facetT *next; /* next facet in the facet_list */ + setT *vertices; /* vertices for this facet, inverse sorted by ID + if simplicial, 1st vertex was apex/furthest */ + setT *ridges; /* explicit ridges for nonsimplicial facets. + for simplicial facets, neighbors define the ridges */ + setT *neighbors; /* neighbors of the facet. If simplicial, the kth + neighbor is opposite the kth vertex, and the first + neighbor is the horizon facet for the first vertex*/ + setT *outsideset; /* set of points outside this facet + if non-empty, last point is furthest + if NARROWhull, includes coplanars for partitioning*/ + setT *coplanarset; /* set of points coplanar with this facet + > qh.min_vertex and <= facet->max_outside + a point is assigned to the furthest facet + if non-empty, last point is furthest away */ + unsigned visitid; /* visit_id, for visiting all neighbors, + all uses are independent */ + unsigned id; /* unique identifier from qh.facet_id */ + unsigned nummerge:9; /* number of merges */ +#define qh_MAXnummerge 511 /* 2^9-1, 32 flags total, see "flags:" in io_r.c */ + flagT tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */ + /* all tricoplanars share the same apex */ + /* all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */ + /* ->keepcentrum is true for the owner. It has the ->coplanareset */ + /* if ->degenerate, does not span facet (one logical ridge) */ + /* 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 (qhT *qh, 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_r.htm#TOC" + >--------------------------------</a><a name="ridgeT">-</a> + + ridgeT + defines a ridge + + notes: + a ridge is hull_dim-1 simplex between two neighboring facets. If the + facets are non-simplicial, there may be more than one ridge between + two facets. E.G. a 4-d hypercube has two triangles between each pair + of neighboring facets. + + topological information: + vertices a set of vertices + top,bottom neighboring facets with orientation + + geometric information: + tested True if ridge is clearly convex + nonconvex True if ridge is non-convex +*/ +struct ridgeT { + setT *vertices; /* vertices belonging to this ridge, inverse sorted by ID + NULL if a degen ridge (matchsame) */ + facetT *top; /* top facet this ridge is part of */ + facetT *bottom; /* bottom facet this ridge is part of */ + unsigned id; /* unique identifier. Same size as vertex_id and ridge_id */ + flagT seen:1; /* used to perform operations only once */ + flagT tested:1; /* True when ridge is tested for convexity */ + flagT nonconvex:1; /* True if getmergeset detected a non-convex neighbor + only one ridge between neighbors may have nonconvex */ +}; + +/*-<a href="qh-poly_r.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_r.c or after first merge */ + unsigned id; /* unique identifier. Same size as qh.vertex_id and qh.ridge_id */ + unsigned visitid; /* for use with qh.vertex_visit, size must match */ + 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_r.htm#TOC" + >--------------------------------</a><a name="qh">-</a> + + qhT + All global variables for qhull are in qhT. It includes qhmemT, qhstatT, and rbox globals + + This version of Qhull is reentrant, but it is not thread-safe. + + Do not run separate threads on the same instance of qhT. + + QHULL_LIB_CHECK checks that a program and the corresponding + qhull library were built with the same type of header files. +*/ + +#define QHULL_NON_REENTRANT 0 +#define QHULL_QH_POINTER 1 +#define QHULL_REENTRANT 2 + +#define QHULL_LIB_TYPE QHULL_REENTRANT + +#define QHULL_LIB_CHECK qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), sizeof(setT), sizeof(qhmemT)); +#define QHULL_LIB_CHECK_RBOX qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), 0, 0); + +struct qhT { + +/*-<a href="qh-globa_r.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_r.htm#options defines the flags. +*/ + boolT ALLpoints; /* true 'Qs' if search all points for initial simplex */ + boolT ANGLEmerge; /* true 'Qa' if sort potential merges by angle */ + boolT APPROXhull; /* true 'Wn' if MINoutside set */ + realT MINoutside; /* 'Wn' min. distance for an outside point */ + boolT ANNOTATEoutput; /* true 'Ta' if annotate output with message codes */ + boolT ATinfinity; /* true 'Qz' if point num_points-1 is "at-infinity" + for improving precision in Delaunay triangulations */ + boolT AVOIDold; /* true 'Q4' if avoid old->new merges */ + boolT BESToutside; /* true 'Qf' if partition points into best outsideset */ + boolT CDDinput; /* true 'Pc' if input uses CDD format (1.0/offset first) */ + boolT CDDoutput; /* true 'PC' if print normals in CDD format (offset first) */ + boolT CHECKfrequently; /* true 'Tc' if checking frequently */ + realT premerge_cos; /* 'A-n' cos_max when pre merging */ + realT postmerge_cos; /* 'An' cos_max when post merging */ + boolT DELAUNAY; /* true 'd' if computing DELAUNAY triangulation */ + boolT DOintersections; /* true 'Gh' if print hyperplane intersections */ + int DROPdim; /* drops dim 'GDn' for 4-d -> 3-d output */ + boolT FORCEoutput; /* true 'Po' if forcing output despite degeneracies */ + int GOODpoint; /* 1+n for 'QGn', good facet if visible/not(-) from point n*/ + pointT *GOODpointp; /* the actual point */ + boolT GOODthreshold; /* true if qh.lower_threshold/upper_threshold defined + false if qh.SPLITthreshold */ + int GOODvertex; /* 1+n, good facet if vertex for point n */ + pointT *GOODvertexp; /* the actual point */ + boolT HALFspace; /* true 'Hn,n,n' if halfspace intersection */ + boolT ISqhullQh; /* Set by Qhull.cpp on initialization */ + 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 NOwide; /* true 'Q12' if no error on wide merge due to duplicate ridge */ + boolT ONLYgood; /* true 'Qg' if process points with good visible or horizon facets */ + boolT ONLYmax; /* true 'Qm' if only process points that increase max_outside */ + boolT PICKfurthest; /* true 'Q9' if process furthest of furthest points*/ + boolT POSTmerge; /* true if merging after buildhull (Cn or An) */ + boolT PREmerge; /* true if merging during buildhull (C-n or A-n) */ + /* NOTE: some of these names are similar to qh_PRINT names */ + boolT PRINTcentrums; /* true 'Gc' if printing centrums */ + boolT PRINTcoplanar; /* true 'Gp' if printing coplanar points */ + int PRINTdim; /* print dimension for Geomview output */ + boolT PRINTdots; /* true 'Ga' if printing all points as dots */ + boolT PRINTgood; /* true 'Pg' if printing good facets */ + boolT PRINTinner; /* true 'Gi' if printing inner planes */ + boolT PRINTneighbors; /* true 'PG' if printing neighbors of good facets */ + boolT PRINTnoplanes; /* true 'Gn' if printing no planes */ + boolT PRINToptions1st; /* true 'FO' if printing options to stderr */ + boolT PRINTouter; /* true 'Go' if printing outer planes */ + boolT PRINTprecision; /* false 'Pp' if not reporting precision problems */ + qh_PRINT PRINTout[qh_PRINTEND]; /* list of output formats to print */ + boolT PRINTridges; /* true 'Gr' if print ridges */ + boolT PRINTspheres; /* true 'Gv' if print vertices as spheres */ + boolT PRINTstatistics; /* true 'Ts' if printing statistics to stderr */ + boolT PRINTsummary; /* true 's' if printing summary to stderr */ + boolT PRINTtransparent; /* true 'Gt' if print transparent outer ridges */ + boolT PROJECTdelaunay; /* true if DELAUNAY, no readpoints() and + need projectinput() for Delaunay in qh_init_B */ + int PROJECTinput; /* number of projected dimensions 'bn:0Bn:0' */ + boolT QUICKhelp; /* true if quick help message for degen input */ + boolT RANDOMdist; /* true if randomly change distplane and setfacetplane */ + realT RANDOMfactor; /* maximum random perturbation */ + realT RANDOMa; /* qh_randomfactor is randr * RANDOMa + RANDOMb */ + realT RANDOMb; + boolT RANDOMoutside; /* true if select a random outside point */ + int REPORTfreq; /* buildtracing reports every n facets */ + int REPORTfreq2; /* tracemerging reports every REPORTfreq/2 facets */ + int RERUN; /* 'TRn' rerun qhull n times (qh.build_cnt) */ + int ROTATErandom; /* 'QRn' seed, 0 time, >= rotate input */ + boolT SCALEinput; /* true 'Qbk' if scaling input */ + boolT SCALElast; /* true 'Qbb' if scale last coord to max prev coord */ + boolT SETroundoff; /* true 'E' if qh.DISTround is predefined */ + boolT SKIPcheckmax; /* true 'Q5' if skip qh_check_maxout */ + boolT SKIPconvex; /* true 'Q6' if skip convexity testing during pre-merge */ + boolT SPLITthresholds; /* true if upper_/lower_threshold defines a region + used only for printing (!for qh.ONLYgood) */ + int STOPcone; /* 'TCn' 1+n for stopping after cone for point n */ + /* also used by qh_build_withresart for err exit*/ + int STOPpoint; /* 'TVn' 'TV-n' 1+n for stopping after/before(-) + adding point n */ + int TESTpoints; /* 'QTn' num of test points after qh.num_points. Test points always coplanar. */ + boolT TESTvneighbors; /* true 'Qv' if test vertex neighbors at end */ + int TRACElevel; /* 'Tn' conditional IStracing level */ + int TRACElastrun; /* qh.TRACElevel applies to last qh.RERUN */ + int TRACEpoint; /* 'TPn' start tracing when point n is a vertex */ + realT TRACEdist; /* 'TWn' start tracing when merge distance too big */ + int TRACEmerge; /* 'TMn' start tracing before this merge */ + boolT TRIangulate; /* true 'Qt' if triangulate non-simplicial facets */ + boolT TRInormals; /* true 'Q11' if triangulate duplicates ->normal and ->center (sets Qt) */ + boolT UPPERdelaunay; /* true 'Qu' if computing furthest-site Delaunay */ + boolT USEstdout; /* true 'Tz' if using stdout instead of stderr */ + boolT VERIFYoutput; /* true 'Tv' if verify output at end of qhull */ + boolT VIRTUALmemory; /* true 'Q7' if depth-first processing in buildhull */ + boolT VORONOI; /* true 'v' if computing Voronoi diagram */ + + /*--------input constants ---------*/ + realT AREAfactor; /* 1/(hull_dim-1)! for converting det's to area */ + boolT DOcheckmax; /* true if calling qh_check_maxout (qhT *qh, 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_r.c */ + boolT KEEPnearinside; /* true if near-inside points in coplanarset */ + int hull_dim; /* dimension of hull, set by initbuffers */ + int input_dim; /* dimension of input, set by initbuffers */ + int num_points; /* number of input points */ + pointT *first_point; /* array of input points, see POINTSmalloc */ + boolT POINTSmalloc; /* true if qh.first_point/num_points allocated */ + pointT *input_points; /* copy of original qh.first_point for input points for qh_joggleinput */ + boolT input_malloc; /* true if qh.input_points malloc'd */ + char qhull_command[256];/* command line that invoked this program */ + int qhull_commandsiz2; /* size of qhull_command at qh_clear_outputflags */ + char rbox_command[256]; /* command line that produced the input points */ + char qhull_options[512];/* descriptive list of options */ + int qhull_optionlen; /* length of last line */ + int qhull_optionsiz; /* size of qhull_options at qh_build_withrestart */ + int qhull_optionsiz2; /* size of qhull_options at qh_clear_outputflags */ + int run_id; /* non-zero, random identifier for this instance of qhull */ + boolT VERTEXneighbors; /* true if maintaining vertex neighbors */ + boolT ZEROcentrum; /* true if 'C-0' or 'C-0 Qx'. sets ZEROall_ok */ + realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k] + must set either GOODthreshold or SPLITthreshold + if Delaunay, default is 0.0 for upper envelope */ + realT *lower_threshold; /* don't print if facet->normal[k] <=lower_threshold[k] */ + realT *upper_bound; /* scale point[k] to new upper bound */ + realT *lower_bound; /* scale point[k] to new lower bound + project if both upper_ and lower_bound == 0 */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-prec">-</a> + + qh precision constants + precision constants for Qhull + + notes: + qh_detroundoff(qh) 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 qh_distround() */ + 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_r.htm#TOC" + >--------------------------------</a><a name="qh-codetern">-</a> + + qh internal constants + internal constants for Qhull +*/ + char qhull[sizeof("qhull")]; /* "qhull" for checking ownership while debugging */ + jmp_buf errexit; /* exit label for qh_errexit, defined by setjmp() and NOerrexit */ + 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() and ALLOWrestart */ + char jmpXtra2[40]; /* extra bytes in case jmp_buf is defined wrong by compiler*/ + FILE *fin; /* pointer to input file, init by qh_initqhull_start */ + 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_r.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 preceding 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 visible 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_r.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 */ + boolT hasAreaVolume; /* true if totarea, totvol was defined by qh_getarea */ + boolT hasTriangulation; /* true if triangulation created by qh_triangulate */ + realT JOGGLEmax; /* set 'QJn' if randomly joggle input */ + boolT maxoutdone; /* set qh_check_maxout(), cleared by qh_addpoint() */ + realT max_outside; /* maximum distance from a point to a facet, + before roundoff, not simplicial vertices + actual outer plane is +DISTround and + computed outer plane is +2*DISTround */ + realT max_vertex; /* maximum distance (>0) from vertex to a facet, + before roundoff, due to a merge */ + realT min_vertex; /* minimum distance (<0) from vertex to a facet, + before roundoff, due to a merge + if qh.JOGGLEmax, qh_makenewplanes sets it + recomputed if qh.DOcheckmax, default -qh.DISTround */ + boolT NEWfacets; /* true while visible facets invalid due to new or merge + from makecone/attachnewfacets to deletevisible */ + boolT findbestnew; /* true if partitioning calls qh_findbestnew */ + boolT findbest_notsharp; /* true if new facets are at least 90 degrees */ + boolT NOerrexit; /* true if qh.errexit is not available, cleared after setjmp */ + realT PRINTcradius; /* radius for printing centrums */ + realT PRINTradius; /* radius for printing vertex spheres and points */ + boolT POSTmerging; /* true when post merging */ + int printoutvar; /* temporary variable for qh_printbegin, etc. */ + int printoutnum; /* number of facets printed */ + boolT QHULLfinished; /* True after qhull() is finished */ + realT totarea; /* 'FA': total facet area computed by qh_getarea, hasAreaVolume */ + realT totvol; /* 'FA': total volume computed by qh_getarea, hasAreaVolume */ + unsigned int visit_id; /* unique ID for searching neighborhoods, */ + unsigned int vertex_visit; /* unique ID for searching vertices, reset with qh_buildtracing */ + boolT ZEROall_ok; /* True if qh_checkzero always succeeds */ + boolT WAScoplanar; /* True if qh_partitioncoplanar (qhT *qh, qh_check_maxout) */ + +/*-<a href="qh-globa_r.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 */ + setT *del_vertices; /* vertices to partition and delete with visible + facets. Have deleted set for checkfacet */ + +/*-<a href="qh-globa_r.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_r.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_r.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 qh_errexit (qhT *qh, prevents duplicate calls */ + boolT firstcentrum; /* for qh_printcentrum */ + boolT old_randomdist; /* save RANDOMdist flag during io, tracing, or statistics */ + setT *coplanarfacetset; /* set of coplanar facets for searching qh_findbesthorizon() */ + realT last_low; /* qh_scalelast parameters for qh_setdelaunay */ + realT last_high; + realT last_newhigh; + unsigned lastreport; /* for qh_buildtracing */ + int mergereport; /* for qh_tracemerging */ + setT *old_tempstack; /* for saving qh->qhmem.tempstack in save_qhull */ + int ridgeoutnum; /* number of ridges for 4OFF output (qh_printbegin,etc) */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-const">-</a> + + qh memory management, rbox globals, and statistics + + Replaces global data structures defined for libqhull +*/ + int last_random; /* Last random number from qh_rand (random_r.c) */ + jmp_buf rbox_errexit; /* errexit from rboxlib_r.c, defined by qh_rboxpoints() only */ + char jmpXtra3[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */ + int rbox_isinteger; + double rbox_out_offset; + void * cpp_object; /* C++ pointer. Currently used by RboxPoints.qh_fprintf_rbox */ + + /* Last, otherwise zero'd by qh_initqhull_start2 (global_r.c */ + qhmemT qhmem; /* Qhull managed memory (mem_r.h) */ + /* After qhmem because its size depends on the number of statistics */ + qhstatT qhstat; /* Qhull statistics (stat_r.h) */ +}; + +/*=========== -macros- =========================*/ + +/*-<a href="qh-poly_r.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_r.htm#TOC" + >--------------------------------</a><a name="getid_">-</a> + + getid_(p) + return int ID for facet, ridge, or vertex + return qh_IDunknown(-1) if NULL +*/ +#define getid_(p) ((p) ? (int)((p)->id) : qh_IDunknown) + +/*============== FORALL macros ===================*/ + +/*-<a href="qh-poly_r.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 + assumes qh defined + + see: + FORALLfacet_( facetlist ) +*/ +#define FORALLfacets for (facet=qh->facet_list;facet && facet->next;facet=facet->next) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLpoints">-</a> + + FORALLpoints { ... } + assign 'point' to each point in qh.first_point, qh.num_points + + notes: + assumes qh defined + + declare: + coordT *point, *pointtemp; +*/ +#define FORALLpoints FORALLpoint_(qh, qh->first_point, qh->num_points) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLpoint_">-</a> + + FORALLpoint_( qh, points, num) { ... } + assign 'point' to each point in points array of num points + + declare: + coordT *point, *pointtemp; +*/ +#define FORALLpoint_(qh, points, num) for (point= (points), \ + pointtemp= (points)+qh->hull_dim*(num); point < pointtemp; point += qh->hull_dim) + +/*-<a href="qh-poly_r.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 + assumes qh defined +*/ +#define FORALLvertices for (vertex=qh->vertex_list;vertex && vertex->next;vertex= vertex->next) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHfacet_">-</a> + + FOREACHfacet_( facets ) { ... } + assign 'facet' to each facet in facets + + declare: + facetT *facet, **facetp; + + see: + <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet) + +/*-<a href="qh-poly_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHneighbor_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighbor) + +/*-<a href="qh-poly_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHpoint_(points) FOREACHsetelement_(pointT, points, point) + +/*-<a href="qh-poly_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHridge_(ridges) FOREACHsetelement_(ridgeT, ridges, ridge) + +/*-<a href="qh-poly_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHfacet_i_">-</a> + + FOREACHfacet_i_( qh, facets ) { ... } + assign 'facet' and 'facet_i' for each facet in facets set + + declare: + facetT *facet; + int facet_n, facet_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHfacet_i_(qh, facets) FOREACHsetelement_i_(qh, facetT, facets, facet) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHneighbor_i_">-</a> + + FOREACHneighbor_i_( qh, facet ) { ... } + assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors + + FOREACHneighbor_i_( qh, vertex ) { ... } + assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors + + declare: + facetT *neighbor; + int neighbor_n, neighbor_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHneighbor_i_(qh, facet) FOREACHsetelement_i_(qh, facetT, facet->neighbors, neighbor) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHpoint_i_">-</a> + + FOREACHpoint_i_( qh, points ) { ... } + assign 'point' and 'point_i' for each point in points set + + declare: + pointT *point; + int point_n, point_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHpoint_i_(qh, points) FOREACHsetelement_i_(qh, pointT, points, point) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHridge_i_">-</a> + + FOREACHridge_i_( qh, ridges ) { ... } + assign 'ridge' and 'ridge_i' for each ridge in ridges set + + declare: + ridgeT *ridge; + int ridge_n, ridge_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHridge_i_(qh, ridges) FOREACHsetelement_i_(qh, ridgeT, ridges, ridge) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHvertex_i_">-</a> + + FOREACHvertex_i_( qh, vertices ) { ... } + assign 'vertex' and 'vertex_i' for each vertex in vertices set + + declare: + vertexT *vertex; + int vertex_n, vertex_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHvertex_i_(qh, vertices) FOREACHsetelement_i_(qh, vertexT, vertices,vertex) + +#ifdef __cplusplus +extern "C" { +#endif + +/********* -libqhull_r.c prototypes (duplicated from qhull_ra.h) **********************/ + +void qh_qhull(qhT *qh); +boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist); +void qh_printsummary(qhT *qh, FILE *fp); + +/********* -user.c prototypes (alphabetical) **********************/ + +void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge); +void qh_errprint(qhT *qh, const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex); +int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile); +void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall); +void qh_printhelp_degenerate(qhT *qh, FILE *fp); +void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle); +void qh_printhelp_singular(qhT *qh, FILE *fp); +void qh_user_memsizes(qhT *qh); + +/********* -usermem_r.c prototypes (alphabetical) **********************/ +void qh_exit(int exitcode); +void qh_fprintf_stderr(int msgcode, const char *fmt, ... ); +void qh_free(void *mem); +void *qh_malloc(size_t size); + +/********* -userprintf_r.c and userprintf_rbox_r.c prototypes **********************/ +void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ); +void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ); + +/***** -geom_r.c/geom2_r.c/random_r.c prototypes (duplicated from geom_r.h, random_r.h) ****************/ + +facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet, + boolT bestoutside, boolT newfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart); +facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet, + realT *dist, boolT bestoutside, boolT *isoutside, int *numpart); +boolT qh_gram_schmidt(qhT *qh, int dim, realT **rows); +void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane); +void qh_printsummary(qhT *qh, FILE *fp); +void qh_projectinput(qhT *qh); +void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row); +void qh_rotateinput(qhT *qh, realT **rows); +void qh_scaleinput(qhT *qh); +void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points); +coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible); + +/***** -global_r.c prototypes (alphabetical) ***********************/ + +unsigned long qh_clock(qhT *qh); +void qh_checkflags(qhT *qh, char *command, char *hiddenflags); +void qh_clear_outputflags(qhT *qh); +void qh_freebuffers(qhT *qh); +void qh_freeqhull(qhT *qh, boolT allmem); +void qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]); +void qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_init_qhull_command(qhT *qh, int argc, char *argv[]); +void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_initflags(qhT *qh, char *command); +void qh_initqhull_buffers(qhT *qh); +void qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_initqhull_mem(qhT *qh); +void qh_initqhull_outputflags(qhT *qh); +void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile); +void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile); +void qh_initthresholds(qhT *qh, char *command); +void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize); +void qh_option(qhT *qh, const char *option, int *i, realT *r); +void qh_zero(qhT *qh, FILE *errfile); + +/***** -io_r.c prototypes (duplicated from io_r.h) ***********************/ + +void qh_dfacet(qhT *qh, unsigned id); +void qh_dvertex(qhT *qh, unsigned id); +void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); +void qh_produce_output(qhT *qh); +coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc); + + +/********* -mem_r.c prototypes (duplicated from mem_r.h) **********************/ + +void qh_meminit(qhT *qh, FILE *ferr); +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong); + +/********* -poly_r.c/poly2_r.c prototypes (duplicated from poly_r.h) **********************/ + +void qh_check_output(qhT *qh); +void qh_check_points(qhT *qh); +setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets); +facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside); +vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp); +pointT *qh_point(qhT *qh, int id); +setT *qh_pointfacet(qhT *qh /*qh.facet_list*/); +int qh_pointid(qhT *qh, pointT *point); +setT *qh_pointvertex(qhT *qh /*qh.facet_list*/); +void qh_setvoronoi_all(qhT *qh); +void qh_triangulate(qhT *qh /*qh.facet_list*/); + +/********* -rboxpoints_r.c prototypes **********************/ +int qh_rboxpoints(qhT *qh, char* rbox_command); +void qh_errexit_rbox(qhT *qh, int exitcode); + +/********* -stat_r.c prototypes (duplicated from stat_r.h) **********************/ + +void qh_collectstatistics(qhT *qh); +void qh_printallstatistics(qhT *qh, FILE *fp, const char *string); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFlibqhull */
+ C/mem_r.c view
@@ -0,0 +1,562 @@+/*<html><pre> -<a href="qh-mem_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + mem_r.c + memory management routines for qhull + + See libqhull/mem_r.c for a standalone program. + + To initialize memory: + + qh_meminit(qh, stderr); + qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf); + qh_memsize(qh, (int)sizeof(facetT)); + qh_memsize(qh, (int)sizeof(facetT)); + ... + qh_memsetup(qh); + + To free up all memory buffers: + qh_memfreeshort(qh, &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_r.htm and mem_r.h + global_r.c (qh_initbuffers) for an example of using mem_r.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/mem_r.c#5 $$Change: 2065 $ + $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $ +*/ + +#include "libqhull_r.h" /* includes user_r.h and mem_r.h */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef qh_NOmem + +/*============= internal functions ==============*/ + +static int qh_intcompare(const void *i, const void *j); + +/*========== functions in alphabetical order ======== */ + +/*-<a href="qh-mem_r.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(*((const int *)i) - *((const int *)j)); +} /* intcompare */ + + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memalloc">-</a> + + qh_memalloc( qh, 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' + caller is responsible for freeing the memory. + short memory is freed on shutdown by qh_memfreeshort unless qh_NOmem + + design: + if size < qh->qhmem.LASTsize + if qh->qhmem.freelists[size] non-empty + return first object on freelist + else + round up request to size of qh->qhmem.freelists[size] + allocate new allocation buffer if necessary + allocate object from allocation buffer + else + allocate object with qh_malloc() in user.c +*/ +void *qh_memalloc(qhT *qh, int insize) { + void **freelistp, *newbuffer; + int idx, size, n; + int outsize, bufsize; + void *object; + + if (insize<0) { + qh_fprintf(qh, qh->qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d). Did int overflow due to high-D?\n", insize); /* WARN64 */ + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if (insize>=0 && insize <= qh->qhmem.LASTsize) { + idx= qh->qhmem.indextable[insize]; + outsize= qh->qhmem.sizetable[idx]; + qh->qhmem.totshort += outsize; + freelistp= qh->qhmem.freelists+idx; + if ((object= *freelistp)) { + qh->qhmem.cntquick++; + qh->qhmem.totfree -= outsize; + *freelistp= *((void **)*freelistp); /* replace freelist with next object */ +#ifdef qh_TRACEshort + n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort); +#endif + return(object); + }else { + qh->qhmem.cntshort++; + if (outsize > qh->qhmem.freesize) { + qh->qhmem.totdropped += qh->qhmem.freesize; + if (!qh->qhmem.curbuffer) + bufsize= qh->qhmem.BUFinit; + else + bufsize= qh->qhmem.BUFsize; + if (!(newbuffer= qh_malloc((size_t)bufsize))) { + qh_fprintf(qh, qh->qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + *((void **)newbuffer)= qh->qhmem.curbuffer; /* prepend newbuffer to curbuffer + list. newbuffer!=0 by QH6080 */ + qh->qhmem.curbuffer= newbuffer; + size= (sizeof(void **) + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask; + qh->qhmem.freemem= (void *)((char *)newbuffer+size); + qh->qhmem.freesize= bufsize - size; + qh->qhmem.totbuffer += bufsize - size; /* easier to check */ + /* Periodically test totbuffer. It matches at beginning and exit of every call */ + n = qh->qhmem.totshort + qh->qhmem.totfree + qh->qhmem.totdropped + qh->qhmem.freesize - outsize; + if (qh->qhmem.totbuffer != n) { + qh_fprintf(qh, qh->qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qh->qhmem.totbuffer, n); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + } + object= qh->qhmem.freemem; + qh->qhmem.freemem= (void *)((char *)qh->qhmem.freemem + outsize); + qh->qhmem.freesize -= outsize; + qh->qhmem.totunused += outsize - insize; +#ifdef qh_TRACEshort + n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort); +#endif + return object; + } + }else { /* long allocation */ + if (!qh->qhmem.indextable) { + qh_fprintf(qh, qh->qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n"); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + outsize= insize; + qh->qhmem.cntlong++; + qh->qhmem.totlong += outsize; + if (qh->qhmem.maxlong < qh->qhmem.totlong) + qh->qhmem.maxlong= qh->qhmem.totlong; + if (!(object= qh_malloc((size_t)outsize))) { + qh_fprintf(qh, qh->qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, outsize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong); + } + return(object); +} /* memalloc */ + + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memcheck">-</a> + + qh_memcheck(qh) +*/ +void qh_memcheck(qhT *qh) { + int i, count, totfree= 0; + void *object; + + if (!qh) { + qh_fprintf_stderr(6243, "qh_memcheck(qh) error: qh is 0. It does not point to a qhT"); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + if (qh->qhmem.ferr == 0 || qh->qhmem.IStracing < 0 || qh->qhmem.IStracing > 10 || (((qh->qhmem.ALIGNmask+1) & qh->qhmem.ALIGNmask) != 0)) { + qh_fprintf_stderr(6244, "qh_memcheck error: either qh->qhmem is overwritten or qh->qhmem is not initialized. Call qh_mem_new() or qh_new_qhull() before calling qh_mem routines. ferr 0x%x IsTracing %d ALIGNmask 0x%x", qh->qhmem.ferr, qh->qhmem.IStracing, qh->qhmem.ALIGNmask); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + return; + } + if (qh->qhmem.IStracing != 0) + qh_fprintf(qh, qh->qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qh->qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qh->qhmem\n"); + for (i=0; i < qh->qhmem.TABLEsize; i++) { + count=0; + for (object= qh->qhmem.freelists[i]; object; object= *((void **)object)) + count++; + totfree += qh->qhmem.sizetable[i] * count; + } + if (totfree != qh->qhmem.totfree) { + qh_fprintf(qh, qh->qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qh->qhmem.totfree, totfree); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + if (qh->qhmem.IStracing != 0) + qh_fprintf(qh, qh->qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qh->qhmem.totfree\n", totfree); +} /* memcheck */ + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memfree">-</a> + + qh_memfree(qh, object, insize ) + free up an object of size bytes + size is insize from qh_memalloc + + notes: + object may be NULL + type checking warns if using (void **)object + use qh_memfree_() for quick free's of small objects + + design: + if size <= qh->qhmem.LASTsize + append object to corresponding freelist + else + call qh_free(object) +*/ +void qh_memfree(qhT *qh, void *object, int insize) { + void **freelistp; + int idx, outsize; + + if (!object) + return; + if (insize <= qh->qhmem.LASTsize) { + qh->qhmem.freeshort++; + idx= qh->qhmem.indextable[insize]; + outsize= qh->qhmem.sizetable[idx]; + qh->qhmem.totfree += outsize; + qh->qhmem.totshort -= outsize; + freelistp= qh->qhmem.freelists + idx; + *((void **)object)= *freelistp; + *freelistp= object; +#ifdef qh_TRACEshort + idx= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort); +#endif + }else { + qh->qhmem.freelong++; + qh->qhmem.totlong -= insize; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong); + qh_free(object); + } +} /* memfree */ + + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="memfreeshort">-</a> + + qh_memfreeshort(qh, curlong, totlong ) + frees up all short and qhmem memory allocations + + returns: + number and size of current long allocations + + notes: + if qh_NOmem (qh_malloc() for all allocations), + short objects (e.g., facetT) are not recovered. + use qh_freeqhull(qh, qh_ALL) instead. + + see: + qh_freeqhull(qh, allMem) + qh_memtotal(qh, curlong, totlong, curshort, totshort, maxlong, totbuffer); +*/ +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) { + void *buffer, *nextbuffer; + FILE *ferr; + + *curlong= qh->qhmem.cntlong - qh->qhmem.freelong; + *totlong= qh->qhmem.totlong; + for (buffer= qh->qhmem.curbuffer; buffer; buffer= nextbuffer) { + nextbuffer= *((void **) buffer); + qh_free(buffer); + } + qh->qhmem.curbuffer= NULL; + if (qh->qhmem.LASTsize) { + qh_free(qh->qhmem.indextable); + qh_free(qh->qhmem.freelists); + qh_free(qh->qhmem.sizetable); + } + ferr= qh->qhmem.ferr; + memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */ + qh->qhmem.ferr= ferr; +} /* memfreeshort */ + + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="meminit">-</a> + + qh_meminit(qh, ferr ) + initialize qhmem and test sizeof( void*) + Does not throw errors. qh_exit on failure +*/ +void qh_meminit(qhT *qh, FILE *ferr) { + + memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */ + if (ferr) + qh->qhmem.ferr= ferr; + else + qh->qhmem.ferr= stderr; + if (sizeof(void*) < sizeof(int)) { + qh_fprintf(qh, qh->qhmem.ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int)); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + if (sizeof(void*) > sizeof(ptr_intT)) { + qh_fprintf(qh, qh->qhmem.ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT)); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + qh_memcheck(qh); +} /* meminit */ + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="meminitbuffers">-</a> + + qh_meminitbuffers(qh, 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(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) { + + qh->qhmem.IStracing= tracelevel; + qh->qhmem.NUMsizes= numsizes; + qh->qhmem.BUFsize= bufsize; + qh->qhmem.BUFinit= bufinit; + qh->qhmem.ALIGNmask= alignment-1; + if (qh->qhmem.ALIGNmask & ~qh->qhmem.ALIGNmask) { + qh_fprintf(qh, qh->qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + qh->qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int)); + qh->qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *)); + if (!qh->qhmem.sizetable || !qh->qhmem.freelists) { + qh_fprintf(qh, qh->qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n"); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if (qh->qhmem.IStracing >= 1) + qh_fprintf(qh, qh->qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment); +} /* meminitbuffers */ + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="memsetup">-</a> + + qh_memsetup(qh) + set up memory after running memsize() +*/ +void qh_memsetup(qhT *qh) { + int k,i; + + qsort(qh->qhmem.sizetable, (size_t)qh->qhmem.TABLEsize, sizeof(int), qh_intcompare); + qh->qhmem.LASTsize= qh->qhmem.sizetable[qh->qhmem.TABLEsize-1]; + if(qh->qhmem.LASTsize >= qh->qhmem.BUFsize || qh->qhmem.LASTsize >= qh->qhmem.BUFinit) { + qh_fprintf(qh, qh->qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n", + qh->qhmem.LASTsize, qh->qhmem.BUFsize, qh->qhmem.BUFinit); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if (!(qh->qhmem.indextable= (int *)qh_malloc((qh->qhmem.LASTsize+1) * sizeof(int)))) { + qh_fprintf(qh, qh->qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n"); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + for (k=qh->qhmem.LASTsize+1; k--; ) + qh->qhmem.indextable[k]= k; + i= 0; + for (k=0; k <= qh->qhmem.LASTsize; k++) { + if (qh->qhmem.indextable[k] <= qh->qhmem.sizetable[i]) + qh->qhmem.indextable[k]= i; + else + qh->qhmem.indextable[k]= ++i; + } +} /* memsetup */ + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="memsize">-</a> + + qh_memsize(qh, size ) + define a free list for this size +*/ +void qh_memsize(qhT *qh, int size) { + int k; + + if(qh->qhmem.LASTsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n"); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + size= (size + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask; + for (k=qh->qhmem.TABLEsize; k--; ) { + if (qh->qhmem.sizetable[k] == size) + return; + } + if (qh->qhmem.TABLEsize < qh->qhmem.NUMsizes) + qh->qhmem.sizetable[qh->qhmem.TABLEsize++]= size; + else + qh_fprintf(qh, qh->qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qh->qhmem.NUMsizes); +} /* memsize */ + + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="memstatistics">-</a> + + qh_memstatistics(qh, fp ) + print out memory statistics + + Verifies that qh->qhmem.totfree == sum of freelists +*/ +void qh_memstatistics(qhT *qh, FILE *fp) { + int i; + int count; + void *object; + + qh_memcheck(qh); + qh_fprintf(qh, fp, 9278, "\nmemory statistics:\n\ +%7d quick allocations\n\ +%7d short allocations\n\ +%7d long allocations\n\ +%7d short frees\n\ +%7d long frees\n\ +%7d bytes of short memory in use\n\ +%7d bytes of short memory in freelists\n\ +%7d bytes of dropped short memory\n\ +%7d bytes of unused short memory (estimated)\n\ +%7d bytes of long memory allocated (max, except for input)\n\ +%7d bytes of long memory in use (in %d pieces)\n\ +%7d bytes of short memory buffers (minus links)\n\ +%7d bytes per short memory buffer (initially %d bytes)\n", + qh->qhmem.cntquick, qh->qhmem.cntshort, qh->qhmem.cntlong, + qh->qhmem.freeshort, qh->qhmem.freelong, + qh->qhmem.totshort, qh->qhmem.totfree, + qh->qhmem.totdropped + qh->qhmem.freesize, qh->qhmem.totunused, + qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong, + qh->qhmem.totbuffer, qh->qhmem.BUFsize, qh->qhmem.BUFinit); + if (qh->qhmem.cntlarger) { + qh_fprintf(qh, fp, 9279, "%7d calls to qh_setlarger\n%7.2g average copy size\n", + qh->qhmem.cntlarger, ((float)qh->qhmem.totlarger)/(float)qh->qhmem.cntlarger); + qh_fprintf(qh, fp, 9280, " freelists(bytes->count):"); + } + for (i=0; i < qh->qhmem.TABLEsize; i++) { + count=0; + for (object= qh->qhmem.freelists[i]; object; object= *((void **)object)) + count++; + qh_fprintf(qh, fp, 9281, " %d->%d", qh->qhmem.sizetable[i], count); + } + qh_fprintf(qh, fp, 9282, "\n\n"); +} /* memstatistics */ + + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="NOmem">-</a> + + qh_NOmem + turn off quick-fit memory allocation + + notes: + uses qh_malloc() and qh_free() instead +*/ +#else /* qh_NOmem */ + +void *qh_memalloc(qhT *qh, int insize) { + void *object; + + if (!(object= qh_malloc((size_t)insize))) { + qh_fprintf(qh, qh->qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n"); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + qh->qhmem.cntlong++; + qh->qhmem.totlong += insize; + if (qh->qhmem.maxlong < qh->qhmem.totlong) + qh->qhmem.maxlong= qh->qhmem.totlong; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong); + return object; +} + +void qh_memfree(qhT *qh, void *object, int insize) { + + if (!object) + return; + qh_free(object); + qh->qhmem.freelong++; + qh->qhmem.totlong -= insize; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong); +} + +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) { + *totlong= qh->qhmem.totlong; + *curlong= qh->qhmem.cntlong - qh->qhmem.freelong; + memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */ +} + +void qh_meminit(qhT *qh, FILE *ferr) { + + memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */ + if (ferr) + qh->qhmem.ferr= ferr; + else + qh->qhmem.ferr= stderr; + if (sizeof(void*) < sizeof(int)) { + qh_fprintf(qh, qh->qhmem.ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int)); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } +} + +void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) { + + qh->qhmem.IStracing= tracelevel; +} + +void qh_memsetup(qhT *qh) { + +} + +void qh_memsize(qhT *qh, int size) { + +} + +void qh_memstatistics(qhT *qh, FILE *fp) { + + qh_fprintf(qh, fp, 9409, "\nmemory statistics:\n\ +%7d long allocations\n\ +%7d long frees\n\ +%7d bytes of long memory allocated (max, except for input)\n\ +%7d bytes of long memory in use (in %d pieces)\n", + qh->qhmem.cntlong, + qh->qhmem.freelong, + qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong); +} + +#endif /* qh_NOmem */ + +/*-<a href="qh-mem_r.htm#TOC" +>-------------------------------</a><a name="memtotlong">-</a> + + qh_memtotal(qh, totlong, curlong, totshort, curshort, maxlong, totbuffer ) + Return the total, allocated long and short memory + + returns: + Returns the total current bytes of long and short allocations + Returns the current count of long and short allocations + Returns the maximum long memory and total short buffer (minus one link per buffer) + Does not error (for deprecated UsingLibQhull.cpp (libqhullpcpp)) +*/ +void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) { + *totlong= qh->qhmem.totlong; + *curlong= qh->qhmem.cntlong - qh->qhmem.freelong; + *totshort= qh->qhmem.totshort; + *curshort= qh->qhmem.cntshort + qh->qhmem.cntquick - qh->qhmem.freeshort; + *maxlong= qh->qhmem.maxlong; + *totbuffer= qh->qhmem.totbuffer; +} /* memtotlong */
+ C/mem_r.h view
@@ -0,0 +1,234 @@+/*<html><pre> -<a href="qh-mem_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + mem_r.h + prototypes for memory management functions + + see qh-mem_r.htm, mem_r.c and qset_r.h + + for error handling, writes message and calls + qh_errexit(qhT *qh, qhmem_ERRmem, NULL, NULL) if insufficient memory + and + qh_errexit(qhT *qh, qhmem_ERRqhull, NULL, NULL) otherwise + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/mem_r.h#4 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ +*/ + +#ifndef qhDEFmem +#define qhDEFmem 1 + +#include <stdio.h> + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* defined in qset_r.h */ +#endif + +#ifndef DEFqhT +#define DEFqhT 1 +typedef struct qhT qhT; /* defined in libqhull_r.h */ +#endif + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="NOmem">-</a> + + qh_NOmem + turn off quick-fit memory allocation + + notes: + mem_r.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 +*/ + +/*-<a href="qh-mem_r.htm#TOC" +>-------------------------------</a><a name="TRACEshort">-</a> + +qh_TRACEshort +Trace short and quick memory allocations at T5 + +*/ +#define qh_TRACEshort + +/*------------------------------------------- + to avoid bus errors, memory allocation must consider alignment requirements. + malloc() automatically takes care of alignment. Since mem_r.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 libqhull_r.h */ +#define qhmem_ERRqhull 5 /* matches qh_ERRqhull in libqhull_r.h */ + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="ptr_intT">-</a> + + ptr_intT + for casting a void * to an integer-type that holds a pointer + Used for integer expressions (e.g., computing qh_gethash() in poly_r.c) + + notes: + WARN64 -- these notes indicate 64-bit issues + On 64-bit machines, a pointer may be larger than an 'int'. + qh_meminit()/mem_r.c checks that 'ptr_intT' holds a 'void*' + ptr_intT is typically a signed value, but not necessarily so + size_t is typically unsigned, but should match the parameter type + Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc. + This matches Qt convention and is easier to work with. +*/ +#if (defined(__MINGW64__)) && defined(_WIN64) +typedef long long ptr_intT; +#elif (_MSC_VER) && defined(_WIN64) +typedef long long ptr_intT; +#else +typedef long ptr_intT; +#endif + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="qhmemT">-</a> + + qhmemT + global memory structure for mem_r.c + + notes: + users should ignore qhmem except for writing extensions + qhmem is allocated in mem_r.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 qh->qhmem. +*/ +typedef struct qhmemT qhmemT; + +/* Update qhmem in mem_r.c if add or remove fields */ +struct qhmemT { /* global memory management variables */ + int BUFsize; /* size of memory allocation buffer */ + int BUFinit; /* initial size of memory allocation buffer */ + int TABLEsize; /* actual number of sizes in free list table */ + int NUMsizes; /* maximum number of sizes in free list table */ + int LASTsize; /* last size in free list table */ + int ALIGNmask; /* worst-case alignment, must be 2^n-1 */ + void **freelists; /* free list table, linked by offset 0 */ + int *sizetable; /* size of each freelist */ + int *indextable; /* size->index table */ + void *curbuffer; /* current buffer, linked by offset 0 */ + void *freemem; /* free memory in curbuffer */ + int freesize; /* size of freemem in bytes */ + setT *tempstack; /* stack of temporary memory, managed by users */ + FILE *ferr; /* file for reporting errors when 'qh' may be undefined */ + int IStracing; /* =5 if tracing memory allocations */ + int cntquick; /* count of quick allocations */ + /* Note: removing statistics doesn't effect speed */ + int cntshort; /* count of short allocations */ + int cntlong; /* count of long allocations */ + int freeshort; /* count of short memfrees */ + int freelong; /* count of long memfrees */ + int totbuffer; /* total short memory buffers minus buffer links */ + int totdropped; /* total dropped memory at end of short memory buffers (e.g., freesize) */ + int totfree; /* total size of free, short memory on freelists */ + int totlong; /* total size of long memory in use */ + int maxlong; /* maximum totlong */ + int totshort; /* total size of short memory in use */ + int totunused; /* total unused short memory (estimated, short size - request size of first allocations) */ + int cntlarger; /* count of setlarger's */ + int totlarger; /* total copied by setlarger */ +}; + + +/*==================== -macros ====================*/ + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memalloc_">-</a> + + qh_memalloc_(qh, insize, freelistp, object, type) + returns object of size bytes + assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp +*/ + +#if defined qh_NOmem +#define qh_memalloc_(qh, insize, freelistp, object, type) {\ + object= (type*)qh_memalloc(qh, insize); } +#elif defined qh_TRACEshort +#define qh_memalloc_(qh, insize, freelistp, object, type) {\ + freelistp= NULL; /* Avoid warnings */ \ + object= (type*)qh_memalloc(qh, insize); } +#else /* !qh_NOmem */ + +#define qh_memalloc_(qh, insize, freelistp, object, type) {\ + freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\ + if ((object= (type*)*freelistp)) {\ + qh->qhmem.totshort += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \ + qh->qhmem.totfree -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \ + qh->qhmem.cntquick++; \ + *freelistp= *((void **)*freelistp);\ + }else object= (type*)qh_memalloc(qh, insize);} +#endif + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memfree_">-</a> + + qh_memfree_(qh, object, insize, freelistp) + free up an object + + notes: + object may be NULL + assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp +*/ +#if defined qh_NOmem +#define qh_memfree_(qh, object, insize, freelistp) {\ + qh_memfree(qh, object, insize); } +#elif defined qh_TRACEshort +#define qh_memfree_(qh, object, insize, freelistp) {\ + freelistp= NULL; /* Avoid warnings */ \ + qh_memfree(qh, object, insize); } +#else /* !qh_NOmem */ + +#define qh_memfree_(qh, object, insize, freelistp) {\ + if (object) { \ + qh->qhmem.freeshort++;\ + freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\ + qh->qhmem.totshort -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \ + qh->qhmem.totfree += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \ + *((void **)object)= *freelistp;\ + *freelistp= object;}} +#endif + +/*=============== prototypes in alphabetical order ============*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void *qh_memalloc(qhT *qh, int insize); +void qh_memcheck(qhT *qh); +void qh_memfree(qhT *qh, void *object, int insize); +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong); +void qh_meminit(qhT *qh, FILE *ferr); +void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, + int bufsize, int bufinit); +void qh_memsetup(qhT *qh); +void qh_memsize(qhT *qh, int size); +void qh_memstatistics(qhT *qh, FILE *fp); +void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFmem */
+ C/merge_r.c view
@@ -0,0 +1,3627 @@+/*<html><pre> -<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + merge_r.c + merges non-convex facets + + see qh-merge_r.htm and merge_r.h + + other modules call qh_premerge() and qh_postmerge() + + the user may call qh_postmerge() to perform additional merges. + + To remove deleted facets and vertices (qhull() in libqhull_r.c): + qh_partitionvisible(qh, !qh_ALL, &numoutside); // visible_list, newfacet_list + qh_deletevisible(); // qh.visible_list + qh_resetlists(qh, 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-2015 C.B. Barber. + $Id: //main/2015/qhull/src/libqhull_r/merge_r.c#5 $$Change: 2064 $ + $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +#ifndef qh_NOmerge + +/*===== functions(alphabetical after premerge and postmerge) ======*/ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="premerge">-</a> + + qh_premerge(qh, 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(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) { + boolT othermerge= False; + facetT *newfacet; + + if (qh->ZEROcentrum && qh_checkzero(qh, !qh_ALL)) + return; + trace2((qh, qh->ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n", + maxcentrum, maxangle, apex->id, getid_(qh->newfacet_list))); + if (qh->IStracing >= 4 && qh->num_facets < 50) + qh_printlists(qh); + qh->centrum_radius= maxcentrum; + qh->cos_max= maxangle; + qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize); + qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize); + if (qh->hull_dim >=3) { + qh_mark_dupridges(qh, qh->newfacet_list); /* facet_mergeset */ + qh_mergecycle_all(qh, qh->newfacet_list, &othermerge); + qh_forcedmerges(qh, &othermerge /* qh->facet_mergeset */); + FORALLnew_facets { /* test samecycle merges */ + if (!newfacet->simplicial && !newfacet->mergeridge) + qh_degen_redundant_neighbors(qh, newfacet, NULL); + } + if (qh_merge_degenredundant(qh)) + othermerge= True; + }else /* qh->hull_dim == 2 */ + qh_mergecycle_all(qh, qh->newfacet_list, &othermerge); + qh_flippedmerges(qh, qh->newfacet_list, &othermerge); + if (!qh->MERGEexact || zzval_(Ztotmerge)) { + zinc_(Zpremergetot); + qh->POSTmerging= False; + qh_getmergeset_initial(qh, qh->newfacet_list); + qh_all_merges(qh, othermerge, False); + } + qh_settempfree(qh, &qh->facet_mergeset); + qh_settempfree(qh, &qh->degen_mergeset); +} /* premerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="postmerge">-</a> + + qh_postmerge(qh, 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(qhT *qh, const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { + facetT *newfacet; + boolT othermerges= False; + vertexT *vertex; + + if (qh->REPORTfreq || qh->IStracing) { + qh_buildtracing(qh, NULL, NULL); + qh_printsummary(qh, qh->ferr); + if (qh->PRINTstatistics) + qh_printallstatistics(qh, qh->ferr, "reason"); + qh_fprintf(qh, qh->ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n", + reason, maxcentrum, maxangle); + } + trace2((qh, qh->ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n", + vneighbors)); + qh->centrum_radius= maxcentrum; + qh->cos_max= maxangle; + qh->POSTmerging= True; + qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize); + qh->facet_mergeset= qh_settemp(qh, 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(qh); /* was skipped during pre-merging */ + } + } + if (!qh->PREmerge && !qh->MERGEexact) + qh_flippedmerges(qh, qh->newfacet_list, &othermerges); + } + qh_getmergeset_initial(qh, qh->newfacet_list); + qh_all_merges(qh, False, vneighbors); + qh_settempfree(qh, &qh->facet_mergeset); + qh_settempfree(qh, &qh->degen_mergeset); +} /* post_merge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="all_merges">-</a> + + qh_all_merges(qh, 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(qhT *qh, boolT othermerge, boolT vneighbors) { + facetT *facet1, *facet2; + mergeT *merge; + boolT wasmerge= True, isreduce; + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + vertexT *vertex; + mergeType mergetype; + int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0; + + trace2((qh, qh->ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n", + getid_(qh->newfacet_list))); + while (True) { + wasmerge= False; + while (qh_setsize(qh, qh->facet_mergeset)) { + while ((merge= (mergeT*)qh_setdellast(qh->facet_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + mergetype= merge->type; + qh_memfree_(qh, merge, (int)sizeof(mergeT), freelistp); + if (facet1->visible || facet2->visible) /*deleted facet*/ + continue; + if ((facet1->newfacet && !facet1->tested) + || (facet2->newfacet && !facet2->tested)) { + if (qh->MERGEindependent && mergetype <= MRGanglecoplanar) + continue; /* perform independent sets of merges */ + } + qh_merge_nonconvex(qh, facet1, facet2, mergetype); + numdegenredun += qh_merge_degenredundant(qh); + 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(qh); /* otherwise large post merges too slow */ + } + qh_getmergeset(qh, 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)) { + qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */ + continue; + } + } + } + if (vneighbors && qh_test_vneighbors(qh /* qh->newfacet_list */)) + continue; + break; + } /* while (True) */ + if (qh->CHECKfrequently && !qh->MERGEexact) { + qh->old_randomdist= qh->RANDOMdist; + qh->RANDOMdist= False; + qh_checkconvex(qh, qh->newfacet_list, qh_ALGORITHMfault); + /* qh_checkconnect(qh); [this is slow and it changes the facet order] */ + qh->RANDOMdist= qh->old_randomdist; + } + trace1((qh, qh->ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n", + numcoplanar, numconcave, numdegenredun)); + if (qh->IStracing >= 4 && qh->num_facets < 50) + qh_printlists(qh); +} /* all_merges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="appendmergeset">-</a> + + qh_appendmergeset(qh, 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(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) { + mergeT *merge, *lastmerge; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + if (facet->redundant) + return; + if (facet->degenerate && mergetype == MRGdegen) + return; + qh_memalloc_(qh, (int)sizeof(mergeT), freelistp, merge, mergeT); + merge->facet1= facet; + merge->facet2= neighbor; + merge->type= mergetype; + if (angle && qh->ANGLEmerge) + merge->angle= *angle; + if (mergetype < MRGdegen) + qh_setappend(qh, &(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, &(qh->degen_mergeset), merge); + else + qh_setaddnth(qh, &(qh->degen_mergeset), 0, merge); + }else if (mergetype == MRGredundant) { + facet->redundant= True; + qh_setappend(qh, &(qh->degen_mergeset), merge); + }else /* mergetype == MRGmirror */ { + if (facet->redundant || neighbor->redundant) { + qh_fprintf(qh, qh->ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n", + facet->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facet, neighbor); + } + if (!qh_setequal(facet->vertices, neighbor->vertices)) { + qh_fprintf(qh, qh->ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n", + facet->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facet, neighbor); + } + facet->redundant= True; + neighbor->redundant= True; + qh_setappend(qh, &(qh->degen_mergeset), merge); + } +} /* appendmergeset */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="basevertices">-</a> + + qh_basevertices(qh, 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(qhT *qh, facetT *samecycle) { + facetT *same; + vertexT *apex, *vertex, **vertexp; + setT *vertices= qh_settemp(qh, 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(qh, &vertices, vertex); + vertex->visitid= qh->vertex_visit; + vertex->seen= False; + } + } + } + trace4((qh, qh->ferr, 4019, "qh_basevertices: found %d vertices\n", + qh_setsize(qh, vertices))); + return vertices; +} /* basevertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="checkconnect">-</a> + + qh_checkconnect(qh) + 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(qhT *qh /* qh->newfacet_list */) { + facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp; + + facet= qh->newfacet_list; + qh_removefacet(qh, facet); + qh_appendfacet(qh, facet); + facet->visitid= ++qh->visit_id; + FORALLfacet_(facet) { + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh->visit_id) { + qh_removefacet(qh, neighbor); + qh_appendfacet(qh, neighbor); + neighbor->visitid= qh->visit_id; + } + } + } + FORALLnew_facets { + if (newfacet->visitid == qh->visit_id) + break; + qh_fprintf(qh, qh->ferr, 6094, "qhull error: f%d is not attached to the new facets\n", + newfacet->id); + errfacet= newfacet; + } + if (errfacet) + qh_errexit(qh, qh_ERRqhull, errfacet, NULL); +} /* checkconnect */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="checkzero">-</a> + + qh_checkzero(qh, 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(qhT *qh, 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, qh->ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n")); + return True; + } + } + FORALLfacet_(facetlist) { + qh->vertex_visit++; + neighbor_i= 0; + horizon= NULL; + FOREACHneighbor_(facet) { + if (!neighbor_i && !testall) { + horizon= neighbor; + neighbor_i++; + continue; /* horizon facet tested in qh_findhorizon */ + } + vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT); + vertex->visitid= qh->vertex_visit; + zzinc_(Zdistzero); + qh_distplane(qh, vertex->point, neighbor, &dist); + if (dist >= -qh->DISTround) { + qh->ZEROall_ok= False; + if (!qh->MERGEexact || testall || dist > qh->DISTround) + goto LABELnonconvex; + } + } + if (!testall && horizon) { + FOREACHvertex_(horizon->vertices) { + if (vertex->visitid != qh->vertex_visit) { + zzinc_(Zdistzero); + qh_distplane(qh, vertex->point, facet, &dist); + if (dist >= -qh->DISTround) { + qh->ZEROall_ok= False; + if (!qh->MERGEexact || dist > qh->DISTround) + goto LABELnonconvex; + } + break; + } + } + } + } + trace2((qh, qh->ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall, + (qh->MERGEexact && !testall) ? + "not concave, flipped, or duplicate ridged" : "clearly convex")); + return True; + + LABELproblem: + qh->ZEROall_ok= False; + trace2((qh, qh->ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n", + facet->id)); + return False; + + LABELnonconvex: + trace2((qh, qh->ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n", + facet->id, neighbor->id, vertex->id, dist)); + return False; +} /* checkzero */ + +/*-<a href="qh-merge_r.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) { + const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); + + return((a->angle > b->angle) ? 1 : -1); +} /* compareangle */ + +/*-<a href="qh-merge_r.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) { + const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); + + return(a->type - b->type); +} /* comparemerge */ + +/*-<a href="qh-merge_r.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) { + const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); + + return(a->visitid - b->visitid); +} /* comparevisit */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="copynonconvex">-</a> + + qh_copynonconvex(qh, 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(qhT *qh, 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, qh->ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n", + atridge->id, ridge->id)); + break; + } + } +} /* copynonconvex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="degen_redundant_facet">-</a> + + qh_degen_redundant_facet(qh, 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(qhT *qh, facetT *facet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + + trace4((qh, qh->ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n", + facet->id)); + FOREACHneighbor_(facet) { + qh->vertex_visit++; + FOREACHvertex_(neighbor->vertices) + vertex->visitid= qh->vertex_visit; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) + break; + } + if (!vertex) { + qh_appendmergeset(qh, facet, neighbor, MRGredundant, NULL); + trace2((qh, qh->ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id)); + return; + } + } + if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) { + qh_appendmergeset(qh, facet, facet, MRGdegen, NULL); + trace2((qh, qh->ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id)); + } +} /* degen_redundant_facet */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="degen_redundant_neighbors">-</a> + + qh_degen_redundant_neighbors(qh, 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(qhT *qh, facetT *facet, facetT *delfacet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + int size; + + trace4((qh, qh->ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n", + facet->id, getid_(delfacet))); + if ((size= qh_setsize(qh, facet->neighbors)) < qh->hull_dim) { + qh_appendmergeset(qh, facet, facet, MRGdegen, NULL); + trace2((qh, qh->ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size)); + } + if (!delfacet) + delfacet= facet; + qh->vertex_visit++; + FOREACHvertex_(facet->vertices) + vertex->visitid= qh->vertex_visit; + FOREACHneighbor_(delfacet) { + /* uses early out instead of checking vertex count */ + if (neighbor == facet) + continue; + FOREACHvertex_(neighbor->vertices) { + if (vertex->visitid != qh->vertex_visit) + break; + } + if (!vertex) { + qh_appendmergeset(qh, neighbor, facet, MRGredundant, NULL); + trace2((qh, qh->ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id)); + } + } + FOREACHneighbor_(delfacet) { /* redundant merges occur first */ + if (neighbor == facet) + continue; + if ((size= qh_setsize(qh, neighbor->neighbors)) < qh->hull_dim) { + qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, NULL); + trace2((qh, qh->ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id)); + } + } +} /* degen_redundant_neighbors */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="find_newvertex">-</a> + + qh_find_newvertex(qh, 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(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges) { + vertexT *vertex, **vertexp; + setT *newridges; + ridgeT *ridge, **ridgep; + int size, hashsize; + int hash; + +#ifndef qh_NOtrace + if (qh->IStracing >= 4) { + qh_fprintf(qh, qh->ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ", + oldvertex->id); + FOREACHvertex_(vertices) + qh_fprintf(qh, qh->ferr, 8064, "v%d ", vertex->id); + FOREACHridge_(ridges) + qh_fprintf(qh, qh->ferr, 8065, "r%d ", ridge->id); + qh_fprintf(qh, qh->ferr, 8066, "\n"); + } +#endif + FOREACHvertex_(vertices) + vertex->visitid= 0; + FOREACHridge_(ridges) { + FOREACHvertex_(ridge->vertices) + vertex->visitid++; + } + FOREACHvertex_(vertices) { + if (!vertex->visitid) { + qh_setdelnth(qh, vertices, SETindex_(vertices,vertex)); + vertexp--; /* repeat since deleted this vertex */ + } + } + qh->vertex_visit += (unsigned int)qh_setsize(qh, ridges); + if (!qh_setsize(qh, vertices)) { + trace4((qh, qh->ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n", + oldvertex->id)); + return NULL; + } + qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(qh, vertices), + sizeof(vertexT *), qh_comparevisit); + /* can now use qh->vertex_visit */ + if (qh->PRINTstatistics) { + size= qh_setsize(qh, vertices); + zinc_(Zintersect); + zadd_(Zintersecttot, size); + zmax_(Zintersectmax, size); + } + hashsize= qh_newhashtable(qh, qh_setsize(qh, ridges)); + FOREACHridge_(ridges) + qh_hashridge(qh, qh->hash_table, hashsize, ridge, oldvertex); + FOREACHvertex_(vertices) { + newridges= qh_vertexridges(qh, vertex); + FOREACHridge_(newridges) { + if (qh_hashridge_find(qh, qh->hash_table, hashsize, ridge, vertex, oldvertex, &hash)) { + zinc_(Zdupridge); + break; + } + } + qh_settempfree(qh, &newridges); + if (!ridge) + break; /* found a rename */ + } + if (vertex) { + /* counted in qh_renamevertex */ + trace2((qh, qh->ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n", + vertex->id, oldvertex->id, qh_setsize(qh, vertices), qh_setsize(qh, ridges))); + }else { + zinc_(Zfindfail); + trace0((qh, qh->ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n", + oldvertex->id, qh->furthest_id)); + } + qh_setfree(qh, &qh->hash_table); + return vertex; +} /* find_newvertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="findbest_test">-</a> + + qh_findbest_test(qh, 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(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor, + facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) { + realT dist, mindist, maxdist; + + if (testcentrum) { + zzinc_(Zbestdist); + qh_distplane(qh, facet->center, neighbor, &dist); + dist *= qh->hull_dim; /* estimate furthest vertex */ + if (dist < 0) { + maxdist= 0; + mindist= dist; + dist= -dist; + }else { + mindist= 0; + maxdist= dist; + } + }else + dist= qh_getdistance(qh, facet, neighbor, &mindist, &maxdist); + if (dist < *distp) { + *bestfacet= neighbor; + *mindistp= mindist; + *maxdistp= maxdist; + *distp= dist; + } +} /* findbest_test */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="findbestneighbor">-</a> + + qh_findbestneighbor(qh, 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: + error if qh_ASvoronoi + 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(qhT *qh, 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(qh, facet->vertices); + + if(qh->CENTERtype==qh_ASvoronoi){ + qh_fprintf(qh, qh->ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + *distp= REALmax; + if (size > qh_BESTcentrum2 * qh->hull_dim + qh_BESTcentrum) { + testcentrum= True; + zinc_(Zbestcentrum); + if (!facet->center) + facet->center= qh_getcentrum(qh, facet); + } + if (size > qh->hull_dim + qh_BESTnonconvex) { + FOREACHridge_(facet->ridges) { + if (ridge->nonconvex) { + neighbor= otherfacet_(ridge, facet); + qh_findbest_test(qh, testcentrum, facet, neighbor, + &bestfacet, distp, mindistp, maxdistp); + } + } + } + if (!bestfacet) { + nonconvex= False; + FOREACHneighbor_(facet) + qh_findbest_test(qh, testcentrum, facet, neighbor, + &bestfacet, distp, mindistp, maxdistp); + } + if (!bestfacet) { + qh_fprintf(qh, qh->ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (testcentrum) + qh_getdistance(qh, facet, bestfacet, mindistp, maxdistp); + trace3((qh, qh->ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n", + bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp)); + return(bestfacet); +} /* findbestneighbor */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="flippedmerges">-</a> + + qh_flippedmerges(qh, 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(qhT *qh, facetT *facetlist, boolT *wasmerge) { + facetT *facet, *neighbor, *facet1; + realT dist, mindist, maxdist; + mergeT *merge, **mergep; + setT *othermerges; + int nummerge=0; + + trace4((qh, qh->ferr, 4024, "qh_flippedmerges: begin\n")); + FORALLfacet_(facetlist) { + if (facet->flipped && !facet->visible) + qh_appendmergeset(qh, facet, facet, MRGflip, NULL); + } + othermerges= qh_settemppop(qh); /* was facet_mergeset */ + qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize); + qh_settemppush(qh, othermerges); + FOREACHmerge_(othermerges) { + facet1= merge->facet1; + if (merge->type != MRGflip || facet1->visible) + continue; + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist); + trace0((qh, qh->ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n", + facet1->id, neighbor->id, dist, qh->furthest_id)); + qh_mergefacet(qh, facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex); + nummerge++; + if (qh->PRINTstatistics) { + zinc_(Zflipped); + wadd_(Wflippedtot, dist); + wmax_(Wflippedmax, dist); + } + qh_merge_degenredundant(qh); + } + FOREACHmerge_(othermerges) { + if (merge->facet1->visible || merge->facet2->visible) + qh_memfree(qh, merge, (int)sizeof(mergeT)); + else + qh_setappend(qh, &qh->facet_mergeset, merge); + } + qh_settempfree(qh, &othermerges); + if (nummerge) + *wasmerge= True; + trace1((qh, qh->ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge)); +} /* flippedmerges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="forcedmerges">-</a> + + qh_forcedmerges(qh, 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 + check for wide merge due to duplicate ridge + determine best direction for facet + merge one facet into the other + remove duplicate ridges from qh.facet_mergeset +*/ +void qh_forcedmerges(qhT *qh, 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)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + trace4((qh, qh->ferr, 4025, "qh_forcedmerges: begin\n")); + othermerges= qh_settemppop(qh); /* was facet_mergeset */ + qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize); + qh_settemppush(qh, othermerges); + FOREACHmerge_(othermerges) { + if (merge->type != MRGridge) + continue; + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + facet1= merge->facet1; + facet2= merge->facet2; + while (facet1->visible) /* must exist, no qh_merge_degenredunant */ + facet1= facet1->f.replace; /* previously merged facet */ + while (facet2->visible) + facet2= facet2->f.replace; /* previously merged facet */ + if (facet1 == facet2) + continue; + if (!qh_setin(facet2->neighbors, facet1)) { + qh_fprintf(qh, qh->ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n", + merge->facet1->id, merge->facet2->id, facet1->id, facet2->id); + qh_errexit2(qh, qh_ERRqhull, facet1, facet2); + } + dist1= qh_getdistance(qh, facet1, facet2, &mindist1, &maxdist1); + dist2= qh_getdistance(qh, facet2, facet1, &mindist2, &maxdist2); + qh_check_dupridge(qh, facet1, dist1, facet2, dist2); + if (dist1 < dist2) + qh_mergefacet(qh, facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex); + else { + qh_mergefacet(qh, 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(qh, merge, (int)sizeof(mergeT)); + else + qh_setappend(qh, &qh->facet_mergeset, merge); + } + qh_settempfree(qh, &othermerges); + if (nummerge) + *wasmerge= True; + trace1((qh, qh->ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n", + nummerge, numflip)); +} /* forcedmerges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="getmergeset">-</a> + + qh_getmergeset(qh, 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(qhT *qh, facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int nummerges; + + nummerges= qh_setsize(qh, qh->facet_mergeset); + trace4((qh, qh->ferr, 4026, "qh_getmergeset: started.\n")); + qh->visit_id++; + FORALLfacet_(facetlist) { + if (facet->tested) + continue; + facet->visitid= qh->visit_id; + facet->tested= True; /* must be non-simplicial due to merge */ + FOREACHneighbor_(facet) + neighbor->seen= False; + FOREACHridge_(facet->ridges) { + if (ridge->tested && !ridge->nonconvex) + continue; + /* if tested & nonconvex, need to append merge */ + neighbor= otherfacet_(ridge, facet); + if (neighbor->seen) { + ridge->tested= True; + ridge->nonconvex= False; + }else if (neighbor->visitid != qh->visit_id) { + ridge->tested= True; + ridge->nonconvex= False; + neighbor->seen= True; /* only one ridge is marked nonconvex */ + if (qh_test_appendmerge(qh, facet, neighbor)) + ridge->nonconvex= True; + } + } + } + nummerges= qh_setsize(qh, qh->facet_mergeset); + if (qh->ANGLEmerge) + qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle); + else + qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge); + if (qh->POSTmerging) { + zadd_(Zmergesettot2, nummerges); + }else { + zadd_(Zmergesettot, nummerges); + zmax_(Zmergesetmax, nummerges); + } + trace2((qh, qh->ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges)); +} /* getmergeset */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="getmergeset_initial">-</a> + + qh_getmergeset_initial(qh, 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(qhT *qh, 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(qh, 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, qh->facet_mergeset); + if (qh->ANGLEmerge) + qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle); + else + qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge); + if (qh->POSTmerging) { + zadd_(Zmergeinittot2, nummerges); + }else { + zadd_(Zmergeinittot, nummerges); + zmax_(Zmergeinitmax, nummerges); + } + trace2((qh, qh->ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges)); +} /* getmergeset_initial */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="hashridge">-</a> + + qh_hashridge(qh, 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(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) { + int hash; + ridgeT *ridgeA; + + hash= qh_gethash(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="hashridge_find">-</a> + + qh_hashridge_find(qh, 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(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, + vertexT *vertex, vertexT *oldvertex, int *hashslot) { + int hash; + ridgeT *ridgeA; + + *hashslot= 0; + zinc_(Zhashridge); + hash= qh_gethash(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="makeridges">-</a> + + qh_makeridges(qh, 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(qhT *qh, facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int neighbor_i, neighbor_n; + boolT toporient, mergeridge= False; + + if (!facet->simplicial) + return; + trace4((qh, qh->ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id)); + facet->simplicial= False; + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) + mergeridge= True; + else + neighbor->seen= False; + } + FOREACHridge_(facet->ridges) + otherfacet_(ridge, facet)->seen= True; + FOREACHneighbor_i_(qh, facet) { + if (neighbor == qh_MERGEridge) + continue; /* fixed by qh_mark_dupridges */ + else if (!neighbor->seen) { /* no current ridges */ + ridge= qh_newridge(qh); + ridge->vertices= qh_setnew_delnthsorted(qh, 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(qh, &(facet->ridges), ridge); + qh_setappend(qh, &(neighbor->ridges), ridge); + } + } + if (mergeridge) { + while (qh_setdel(facet->neighbors, qh_MERGEridge)) + ; /* delete each one */ + } +} /* makeridges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mark_dupridges">-</a> + + qh_mark_dupridges(qh, 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 (thus removing subridge) + 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(qhT *qh, facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + int nummerge=0; + mergeT *merge, **mergep; + + + trace4((qh, qh->ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n")); + FORALLfacet_(facetlist) { + if (facet->dupridge) { + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) { + facet->mergeridge= True; + continue; + } + if (neighbor->dupridge + && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */ + qh_appendmergeset(qh, 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(qh, facet); + } + FOREACHmerge_(qh->facet_mergeset) { /* restore the missing neighbors */ + if (merge->type == MRGridge) { + qh_setappend(qh, &merge->facet2->neighbors, merge->facet1); + qh_makeridges(qh, merge->facet1); /* and the missing ridges */ + } + } + trace1((qh, qh->ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n", + nummerge)); +} /* mark_dupridges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="maydropneighbor">-</a> + + qh_maydropneighbor(qh, 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(qhT *qh, facetT *facet) { + ridgeT *ridge, **ridgep; + realT angledegen= qh_ANGLEdegen; + facetT *neighbor, **neighborp; + + qh->visit_id++; + trace4((qh, qh->ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n", + facet->id)); + FOREACHridge_(facet->ridges) { + ridge->top->visitid= qh->visit_id; + ridge->bottom->visitid= qh->visit_id; + } + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh->visit_id) { + trace0((qh, qh->ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n", + facet->id, neighbor->id, qh->furthest_id)); + zinc_(Zdropneighbor); + qh_setdel(facet->neighbors, neighbor); + neighborp--; /* repeat, deleted a neighbor */ + qh_setdel(neighbor->neighbors, facet); + if (qh_setsize(qh, neighbor->neighbors) < qh->hull_dim) { + zinc_(Zdropdegen); + qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, &angledegen); + trace2((qh, qh->ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id)); + } + } + } + if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) { + zinc_(Zdropdegen); + qh_appendmergeset(qh, facet, facet, MRGdegen, &angledegen); + trace2((qh, qh->ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id)); + } +} /* maydropneighbor */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="merge_degenredundant">-</a> + + qh_merge_degenredundant(qh) + 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(qhT *qh) { + 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(qh, merge, (int)sizeof(mergeT)); + if (facet1->visible) + continue; + facet1->degenerate= False; + facet1->redundant= False; + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + if (mergetype == MRGredundant) { + zinc_(Zneighbor); + while (facet2->visible) { + if (!facet2->f.replace) { + qh_fprintf(qh, qh->ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n", + facet1->id, facet2->id); + qh_errexit2(qh, qh_ERRqhull, facet1, facet2); + } + facet2= facet2->f.replace; + } + if (facet1 == facet2) { + qh_degen_redundant_facet(qh, facet1); /* in case of others */ + continue; + } + trace2((qh, qh->ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n", + facet1->id, facet2->id)); + qh_mergefacet(qh, 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(qh, facet1->neighbors))) { + zinc_(Zdelfacetdup); + trace2((qh, qh->ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id)); + qh_willdelete(qh, facet1, NULL); + FOREACHvertex_(facet1->vertices) { + qh_setdel(vertex->neighbors, facet1); + if (!SETfirst_(vertex->neighbors)) { + zinc_(Zdegenvertex); + trace2((qh, qh->ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n", + vertex->id, facet1->id)); + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + } + } + nummerges++; + }else if (size < qh->hull_dim) { + bestneighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist); + trace2((qh, qh->ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n", + facet1->id, size, bestneighbor->id, dist)); + qh_mergefacet(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="merge_nonconvex">-</a> + + qh_merge_nonconvex(qh, 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(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) { + facetT *bestfacet, *bestneighbor, *neighbor; + realT dist, dist2, mindist, mindist2, maxdist, maxdist2; + + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + trace3((qh, qh->ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n", + zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype)); + /* concave or coplanar */ + if (!facet1->newfacet) { + bestfacet= facet2; /* avoid merging old facet if new is ok */ + facet2= facet1; + facet1= bestfacet; + }else + bestfacet= facet1; + bestneighbor= qh_findbestneighbor(qh, bestfacet, &dist, &mindist, &maxdist); + neighbor= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2); + if (dist < dist2) { + qh_mergefacet(qh, 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, qh->ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n", + facet2->id, dist2, facet1->id, dist2)); + qh_mergefacet(qh, bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + }else { + qh_mergefacet(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="mergecycle">-</a> + + qh_mergecycle(qh, 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(qhT *qh, facetT *samecycle, facetT *newfacet) { + int traceonce= False, tracerestore= 0; + vertexT *apex; +#ifndef qh_NOtrace + facetT *same; +#endif + + if (newfacet->tricoplanar) { + if (!qh->TRInormals) { + qh_fprintf(qh, qh->ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh, qh_ERRqhull, newfacet, NULL); + } + newfacet->tricoplanar= False; + newfacet->keepcentrum= False; + } + if (!qh->VERTEXneighbors) + qh_vertexneighbors(qh); + zzinc_(Ztotmerge); + if (qh->REPORTfreq2 && qh->POSTmerging) { + if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2) + qh_tracemerging(qh); + } +#ifndef qh_NOtrace + if (qh->TRACEmerge == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + trace2((qh, qh->ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n", + zzval_(Ztotmerge), samecycle->id, newfacet->id)); + if (newfacet == qh->tracefacet) { + tracerestore= qh->IStracing; + qh->IStracing= 4; + qh_fprintf(qh, qh->ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n", + zzval_(Ztotmerge), samecycle->id, newfacet->id, qh->furthest_id); + traceonce= True; + } + if (qh->IStracing >=4) { + qh_fprintf(qh, qh->ferr, 8069, " same cycle:"); + FORALLsame_cycle_(samecycle) + qh_fprintf(qh, qh->ferr, 8070, " f%d", same->id); + qh_fprintf(qh, qh->ferr, 8071, "\n"); + } + if (qh->IStracing >=4) + qh_errprint(qh, "MERGING CYCLE", samecycle, newfacet, NULL, NULL); +#endif /* !qh_NOtrace */ + apex= SETfirstt_(samecycle->vertices, vertexT); + qh_makeridges(qh, newfacet); + qh_mergecycle_neighbors(qh, samecycle, newfacet); + qh_mergecycle_ridges(qh, samecycle, newfacet); + qh_mergecycle_vneighbors(qh, samecycle, newfacet); + if (SETfirstt_(newfacet->vertices, vertexT) != apex) + qh_setaddnth(qh, &newfacet->vertices, 0, apex); /* apex has last id */ + if (!newfacet->newfacet) + qh_newvertices(qh, newfacet->vertices); + qh_mergecycle_facets(qh, samecycle, newfacet); + qh_tracemerge(qh, samecycle, newfacet); + /* check for degen_redundant_neighbors after qh_forcedmerges() */ + if (traceonce) { + qh_fprintf(qh, qh->ferr, 8072, "qh_mergecycle: end of trace facet\n"); + qh->IStracing= tracerestore; + } +} /* mergecycle */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_all">-</a> + + qh_mergecycle_all(qh, 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(qhT *qh, 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, qh->ferr, 2031, "qh_mergecycle_all: begin\n")); + for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) { + if (facet->normal) + continue; + if (!facet->mergehorizon) { + qh_fprintf(qh, qh->ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id); + qh_errexit(qh, 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(qh, 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(qh, 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(qh, samecycle, horizon); + nummerge= horizon->nummerge + facets; + if (nummerge > qh_MAXnummerge) + horizon->nummerge= qh_MAXnummerge; + else + horizon->nummerge= (short unsigned int)nummerge; + zzinc_(Zcyclehorizon); + total += facets; + zzadd_(Zcyclefacettot, facets); + zmax_(Zcyclefacetmax, facets); + } + cycles++; + } + if (cycles) + *wasmerge= True; + trace1((qh, qh->ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles)); +} /* mergecycle_all */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_facets">-</a> + + qh_mergecycle_facets(qh, 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(qhT *qh, facetT *samecycle, facetT *newfacet) { + facetT *same, *next; + + trace4((qh, qh->ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n")); + qh_removefacet(qh, newfacet); /* append as a newfacet to end of qh->facet_list */ + qh_appendfacet(qh, 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(qh, same, newfacet); + } + if (newfacet->center + && qh_setsize(qh, newfacet->vertices) <= qh->hull_dim + qh_MAXnewcentrum) { + qh_memfree(qh, newfacet->center, qh->normal_size); + newfacet->center= NULL; + } + trace3((qh, qh->ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_facets */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_neighbors">-</a> + + qh_mergecycle_neighbors(qh, 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(qhT *qh, 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(qh, samecycle); + same->visitid= samevisitid; + } + newfacet->visitid= ++qh->visit_id; + trace4((qh, qh->ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n")); + FOREACHneighbor_(newfacet) { + if (neighbor->visitid == samevisitid) { + SETref_(neighbor)= NULL; /* samecycle neighbors deleted */ + delneighbors++; + }else + neighbor->visitid= qh->visit_id; + } + qh_setcompact(qh, newfacet->neighbors); + + trace4((qh, qh->ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n")); + FORALLsame_cycle_(samecycle) { + FOREACHneighbor_(same) { + if (neighbor->visitid == samevisitid) + continue; + if (neighbor->simplicial) { + if (neighbor->visitid != qh->visit_id) { + qh_setappend(qh, &newfacet->neighbors, neighbor); + qh_setreplace(qh, 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(qh, 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(qh, &neighbor->neighbors, newfacet); + qh_setappend(qh, &newfacet->neighbors, neighbor); + neighbor->visitid= qh->visit_id; + newneighbors++; + } + } + } + } + trace2((qh, qh->ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n", + delneighbors, newneighbors)); +} /* mergecycle_neighbors */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_ridges">-</a> + + qh_mergecycle_ridges(qh, 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(qhT *qh, 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 if !qh_NOmem by qh_memfree_() */ + + trace4((qh, qh->ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n")); + samevisitid= qh->visit_id -1; + FOREACHridge_(newfacet->ridges) { + neighbor= otherfacet_(ridge, newfacet); + if (neighbor->visitid == samevisitid) + SETref_(ridge)= NULL; /* ridge free'd below */ + } + qh_setcompact(qh, newfacet->ridges); + + trace4((qh, qh->ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n")); + FORALLsame_cycle_(samecycle) { + FOREACHridge_(same->ridges) { + if (ridge->top == same) { + ridge->top= newfacet; + neighbor= ridge->bottom; + }else if (ridge->bottom == same) { + ridge->bottom= newfacet; + neighbor= ridge->top; + }else if (ridge->top == newfacet || ridge->bottom == newfacet) { + qh_setappend(qh, &newfacet->ridges, ridge); + numold++; /* already set by qh_mergecycle_neighbors */ + continue; + }else { + qh_fprintf(qh, qh->ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id); + qh_errexit(qh, qh_ERRqhull, NULL, ridge); + } + if (neighbor == newfacet) { + qh_setfree(qh, &(ridge->vertices)); + qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp); + numold++; + }else if (neighbor->visitid == samevisitid) { + qh_setdel(neighbor->ridges, ridge); + qh_setfree(qh, &(ridge->vertices)); + qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp); + numold++; + }else { + qh_setappend(qh, &newfacet->ridges, ridge); + numold++; + } + } + if (same->ridges) + qh_settruncate(qh, same->ridges, 0); + if (!same->simplicial) + continue; + FOREACHneighbor_i_(qh, same) { /* note: !newfact->simplicial */ + if (neighbor->visitid != samevisitid && neighbor->simplicial) { + ridge= qh_newridge(qh); + ridge->vertices= qh_setnew_delnthsorted(qh, 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(qh, &(newfacet->ridges), ridge); + qh_setappend(qh, &(neighbor->ridges), ridge); + numnew++; + } + } + } + + trace2((qh, qh->ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n", + numold, numnew)); +} /* mergecycle_ridges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_vneighbors">-</a> + + qh_mergecycle_vneighbors(qh, 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(qhT *qh, facetT *samecycle, facetT *newfacet) { + facetT *neighbor, **neighborp; + unsigned int mergeid; + vertexT *vertex, **vertexp, *apex; + setT *vertices; + + trace4((qh, qh->ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n")); + mergeid= qh->visit_id - 1; + newfacet->visitid= mergeid; + vertices= qh_basevertices(qh, samecycle); /* temp */ + apex= SETfirstt_(samecycle->vertices, vertexT); + qh_setappend(qh, &vertices, apex); + FOREACHvertex_(vertices) { + vertex->delridge= True; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == mergeid) + SETref_(neighbor)= NULL; + } + qh_setcompact(qh, vertex->neighbors); + qh_setappend(qh, &vertex->neighbors, newfacet); + if (!SETsecond_(vertex->neighbors)) { + zinc_(Zcyclevertex); + trace2((qh, qh->ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n", + vertex->id, samecycle->id, newfacet->id)); + qh_setdelsorted(newfacet->vertices, vertex); + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + } + } + qh_settempfree(qh, &vertices); + trace3((qh, qh->ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_vneighbors */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergefacet">-</a> + + qh_mergefacet(qh, facet1, facet2, mindist, maxdist, mergeapex ) + merges facet1 into facet2 + mergeapex==qh_MERGEapex if merging new facet into coplanar horizon + + returns: + qh.max_outside and qh.min_vertex updated + initializes vertex neighbors on first merge + + returns: + facet2 contains facet1's vertices, neighbors, and ridges + facet2 moved to end of qh.facet_list + makes facet2 a newfacet + sets facet2->newmerge set + clears facet2->center (unless merging into a large facet) + clears facet2->tested and ridge->tested for facet1 + + facet1 prepended to visible_list for later deletion and partitioning + facet1->f.replace == facet2 + + adds neighboring facets to facet_mergeset if redundant or degenerate + + notes: + mindist/maxdist may be NULL (only if both NULL) + traces merge if fmax_(maxdist,-mindist) > TRACEdist + + see: + qh_mergecycle() + + design: + trace merge and check for degenerate simplex + make ridges for both facets + update qh.max_outside, qh.max_vertex, qh.min_vertex + update facet2->maxoutside and keepcentrum + update facet2->nummerge + update tested flags for facet2 + if facet1 is simplicial + merge facet1 into facet2 + else + merge facet1's neighbors into facet2 + merge facet1's ridges into facet2 + merge facet1's vertices into facet2 + merge facet1's vertex neighbors into facet2 + add facet2's vertices to qh.new_vertexlist + unless qh_MERGEapex + test facet2 for degenerate or redundant neighbors + move facet1 to qh.visible_list for later deletion + move facet2 to end of qh.newfacet_list +*/ +void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) { + boolT traceonce= False; + vertexT *vertex, **vertexp; + int tracerestore=0, nummerge; + + if (facet1->tricoplanar || facet2->tricoplanar) { + if (!qh->TRInormals) { + qh_fprintf(qh, qh->ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit2(qh, 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(qh); + } +#ifndef qh_NOtrace + if (qh->build_cnt >= qh->RERUN) { + if (mindist && (-*mindist > qh->TRACEdist || *maxdist > qh->TRACEdist)) { + tracerestore= 0; + qh->IStracing= qh->TRACElevel; + traceonce= True; + qh_fprintf(qh, qh->ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge), + fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh->furthest_id); + }else if (facet1 == qh->tracefacet || facet2 == qh->tracefacet) { + tracerestore= qh->IStracing; + qh->IStracing= 4; + traceonce= True; + qh_fprintf(qh, qh->ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n", + zzval_(Ztotmerge), qh->tracefacet_id, qh->furthest_id); + } + } + if (qh->IStracing >= 2) { + realT mergemin= -2; + realT mergemax= -2; + + if (mindist) { + mergemin= *mindist; + mergemax= *maxdist; + } + qh_fprintf(qh, qh->ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n", + zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax); + } +#endif /* !qh_NOtrace */ + if (facet1 == facet2 || facet1->visible || facet2->visible) { + qh_fprintf(qh, qh->ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n", + facet1->id, facet2->id); + qh_errexit2(qh, qh_ERRqhull, facet1, facet2); + } + if (qh->num_facets - qh->num_visible <= qh->hull_dim + 1) { + qh_fprintf(qh, qh->ferr, 6227, "\n\ +qhull precision error: Only %d facets remain. Can not merge another\n\ +pair. The input is too degenerate or the convexity constraints are\n\ +too strong.\n", qh->hull_dim+1); + if (qh->hull_dim >= 5 && !qh->MERGEexact) + qh_fprintf(qh, qh->ferr, 8079, "Option 'Qx' may avoid this problem.\n"); + qh_errexit(qh, qh_ERRprec, NULL, NULL); + } + if (!qh->VERTEXneighbors) + qh_vertexneighbors(qh); + qh_makeridges(qh, facet1); + qh_makeridges(qh, facet2); + if (qh->IStracing >=4) + qh_errprint(qh, "MERGING", facet1, facet2, NULL, NULL); + if (mindist) { + maximize_(qh->max_outside, *maxdist); + maximize_(qh->max_vertex, *maxdist); +#if qh_MAXoutside + maximize_(facet2->maxoutside, *maxdist); +#endif + minimize_(qh->min_vertex, *mindist); + if (!facet2->keepcentrum + && (*maxdist > qh->WIDEfacet || *mindist < -qh->WIDEfacet)) { + facet2->keepcentrum= True; + zinc_(Zwidefacet); + } + } + nummerge= facet1->nummerge + facet2->nummerge + 1; + if (nummerge >= qh_MAXnummerge) + facet2->nummerge= qh_MAXnummerge; + else + facet2->nummerge= (short unsigned int)nummerge; + facet2->newmerge= True; + facet2->dupridge= False; + qh_updatetested(qh, facet1, facet2); + if (qh->hull_dim > 2 && qh_setsize(qh, facet1->vertices) == qh->hull_dim) + qh_mergesimplex(qh, facet1, facet2, mergeapex); + else { + qh->vertex_visit++; + FOREACHvertex_(facet2->vertices) + vertex->visitid= qh->vertex_visit; + if (qh->hull_dim == 2) + qh_mergefacet2d(qh, facet1, facet2); + else { + qh_mergeneighbors(qh, facet1, facet2); + qh_mergevertices(qh, facet1->vertices, &facet2->vertices); + } + qh_mergeridges(qh, facet1, facet2); + qh_mergevertex_neighbors(qh, facet1, facet2); + if (!facet2->newfacet) + qh_newvertices(qh, facet2->vertices); + } + if (!mergeapex) + qh_degen_redundant_neighbors(qh, facet2, facet1); + if (facet2->coplanar || !facet2->newfacet) { + zinc_(Zmergeintohorizon); + }else if (!facet1->newfacet && facet2->newfacet) { + zinc_(Zmergehorizon); + }else { + zinc_(Zmergenew); + } + qh_willdelete(qh, facet1, facet2); + qh_removefacet(qh, facet2); /* append as a newfacet to end of qh->facet_list */ + qh_appendfacet(qh, facet2); + facet2->newfacet= True; + facet2->tested= False; + qh_tracemerge(qh, facet1, facet2); + if (traceonce) { + qh_fprintf(qh, qh->ferr, 8080, "qh_mergefacet: end of wide tracing\n"); + qh->IStracing= tracerestore; + } +} /* mergefacet */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergefacet2d">-</a> + + qh_mergefacet2d(qh, 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(qhT *qh, 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(qh, neighborB); + qh_setreplace(qh, neighborB->neighbors, facet1, facet2); + trace4((qh, qh->ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n", + vertexA->id, neighborB->id, facet1->id, facet2->id)); +} /* mergefacet2d */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergeneighbors">-</a> + + qh_mergeneighbors(qh, 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(qhT *qh, facetT *facet1, facetT *facet2) { + facetT *neighbor, **neighborp; + + trace4((qh, qh->ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n", + facet1->id, facet2->id)); + qh->visit_id++; + FOREACHneighbor_(facet2) { + neighbor->visitid= qh->visit_id; + } + FOREACHneighbor_(facet1) { + if (neighbor->visitid == qh->visit_id) { + if (neighbor->simplicial) /* is degen, needs ridges */ + qh_makeridges(qh, neighbor); + if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/ + qh_setdel(neighbor->neighbors, facet1); + else { + qh_setdel(neighbor->neighbors, facet2); + qh_setreplace(qh, neighbor->neighbors, facet1, facet2); + } + }else if (neighbor != facet2) { + qh_setappend(qh, &(facet2->neighbors), neighbor); + qh_setreplace(qh, neighbor->neighbors, facet1, facet2); + } + } + qh_setdel(facet1->neighbors, facet2); /* here for makeridges */ + qh_setdel(facet2->neighbors, facet1); +} /* mergeneighbors */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergeridges">-</a> + + qh_mergeridges(qh, 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(qhT *qh, facetT *facet1, facetT *facet2) { + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + + trace4((qh, qh->ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n", + facet1->id, facet2->id)); + FOREACHridge_(facet2->ridges) { + if ((ridge->top == facet1) || (ridge->bottom == facet1)) { + FOREACHvertex_(ridge->vertices) + vertex->delridge= True; + qh_delridge(qh, 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(qh, &(facet2->ridges), ridge); + } +} /* mergeridges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergesimplex">-</a> + + qh_mergesimplex(qh, 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(qhT *qh, 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(qh, facet2->vertices); /* apex is new */ + apex= SETfirstt_(facet1->vertices, vertexT); + if (SETfirstt_(facet2->vertices, vertexT) != apex) + qh_setaddnth(qh, &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, qh->ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n", + apex->id, facet1->id, facet2->id)); + FOREACHvertex_i_(qh, facet2->vertices) { + if (vertex->id < apex->id) { + break; + }else if (vertex->id == apex->id) { + issubset= True; + break; + } + } + if (!issubset) + qh_setaddnth(qh, &facet2->vertices, vertex_i, apex); + if (!facet2->newfacet) + qh_newvertices(qh, facet2->vertices); + else if (!apex->newlist) { + qh_removevertex(qh, apex); + qh_appendvertex(qh, apex); + } + } + trace4((qh, qh->ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n", + facet1->id)); + FOREACHvertex_(facet1->vertices) { + if (vertex == apex && !issubset) + qh_setreplace(qh, vertex->neighbors, facet1, facet2); + else { + qh_setdel(vertex->neighbors, facet1); + if (!SETsecond_(vertex->neighbors)) + qh_mergevertex_del(qh, vertex, facet1, facet2); + } + } + trace4((qh, qh->ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n", + facet1->id, facet2->id)); + qh->visit_id++; + FOREACHneighbor_(facet2) + neighbor->visitid= qh->visit_id; + FOREACHridge_(facet1->ridges) { + otherfacet= otherfacet_(ridge, facet1); + if (otherfacet == facet2) { + qh_setdel(facet2->ridges, ridge); + qh_setfree(qh, &(ridge->vertices)); + qh_memfree(qh, ridge, (int)sizeof(ridgeT)); + qh_setdel(facet2->neighbors, facet1); + }else { + qh_setappend(qh, &facet2->ridges, ridge); + if (otherfacet->visitid != qh->visit_id) { + qh_setappend(qh, &facet2->neighbors, otherfacet); + qh_setreplace(qh, otherfacet->neighbors, facet1, facet2); + otherfacet->visitid= qh->visit_id; + }else { + if (otherfacet->simplicial) /* is degen, needs ridges */ + qh_makeridges(qh, otherfacet); + if (SETfirstt_(otherfacet->neighbors, facetT) != facet1) + qh_setdel(otherfacet->neighbors, facet1); + else { /*keep newfacet->neighbors->horizon*/ + qh_setdel(otherfacet->neighbors, facet2); + qh_setreplace(qh, 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, qh->ferr, 3006, "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_r.htm#TOC" + >-------------------------------</a><a name="mergevertex_del">-</a> + + qh_mergevertex_del(qh, 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(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2) { + + zinc_(Zmergevertex); + trace2((qh, qh->ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n", + vertex->id, facet1->id, facet2->id)); + qh_setdelsorted(facet2->vertices, vertex); + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); +} /* mergevertex_del */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergevertex_neighbors">-</a> + + qh_mergevertex_neighbors(qh, 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(qhT *qh, facetT *facet1, facetT *facet2) { + vertexT *vertex, **vertexp; + + trace4((qh, qh->ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n", + facet1->id, facet2->id)); + if (qh->tracevertex) { + qh_fprintf(qh, qh->ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n", + facet1->id, facet2->id, qh->furthest_id, qh->tracevertex->neighbors->e[0].p); + qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex); + } + FOREACHvertex_(facet1->vertices) { + if (vertex->visitid != qh->vertex_visit) + qh_setreplace(qh, vertex->neighbors, facet1, facet2); + else { + qh_setdel(vertex->neighbors, facet1); + if (!SETsecond_(vertex->neighbors)) + qh_mergevertex_del(qh, vertex, facet1, facet2); + } + } + if (qh->tracevertex) + qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex); +} /* mergevertex_neighbors */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergevertices">-</a> + + qh_mergevertices(qh, 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(qhT *qh, setT *vertices1, setT **vertices2) { + int newsize= qh_setsize(qh, vertices1)+qh_setsize(qh, *vertices2) - qh->hull_dim + 1; + setT *mergedvertices; + vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT); + + mergedvertices= qh_settemp(qh, newsize); + FOREACHvertex_(vertices1) { + if (!*vertex2 || vertex->id > (*vertex2)->id) + qh_setappend(qh, &mergedvertices, vertex); + else { + while (*vertex2 && (*vertex2)->id > vertex->id) + qh_setappend(qh, &mergedvertices, *vertex2++); + if (!*vertex2 || (*vertex2)->id < vertex->id) + qh_setappend(qh, &mergedvertices, vertex); + else + qh_setappend(qh, &mergedvertices, *vertex2++); + } + } + while (*vertex2) + qh_setappend(qh, &mergedvertices, *vertex2++); + if (newsize < qh_setsize(qh, mergedvertices)) { + qh_fprintf(qh, qh->ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_setfree(qh, vertices2); + *vertices2= mergedvertices; + qh_settemppop(qh); +} /* mergevertices */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="neighbor_intersections">-</a> + + qh_neighbor_intersections(qh, 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(qhT *qh, 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(qh, neighborA->vertices, 0); + else + intersect= qh_vertexintersect_new(qh, neighborA->vertices, neighborB->vertices); + qh_settemppush(qh, intersect); + qh_setdelsorted(intersect, vertex); + FOREACHneighbor_i_(qh, vertex) { + if (neighbor_i >= 2) { + zinc_(Zintersectnum); + qh_vertexintersect(qh, &intersect, neighbor->vertices); + if (!SETfirst_(intersect)) { + zinc_(Zintersectfail); + qh_settempfree(qh, &intersect); + return NULL; + } + } + } + trace3((qh, qh->ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n", + qh_setsize(qh, intersect), vertex->id)); + return intersect; +} /* neighbor_intersections */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="newvertices">-</a> + + qh_newvertices(qh, 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(qhT *qh, setT *vertices) { + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newlist) { + qh_removevertex(qh, vertex); + qh_appendvertex(qh, vertex); + } + } +} /* newvertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="reducevertices">-</a> + + qh_reducevertices(qh) + 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(qhT *qh) { + int numshare=0, numrename= 0; + boolT degenredun= False; + facetT *newfacet; + vertexT *vertex, **vertexp; + + if (qh->hull_dim == 2) + return False; + if (qh_merge_degenredundant(qh)) + degenredun= True; + LABELrestart: + FORALLnew_facets { + if (newfacet->newmerge) { + if (!qh->MERGEvertices) + newfacet->newmerge= False; + qh_remove_extravertices(qh, newfacet); + } + } + if (!qh->MERGEvertices) + return False; + FORALLnew_facets { + if (newfacet->newmerge) { + newfacet->newmerge= False; + FOREACHvertex_(newfacet->vertices) { + if (vertex->delridge) { + if (qh_rename_sharedvertex(qh, 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(qh, vertex)) { + numrename++; + if (qh_merge_degenredundant(qh)) { + degenredun= True; + goto LABELrestart; + } + } + } + } + trace1((qh, qh->ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n", + numshare, numrename, degenredun)); + return degenredun; +} /* reducevertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="redundant_vertex">-</a> + + qh_redundant_vertex(qh, 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(qhT *qh, vertexT *vertex) { + vertexT *newvertex= NULL; + setT *vertices, *ridges; + + trace3((qh, qh->ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id)); + if ((vertices= qh_neighbor_intersections(qh, vertex))) { + ridges= qh_vertexridges(qh, vertex); + if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges))) + qh_renamevertex(qh, vertex, newvertex, ridges, NULL, NULL); + qh_settempfree(qh, &ridges); + qh_settempfree(qh, &vertices); + } + return newvertex; +} /* redundant_vertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="remove_extravertices">-</a> + + qh_remove_extravertices(qh, 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(qhT *qh, facetT *facet) { + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + boolT foundrem= False; + + trace4((qh, qh->ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n", + facet->id)); + FOREACHvertex_(facet->vertices) + vertex->seen= False; + FOREACHridge_(facet->ridges) { + FOREACHvertex_(ridge->vertices) + vertex->seen= True; + } + FOREACHvertex_(facet->vertices) { + if (!vertex->seen) { + foundrem= True; + zinc_(Zremvertex); + qh_setdelsorted(facet->vertices, vertex); + qh_setdel(vertex->neighbors, facet); + if (!qh_setsize(qh, vertex->neighbors)) { + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + zinc_(Zremvertexdel); + trace2((qh, qh->ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id)); + }else + trace3((qh, qh->ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id)); + vertexp--; /*repeat*/ + } + } + return foundrem; +} /* remove_extravertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="rename_sharedvertex">-</a> + + qh_rename_sharedvertex(qh, 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(qhT *qh, vertexT *vertex, facetT *facet) { + facetT *neighbor, **neighborp, *neighborA= NULL; + setT *vertices, *ridges; + vertexT *newvertex; + + if (qh_setsize(qh, vertex->neighbors) == 2) { + neighborA= SETfirstt_(vertex->neighbors, facetT); + if (neighborA == facet) + neighborA= SETsecondt_(vertex->neighbors, facetT); + }else if (qh->hull_dim == 3) + return NULL; + else { + qh->visit_id++; + FOREACHneighbor_(facet) + neighbor->visitid= qh->visit_id; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == qh->visit_id) { + if (neighborA) + return NULL; + neighborA= neighbor; + } + } + if (!neighborA) { + qh_fprintf(qh, qh->ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n", + vertex->id, facet->id); + qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, vertex); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + } + /* the vertex is shared by facet and neighborA */ + ridges= qh_settemp(qh, qh->TEMPsize); + neighborA->visitid= ++qh->visit_id; + qh_vertexridges_facet(qh, vertex, facet, &ridges); + trace2((qh, qh->ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n", + qh_pointid(qh, vertex->point), vertex->id, facet->id, qh_setsize(qh, ridges), neighborA->id)); + zinc_(Zintersectnum); + vertices= qh_vertexintersect_new(qh, facet->vertices, neighborA->vertices); + qh_setdel(vertices, vertex); + qh_settemppush(qh, vertices); + if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges))) + qh_renamevertex(qh, vertex, newvertex, ridges, facet, neighborA); + qh_settempfree(qh, &vertices); + qh_settempfree(qh, &ridges); + return newvertex; +} /* rename_sharedvertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="renameridgevertex">-</a> + + qh_renameridgevertex(qh, 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(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) { + int nth= 0, oldnth; + facetT *temp; + vertexT *vertex, **vertexp; + + oldnth= qh_setindex(ridge->vertices, oldvertex); + qh_setdelnthsorted(qh, ridge->vertices, oldnth); + FOREACHvertex_(ridge->vertices) { + if (vertex == newvertex) { + zinc_(Zdelridge); + if (ridge->nonconvex) /* only one ridge has nonconvex set */ + qh_copynonconvex(qh, ridge); + trace2((qh, qh->ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n", + ridge->id, oldvertex->id, newvertex->id)); + qh_delridge(qh, ridge); + return; + } + if (vertex->id < newvertex->id) + break; + nth++; + } + qh_setaddnth(qh, &ridge->vertices, nth, newvertex); + if (abs(oldnth - nth)%2) { + trace3((qh, qh->ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n", + ridge->id)); + temp= ridge->top; + ridge->top= ridge->bottom; + ridge->bottom= temp; + } +} /* renameridgevertex */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="renamevertex">-</a> + + qh_renamevertex(qh, 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(qhT *qh, 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(qh, ridge, oldvertex, newvertex); + if (!oldfacet) { + zinc_(Zrenameall); + if (istrace) + qh_fprintf(qh, qh->ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n", + oldvertex->id, newvertex->id); + FOREACHneighbor_(oldvertex) { + qh_maydropneighbor(qh, neighbor); + qh_setdelsorted(neighbor->vertices, oldvertex); + if (qh_remove_extravertices(qh, neighbor)) + neighborp--; /* neighbor may be deleted */ + } + if (!oldvertex->deleted) { + oldvertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, oldvertex); + } + }else if (qh_setsize(qh, oldvertex->neighbors) == 2) { + zinc_(Zrenameshare); + if (istrace) + qh_fprintf(qh, qh->ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n", + oldvertex->id, newvertex->id, oldfacet->id); + FOREACHneighbor_(oldvertex) + qh_setdelsorted(neighbor->vertices, oldvertex); + oldvertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, oldvertex); + }else { + zinc_(Zrenamepinch); + if (istrace || qh->IStracing) + qh_fprintf(qh, qh->ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n", + oldvertex->id, newvertex->id, oldfacet->id, neighborA->id); + qh_setdelsorted(oldfacet->vertices, oldvertex); + qh_setdel(oldvertex->neighbors, oldfacet); + qh_remove_extravertices(qh, neighborA); + } +} /* renamevertex */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="test_appendmerge">-</a> + + qh_test_appendmerge(qh, 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(qhT *qh, 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(qh, facet->normal, neighbor->normal); + zinc_(Zangletests); + if (angle > qh->cos_max) { + zinc_(Zcoplanarangle); + qh_appendmergeset(qh, facet, neighbor, MRGanglecoplanar, &angle); + trace2((qh, qh->ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n", + angle, facet->id, neighbor->id)); + return True; + }else + okangle= True; + } + if (!facet->center) + facet->center= qh_getcentrum(qh, facet); + zzinc_(Zcentrumtests); + qh_distplane(qh, 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(qh, neighbor); + zzinc_(Zcentrumtests); + qh_distplane(qh, 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(qh, facet->normal, neighbor->normal); + zinc_(Zangletests); + } + if (isconcave) { + zinc_(Zconcaveridge); + if (qh->ANGLEmerge) + angle += qh_ANGLEconcave + 0.5; + qh_appendmergeset(qh, facet, neighbor, MRGconcave, &angle); + trace0((qh, qh->ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n", + facet->id, neighbor->id, dist, dist2, angle, qh->furthest_id)); + }else /* iscoplanar */ { + zinc_(Zcoplanarcentrum); + qh_appendmergeset(qh, facet, neighbor, MRGcoplanar, &angle); + trace2((qh, qh->ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n", + facet->id, neighbor->id, dist, dist2, angle)); + } + return True; +} /* test_appendmerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="test_vneighbors">-</a> + + qh_test_vneighbors(qh) + 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(qhT *qh /* qh->newfacet_list */) { + facetT *newfacet, *neighbor, **neighborp; + vertexT *vertex, **vertexp; + int nummerges= 0; + + trace1((qh, qh->ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n")); + if (!qh->VERTEXneighbors) + qh_vertexneighbors(qh); + 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(qh, newfacet, neighbor)) + nummerges++; + } + } + } + zadd_(Ztestvneighbor, nummerges); + trace1((qh, qh->ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n", + nummerges)); + return (nummerges > 0); +} /* test_vneighbors */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="tracemerge">-</a> + + qh_tracemerge(qh, facet1, facet2 ) + print trace message after merge +*/ +void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2) { + boolT waserror= False; + +#ifndef qh_NOtrace + if (qh->IStracing >= 4) + qh_errprint(qh, "MERGED", facet2, NULL, NULL, NULL); + if (facet2 == qh->tracefacet || (qh->tracevertex && qh->tracevertex->newlist)) { + qh_fprintf(qh, qh->ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh->furthest_id); + if (facet2 != qh->tracefacet) + qh_errprint(qh, "TRACE", qh->tracefacet, + (qh->tracevertex && qh->tracevertex->neighbors) ? + SETfirstt_(qh->tracevertex->neighbors, facetT) : NULL, + NULL, qh->tracevertex); + } + if (qh->tracevertex) { + if (qh->tracevertex->deleted) + qh_fprintf(qh, qh->ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n", + qh->furthest_id); + else + qh_checkvertex(qh, qh->tracevertex); + } + if (qh->tracefacet) { + qh_checkfacet(qh, qh->tracefacet, True, &waserror); + if (waserror) + qh_errexit(qh, qh_ERRqhull, qh->tracefacet, NULL); + } +#endif /* !qh_NOtrace */ + if (qh->CHECKfrequently || qh->IStracing >= 4) { /* can't check polygon here */ + qh_checkfacet(qh, facet2, True, &waserror); + if (waserror) + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +} /* tracemerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="tracemerging">-</a> + + qh_tracemerging(qh) + print trace message during POSTmerging + + returns: + updates qh.mergereport + + notes: + called from qh_mergecycle() and qh_mergefacet() + + see: + qh_buildtracing() +*/ +void qh_tracemerging(qhT *qh) { + realT cpu; + int total; + time_t timedata; + struct tm *tp; + + qh->mergereport= zzval_(Ztotmerge); + time(&timedata); + tp= localtime(&timedata); + cpu= qh_CPUclock; + cpu /= qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + qh_fprintf(qh, qh->ferr, 8087, "\n\ +At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\ + contains %d facets and %d vertices.\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, + total, qh->num_facets - qh->num_visible, + qh->num_vertices-qh_setsize(qh, qh->del_vertices)); +} /* tracemerging */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="updatetested">-</a> + + qh_updatetested(qh, 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(qhT *qh, 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(qh, 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(qh, facet2->center, qh->normal_size); + facet2->center= NULL; + FOREACHridge_(facet2->ridges) + ridge->tested= False; + } +} /* updatetested */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="vertexridges">-</a> + + qh_vertexridges(qh, 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(qhT *qh, vertexT *vertex) { + facetT *neighbor, **neighborp; + setT *ridges= qh_settemp(qh, 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(qh, vertex, neighbor, &ridges); + } + if (qh->PRINTstatistics || qh->IStracing) { + size= qh_setsize(qh, ridges); + zinc_(Zvertexridge); + zadd_(Zvertexridgetot, size); + zmax_(Zvertexridgemax, size); + trace3((qh, qh->ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n", + size, vertex->id)); + } + return ridges; +} /* vertexridges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="vertexridges_facet">-</a> + + qh_vertexridges_facet(qh, 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(qhT *qh, 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(qh, ridges, ridge); + } + facet->visitid= qh->visit_id-1; +} /* vertexridges_facet */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="willdelete">-</a> + + qh_willdelete(qh, facet, replace ) + moves facet to visible list + sets facet->f.replace to replace (may be NULL) + + returns: + bumps qh.num_visible +*/ +void qh_willdelete(qhT *qh, facetT *facet, facetT *replace) { + + qh_removefacet(qh, facet); + qh_prependfacet(qh, facet, &qh->visible_list); + qh->num_visible++; + facet->visible= True; + facet->f.replace= replace; +} /* willdelete */ + +#else /* qh_NOmerge */ +void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) { +} +void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { +} +boolT qh_checkzero(qhT *qh, boolT testall) { + } +#endif /* qh_NOmerge */ +
+ C/merge_r.h view
@@ -0,0 +1,186 @@+/*<html><pre> -<a href="qh-merge_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + merge_r.h + header file for merge_r.c + + see qh-merge_r.htm and merge_r.c + + Copyright (c) 1993-2015 C.B. Barber. + $Id: //main/2015/qhull/src/libqhull_r/merge_r.h#3 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ +*/ + +#ifndef qhDEFmerge +#define qhDEFmerge 1 + +#include "libqhull_r.h" + + +/*============ -constants- ==============*/ + +/*-<a href="qh-merge_r.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_r.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_r.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_r.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 (!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_r.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_r.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_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge) + +/*============ prototypes in alphabetical order after pre/postmerge =======*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle); +void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors); +void qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors); +void qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle); +setT *qh_basevertices(qhT *qh, facetT *samecycle); +void qh_checkconnect(qhT *qh /* qh.new_facets */); +boolT qh_checkzero(qhT *qh, 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(qhT *qh, ridgeT *atridge); +void qh_degen_redundant_facet(qhT *qh, facetT *facet); +void qh_degen_redundant_neighbors(qhT *qh, facetT *facet, facetT *delfacet); +vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges); +void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor, + facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp); +facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp); +void qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge); +void qh_forcedmerges(qhT *qh, boolT *wasmerge); +void qh_getmergeset(qhT *qh, facetT *facetlist); +void qh_getmergeset_initial(qhT *qh, facetT *facetlist); +void qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex); +ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, + vertexT *vertex, vertexT *oldvertex, int *hashslot); +void qh_makeridges(qhT *qh, facetT *facet); +void qh_mark_dupridges(qhT *qh, facetT *facetlist); +void qh_maydropneighbor(qhT *qh, facetT *facet); +int qh_merge_degenredundant(qhT *qh); +void qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype); +void qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge); +void qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex); +void qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex); +void qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2); +void qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices); +setT *qh_neighbor_intersections(qhT *qh, vertexT *vertex); +void qh_newvertices(qhT *qh, setT *vertices); +boolT qh_reducevertices(qhT *qh); +vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex); +boolT qh_remove_extravertices(qhT *qh, facetT *facet); +vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet); +void qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex); +void qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges, + facetT *oldfacet, facetT *neighborA); +boolT qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor); +boolT qh_test_vneighbors(qhT *qh /* qh.newfacet_list */); +void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2); +void qh_tracemerging(qhT *qh); +void qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2); +setT *qh_vertexridges(qhT *qh, vertexT *vertex); +void qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges); +void qh_willdelete(qhT *qh, facetT *facet, facetT *replace); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFmerge */
+ C/poly2_r.c view
@@ -0,0 +1,3222 @@+/*<html><pre> -<a href="qh-poly_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly2_r.c + implements polygons and simplicies + + see qh-poly_r.htm, poly_r.h and libqhull_r.h + + frequently used code is in poly_r.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/poly2_r.c#10 $$Change: 2069 $ + $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*======== functions in alphabetical order ==========*/ + +/*-<a href="qh-poly_r.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, int hash) { + int scan; + void *elem; + + for (scan= (int)hash; (elem= SETelem_(hashtable, scan)); + scan= (++scan >= hashsize ? 0 : scan)) { + if (elem == newelem) + break; + } + /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */ + if (!elem) + SETelem_(hashtable, scan)= newelem; +} /* addhash */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_bestdist">-</a> + + qh_check_bestdist(qh) + 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(qhT *qh) { + 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, qh->ferr, 1020, "qh_check_bestdist: check points below nearest facet. Facet_list f%d\n", + qh->facet_list->id)); + maxoutside= qh_maxouter(qh); + maxoutside += qh->DISTround; + /* one more qh.DISTround for check computation */ + trace1((qh, qh->ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside)); + facets= qh_pointfacet(qh /*qh.facet_list*/); + if (!qh_QUICKhelp && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 8091, "\n\ +qhull output completed. Verifying that %d points are\n\ +below %2.2g of the nearest %sfacet.\n", + qh_setsize(qh, facets), maxoutside, (qh->ONLYgood ? "good " : "")); + FOREACHfacet_i_(qh, facets) { /* for each point with facet assignment */ + if (facet) + unassigned= False; + else { + unassigned= True; + facet= qh->facet_list; + } + point= qh_point(qh, facet_i); + if (point == qh->GOODpointp) + continue; + qh_distplane(qh, point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(qh, !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(qh, point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) + notgood++; + else { + waserror= True; + qh_fprintf(qh, qh->ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n", + facet_i, bestfacet->id, dist, maxoutside); + if (errfacet1 != bestfacet) { + errfacet2= errfacet1; + errfacet1= bestfacet; + } + } + }else if (unassigned && dist < -qh->MAXcoplanar) + notverified++; + } + qh_settempfree(qh, &facets); + if (notverified && !qh->DELAUNAY && !qh_QUICKhelp && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 8092, "\n%d points were well inside the hull. If the hull contains\n\ +a lens-shaped component, these points were not verified. Use\n\ +options 'Qci Tv' to verify all points.\n", notverified); + if (maxdist > qh->outside_err) { + qh_fprintf(qh, qh->ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n", + maxdist, qh->outside_err); + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2); + }else if (waserror && qh->outside_err > REALmax/2) + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2); + /* else if waserror, the error was logged to qh.ferr but does not effect the output */ + trace0((qh, qh->ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist)); +} /* check_bestdist */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_dupridge">-</a> + + qh_check_dupridge(qh, facet1, dist1, facet2, dist2) + Check duplicate ridge between facet1 and facet2 for wide merge + dist1 is the maximum distance of facet1's vertices to facet2 + dist2 is the maximum distance of facet2's vertices to facet1 + + Returns + Level 1 log of the duplicate ridge with the minimum distance between vertices + Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x) + + called from: + qh_forcedmerges() +*/ +#ifndef qh_NOmerge +void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2) { + vertexT *vertex, **vertexp, *vertexA, **vertexAp; + realT dist, innerplane, mergedist, outerplane, prevdist, ratio; + realT minvertex= REALmax; + + mergedist= fmin_(dist1, dist2); + qh_outerinner(qh, NULL, &outerplane, &innerplane); /* ratio from qh_printsummary */ + prevdist= fmax_(outerplane, innerplane); + maximize_(prevdist, qh->ONEmerge + qh->DISTround); + maximize_(prevdist, qh->MINoutside + qh->DISTround); + ratio= mergedist/prevdist; + FOREACHvertex_(facet1->vertices) { /* The duplicate ridge is between facet1 and facet2, so either facet can be tested */ + FOREACHvertexA_(facet1->vertices) { + if (vertex > vertexA){ /* Test each pair once */ + dist= qh_pointdist(vertex->point, vertexA->point, qh->hull_dim); + minimize_(minvertex, dist); + } + } + } + trace0((qh, qh->ferr, 16, "qh_check_dupridge: duplicate ridge between f%d and f%d due to nearly-coincident vertices (%2.2g), dist %2.2g, reverse dist %2.2g, ratio %2.2g while processing p%d\n", + facet1->id, facet2->id, minvertex, dist1, dist2, ratio, qh->furthest_id)); + if (ratio > qh_WIDEduplicate) { + qh_fprintf(qh, qh->ferr, 6271, "qhull precision error (qh_check_dupridge): wide merge (%.0f times wider) due to duplicate ridge with nearly coincident points (%2.2g) between f%d and f%d, merge dist %2.2g, while processing p%d\n- Ignore error with option 'Q12'\n- To be fixed in a later version of Qhull\n", + ratio, minvertex, facet1->id, facet2->id, mergedist, qh->furthest_id); + if (qh->DELAUNAY) + qh_fprintf(qh, qh->ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n"); + if(minvertex > qh_WIDEduplicate*prevdist) + qh_fprintf(qh, qh->ferr, 8146, "- Vertex distance %2.2g is greater than %d times maximum distance %2.2g\n Please report to bradb@shore.net with steps to reproduce and all output\n", + minvertex, qh_WIDEduplicate, prevdist); + if (!qh->NOwide) + qh_errexit2(qh, qh_ERRqhull, facet1, facet2); + } +} /* check_dupridge */ +#endif + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_maxout">-</a> + + qh_check_maxout(qh) + 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(qhT *qh) { + 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, qh->ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n")); + maxoutside= minvertex= 0; + if (qh->VERTEXneighbors + && (qh->PRINTsummary || qh->KEEPinside || qh->KEEPcoplanar + || qh->TRACElevel || qh->PRINTstatistics + || qh->PRINTout[0] == qh_PRINTsummary || qh->PRINTout[0] == qh_PRINTnone)) { + trace1((qh, qh->ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n")); + vertices= qh_pointvertex(qh /*qh.facet_list*/); + FORALLvertices { + FOREACHneighbor_(vertex) { + zinc_(Zdistvertex); /* distance also computed by main loop below */ + qh_distplane(qh, vertex->point, neighbor, &dist); + minimize_(minvertex, dist); + if (-dist > qh->TRACEdist || dist > qh->TRACEdist + || neighbor == qh->tracefacet || vertex == qh->tracevertex) + qh_fprintf(qh, qh->ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n", + qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id); + } + } + if (qh->MERGING) { + wmin_(Wminvertex, qh->min_vertex); + } + qh->min_vertex= minvertex; + qh_settempfree(qh, &vertices); + } + facets= qh_pointfacet(qh /*qh.facet_list*/); + do { + old_maxoutside= fmax_(qh->max_outside, maxoutside); + FOREACHfacet_i_(qh, facets) { /* for each point with facet assignment */ + if (facet) { + point= qh_point(qh, facet_i); + if (point == qh->GOODpointp) + continue; + zzinc_(Ztotcheck); + qh_distplane(qh, point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(qh, qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart); + if (bestfacet && dist > maxoutside) { + if (qh->ONLYgood && !bestfacet->good + && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) + notgood++; + else + maxoutside= dist; + } + if (dist > qh->TRACEdist || (bestfacet && bestfacet == qh->tracefacet)) + qh_fprintf(qh, qh->ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n", + qh_pointid(qh, point), dist, (bestfacet ? bestfacet->id : UINT_MAX)); + } + } + }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(qh, &facets); + wval_(Wmaxout)= maxoutside - qh->max_outside; + wmax_(Wmaxoutside, qh->max_outside); + qh->max_outside= maxoutside; + qh_nearcoplanar(qh /*qh.facet_list*/); + qh->maxoutdone= True; + trace1((qh, qh->ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n", + maxoutside, qh->min_vertex, notgood)); +} /* check_maxout */ +#else /* qh_NOmerge */ +void qh_check_maxout(qhT *qh) { +} +#endif + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_output">-</a> + + qh_check_output(qh) + 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(qhT *qh) { + int i; + + if (qh->STOPcone) + return; + if (qh->VERIFYoutput | qh->IStracing | qh->CHECKfrequently) { + qh_checkpolygon(qh, qh->facet_list); + qh_checkflipped_all(qh, qh->facet_list); + qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault); + }else if (!qh->MERGING && qh_newstats(qh, qh->qhstat.precision, &i)) { + qh_checkflipped_all(qh, qh->facet_list); + qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault); + } +} /* check_output */ + + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_point">-</a> + + qh_check_point(qh, point, facet, maxoutside, maxdist, errfacet1, errfacet2 ) + check that point is less than maxoutside from facet +*/ +void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) { + realT dist; + + /* occurs after statistics reported */ + qh_distplane(qh, point, facet, &dist); + if (dist > *maxoutside) { + if (*errfacet1 != facet) { + *errfacet2= *errfacet1; + *errfacet1= facet; + } + qh_fprintf(qh, qh->ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n", + qh_pointid(qh, point), facet->id, dist, *maxoutside); + } + maximize_(*maxdist, dist); +} /* qh_check_point */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_points">-</a> + + qh_check_points(qh) + 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(qhT *qh) { + facetT *facet, *errfacet1= NULL, *errfacet2= NULL; + realT total, maxoutside, maxdist= -REALmax; + pointT *point, **pointp, *pointtemp; + boolT testouter; + + maxoutside= qh_maxouter(qh); + maxoutside += qh->DISTround; + /* one more qh.DISTround for check computation */ + trace1((qh, qh->ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n", + maxoutside)); + if (qh->num_good) /* miss counts other_points and !good facets */ + total= (float)qh->num_good * (float)qh->num_points; + else + total= (float)qh->num_facets * (float)qh->num_points; + if (total >= qh_VERIFYdirect && !qh->maxoutdone) { + if (!qh_QUICKhelp && qh->SKIPcheckmax && qh->MERGING) + qh_fprintf(qh, qh->ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\ +Verify may report that a point is outside of a facet.\n"); + qh_check_bestdist(qh); + }else { + if (qh_MAXoutside && qh->maxoutdone) + testouter= True; + else + testouter= False; + if (!qh_QUICKhelp) { + if (qh->MERGEexact) + qh_fprintf(qh, qh->ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point\n\ +is outside of a facet. See qh-optq.htm#Qx\n"); + else if (qh->SKIPcheckmax || qh->NOnearinside) + qh_fprintf(qh, qh->ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\ +near-inside points ('Q8'). Verify may report that a point is outside\n\ +of a facet.\n"); + } + if (qh->PRINTprecision) { + if (testouter) + qh_fprintf(qh, qh->ferr, 8098, "\n\ +Output completed. Verifying that all points are below outer planes of\n\ +all %sfacets. Will make %2.0f distance computations.\n", + (qh->ONLYgood ? "good " : ""), total); + else + qh_fprintf(qh, qh->ferr, 8099, "\n\ +Output completed. Verifying that all points are below %2.2g of\n\ +all %sfacets. Will make %2.0f distance computations.\n", + maxoutside, (qh->ONLYgood ? "good " : ""), total); + } + FORALLfacets { + if (!facet->good && qh->ONLYgood) + continue; + if (facet->flipped) + continue; + if (!facet->normal) { + qh_fprintf(qh, qh->ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id); + continue; + } + if (testouter) { +#if qh_MAXoutside + maxoutside= facet->maxoutside + 2* qh->DISTround; + /* one DISTround to actual point and another to computed point */ +#endif + } + FORALLpoints { + if (point != qh->GOODpointp) + qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2); + } + FOREACHpoint_(qh->other_points) { + if (point != qh->GOODpointp) + qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2); + } + } + if (maxdist > qh->outside_err) { + qh_fprintf(qh, qh->ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n", + maxdist, qh->outside_err ); + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 ); + }else if (errfacet1 && qh->outside_err > REALmax/2) + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 ); + /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */ + trace0((qh, qh->ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist)); + } +} /* check_points */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkconvex">-</a> + + qh_checkconvex(qh, 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(qhT *qh, 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, qh->ferr, 1026, "qh_checkconvex: check all ridges are convex\n")); + if (!qh->RERUN) { + zzval_(Zconcaveridges)= 0; + zzval_(Zcoplanarridges)= 0; + } + FORALLfacet_(facetlist) { + if (facet->flipped) { + qh_precision(qh, "flipped facet"); + qh_fprintf(qh, qh->ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n", + facet->id); + errfacet1= facet; + waserror= True; + continue; + } + if (qh->MERGING && (!qh->ZEROcentrum || !facet->simplicial || facet->tricoplanar)) + allsimplicial= False; + else { + allsimplicial= True; + neighbor_i= 0; + FOREACHneighbor_(facet) { + vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT); + if (!neighbor->simplicial || neighbor->tricoplanar) { + allsimplicial= False; + continue; + } + qh_distplane(qh, vertex->point, neighbor, &dist); + if (dist > -qh->DISTround) { + if (fault == qh_DATAfault) { + qh_precision(qh, "coplanar or concave ridge"); + qh_fprintf(qh, qh->ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist); + qh_errexit(qh, qh_ERRsingular, NULL, NULL); + } + if (dist > qh->DISTround) { + zzinc_(Zconcaveridges); + qh_precision(qh, "concave ridge"); + qh_fprintf(qh, qh->ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n", + facet->id, neighbor->id, qh_pointid(qh, 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(qh, "coplanar ridge"); + qh_fprintf(qh, qh->ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n", + facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + } + }else { + zzinc_(Zcoplanarridges); + qh_precision(qh, "coplanar ridge"); + trace0((qh, qh->ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n", + facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist, qh->furthest_id)); + } + } + } + } + if (!allsimplicial) { + if (qh->CENTERtype == qh_AScentrum) { + if (!facet->center) + facet->center= qh_getcentrum(qh, facet); + centrum= facet->center; + }else { + if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) { + centrum_warning= True; + qh_fprintf(qh, qh->ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n"); + } + centrum= qh_getcentrum(qh, facet); + tempcentrum= True; + } + FOREACHneighbor_(facet) { + if (qh->ZEROcentrum && facet->simplicial && neighbor->simplicial) + continue; + if (facet->tricoplanar || neighbor->tricoplanar) + continue; + zzinc_(Zdistconvex); + qh_distplane(qh, centrum, neighbor, &dist); + if (dist > qh->DISTround) { + zzinc_(Zconcaveridges); + qh_precision(qh, "concave ridge"); + qh_fprintf(qh, qh->ferr, 6117, "qhull precision error: f%d is concave to f%d. Centrum of f%d is %6.4g above f%d\n", + facet->id, neighbor->id, facet->id, dist, neighbor->id); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + }else if (dist >= 0.0) { /* if arithmetic always rounds the same, + can test against centrum radius instead */ + zzinc_(Zcoplanarridges); + qh_precision(qh, "coplanar ridge"); + qh_fprintf(qh, qh->ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d. Centrum of f%d is %6.4g above f%d\n", + facet->id, neighbor->id, facet->id, dist, neighbor->id); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + } + } + if (tempcentrum) + qh_memfree(qh, centrum, qh->normal_size); + } + } + if (waserror && !qh->FORCEoutput) + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2); +} /* checkconvex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkfacet">-</a> + + qh_checkfacet(qh, facet, newmerge, waserror ) + checks for consistency errors in facet + newmerge set if from merge_r.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(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp) { + facetT *neighbor, **neighborp, *errother=NULL; + ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2; + vertexT *vertex, **vertexp; + unsigned previousid= INT_MAX; + int numneighbors, numvertices, numridges=0, numRvertices=0; + boolT waserror= False; + int skipA, skipB, ridge_i, ridge_n, i; + setT *intersection; + + if (facet->visible) { + qh_fprintf(qh, qh->ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n", + facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (!facet->normal) { + qh_fprintf(qh, qh->ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n", + facet->id); + waserror= True; + } + qh_setcheck(qh, facet->vertices, "vertices for f", facet->id); + qh_setcheck(qh, facet->ridges, "ridges for f", facet->id); + qh_setcheck(qh, facet->outsideset, "outsideset for f", facet->id); + qh_setcheck(qh, facet->coplanarset, "coplanarset for f", facet->id); + qh_setcheck(qh, facet->neighbors, "neighbors for f", facet->id); + FOREACHvertex_(facet->vertices) { + if (vertex->deleted) { + qh_fprintf(qh, qh->ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id); + qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex); + waserror= True; + } + if (vertex->id >= previousid) { + qh_fprintf(qh, qh->ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id); + waserror= True; + break; + } + previousid= vertex->id; + } + numneighbors= qh_setsize(qh, facet->neighbors); + numvertices= qh_setsize(qh, facet->vertices); + numridges= qh_setsize(qh, facet->ridges); + if (facet->simplicial) { + if (numvertices+numneighbors != 2*qh->hull_dim + && !facet->degenerate && !facet->redundant) { + qh_fprintf(qh, qh->ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh->hull_dim\n", + facet->id, numvertices, numneighbors); + qh_setprint(qh, qh->ferr, "", facet->neighbors); + waserror= True; + } + }else { /* non-simplicial */ + if (!newmerge + &&(numvertices < qh->hull_dim || numneighbors < qh->hull_dim) + && !facet->degenerate && !facet->redundant) { + qh_fprintf(qh, qh->ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh->hull_dim\n", + facet->id, numvertices, numneighbors); + waserror= True; + } + /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */ + if (numridges < numneighbors + ||(qh->hull_dim == 3 && numvertices > numridges && !qh->NEWfacets) + ||(qh->hull_dim == 2 && numridges + numvertices + numneighbors != 6)) { + if (!facet->degenerate && !facet->redundant) { + qh_fprintf(qh, qh->ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n", + facet->id, numridges, numneighbors, numvertices); + waserror= True; + } + } + } + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) { + qh_fprintf(qh, qh->ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + neighbor->seen= True; + } + FOREACHneighbor_(facet) { + if (!qh_setin(neighbor->neighbors, facet)) { + qh_fprintf(qh, qh->ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n", + facet->id, neighbor->id, neighbor->id, facet->id); + errother= neighbor; + waserror= True; + } + if (!neighbor->seen) { + qh_fprintf(qh, qh->ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n", + facet->id, neighbor->id); + errother= neighbor; + waserror= True; + } + neighbor->seen= False; + } + FOREACHridge_(facet->ridges) { + qh_setcheck(qh, ridge->vertices, "vertices for r", ridge->id); + ridge->seen= False; + } + FOREACHridge_(facet->ridges) { + if (ridge->seen) { + qh_fprintf(qh, qh->ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n", + facet->id, ridge->id); + errridge= ridge; + waserror= True; + } + ridge->seen= True; + numRvertices= qh_setsize(qh, ridge->vertices); + if (numRvertices != qh->hull_dim - 1) { + qh_fprintf(qh, qh->ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n", + ridge->top->id, ridge->bottom->id, numRvertices); + errridge= ridge; + waserror= True; + } + neighbor= otherfacet_(ridge, facet); + neighbor->seen= True; + if (!qh_setin(facet->neighbors, neighbor)) { + qh_fprintf(qh, qh->ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n", + facet->id, neighbor->id, ridge->id); + errridge= ridge; + waserror= True; + } + } + if (!facet->simplicial) { + FOREACHneighbor_(facet) { + if (!neighbor->seen) { + qh_fprintf(qh, qh->ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n", + facet->id, neighbor->id); + errother= neighbor; + waserror= True; + } + intersection= qh_vertexintersect_new(qh, facet->vertices, neighbor->vertices); + qh_settemppush(qh, intersection); + FOREACHvertex_(facet->vertices) { + vertex->seen= False; + vertex->seen2= False; + } + FOREACHvertex_(intersection) + vertex->seen= True; + FOREACHridge_(facet->ridges) { + if (neighbor != otherfacet_(ridge, facet)) + continue; + FOREACHvertex_(ridge->vertices) { + if (!vertex->seen) { + qh_fprintf(qh, qh->ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n", + vertex->id, ridge->id, facet->id, neighbor->id); + qh_errexit(qh, qh_ERRqhull, facet, ridge); + } + vertex->seen2= True; + } + } + if (!newmerge) { + FOREACHvertex_(intersection) { + if (!vertex->seen2) { + if (qh->IStracing >=3 || !qh->MERGING) { + qh_fprintf(qh, qh->ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\ + not in a ridge. This is ok under merging. Last point was p%d\n", + vertex->id, facet->id, neighbor->id, qh->furthest_id); + if (!qh->FORCEoutput && !qh->MERGING) { + qh_errprint(qh, "ERRONEOUS", facet, neighbor, NULL, vertex); + if (!qh->MERGING) + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + } + } + } + } + qh_settempfree(qh, &intersection); + } + }else { /* simplicial */ + FOREACHneighbor_(facet) { + if (neighbor->simplicial) { + skipA= SETindex_(facet->neighbors, neighbor); + skipB= qh_setindex(neighbor->neighbors, facet); + if (skipA<0 || skipB<0 || !qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) { + qh_fprintf(qh, qh->ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n", + facet->id, skipA, neighbor->id, skipB); + errother= neighbor; + waserror= True; + } + } + } + } + if (qh->hull_dim < 5 && (qh->IStracing > 2 || qh->CHECKfrequently)) { + FOREACHridge_i_(qh, facet->ridges) { /* expensive */ + for (i=ridge_i+1; i < ridge_n; i++) { + ridge2= SETelemt_(facet->ridges, i, ridgeT); + if (qh_setequal(ridge->vertices, ridge2->vertices)) { + qh_fprintf(qh, qh->ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n", + ridge->id, ridge2->id); + errridge= ridge; + waserror= True; + } + } + } + } + if (waserror) { + qh_errprint(qh, "ERRONEOUS", facet, errother, errridge, NULL); + *waserrorp= True; + } +} /* checkfacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkflipped_all">-</a> + + qh_checkflipped_all(qh, facetlist ) + checks orientation of facets in list against interior point +*/ +void qh_checkflipped_all(qhT *qh, facetT *facetlist) { + facetT *facet; + boolT waserror= False; + realT dist; + + if (facetlist == qh->facet_list) + zzval_(Zflippedfacets)= 0; + FORALLfacet_(facetlist) { + if (facet->normal && !qh_checkflipped(qh, facet, &dist, !qh_ALL)) { + qh_fprintf(qh, qh->ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n", + facet->id, dist); + if (!qh->FORCEoutput) { + qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, NULL); + waserror= True; + } + } + } + if (waserror) { + qh_fprintf(qh, qh->ferr, 8101, "\n\ +A flipped facet occurs when its distance to the interior point is\n\ +greater than %2.2g, the maximum roundoff error.\n", -qh->DISTround); + qh_errexit(qh, qh_ERRprec, NULL, NULL); + } +} /* checkflipped_all */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkpolygon">-</a> + + qh_checkpolygon(qh, 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(qhT *qh, 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, qh->ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id)); + if (facetlist != qh->facet_list || qh->ONLYgood) + nextseen= True; + FORALLfacet_(facetlist) { + if (facet == qh->visible_list) + visibleseen= True; + if (!facet->visible) { + if (!nextseen) { + if (facet == qh->facet_next) + nextseen= True; + else if (qh_setsize(qh, facet->outsideset)) { + if (!qh->NARROWhull +#if !qh_COMPUTEfurthest + || facet->furthestdist >= qh->MINoutside +#endif + ) { + qh_fprintf(qh, qh->ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh->facet_next\n", + facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + } + } + numfacets++; + qh_checkfacet(qh, facet, False, &waserror); + } + } + if (qh->visible_list && !visibleseen && facetlist == qh->facet_list) { + qh_fprintf(qh, qh->ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh->visible_list->id); + qh_printlists(qh); + qh_errexit(qh, 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(qh, facet->ridges); + FOREACHvertex_(facet->vertices) { + vertex->visitid++; + if (!vertex->seen) { + vertex->seen= True; + numvertices++; + if (qh_pointid(qh, vertex->point) == qh_IDunknown) { + qh_fprintf(qh, qh->ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n", + vertex->point, vertex->id, qh->first_point); + waserror= True; + } + } + } + } + qh->vertex_visit += (unsigned int)numfacets; + if (facetlist == qh->facet_list) { + if (numfacets != qh->num_facets - qh->num_visible) { + qh_fprintf(qh, qh->ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n", + numfacets, qh->num_facets, qh->num_visible); + waserror= True; + } + qh->vertex_visit++; + if (qh->VERTEXneighbors) { + FORALLvertices { + qh_setcheck(qh, vertex->neighbors, "neighbors for v", vertex->id); + if (vertex->deleted) + continue; + totvneighbors += qh_setsize(qh, vertex->neighbors); + } + FORALLfacet_(facetlist) + totvertices += qh_setsize(qh, facet->vertices); + if (totvneighbors != totvertices) { + qh_fprintf(qh, qh->ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent. Totvneighbors %d, totvertices %d\n", + totvneighbors, totvertices); + waserror= True; + } + } + if (numvertices != qh->num_vertices - qh_setsize(qh, qh->del_vertices)) { + qh_fprintf(qh, qh->ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n", + numvertices, qh->num_vertices - qh_setsize(qh, qh->del_vertices)); + waserror= True; + } + if (qh->hull_dim == 2 && numvertices != numfacets) { + qh_fprintf(qh, qh->ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n", + numvertices, numfacets); + waserror= True; + } + if (qh->hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) { + qh_fprintf(qh, qh->ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\ + A vertex appears twice in a edge list. May occur during merging.", + numvertices, numfacets, numridges/2); + /* occurs if lots of merging and a vertex ends up twice in an edge list. e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */ + } + } + if (waserror) + qh_errexit(qh, qh_ERRqhull, NULL, NULL); +} /* checkpolygon */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkvertex">-</a> + + qh_checkvertex(qh, vertex ) + check vertex for consistency + checks vertex->neighbors + + notes: + neighbors checked efficiently in checkpolygon +*/ +void qh_checkvertex(qhT *qh, vertexT *vertex) { + boolT waserror= False; + facetT *neighbor, **neighborp, *errfacet=NULL; + + if (qh_pointid(qh, vertex->point) == qh_IDunknown) { + qh_fprintf(qh, qh->ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point); + waserror= True; + } + if (vertex->id >= qh->vertex_id) { + qh_fprintf(qh, qh->ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id); + waserror= True; + } + if (!waserror && !vertex->deleted) { + if (qh_setsize(qh, vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (!qh_setin(neighbor->vertices, vertex)) { + qh_fprintf(qh, qh->ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id); + errfacet= neighbor; + waserror= True; + } + } + } + } + if (waserror) { + qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex); + qh_errexit(qh, qh_ERRqhull, errfacet, NULL); + } +} /* checkvertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="clearcenters">-</a> + + qh_clearcenters(qh, type ) + clear old data from facet->center + + notes: + sets new centertype + nop if CENTERtype is the same +*/ +void qh_clearcenters(qhT *qh, qh_CENTER type) { + facetT *facet; + + if (qh->CENTERtype != type) { + FORALLfacets { + if (facet->tricoplanar && !facet->keepcentrum) + facet->center= NULL; /* center is owned by the ->keepcentrum facet */ + else if (qh->CENTERtype == qh_ASvoronoi){ + if (facet->center) { + qh_memfree(qh, facet->center, qh->center_size); + facet->center= NULL; + } + }else /* qh->CENTERtype == qh_AScentrum */ { + if (facet->center) { + qh_memfree(qh, facet->center, qh->normal_size); + facet->center= NULL; + } + } + } + qh->CENTERtype= type; + } + trace2((qh, qh->ferr, 2043, "qh_clearcenters: switched to center type %d\n", type)); +} /* clearcenters */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="createsimplex">-</a> + + qh_createsimplex(qh, 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(qhT *qh, setT *vertices) { + facetT *facet= NULL, *newfacet; + boolT toporient= True; + int vertex_i, vertex_n, nth; + setT *newfacets= qh_settemp(qh, qh->hull_dim+1); + vertexT *vertex; + + qh->facet_list= qh->newfacet_list= qh->facet_tail= qh_newfacet(qh); + qh->num_facets= qh->num_vertices= qh->num_visible= 0; + qh->vertex_list= qh->newvertex_list= qh->vertex_tail= qh_newvertex(qh, NULL); + FOREACHvertex_i_(qh, vertices) { + newfacet= qh_newfacet(qh); + newfacet->vertices= qh_setnew_delnthsorted(qh, vertices, vertex_n, + vertex_i, 0); + newfacet->toporient= (unsigned char)toporient; + qh_appendfacet(qh, newfacet); + newfacet->newfacet= True; + qh_appendvertex(qh, vertex); + qh_setappend(qh, &newfacets, newfacet); + toporient ^= True; + } + FORALLnew_facets { + nth= 0; + FORALLfacet_(qh->newfacet_list) { + if (facet != newfacet) + SETelem_(newfacet->neighbors, nth++)= facet; + } + qh_settruncate(qh, newfacet->neighbors, qh->hull_dim); + } + qh_settempfree(qh, &newfacets); + trace1((qh, qh->ferr, 1028, "qh_createsimplex: created simplex\n")); +} /* createsimplex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="delridge">-</a> + + qh_delridge(qh, ridge ) + deletes ridge from data structures it belongs to + frees up its memory + + notes: + in merge_r.c, caller sets vertex->delridge for each vertex + ridges also freed in qh_freeqhull +*/ +void qh_delridge(qhT *qh, ridgeT *ridge) { + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + qh_setdel(ridge->top->ridges, ridge); + qh_setdel(ridge->bottom->ridges, ridge); + qh_setfree(qh, &(ridge->vertices)); + qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp); +} /* delridge */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="delvertex">-</a> + + qh_delvertex(qh, vertex ) + deletes a vertex and frees its memory + + notes: + assumes vertex->adjacencies have been updated if needed + unlinks from vertex_list +*/ +void qh_delvertex(qhT *qh, vertexT *vertex) { + + if (vertex == qh->tracevertex) + qh->tracevertex= NULL; + qh_removevertex(qh, vertex); + qh_setfree(qh, &vertex->neighbors); + qh_memfree(qh, vertex, (int)sizeof(vertexT)); +} /* delvertex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="facet3vertex">-</a> + + qh_facet3vertex(qh, ) + 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(qhT *qh, facetT *facet) { + ridgeT *ridge, *firstridge; + vertexT *vertex; + int cntvertices, cntprojected=0; + setT *vertices; + + cntvertices= qh_setsize(qh, facet->vertices); + vertices= qh_settemp(qh, cntvertices); + if (facet->simplicial) { + if (cntvertices != 3) { + qh_fprintf(qh, qh->ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n", + cntvertices, facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + qh_setappend(qh, &vertices, SETfirst_(facet->vertices)); + if (facet->toporient ^ qh_ORIENTclock) + qh_setappend(qh, &vertices, SETsecond_(facet->vertices)); + else + qh_setaddnth(qh, &vertices, 0, SETsecond_(facet->vertices)); + qh_setappend(qh, &vertices, SETelem_(facet->vertices, 2)); + }else { + ridge= firstridge= SETfirstt_(facet->ridges, ridgeT); /* no infinite */ + while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) { + qh_setappend(qh, &vertices, vertex); + if (++cntprojected > cntvertices || ridge == firstridge) + break; + } + if (!ridge || cntprojected != cntvertices) { + qh_fprintf(qh, qh->ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up. got at least %d\n", + facet->id, cntprojected); + qh_errexit(qh, qh_ERRqhull, facet, ridge); + } + } + return vertices; +} /* facet3vertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findbestfacet">-</a> + + qh_findbestfacet(qh, 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-code.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_r.c#findbest">qh_findbest</a> +*/ +facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside) { + facetT *bestfacet= NULL; + int numpart, totpart= 0; + + bestfacet= qh_findbest(qh, point, qh->facet_list, + bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */, + bestdist, isoutside, &totpart); + if (*bestdist < -qh->DISTround) { + bestfacet= qh_findfacet_all(qh, point, bestdist, isoutside, &numpart); + totpart += numpart; + if ((isoutside && *isoutside && bestoutside) + || (isoutside && !*isoutside && bestfacet->upperdelaunay)) { + bestfacet= qh_findbest(qh, point, bestfacet, + bestoutside, False, bestoutside, + bestdist, isoutside, &totpart); + totpart += numpart; + } + } + trace3((qh, qh->ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n", + bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart)); + return bestfacet; +} /* findbestfacet */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findbestlower">-</a> + + qh_findbestlower(qh, 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(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) { + facetT *neighbor, **neighborp, *bestfacet= NULL; + realT bestdist= -REALmax/2 /* avoid underflow */; + realT dist; + vertexT *vertex; + boolT isoutside= False; /* not used */ + + zinc_(Zbestlower); + FOREACHneighbor_(upperfacet) { + if (neighbor->upperdelaunay || neighbor->flipped) + continue; + (*numpart)++; + qh_distplane(qh, 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(qh, upperfacet, point, &dist); + qh_vertexneighbors(qh); + FOREACHneighbor_(vertex) { + if (neighbor->upperdelaunay || neighbor->flipped) + continue; + (*numpart)++; + qh_distplane(qh, point, neighbor, &dist); + if (dist > bestdist) { + bestfacet= neighbor; + bestdist= dist; + } + } + } + if (!bestfacet) { + zinc_(Zbestlowerall); /* invoked once per point in outsideset */ + zmax_(Zbestloweralln, qh->num_facets); + /* [dec'15] Previously reported as QH6228 */ + trace3((qh, qh->ferr, 3025, "qh_findbestlower: all neighbors of facet %d are flipped or upper Delaunay. Search all facets\n", + upperfacet->id)); + /* rarely called */ + bestfacet= qh_findfacet_all(qh, point, &bestdist, &isoutside, numpart); + } + *bestdistp= bestdist; + trace3((qh, qh->ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n", + bestfacet->id, bestdist, upperfacet->id, qh_pointid(qh, point))); + return bestfacet; +} /* findbestlower */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findfacet_all">-</a> + + qh_findfacet_all(qh, point, bestdist, isoutside, numpart ) + exhaustive search for facet below a point + + for Delaunay triangulations, + Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates. + + returns: + returns first facet below point + if point is inside, + returns nearest facet + distance to facet + isoutside if point is outside of the hull + number of distance tests + + notes: + primarily for library users, rarely used by Qhull +*/ +facetT *qh_findfacet_all(qhT *qh, pointT *point, realT *bestdist, boolT *isoutside, + int *numpart) { + facetT *bestfacet= NULL, *facet; + realT dist; + int totpart= 0; + + *bestdist= -REALmax; + *isoutside= False; + FORALLfacets { + if (facet->flipped || !facet->normal) + continue; + totpart++; + qh_distplane(qh, point, facet, &dist); + if (dist > *bestdist) { + *bestdist= dist; + bestfacet= facet; + if (dist > qh->MINoutside) { + *isoutside= True; + break; + } + } + } + *numpart= totpart; + trace3((qh, qh->ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n", + getid_(bestfacet), *bestdist, *isoutside, totpart)); + return bestfacet; +} /* findfacet_all */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findgood">-</a> + + qh_findgood(qh, 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(qhT *qh, 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, 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(qh, 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, 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, qh->ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n", + bestfacet->id, bestangle)); + return numgood; + } + }else if (qh->GOODclosest) { /* numgood > 0 */ + qh->GOODclosest->good= False; + qh->GOODclosest= NULL; + } + } + zadd_(Zgoodfacet, numgood); + trace2((qh, qh->ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n", + numgood, goodhorizon)); + if (!numgood && qh->GOODvertex>0 && !qh->MERGING) + return goodhorizon; + return numgood; +} /* findgood */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findgood_all">-</a> + + qh_findgood_all(qh, 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(qhT *qh, 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, qh->facet_list, 0); + FORALLfacet_(facetlist) { + if (facet->good) + numgood++; + } + if (qh->GOODvertex <0 || (qh->GOODvertex > 0 && qh->MERGING)) { + FORALLfacet_(facetlist) { + if (facet->good && ((qh->GOODvertex > 0) ^ !!qh_isvertex(qh->GOODvertexp, facet->vertices))) { + if (!--numgood) { + if (qh->ONLYgood) { + qh_fprintf(qh, qh->ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n", + qh_pointid(qh, qh->GOODvertexp), facet->id); + return; + }else if (qh->GOODvertex > 0) + qh_fprintf(qh, qh->ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n", + qh->GOODvertex-1, qh->GOODvertex-1); + else + qh_fprintf(qh, qh->ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n", + -qh->GOODvertex - 1, -qh->GOODvertex - 1); + } + facet->good= False; + } + } + } + startgood= numgood; + if (qh->SPLITthresholds) { + FORALLfacet_(facetlist) { + if (facet->good) { + if (!qh_inthresholds(qh, facet->normal, &angle)) { + facet->good= False; + numgood--; + if (angle < bestangle) { + bestangle= angle; + bestfacet= facet; + } + } + } + } + if (!numgood && bestfacet) { + bestfacet->good= True; + numgood++; + trace0((qh, qh->ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n", + bestfacet->id, bestangle)); + return; + } + } + qh->num_good= numgood; + trace0((qh, qh->ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n", + numgood, startgood)); +} /* findgood_all */ + +/*-<a href="qh-poly_r.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(qhT *qh /* 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(qh, furthest, facet, &dist); +#else + dist= facet->furthestdist; +#endif + if (dist > bestdist) { + bestfacet= facet; + bestdist= dist; + } + } + } + if (bestfacet) { + qh_removefacet(qh, bestfacet); + qh_prependfacet(qh, bestfacet, &qh->facet_next); + trace1((qh, qh->ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n", + bestfacet->id, bestdist)); + } +} /* furthestnext */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="furthestout">-</a> + + qh_furthestout(qh, 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(qhT *qh, facetT *facet) { + pointT *point, **pointp, *bestpoint= NULL; + realT dist, bestdist= -REALmax; + + FOREACHpoint_(facet->outsideset) { + qh_distplane(qh, point, facet, &dist); + zinc_(Zcomputefurthest); + if (dist > bestdist) { + bestpoint= point; + bestdist= dist; + } + } + if (bestpoint) { + qh_setdel(facet->outsideset, point); + qh_setappend(qh, &facet->outsideset, point); +#if !qh_COMPUTEfurthest + facet->furthestdist= bestdist; +#endif + } + facet->notfurthest= False; + trace3((qh, qh->ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n", + qh_pointid(qh, point), facet->id)); +} /* furthestout */ + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="infiniteloop">-</a> + + qh_infiniteloop(qh, facet ) + report infinite loop error due to facet +*/ +void qh_infiniteloop(qhT *qh, facetT *facet) { + + qh_fprintf(qh, qh->ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n"); + qh_errexit(qh, qh_ERRqhull, facet, NULL); +} /* qh_infiniteloop */ + +/*-<a href="qh-poly_r.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(qhT *qh) { + setT *maxpoints, *vertices; + facetT *facet; + int i, numpart; + realT dist; + boolT isoutside; + + qh->furthest_id= qh_IDunknown; + 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, qh->GOODpoint-1); + else if (qh->GOODpoint < 0) + qh->GOODpointp= qh_point(qh, -qh->GOODpoint-1); + if (qh->GOODvertex > 0) + qh->GOODvertexp= qh_point(qh, qh->GOODvertex-1); + else if (qh->GOODvertex < 0) + qh->GOODvertexp= qh_point(qh, -qh->GOODvertex-1); + if ((qh->GOODpoint + && (qh->GOODpointp < qh->first_point /* also catches !GOODpointp */ + || qh->GOODpointp > qh_point(qh, qh->num_points-1))) + || (qh->GOODvertex + && (qh->GOODvertexp < qh->first_point /* also catches !GOODvertexp */ + || qh->GOODvertexp > qh_point(qh, qh->num_points-1)))) { + qh_fprintf(qh, qh->ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n", + qh->num_points-1); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + maxpoints= qh_maxmin(qh, qh->first_point, qh->num_points, qh->hull_dim); + if (qh->SCALElast) + qh_scalelast(qh, qh->first_point, qh->num_points, qh->hull_dim, + qh->MINlastcoord, qh->MAXlastcoord, qh->MAXwidth); + qh_detroundoff(qh); + 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, qh->hull_dim, maxpoints, qh->first_point, qh->num_points); + qh_initialhull(qh, vertices); /* initial qh->facet_list */ + qh_partitionall(qh, vertices, qh->first_point, qh->num_points); + if (qh->PRINToptions1st || qh->TRACElevel || qh->IStracing) { + if (qh->TRACElevel || qh->IStracing) + qh_fprintf(qh, qh->ferr, 8103, "\nTrace level %d for %s | %s\n", + qh->IStracing ? qh->IStracing : qh->TRACElevel, qh->rbox_command, qh->qhull_command); + qh_fprintf(qh, qh->ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options); + } + qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + qh->facet_next= qh->facet_list; + qh_furthestnext(qh /* qh->facet_list */); + if (qh->PREmerge) { + qh->cos_max= qh->premerge_cos; + qh->centrum_radius= qh->premerge_centrum; + } + if (qh->ONLYgood) { + if (qh->GOODvertex > 0 && qh->MERGING) { + qh_fprintf(qh, qh->ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (!(qh->GOODthreshold || qh->GOODpoint + || (!qh->MERGEexact && !qh->PREmerge && qh->GOODvertexp))) { + qh_fprintf(qh, qh->ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\ +good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->GOODvertex > 0 && !qh->MERGING /* matches qh_partitionall */ + && !qh_isvertex(qh->GOODvertexp, vertices)) { + facet= qh_findbestnew(qh, qh->GOODvertexp, qh->facet_list, + &dist, !qh_ALL, &isoutside, &numpart); + zadd_(Zdistgood, numpart); + if (!isoutside) { + qh_fprintf(qh, qh->ferr, 6153, "qhull input error: point for QV%d is inside initial simplex. It can not be made a vertex.\n", + qh_pointid(qh, qh->GOODvertexp)); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (!qh_addpoint(qh, qh->GOODvertexp, facet, False)) { + qh_settempfree(qh, &vertices); + qh_settempfree(qh, &maxpoints); + return; + } + } + qh_findgood(qh, qh->facet_list, 0); + } + qh_settempfree(qh, &vertices); + qh_settempfree(qh, &maxpoints); + trace1((qh, qh->ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n")); +} /* initbuild */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="initialhull">-</a> + + qh_initialhull(qh, 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(qhT *qh, setT *vertices) { + facetT *facet, *firstfacet, *neighbor, **neighborp; + realT dist, angle, minangle= REALmax; +#ifndef qh_NOtrace + int k; +#endif + + qh_createsimplex(qh, vertices); /* qh->facet_list */ + qh_resetlists(qh, False, qh_RESETvisible); + qh->facet_next= qh->facet_list; /* advance facet when processed */ + qh->interior_point= qh_getcenter(qh, vertices); + firstfacet= qh->facet_list; + qh_setfacetplane(qh, firstfacet); + zinc_(Znumvisibility); /* needs to be in printsummary */ + qh_distplane(qh, qh->interior_point, firstfacet, &dist); + if (dist > 0) { + FORALLfacets + facet->toporient ^= (unsigned char)True; + } + FORALLfacets + qh_setfacetplane(qh, facet); + FORALLfacets { + if (!qh_checkflipped(qh, facet, NULL, qh_ALL)) {/* due to axis-parallel facet */ + trace1((qh, qh->ferr, 1031, "qh_initialhull: initial orientation incorrect. Correct all facets\n")); + facet->flipped= False; + FORALLfacets { + facet->toporient ^= (unsigned char)True; + qh_orientoutside(qh, facet); + } + break; + } + } + FORALLfacets { + if (!qh_checkflipped(qh, facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */ + if (qh->DELAUNAY && ! qh->ATinfinity) { + if (qh->UPPERdelaunay) + qh_fprintf(qh, qh->ferr, 6240, "Qhull precision error: Initial simplex is cocircular or cospherical. Option 'Qs' searches all points. Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n"); + else + qh_fprintf(qh, qh->ferr, 6239, "Qhull precision error: Initial simplex is cocircular or cospherical. Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points. Option 'Qz' adds a point \"at infinity\". Use option 'Qs' to search all points for the initial simplex.\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + qh_precision(qh, "initial simplex is flat"); + qh_fprintf(qh, qh->ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n", + facet->id); + qh_errexit(qh, qh_ERRsingular, NULL, NULL); /* calls qh_printhelp_singular */ + } + FOREACHneighbor_(facet) { + angle= qh_getangle(qh, facet->normal, neighbor->normal); + minimize_( minangle, angle); + } + } + if (minangle < qh_MAXnarrow && !qh->NOnarrow) { + realT diff= 1.0 + minangle; + + qh->NARROWhull= True; + qh_option(qh, "_narrow-hull", NULL, &diff); + if (minangle < qh_WARNnarrow && !qh->RERUN && qh->PRINTprecision) + qh_printhelp_narrowhull(qh, qh->ferr, minangle); + } + zzval_(Zprocessed)= qh->hull_dim+1; + qh_checkpolygon(qh, qh->facet_list); + qh_checkconvex(qh, qh->facet_list, qh_DATAfault); +#ifndef qh_NOtrace + if (qh->IStracing >= 1) { + qh_fprintf(qh, qh->ferr, 8105, "qh_initialhull: simplex constructed, interior point:"); + for (k=0; k < qh->hull_dim; k++) + qh_fprintf(qh, qh->ferr, 8106, " %6.4g", qh->interior_point[k]); + qh_fprintf(qh, qh->ferr, 8107, "\n"); + } +#endif +} /* initialhull */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="initialvertices">-</a> + + qh_initialvertices(qh, 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(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints) { + pointT *point, **pointp; + setT *vertices, *simplex, *tested; + realT randr; + int idx, point_i, point_n, k; + boolT nearzero= False; + + vertices= qh_settemp(qh, dim + 1); + simplex= qh_settemp(qh, dim+1); + if (qh->ALLpoints) + qh_maxsimplex(qh, dim, NULL, points, numpoints, &simplex); + else if (qh->RANDOMoutside) { + while (qh_setsize(qh, simplex) != dim+1) { + randr= qh_RANDOMint; + randr= randr/(qh_RANDOMmax+1); + idx= (int)floor(qh->num_points * randr); + while (qh_setin(simplex, qh_point(qh, idx))) { + idx++; /* in case qh_RANDOMint always returns the same value */ + idx= idx < qh->num_points ? idx : 0; + } + qh_setappend(qh, &simplex, qh_point(qh, idx)); + } + }else if (qh->hull_dim >= qh_INITIALmax) { + tested= qh_settemp(qh, dim+1); + qh_setappend(qh, &simplex, SETfirst_(maxpoints)); /* max and min X coord */ + qh_setappend(qh, &simplex, SETsecond_(maxpoints)); + qh_maxsimplex(qh, fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex); + k= qh_setsize(qh, simplex); + FOREACHpoint_i_(qh, maxpoints) { + if (point_i & 0x1) { /* first pick up max. coord. points */ + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(qh, point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(qh, &tested, point); + else { + qh_setappend(qh, &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(qh, point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(qh, &tested, point); + else { + qh_setappend(qh, &simplex, point); + k++; + } + } + } + idx= 0; + while (k != dim && (point= qh_point(qh, idx++))) { + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(qh, point, simplex, k, &nearzero); + if (!nearzero){ + qh_setappend(qh, &simplex, point); + k++; + } + } + } + qh_settempfree(qh, &tested); + qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex); + }else + qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex); + FOREACHpoint_(simplex) + qh_setaddnth(qh, &vertices, 0, qh_newvertex(qh, point)); /* descending order */ + qh_settempfree(qh, &simplex); + return vertices; +} /* initialvertices */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="isvertex">-</a> + + qh_isvertex( point, vertices ) + 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_r.htm#TOC" + >-------------------------------</a><a name="makenewfacets">-</a> + + qh_makenewfacets(qh, 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(qhT *qh, 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(qh, point); + qh_appendvertex(qh, 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(qh, visible, apex, &numnew); + } + if (visible->simplicial) + newfacet= qh_makenew_simplicial(qh, 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, qh->ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n", + numnew, qh_pointid(qh, point))); + if (qh->IStracing >= 4) + qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL); + return apex; +} /* makenewfacets */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="matchduplicates">-</a> + + qh_matchduplicates(qh, 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(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) { + boolT same, ismatch; + int hash, scan; + facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet; + int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch; + realT maxdist= -REALmax, mindist, dist2, low, high; + + hash= qh_gethash(qh, hashsize, atfacet->vertices, qh->hull_dim, 1, + SETelem_(atfacet->vertices, atskip)); + trace2((qh, qh->ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n", + atfacet->id, atskip, hash, *hashcount)); + for (makematch= 0; makematch < 2; makematch++) { + qh->visit_id++; + for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) { + zinc_(Zhashlookup); + nextfacet= NULL; + newfacet->visitid= qh->visit_id; + for (scan= hash; (facet= SETelemt_(qh->hash_table, scan, facetT)); + scan= (++scan >= hashsize ? 0 : scan)) { + if (!facet->dupridge || facet->visitid == qh->visit_id) + continue; + zinc_(Zhashtests); + if (qh_matchvertices(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient)); + if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) { + if (!makematch) { + qh_fprintf(qh, qh->ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n", + facet->id, skip, newfacet->id, newskip, hash); + qh_errexit2(qh, 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, qh->ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n", + facet->id, skip, newfacet->id, newskip)); + } + }else if (ismatch) { + mindist= qh_getdistance(qh, facet, newfacet, &low, &high); + dist2= qh_getdistance(qh, newfacet, facet, &low, &high); + minimize_(mindist, dist2); + if (mindist > maxdist) { + maxdist= mindist; + maxmatch= facet; + maxskip= skip; + maxmatch2= newfacet; + maxskip2= newskip; + } + trace3((qh, qh->ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n", + facet->id, skip, newfacet->id, newskip, mindist, + maxmatch->id, maxmatch2->id)); + }else { /* !ismatch */ + nextfacet= facet; + nextskip= skip; + } + } + if (makematch && !facet + && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) { + qh_fprintf(qh, qh->ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n", + newfacet->id, newskip, hash); + qh_errexit(qh, qh_ERRqhull, newfacet, NULL); + } + } + } /* end of for each new facet at hash */ + if (!makematch) { + if (!maxmatch) { + qh_fprintf(qh, qh->ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n", + atfacet->id, atskip, hash); + qh_errexit(qh, qh_ERRqhull, atfacet, NULL); + } + SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; /* maxmatch!=0 by QH6157 */ + SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch; + *hashcount -= 2; /* removed two unmatched facets */ + zzinc_(Zmultiridge); + trace0((qh, qh->ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n", + maxmatch->id, maxskip, maxmatch2->id, maxskip2)); + qh_precision(qh, "ridge with multiple neighbors"); + if (qh->IStracing >= 4) + qh_errprint(qh, "DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL); + } + } +} /* matchduplicates */ + +/*-<a href="qh-poly_r.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(qh) + 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(qhT *qh /* qh.facet_list */) { + facetT *facet; + pointT *point, **pointp; + int numpart; + realT dist, innerplane; + + if (!qh->KEEPcoplanar && !qh->KEEPinside) { + FORALLfacets { + if (facet->coplanarset) + qh_setfree(qh, &facet->coplanarset); + } + }else if (!qh->KEEPcoplanar || !qh->KEEPinside) { + qh_outerinner(qh, NULL, NULL, &innerplane); + if (qh->JOGGLEmax < REALmax/2) + innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim); + numpart= 0; + FORALLfacets { + if (facet->coplanarset) { + FOREACHpoint_(facet->coplanarset) { + numpart++; + qh_distplane(qh, point, facet, &dist); + if (dist < innerplane) { + if (!qh->KEEPinside) + SETref_(point)= NULL; + }else if (!qh->KEEPcoplanar) + SETref_(point)= NULL; + } + qh_setcompact(qh, facet->coplanarset); + } + } + zzadd_(Zcheckpart, numpart); + } +} /* nearcoplanar */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="nearvertex">-</a> + + qh_nearvertex(qh, 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(qhT *qh, facetT *facet, pointT *point, realT *bestdistp) { + realT bestdist= REALmax, dist; + vertexT *bestvertex= NULL, *vertex, **vertexp, *apex; + coordT *center; + facetT *neighbor, **neighborp; + setT *vertices; + int dim= qh->hull_dim; + + if (qh->DELAUNAY) + dim--; + if (facet->tricoplanar) { + if (!qh->VERTEXneighbors || !facet->center) { + qh_fprintf(qh, qh->ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n"); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + vertices= qh_settemp(qh, qh->TEMPsize); + apex= SETfirstt_(facet->vertices, vertexT); + center= facet->center; + FOREACHneighbor_(apex) { + if (neighbor->center == center) { + FOREACHvertex_(neighbor->vertices) + qh_setappend(qh, &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(qh, &vertices); + *bestdistp= sqrt(bestdist); + if (!bestvertex) { + qh_fprintf(qh, qh->ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(qh, point)); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + trace3((qh, qh->ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n", + bestvertex->id, *bestdistp, facet->id, qh_pointid(qh, point))); /* bestvertex!=0 by QH2161 */ + return bestvertex; +} /* nearvertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="newhashtable">-</a> + + qh_newhashtable(qh, 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(qhT *qh, int newsize) { + int size; + + size= ((newsize+1)*qh_HASHfactor) | 0x1; /* odd number */ + while (True) { + if (newsize<0 || size<0) { + qh_fprintf(qh, qh->qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d). Did int overflow due to high-D?\n", newsize, size); /* WARN64 */ + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if ((size%3) && (size%5)) + break; + size += 2; + /* loop terminates because there is an infinite number of primes */ + } + qh->hash_table= qh_setnew(qh, size); + qh_setzero(qh, qh->hash_table, 0, size); + return size; +} /* newhashtable */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="newvertex">-</a> + + qh_newvertex(qh, point ) + returns a new vertex for point +*/ +vertexT *qh_newvertex(qhT *qh, pointT *point) { + vertexT *vertex; + + zinc_(Ztotvertices); + vertex= (vertexT *)qh_memalloc(qh, (int)sizeof(vertexT)); + memset((char *) vertex, (size_t)0, sizeof(vertexT)); + if (qh->vertex_id == UINT_MAX) { + qh_memfree(qh, vertex, (int)sizeof(vertexT)); + qh_fprintf(qh, qh->ferr, 6159, "qhull error: more than 2^32 vertices. vertexT.id field overflows. Vertices would not be sorted correctly.\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (qh->vertex_id == qh->tracevertex_id) + qh->tracevertex= vertex; + vertex->id= qh->vertex_id++; + vertex->point= point; + trace4((qh, qh->ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(qh, vertex->point), + vertex->id)); + return(vertex); +} /* newvertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="nextridge3d">-</a> + + qh_nextridge3d( atridge, facet, vertex ) + return next ridge and vertex for a 3d facet + returns NULL on error + [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qhT. + + notes: + in qh_ORIENTclock order + this is a O(n^2) implementation to trace all ridges + be sure to stop on any 2nd visit + same as QhullRidge::nextRidge3d + does not use qhT or qh_errexit [QhullFacet.cpp] + + design: + for each ridge + exit if it is the ridge after atridge +*/ +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) { + vertexT *atvertex, *vertex, *othervertex; + ridgeT *ridge, **ridgep; + + if ((atridge->top == facet) ^ qh_ORIENTclock) + atvertex= SETsecondt_(atridge->vertices, vertexT); + else + atvertex= SETfirstt_(atridge->vertices, vertexT); + FOREACHridge_(facet->ridges) { + if (ridge == atridge) + continue; + if ((ridge->top == facet) ^ qh_ORIENTclock) { + othervertex= SETsecondt_(ridge->vertices, vertexT); + vertex= SETfirstt_(ridge->vertices, vertexT); + }else { + vertex= SETsecondt_(ridge->vertices, vertexT); + othervertex= SETfirstt_(ridge->vertices, vertexT); + } + if (vertex == atvertex) { + if (vertexp) + *vertexp= othervertex; + return ridge; + } + } + return NULL; +} /* nextridge3d */ +#else /* qh_NOmerge */ +void qh_matchduplicates(qhT *qh, 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_r.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(qhT *qh /* facet_list */) { + pointT *point, **pointp; + facetT *facet; + realT dist; + + trace1((qh, qh->ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh->NARROWhull\n")); + FORALLfacets { + FOREACHpoint_(facet->outsideset) { + qh->num_outside--; + if (qh->KEEPcoplanar || qh->KEEPnearinside) { + qh_distplane(qh, point, facet, &dist); + zinc_(Zpartition); + qh_partitioncoplanar(qh, point, facet, &dist); + } + } + qh_setfree(qh, &facet->outsideset); + } +} /* outcoplanar */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="point">-</a> + + qh_point(qh, 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(qhT *qh, 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, qh->other_points)) + return SETelemt_(qh->other_points, id, pointT); + return NULL; +} /* point */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="point_add">-</a> + + qh_point_add(qh, 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(qhT *qh, setT *set, pointT *point, void *elem) { + int id, size; + + SETreturnsize_(set, size); + if ((id= qh_pointid(qh, point)) < 0) + qh_fprintf(qh, qh->ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n", + point, id); + else if (id >= size) { + qh_fprintf(qh, qh->ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n", + id, size); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + }else + SETelem_(set, id)= elem; +} /* point_add */ + + +/*-<a href="qh-poly_r.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_(qh, facets) { ... } + SETelem_(facets, i) + + design: + for each facet + add each vertex + add each coplanar point + add each outside point +*/ +setT *qh_pointfacet(qhT *qh /*qh.facet_list*/) { + int numpoints= qh->num_points + qh_setsize(qh, qh->other_points); + setT *facets; + facetT *facet; + vertexT *vertex, **vertexp; + pointT *point, **pointp; + + facets= qh_settemp(qh, numpoints); + qh_setzero(qh, facets, 0, numpoints); + qh->vertex_visit++; + FORALLfacets { + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + qh_point_add(qh, facets, vertex->point, facet); + } + } + FOREACHpoint_(facet->coplanarset) + qh_point_add(qh, facets, point, facet); + FOREACHpoint_(facet->outsideset) + qh_point_add(qh, facets, point, facet); + } + return facets; +} /* pointfacet */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="pointvertex">-</a> + + qh_pointvertex(qh, ) + 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_(qh, vertices) { ... } + SETelem_(vertices, i) +*/ +setT *qh_pointvertex(qhT *qh /*qh.facet_list*/) { + int numpoints= qh->num_points + qh_setsize(qh, qh->other_points); + setT *vertices; + vertexT *vertex; + + vertices= qh_settemp(qh, numpoints); + qh_setzero(qh, vertices, 0, numpoints); + FORALLvertices + qh_point_add(qh, vertices, vertex->point, vertex); + return vertices; +} /* pointvertex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="prependfacet">-</a> + + qh_prependfacet(qh, 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(qhT *qh, facetT *facet, facetT **facetlist) { + facetT *prevfacet, *list; + + + trace4((qh, qh->ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n", + facet->id, getid_(*facetlist))); + if (!*facetlist) + (*facetlist)= qh->facet_tail; + list= *facetlist; + prevfacet= list->previous; + facet->previous= prevfacet; + if (prevfacet) + prevfacet->next= facet; + list->previous= facet; + facet->next= *facetlist; + if (qh->facet_list == list) /* this may change *facetlist */ + qh->facet_list= facet; + if (qh->facet_next == list) + qh->facet_next= facet; + *facetlist= facet; + qh->num_facets++; +} /* prependfacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="printhashtable">-</a> + + qh_printhashtable(qh, fp ) + print hash table to fp + + notes: + not in I/O to avoid bringing io_r.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(qhT *qh, FILE *fp) { + facetT *facet, *neighbor; + int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0; + vertexT *vertex, **vertexp; + + FOREACHfacet_i_(qh, qh->hash_table) { + if (facet) { + FOREACHneighbor_i_(qh, facet) { + if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) + break; + } + if (neighbor_i == neighbor_n) + continue; + qh_fprintf(qh, fp, 9283, "hash %d f%d ", facet_i, facet->id); + FOREACHvertex_(facet->vertices) + qh_fprintf(qh, fp, 9284, "v%d ", vertex->id); + qh_fprintf(qh, fp, 9285, "\n neighbors:"); + FOREACHneighbor_i_(qh, facet) { + if (neighbor == qh_MERGEridge) + id= -3; + else if (neighbor == qh_DUPLICATEridge) + id= -2; + else + id= getid_(neighbor); + qh_fprintf(qh, fp, 9286, " %d", id); + } + qh_fprintf(qh, fp, 9287, "\n"); + } + } +} /* printhashtable */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="printlists">-</a> + + qh_printlists(qh, fp ) + print out facet and vertex list for debugging (without 'f/v' tags) +*/ +void qh_printlists(qhT *qh) { + facetT *facet; + vertexT *vertex; + int count= 0; + + qh_fprintf(qh, qh->ferr, 8108, "qh_printlists: facets:"); + FORALLfacets { + if (++count % 100 == 0) + qh_fprintf(qh, qh->ferr, 8109, "\n "); + qh_fprintf(qh, qh->ferr, 8110, " %d", facet->id); + } + qh_fprintf(qh, qh->ferr, 8111, "\n new facets %d visible facets %d next facet for qh_addpoint %d\n vertices(new %d):", + getid_(qh->newfacet_list), getid_(qh->visible_list), getid_(qh->facet_next), + getid_(qh->newvertex_list)); + count = 0; + FORALLvertices { + if (++count % 100 == 0) + qh_fprintf(qh, qh->ferr, 8112, "\n "); + qh_fprintf(qh, qh->ferr, 8113, " %d", vertex->id); + } + qh_fprintf(qh, qh->ferr, 8114, "\n"); +} /* printlists */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="resetlists">-</a> + + qh_resetlists(qh, 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(qhT *qh, 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_r.htm#TOC" + >-------------------------------</a><a name="setvoronoi_all">-</a> + + qh_setvoronoi_all(qh) + 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(qhT *qh) { + facetT *facet; + + qh_clearcenters(qh, qh_ASvoronoi); + qh_vertexneighbors(qh); + + FORALLfacets { + if (!facet->normal || !facet->upperdelaunay || qh->UPPERdelaunay) { + if (!facet->center) + facet->center= qh_facetcenter(qh, facet->vertices); + } + } +} /* setvoronoi_all */ + +#ifndef qh_NOmerge + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate">-</a> + + qh_triangulate() + triangulate non-simplicial facets on qh.facet_list, + if qh->VORONOI, sets Voronoi centers of non-simplicial facets + nop if hasTriangulation + + returns: + all facets simplicial + each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc. + + notes: + call after qh_check_output since may switch to Voronoi centers + Output may overwrite ->f.triowner with ->f.area +*/ +void qh_triangulate(qhT *qh /*qh.facet_list*/) { + facetT *facet, *nextfacet, *owner; + int onlygood= qh->ONLYgood; + facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL; + facetT *orig_neighbor= NULL, *otherfacet; + vertexT *new_vertex_list= NULL; + mergeT *merge; + mergeType mergetype; + int neighbor_i, neighbor_n; + + if (qh->hasTriangulation) + return; + trace1((qh, qh->ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n")); + if (qh->hull_dim == 2) + return; + if (qh->VORONOI) { /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */ + qh_clearcenters(qh, qh_ASvoronoi); + qh_vertexneighbors(qh); + } + qh->ONLYgood= False; /* for makenew_nonsimplicial */ + qh->visit_id++; + qh->NEWfacets= True; + qh->degen_mergeset= qh_settemp(qh, 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(qh, facet, &new_vertex_list); + } + trace2((qh, qh->ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list))); + for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */ + nextfacet= facet->next; + if (facet->visible) + continue; + if (facet->ridges) { + if (qh_setsize(qh, facet->ridges) > 0) { + qh_fprintf(qh, qh->ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + qh_setfree(qh, &facet->ridges); + } + if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) { + zinc_(Ztrinull); + qh_triangulate_null(qh, facet); + } + } + trace2((qh, qh->ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh, 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(qh, merge, (int)sizeof(mergeT)); + if (mergetype == MRGmirror) { + zinc_(Ztrimirror); + qh_triangulate_mirror(qh, facet1, facet2); + } + } + qh_settempfree(qh, &qh->degen_mergeset); + trace2((qh, qh->ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list))); + qh->newvertex_list= new_vertex_list; /* all vertices of new facets */ + qh->visible_list= NULL; + qh_updatevertices(qh /*qh.newvertex_list, empty newfacet_list and visible_list*/); + qh_resetlists(qh, False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/); + + trace2((qh, qh->ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list))); + trace2((qh, qh->ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n")); + FORALLfacet_(new_facet_list) { + if (facet->tricoplanar && !facet->visible) { + FOREACHneighbor_i_(qh, 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, qh->ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n")); + owner= NULL; + visible= NULL; + for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */ + nextfacet= facet->next; + if (facet->visible) { + if (facet->tricoplanar) { /* a null or mirrored facet */ + qh_delfacet(qh, 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, qh->ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n", + visible->id)); + qh_delfacet(qh, visible); + qh->num_visible--; + } + visible= facet; + owner= NULL; + } + }else if (facet->tricoplanar) { + if (facet->f.triowner != visible || visible==NULL) { + qh_fprintf(qh, qh->ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible)); + qh_errexit2(qh, qh_ERRqhull, facet, visible); + } + if (owner) + facet->f.triowner= owner; + else if (!facet->degenerate) { + owner= facet; + nextfacet= visible->next; /* rescan tricoplanar facets with owner, visible!=0 by QH6162 */ + 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(qh, visible); + qh->num_visible--; + } + } + } + if (visible && !owner) { + trace2((qh, qh->ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n", + visible->id)); + qh_delfacet(qh, visible); + qh->num_visible--; + } + qh->NEWfacets= False; + qh->ONLYgood= onlygood; /* restore value */ + if (qh->CHECKfrequently) + qh_checkpolygon(qh, qh->facet_list); + qh->hasTriangulation= True; +} /* triangulate */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate_facet">-</a> + + qh_triangulate_facet(qh, facetA, &firstVertex ) + 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: + only called by qh_triangulate + qh_makenew_nonsimplicial uses neighbor->seen for the same + if qh.TRInormals, newfacet->normal will need qh_free + if qh.TRInormals and qh_AScentrum, newfacet->center will need qh_free + keepcentrum is also set on Zwidefacet in qh_mergefacet + freed by qh_clearcenters + + 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(qhT *qh, facetT *facetA, vertexT **first_vertex) { + facetT *newfacet; + facetT *neighbor, **neighborp; + vertexT *apex; + int numnew=0; + + trace3((qh, qh->ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id)); + + if (qh->IStracing >= 4) + qh_printfacet(qh, 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(qh, facetA->vertices); + } + qh_willdelete(qh, facetA, NULL); + qh->newfacet_list= qh->facet_tail; + facetA->visitid= qh->visit_id; + apex= SETfirstt_(facetA->vertices, vertexT); + qh_makenew_nonsimplicial(qh, 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) { /* 'Q11' triangulate duplicates ->normal and ->center */ + newfacet->keepcentrum= True; + if(facetA->normal){ + newfacet->normal= qh_memalloc(qh, qh->normal_size); + memcpy((char *)newfacet->normal, facetA->normal, qh->normal_size); + } + if (qh->CENTERtype == qh_AScentrum) + newfacet->center= qh_getcentrum(qh, newfacet); + else if (qh->CENTERtype == qh_ASvoronoi && facetA->center){ + newfacet->center= qh_memalloc(qh, qh->center_size); + memcpy((char *)newfacet->center, facetA->center, qh->center_size); + } + }else { + newfacet->keepcentrum= False; + /* one facet will have keepcentrum=True at end of qh_triangulate */ + newfacet->normal= facetA->normal; + newfacet->center= facetA->center; + } + newfacet->offset= facetA->offset; +#if qh_MAXoutside + newfacet->maxoutside= facetA->maxoutside; +#endif + } + qh_matchnewfacets(qh /*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 /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/); + qh_resetlists(qh, False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/); +} /* triangulate_facet */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate_link">-</a> + + qh_triangulate_link(qh, 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(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) { + int errmirror= False; + + trace3((qh, qh->ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n", + oldfacetA->id, oldfacetB->id, facetA->id, facetB->id)); + if (qh_setin(facetA->neighbors, facetB)) { + if (!qh_setin(facetB->neighbors, facetA)) + errmirror= True; + else + qh_appendmergeset(qh, facetA, facetB, MRGmirror, NULL); + }else if (qh_setin(facetB->neighbors, facetA)) + errmirror= True; + if (errmirror) { + qh_fprintf(qh, qh->ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n", + facetA->id, facetB->id, oldfacetA->id, oldfacetB->id); + qh_errexit2(qh, qh_ERRqhull, facetA, facetB); + } + qh_setreplace(qh, facetB->neighbors, oldfacetB, facetA); + qh_setreplace(qh, facetA->neighbors, oldfacetA, facetB); +} /* triangulate_link */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate_mirror">-</a> + + qh_triangulate_mirror(qh, 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(qhT *qh, facetT *facetA, facetT *facetB) { + facetT *neighbor, *neighborB; + int neighbor_i, neighbor_n; + + trace3((qh, qh->ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n", + facetA->id, facetB->id)); + FOREACHneighbor_i_(qh, facetA) { + neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT); + if (neighbor == neighborB) + continue; /* occurs twice */ + qh_triangulate_link(qh, facetA, neighbor, facetB, neighborB); + } + qh_willdelete(qh, facetA, NULL); + qh_willdelete(qh, facetB, NULL); +} /* triangulate_mirror */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate_null">-</a> + + qh_triangulate_null(qh, 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(qhT *qh, facetT *facetA) { + facetT *neighbor, *otherfacet; + + trace3((qh, qh->ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id)); + neighbor= SETfirstt_(facetA->neighbors, facetT); + otherfacet= SETsecondt_(facetA->neighbors, facetT); + qh_triangulate_link(qh, facetA, neighbor, facetA, otherfacet); + qh_willdelete(qh, facetA, NULL); +} /* triangulate_null */ + +#else /* qh_NOmerge */ +void qh_triangulate(qhT *qh) { +} +#endif /* qh_NOmerge */ + + /*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="vertexintersect">-</a> + + qh_vertexintersect(qh, vertexsetA, vertexsetB ) + intersects two vertex sets (inverse id ordered) + vertexsetA is a temporary set at the top of qh->qhmem.tempstack + + returns: + replaces vertexsetA with the intersection + + notes: + could overwrite vertexsetA if currently too slow +*/ +void qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB) { + setT *intersection; + + intersection= qh_vertexintersect_new(qh, *vertexsetA, vertexsetB); + qh_settempfree(qh, vertexsetA); + *vertexsetA= intersection; + qh_settemppush(qh, intersection); +} /* vertexintersect */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="vertexintersect_new">-</a> + + qh_vertexintersect_new(qh, ) + intersects two vertex sets (inverse id ordered) + + returns: + a new set +*/ +setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB) { + setT *intersection= qh_setnew(qh, qh->hull_dim - 1); + vertexT **vertexA= SETaddr_(vertexsetA, vertexT); + vertexT **vertexB= SETaddr_(vertexsetB, vertexT); + + while (*vertexA && *vertexB) { + if (*vertexA == *vertexB) { + qh_setappend(qh, &intersection, *vertexA); + vertexA++; vertexB++; + }else { + if ((*vertexA)->id > (*vertexB)->id) + vertexA++; + else + vertexB++; + } + } + return intersection; +} /* vertexintersect_new */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="vertexneighbors">-</a> + + qh_vertexneighbors(qh) + 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(qhT *qh /*qh.facet_list*/) { + facetT *facet; + vertexT *vertex, **vertexp; + + if (qh->VERTEXneighbors) + return; + trace1((qh, qh->ferr, 1035, "qh_vertexneighbors: determining 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, qh->hull_dim); + } + qh_setappend(qh, &vertex->neighbors, facet); + } + } + qh->VERTEXneighbors= True; +} /* vertexneighbors */ + +/*-<a href="qh-poly_r.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 */
+ C/poly_r.c view
@@ -0,0 +1,1205 @@+/*<html><pre> -<a href="qh-poly_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly_r.c + implements polygons and simplices + + see qh-poly_r.htm, poly_r.h and libqhull_r.h + + infrequent code is in poly2_r.c + (all but top 50 and their callers 12/3/95) + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/poly_r.c#3 $$Change: 2064 $ + $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*======== functions in alphabetical order ==========*/ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="appendfacet">-</a> + + qh_appendfacet(qh, 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(qhT *qh, 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, qh->ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id)); +} /* appendfacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="appendvertex">-</a> + + qh_appendvertex(qh, 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(qhT *qh, 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, qh->ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id)); +} /* appendvertex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="attachnewfacets">-</a> + + qh_attachnewfacets(qh, ) + 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(qhT *qh /* qh.visible_list, newfacet_list */) { + facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible; + ridgeT *ridge, **ridgep; + + qh->NEWfacets= True; + trace3((qh, qh->ferr, 3012, "qh_attachnewfacets: delete interior ridges\n")); + qh->visit_id++; + FORALLvisible_facets { + visible->visitid= qh->visit_id; + if (visible->ridges) { + FOREACHridge_(visible->ridges) { + neighbor= otherfacet_(ridge, visible); + if (neighbor->visitid == qh->visit_id + || (!neighbor->visible && neighbor->simplicial)) { + if (!neighbor->visible) /* delete ridge for simplicial horizon */ + qh_setdel(neighbor->ridges, ridge); + qh_setfree(qh, &(ridge->vertices)); /* delete on 2nd visit */ + qh_memfree(qh, ridge, (int)sizeof(ridgeT)); + } + } + SETfirst_(visible->ridges)= NULL; + } + SETfirst_(visible->neighbors)= NULL; + } + trace1((qh, qh->ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n")); + FORALLnew_facets { + horizon= SETfirstt_(newfacet->neighbors, facetT); + if (horizon->simplicial) { + visible= NULL; + FOREACHneighbor_(horizon) { /* may have more than one horizon ridge */ + if (neighbor->visible) { + if (visible) { + if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices, + SETindex_(horizon->neighbors, neighbor))) { + visible= neighbor; + break; + } + }else + visible= neighbor; + } + } + if (visible) { + visible->f.replace= newfacet; + qh_setreplace(qh, horizon->neighbors, visible, newfacet); + }else { + qh_fprintf(qh, qh->ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n", + horizon->id, newfacet->id); + qh_errexit2(qh, 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(qh, horizon->neighbors, + SETindex_(horizon->neighbors, neighbor)); + neighborp--; /* repeat */ + } + } + qh_setappend(qh, &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_r.htm#TOC" + >-------------------------------</a><a name="checkflipped">-</a> + + qh_checkflipped(qh, 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(qhT *qh, facetT *facet, realT *distp, boolT allerror) { + realT dist; + + if (facet->flipped && !distp) + return False; + zzinc_(Zdistcheck); + qh_distplane(qh, 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, qh->ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n", + facet->id, dist, qh->furthest_id)); + qh_precision(qh, "flipped facet"); + return False; + } + return True; +} /* checkflipped */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="delfacet">-</a> + + qh_delfacet(qh, facet ) + removes facet from facet_list and frees up its memory + + notes: + assumes vertices and ridges already freed +*/ +void qh_delfacet(qhT *qh, facetT *facet) { + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + trace4((qh, qh->ferr, 4046, "qh_delfacet: delete f%d\n", facet->id)); + if (facet == qh->tracefacet) + qh->tracefacet= NULL; + if (facet == qh->GOODclosest) + qh->GOODclosest= NULL; + qh_removefacet(qh, facet); + if (!facet->tricoplanar || facet->keepcentrum) { + qh_memfree_(qh, facet->normal, qh->normal_size, freelistp); + if (qh->CENTERtype == qh_ASvoronoi) { /* braces for macro calls */ + qh_memfree_(qh, facet->center, qh->center_size, freelistp); + }else /* AScentrum */ { + qh_memfree_(qh, facet->center, qh->normal_size, freelistp); + } + } + qh_setfree(qh, &(facet->neighbors)); + if (facet->ridges) + qh_setfree(qh, &(facet->ridges)); + qh_setfree(qh, &(facet->vertices)); + if (facet->outsideset) + qh_setfree(qh, &(facet->outsideset)); + if (facet->coplanarset) + qh_setfree(qh, &(facet->coplanarset)); + qh_memfree_(qh, facet, (int)sizeof(facetT), freelistp); +} /* delfacet */ + + +/*-<a href="qh-poly_r.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(qhT *qh /*qh.visible_list*/) { + facetT *visible, *nextfacet; + vertexT *vertex, **vertexp; + int numvisible= 0, numdel= qh_setsize(qh, qh->del_vertices); + + trace1((qh, qh->ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n", + qh->num_visible, numdel)); + for (visible= qh->visible_list; visible && visible->visible; + visible= nextfacet) { /* deleting current */ + nextfacet= visible->next; + numvisible++; + qh_delfacet(qh, visible); + } + if (numvisible != qh->num_visible) { + qh_fprintf(qh, qh->ferr, 6103, "qhull internal error (qh_deletevisible): qh->num_visible %d is not number of visible facets %d\n", + qh->num_visible, numvisible); + qh_errexit(qh, 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(qh, vertex); + qh_settruncate(qh, qh->del_vertices, 0); +} /* deletevisible */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="facetintersect">-</a> + + qh_facetintersect(qh, 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(qhT *qh, facetT *facetA, facetT *facetB, + int *skipA,int *skipB, int prepend) { + setT *intersect; + int dim= qh->hull_dim, i, j; + facetT **neighborsA, **neighborsB; + + neighborsA= SETaddr_(facetA->neighbors, facetT); + neighborsB= SETaddr_(facetB->neighbors, facetT); + i= j= 0; + if (facetB == *neighborsA++) + *skipA= 0; + else if (facetB == *neighborsA++) + *skipA= 1; + else if (facetB == *neighborsA++) + *skipA= 2; + else { + for (i=3; i < dim; i++) { + if (facetB == *neighborsA++) { + *skipA= i; + break; + } + } + } + if (facetA == *neighborsB++) + *skipB= 0; + else if (facetA == *neighborsB++) + *skipB= 1; + else if (facetA == *neighborsB++) + *skipB= 2; + else { + for (j=3; j < dim; j++) { + if (facetA == *neighborsB++) { + *skipB= j; + break; + } + } + } + if (i >= dim || j >= dim) { + qh_fprintf(qh, qh->ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n", + facetA->id, facetB->id); + qh_errexit2(qh, qh_ERRqhull, facetA, facetB); + } + intersect= qh_setnew_delnthsorted(qh, facetA->vertices, qh->hull_dim, *skipA, prepend); + trace4((qh, qh->ferr, 4047, "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_r.htm#TOC" + >-------------------------------</a><a name="gethash">-</a> + + qh_gethash(qh, hashsize, set, size, firstindex, skipelem ) + return hashvalue for a set with firstindex and skipelem + + notes: + returned hash is in [0,hashsize) + assumes at least firstindex+1 elements + assumes skipelem is NULL, in set, or part of hash + + hashes memory addresses which may change over different runs of the same data + using sum for hash does badly in high d +*/ +int qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem) { + void **elemp= SETelemaddr_(set, firstindex, void); + ptr_intT hash = 0, elem; + unsigned result; + int i; +#ifdef _MSC_VER /* Microsoft Visual C++ -- warn about 64-bit issues */ +#pragma warning( push) /* WARN64 -- ptr_intT holds a 64-bit pointer */ +#pragma warning( disable : 4311) /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */ +#endif + + switch (size-firstindex) { + case 1: + hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem; + break; + case 2: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem; + break; + case 3: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + - (ptr_intT) skipelem; + break; + case 4: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] - (ptr_intT) skipelem; + break; + case 5: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem; + break; + case 6: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5] + - (ptr_intT) skipelem; + break; + default: + hash= 0; + i= 3; + do { /* this is about 10% in 10-d */ + if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) { + hash ^= (elem << i) + (elem >> (32-i)); + i += 3; + if (i >= 32) + i -= 32; + } + }while (*elemp); + break; + } + if (hashsize<0) { + qh_fprintf(qh, qh->ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize); + qh_errexit2(qh, qh_ERRqhull, NULL, NULL); + } + result= (unsigned)hash; + result %= (unsigned)hashsize; + /* result= 0; for debugging */ + return result; +#ifdef _MSC_VER +#pragma warning( pop) +#endif +} /* gethash */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="makenewfacet">-</a> + + qh_makenewfacet(qh, 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(qhT *qh, setT *vertices, boolT toporient,facetT *horizon) { + facetT *newfacet; + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newlist) { + qh_removevertex(qh, vertex); + qh_appendvertex(qh, vertex); + } + } + newfacet= qh_newfacet(qh); + newfacet->vertices= vertices; + newfacet->toporient= (unsigned char)toporient; + if (horizon) + qh_setappend(qh, &(newfacet->neighbors), horizon); + qh_appendfacet(qh, newfacet); + return(newfacet); +} /* makenewfacet */ + + +/*-<a href="qh-poly_r.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(qhT *qh /* qh.newfacet_list */) { + facetT *newfacet; + + FORALLnew_facets { + if (!newfacet->mergehorizon) + qh_setfacetplane(qh, newfacet); + } + if (qh->JOGGLEmax < REALmax/2) + minimize_(qh->min_vertex, -wwval_(Wnewvertexmax)); +} /* makenewplanes */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="makenew_nonsimplicial">-</a> + + qh_makenew_nonsimplicial(qh, 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(qhT *qh, facetT *visible, vertexT *apex, int *numnew) { + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + 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(qh, &(ridge->vertices)); /* delete on 2nd visit */ + qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp); + } + } + }else { /* neighbor is an horizon facet */ + toporient= (ridge->top == visible); + vertices= qh_setnew(qh, qh->hull_dim); /* makes sure this is quick */ + qh_setappend(qh, &vertices, apex); + qh_setappend_set(qh, &vertices, ridge->vertices); + newfacet= qh_makenewfacet(qh, 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(qh, &(newfacet->ridges), ridge); + }else { /* qh_attachnewfacets */ + if (neighbor->seen) { + if (neighbor->simplicial) { + qh_fprintf(qh, qh->ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n", + neighbor->id, visible->id); + qh_errexit2(qh, qh_ERRqhull, neighbor, visible); + } + qh_setappend(qh, &(neighbor->neighbors), newfacet); + }else + qh_setreplace(qh, neighbor->neighbors, visible, newfacet); + if (neighbor->simplicial) { + qh_setdel(neighbor->ridges, ridge); + qh_setfree(qh, &(ridge->vertices)); + qh_memfree(qh, ridge, (int)sizeof(ridgeT)); + }else { + qh_setappend(qh, &(newfacet->ridges), ridge); + if (toporient) + ridge->top= newfacet; + else + ridge->bottom= newfacet; + } + trace4((qh, qh->ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n", + newfacet->id, apex->id, ridgeid, neighbor->id)); + } + } + neighbor->seen= True; + } /* for each ridge */ + if (!qh->ONLYgood) + SETfirst_(visible->ridges)= NULL; + return newfacet; +} /* makenew_nonsimplicial */ +#else /* qh_NOmerge */ +facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) { + return NULL; +} +#endif /* qh_NOmerge */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="makenew_simplicial">-</a> + + qh_makenew_simplicial(qh, 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(qhT *qh, facetT *visible, vertexT *apex, int *numnew) { + facetT *neighbor, **neighborp, *newfacet= NULL; + setT *vertices; + boolT flip, toporient; + int horizonskip= 0, visibleskip= 0; + + FOREACHneighbor_(visible) { + if (!neighbor->seen && !neighbor->visible) { + vertices= qh_facetintersect(qh, 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(qh, 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, qh->ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n", + newfacet->id, toporient, apex->id, neighbor->id, horizonskip, + neighbor->toporient, visible->id, visibleskip, flip)); + } + } + return newfacet; +} /* makenew_simplicial */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="matchneighbor">-</a> + + qh_matchneighbor(qh, 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(qhT *qh, facetT *newfacet, int newskip, int hashsize, int *hashcount) { + boolT newfound= False; /* True, if new facet is already in hash chain */ + boolT same, ismatch; + int hash, scan; + facetT *facet, *matchfacet; + int skip, matchskip; + + hash= qh_gethash(qh, hashsize, newfacet->vertices, qh->hull_dim, 1, + SETelem_(newfacet->vertices, newskip)); + trace4((qh, qh->ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n", + newfacet->id, newskip, hash, *hashcount)); + zinc_(Zhashlookup); + for (scan= hash; (facet= SETelemt_(qh->hash_table, scan, facetT)); + scan= (++scan >= hashsize ? 0 : scan)) { + if (facet == newfacet) { + newfound= True; + continue; + } + zinc_(Zhashtests); + if (qh_matchvertices(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + if (SETelem_(newfacet->vertices, newskip) == + SETelem_(facet->vertices, skip)) { + qh_precision(qh, "two facets with the same vertices"); + qh_fprintf(qh, qh->ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d. Can not force output.\n", + facet->id, newfacet->id); + qh_errexit2(qh, qh_ERRprec, facet, newfacet); + } + ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient))); + matchfacet= SETelemt_(facet->neighbors, skip, facetT); + if (ismatch && !matchfacet) { + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= facet; + (*hashcount)--; + trace4((qh, qh->ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n", + facet->id, skip, newfacet->id, newskip)); + return; + } + if (!qh->PREmerge && !qh->MERGEexact) { + qh_precision(qh, "a ridge with more than two neighbors"); + qh_fprintf(qh, qh->ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue.\n", + facet->id, newfacet->id, getid_(matchfacet)); + qh_errexit2(qh, qh_ERRprec, facet, newfacet); + } + SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge; + newfacet->dupridge= True; + if (!newfacet->normal) + qh_setfacetplane(qh, newfacet); + qh_addhash(newfacet, qh->hash_table, hashsize, hash); + (*hashcount)++; + if (!facet->normal) + qh_setfacetplane(qh, facet); + if (matchfacet != qh_DUPLICATEridge) { + SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge; + facet->dupridge= True; + if (!facet->normal) + qh_setfacetplane(qh, facet); + if (matchfacet) { + matchskip= qh_setindex(matchfacet->neighbors, facet); + if (matchskip<0) { + qh_fprintf(qh, qh->ferr, 6260, "qhull internal error (qh_matchneighbor): matchfacet f%d is in f%d neighbors but not vice versa. Can not continue.\n", + matchfacet->id, facet->id); + qh_errexit2(qh, qh_ERRqhull, matchfacet, facet); + } + SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */ + matchfacet->dupridge= True; + if (!matchfacet->normal) + qh_setfacetplane(qh, matchfacet); + qh_addhash(matchfacet, qh->hash_table, hashsize, hash); + *hashcount += 2; + } + } + trace4((qh, qh->ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n", + newfacet->id, newskip, facet->id, skip, + (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)), + ismatch, hash)); + return; /* end of duplicate ridge */ + } + } + if (!newfound) + SETelem_(qh->hash_table, scan)= newfacet; /* same as qh_addhash */ + (*hashcount)++; + trace4((qh, qh->ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n", + newfacet->id, newskip, hash)); +} /* matchneighbor */ + + +/*-<a href="qh-poly_r.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(qhT *qh /* 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, qh->ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n")); + FORALLnew_facets { + numnew++; + { /* inline qh_setzero(qh, 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(qh, numnew*(qh->hull_dim-1)); /* twice what is normally needed, + but every ridge could be DUPLICATEridge */ + hashsize= qh_setsize(qh, qh->hash_table); + FORALLnew_facets { + for (newskip=1; newskip<qh->hull_dim; newskip++) /* furthest/horizon already matched */ + /* hashsize>0 because hull_dim>1 and numnew>0 */ + qh_matchneighbor(qh, 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) { + qh_fprintf(qh, qh->ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n", + newfacet->id, hashcount, count); + qh_errexit(qh, qh_ERRqhull, newfacet, NULL); + } + } +#endif /* end of trap code */ + } + if (hashcount) { + FORALLnew_facets { + if (newfacet->dupridge) { + FOREACHneighbor_i_(qh, newfacet) { + if (neighbor == qh_DUPLICATEridge) { + qh_matchduplicates(qh, newfacet, neighbor_i, hashsize, &hashcount); + /* this may report MERGEfacet */ + } + } + } + } + } + if (hashcount) { + qh_fprintf(qh, qh->ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n", + hashcount); + qh_printhashtable(qh, qh->ferr); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +#ifndef qh_NOtrace + if (qh->IStracing >= 2) { + FOREACHfacet_i_(qh, qh->hash_table) { + if (!facet) + numfree++; + } + qh_fprintf(qh, qh->ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries . hashsize %d\n", + numnew, numfree, qh_setsize(qh, qh->hash_table)); + } +#endif /* !qh_NOtrace */ + qh_setfree(qh, &qh->hash_table); + if (qh->PREmerge || qh->MERGEexact) { + if (qh->IStracing >= 4) + qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL); + FORALLnew_facets { + if (newfacet->normal) + qh_checkflipped(qh, newfacet, NULL, qh_ALL); + } + }else if (qh->FORCEoutput) + qh_checkflipped_all(qh, qh->newfacet_list); /* prints warnings for flipped */ +} /* matchnewfacets */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="matchvertices">-</a> + + qh_matchvertices(qh, 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(qhT *qh, int firstindex, setT *verticesA, int skipA, + setT *verticesB, int *skipB, boolT *same) { + vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp; + + elemAp= SETelemaddr_(verticesA, firstindex, vertexT); + elemBp= SETelemaddr_(verticesB, firstindex, vertexT); + skipAp= SETelemaddr_(verticesA, skipA, vertexT); + do if (elemAp != skipAp) { + while (*elemAp != *elemBp++) { + if (skipBp) + return False; + skipBp= elemBp; /* one extra like FOREACH */ + } + }while (*(++elemAp)); + if (!skipBp) + skipBp= ++elemBp; + *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */ + *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */ + trace4((qh, qh->ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n", + skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same)); + return(True); +} /* matchvertices */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="newfacet">-</a> + + qh_newfacet(qh) + return a new facet + + returns: + all fields initialized or cleared (NULL) + preallocates neighbors set +*/ +facetT *qh_newfacet(qhT *qh) { + facetT *facet; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_(qh, (int)sizeof(facetT), freelistp, facet, facetT); + memset((char *)facet, (size_t)0, sizeof(facetT)); + if (qh->facet_id == qh->tracefacet_id) + qh->tracefacet= facet; + facet->id= qh->facet_id++; + facet->neighbors= qh_setnew(qh, 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, qh->ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id)); + return(facet); +} /* newfacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="newridge">-</a> + + qh_newridge() + return a new ridge +*/ +ridgeT *qh_newridge(qhT *qh) { + ridgeT *ridge; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_(qh, (int)sizeof(ridgeT), freelistp, ridge, ridgeT); + memset((char *)ridge, (size_t)0, sizeof(ridgeT)); + zinc_(Ztotridges); + if (qh->ridge_id == UINT_MAX) { + qh_fprintf(qh, qh->ferr, 7074, "\ +qhull warning: more than 2^32 ridges. Qhull results are OK. Since the ridge ID wraps around to 0, two ridges may have the same identifier.\n"); + } + ridge->id= qh->ridge_id++; + trace4((qh, qh->ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id)); + return(ridge); +} /* newridge */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="pointid">-</a> + + qh_pointid(qh, point ) + return id for a point, + returns qh_IDnone(-3) if null, qh_IDinterior(-2) if interior, or qh_IDunknown(-1) if not known + + alternative code if point is in qh.first_point... + unsigned long id; + id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size; + + notes: + Valid points are non-negative + WARN64 -- id truncated to 32-bits, at most 2G points + NOerrors returned (QhullPoint::id) + if point not in point array + the code does a comparison of unrelated pointers. +*/ +int qh_pointid(qhT *qh, pointT *point) { + ptr_intT offset, id; + + if (!point || !qh) + return qh_IDnone; + else if (point == qh->interior_point) + return qh_IDinterior; + else if (point >= qh->first_point + && point < qh->first_point + qh->num_points * qh->hull_dim) { + offset= (ptr_intT)(point - qh->first_point); + id= offset / qh->hull_dim; + }else if ((id= qh_setindex(qh->other_points, point)) != -1) + id += qh->num_points; + else + return qh_IDunknown; + return (int)id; +} /* pointid */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="removefacet">-</a> + + qh_removefacet(qh, 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(qhT *qh, 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, qh->ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id)); +} /* removefacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="removevertex">-</a> + + qh_removevertex(qh, vertex ) + unlinks vertex from qh.vertex_list, + + returns: + updates qh.vertex_list .newvertex_list + decrements qh.num_vertices +*/ +void qh_removevertex(qhT *qh, 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, qh->ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id)); +} /* removevertex */ + + +/*-<a href="qh-poly_r.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(qhT *qh /*qh.newvertex_list, newfacet_list, visible_list*/) { + facetT *newfacet= NULL, *neighbor, **neighborp, *visible; + vertexT *vertex, **vertexp; + + trace3((qh, qh->ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n")); + if (qh->VERTEXneighbors) { + FORALLvertex_(qh->newvertex_list) { + FOREACHneighbor_(vertex) { + if (neighbor->visible) + SETref_(neighbor)= NULL; + } + qh_setcompact(qh, vertex->neighbors); + } + FORALLnew_facets { + FOREACHvertex_(newfacet->vertices) + qh_setappend(qh, &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, &qh->del_vertices, vertex); + trace2((qh, qh->ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n", + qh_pointid(qh, vertex->point), vertex->id, visible->id)); + } + } + } + } + }else { /* !VERTEXneighbors */ + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newlist && !vertex->deleted) { + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + trace2((qh, qh->ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n", + qh_pointid(qh, vertex->point), vertex->id, visible->id)); + } + } + } + } +} /* updatevertices */ + + +
+ C/poly_r.h view
@@ -0,0 +1,303 @@+/*<html><pre> -<a href="qh-poly_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly_r.h + header file for poly_r.c and poly2_r.c + + see qh-poly_r.htm, libqhull_r.h and poly_r.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/poly_r.h#5 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ +*/ + +#ifndef qhDEFpoly +#define qhDEFpoly 1 + +#include "libqhull_r.h" + +/*=============== constants ========================== */ + +/*-<a href="qh-geom_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHneighborA_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighborA) + +/*-<a href="qh-poly_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible) + +/*-<a href="qh-poly_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet) + +/*-<a href="qh-poly_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA) + +/*-<a href="qh-poly_r.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_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex) + + +/*=============== prototypes poly_r.c in alphabetical order ================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_appendfacet(qhT *qh, facetT *facet); +void qh_appendvertex(qhT *qh, vertexT *vertex); +void qh_attachnewfacets(qhT *qh /* qh.visible_list, qh.newfacet_list */); +boolT qh_checkflipped(qhT *qh, facetT *facet, realT *dist, boolT allerror); +void qh_delfacet(qhT *qh, facetT *facet); +void qh_deletevisible(qhT *qh /* qh.visible_list, qh.horizon_list */); +setT *qh_facetintersect(qhT *qh, facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra); +int qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem); +facetT *qh_makenewfacet(qhT *qh, setT *vertices, boolT toporient, facetT *facet); +void qh_makenewplanes(qhT *qh /* qh.newfacet_list */); +facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew); +facetT *qh_makenew_simplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew); +void qh_matchneighbor(qhT *qh, facetT *newfacet, int newskip, int hashsize, + int *hashcount); +void qh_matchnewfacets(qhT *qh); +boolT qh_matchvertices(qhT *qh, int firstindex, setT *verticesA, int skipA, + setT *verticesB, int *skipB, boolT *same); +facetT *qh_newfacet(qhT *qh); +ridgeT *qh_newridge(qhT *qh); +int qh_pointid(qhT *qh, pointT *point); +void qh_removefacet(qhT *qh, facetT *facet); +void qh_removevertex(qhT *qh, vertexT *vertex); +void qh_updatevertices(qhT *qh); + + +/*========== -prototypes poly2_r.c in alphabetical order ===========*/ + +void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash); +void qh_check_bestdist(qhT *qh); +void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2); +void qh_check_maxout(qhT *qh); +void qh_check_output(qhT *qh); +void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2); +void qh_check_points(qhT *qh); +void qh_checkconvex(qhT *qh, facetT *facetlist, int fault); +void qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp); +void qh_checkflipped_all(qhT *qh, facetT *facetlist); +void qh_checkpolygon(qhT *qh, facetT *facetlist); +void qh_checkvertex(qhT *qh, vertexT *vertex); +void qh_clearcenters(qhT *qh, qh_CENTER type); +void qh_createsimplex(qhT *qh, setT *vertices); +void qh_delridge(qhT *qh, ridgeT *ridge); +void qh_delvertex(qhT *qh, vertexT *vertex); +setT *qh_facet3vertex(qhT *qh, facetT *facet); +facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside); +facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart); +facetT *qh_findfacet_all(qhT *qh, pointT *point, realT *bestdist, boolT *isoutside, + int *numpart); +int qh_findgood(qhT *qh, facetT *facetlist, int goodhorizon); +void qh_findgood_all(qhT *qh, facetT *facetlist); +void qh_furthestnext(qhT *qh /* qh.facet_list */); +void qh_furthestout(qhT *qh, facetT *facet); +void qh_infiniteloop(qhT *qh, facetT *facet); +void qh_initbuild(qhT *qh); +void qh_initialhull(qhT *qh, setT *vertices); +setT *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints); +vertexT *qh_isvertex(pointT *point, setT *vertices); +vertexT *qh_makenewfacets(qhT *qh, pointT *point /*horizon_list, visible_list*/); +void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount); +void qh_nearcoplanar(qhT *qh /* qh.facet_list */); +vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp); +int qh_newhashtable(qhT *qh, int newsize); +vertexT *qh_newvertex(qhT *qh, pointT *point); +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp); +void qh_outcoplanar(qhT *qh /* qh.facet_list */); +pointT *qh_point(qhT *qh, int id); +void qh_point_add(qhT *qh, setT *set, pointT *point, void *elem); +setT *qh_pointfacet(qhT *qh /*qh.facet_list*/); +setT *qh_pointvertex(qhT *qh /*qh.facet_list*/); +void qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist); +void qh_printhashtable(qhT *qh, FILE *fp); +void qh_printlists(qhT *qh); +void qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /*qh.newvertex_list qh.newfacet_list qh.visible_list*/); +void qh_setvoronoi_all(qhT *qh); +void qh_triangulate(qhT *qh /*qh.facet_list*/); +void qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex); +void qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB); +void qh_triangulate_mirror(qhT *qh, facetT *facetA, facetT *facetB); +void qh_triangulate_null(qhT *qh, facetT *facetA); +void qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB); +setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB); +void qh_vertexneighbors(qhT *qh /*qh.facet_list*/); +boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFpoly */
+ C/qhull_ra.h view
@@ -0,0 +1,158 @@+/*<html><pre> -<a href="qh-qhull_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + qhull_ra.h + all header files for compiling qhull with reentrant code + included before C++ headers for user_r.h:QHULL_CRTDBG + + see qh-qhull.htm + + see libqhull_r.h for user-level definitions + + see user_r.h for user-definable constants + + defines internal functions for libqhull_r.c global_r.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/qhull_ra.h#6 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ + + Notes: grep for ((" and (" to catch fprintf("lkasdjf"); + full parens around (x?y:z) + use '#include "libqhull_r/qhull_ra.h"' to avoid name clashes +*/ + +#ifndef qhDEFqhulla +#define qhDEFqhulla 1 + +#include "libqhull_r.h" /* Includes user_r.h and data types */ + +#include "stat_r.h" +#include "random_r.h" +#include "mem_r.h" +#include "qset_r.h" +#include "geom_r.h" +#include "merge_r.h" +#include "poly_r.h" +#include "io_r.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> +#include <stdio.h> +#include <stdlib.h> +/*** uncomment here and qset_r.c + if string.h does not define memcpy() +#include <memory.h> +*/ + +#if qh_CLOCKtype == 2 /* defined in user_r.h from libqhull_r.h */ +#include <sys/types.h> +#include <sys/times.h> +#include <unistd.h> +#endif + +#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +#pragma warning( disable : 4100) /* unreferenced formal parameter */ +#pragma warning( disable : 4127) /* conditional expression is constant */ +#pragma warning( disable : 4706) /* assignment within conditional function */ +#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */ +#endif + +/* ======= -macros- =========== */ + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="traceN">-</a> + + traceN((qh, qh->ferr, 0Nnnn, "format\n", vars)); + calls qh_fprintf if qh.IStracing >= N + + Add debugging traps to the end of qh_fprintf + + notes: + removing tracing reduces code size but doesn't change execution speed +*/ +#ifndef qh_NOtrace +#define trace0(args) {if (qh->IStracing) qh_fprintf args;} +#define trace1(args) {if (qh->IStracing >= 1) qh_fprintf args;} +#define trace2(args) {if (qh->IStracing >= 2) qh_fprintf args;} +#define trace3(args) {if (qh->IStracing >= 3) qh_fprintf args;} +#define trace4(args) {if (qh->IStracing >= 4) qh_fprintf args;} +#define trace5(args) {if (qh->IStracing >= 5) qh_fprintf args;} +#else /* qh_NOtrace */ +#define trace0(args) {} +#define trace1(args) {} +#define trace2(args) {} +#define trace3(args) {} +#define trace4(args) {} +#define trace5(args) {} +#endif /* qh_NOtrace */ + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="QHULL_UNUSED">-</a> + + Define an unused variable to avoid compiler warnings + + Derived from Qt's corelib/global/qglobal.h + +*/ + +#if defined(__cplusplus) && defined(__INTEL_COMPILER) && !defined(QHULL_OS_WIN) +template <typename T> +inline void qhullUnused(T &x) { (void)x; } +# define QHULL_UNUSED(x) qhullUnused(x); +#else +# define QHULL_UNUSED(x) (void)x; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/***** -libqhull_r.c prototypes (alphabetical after qhull) ********************/ + +void qh_qhull(qhT *qh); +boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist); +void qh_buildhull(qhT *qh); +void qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet); +void qh_build_withrestart(qhT *qh); +void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet); +void qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible,int *goodhorizon); +pointT *qh_nextfurthest(qhT *qh, facetT **visible); +void qh_partitionall(qhT *qh, setT *vertices, pointT *points,int npoints); +void qh_partitioncoplanar(qhT *qh, pointT *point, facetT *facet, realT *dist); +void qh_partitionpoint(qhT *qh, pointT *point, facetT *facet); +void qh_partitionvisible(qhT *qh, boolT allpoints, int *numpoints); +void qh_precision(qhT *qh, const char *reason); +void qh_printsummary(qhT *qh, FILE *fp); + +/***** -global_r.c internal prototypes (alphabetical) ***********************/ + +void qh_appendprint(qhT *qh, qh_PRINT format); +void qh_freebuild(qhT *qh, boolT allmem); +void qh_freebuffers(qhT *qh); +void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); + +/***** -stat_r.c internal prototypes (alphabetical) ***********************/ + +void qh_allstatA(qhT *qh); +void qh_allstatB(qhT *qh); +void qh_allstatC(qhT *qh); +void qh_allstatD(qhT *qh); +void qh_allstatE(qhT *qh); +void qh_allstatE2(qhT *qh); +void qh_allstatF(qhT *qh); +void qh_allstatG(qhT *qh); +void qh_allstatH(qhT *qh); +void qh_freebuffers(qhT *qh); +void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFqhulla */
+ C/qset_r.c view
@@ -0,0 +1,1340 @@+/*<html><pre> -<a href="qh-set_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + qset_r.c + implements set manipulations needed for quickhull + + see qh-set_r.htm and qset_r.h + + Be careful of strict aliasing (two pointers of different types + that reference the same location). The last slot of a set is + either the actual size of the set plus 1, or the NULL terminator + of the set (i.e., setelemT). + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/qset_r.c#3 $$Change: 2062 $ + $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ +*/ + +#include "libqhull_r.h" /* for qhT and QHULL_CRTDBG */ +#include "qset_r.h" +#include "mem_r.h" +#include <stdio.h> +#include <string.h> +/*** uncomment here and qhull_ra.h + if string.h does not define memcpy() +#include <memory.h> +*/ + +#ifndef qhDEFlibqhull +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +void qh_errexit(qhT *qh, int exitcode, facetT *, ridgeT *); +void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ); +# ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +# pragma warning( disable : 4127) /* conditional expression is constant */ +# pragma warning( disable : 4706) /* assignment within conditional function */ +# endif +#endif + +/*=============== internal macros ===========================*/ + +/*============ functions in alphabetical order ===================*/ + +/*-<a href="qh-set_r.htm#TOC" + >--------------------------------<a name="setaddnth">-</a> + + qh_setaddnth(qh, 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(qhT *qh, setT **setp, int nth, void *newelem) { + int oldsize, i; + setelemT *sizep; /* avoid strict aliasing */ + setelemT *oldp, *newp; + + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(qh, setp); + sizep= SETsizeaddr_(*setp); + } + oldsize= sizep->i - 1; + if (nth < 0 || nth > oldsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qh, qh->qhmem.ferr, "", *setp); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + sizep->i++; + oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void); /* NULL */ + newp= oldp+1; + for (i=oldsize-nth+1; i--; ) /* move at least NULL */ + (newp--)->p= (oldp--)->p; /* may overwrite *sizep */ + newp->p= newelem; +} /* setaddnth */ + + +/*-<a href="qh-set_r.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(qhT *qh, 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(qh, setp, newindex, newelem); +} /* setaddsorted */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setappend">-</a> + + qh_setappend(qh, 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(qhT *qh, setT **setp, void *newelem) { + setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */ + setelemT *endp; + int count; + + if (!newelem) + return; + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(qh, setp); + sizep= SETsizeaddr_(*setp); + } + count= (sizep->i)++ - 1; + endp= (setelemT *)SETelemaddr_(*setp, count, void); + (endp++)->p= newelem; + endp->p= NULL; +} /* setappend */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setappend_set">-</a> + + qh_setappend_set(qh, 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(qhT *qh, setT **setp, setT *setA) { + int sizeA, size; + setT *oldset; + setelemT *sizep; + + if (!setA) + return; + SETreturnsize_(setA, sizeA); + if (!*setp) + *setp= qh_setnew(qh, sizeA); + sizep= SETsizeaddr_(*setp); + if (!(size= sizep->i)) + size= (*setp)->maxsize; + else + size--; + if (size + sizeA > (*setp)->maxsize) { + oldset= *setp; + *setp= qh_setcopy(qh, oldset, sizeA); + qh_setfree(qh, &oldset); + sizep= SETsizeaddr_(*setp); + } + if (sizeA > 0) { + sizep->i= size+sizeA+1; /* memcpy may overwrite */ + memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize); + } +} /* setappend_set */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setappend2ndlast">-</a> + + qh_setappend2ndlast(qh, 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(qhT *qh, setT **setp, void *newelem) { + setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */ + setelemT *endp, *lastp; + int count; + + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(qh, setp); + sizep= SETsizeaddr_(*setp); + } + count= (sizep->i)++ - 1; + endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */ + lastp= endp-1; + *(endp++)= *lastp; + endp->p= NULL; /* may overwrite *sizep */ + lastp->p= newelem; +} /* setappend2ndlast */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setcheck">-</a> + + qh_setcheck(qh, 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(qhT *qh, setT *set, const char *tname, unsigned id) { + int maxsize, size; + int waserr= 0; + + if (!set) + return; + SETreturnsize_(set, size); + maxsize= set->maxsize; + if (size > maxsize || !maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n", + size, tname, id, maxsize); + waserr= 1; + }else if (set->e[size].p) { + qh_fprintf(qh, qh->qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n", + tname, id, size-1, maxsize); + waserr= 1; + } + if (waserr) { + qh_setprint(qh, qh->qhmem.ferr, "ERRONEOUS", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } +} /* setcheck */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setcompact">-</a> + + qh_setcompact(qh, 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(qhT *qh, 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(qh, set, (int)(destp-firstp)); /* WARN64 */ +} /* setcompact */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setcopy">-</a> + + qh_setcopy(qh, 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(qhT *qh, setT *set, int extra) { + setT *newset; + int size; + + if (extra < 0) + extra= 0; + SETreturnsize_(set, size); + newset= qh_setnew(qh, size+extra); + SETsizeaddr_(newset)->i= size+1; /* memcpy may overwrite */ + memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize); + return(newset); +} /* setcopy */ + + +/*-<a href="qh-set_r.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) { + setelemT *sizep; + setelemT *elemp; + setelemT *lastp; + + if (!set) + return NULL; + elemp= (setelemT *)SETaddr_(set, void); + while (elemp->p != oldelem && elemp->p) + elemp++; + if (elemp->p) { + sizep= SETsizeaddr_(set); + if (!(sizep->i)--) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void); + elemp->p= lastp->p; /* may overwrite itself */ + lastp->p= NULL; + return oldelem; + } + return NULL; +} /* setdel */ + + +/*-<a href="qh-set_r.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; + setelemT *sizep; + void *returnvalue; + + if (!set || !(set->e[0].p)) + return NULL; + sizep= SETsizeaddr_(set); + if ((setsize= sizep->i)) { + returnvalue= set->e[setsize - 2].p; + set->e[setsize - 2].p= NULL; + sizep->i--; + }else { + maxsize= set->maxsize; + returnvalue= set->e[maxsize - 1].p; + set->e[maxsize - 1].p= NULL; + sizep->i= maxsize; + } + return returnvalue; +} /* setdellast */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setdelnth">-</a> + + qh_setdelnth(qh, 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(qhT *qh, setT *set, int nth) { + void *elem; + setelemT *sizep; + setelemT *elemp, *lastp; + + sizep= SETsizeaddr_(set); + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + if (nth < 0 || nth >= sizep->i) { + qh_fprintf(qh, qh->qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + elemp= (setelemT *)SETelemaddr_(set, nth, void); /* nth valid by QH6174 */ + lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void); + elem= elemp->p; + elemp->p= lastp->p; /* may overwrite itself */ + lastp->p= NULL; + return elem; +} /* setdelnth */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setdelnthsorted">-</a> + + qh_setdelnthsorted(qh, 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(qhT *qh, setT *set, int nth) { + void *elem; + setelemT *sizep; + setelemT *newp, *oldp; + + sizep= SETsizeaddr_(set); + if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + newp= (setelemT *)SETelemaddr_(set, nth, void); + elem= newp->p; + oldp= newp+1; + while (((newp++)->p= (oldp++)->p)) + ; /* copy remaining elements and NULL */ + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */ + return elem; +} /* setdelnthsorted */ + + +/*-<a href="qh-set_r.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) { + setelemT *sizep; + setelemT *newp, *oldp; + + if (!set) + return NULL; + newp= (setelemT *)SETaddr_(set, void); + while(newp->p != oldelem && newp->p) + newp++; + if (newp->p) { + oldp= newp+1; + while (((newp++)->p= (oldp++)->p)) + ; /* copy remaining elements */ + sizep= SETsizeaddr_(set); + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */ + return oldelem; + } + return NULL; +} /* setdelsorted */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setduplicate">-</a> + + qh_setduplicate(qh, 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(qhT *qh, setT *set, int elemsize) { + void *elem, **elemp, *newElem; + setT *newSet; + int size; + + if (!(size= qh_setsize(qh, set))) + return NULL; + newSet= qh_setnew(qh, size); + FOREACHelem_(set) { + newElem= qh_memalloc(qh, elemsize); + memcpy(newElem, elem, (size_t)elemsize); + qh_setappend(qh, &newSet, newElem); + } + return newSet; +} /* setduplicate */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setendpointer">-</a> + + qh_setendpointer( set ) + Returns pointer to NULL terminator of a set's elements + set can not be NULL + +*/ +void **qh_setendpointer(setT *set) { + + setelemT *sizep= SETsizeaddr_(set); + int n= sizep->i; + return (n ? &set->e[n-1].p : &sizep->p); +} /* qh_setendpointer */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setequal">-</a> + + qh_setequal( setA, setB ) + returns 1 if two sorted sets are equal, otherwise returns 0 + + notes: + either set may be NULL + + design: + check size of each set + setup pointers + compare elements of each set +*/ +int qh_setequal(setT *setA, setT *setB) { + void **elemAp, **elemBp; + int sizeA= 0, sizeB= 0; + + if (setA) { + SETreturnsize_(setA, sizeA); + } + if (setB) { + SETreturnsize_(setB, sizeB); + } + if (sizeA != sizeB) + return 0; + if (!sizeA) + return 1; + elemAp= SETaddr_(setA, void); + elemBp= SETaddr_(setB, void); + if (!memcmp((char *)elemAp, (char *)elemBp, sizeA*SETelemsize)) + return 1; + return 0; +} /* setequal */ + + +/*-<a href="qh-set_r.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_r.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_r.htm#TOC" + >-------------------------------<a name="setfree">-</a> + + qh_setfree(qh, 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(qhT *qh, setT **setp) { + int size; + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + if (*setp) { + size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + if (size <= qh->qhmem.LASTsize) { + qh_memfree_(qh, *setp, size, freelistp); + }else + qh_memfree(qh, *setp, size); + *setp= NULL; + } +} /* setfree */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setfree2">-</a> + + qh_setfree2(qh, 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(qhT *qh, setT **setp, int elemsize) { + void *elem, **elemp; + + FOREACHelem_(*setp) + qh_memfree(qh, elem, elemsize); + qh_setfree(qh, setp); +} /* setfree2 */ + + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setfreelong">-</a> + + qh_setfreelong(qh, 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(qhT *qh, setT **setp) { + int size; + + if (*setp) { + size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + if (size > qh->qhmem.LASTsize) { + qh_memfree(qh, *setp, size); + *setp= NULL; + } + } +} /* setfreelong */ + + +/*-<a href="qh-set_r.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_r.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. + NOerrors returned (qh_pointid, QhullPoint::id) + + design: + checks maxsize + scans set for atelem +*/ +int qh_setindex(setT *set, void *atelem) { + void **elem; + int size, i; + + if (!set) + return -1; + SETreturnsize_(set, size); + if (size > set->maxsize) + return -1; + elem= SETaddr_(set, void); + for (i=0; i < size; i++) { + if (*elem++ == atelem) + return i; + } + return -1; +} /* setindex */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setlarger">-</a> + + qh_setlarger(qh, oldsetp ) + returns a larger set that contains all elements of *oldsetp + + notes: + the set is at least twice as large + if temp set, updates qh->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(qhT *qh, setT **oldsetp) { + int size= 1; + setT *newset, *set, **setp, *oldset; + setelemT *sizep; + setelemT *newp, *oldp; + + if (*oldsetp) { + oldset= *oldsetp; + SETreturnsize_(oldset, size); + qh->qhmem.cntlarger++; + qh->qhmem.totlarger += size+1; + newset= qh_setnew(qh, 2 * size); + oldp= (setelemT *)SETaddr_(oldset, void); + newp= (setelemT *)SETaddr_(newset, void); + memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize); + sizep= SETsizeaddr_(newset); + sizep->i= size+1; + FOREACHset_((setT *)qh->qhmem.tempstack) { + if (set == oldset) + *(setp-1)= newset; + } + qh_setfree(qh, oldsetp); + }else + newset= qh_setnew(qh, 3); + *oldsetp= newset; +} /* setlarger */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setlast">-</a> + + qh_setlast( set ) + return last element of set or NULL (use type conversion) + + notes: + set may be NULL + + design: + return last element +*/ +void *qh_setlast(setT *set) { + int size; + + if (set) { + size= SETsizeaddr_(set)->i; + if (!size) + return SETelem_(set, set->maxsize - 1); + else if (size > 1) + return SETelem_(set, size - 2); + } + return NULL; +} /* setlast */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setnew">-</a> + + qh_setnew(qh, setsize ) + creates and allocates space for a set + + notes: + setsize means the number of elements (!including the NULL terminator) + use qh_settemp/qh_setfreetemp if set is temporary + + design: + allocate memory for set + roundup memory if small set + initialize as empty set +*/ +setT *qh_setnew(qhT *qh, int setsize) { + setT *set; + int sizereceived; /* used if !qh_NOmem */ + int size; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + if (!setsize) + setsize++; + size= sizeof(setT) + setsize * SETelemsize; + if (size>0 && size <= qh->qhmem.LASTsize) { + qh_memalloc_(qh, size, freelistp, set, setT); +#ifndef qh_NOmem + sizereceived= qh->qhmem.sizetable[ qh->qhmem.indextable[size]]; + if (sizereceived > size) + setsize += (sizereceived - size)/SETelemsize; +#endif + }else + set= (setT*)qh_memalloc(qh, size); + set->maxsize= setsize; + set->e[setsize].i= 1; + set->e[0].p= NULL; + return(set); +} /* setnew */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setnew_delnthsorted">-</a> + + qh_setnew_delnthsorted(qh, 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(qhT *qh, setT *set, int size, int nth, int prepend) { + setT *newset; + void **oldp, **newp; + int tailsize= size - nth -1, newsize; + + if (tailsize < 0) { + qh_fprintf(qh, qh->qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + newsize= size-1 + prepend; + newset= qh_setnew(qh, newsize); + newset->e[newset->maxsize].i= newsize+1; /* may be overwritten */ + oldp= SETaddr_(set, void); + newp= SETaddr_(newset, void) + prepend; + switch (nth) { + case 0: + break; + case 1: + *(newp++)= *oldp++; + break; + case 2: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 3: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 4: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + default: + memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize); + newp += nth; + oldp += nth; + break; + } + oldp++; + switch (tailsize) { + case 0: + break; + case 1: + *(newp++)= *oldp++; + break; + case 2: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 3: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 4: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + default: + memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize); + newp += tailsize; + } + *newp= NULL; + return(newset); +} /* setnew_delnthsorted */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setprint">-</a> + + qh_setprint(qh, fp, string, set ) + print set elements to fp with identifying string + + notes: + never errors +*/ +void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set) { + int size, k; + + if (!set) + qh_fprintf(qh, fp, 9346, "%s set is null\n", string); + else { + SETreturnsize_(set, size); + qh_fprintf(qh, fp, 9347, "%s set=%p maxsize=%d size=%d elems=", + string, set, set->maxsize, size); + if (size > set->maxsize) + size= set->maxsize+1; + for (k=0; k < size; k++) + qh_fprintf(qh, fp, 9348, " %p", set->e[k].p); + qh_fprintf(qh, fp, 9349, "\n"); + } +} /* setprint */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setreplace">-</a> + + qh_setreplace(qh, 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(qhT *qh, setT *set, void *oldelem, void *newelem) { + void **elemp; + + elemp= SETaddr_(set, void); + while (*elemp != oldelem && *elemp) + elemp++; + if (*elemp) + *elemp= newelem; + else { + qh_fprintf(qh, qh->qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n", + oldelem); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } +} /* setreplace */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setsize">-</a> + + qh_setsize(qh, set ) + returns the size of a set + + notes: + errors if set's maxsize is incorrect + same as SETreturnsize_(set) + same code for qh_setsize [qset_r.c] and QhullSetBase::count + + design: + determine actual size of set from maxsize +*/ +int qh_setsize(qhT *qh, setT *set) { + int size; + setelemT *sizep; + + if (!set) + return(0); + sizep= SETsizeaddr_(set); + if ((size= sizep->i)) { + size--; + if (size > set->maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n", + size, set->maxsize); + qh_setprint(qh, qh->qhmem.ferr, "set: ", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + }else + size= set->maxsize; + return size; +} /* setsize */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settemp">-</a> + + qh_settemp(qh, setsize ) + return a stacked, temporary set of upto setsize elements + + notes: + use settempfree or settempfree_all to release from qh->qhmem.tempstack + see also qh_setnew + + design: + allocate set + append to qh->qhmem.tempstack + +*/ +setT *qh_settemp(qhT *qh, int setsize) { + setT *newset; + + newset= qh_setnew(qh, setsize); + qh_setappend(qh, &qh->qhmem.tempstack, newset); + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n", + newset, newset->maxsize, qh_setsize(qh, qh->qhmem.tempstack)); + return newset; +} /* settemp */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settempfree">-</a> + + qh_settempfree(qh, set ) + free temporary set at top of qh->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 qh->qhmem.tempstack + free it +*/ +void qh_settempfree(qhT *qh, setT **set) { + setT *stackedset; + + if (!*set) + return; + stackedset= qh_settemppop(qh); + if (stackedset != *set) { + qh_settemppush(qh, stackedset); + qh_fprintf(qh, qh->qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n", + *set, qh_setsize(qh, *set), qh_setsize(qh, qh->qhmem.tempstack)+1, + stackedset, qh_setsize(qh, stackedset)); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + qh_setfree(qh, set); +} /* settempfree */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settempfree_all">-</a> + + qh_settempfree_all(qh) + free all temporary sets in qh->qhmem.tempstack + + design: + for each set in tempstack + free set + free qh->qhmem.tempstack +*/ +void qh_settempfree_all(qhT *qh) { + setT *set, **setp; + + FOREACHset_(qh->qhmem.tempstack) + qh_setfree(qh, &set); + qh_setfree(qh, &qh->qhmem.tempstack); +} /* settempfree_all */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settemppop">-</a> + + qh_settemppop(qh) + pop and return temporary set from qh->qhmem.tempstack + + notes: + the returned set is permanent + + design: + pop and check top of qh->qhmem.tempstack +*/ +setT *qh_settemppop(qhT *qh) { + setT *stackedset; + + stackedset= (setT*)qh_setdellast(qh->qhmem.tempstack); + if (!stackedset) { + qh_fprintf(qh, qh->qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n"); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n", + qh_setsize(qh, qh->qhmem.tempstack)+1, stackedset, qh_setsize(qh, stackedset)); + return stackedset; +} /* settemppop */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settemppush">-</a> + + qh_settemppush(qh, set ) + push temporary set unto qh->qhmem.tempstack (makes it temporary) + + notes: + duplicates settemp() for tracing + + design: + append set to tempstack +*/ +void qh_settemppush(qhT *qh, setT *set) { + if (!set) { + qh_fprintf(qh, qh->qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n"); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + qh_setappend(qh, &qh->qhmem.tempstack, set); + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n", + qh_setsize(qh, qh->qhmem.tempstack), set, qh_setsize(qh, set)); +} /* settemppush */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settruncate">-</a> + + qh_settruncate(qh, 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(qhT *qh, setT *set, int size) { + + if (size < 0 || size > set->maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + set->e[set->maxsize].i= size+1; /* maybe overwritten */ + set->e[size].p= NULL; +} /* settruncate */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setunique">-</a> + + qh_setunique(qh, 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(qhT *qh, setT **set, void *elem) { + + if (!qh_setin(*set, elem)) { + qh_setappend(qh, set, elem); + return 1; + } + return 0; +} /* setunique */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setzero">-</a> + + qh_setzero(qh, 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(qhT *qh, setT *set, int idx, int size) { + int count; + + if (idx < 0 || idx >= size || size > set->maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + set->e[set->maxsize].i= size+1; /* may be overwritten */ + count= size - idx + 1; /* +1 for NULL terminator */ + memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize); +} /* setzero */ + +
+ C/qset_r.h view
@@ -0,0 +1,502 @@+/*<html><pre> -<a href="qh-set_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + qset_r.h + header file for qset_r.c that implements set + + see qh-set_r.htm and qset_r.c + + only uses mem_r.c, malloc/free + + for error handling, writes message and calls + qh_errexit(qhT *qh, 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-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/qset_r.h#4 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ +*/ + +#ifndef qhDEFset +#define qhDEFset 1 + +#include <stdio.h> + +/*================= -structures- ===============*/ + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* a set is a sorted or unsorted array of pointers */ +#endif + +#ifndef DEFqhT +#define DEFqhT 1 +typedef struct qhT qhT; /* defined in libqhull_r.h */ +#endif + +/* [jan'15] Decided not to use countT. Most sets are small. The code uses signed tests */ + +/*-<a href="qh-set_r.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_r.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_r.htm#TOC" + >-----------------------------------</a><a name="SETelemsize">-</a> + + SETelemsize + size of a set element in bytes +*/ +#define SETelemsize ((int)sizeof(setelemT)) + + +/*=========== -macros- =========================*/ + +/*-<a href="qh-set_r.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_r.htm#TOC" + >----------------------------------------</a><a name="FOREACHsetelement_i_">-</a> + + FOREACHsetelement_i_(qh, 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_( qh, facets ) FOREACHsetelement_i_( qh, 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_(qh, type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##_i= 0, variable= (type *)((set)->e[0].p), \ + variable##_n= qh_setsize(qh, set);\ + variable##_i < variable##_n;\ + variable= (type *)((set)->e[++variable##_i].p) ) + +/*-<a href="qh-set_r.htm#TOC" + >--------------------------------------</a><a name="FOREACHsetelementreverse_">-</a> + + FOREACHsetelementreverse_(qh, 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_(qh, type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##temp= qh_setsize(qh, set)-1, variable= qh_setlast(qh, set);\ + variable; variable= \ + ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL)) + +/*-<a href="qh-set_r.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_r.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_r.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_r.htm#TOC" + >-----------------------------------------</a><a name="SETindex_">-</a> + + SETindex_( set, elem ) + return index of elem in set + + notes: + for use with FOREACH iteration + WARN64 -- Maximum set size is 2G + + example: + i= SETindex_(ridges, ridge) +*/ +#define SETindex_(set, elem) ((int)((void **)elem##p - (void **)&(set)->e[1].p)) + +/*-<a href="qh-set_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.htm#TOC" + >---------------------------------------</a><a name="SETreturnsize_">-</a> + + SETreturnsize_(set, size) + return size of a set + + notes: + set must be defined + use qh_setsize(qhT *qh, set) unless speed is critical +*/ +#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize)) + +/*-<a href="qh-set_r.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_r.htm#TOC" + >-------------------------------<a name="SETsizeaddr_">-</a> + + SETsizeaddr_(set) + return pointer to 'actual size+1' of set (set CANNOT be NULL!!) + Its type is setelemT* for strict aliasing + All SETelemaddr_ must be cast to setelemT + + + notes: + *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL +*/ +#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize])) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETtruncate_">-</a> + + SETtruncate_(set, size) + truncate set to size + + see: + qh_settruncate() + +*/ +#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \ + set->e[size].p= NULL;} + +/*======= prototypes in alphabetical order ============*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_setaddsorted(qhT *qh, setT **setp, void *elem); +void qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem); +void qh_setappend(qhT *qh, setT **setp, void *elem); +void qh_setappend_set(qhT *qh, setT **setp, setT *setA); +void qh_setappend2ndlast(qhT *qh, setT **setp, void *elem); +void qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned id); +void qh_setcompact(qhT *qh, setT *set); +setT *qh_setcopy(qhT *qh, setT *set, int extra); +void *qh_setdel(setT *set, void *elem); +void *qh_setdellast(setT *set); +void *qh_setdelnth(qhT *qh, setT *set, int nth); +void *qh_setdelnthsorted(qhT *qh, setT *set, int nth); +void *qh_setdelsorted(setT *set, void *newelem); +setT *qh_setduplicate(qhT *qh, setT *set, int elemsize); +void **qh_setendpointer(setT *set); +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(qhT *qh, setT **set); +void qh_setfree2(qhT *qh, setT **setp, int elemsize); +void qh_setfreelong(qhT *qh, setT **set); +int qh_setin(setT *set, void *setelem); +int qh_setindex(setT *set, void *setelem); +void qh_setlarger(qhT *qh, setT **setp); +void *qh_setlast(setT *set); +setT *qh_setnew(qhT *qh, int size); +setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend); +void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set); +void qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem); +int qh_setsize(qhT *qh, setT *set); +setT *qh_settemp(qhT *qh, int setsize); +void qh_settempfree(qhT *qh, setT **set); +void qh_settempfree_all(qhT *qh); +setT *qh_settemppop(qhT *qh); +void qh_settemppush(qhT *qh, setT *set); +void qh_settruncate(qhT *qh, setT *set, int size); +int qh_setunique(qhT *qh, setT **set, void *elem); +void qh_setzero(qhT *qh, setT *set, int idx, int size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFset */
+ C/random_r.c view
@@ -0,0 +1,247 @@+/*<html><pre> -<a href="index_r.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + random_r.c and utilities + Park & Miller's minimimal standard random number generator + argc/argv conversion + + Used by rbox. Do not use 'qh' +*/ + +#include "libqhull_r.h" +#include "random_r.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +#pragma warning( disable : 4706) /* assignment within conditional function */ +#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */ +#endif + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="argv_to_command">-</a> + + qh_argv_to_command(argc, argv, command, max_size ) + + build command from argc/argv + max_size is at least + + returns: + a space-delimited string of options (just as typed) + returns false if max_size is too short + + notes: + silently removes + makes option string easy to input and output + matches qh_argv_to_command_size() + + argc may be 0 +*/ +int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) { + int i, remaining; + char *s; + *command= '\0'; /* max_size > 0 */ + + if (argc) { + if ((s= strrchr( argv[0], '\\')) /* get filename w/o .exe extension */ + || (s= strrchr( argv[0], '/'))) + s++; + else + s= argv[0]; + if ((int)strlen(s) < max_size) /* WARN64 */ + strcpy(command, s); + else + goto error_argv; + if ((s= strstr(command, ".EXE")) + || (s= strstr(command, ".exe"))) + *s= '\0'; + } + for (i=1; i < argc; i++) { + s= argv[i]; + remaining= max_size - (int)strlen(command) - (int)strlen(s) - 2; /* WARN64 */ + if (!*s || strchr(s, ' ')) { + char *t= command + strlen(command); + remaining -= 2; + if (remaining < 0) { + goto error_argv; + } + *t++= ' '; + *t++= '"'; + while (*s) { + if (*s == '"') { + if (--remaining < 0) + goto error_argv; + *t++= '\\'; + } + *t++= *s++; + } + *t++= '"'; + *t= '\0'; + }else if (remaining < 0) { + goto error_argv; + }else + strcat(command, " "); + strcat(command, s); + } + return 1; + +error_argv: + return 0; +} /* argv_to_command */ + +/*-<a href="qh-globa_r.htm#TOC" +>-------------------------------</a><a name="argv_to_command_size">-</a> + +qh_argv_to_command_size(argc, argv ) + + return size to allocate for qh_argv_to_command() + +notes: + argc may be 0 + actual size is usually shorter +*/ +int qh_argv_to_command_size(int argc, char *argv[]) { + unsigned int count= 1; /* null-terminator if argc==0 */ + int i; + char *s; + + for (i=0; i<argc; i++){ + count += (int)strlen(argv[i]) + 1; /* WARN64 */ + if (i>0 && strchr(argv[i], ' ')) { + count += 2; /* quote delimiters */ + for (s=argv[i]; *s; s++) { + if (*s == '"') { + count++; + } + } + } + } + return count; +} /* argv_to_command_size */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="rand">-</a> + + qh_rand() + qh_srand(qh, seed ) + generate pseudo-random number between 1 and 2^31 -2 + + notes: + For qhull and rbox, called from qh_RANDOMint(),etc. [user.h] + + From Park & Miller's minimal standard random number generator + Communications of the ACM, 31:1192-1201, 1988. + Does not use 0 or 2^31 -1 + this is silently enforced by qh_srand() + Can make 'Rn' much faster by moving qh_rand to qh_distplane +*/ + +/* Global variables and constants */ + +#define qh_rand_a 16807 +#define qh_rand_m 2147483647 +#define qh_rand_q 127773 /* m div a */ +#define qh_rand_r 2836 /* m mod a */ + +int qh_rand(qhT *qh) { + int lo, hi, test; + int seed = qh->last_random; + + 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->last_random= seed; + /* seed = seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax; for testing */ + /* seed = qh_RANDOMmax; for testing */ + return seed; +} /* rand */ + +void qh_srand(qhT *qh, int seed) { + if (seed < 1) + qh->last_random= 1; + else if (seed >= qh_rand_m) + qh->last_random= qh_rand_m - 1; + else + qh->last_random= seed; +} /* qh_srand */ + +/*-<a href="qh-geom_r.htm#TOC" +>-------------------------------</a><a name="randomfactor">-</a> + +qh_randomfactor(qh, scale, offset ) + return a random factor r * scale + offset + +notes: + qh.RANDOMa/b are defined in global_r.c + qh_RANDOMint requires 'qh' +*/ +realT qh_randomfactor(qhT *qh, realT scale, realT offset) { + realT randr; + + randr= qh_RANDOMint; + return randr * scale + offset; +} /* randomfactor */ + +/*-<a href="qh-geom_r.htm#TOC" +>-------------------------------</a><a name="randommatrix">-</a> + +qh_randommatrix(qh, 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 + + notes: + qh_RANDOMint requires 'qh' +*/ +void qh_randommatrix(qhT *qh, 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-globa_r.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); /* WARN64 */ + if (s< (*endp) && (*endp)[-1] == ' ') + (*endp)--; + return result; +} /* strtol */
+ C/random_r.h view
@@ -0,0 +1,41 @@+/*<html><pre> -<a href="qh-geom_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + random.h + header file for random and utility routines + + see qh-geom_r.htm and random_r.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/random_r.h#4 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ +*/ + +#ifndef qhDEFrandom +#define qhDEFrandom 1 + +#include "libqhull_r.h" + +/*============= prototypes in alphabetical order ======= */ + +#ifdef __cplusplus +extern "C" { +#endif + +int qh_argv_to_command(int argc, char *argv[], char* command, int max_size); +int qh_argv_to_command_size(int argc, char *argv[]); +int qh_rand(qhT *qh); +void qh_srand(qhT *qh, int seed); +realT qh_randomfactor(qhT *qh, realT scale, realT offset); +void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row); +int qh_strtol(const char *s, char **endp); +double qh_strtod(const char *s, char **endp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFrandom */ + + +
+ C/stat_r.c view
@@ -0,0 +1,682 @@+/*<html><pre> -<a href="qh-stat_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + stat_r.c + contains all statistics that are collected for qhull + + see qh-stat_r.htm and stat_r.h + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/stat_r.c#5 $$Change: 2062 $ + $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*========== functions in alphabetic order ================*/ + +/*-<a href="qh-stat_r.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(qhT *qh) { + + /* zdef_(type,name,doc,average) */ + zzdef_(zdoc, Zdoc2, "precision statistics", -1); + zdef_(zinc, Znewvertex, NULL, -1); + zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex); + zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1); + zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1); + zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1); + zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1); + + qh->qhstat.precision= qh->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(qhT *qh) { + 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(qhT *qh) { + zdef_(zdoc, Zdoc9, "build hull statistics", -1); + zzdef_(zinc, Zprocessed, "points processed", -1); + zzdef_(zinc, Zretry, "retries due to precision problems", -1); + zdef_(wmax, Wretrymax, " max. random joggle", -1); + zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1); + zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed); + zdef_(zinc, Zinsidevisible, " ave. visible facets without an horizon neighbor", Zprocessed); + zdef_(zadd, Zvisfacettot, " ave. facets deleted per iteration", Zprocessed); + zdef_(zmax, Zvisfacetmax, " maximum", -1); + zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed); + zdef_(zmax, Zvisvertexmax, " maximum", -1); + zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed); + zdef_(zadd, Znewfacettot, "ave. new or merged facets per iteration", Zprocessed); + zdef_(zmax, Znewfacetmax, " maximum(includes initial simplex)", -1); + zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed); + zdef_(wadd, Wnewbalance2, " standard deviation", -1); + zdef_(wadd, Wpbalance, "average partition balance", Zpbalance); + zdef_(wadd, Wpbalance2, " standard deviation", -1); + zdef_(zinc, Zpbalance, " number of trials", -1); + zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1); + zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1); + zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1); + zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1); + zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1); + zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1); + zdef_(zinc, Zgoodfacet, "good facets found", -1); + zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1); + zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1); + zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1); + zzdef_(zinc, Zcheckpart, " ave. distance tests per check", Ztotcheck); +} +void qh_allstatD(qhT *qh) { + zdef_(zinc, Zvisit, "resets of visit_id", -1); + zdef_(zinc, Zvvisit, " resets of vertex_visit", -1); + zdef_(zmax, Zvisit2max, " max visit_id/2", -1); + zdef_(zmax, Zvvisit2max, " max vertex_visit/2", -1); + + zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1); + zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1); + zdef_(zmax, Zdelvertexmax, " maximum vertices deleted per iteration", -1); + zdef_(zinc, Zfindbest, "calls to findbest", -1); + zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest); + zdef_(zmax, Zfindbestmax, " max. facets tested", -1); + zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest); + zdef_(zinc, Zfindnew, "calls to findbestnew", -1); + zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew); + zdef_(zmax, Zfindnewmax, " max. facets tested", -1); + zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew); + zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1); + zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1); + zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon); + zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1); + zdef_(zinc, Zfindjump, " ave. clearly better", Zfindhorizon); + zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1); + zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1); + zdef_(zinc, Zpartflip, " repartitioned coplanar points for flipped orientation", -1); +} +void qh_allstatE(qhT *qh) { + 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_(zinc, Zbestlowerall, " with rare search of all facets", -1); + zdef_(zmax, Zbestloweralln, " facets per search of all facets", -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(qhT *qh) { + 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(qhT *qh) { + 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(qhT *qh) { + 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(qhT *qh) { + 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, outside and coplanar sets, and qhT",-1); + zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1); +} /* allstat */ + +void qh_allstatI(qhT *qh) { + qh->qhstat.vridges= qh->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_r.htm#TOC" + >-------------------------------</a><a name="allstatistics">-</a> + + qh_allstatistics() + reset printed flag for all statistics +*/ +void qh_allstatistics(qhT *qh) { + int i; + + for(i=ZEND; i--; ) + qh->qhstat.printed[i]= False; +} /* allstatistics */ + +#if qh_KEEPstatistics +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="collectstatistics">-</a> + + qh_collectstatistics() + collect statistics for qh.facet_list + +*/ +void qh_collectstatistics(qhT *qh) { + 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); + 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, 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(qh, facet->vertices); + sizneighbors= qh_setsize(qh, facet->neighbors); + sizridges= qh_setsize(qh, 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(qh, facet->outsideset)); + if (facet->coplanarset) + zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(qh, 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(qh, 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(qh, 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(qh, 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_r.htm#TOC" + >-------------------------------</a><a name="initstatistics">-</a> + + qh_initstatistics(qh) + initialize statistics + + notes: + NOerrors -- qh_initstatistics can not use qh_errexit(), qh_fprintf, or qh.ferr + On first call, only qhmem.ferr is defined. qh_memalloc is not setup. + Also invoked by QhullQh(). +*/ +void qh_initstatistics(qhT *qh) { + int i; + realT realx; + int intx; + + qh->qhstat.next= 0; + qh_allstatA(qh); + qh_allstatB(qh); + qh_allstatC(qh); + qh_allstatD(qh); + qh_allstatE(qh); + qh_allstatE2(qh); + qh_allstatF(qh); + qh_allstatG(qh); + qh_allstatH(qh); + qh_allstatI(qh); + if (qh->qhstat.next > (int)sizeof(qh->qhstat.id)) { + qh_fprintf(qh, qh->qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\ + qhstat.next %d should be <= sizeof(qh->qhstat.id) %d\n", qh->qhstat.next, (int)sizeof(qh->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 (qh->qhstat.id[i] == qh->qhstat.id[j]) { + qh_fprintf(qh, qh->qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n", + qh->qhstat.id[i], i, j); + } + } + } +#endif + qh_exit(qh_ERRqhull); /* can not use qh_errexit() */ + } + qh->qhstat.init[zinc].i= 0; + qh->qhstat.init[zadd].i= 0; + qh->qhstat.init[zmin].i= INT_MAX; + qh->qhstat.init[zmax].i= INT_MIN; + qh->qhstat.init[wadd].r= 0; + qh->qhstat.init[wmin].r= REALmax; + qh->qhstat.init[wmax].r= -REALmax; + for(i=0; i < ZEND; i++) { + if (qh->qhstat.type[i] > ZTYPEreal) { + realx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r; + qh->qhstat.stats[i].r= realx; + }else if (qh->qhstat.type[i] != zdoc) { + intx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i; + qh->qhstat.stats[i].i= intx; + } + } +} /* initstatistics */ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="newstats">-</a> + + qh_newstats(qh, ) + returns True if statistics for zdoc + + returns: + next zdoc +*/ +boolT qh_newstats(qhT *qh, int idx, int *nextindex) { + boolT isnew= False; + int start, i; + + if (qh->qhstat.type[qh->qhstat.id[idx]] == zdoc) + start= idx+1; + else + start= idx; + for(i= start; i < qh->qhstat.next && qh->qhstat.type[qh->qhstat.id[i]] != zdoc; i++) { + if (!qh_nostatistic(qh, qh->qhstat.id[i]) && !qh->qhstat.printed[qh->qhstat.id[i]]) + isnew= True; + } + *nextindex= i; + return isnew; +} /* newstats */ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="nostatistic">-</a> + + qh_nostatistic(qh, index ) + true if no statistic to print +*/ +boolT qh_nostatistic(qhT *qh, int i) { + + if ((qh->qhstat.type[i] > ZTYPEreal + &&qh->qhstat.stats[i].r == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r) + || (qh->qhstat.type[i] < ZTYPEreal + &&qh->qhstat.stats[i].i == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i)) + return True; + return False; +} /* nostatistic */ + +#if qh_KEEPstatistics +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="printallstatistics">-</a> + + qh_printallstatistics(qh, fp, string ) + print all statistics with header 'string' +*/ +void qh_printallstatistics(qhT *qh, FILE *fp, const char *string) { + + qh_allstatistics(qh); + qh_collectstatistics(qh); + qh_printstatistics(qh, fp, string); + qh_memstatistics(qh, fp); +} + + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="printstatistics">-</a> + + qh_printstatistics(qh, 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(qhT *qh, FILE *fp, const char *string) { + int i, k; + realT ave; + + if (qh->num_points != qh->num_vertices) { + wval_(Wpbalance)= 0; + wval_(Wpbalance2)= 0; + }else + wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance), + wval_(Wnewbalance2), &ave); + qh_fprintf(qh, fp, 9350, "\n\ +%s\n\ + qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh->rbox_command, + qh->qhull_command, qh_version, qh->qhull_options); + qh_fprintf(qh, fp, 9351, "\nprecision constants:\n\ + %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\ + %6.2g max. roundoff error for distance computation('En')\n\ + %6.2g max. roundoff error for angle computations\n\ + %6.2g min. distance for outside points ('Wn')\n\ + %6.2g min. distance for visible facets ('Vn')\n\ + %6.2g max. distance for coplanar facets ('Un')\n\ + %6.2g max. facet width for recomputing centrum and area\n\ +", + qh->MAXabs_coord, qh->DISTround, qh->ANGLEround, qh->MINoutside, + qh->MINvisible, qh->MAXcoplanar, qh->WIDEfacet); + if (qh->KEEPnearinside) + qh_fprintf(qh, fp, 9352, "\ + %6.2g max. distance for near-inside points\n", qh->NEARinside); + if (qh->premerge_cos < REALmax/2) qh_fprintf(qh, fp, 9353, "\ + %6.2g max. cosine for pre-merge angle\n", qh->premerge_cos); + if (qh->PREmerge) qh_fprintf(qh, fp, 9354, "\ + %6.2g radius of pre-merge centrum\n", qh->premerge_centrum); + if (qh->postmerge_cos < REALmax/2) qh_fprintf(qh, fp, 9355, "\ + %6.2g max. cosine for post-merge angle\n", qh->postmerge_cos); + if (qh->POSTmerge) qh_fprintf(qh, fp, 9356, "\ + %6.2g radius of post-merge centrum\n", qh->postmerge_centrum); + qh_fprintf(qh, fp, 9357, "\ + %6.2g max. distance for merging two simplicial facets\n\ + %6.2g max. roundoff error for arithmetic operations\n\ + %6.2g min. denominator for divisions\n\ + zero diagonal for Gauss: ", qh->ONEmerge, REALepsilon, qh->MINdenom); + for(k=0; k < qh->hull_dim; k++) + qh_fprintf(qh, fp, 9358, "%6.2e ", qh->NEARzero[k]); + qh_fprintf(qh, fp, 9359, "\n\n"); + for(i=0 ; i < qh->qhstat.next; ) + qh_printstats(qh, fp, i, &i); +} /* printstatistics */ +#endif /* qh_KEEPstatistics */ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="printstatlevel">-</a> + + qh_printstatlevel(qh, fp, id ) + print level information for a statistic + + notes: + nop if id >= ZEND, printed, or same as initial value +*/ +void qh_printstatlevel(qhT *qh, FILE *fp, int id) { +#define NULLfield " " + + if (id >= ZEND || qh->qhstat.printed[id]) + return; + if (qh->qhstat.type[id] == zdoc) { + qh_fprintf(qh, fp, 9360, "%s\n", qh->qhstat.doc[id]); + return; + } + if (qh_nostatistic(qh, id) || !qh->qhstat.doc[id]) + return; + qh->qhstat.printed[id]= True; + if (qh->qhstat.count[id] != -1 + && qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i == 0) + qh_fprintf(qh, fp, 9361, " *0 cnt*"); + else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] == -1) + qh_fprintf(qh, fp, 9362, "%7.2g", qh->qhstat.stats[id].r); + else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] != -1) + qh_fprintf(qh, fp, 9363, "%7.2g", qh->qhstat.stats[id].r/ qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i); + else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] == -1) + qh_fprintf(qh, fp, 9364, "%7d", qh->qhstat.stats[id].i); + else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] != -1) + qh_fprintf(qh, fp, 9365, "%7.3g", (realT) qh->qhstat.stats[id].i / qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i); + qh_fprintf(qh, fp, 9366, " %s\n", qh->qhstat.doc[id]); +} /* printstatlevel */ + + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="printstats">-</a> + + qh_printstats(qh, fp, index, nextindex ) + print statistics for a zdoc group + + returns: + next zdoc if non-null +*/ +void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex) { + int j, nexti; + + if (qh_newstats(qh, idx, &nexti)) { + qh_fprintf(qh, fp, 9367, "\n"); + for (j=idx; j<nexti; j++) + qh_printstatlevel(qh, fp, qh->qhstat.id[j]); + } + if (nextindex) + *nextindex= nexti; +} /* printstats */ + +#if qh_KEEPstatistics + +/*-<a href="qh-stat_r.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(qhT *qh) {} +void qh_printallstatistics(qhT *qh, FILE *fp, char *string) {}; +void qh_printstatistics(qhT *qh, FILE *fp, char *string) {} +#endif +
+ C/stat_r.h view
@@ -0,0 +1,533 @@+/*<html><pre> -<a href="qh-stat_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + stat_r.h + contains all statistics that are collected for qhull + + see qh-stat_r.htm and stat_r.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull_r/stat_r.h#5 $$Change: 2079 $ + $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $ + + recompile qhull if you change this file + + Integer statistics are Z* while real statistics are W*. + + define MAYdebugx to call a routine at every statistic event + +*/ + +#ifndef qhDEFstat +#define qhDEFstat 1 + +/* Depends on realT. Do not include libqhull_r to avoid circular dependency */ + +#ifndef DEFqhT +#define DEFqhT 1 +typedef struct qhT qhT; /* Defined by libqhull_r.h */ +#endif + +#ifndef DEFqhstatT +#define DEFqhstatT 1 +typedef struct qhstatT qhstatT; /* Defined here */ +#endif + +/*-<a href="qh-stat_r.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_r.htm#TOC" + >-------------------------------</a><a name="statistics">-</a> + + Zxxx for integers, Wxxx for reals + + notes: + be sure that all statistics are defined in stat_r.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 qh_statistics { /* alphabetical after Z/W */ + Zacoplanar, + Wacoplanarmax, + Wacoplanartot, + Zangle, + Wangle, + Wanglemax, + Wanglemin, + Zangletests, + Wareatot, + Wareamax, + Wareamin, + Zavoidold, + Wavoidoldmax, + Wavoidoldtot, + Zback0, + Zbestcentrum, + Zbestdist, + Zbestlower, + Zbestlowerall, + Zbestloweralln, + Zbestlowerv, + Zcentrumtests, + Zcheckpart, + Zcomputefurthest, + Zconcave, + Wconcavemax, + Wconcavetot, + Zconcaveridges, + Zconcaveridge, + Zcoplanar, + Wcoplanarmax, + Wcoplanartot, + Zcoplanarangle, + Zcoplanarcentrum, + Zcoplanarhorizon, + Zcoplanarinside, + Zcoplanarpart, + Zcoplanarridges, + Wcpu, + Zcyclefacetmax, + Zcyclefacettot, + Zcyclehorizon, + Zcyclevertex, + Zdegen, + Wdegenmax, + Wdegentot, + Zdegenvertex, + Zdelfacetdup, + Zdelridge, + Zdelvertextot, + Zdelvertexmax, + Zdetsimplex, + Zdistcheck, + Zdistconvex, + Zdistgood, + Zdistio, + Zdistplane, + Zdiststat, + Zdistvertex, + Zdistzero, + Zdoc1, + Zdoc2, + Zdoc3, + Zdoc4, + Zdoc5, + Zdoc6, + Zdoc7, + Zdoc8, + Zdoc9, + Zdoc10, + Zdoc11, + Zdoc12, + Zdropdegen, + Zdropneighbor, + Zdupflip, + Zduplicate, + Wduplicatemax, + Wduplicatetot, + Zdupridge, + Zdupsame, + Zflipped, + Wflippedmax, + Wflippedtot, + Zflippedfacets, + Zfindbest, + Zfindbestmax, + Zfindbesttot, + Zfindcoplanar, + Zfindfail, + Zfindhorizon, + Zfindhorizonmax, + Zfindhorizontot, + Zfindjump, + Zfindnew, + Zfindnewmax, + Zfindnewtot, + Zfindnewjump, + Zfindnewsharp, + Zgauss0, + Zgoodfacet, + Zhashlookup, + Zhashridge, + Zhashridgetest, + Zhashtests, + Zinsidevisible, + Zintersect, + Zintersectfail, + Zintersectmax, + Zintersectnum, + Zintersecttot, + Zmaxneighbors, + Wmaxout, + Wmaxoutside, + Zmaxridges, + Zmaxvertex, + Zmaxvertices, + Zmaxvneighbors, + Zmemfacets, + Zmempoints, + Zmemridges, + Zmemvertices, + Zmergeflipdup, + Zmergehorizon, + Zmergeinittot, + Zmergeinitmax, + Zmergeinittot2, + Zmergeintohorizon, + Zmergenew, + Zmergesettot, + Zmergesetmax, + Zmergesettot2, + Zmergesimplex, + Zmergevertex, + Wmindenom, + Wminvertex, + Zminnorm, + Zmultiridge, + Znearlysingular, + Zneighbor, + Wnewbalance, + Wnewbalance2, + Znewfacettot, + Znewfacetmax, + Znewvertex, + Wnewvertex, + Wnewvertexmax, + Znoarea, + Znonsimplicial, + Znowsimplicial, + Znotgood, + Znotgoodnew, + Znotmax, + Znumfacets, + Znummergemax, + Znummergetot, + Znumneighbors, + Znumridges, + Znumvertices, + Znumvisibility, + Znumvneighbors, + Zonehorizon, + Zpartangle, + Zpartcoplanar, + Zpartflip, + Zparthorizon, + Zpartinside, + Zpartition, + Zpartitionall, + Zpartnear, + Zpbalance, + Wpbalance, + Wpbalance2, + Zpostfacets, + Zpremergetot, + Zprocessed, + Zremvertex, + Zremvertexdel, + Zrenameall, + Zrenamepinch, + Zrenameshare, + Zretry, + Wretrymax, + Zridge, + Wridge, + Wridgemax, + Zridge0, + Wridge0, + Wridge0max, + Zridgemid, + Wridgemid, + Wridgemidmax, + Zridgeok, + Wridgeok, + Wridgeokmax, + Zsearchpoints, + Zsetplane, + Ztestvneighbor, + Ztotcheck, + Ztothorizon, + Ztotmerge, + Ztotpartcoplanar, + Ztotpartition, + Ztotridges, + Ztotvertices, + Ztotvisible, + Ztricoplanar, + Ztricoplanarmax, + Ztricoplanartot, + Ztridegen, + Ztrimirror, + Ztrinull, + Wvertexmax, + Wvertexmin, + Zvertexridge, + Zvertexridgetot, + Zvertexridgemax, + Zvertices, + Zvisfacettot, + Zvisfacetmax, + Zvisit, + Zvisit2max, + Zvisvertextot, + Zvisvertexmax, + Zvvisit, + Zvvisit2max, + Zwidefacet, + Zwidevertices, + ZEND}; + +/*-<a href="qh-stat_r.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 qh_statistics { /* for zzdef etc. macros */ + Zback0, + Zbestdist, + Zcentrumtests, + Zcheckpart, + Zconcaveridges, + Zcoplanarhorizon, + Zcoplanarpart, + Zcoplanarridges, + Zcyclefacettot, + Zcyclehorizon, + Zdelvertextot, + Zdistcheck, + Zdistconvex, + Zdistzero, + Zdoc1, + Zdoc2, + Zdoc3, + Zdoc11, + Zflippedfacets, + Zgauss0, + Zminnorm, + Zmultiridge, + Znearlysingular, + Wnewvertexmax, + Znumvisibility, + Zpartcoplanar, + Zpartition, + Zpartitionall, + Zprocessed, + Zretry, + Zridge, + Wridge, + Wridgemax, + Zridge0, + Wridge0, + Wridge0max, + Zridgemid, + Wridgemid, + Wridgemidmax, + Zridgeok, + Wridgeok, + Wridgeokmax, + Zsetplane, + Ztotcheck, + Ztotmerge, + ZEND}; +#endif + +/*-<a href="qh-stat_r.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_r.htm#TOC" + >--------------------------------</a><a name="MAYdebugx">-</a> + + MAYdebugx + define as maydebug() to be called frequently for error trapping +*/ +#define MAYdebugx + +/*-<a href="qh-stat_r.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) qh->qhstat.id[qh->qhstat.next++]=name; \ + qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype +#if qh_KEEPstatistics +#define zdef_(stype,name,string,cnt) qh->qhstat.id[qh->qhstat.next++]=name; \ + qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype +#else +#define zdef_(type,name,doc,count) +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zinc_">-</a> + + zzinc_( name ), zinc_( name) + increment an integer statistic +*/ +#define zzinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;} +#if qh_KEEPstatistics +#define zinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;} +#else +#define zinc_(id) {} +#endif + +/*-<a href="qh-stat_r.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; qh->qhstat.stats[id].i += (val);} +#define wwadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);} +#if qh_KEEPstatistics +#define zadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);} +#define wadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);} +#else +#define zadd_(id, val) {} +#define wadd_(id, val) {} +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zval_">-</a> + + zzval_( name ), zval_( name ), wwval_( name ) + set or return value of a statistic +*/ +#define zzval_(id) ((qh->qhstat.stats[id]).i) +#define wwval_(id) ((qh->qhstat.stats[id]).r) +#if qh_KEEPstatistics +#define zval_(id) ((qh->qhstat.stats[id]).i) +#define wval_(id) ((qh->qhstat.stats[id]).r) +#else +#define zval_(id) qh->qhstat.tempi +#define wval_(id) qh->qhstat.tempr +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zmax_">-</a> + + zmax_( id, val ), wmax_( id, value ) + maximize id with val +*/ +#define wwmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));} +#if qh_KEEPstatistics +#define zmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].i,(val));} +#define wmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));} +#else +#define zmax_(id, val) {} +#define wmax_(id, val) {} +#endif + +/*-<a href="qh-stat_r.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_(qh->qhstat.stats[id].i,(val));} +#define wmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].r,(val));} +#else +#define zmin_(id, val) {} +#define wmin_(id, val) {} +#endif + +/*================== stat_r.h types ==============*/ + + +/*-<a href="qh-stat_r.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_r.htm#TOC" + >--------------------------------</a><a name="qhstat">-</a> + + qhstat + Data structure for statistics, similar to qh and qhrbox + + Allocated as part of qhT (libqhull_r.h) +*/ + +struct qhstatT { + intrealT stats[ZEND]; /* integer and real statistics */ + unsigned char id[ZEND+10]; /* id's in print order */ + const char *doc[ZEND]; /* array of documentation strings */ + short int count[ZEND]; /* -1 if none, else index of count to use */ + char type[ZEND]; /* type, see ztypes above */ + char printed[ZEND]; /* true, if statistic has been printed */ + intrealT init[ZTYPEend]; /* initial values by types, set initstatistics */ + + int next; /* next index for zdef_ */ + int precision; /* index for precision problems */ + int vridges; /* index for Voronoi ridges */ + int tempi; + realT tempr; +}; + +/*========== function prototypes ===========*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_allstatA(qhT *qh); +void qh_allstatB(qhT *qh); +void qh_allstatC(qhT *qh); +void qh_allstatD(qhT *qh); +void qh_allstatE(qhT *qh); +void qh_allstatE2(qhT *qh); +void qh_allstatF(qhT *qh); +void qh_allstatG(qhT *qh); +void qh_allstatH(qhT *qh); +void qh_allstatI(qhT *qh); +void qh_allstatistics(qhT *qh); +void qh_collectstatistics(qhT *qh); +void qh_initstatistics(qhT *qh); +boolT qh_newstats(qhT *qh, int idx, int *nextindex); +boolT qh_nostatistic(qhT *qh, int i); +void qh_printallstatistics(qhT *qh, FILE *fp, const char *string); +void qh_printstatistics(qhT *qh, FILE *fp, const char *string); +void qh_printstatlevel(qhT *qh, FILE *fp, int id); +void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex); +realT qh_stddev(int num, realT tot, realT tot2, realT *ave); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFstat */
+ C/user_r.c view
@@ -0,0 +1,527 @@+/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + user.c + user redefinable functions + + see user2_r.c for qh_fprintf, qh_malloc, qh_free + + see README.txt see COPYING.txt for copyright information. + + see libqhull_r.h for data structures, macros, and user-callable functions. + + see user_eg.c, user_eg2.c, and unix.c for examples. + + see user.h for user-definable constants + + use qh_NOmem in mem_r.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 + + Qhull-template 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_r.c for an + example. We recommend keeping io.o for the extra debugging + information it supplies. +*/ + +#include "qhull_ra.h" + +#include <stdarg.h> + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qhull_template">-</a> + + Qhull-template + Template for calling qhull from inside your program + + returns: + exit code(see qh_ERR... in libqhull_r.h) + all memory freed + + notes: + This can be called any number of times. + +*/ +#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(qh) + use NULL to skip qh_produce_output(qh) */ + 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 */ + + qhT qh_qh; /* Qhull's data structure. First argument of most calls */ + qhT *qh= &qh_qh; /* Alternatively -- qhT *qh= (qhT*)malloc(sizeof(qhT)) */ + + QHULL_LIB_CHECK /* Check for compatible library */ + + qh_zero(qh, errfile); + + /* initialize dim, numpoints, points[], ismalloc here */ + exitcode= qh_new_qhull(qh, 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, !qh_ALL); + qh_memfreeshort(qh, &curlong, &totlong); + if (curlong || totlong) + qh_fprintf(qh, errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong); +} +#endif + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="new_qhull">-</a> + + qh_new_qhull(qh, dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile ) + Run qhull and return results in qh. + Returns exitcode (0 if no errors). + Before first call, either call qh_zero(qh, errfile), or set qh to all zero. + + 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(). + do not call qh_init_A (global_r.c) + + Default errfile is stderr, 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') + + see: + Qhull-template at the beginning of this file. + An example of using qh_new_qhull is user_eg_r.c +*/ +int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile) { + /* gcc may issue a "might be clobbered" warning for dim, points, and ismalloc [-Wclobbered]. + These parameters are not referenced after a longjmp() and hence not clobbered. + See http://stackoverflow.com/questions/7721854/what-sense-do-these-clobbered-variable-warnings-make */ + int exitcode, hulldim; + boolT new_ismalloc; + coordT *new_points; + + if(!errfile){ + errfile= stderr; + } + if (!qh->qhmem.ferr) { + qh_meminit(qh, errfile); + } else { + qh_memcheck(qh); + } + if (strncmp(qhull_cmd, "qhull ", (size_t)6)) { + qh_fprintf(qh, errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n"); + return qh_ERRinput; + } + qh_initqhull_start(qh, NULL, outfile, errfile); + trace1((qh, qh->ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd)); + exitcode = setjmp(qh->errexit); + if (!exitcode) + { + qh->NOerrexit = False; + qh_initflags(qh, 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(qh, hulldim); + new_points= qh_sethalfspace_all(qh, dim, numpoints, points, qh->feasible_point); + new_ismalloc= True; + if (ismalloc) + qh_free(points); + }else { + hulldim= dim; + new_points= points; + new_ismalloc= ismalloc; + } + qh_init_B(qh, new_points, numpoints, hulldim, new_ismalloc); + qh_qhull(qh); + qh_check_output(qh); + if (outfile) { + qh_produce_output(qh); + }else { + qh_prepare_output(qh); + } + if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone) + qh_check_points(qh); + } + qh->NOerrexit = True; + return exitcode; +} /* new_qhull */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="errexit">-</a> + + qh_errexit(qh, exitcode, facet, ridge ) + report and exit from an error + report facet and ridge if non-NULL + reports useful information such as last point processed + set qh.FORCEoutput to print neighborhood of facet + + see: + qh_errexit2() in libqhull_r.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(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge) { + + if (qh->ERREXITcalled) { + qh_fprintf(qh, qh->ferr, 8126, "\nqhull error while processing previous error. Exit program\n"); + qh_exit(qh_ERRqhull); + } + qh->ERREXITcalled= True; + if (!qh->QHULLfinished) + qh->hulltime= qh_CPUclock - qh->hulltime; + qh_errprint(qh, "ERRONEOUS", facet, NULL, ridge, NULL); + qh_fprintf(qh, qh->ferr, 8127, "\nWhile executing: %s | %s\n", qh->rbox_command, qh->qhull_command); + qh_fprintf(qh, qh->ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options); + if (qh->furthest_id >= 0) { + qh_fprintf(qh, qh->ferr, 8129, "Last point added to hull was p%d.", qh->furthest_id); + if (zzval_(Ztotmerge)) + qh_fprintf(qh, qh->ferr, 8130, " Last merge was #%d.", zzval_(Ztotmerge)); + if (qh->QHULLfinished) + qh_fprintf(qh, qh->ferr, 8131, "\nQhull has finished constructing the hull."); + else if (qh->POSTmerging) + qh_fprintf(qh, qh->ferr, 8132, "\nQhull has started post-merging."); + qh_fprintf(qh, qh->ferr, 8133, "\n"); + } + if (qh->FORCEoutput && (qh->QHULLfinished || (!facet && !ridge))) + qh_produce_output(qh); + else if (exitcode != qh_ERRinput) { + if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh->hull_dim+1) { + qh_fprintf(qh, qh->ferr, 8134, "\nAt error exit:\n"); + qh_printsummary(qh, qh->ferr); + if (qh->PRINTstatistics) { + qh_collectstatistics(qh); + qh_printstatistics(qh, qh->ferr, "at error exit"); + qh_memstatistics(qh, qh->ferr); + } + } + if (qh->PRINTprecision) + qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL); + } + if (!exitcode) + exitcode= qh_ERRqhull; + else if (exitcode == qh_ERRsingular) + qh_printhelp_singular(qh, qh->ferr); + else if (exitcode == qh_ERRprec && !qh->PREmerge) + qh_printhelp_degenerate(qh, qh->ferr); + if (qh->NOerrexit) { + qh_fprintf(qh, qh->ferr, 6187, "qhull error while ending program, or qh->NOerrexit not cleared after setjmp(). Exit program with error.\n"); + qh_exit(qh_ERRqhull); + } + qh->ERREXITcalled= False; + qh->NOerrexit= True; + qh->ALLOWrestart= False; /* longjmp will undo qh_build_withrestart */ + longjmp(qh->errexit, exitcode); +} /* errexit */ + + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="errprint">-</a> + + qh_errprint(qh, 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(qhT *qh, const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) { + int i; + + if (atfacet) { + qh_fprintf(qh, qh->ferr, 8135, "%s FACET:\n", string); + qh_printfacet(qh, qh->ferr, atfacet); + } + if (otherfacet) { + qh_fprintf(qh, qh->ferr, 8136, "%s OTHER FACET:\n", string); + qh_printfacet(qh, qh->ferr, otherfacet); + } + if (atridge) { + qh_fprintf(qh, qh->ferr, 8137, "%s RIDGE:\n", string); + qh_printridge(qh, qh->ferr, atridge); + if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet) + qh_printfacet(qh, qh->ferr, atridge->top); + if (atridge->bottom + && atridge->bottom != atfacet && atridge->bottom != otherfacet) + qh_printfacet(qh, qh->ferr, atridge->bottom); + if (!atfacet) + atfacet= atridge->top; + if (!otherfacet) + otherfacet= otherfacet_(atridge, atfacet); + } + if (atvertex) { + qh_fprintf(qh, qh->ferr, 8138, "%s VERTEX:\n", string); + qh_printvertex(qh, qh->ferr, atvertex); + } + if (qh->fout && qh->FORCEoutput && atfacet && !qh->QHULLfinished && !qh->IStracing) { + qh_fprintf(qh, qh->ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n"); + for (i=0; i < qh_PRINTEND; i++) /* use fout for geomview output */ + qh_printneighborhood(qh, qh->fout, qh->PRINTout[i], atfacet, otherfacet, + !qh_ALL); + } +} /* errprint */ + + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="printfacetlist">-</a> + + qh_printfacetlist(qh, 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(qhT *qh, facetT *facetlist, setT *facets, boolT printall) { + facetT *facet, **facetp; + + qh_printbegin(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall); + FORALLfacet_(facetlist) + qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall); + FOREACHfacet_(facets) + qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall); + qh_printend(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall); +} /* printfacetlist */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printhelp_degenerate">-</a> + + qh_printhelp_degenerate(qh, fp ) + prints descriptive message for precision error + + notes: + no message if qh_QUICKhelp +*/ +void qh_printhelp_degenerate(qhT *qh, FILE *fp) { + + if (qh->MERGEexact || qh->PREmerge || qh->JOGGLEmax < REALmax/2) + qh_fprintf(qh, fp, 9368, "\n\ +A Qhull error has occurred. Qhull should have corrected the above\n\ +precision error. Please send the input and all of the output to\n\ +qhull_bug@qhull.org\n"); + else if (!qh_QUICKhelp) { + qh_fprintf(qh, fp, 9369, "\n\ +Precision problems were detected during construction of the convex hull.\n\ +This occurs because convex hull algorithms assume that calculations are\n\ +exact, but floating-point arithmetic has roundoff errors.\n\ +\n\ +To correct for precision problems, do not use 'Q0'. By default, Qhull\n\ +selects 'C-0' or 'Qx' and merges non-convex facets. With option 'QJ',\n\ +Qhull joggles the input to prevent precision problems. See \"Imprecision\n\ +in Qhull\" (qh-impre.htm).\n\ +\n\ +If you use 'Q0', the output may include\n\ +coplanar ridges, concave ridges, and flipped facets. In 4-d and higher,\n\ +Qhull may produce a ridge with four neighbors or two facets with the same \n\ +vertices. Qhull reports these events when they occur. It stops when a\n\ +concave ridge, flipped facet, or duplicate facet occurs.\n"); +#if REALfloat + qh_fprintf(qh, fp, 9370, "\ +\n\ +Qhull is currently using single precision arithmetic. The following\n\ +will probably remove the precision problems:\n\ + - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n"); +#endif + if (qh->DELAUNAY && !qh->SCALElast && qh->MAXabs_coord > 1e4) + qh_fprintf(qh, fp, 9371, "\ +\n\ +When computing the Delaunay triangulation of coordinates > 1.0,\n\ + - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n"); + if (qh->DELAUNAY && !qh->ATinfinity) + qh_fprintf(qh, fp, 9372, "\ +When computing the Delaunay triangulation:\n\ + - use 'Qz' to add a point at-infinity. This reduces precision problems.\n"); + + qh_fprintf(qh, fp, 9373, "\ +\n\ +If you need triangular output:\n\ + - use option 'Qt' to triangulate the output\n\ + - use option 'QJ' to joggle the input points and remove precision errors\n\ + - use option 'Ft'. It triangulates non-simplicial facets with added points.\n\ +\n\ +If you must use 'Q0',\n\ +try one or more of the following options. They can not guarantee an output.\n\ + - use 'QbB' to scale the input to a cube.\n\ + - use 'Po' to produce output and prevent partitioning for flipped facets\n\ + - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\ + - use 'En' to specify a maximum roundoff error less than %2.2g.\n\ + - options 'Qf', 'Qbb', and 'QR0' may also help\n", + qh->DISTround); + qh_fprintf(qh, fp, 9374, "\ +\n\ +To guarantee simplicial output:\n\ + - use option 'Qt' to triangulate the output\n\ + - use option 'QJ' to joggle the input points and remove precision errors\n\ + - use option 'Ft' to triangulate the output by adding points\n\ + - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\ +"); + } +} /* printhelp_degenerate */ + + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="printhelp_narrowhull">-</a> + + qh_printhelp_narrowhull(qh, minangle ) + Warn about a narrow hull + + notes: + Alternatively, reduce qh_WARNnarrow in user.h + +*/ +void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle) { + + qh_fprintf(qh, fp, 9375, "qhull precision warning: \n\ +The initial hull is narrow (cosine of min. angle is %.16f).\n\ +Is the input lower dimensional (e.g., on a plane in 3-d)? Qhull may\n\ +produce a wide facet. Options 'QbB' (scale to unit box) or 'Qbb' (scale\n\ +last coordinate) may remove this warning. Use 'Pp' to skip this warning.\n\ +See 'Limitations' in qh-impre.htm.\n", + -minangle); /* convert from angle between normals to angle between facets */ +} /* printhelp_narrowhull */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printhelp_singular">-</a> + + qh_printhelp_singular(qh, fp ) + prints descriptive message for singular input +*/ +void qh_printhelp_singular(qhT *qh, FILE *fp) { + facetT *facet; + vertexT *vertex, **vertexp; + realT min, max, *coord, dist; + int i,k; + + qh_fprintf(qh, fp, 9376, "\n\ +The input to qhull appears to be less than %d dimensional, or a\n\ +computation has overflowed.\n\n\ +Qhull could not construct a clearly convex simplex from points:\n", + qh->hull_dim); + qh_printvertexlist(qh, fp, "", qh->facet_list, NULL, qh_ALL); + if (!qh_QUICKhelp) + qh_fprintf(qh, fp, 9377, "\n\ +The center point is coplanar with a facet, or a vertex is coplanar\n\ +with a neighboring facet. The maximum round off error for\n\ +computing distances is %2.2g. The center point, facets and distances\n\ +to the center point are as follows:\n\n", qh->DISTround); + qh_printpointid(qh, fp, "center point", qh->hull_dim, qh->interior_point, qh_IDunknown); + qh_fprintf(qh, fp, 9378, "\n"); + FORALLfacets { + qh_fprintf(qh, fp, 9379, "facet"); + FOREACHvertex_(facet->vertices) + qh_fprintf(qh, fp, 9380, " p%d", qh_pointid(qh, vertex->point)); + zinc_(Zdistio); + qh_distplane(qh, qh->interior_point, facet, &dist); + qh_fprintf(qh, fp, 9381, " distance= %4.2g\n", dist); + } + if (!qh_QUICKhelp) { + if (qh->HALFspace) + qh_fprintf(qh, fp, 9382, "\n\ +These points are the dual of the given halfspaces. They indicate that\n\ +the intersection is degenerate.\n"); + qh_fprintf(qh, fp, 9383,"\n\ +These points either have a maximum or minimum x-coordinate, or\n\ +they maximize the determinant for k coordinates. Trial points\n\ +are first selected from points that maximize a coordinate.\n"); + if (qh->hull_dim >= qh_INITIALmax) + qh_fprintf(qh, fp, 9384, "\n\ +Because of the high dimension, the min x-coordinate and max-coordinate\n\ +points are used if the determinant is non-zero. Option 'Qs' will\n\ +do a better, though much slower, job. Instead of 'Qs', you can change\n\ +the points by randomly rotating the input with 'QR0'.\n"); + } + qh_fprintf(qh, fp, 9385, "\nThe min and max coordinates for each dimension are:\n"); + for (k=0; k < qh->hull_dim; k++) { + min= REALmax; + max= -REALmin; + for (i=qh->num_points, coord= qh->first_point+k; i--; coord += qh->hull_dim) { + maximize_(max, *coord); + minimize_(min, *coord); + } + qh_fprintf(qh, fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min); + } + if (!qh_QUICKhelp) { + qh_fprintf(qh, fp, 9387, "\n\ +If the input should be full dimensional, you have several options that\n\ +may determine an initial simplex:\n\ + - use 'QJ' to joggle the input and make it full dimensional\n\ + - use 'QbB' to scale the points to the unit cube\n\ + - use 'QR0' to randomly rotate the input for different maximum points\n\ + - use 'Qs' to search all points for the initial simplex\n\ + - use 'En' to specify a maximum roundoff error less than %2.2g.\n\ + - trace execution with 'T3' to see the determinant for each point.\n", + qh->DISTround); +#if REALfloat + qh_fprintf(qh, fp, 9388, "\ + - recompile qhull for realT precision(#define REALfloat 0 in libqhull_r.h).\n"); +#endif + qh_fprintf(qh, fp, 9389, "\n\ +If the input is lower dimensional:\n\ + - use 'QJ' to joggle the input and make it full dimensional\n\ + - use 'Qbk:0Bk:0' to delete coordinate k from the input. You should\n\ + pick the coordinate with the least range. The hull will have the\n\ + correct topology.\n\ + - determine the flat containing the points, rotate the points\n\ + into a coordinate plane, and delete the other coordinates.\n\ + - add one or more points to make the input full dimensional.\n\ +"); + } +} /* printhelp_singular */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="user_memsizes">-</a> + + qh_user_memsizes(qh) + allocate up to 10 additional, quick allocation sizes + + notes: + increase maximum number of allocations in qh_initqhull_mem() +*/ +void qh_user_memsizes(qhT *qh) { + + QHULL_UNUSED(qh) + /* qh_memsize(qh, size); */ +} /* user_memsizes */ + +
+ C/user_r.h view
@@ -0,0 +1,882 @@+/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + user.h + user redefinable constants + + for each source file, user_r.h is included first + + see qh-user_r.htm. see COPYING for copyright information. + + See user_r.c for sample code. + + before reading any code, review libqhull_r.h for data structure definitions + +Sections: + ============= qhull library constants ====================== + ============= data types and configuration macros ========== + ============= performance related constants ================ + ============= memory constants ============================= + ============= joggle constants ============================= + ============= conditional compilation ====================== + ============= -merge constants- ============================ + +Code flags -- + NOerrors -- the code does not call qh_errexit() + WARN64 -- the code may be incompatible with 64-bit pointers + +*/ + +#include <time.h> + +#ifndef qhDEFuser +#define qhDEFuser 1 + +/* Derived from Qt's corelib/global/qglobal.h */ +#if !defined(SAG_COM) && !defined(__CYGWIN__) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) +# define QHULL_OS_WIN +#elif defined(__MWERKS__) && defined(__INTEL__) /* Metrowerks discontinued before the release of Intel Macs */ +# define QHULL_OS_WIN +#endif + +/*============================================================*/ +/*============= qhull library constants ======================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="filenamelen">-</a> + + FILENAMElen -- max length for TI and TO filenames + +*/ + +#define qh_FILENAMElen 500 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="msgcode">-</a> + + msgcode -- Unique message codes for qh_fprintf + + If add new messages, assign these values and increment in user.h and user_r.h + See QhullError.h for 10000 errors. + + def counters = [27, 1048, 2059, 3026, 4068, 5003, + 6273, 7081, 8147, 9411, 10000, 11029] + + See: qh_ERR* [libqhull_r.h] +*/ + +#define MSG_TRACE0 0 +#define MSG_TRACE1 1000 +#define MSG_TRACE2 2000 +#define MSG_TRACE3 3000 +#define MSG_TRACE4 4000 +#define MSG_TRACE5 5000 +#define MSG_ERROR 6000 /* errors written to qh.ferr */ +#define MSG_WARNING 7000 +#define MSG_STDERR 8000 /* log messages Written to qh.ferr */ +#define MSG_OUTPUT 9000 +#define MSG_QHULL_ERROR 10000 /* errors thrown by QhullError.cpp (QHULLlastError is in QhullError.h) */ +#define MSG_FIXUP 11000 /* FIXUP QH11... */ +#define MSG_MAXLEN 3000 /* qh_printhelp_degenerate() in user.c */ + + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="qh_OPTIONline">-</a> + + qh_OPTIONline -- max length of an option line 'FO' +*/ +#define qh_OPTIONline 80 + +/*============================================================*/ +/*============= data types and configuration macros ==========*/ +/*============================================================*/ + +/*-<a href="qh-user_r.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 + helpful if high-d where convex hulls are space limited. Using 'float' also + reduces the printed size of Qhull's output since numbers have 8 digits of + precision. + + Use 'double' when greater arithmetic precision is needed. This is needed + for Delaunay triangulations and Voronoi diagrams when you are not merging + facets. + + If 'double' gives insufficient precision, your data probably includes + degeneracies. If so you should use facet merging (done by default) + or exact arithmetic (see imprecision section of manual, qh-impre.htm). + You may also use option 'Po' to force output despite precision errors. + + You may use 'long double', but many format statements need to be changed + and you may need a 'long double' square root routine. S. Grundmann + (sg@eeiwzb.et.tu-dresden.de) has done this. He reports that the code runs + much slower with little gain in precision. + + WARNING: on some machines, int f(){realT a= REALmax;return (a == REALmax);} + returns False. Use (a > REALmax/2) instead of (a == REALmax). + + REALfloat = 1 all numbers are 'float' type + = 0 all numbers are 'double' type +*/ +#define REALfloat 0 + +#if (REALfloat == 1) +#define realT float +#define REALmax FLT_MAX +#define REALmin FLT_MIN +#define REALepsilon FLT_EPSILON +#define qh_REALdigits 8 /* maximum number of significant digits */ +#define qh_REAL_1 "%6.8g " +#define qh_REAL_2n "%6.8g %6.8g\n" +#define qh_REAL_3n "%6.8g %6.8g %6.8g\n" + +#elif (REALfloat == 0) +#define realT double +#define REALmax DBL_MAX +#define REALmin DBL_MIN +#define REALepsilon DBL_EPSILON +#define qh_REALdigits 16 /* maximum number of significant digits */ +#define qh_REAL_1 "%6.16g " +#define qh_REAL_2n "%6.16g %6.16g\n" +#define qh_REAL_3n "%6.16g %6.16g %6.16g\n" + +#else +#error unknown float option +#endif + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="countT">-</a> + + countT + The type for counts and identifiers (e.g., the number of points, vertex identifiers) + Currently used by C++ code-only. Decided against using it for setT because most sets are small. + + Defined as 'int' for C-code compatibility and QH11026 + + FIXUP QH11026 countT may be defined as a unsigned value, but several code issues need to be solved first. See countT in Changes.txt +*/ + +#ifndef DEFcountT +#define DEFcountT 1 +typedef int countT; +#endif +#define COUNTmax 0x7fffffff + + +/*-<a href="qh-user_r.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. By default, + <time.h> defines clock_t as 'long' + + Set qh_CLOCKtype to + + 1 for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond + Note: may fail if more than 1 hour elapsed time + + 2 use qh_clock() with POSIX times() (see global_r.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_r.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(qh) with 31 bits (included with Qhull, requires 'qh') + + 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_(qh, 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_(qh, seed) srand((unsigned)seed); + +#elif (qh_RANDOMtype == 3) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, Sun */ +#define qh_RANDOMint rand() +#define qh_RANDOMseed_(qh, seed) srand((unsigned)seed); + +#elif (qh_RANDOMtype == 4) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, lrand38()/MAX */ +#define qh_RANDOMint lrand48() +#define qh_RANDOMseed_(qh, seed) srand48(seed); + +#elif (qh_RANDOMtype == 5) /* 'qh' is an implicit parameter */ +#define qh_RANDOMmax ((realT)2147483646UL) /* 31 bits, qh_rand/MAX */ +#define qh_RANDOMint qh_rand(qh) +#define qh_RANDOMseed_(qh, seed) qh_srand(qh, seed); +/* unlike rand(), never returns 0 */ + +#else +#error: unknown random option +#endif + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="ORIENTclock">-</a> + + qh_ORIENTclock + 0 for inward pointing normals by Geomview convention +*/ +#define qh_ORIENTclock 0 + + +/*============================================================*/ +/*============= joggle constants =============================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.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_r.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_r.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_r.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_r.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_r.htm#TOC" +>--------------------------------</a><a name="JOGGLEmaxretry">-</a> + +qh_JOGGLEmaxretry +stop after qh_JOGGLEmaxretry attempts +*/ +#define qh_JOGGLEmaxretry 100 + +/*============================================================*/ +/*============= performance related constants ================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.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_r.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_r.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_r.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 + +/*============================================================*/ +/*============= memory constants =============================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MEMalign">-</a> + + qh_MEMalign + memory alignment for qh_meminitbuffers() in global_r.c + + notes: + to avoid bus errors, memory allocation must consider alignment requirements. + malloc() automatically takes care of alignment. Since mem_r.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 [fmax_() is defined in geom_r.h] + #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *)) +*/ +#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *)))) + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MEMbufsize">-</a> + + qh_MEMbufsize + size of additional memory buffers + + notes: + used for qh_meminitbuffers() in global_r.c +*/ +#define qh_MEMbufsize 0x10000 /* allocate 64K memory buffers */ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MEMinitbuf">-</a> + + qh_MEMinitbuf + size of initial memory buffer + + notes: + use for qh_meminitbuffers() in global_r.c +*/ +#define qh_MEMinitbuf 0x20000 /* initially allocate 128K buffer */ + +/*-<a href="qh-user_r.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_r.htm#TOC" + >--------------------------------</a><a name="DEFAULTbox">-</a> + + qh_DEFAULTbox + default box size (Geomview expects 0.5) + + qh_DEFAULTbox + default box size for integer coorindate (rbox only) +*/ +#define qh_DEFAULTbox 0.5 +#define qh_DEFAULTzbox 1e6 + +/*============================================================*/ +/*============= conditional compilation ======================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="compiler">-</a> + + __cplusplus + defined by C++ compilers + + __MSC_VER + defined by Microsoft Visual C++ + + __MWERKS__ && __INTEL__ + defined by Metrowerks when compiling for Windows (not Intel-based Macintosh) + + __MWERKS__ && __POWERPC__ + defined by Metrowerks when compiling for PowerPC-based Macintosh + + __STDC__ + defined for strict ANSI C +*/ + +/*-<a href="qh-user_r.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_r.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_r.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_r.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_r.h#NOmem">qh_NOmem</a> in mem_r.c + + see user_r.c/user_eg.c for removing io_r.o +*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="NOtrace">-</a> + + qh_NOtrace + no tracing if defined + + notes: + This saves about 5% space. + + #define qh_NOtrace +*/ + +#if 0 /* sample code */ + exitcode= qh_new_qhull(qhT *qh, dim, numpoints, points, ismalloc, + flags, outfile, errfile); + qh_freeqhull(qhT *qh, !qh_ALL); /* frees long memory used by second call */ + qh_memfreeshort(qhT *qh, &curlong, &totlong); /* frees short memory and memory allocator */ +#endif + +/*-<a href="qh-user_r.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 them. They effect the performance of facet merging. +*/ + +/*-<a href="qh-user_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.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_r.htm#TOC" + >--------------------------------</a><a name="WIDEduplicate">-</a> + + qh_WIDEduplicate + Merge ratio for errexit from qh_forcedmerges due to duplicate ridge + Override with option Q12 no-wide-duplicate + + Notes: + Merging a duplicate ridge can lead to very wide facets. + A future release of qhull will avoid duplicate ridges by removing duplicate sub-ridges from the horizon +*/ +#define qh_WIDEduplicate 100 + +/*-<a href="qh-user_r.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_r.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_r.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 + +/*============================================================*/ +/*============= Microsoft DevStudio ==========================*/ +/*============================================================*/ + +/* + Finding Memory Leaks Using the CRT Library + https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.100).aspx + + Reports enabled in qh_lib_check for Debug window and stderr + + From 2005=>msvcr80d, 2010=>msvcr100d, 2012=>msvcr110d + + Watch: {,,msvcr80d.dll}_crtBreakAlloc Value from {n} in the leak report + _CrtSetBreakAlloc(689); // qh_lib_check() [global_r.c] + + Examples + http://free-cad.sourceforge.net/SrcDocu/d2/d7f/MemDebug_8cpp_source.html + https://github.com/illlust/Game/blob/master/library/MemoryLeak.cpp +*/ +#if 0 /* off (0) by default for QHULL_CRTDBG */ +#define QHULL_CRTDBG +#endif + +#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) +#define _CRTDBG_MAP_ALLOC +#include <stdlib.h> +#include <crtdbg.h> +#endif + +#endif /* qh_DEFuser */ + + +
+ C/usermem_r.c view
@@ -0,0 +1,94 @@+/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + usermem_r.c + qh_exit(), qh_free(), and qh_malloc() + + See README.txt. + + If you redefine one of these functions you must redefine all of them. + If you recompile and load this file, then usermem.o will not be loaded + from qhull.a or qhull.lib + + See libqhull_r.h for data structures, macros, and user-callable functions. + See user_r.c for qhull-related, redefinable functions + see user_r.h for user-definable constants + See userprintf_r.c for qh_fprintf and userprintf_rbox_r.c for qh_fprintf_rbox + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull_r.h" + +#include <stdarg.h> +#include <stdlib.h> + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_exit">-</a> + + qh_exit( exitcode ) + exit program + + notes: + qh_exit() is called when qh_errexit() and longjmp() are not available. + + This is the only use of exit() in Qhull + To replace qh_exit with 'throw', see libqhullcpp/usermem_r-cpp.cpp +*/ +void qh_exit(int exitcode) { + exit(exitcode); +} /* exit */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_fprintf_stderr">-</a> + + qh_fprintf_stderr( msgcode, format, list of args ) + fprintf to stderr with msgcode (non-zero) + + notes: + qh_fprintf_stderr() is called when qh->ferr is not defined, usually due to an initialization error + + It is typically followed by qh_errexit(). + + Redefine this function to avoid using stderr + + Use qh_fprintf [userprintf_r.c] for normal printing +*/ +void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) { + va_list args; + + va_start(args, fmt); + if(msgcode) + fprintf(stderr, "QH%.4d ", msgcode); + vfprintf(stderr, fmt, args); + va_end(args); +} /* fprintf_stderr */ + +/*-<a href="qh-user_r.htm#TOC" +>-------------------------------</a><a name="qh_free">-</a> + + qh_free(qhT *qh, mem ) + free memory + + notes: + same as free() + No calls to qh_errexit() +*/ +void qh_free(void *mem) { + free(mem); +} /* free */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_malloc">-</a> + + qh_malloc( mem ) + allocate memory + + notes: + same as malloc() +*/ +void *qh_malloc(size_t size) { + return malloc(size); +} /* malloc */ + +
+ C/userprintf_r.c view
@@ -0,0 +1,65 @@+/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + userprintf_r.c + qh_fprintf() + + see README.txt see COPYING.txt for copyright information. + + If you recompile and load this file, then userprintf_r.o will not be loaded + from qhull.a or qhull.lib + + See libqhull_r.h for data structures, macros, and user-callable functions. + See user_r.c for qhull-related, redefinable functions + see user_r.h for user-definable constants + See usermem_r.c for qh_exit(), qh_free(), and qh_malloc() + see Qhull.cpp and RboxPoints.cpp for examples. + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull_r.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_fprintf">-</a> + + qh_fprintf(qh, fp, msgcode, format, list of args ) + print arguments to *fp according to format + Use qh_fprintf_rbox() for rboxlib_r.c + + notes: + same as fprintf() + fgets() is not trapped like fprintf() + exit qh_fprintf via qh_errexit() + may be called for errors in qh_initstatistics and qh_meminit +*/ + +void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) { + va_list args; + + if (!fp) { + if(!qh){ + qh_fprintf_stderr(6241, "userprintf_r.c: fp and qh not defined for qh_fprintf '%s'", fmt); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + /* could use qh->qhmem.ferr, but probably better to be cautious */ + qh_fprintf_stderr(6232, "Qhull internal error (userprintf_r.c): fp is 0. Wrong qh_fprintf called.\n"); + qh_errexit(qh, 6232, NULL, NULL); + } + va_start(args, fmt); + if (qh && qh->ANNOTATEoutput) { + fprintf(fp, "[QH%.4d]", msgcode); + }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) { + fprintf(fp, "QH%.4d ", msgcode); + } + vfprintf(fp, fmt, args); + va_end(args); + + /* Place debugging traps here. Use with option 'Tn' */ + +} /* qh_fprintf */ +
+ C/utils.c view
@@ -0,0 +1,91 @@+#include <stdlib.h> // to use realloc +#include <math.h> // to use NAN +#include <stdio.h> // to use printf + +double* getpoint(double* points, unsigned dim, unsigned id){ + double* out = malloc(dim * sizeof(double)); + for(unsigned i=0; i < dim; i++){ + out[i] = points[id*dim+i]; + } + return out; +} + +/* dot product of two vectors */ +double dotproduct(double* p1, double* p2, unsigned dim){ + double out = 0; + for(unsigned i=0; i < dim; i++){ + out += p1[i] * p2[i]; + } + return out; +} + +/* middle of segment [p1,p2] */ +double* middle(double* p1, double* p2, unsigned dim){ + double* out = malloc(dim * sizeof(double)); + for(unsigned i=0; i<dim; i++){ + out[i] = (p1[i] + p2[i])/2; + } + return out; +} + +/* vector of NANs */ +double* nanvector(int dim){ + double* out = malloc(dim * sizeof(double)); + for(unsigned i=0; i < dim; i++){ + out[i] = NAN; + } + return out; +} + +// to use the qsort function +int cmpfunc (const void * a, const void * b) { + return ( *(int*)a - *(int*)b ); +} +int cmpfuncdbl (const void * a, const void * b) { + return ( *(double*)a - *(double*)b > 0 ? 1 : -1); +} +void qsortu(unsigned* vector, unsigned length){ + qsort(vector, length, sizeof(unsigned), cmpfunc); +} + + +double square(double x){ + return x*x; +} + +/* append to a vector of unsigned */ +void appendu(unsigned x, unsigned** array, unsigned length, unsigned* flag){ + *flag = 1; + for(unsigned i=0; i<length; i++){ + if(x==*(*array + i)){ + *flag = 0; + break; + } + } + if(*flag==1){ + *array = realloc(*array, (length+1)*sizeof(unsigned)); + if(*array == NULL){ + printf("realloc failure - exiting"); + exit(1); + } + *(*array + length) = x; + } +} + +/* make a vector of zeros */ +unsigned* uzeros(unsigned length){ + unsigned* out = malloc(length * sizeof(unsigned)); + for(unsigned i=0; i < length; i++){ + out[i] = 0; + } + return out; +} + +/* squared distance between two points */ +double squaredDistance(double* p1, double* p2, unsigned dim){ + double out = 0; + for(unsigned i=0; i < dim; i++){ + out += square(p1[i] - p2[i]); + } + return out; +}
+ C/utils.h view
@@ -0,0 +1,20 @@+ +int cmpfunc(const void*, const void*); +int cmpfuncdbl(const void*, const void*); +void qsortu(unsigned*, unsigned); + +double square(double); + +void appendu(unsigned, unsigned**, unsigned, unsigned*); + +double* nanvector(int); + +double* middle(double*, double*, unsigned); + +double* getpoint(double*, unsigned, unsigned); + +double dotproduct(double*, double*, unsigned); + +unsigned* uzeros(unsigned); + +double squaredDistance(double*, double*, unsigned);
+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Changelog for `vertexenum`++## 0.1.0.0 - 2023-11-18++First release.
+ LICENSE view
@@ -0,0 +1,674 @@+ GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>.
+ README.md view
@@ -0,0 +1,46 @@+# vertexenum++<!-- badges: start -->+[](https://github.com/stla/vertexenum/actions/workflows/Stack.yml)+<!-- badges: end -->++*Get the vertices of an intersection of halfspaces.*++____++Consider the following system of linear inequalities:++$$\left\{\begin{matrix} -5 & \leqslant & x & \leqslant & 4 \\\\ -5 & \leqslant & y & \leqslant & 3-x \\\\ -10 & \leqslant & z & \leqslant & 6-2x-y \end{matrix}.\right.$$++Each inequality defines a halfspace. The intersection of the six halfspaces is+a convex polytope. The `vertexenum` function can calculate the vertices of this +polytope:++```haskell+import Data.Ratio ( (%) )+import Data.VectorSpace ( AdditiveGroup((^+^), (^-^))+ , VectorSpace((*^)) )+import Geometry.VertexEnum++constraints :: [Constraint]+constraints =+ [ x .>= (-5) -- shortcut for `x .>=. cst (-5)`+ , x .<= 4+ , y .>= (-5)+ , y .<=. cst 3 ^-^ x -- we need `cst` here+ , z .>= (-10)+ , z .<=. cst 6 ^-^ 2*^x ^-^ y ]+ where+ x = newVar 1+ y = newVar 2+ z = newVar 3++vertexenum constraints Nothing+```++The type of the second argument of `vertexenum` is `Maybe [Double]`. If this +argument is `Just point`, then `point` must be the coordinates of a point +interior to the polytope. If this argument is `Nothing`, an interior point +is automatically calculated. You can get it with the `interiorPoint` function. +It is easy to mentally get an interior point for the above example, but in +general this is not an easy problem.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Geometry/VertexEnum.hs view
@@ -0,0 +1,25 @@+{-| +Module : Geometry.VertexEnum +Description : Vertex enumeration of convex polytopes. +Copyright : (c) Stéphane Laurent, 2023 +License : GPL-3 +Maintainer : laurent_step@outlook.fr + +See README for an example. +-} +module Geometry.VertexEnum + ( module X ) + where +import Geometry.VertexEnum.Constraint as X ( Constraint(..) + , Sense(..) + , (.>=.), (.<=.), (.>=), (.<=) + ) +import Geometry.VertexEnum.LinearCombination as X ( VarIndex + , LinearCombination(..) + , newVar + , linearCombination + , constant + , cst ) +import Geometry.VertexEnum.VertexEnum as X ( vertexenum + , checkConstraints + , interiorPoint )
+ src/Geometry/VertexEnum/CVertexEnum.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE ForeignFunctionInterface #-} +module Geometry.VertexEnum.CVertexEnum + ( c_intersections ) + where +import Foreign ( Ptr ) +import Foreign.C.Types ( CDouble, CUInt(..) ) + +foreign import ccall unsafe "intersections" c_intersections + :: Ptr CDouble -- halfspaces + -> Ptr CDouble -- interior point + -> CUInt -- dim + -> CUInt -- n halfspaces + -> Ptr CUInt -- n intersections + -> Ptr CUInt -- exitcode + -> CUInt -- 0/1 print to stdout + -> IO (Ptr (Ptr CDouble))
+ src/Geometry/VertexEnum/Constraint.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE InstanceSigs #-} +module Geometry.VertexEnum.Constraint + ( Sense (..) + , Constraint (..) + , (.>=.) + , (.<=.) + , (.>=) + , (.<=) ) + where +import Geometry.VertexEnum.LinearCombination ( LinearCombination, constant ) + +data Sense = Gt | Lt + deriving Eq + +instance Show Sense where + show :: Sense -> String + show Gt = ">=" + show Lt = "<=" + +data Constraint = Constraint LinearCombination Sense LinearCombination + deriving (Eq, Show) + +(.>=.) :: LinearCombination -> LinearCombination -> Constraint +(.>=.) lhs rhs = Constraint lhs Gt rhs + +(.<=.) :: LinearCombination -> LinearCombination -> Constraint +(.<=.) lhs rhs = Constraint lhs Lt rhs + +(.>=) :: LinearCombination -> Rational -> Constraint +(.>=) lhs x = (.>=.) lhs (constant x) + +(.<=) :: LinearCombination -> Rational -> Constraint +(.<=) lhs x = (.<=.) lhs (constant x) + +infix 4 .<=., .>=.
+ src/Geometry/VertexEnum/Internal.hs view
@@ -0,0 +1,87 @@+module Geometry.VertexEnum.Internal + ( normalizeConstraints + , varsOfConstraint + , iPoint ) + where +import Data.IntMap.Strict ( IntMap, mergeWithKey ) +import qualified Data.IntMap.Strict as IM +import Data.List ( nub, union ) +import Data.Ratio ( (%), numerator, denominator ) +import Geometry.VertexEnum.Constraint ( Constraint (..), Sense (..) ) +import Geometry.VertexEnum.LinearCombination ( LinearCombination (..), VarIndex ) +import Numeric.LinearProgramming ( simplex, + Bound(Free, (:<=:)), + Constraints(Dense), + Optimization(Maximize), + Solution( + Undefined + , Feasible + , Infeasible + , NoFeasible + , Optimal + , Unbounded) ) + +normalizeLinearCombination :: + [VarIndex] -> LinearCombination -> IntMap Rational +normalizeLinearCombination vars (LinearCombination lc) = + IM.union lc (IM.fromList [(i,0) | i <- vars `union` [0]]) + +varsOfLinearCombo :: LinearCombination -> [VarIndex] +varsOfLinearCombo (LinearCombination imap) = IM.keys imap + +varsOfConstraint :: Constraint -> [VarIndex] +varsOfConstraint (Constraint lhs _ rhs) = + varsOfLinearCombo lhs `union` varsOfLinearCombo rhs + +normalizeConstraint :: [VarIndex] -> Constraint -> [Double] +normalizeConstraint vars (Constraint lhs sense rhs) = + if sense == Lt + then xs ++ [x] + else map negate xs ++ [-x] + where + lhs' = normalizeLinearCombination vars lhs + rhs' = normalizeLinearCombination vars rhs + coefs = IM.elems $ mergeWithKey (\_ a b -> Just (a-b)) id id lhs' rhs' + denominators = map denominator coefs + ppcm = foldr lcm 1 denominators % 1 + (x, xs) = case map (realToFrac . numerator . (*ppcm)) coefs of + (xx:xxs) -> (xx, xxs) + [] -> (0, []) + -- let (x:xs) = map realToFrac $ + -- IM.elems $ mergeWithKey (\_ a b -> Just (a-b)) id id lhs' rhs' + -- in + -- if sense == Lt + -- then xs ++ [x] + -- else map negate xs ++ [-x] + -- where lhs' = normalizeLinearCombination vars lhs + -- rhs' = normalizeLinearCombination vars rhs + +normalizeConstraints :: [Constraint] -> [[Double]] -- for qhalf +normalizeConstraints constraints = + map (normalizeConstraint vars) constraints + where + vars = nub $ concatMap varsOfConstraint constraints + +inequality :: [Double] -> Bound [Double] +inequality row = (coeffs ++ [1.0]) :<=: bound + where + coeffs = init row + bound = -(last row) + +inequalities :: [[Double]] -> Constraints +inequalities normConstraints = Dense (map inequality normConstraints) + +iPoint :: [[Double]] -> [Double] +iPoint halfspacesMatrix = case solution of + Optimal (_, point) -> init point + Undefined -> error "Failed to find interior point (undefined)." + Feasible (_, _) -> error "Failed to find interior point (feasible)." + Infeasible (_, _) -> error "Failed to find interior point (infeasible)." + NoFeasible -> error "Failed to find interior point (no feasible)." + Unbounded -> error "Failed to find interior point (unbounded)." + where + constraints' = inequalities halfspacesMatrix + n = length (head halfspacesMatrix) + objective = Maximize (replicate (n-1) 0 ++ [1]) + bounds = map Free [1 .. (n-1)] + solution = simplex objective constraints' bounds
+ src/Geometry/VertexEnum/LinearCombination.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE InstanceSigs #-} +module Geometry.VertexEnum.LinearCombination + ( LinearCombination (..) + , newVar + , VarIndex + , linearCombination + , constant + , cst + ) + where +import Data.AdditiveGroup ( AdditiveGroup(zeroV, negateV, (^+^)) ) +import Data.IntMap.Strict ( IntMap, mergeWithKey ) +import qualified Data.IntMap.Strict as IM +import Data.List ( intercalate ) +import Data.Ratio ( numerator, denominator ) +import Data.Tuple ( swap ) +import Data.VectorSpace ( linearCombo, VectorSpace(..) ) + +newtype LinearCombination = LinearCombination (IntMap Rational) + deriving Eq + +instance Show LinearCombination where + show :: LinearCombination -> String + show (LinearCombination x) = + intercalate " + " $ + map (\(i, r) -> if i == 0 + then showRational r + else if r == 1 + then "x" ++ show i + else showRational r ++ "*x" ++ show i) + (IM.toAscList x) + where + showRational :: Rational -> String + showRational r = if q == 1 then show p else show p ++ "/" ++ show q + where + p = numerator r + q = denominator r + +instance AdditiveGroup LinearCombination where + zeroV :: LinearCombination + zeroV = LinearCombination (IM.singleton 0 0) + (^+^) :: LinearCombination -> LinearCombination -> LinearCombination + (^+^) (LinearCombination imap1) (LinearCombination imap2) = + LinearCombination + (mergeWithKey (\_ x y -> Just (x+y)) id id imap1 imap2) + negateV :: LinearCombination -> LinearCombination + negateV (LinearCombination imap) = LinearCombination (IM.map negate imap) + +instance VectorSpace LinearCombination where + type Scalar LinearCombination = Rational + (*^) :: Scalar LinearCombination -> LinearCombination -> LinearCombination + (*^) lambda (LinearCombination imap) = + LinearCombination (IM.map (*lambda) imap) + +type Var = LinearCombination +type VarIndex = Int + +-- | new variable +newVar :: VarIndex -> Var +newVar i = if i >= 0 + then LinearCombination (IM.singleton i 1) + else error "negative index" + +-- | linear combination from list of terms +linearCombination :: [(Rational, Var)] -> LinearCombination +linearCombination terms = linearCombo (map swap terms) +-- LinearCombination (IM.fromListWith (+) (map swap terms)) + +-- | constant linear combination +constant :: Rational -> LinearCombination +constant x = LinearCombination (IM.singleton 0 x) + +-- | alias for `constant` +cst :: Rational -> LinearCombination +cst = constant
+ src/Geometry/VertexEnum/VertexEnum.hs view
@@ -0,0 +1,87 @@+module Geometry.VertexEnum.VertexEnum + ( vertexenum + , checkConstraints + , interiorPoint ) + where +import Control.Monad ( unless, when, (<$!>) ) +import Foreign.C.Types ( CDouble, CUInt ) +import Foreign.Marshal.Alloc ( free, mallocBytes ) +import Foreign.Marshal.Array ( peekArray, pokeArray ) +import Foreign.Storable ( peek, sizeOf ) +import Geometry.VertexEnum.CVertexEnum ( c_intersections ) +import Geometry.VertexEnum.Constraint ( Constraint ) +import Geometry.VertexEnum.Internal ( iPoint, normalizeConstraints ) + +hsintersections :: [[Double]] -- halfspaces + -> [Double] -- interior point + -> Bool -- print to stdout + -> IO [[Double]] +hsintersections halfspaces ipoint stdout = do + let n = length halfspaces + dim = length ipoint + unless (all ((== dim+1) . length) halfspaces) $ + error "the points must have the same dimension" + when (dim < 2) $ + error "dimension must be at least 2" + when (n <= dim) $ + error "insufficient number of halfspaces" + hsPtr <- mallocBytes (n * (dim+1) * sizeOf (undefined :: CDouble)) + pokeArray hsPtr (concatMap (map realToFrac) halfspaces) + ipointPtr <- mallocBytes (dim * sizeOf (undefined :: CDouble)) + pokeArray ipointPtr (map realToFrac ipoint) + exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt)) + nintersectionsPtr <- mallocBytes (sizeOf (undefined :: CUInt)) + resultPtr <- c_intersections hsPtr ipointPtr + (fromIntegral dim) (fromIntegral n) + nintersectionsPtr exitcodePtr (fromIntegral $ fromEnum stdout) + exitcode <- peek exitcodePtr + free exitcodePtr + free hsPtr + if exitcode /= 0 + then do + free resultPtr + free nintersectionsPtr + error $ "qhull returned an error (code " ++ show exitcode ++ ")" + else do + nintersections <- (<$!>) fromIntegral (peek nintersectionsPtr) + result <- (<$!>) (map (map realToFrac)) + ((=<<) (mapM (peekArray dim)) + (peekArray nintersections resultPtr)) + free resultPtr + free nintersectionsPtr + return result + +-- | Vertex enumeration +vertexenum :: [Constraint] -- ^ list of inequalities + -> Maybe [Double] -- ^ point in the interior of the polytope + -> IO [[Double]] +vertexenum constraints point = do + let halfspacesMatrix = normalizeConstraints constraints + ipoint = case point of + Just x -> x + Nothing -> iPoint halfspacesMatrix + hsintersections halfspacesMatrix ipoint False + +-- | Check whether a point fulfills some constraints; returns the +-- difference between the upper member and the lower member for each +-- constraint, which is positive in case if the constraint is fulfilled +checkConstraints :: [Constraint] -- ^ list of inequalities + -> [Double] -- ^ point to be tested + -> [(Double, Bool)] -- ^ difference and status for each constraint +checkConstraints constraints point = + if nvars == length point + 1 + then + zip differences (map (>= 0) differences) + else + error "The length of the point does not match the number of variables." + where + halfspacesMatrix = normalizeConstraints constraints + nvars = length (head halfspacesMatrix) + checkRow pt row = - sum (zipWith (*) row (pt ++ [1])) + differences = map (checkRow point) halfspacesMatrix + +-- | Return a point fulfilling a list of constraints +interiorPoint :: [Constraint] -> [Double] +interiorPoint constraints = iPoint halfspacesMatrix + where + halfspacesMatrix = normalizeConstraints constraints
+ tests/Approx.hs view
@@ -0,0 +1,11 @@+module Approx + ( assertApproxZero ) + where +import Test.Tasty.HUnit ( Assertion, assertEqual ) + +-- round x to n digits +approx :: Int -> Double -> Double +approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n) + +assertApproxZero :: String -> Int -> Double -> Assertion +assertApproxZero prefix n x = assertEqual prefix (approx n x) 0.0
+ tests/Main.hs view
@@ -0,0 +1,40 @@+module Main where +import Approx ( assertApproxZero ) +import Geometry.VertexEnum ( (.<=), (.>=), Constraint, newVar + , vertexenum, interiorPoint, checkConstraints ) +import Test.Tasty ( defaultMain, testGroup ) +import Test.Tasty.HUnit ( testCase, assertEqual, assertBool ) + +cubeConstraints :: [Constraint] +cubeConstraints = + [ x .<= 1 + , x .>= (-1) + , y .<= 1 + , y .>= (-1) + , z .<= 1 + , z .>= (-1) ] + where + x = newVar 1 + y = newVar 2 + z = newVar 3 + +main :: IO () +main = defaultMain $ + testGroup "Tests" + [ + + testCase "[0, 0, 0] is in the cube" $ do + let check = checkConstraints cubeConstraints [0, 0, 0] + assertBool "" (all snd check) + + , testCase "cube vertices" $ do + vertices <- vertexenum cubeConstraints (Just [0, 0, 0]) + assertEqual "" (length vertices) 8 + + , testCase "interior point of the cube" $ do + let ipoint = interiorPoint cubeConstraints + norm = (ipoint !! 0) * (ipoint !! 0) + + (ipoint !! 1) * (ipoint !! 1) + (ipoint !! 2) * (ipoint !! 2) + assertApproxZero "" 6 norm + + ]
+ vertexenum.cabal view
@@ -0,0 +1,87 @@+cabal-version: 2.2++name: vertexenum+version: 0.1.0.0+synopsis: Vertex enumeration+description: Vertex enumeration of convex polytopes.+homepage: https://github.com/stla/vertexenum#readme+license: GPL-3.0-only+license-file: LICENSE+author: Stéphane Laurent+maintainer: laurent_step@outlook.fr+copyright: 2023 Stéphane Laurent+category: Math, Geometry+build-type: Simple+extra-source-files: README.md+ CHANGELOG.md++library+ hs-source-dirs: src+ exposed-modules: Geometry.VertexEnum+ other-modules: Geometry.VertexEnum.Constraint+ , Geometry.VertexEnum.Internal+ , Geometry.VertexEnum.LinearCombination+ , Geometry.VertexEnum.CVertexEnum+ , Geometry.VertexEnum.VertexEnum+ build-depends: base >= 4.7 && < 5+ , containers >= 0.6.2.1 && < 0.8+ , hmatrix-glpk >= 0.19.0.0 && < 0.20+ , vector-space >= 0.15 && < 0.17+ other-extensions: ForeignFunctionInterface+ , TypeFamilies+ , InstanceSigs+ default-language: Haskell2010+ include-dirs: C+ C-sources: C/libqhull_r.c+ , C/geom_r.c+ , C/geom2_r.c+ , C/global_r.c+ , C/io_r.c+ , C/mem_r.c+ , C/merge_r.c+ , C/poly_r.c+ , C/poly2_r.c+ , C/qset_r.c+ , C/random_r.c+ , C/usermem_r.c+ , C/userprintf_r.c+ , C/user_r.c+ , C/stat_r.c+ , C/halfspaces.c+ , C/utils.c+ install-includes: C/libqhull_r.h+ , C/geom_r.h+ , C/io_r.h+ , C/mem_r.h+ , C/merge_r.h+ , C/poly_r.h+ , C/qhull_ra.h+ , C/qset_r.h+ , C/random_r.h+ , C/user_r.h+ , C/stat_r.h+ , C/utils.h+ ghc-options: -Wall+ -Wcompat+ -Widentities+ -Wincomplete-record-updates+ -Wincomplete-uni-patterns+ -Wmissing-export-lists+ -Wmissing-home-modules+ -Wpartial-fields+ -Wredundant-constraints++test-suite unit-tests+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: tests/+ other-modules: Approx+ Build-Depends: base >= 4.7 && < 5+ , tasty >= 1.4 && < 1.5+ , tasty-hunit >= 0.10 && < 0.11+ , vertexenum+ Default-Language: Haskell2010++source-repository head+ type: git+ location: https://github.com/stla/vertexenum