diff --git a/3rdparty/qhull/geom.c b/3rdparty/qhull/geom.c index 71444f05a..592364784 100644 --- a/3rdparty/qhull/geom.c +++ b/3rdparty/qhull/geom.c @@ -6,9 +6,9 @@ see qh-geom.htm and geom.h - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/geom.c#2 $$Change: 1995 $ - $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/geom.c#4 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ infrequent code goes into geom2.c */ @@ -28,6 +28,20 @@ notes: dist > 0 if point is above facet (i.e., outside) does not error (for qh_sortfacets, qh_outerinner) + for nearly coplanar points, the returned values may be duplicates + for example pairs of nearly incident points, rbox 175 C1,2e-13 t1538759579 | qhull d T4 + 622 qh_distplane: e-014 # count of two or more duplicate values for unique calls + 258 qh_distplane: e-015 + 38 qh_distplane: e-016 + 40 qh_distplane: e-017 + 6 qh_distplane: e-018 + 5 qh_distplane: -e-018 + 33 qh_distplane: -e-017 + 3153 qh_distplane: -2.775557561562891e-017 # duplicated value for 3153 unique calls + 42 qh_distplane: -e-016 + 307 qh_distplane: -e-015 + 1271 qh_distplane: -e-014 + 13 qh_distplane: -e-013 see: qh_distnorm in geom2.c @@ -66,7 +80,7 @@ void qh_distplane(pointT *point, facetT *facet, realT *dist) { *dist += *coordp++ * *normal++; break; } - zinc_(Zdistplane); + zzinc_(Zdistplane); if (!qh RANDOMdist && qh IStracing < 4) return; if (qh RANDOMdist) { @@ -74,11 +88,13 @@ void qh_distplane(pointT *point, facetT *facet, realT *dist) { *dist += (2.0 * randr / qh_RANDOMmax - 1.0) * qh RANDOMfactor * qh MAXabs_coord; } +#ifndef qh_NOtrace if (qh IStracing >= 4) { qh_fprintf(qh ferr, 8001, "qh_distplane: "); qh_fprintf(qh ferr, 8002, qh_REAL_1, *dist); qh_fprintf(qh ferr, 8003, "from p%d to f%d\n", qh_pointid(point), facet->id); } +#endif return; } /* distplane */ @@ -123,7 +139,7 @@ void qh_distplane(pointT *point, facetT *facet, realT *dist) { 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 + this is best facet overall if dist >= -qh.MAXcoplanar or hull has at least a "spherical" curvature design: @@ -146,21 +162,23 @@ facetT *qh_findbest(pointT *point, facetT *startfacet, 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 */ + boolT testhorizon= True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */ zinc_(Zfindbest); - if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) { +#ifndef qh_NOtrace + if (qh IStracing >= 4 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) { if (qh TRACElevel > qh IStracing) qh IStracing= qh TRACElevel; - qh_fprintf(qh ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n", + qh_fprintf(qh ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g,", qh_pointid(point), startfacet->id, isnewfacets, bestoutside, qh MINoutside); - qh_fprintf(qh ferr, 8005, " testhorizon? %d noupper? %d", testhorizon, noupper); - qh_fprintf(qh ferr, 8006, " Last point added was p%d.", qh furthest_id); - qh_fprintf(qh ferr, 8007, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh max_outside); + qh_fprintf(qh ferr, 8005, " testhorizon? %d, noupper? %d,", testhorizon, noupper); + qh_fprintf(qh ferr, 8006, " Last qh_addpoint p%d,", qh furthest_id); + qh_fprintf(qh ferr, 8007, " Last merge #%d, max_outside %2.2g\n", zzval_(Ztotmerge), qh max_outside); } +#endif if (isoutside) *isoutside= True; - if (!startfacet->flipped) { /* test startfacet */ + if (!startfacet->flipped) { /* test startfacet before testing its neighbors */ *numpart= 1; qh_distplane(point, startfacet, dist); /* this code is duplicated below */ if (!bestoutside && *dist >= qh MINoutside @@ -209,11 +227,11 @@ facetT *qh_findbest(pointT *point, facetT *startfacet, facet= neighbor; /* non-NULL only if *dist>bestdist */ } /* end of while facet (directed search) */ if (isnewfacets) { - if (!bestfacet) { + if (!bestfacet) { /* startfacet is upperdelaunay (or flipped) w/o !flipped newfacet neighbors */ bestdist= -REALmax/2; - bestfacet= qh_findbestnew(point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew); + bestfacet= qh_findbestnew(point, qh newfacet_list, &bestdist, bestoutside, isoutside, &numpartnew); testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */ - }else if (!qh findbest_notsharp && bestdist < - qh DISTround) { + }else if (!qh findbest_notsharp && bestdist < -qh DISTround) { if (qh_sharpnewfacets()) { /* seldom used, qh_findbestnew will retest all facets */ zinc_(Zfindnewsharp); @@ -225,8 +243,8 @@ facetT *qh_findbest(pointT *point, facetT *startfacet, } } if (!bestfacet) - bestfacet= qh_findbestlower(lastfacet, point, &bestdist, numpart); - if (testhorizon) + bestfacet= qh_findbestlower(lastfacet, point, &bestdist, numpart); /* lastfacet is non-NULL because startfacet is non-NULL */ + if (testhorizon) /* qh_findbestnew not called */ bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew); *dist= bestdist; if (isoutside && bestdist < qh MINoutside) @@ -246,17 +264,24 @@ facetT *qh_findbest(pointT *point, facetT *startfacet, qh_findbesthorizon( qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart ) search coplanar and better horizon facets from startfacet/bestdist ischeckmax turns off statistics and minsearch update - all arguments must be initialized + all arguments must be initialized, including *bestdist and *numpart + qh.coplanarfacetset used to maintain current search set, reset whenever best facet is substantially better returns(ischeckmax): best facet + updates f.maxoutside for neighbors of searched facets (if qh_MAXoutside) returns(!ischeckmax): - best facet that is not upperdelaunay + best facet that is not upperdelaunay or newfacet (qh.first_newfacet) allows upperdelaunay that is clearly outside returns: bestdist is distance to bestfacet numpart -- updates number of distance tests notes: + called by qh_findbest if point is not outside a facet (directed search) + called by qh_findbestnew if point is not outside a new facet + called by qh_check_maxout for each point in hull + called by qh_check_bestdist for each point in hull (rarely used) + no early out -- use qh_findbest() or qh_findbestnew() Searches coplanar or better horizon facets @@ -283,10 +308,11 @@ facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, realT dist; facetT *neighbor, **neighborp, *facet; facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */ - int numpartinit= *numpart, coplanarfacetset_size; + int numpartinit= *numpart, coplanarfacetset_size, numcoplanar= 0, numfacet= 0; unsigned int visitid= ++qh visit_id; boolT newbest= False; /* for tracing */ realT minsearch, searchdist; /* skip facets that are too far from point */ + boolT is_5x_minsearch; if (!ischeckmax) { zinc_(Zfindhorizon); @@ -296,30 +322,30 @@ facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, startfacet->maxoutside= *bestdist; #endif } - searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */ + searchdist= qh_SEARCHdist; /* an expression, a 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; + startfacet->visitid= visitid; facet= startfacet; while (True) { - trace4((qh ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n", + numfacet++; + is_5x_minsearch= (ischeckmax && facet->nummerge > 10 && qh_setsize(facet->neighbors) > 100); /* QH11033 FIX: qh_findbesthorizon: many tests for facets with many merges and neighbors. Can hide coplanar facets, e.g., 'rbox 1000 s Z1 G1e-13' with 4400+ neighbors */ + trace4((qh ferr, 4002, "qh_findbesthorizon: test neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g is_5x? %d searchdist %2.2g\n", facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper, - minsearch, searchdist)); + minsearch, is_5x_minsearch, searchdist)); FOREACHneighbor_(facet) { if (neighbor->visitid == visitid) continue; neighbor->visitid= visitid; - if (!neighbor->flipped) { - qh_distplane(point, neighbor, &dist); + if (!neighbor->flipped) { /* neighbors of flipped facets always searched via nextfacet */ + qh_distplane(point, neighbor, &dist); /* duplicate qh_distpane for new facets, they may be coplanar */ (*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) { @@ -327,15 +353,22 @@ facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, coplanarfacetset_size= 0; } } + bestfacet= neighbor; + *bestdist= dist; + newbest= True; } + }else if (is_5x_minsearch) { + if (dist < 5 * minsearch) + continue; /* skip this neighbor, do not set nextfacet. dist is negative */ }else if (dist < minsearch) - continue; /* if ischeckmax, dist can't be positive */ + continue; /* skip this neighbor, do not set nextfacet. If ischeckmax, dist can't be positive */ #if qh_MAXoutside if (ischeckmax && dist > neighbor->maxoutside) neighbor->maxoutside= dist; #endif - } /* end of !flipped */ + } /* end of !flipped, need to search neighbor */ if (nextfacet) { + numcoplanar++; if (!coplanarfacetset_size++) { SETfirst_(qh coplanarfacetset)= nextfacet; SETtruncate_(qh coplanarfacetset, 1); @@ -354,15 +387,16 @@ facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, facet= SETfirstt_(qh coplanarfacetset, facetT); SETtruncate_(qh coplanarfacetset, 0); }else - facet= (facetT*)qh_setdellast(qh coplanarfacetset); - } /* while True, for each facet in qh.coplanarfacetset */ + facet= (facetT *)qh_setdellast(qh coplanarfacetset); + } /* while True, i.e., "for each facet in qh.coplanarfacetset" */ if (!ischeckmax) { zadd_(Zfindhorizontot, *numpart - numpartinit); zmax_(Zfindhorizonmax, *numpart - numpartinit); if (newbest) - zinc_(Zparthorizon); + zinc_(Znewbesthorizon); } - trace4((qh ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist)); + trace4((qh ferr, 4003, "qh_findbesthorizon: p%d, newbest? %d, bestfacet f%d, bestdist %2.2g, numfacet %d, coplanarfacets %d, numdist %d\n", + qh_pointid(point), newbest, getid_(bestfacet), *bestdist, numfacet, numcoplanar, *numpart - numpartinit)); return bestfacet; } /* findbesthorizon */ @@ -417,15 +451,17 @@ facetT *qh_findbestnew(pointT *point, facetT *startfacet, 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 */ + boolT testhorizon= True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */ - if (!startfacet) { - if (qh MERGING) - qh_fprintf(qh ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n"); - else + if (!startfacet || !startfacet->next) { + if (qh MERGING) { + qh_fprintf(qh ferr, 6001, "qhull topology error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n"); + qh_errexit(qh_ERRtopology, NULL, NULL); + }else { qh_fprintf(qh ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n", qh furthest_id); - qh_errexit(qh_ERRqhull, NULL, NULL); + qh_errexit(qh_ERRqhull, NULL, NULL); + } } zinc_(Zfindnew); if (qh BESToutside || bestoutside) @@ -437,14 +473,16 @@ facetT *qh_findbestnew(pointT *point, facetT *startfacet, if (isoutside) *isoutside= True; *numpart= 0; - if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) { +#ifndef qh_NOtrace + if (qh IStracing >= 4 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) { if (qh TRACElevel > qh IStracing) qh IStracing= qh TRACElevel; - qh_fprintf(qh ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n", + qh_fprintf(qh ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g,", qh_pointid(point), startfacet->id, isdistoutside, distoutside); - qh_fprintf(qh ferr, 8009, " Last point added p%d visitid %d.", qh furthest_id, visitid); - qh_fprintf(qh ferr, 8010, " Last merge was #%d.\n", zzval_(Ztotmerge)); + qh_fprintf(qh ferr, 8009, " Last qh_addpoint p%d, qh.visit_id %d, vertex_visit %d,", qh furthest_id, visitid, qh vertex_visit); + qh_fprintf(qh ferr, 8010, " Last merge #%d\n", zzval_(Ztotmerge)); } +#endif /* 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) { @@ -474,7 +512,8 @@ facetT *qh_findbestnew(pointT *point, facetT *startfacet, LABELreturn_bestnew: zadd_(Zfindnewtot, *numpart); zmax_(Zfindnewmax, *numpart); - trace4((qh ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist)); + trace4((qh ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g for p%d f%d bestoutside? %d \n", + getid_(bestfacet), *dist, qh_pointid(point), startfacet->id, bestoutside)); qh IStracing= oldtrace; return bestfacet; } /* findbestnew */ @@ -550,10 +589,10 @@ void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign, } } if (zerocol != -1) { - zzinc_(Zback0); *nearzero= True; trace4((qh ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i)); - qh_precision("zero diagonal on back substitution"); + zzinc_(Zback0); + qh_joggle_restart("zero diagonal on back substitution"); } } /* backnormal */ @@ -603,12 +642,14 @@ void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *near if (pivot_abs <= qh NEARzero[k]) { *nearzero= True; if (pivot_abs == 0.0) { /* remainder of column == 0 */ +#ifndef qh_NOtrace if (qh IStracing >= 4) { qh_fprintf(qh ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh DISTround); qh_printmatrix(qh ferr, "Matrix:", rows, numrow, numcol); } +#endif zzinc_(Zgauss0); - qh_precision("zero pivot for Gaussian elimination"); + qh_joggle_restart("zero pivot for Gaussian elimination"); goto LABELnextcol; } } @@ -652,7 +693,7 @@ realT qh_getangle(pointT *vect1, pointT *vect2) { angle += (2.0 * randr / qh_RANDOMmax - 1.0) * qh RANDOMfactor; } - trace4((qh ferr, 4006, "qh_getangle: %2.2g\n", angle)); + trace4((qh ferr, 4006, "qh_getangle: %4.4g\n", angle)); return(angle); } /* getangle */ @@ -716,7 +757,7 @@ pointT *qh_getcentrum(facetT *facet) { >-------------------------------- qh_getdistance( facet, neighbor, mindist, maxdist ) - returns the maxdist and mindist distance of any vertex from neighbor + returns the min and max distance to neighbor of non-neighbor vertices in facet returns: the max absolute value @@ -725,9 +766,9 @@ pointT *qh_getcentrum(facetT *facet) { for each vertex of facet that is not in neighbor test the distance from vertex to neighbor */ -realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) { +coordT qh_getdistance(facetT *facet, facetT *neighbor, coordT *mindist, coordT *maxdist) { vertexT *vertex, **vertexp; - realT dist, maxd, mind; + coordT dist, maxd, mind; FOREACHvertex_(facet->vertices) vertex->seen= False; @@ -766,7 +807,7 @@ realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *max qh_normalize2 */ void qh_normalize(coordT *normal, int dim, boolT toporient) { - qh_normalize2( normal, dim, toporient, NULL, NULL); + qh_normalize2(normal, dim, toporient, NULL, NULL); } /* normalize */ /*-normal) qh_memalloc_(normsize, freelistp, facet->normal, coordT); +#ifndef qh_NOtrace if (facet == qh tracefacet) { oldtrace= qh IStracing; qh IStracing= 5; @@ -950,6 +993,7 @@ void qh_setfacetplane(facetT *facet) { qh_fprintf(qh ferr, 8015, "\n\nCurrent summary is:\n"); qh_printsummary(qh ferr); } +#endif if (qh hull_dim <= 4) { i= 0; if (qh RANDOMdist) { @@ -991,7 +1035,7 @@ void qh_setfacetplane(facetT *facet) { facet->normal, &facet->offset, &nearzero); if (nearzero) { if (qh_orientoutside(facet)) { - trace0((qh ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh furthest_id)); + trace0((qh ferr, 2, "qh_setfacetplane: flipped orientation due to nearzero gauss and interior_point test. 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 @@ -1036,7 +1080,7 @@ void qh_setfacetplane(facetT *facet) { }else if (-dist > qh TRACEdist) istrace= True; if (istrace) { - qh_fprintf(qh ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n", + qh_fprintf(qh ferr, 3060, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n", qh_pointid(vertex->point), vertex->id, dist, facet->id, qh furthest_id); qh_errprint("DISTANT", facet, NULL, NULL, NULL); } @@ -1044,15 +1088,20 @@ void qh_setfacetplane(facetT *facet) { } qh RANDOMdist= qh old_randomdist; } - if (qh IStracing >= 3) { +#ifndef qh_NOtrace + if (qh IStracing >= 4) { qh_fprintf(qh ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ", facet->id, facet->offset); for (k=0; k < qh hull_dim; k++) qh_fprintf(qh ferr, 8018, "%2.2g ", facet->normal[k]); qh_fprintf(qh ferr, 8019, "\n"); } - if (facet == qh tracefacet) +#endif + qh_checkflipped(facet, NULL, qh_ALL); + if (facet == qh tracefacet) { qh IStracing= oldtrace; + qh_printfacet(qh ferr, facet); + } } /* setfacetplane */ @@ -1166,8 +1215,8 @@ void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0, } if (*nearzero) { zzinc_(Zminnorm); - trace0((qh ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id)); - zzinc_(Znearlysingular); + /* qh_joggle_restart not needed, will call qh_sethyperplane_gauss instead */ + trace0((qh ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d, use qh_sethyperplane_gauss instead.\n", qh furthest_id)); } } /* sethyperplane_det */ @@ -1211,6 +1260,7 @@ void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0, } if (*nearzero) { zzinc_(Znearlysingular); + /* qh_joggle_restart ignored for Znearlysingular, normal part of qh_sethyperplane_gauss */ trace0((qh ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh furthest_id)); qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2); }else { diff --git a/3rdparty/qhull/geom.h b/3rdparty/qhull/geom.h index 16ef48d2d..3f7d6dac1 100644 --- a/3rdparty/qhull/geom.h +++ b/3rdparty/qhull/geom.h @@ -6,9 +6,9 @@ see qh-geom.htm and geom.c - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/geom.h#1 $$Change: 1981 $ - $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/geom.h#2 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #ifndef qhDEFgeom @@ -111,7 +111,7 @@ void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *n realT qh_getangle(pointT *vect1, pointT *vect2); pointT *qh_getcenter(setT *vertices); pointT *qh_getcentrum(facetT *facet); -realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist); +coordT qh_getdistance(facetT *facet, facetT *neighbor, coordT *mindist, coordT *maxdist); void qh_normalize(coordT *normal, int dim, boolT toporient); void qh_normalize2(coordT *normal, int dim, boolT toporient, realT *minnorm, boolT *ismin); @@ -130,6 +130,7 @@ coordT *qh_copypoints(coordT *points, int numpoints, int dimension); void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]); realT qh_determinant(realT **rows, int dim, boolT *nearzero); realT qh_detjoggle(pointT *points, int numpoints, int dimension); +void qh_detmaxoutside(void); void qh_detroundoff(void); realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero); realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp); @@ -140,6 +141,8 @@ realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices, vertexT *notvertex, boolT toporient, coordT *normal, realT *offset); pointT *qh_facetcenter(setT *vertices); facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, facetT **facetlist); +vertexT *qh_furthestnewvertex(unsigned int unvisited, facetT *facet, realT *maxdistp /* qh.newvertex_list */); +vertexT *qh_furthestvertex(facetT *facetA, facetT *facetB, realT *maxdistp, realT *mindistp); void qh_getarea(facetT *facetlist); boolT qh_gram_schmidt(int dim, realT **rows); boolT qh_inthresholds(coordT *normal, realT *angle); @@ -168,6 +171,8 @@ void qh_scalepoints(pointT *points, int numpoints, int dim, boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp, coordT *normal, coordT *offset, coordT *feasible); coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible); +coordT qh_vertex_bestdist(setT *vertices); +coordT qh_vertex_bestdist2(setT *vertices, vertexT **vertexp, vertexT **vertexp2); pointT *qh_voronoi_center(int dim, setT *points); #endif /* qhDEFgeom */ diff --git a/3rdparty/qhull/geom2.c b/3rdparty/qhull/geom2.c index 82ec4936e..60eca899d 100644 --- a/3rdparty/qhull/geom2.c +++ b/3rdparty/qhull/geom2.c @@ -7,9 +7,9 @@ see qh-geom.htm and geom.h - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/geom2.c#6 $$Change: 2065 $ - $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/geom2.c#14 $$Change: 3037 $ + $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $ frequently used code goes into geom.c */ @@ -21,17 +21,19 @@ /*--------------------------------- - qh_copypoints( points, numpoints, dimension) + qh_copypoints( points, numpoints, dimension ) return qh_malloc'd copy of points + notes: qh_free the returned points to avoid a memory leak */ -coordT *qh_copypoints(coordT *points, int numpoints, int dimension) { +coordT *qh_copypoints(coordT *points, int numpoints, int dimension) +{ int size; coordT *newpoints; size= numpoints * dimension * (int)sizeof(coordT); - if (!(newpoints=(coordT*)qh_malloc((size_t)size))) { + if (!(newpoints= (coordT *)qh_malloc((size_t)size))) { qh_fprintf(qh ferr, 6004, "qhull error: insufficient memory to copy %d points\n", numpoints); qh_errexit(qh_ERRmem, NULL, NULL); @@ -91,16 +93,16 @@ realT qh_determinant(realT **rows, int dim, boolT *nearzero) { }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? */ + if (fabs_(det) < 10*qh NEARzero[1]) /* QH11031 FIX: 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 */ + if (fabs_(det) < 10*qh NEARzero[2]) /* QH11031 FIX: 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(rows, dim, dim, &sign, nearzero); /* if nearzero, diagonal still ok*/ + qh_gausselim(rows, dim, dim, &sign, nearzero); /* if nearzero, diagonal still ok */ det= 1.0; for (i=dim; i--; ) det *= (rows[i])[i]; @@ -134,35 +136,62 @@ realT qh_detjoggle(pointT *points, int numpoints, int dimension) { realT maxwidth= 0; int k; - for (k=0; k < dimension; k++) { - if (qh SCALElast && k == dimension-1) - abscoord= maxwidth; - else if (qh DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */ - abscoord= 2 * maxabs * maxabs; /* may be low by qh hull_dim/2 */ - else { - maxcoord= -REALmax; - mincoord= REALmax; - FORALLpoint_(points, numpoints) { - maximize_(maxcoord, point[k]); - minimize_(mincoord, point[k]); + if (qh SETroundoff) + distround= qh DISTround; /* 'En' */ + else{ + for (k=0; k < dimension; k++) { + if (qh SCALElast && k == dimension-1) + abscoord= maxwidth; + else if (qh DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */ + abscoord= 2 * maxabs * maxabs; /* may be low by qh hull_dim/2 */ + else { + maxcoord= -REALmax; + mincoord= REALmax; + FORALLpoint_(points, numpoints) { + maximize_(maxcoord, point[k]); + minimize_(mincoord, point[k]); + } + maximize_(maxwidth, maxcoord-mincoord); + abscoord= fmax_(maxcoord, -mincoord); } - maximize_(maxwidth, maxcoord-mincoord); - abscoord= fmax_(maxcoord, -mincoord); - } - sumabs += abscoord; - maximize_(maxabs, abscoord); - } /* for k */ - distround= qh_distround(qh hull_dim, maxabs, sumabs); + sumabs += abscoord; + maximize_(maxabs, abscoord); + } /* for k */ + distround= qh_distround(qh hull_dim, maxabs, sumabs); + } joggle= distround * qh_JOGGLEdefault; maximize_(joggle, REALepsilon * qh_JOGGLEdefault); trace2((qh ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth)); return joggle; } /* detjoggle */ +/*--------------------------------- + + qh_detmaxoutside(); + determine qh.MAXoutside target for qh_RATIO... tests of distance + updates option '_max-outside' + + notes: + called from qh_addpoint and qh_detroundoff + accounts for qh.ONEmerge, qh.DISTround, qh.MINoutside ('Wn'), qh.max_outside + see qh_maxout for qh.max_outside with qh.DISTround +*/ + +void qh_detmaxoutside(void) { + realT maxoutside; + + maxoutside= fmax_(qh max_outside, qh ONEmerge + qh DISTround); + maximize_(maxoutside, qh MINoutside); + qh MAXoutside= maxoutside; + trace3((qh ferr, 3056, "qh_detmaxoutside: MAXoutside %2.2g from qh.max_outside %2.2g, ONEmerge %2.2g, MINoutside %2.2g, DISTround %2.2g\n", + qh MAXoutside, qh max_outside, qh ONEmerge, qh MINoutside, qh DISTround)); +} /* detmaxoutside */ + /*--------------------------------- - qh_detroundoff() + qh_detroundoff( ) determine maximum roundoff errors from REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1 @@ -194,8 +223,6 @@ void qh_detroundoff(void) { qh_option("_max-width", NULL, &qh MAXwidth); if (!qh SETroundoff) { qh DISTround= qh_distround(qh hull_dim, qh MAXabs_coord, qh MAXsumcoord); - if (qh RANDOMdist) - qh DISTround += qh RANDOMfactor * qh MAXabs_coord; qh_option("Error-roundoff", NULL, &qh DISTround); } qh MINdenom= qh MINdenom_1 * qh MAXabs_coord; @@ -203,8 +230,10 @@ void qh_detroundoff(void) { qh MINdenom_2= qh MINdenom_1_2 * qh MAXabs_coord; /* for inner product */ qh ANGLEround= 1.01 * qh hull_dim * REALepsilon; - if (qh RANDOMdist) + if (qh RANDOMdist) { qh ANGLEround += qh RANDOMfactor; + trace4((qh ferr, 4096, "qh_detroundoff: increase qh.ANGLEround by option 'R%2.2g'\n", qh RANDOMfactor)); + } if (qh premerge_cos < REALmax/2) { qh premerge_cos -= qh ANGLEround; if (qh RANDOMdist) @@ -247,7 +276,7 @@ void qh_detroundoff(void) { if (qh KEEPnearinside) qh_option("_near-inside", NULL, &qh NEARinside); if (qh JOGGLEmax < qh DISTround) { - qh_fprintf(qh ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n", + qh_fprintf(qh ferr, 6006, "qhull option error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n", qh JOGGLEmax, qh DISTround); qh_errexit(qh_ERRinput, NULL, NULL); } @@ -264,7 +293,7 @@ void qh_detroundoff(void) { } if (qh MAXcoplanar > REALmax/2) { qh MAXcoplanar= qh MINvisible; - qh_option("U-coplanar-distance", NULL, &qh MAXcoplanar); + qh_option("U-max-coplanar", NULL, &qh MAXcoplanar); } if (!qh APPROXhull) { /* user may specify qh MINoutside */ qh MINoutside= 2 * qh MINvisible; @@ -283,6 +312,7 @@ void qh_detroundoff(void) { qh max_vertex= qh DISTround; qh min_vertex= -qh DISTround; /* numeric constants reported in printsummary */ + qh_detmaxoutside(); } /* detroundoff */ /*--------------------------------- - qh_distround(dimension, maxabs, maxsumabs ) + qh_distround( dimension, maxabs, maxsumabs ) compute maximum round-off error for a distance computation to a normalized hyperplane maxabs is the maximum absolute value of a coordinate maxsumabs is the maximum possible sum of absolute coordinate values + if qh.RANDOMdist ('Qr'), adjusts qh_distround returns: - max dist round for REALepsilon + max dist round for qh.REALepsilon and qh.RANDOMdist notes: calculate roundoff error according to Golub & van Loan, 1983, Lemma 3.2-1, "Rounding Errors" @@ -374,14 +406,19 @@ realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) { or use maxsumabs since one vector is < 1 */ realT qh_distround(int dimension, realT maxabs, realT maxsumabs) { - realT maxdistsum, maxround; + realT maxdistsum, maxround, delta; maxdistsum= sqrt((realT)dimension) * maxabs; minimize_( maxdistsum, maxsumabs); maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs); /* adds maxabs for offset */ - trace4((qh ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n", - maxround, maxabs, maxsumabs, maxdistsum)); + if (qh RANDOMdist) { + delta= qh RANDOMfactor * maxabs; + maxround += delta; + trace4((qh ferr, 4092, "qh_distround: increase roundoff by random delta %2.2g for option 'R%2.2g'\n", delta, qh RANDOMfactor)); + } + trace4((qh ferr, 4008, "qh_distround: %2.2g, maxabs %2.2g, maxsumabs %2.2g, maxdistsum %2.2g\n", + maxround, maxabs, maxsumabs, maxdistsum)); return maxround; } /* distround */ @@ -566,7 +603,7 @@ realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices, for (k=dim; k--; ) *(gmcoord++)= *normalp++; } - zinc_(Zdetsimplex); + zinc_(Zdetfacetarea); area= qh_determinant(rows, dim, &nearzero); if (toporient) area= -area; @@ -676,6 +713,90 @@ facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, return NULL; } /* findgooddist */ +/*--------------------------------- + + qh_furthestnewvertex( unvisited, facet, &maxdist ) + return furthest unvisited, new vertex to a facet + + return: + NULL if no vertex is above facet + maxdist to facet + updates v.visitid + + notes: + Ignores vertices in facetB + Does not change qh.vertex_visit. Use in conjunction with qh_furthestvertex +*/ +vertexT *qh_furthestnewvertex(unsigned int unvisited, facetT *facet, realT *maxdistp /* qh.newvertex_list */) { + vertexT *maxvertex= NULL, *vertex; + coordT dist, maxdist= 0.0; + + FORALLvertex_(qh newvertex_list) { + if (vertex->newfacet && vertex->visitid <= unvisited) { + vertex->visitid= qh vertex_visit; + qh_distplane(vertex->point, facet, &dist); + if (dist > maxdist) { + maxdist= dist; + maxvertex= vertex; + } + } + } + trace4((qh ferr, 4085, "qh_furthestnewvertex: v%d dist %2.2g is furthest new vertex for f%d\n", + getid_(maxvertex), maxdist, facet->id)); + *maxdistp= maxdist; + return maxvertex; +} /* furthestnewvertex */ + +/*--------------------------------- + + qh_furthestvertex( facetA, facetB, &maxdist, &mindist ) + return furthest vertex in facetA from facetB, or NULL if none + + return: + maxdist and mindist to facetB or 0.0 if none + updates qh.vertex_visit + + notes: + Ignores vertices in facetB +*/ +vertexT *qh_furthestvertex(facetT *facetA, facetT *facetB, realT *maxdistp, realT *mindistp) { + vertexT *maxvertex= NULL, *vertex, **vertexp; + coordT dist, maxdist= -REALmax, mindist= REALmax; + + qh vertex_visit++; + FOREACHvertex_(facetB->vertices) + vertex->visitid= qh vertex_visit; + FOREACHvertex_(facetA->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + zzinc_(Zvertextests); + qh_distplane(vertex->point, facetB, &dist); + if (!maxvertex) { + maxdist= dist; + mindist= dist; + maxvertex= vertex; + }else if (dist > maxdist) { + maxdist= dist; + maxvertex= vertex; + }else if (dist < mindist) + mindist= dist; + } + } + if (!maxvertex) { + trace3((qh ferr, 3067, "qh_furthestvertex: all vertices of f%d are in f%d. Returning 0.0 for max and mindist\n", + facetA->id, facetB->id)); + maxdist= mindist= 0.0; + }else { + trace4((qh ferr, 4084, "qh_furthestvertex: v%d dist %2.2g is furthest (mindist %2.2g) of f%d above f%d\n", + maxvertex->id, maxdist, mindist, facetA->id, facetB->id)); + } + *maxdistp= maxdist; + *mindistp= mindist; + return maxvertex; +} /* furthestvertex */ + /*--------------------------------- @@ -709,7 +830,7 @@ void qh_getarea(facetT *facetlist) { if (qh REPORTfreq) qh_fprintf(qh ferr, 8020, "computing area of each facet and volume of the convex hull\n"); else - trace1((qh ferr, 1001, "qh_getarea: computing volume and area for each facet\n")); + trace1((qh ferr, 1001, "qh_getarea: computing area for each facet and its volume to qh.interior_point (dist*area/dim)\n")); qh totarea= qh totvol= 0.0; FORALLfacet_(facetlist) { if (!facet->normal) @@ -766,7 +887,7 @@ boolT qh_gram_schmidt(int dim, realT **row) { for (i=0; i < dim; i++) { rowi= row[i]; - for (norm= 0.0, k= dim; k--; rowi++) + for (norm=0.0, k=dim; k--; rowi++) norm += *rowi * *rowi; norm= sqrt(norm); wmin_(Wmindenom, norm); @@ -776,7 +897,7 @@ boolT qh_gram_schmidt(int dim, realT **row) { *(--rowi) /= norm; for (j=i+1; j < dim; j++) { rowj= row[j]; - for (norm= 0.0, k=dim; k--; ) + for (norm=0.0, k=dim; k--; ) norm += *rowi++ * *rowj++; for (k=dim; k--; ) *(--rowj) -= *(--rowi) * norm; @@ -842,7 +963,7 @@ boolT qh_inthresholds(coordT *normal, realT *angle) { /*--------------------------------- - qh_joggleinput() + qh_joggleinput( ) randomly joggle input to Qhull by qh.JOGGLEmax initial input is qh.first_point/qh.num_points of qh.hull_dim repeated calls use qh.input_points/qh.num_points @@ -879,8 +1000,8 @@ void qh_joggleinput(void) { 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))) { + size= qh num_points * qh hull_dim * (int)sizeof(coordT); + if (!(qh first_point= (coordT *)qh_malloc((size_t)size))) { qh_fprintf(qh ferr, 6009, "qhull error: insufficient memory to joggle %d points\n", qh num_points); qh_errexit(qh_ERRmem, NULL, NULL); @@ -903,14 +1024,14 @@ void qh_joggleinput(void) { qh_option("QJoggle", NULL, &qh JOGGLEmax); } if (qh build_cnt > 1 && qh JOGGLEmax > fmax_(qh MAXwidth/4, 0.1)) { - qh_fprintf(qh ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n", + qh_fprintf(qh ferr, 6010, "qhull input error (qh_joggleinput): the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n", qh JOGGLEmax); - qh_errexit(qh_ERRqhull, NULL, NULL); + qh_errexit(qh_ERRinput, NULL, NULL); } /* for some reason, using qh ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */ seed= qh_RANDOMint; qh_option("_joggle-seed", &seed, NULL); - trace0((qh ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n", + trace0((qh ferr, 6, "qh_joggleinput: joggle input by %4.4g with seed %d\n", qh JOGGLEmax, seed)); inputp= qh input_points; coordp= qh first_point; @@ -994,12 +1115,12 @@ setT *qh_maxmin(pointT *points, int numpoints, int dimension) { && REALmax > 0.0 && -REALmax < 0.0) ; /* all ok */ else { - qh_fprintf(qh ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\ -REALepsilon %g REALmin %g REALmax %g -REALmax %g\n", - REALepsilon, REALmin, REALmax, -REALmax); + qh_fprintf(qh ferr, 6011, "qhull error: one or more floating point constants in user.h are inconsistent. REALmin %g, -REALmax %g, 0.0, REALepsilon %g, REALmax %g\n", + REALmin, -REALmax, REALepsilon, REALmax); qh_errexit(qh_ERRinput, NULL, NULL); } set= qh_settemp(2*dimension); + trace1((qh ferr, 8082, "qh_maxmin: dim min max width nearzero min-point max-point\n")); for (k=0; k < dimension; k++) { if (points == qh GOODpointp) minimum= maximum= points + dimension; @@ -1018,7 +1139,7 @@ REALepsilon %g REALmin %g REALmax %g -REALmax %g\n", qh MAXlastcoord= maximum[k]; } if (qh SCALElast && k == dimension-1) - maxcoord= qh MAXwidth; + maxcoord= qh MAXabs_coord; else { maxcoord= fmax_(maximum[k], -minimum[k]); if (qh GOODpointp) { @@ -1030,23 +1151,28 @@ REALepsilon %g REALmin %g REALmax %g -REALmax %g\n", } maximize_(qh MAXabs_coord, maxcoord); qh MAXsumcoord += maxcoord; - qh_setappend(&set, maximum); qh_setappend(&set, minimum); + qh_setappend(&set, maximum); /* 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; + trace1((qh ferr, 8106, " %3d % 14.8e % 14.8e % 14.8e %4.4e p%-9d p%-d\n", + k, minimum[k], maximum[k], maximum[k]-minimum[k], qh NEARzero[k], qh_pointid(minimum), qh_pointid(maximum))); + if (qh SCALElast && k == dimension-1) + trace1((qh ferr, 8107, " last coordinate scaled to (%4.4g, %4.4g), width %4.4e for option 'Qbb'\n", + qh MAXabs_coord - qh MAXwidth, qh MAXabs_coord, qh MAXwidth)); } - if (qh IStracing >=1) - qh_printpoints(qh ferr, "qh_maxmin: found the max and min points(by dim):", set); + if (qh IStracing >= 1) + qh_printpoints(qh ferr, "qh_maxmin: found the max and min points (by dim):", set); return(set); } /* maxmin */ /*--------------------------------- - qh_maxouter() + qh_maxouter( ) return maximum distance from facet to outer plane normally this is qh.max_outside+qh.DISTround does not include qh.JOGGLEmax @@ -1056,6 +1182,7 @@ REALepsilon %g REALmin %g REALmax %g -REALmax %g\n", notes: need to add another qh.DISTround if testing actual point with computation + see qh_detmaxoutside for a qh_RATIO... target for joggle: qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex) @@ -1068,7 +1195,7 @@ realT qh_maxouter(void) { dist= fmax_(qh max_outside, qh DISTround); dist += qh DISTround; - trace4((qh ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh max_outside)); + trace4((qh ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %4.4g, qh.max_outside is %4.4g\n", dist, qh max_outside)); return dist; } /* maxouter */ @@ -1077,32 +1204,45 @@ realT qh_maxouter(void) { qh_maxsimplex( dim, maxpoints, points, numpoints, simplex ) determines maximum simplex for a set of points - starts from points already in simplex + maxpoints is the subset of points with a min or max coordinate + may start with 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 + called by qh_initialvertices, qh_detvnorm, and qh_voronoi_center + requires qh.MAXwidth to estimate determinate for each vertex + assumes at least needed 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) + create a simplex of dim+1 vertices as follows + add point from maxpoints that maximizes the determinate of the point and the simplex vertices + if last point and maxdet/prevdet < qh_RATIOmaxsimplex (3.0e-2) + flag maybe_falsenarrow + if no maxpoint or maxnearzero or maybe_falsenarrow + search all points for maximum determinate + early exit if maybe_falsenarrow and !maxnearzero and maxdet > prevdet */ void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) { pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL; - boolT nearzero, maxnearzero= False; - int k, sizinit; - realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax; + boolT nearzero, maxnearzero= False, maybe_falsenarrow; + int i, sizinit; + realT maxdet= -1.0, prevdet= -1.0, det, mincoord= REALmax, maxcoord= -REALmax, mindet, ratio, targetdet; + if (qh MAXwidth <= 0.0) { + qh_fprintf(qh ferr, 6421, "qhull internal error (qh_maxsimplex): qh.MAXwidth required for qh_maxsimplex. Used to estimate determinate\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } sizinit= qh_setsize(*simplex); - if (sizinit < 2) { + if (sizinit >= 2) { + maxdet= pow(qh MAXwidth, sizinit - 1); + }else { if (qh_setsize(maxpoints) >= 2) { FOREACHpoint_(maxpoints) { if (maxcoord < point[0]) { @@ -1128,28 +1268,31 @@ void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT } } } + maxdet= maxcoord - mincoord; qh_setunique(simplex, minx); if (qh_setsize(*simplex) < 2) qh_setunique(simplex, maxx); sizinit= qh_setsize(*simplex); if (sizinit < 2) { - qh_precision("input has same x coordinate"); + qh_joggle_restart("input has same x coordinate"); if (zzval_(Zsetplane) > qh hull_dim+1) { - qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n", - qh_setsize(maxpoints)+numpoints); + qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center): %d points with the same x coordinate %4.4g\n", + qh_setsize(maxpoints)+numpoints, mincoord); qh_errexit(qh_ERRprec, NULL, NULL); }else { - qh_fprintf(qh ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh hull_dim); + qh_fprintf(qh ferr, 6013, "qhull input error: input is less than %d-dimensional since all points have the same x coordinate %4.4g\n", + qh hull_dim, mincoord); qh_errexit(qh_ERRinput, NULL, NULL); } } } - for (k=sizinit; k < dim+1; k++) { + for (i=sizinit; i < dim+1; i++) { + prevdet= maxdet; maxpoint= NULL; - maxdet= -REALmax; + maxdet= -1.0; FOREACHpoint_(maxpoints) { - if (!qh_setin(*simplex, point)) { - det= qh_detsimplex(point, *simplex, k, &nearzero); + if (!qh_setin(*simplex, point) && point != maxpoint) { + det= qh_detsimplex(point, *simplex, i, &nearzero); /* retests maxpoints if duplicate or multiple iterations */ if ((det= fabs_(det)) > maxdet) { maxdet= det; maxpoint= point; @@ -1157,23 +1300,41 @@ void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT } } } - if (!maxpoint || maxnearzero) { + maybe_falsenarrow= False; + ratio= 1.0; + targetdet= prevdet * qh MAXwidth; + mindet= 10 * qh_RATIOmaxsimplex * targetdet; + if (maxdet > 0.0) { + ratio= maxdet / targetdet; + if (ratio < qh_RATIOmaxsimplex) + maybe_falsenarrow= True; + } + if (!maxpoint || maxnearzero || maybe_falsenarrow) { zinc_(Zsearchpoints); if (!maxpoint) { - trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1)); + trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex, better than mindet %4.4g, targetdet %4.4g\n", + i+1, mindet, targetdet)); + }else if (qh ALLpoints) { + trace0((qh ferr, 30, "qh_maxsimplex: searching all points ('Qs') for %d-th initial vertex, better than p%d det %4.4g, targetdet %4.4g, ratio %4.4g\n", + i+1, qh_pointid(maxpoint), maxdet, targetdet, ratio)); + }else if (maybe_falsenarrow) { + trace0((qh ferr, 17, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %4.4g and mindet %4.4g, ratio %4.4g\n", + i+1, qh_pointid(maxpoint), maxdet, mindet, ratio)); }else { - trace0((qh ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n", - k+1, qh_pointid(maxpoint), maxdet)); + trace0((qh ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g and mindet %4.4g, targetdet %4.4g\n", + i+1, qh_pointid(maxpoint), maxdet, mindet, targetdet)); } FORALLpoint_(points, numpoints) { if (point == qh GOODpointp) continue; - if (!qh_setin(*simplex, point)) { - det= qh_detsimplex(point, *simplex, k, &nearzero); + if (!qh_setin(maxpoints, point) && !qh_setin(*simplex, point)) { + det= qh_detsimplex(point, *simplex, i, &nearzero); if ((det= fabs_(det)) > maxdet) { maxdet= det; maxpoint= point; maxnearzero= nearzero; + if (!maxnearzero && !qh ALLpoints && maxdet > mindet) + break; } } } @@ -1183,9 +1344,9 @@ void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT qh_errexit(qh_ERRqhull, NULL, NULL); } qh_setappend(simplex, maxpoint); - trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n", - qh_pointid(maxpoint), k+1, maxdet)); - } /* k */ + trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%4.4g, targetdet=%4.4g, mindet=%4.4g\n", + qh_pointid(maxpoint), i+1, maxdet, prevdet * qh MAXwidth, mindet)); + } /* i */ } /* maxsimplex */ /*--------------------------------- - qh_projectinput() + qh_projectinput( ) project input points using qh.lower_bound/upper_bound and qh DELAUNAY if qh.lower_bound[k]=qh.upper_bound[k]= 0, removes dimension k @@ -1425,14 +1586,14 @@ void qh_projectinput(void) { int k,i; int newdim= qh input_dim, newnum= qh num_points; signed char *project; - int projectsize= (qh input_dim+1)*sizeof(*project); + int projectsize= (qh input_dim + 1) * (int)sizeof(*project); pointT *newpoints, *coord, *infinity; realT paraboloid, maxboloid= 0; - project= (signed char*)qh_memalloc(projectsize); - memset((char*)project, 0, (size_t)projectsize); + project= (signed char *)qh_memalloc(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) { + if (qh lower_bound[k] == 0.0 && qh upper_bound[k] == 0.0) { project[k]= -1; newdim--; } @@ -1448,7 +1609,7 @@ void qh_projectinput(void) { qh_fprintf(qh ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh hull_dim); qh_errexit(qh_ERRqhull, NULL, NULL); } - if (!(newpoints= qh temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){ + if (!(newpoints= qh temp_malloc= (coordT *)qh_malloc((size_t)(newnum * newdim) * sizeof(coordT)))) { qh_memfree(project, projectsize); qh_fprintf(qh ferr, 6016, "qhull error: insufficient memory to project %d points\n", qh num_points); @@ -1499,7 +1660,7 @@ void qh_projectinput(void) { qh num_points++; trace0((qh ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n")); }else if (qh DELAUNAY) /* !qh ATinfinity */ - qh_setdelaunay( qh hull_dim, qh num_points, qh first_point); + qh_setdelaunay(qh hull_dim, qh num_points, qh first_point); } /* projectinput */ @@ -1613,12 +1774,12 @@ void qh_rotatepoints(realT *points, int numpoints, int dim, realT **row) { if (qh IStracing >= 1) qh_printmatrix(qh ferr, "qh_rotatepoints: rotate points by", row, dim, dim); - for (point= points, j= numpoints; j--; point += 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--; ) + for (sum=0.0, k=dim; k--; ) sum += *rowi++ * *coord++; *(newval++)= sum; } @@ -1631,7 +1792,7 @@ void qh_rotatepoints(realT *points, int numpoints, int dim, realT **row) { /*--------------------------------- - qh_scaleinput() + qh_scaleinput( ) scale input points using qh low_bound/high_bound input points given by qh first_point, num_points, hull_dim if qh POINTSmalloc, overwrites input points, else mallocs a new array @@ -1657,16 +1818,20 @@ void qh_scaleinput(void) { >-------------------------------- qh_scalelast( points, numpoints, dim, low, high, newhigh ) - scale last coordinate to [0,m] for Delaunay triangulations + scale last coordinate to [0.0, newhigh], for Delaunay triangulation input points given by points, numpoints, dim returns: - changes scale of last coordinate from [low, high] to [0, newhigh] + changes scale of last coordinate from [low, high] to [0.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 + to reduce precision issues, qh_scalelast makes the last coordinate similar to other coordinates + the last coordinate for Delaunay triangulation is the sum of squares of input coordinates + note that the range [0.0, newwidth] is wrong for narrow distributions with large positive coordinates (e.g., [995933.64, 995963.48]) + + when called by qh_setdelaunay, low/high may not match the data passed to qh_setdelaunay design: compute scale and shift factors @@ -1675,26 +1840,28 @@ void qh_scaleinput(void) { void qh_scalelast(coordT *points, int numpoints, int dim, coordT low, coordT high, coordT newhigh) { realT scale, shift; - coordT *coord; + coordT *coord, newlow; int i; boolT nearzero= False; - trace4((qh ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n", - low, high, newhigh)); + newlow= 0.0; + trace4((qh ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [%2.2g, %2.2g]\n", + low, high, newlow, newhigh)); qh last_low= low; qh last_high= high; qh last_newhigh= newhigh; - scale= qh_divzero(newhigh, high - low, + scale= qh_divzero(newhigh - newlow, high - low, qh MINdenom_1, &nearzero); if (nearzero) { if (qh DELAUNAY) - qh_fprintf(qh ferr, 6019, "qhull input error: can not scale last coordinate. Input is cocircular\n or cospherical. Use option 'Qz' to add a point at infinity.\n"); + qh_fprintf(qh ferr, 6019, "qhull input error (qh_scalelast): can not scale last coordinate to [%4.4g, %4.4g]. Input is cocircular or cospherical. Use option 'Qz' to add a point at infinity.\n", + newlow, newhigh); else - qh_fprintf(qh ferr, 6020, "qhull input error: can not scale last coordinate. New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n", - newhigh, low, high, high-low); + qh_fprintf(qh ferr, 6020, "qhull input error (qh_scalelast): can not scale last coordinate to [%4.4g, %4.4g]. New bounds are too wide for compared to existing bounds [%4.4g, %4.4g] (width %4.4g)\n", + newlow, newhigh, low, high, high-low); qh_errexit(qh_ERRinput, NULL, NULL); } - shift= - low * newhigh / (high-low); + shift= newlow - low * scale; coord= points + dim - 1; for (i=numpoints; i--; coord += dim) *coord= *coord * scale + shift; @@ -1817,7 +1984,7 @@ void qh_setdelaunay(int dim, int count, pointT *points) { coord= *coordp++; paraboloid += coord*coord; } - *coordp++ = paraboloid; + *coordp++= paraboloid; } if (qh last_low < REALmax/2) qh_scalelast(points, count, dim, qh last_low, qh last_high, qh last_newhigh); @@ -1866,6 +2033,7 @@ boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp, } } *nextp= coordp; +#ifndef qh_NOtrace if (qh IStracing >= 4) { qh_fprintf(qh ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset); for (k=dim, coordp=coords; k--; ) { @@ -1874,6 +2042,7 @@ boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp, } qh_fprintf(qh ferr, 8023, "\n"); } +#endif return True; LABELerroroutside: feasiblep= feasible; @@ -1909,8 +2078,8 @@ boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp, 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. + 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 @@ -1922,7 +2091,7 @@ coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feas trace0((qh ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n")); newdim= dim - 1; - if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){ + if (!(newpoints= (coordT *)qh_malloc((size_t)(count * newdim) * sizeof(coordT)))){ qh_fprintf(qh ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n", count); qh_errexit(qh_ERRmem, NULL, NULL); @@ -1945,7 +2114,7 @@ coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feas /*--------------------------------- - qh_sharpnewfacets() + qh_sharpnewfacets( ) returns: true if could be an acute angle (facets in different quadrants) @@ -1960,10 +2129,10 @@ coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feas */ boolT qh_sharpnewfacets(void) { facetT *facet; - boolT issharp = False; + boolT issharp= False; int *quadrant, k; - quadrant= (int*)qh_memalloc(qh hull_dim * sizeof(int)); + quadrant= (int *)qh_memalloc(qh hull_dim * (int)sizeof(int)); FORALLfacet_(qh newfacet_list) { if (facet == qh newfacet_list) { for (k=qh hull_dim; k--; ) @@ -1979,11 +2148,49 @@ boolT qh_sharpnewfacets(void) { if (issharp) break; } - qh_memfree( quadrant, qh hull_dim * sizeof(int)); + qh_memfree(quadrant, qh hull_dim * (int)sizeof(int)); trace3((qh ferr, 3001, "qh_sharpnewfacets: %d\n", issharp)); return issharp; } /* sharpnewfacets */ +/*--------------------------------- + + qh_vertex_bestdist( vertices ) + qh_vertex_bestdist2( vertices, vertexp, vertexp2 ) + return nearest distance between vertices + optionally returns vertex and vertex2 + + notes: + called by qh_partitioncoplanar, qh_mergefacet, qh_check_maxout, qh_checkpoint +*/ +coordT qh_vertex_bestdist(setT *vertices) { + vertexT *vertex, *vertex2; + + return qh_vertex_bestdist2(vertices, &vertex, &vertex2); +} /* vertex_bestdist */ + +coordT qh_vertex_bestdist2(setT *vertices, vertexT **vertexp/*= NULL*/, vertexT **vertexp2/*= NULL*/) { + vertexT *vertex, *vertexA, *bestvertex= NULL, *bestvertex2= NULL; + coordT dist, bestdist= REALmax; + int k, vertex_i, vertex_n; + + FOREACHvertex_i_(vertices) { + for (k= vertex_i+1; k < vertex_n; k++) { + vertexA= SETelemt_(vertices, k, vertexT); + dist= qh_pointdist(vertex->point, vertexA->point, -qh hull_dim); + if (dist < bestdist) { + bestdist= dist; + bestvertex= vertex; + bestvertex2= vertexA; + } + } + } + *vertexp= bestvertex; + *vertexp2= bestvertex2; + return sqrt(bestdist); +} /* vertex_bestdist */ + /*--------------------------------- @@ -2011,7 +2218,7 @@ boolT qh_sharpnewfacets(void) { */ pointT *qh_voronoi_center(int dim, setT *points) { pointT *point, **pointp, *point0; - pointT *center= (pointT*)qh_memalloc(qh center_size); + pointT *center= (pointT *)qh_memalloc(qh center_size); setT *simplex; int i, j, k, size= qh_setsize(points); coordT *gmcoord; @@ -2022,7 +2229,7 @@ pointT *qh_voronoi_center(int dim, setT *points) { simplex= points; else if (size < dim+1) { qh_memfree(center, qh center_size); - qh_fprintf(qh ferr, 6025, "qhull internal error (qh_voronoi_center):\n need at least %d points to construct a Voronoi center\n", + qh_fprintf(qh ferr, 6025, "qhull internal error (qh_voronoi_center): need at least %d points to construct a Voronoi center\n", dim+1); qh_errexit(qh_ERRqhull, NULL, NULL); simplex= points; /* never executed -- avoids warning */ @@ -2075,7 +2282,7 @@ pointT *qh_voronoi_center(int dim, setT *points) { } #ifndef qh_NOtrace if (qh IStracing >= 3) { - qh_fprintf(qh ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor); + qh_fprintf(qh ferr, 3061, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor); qh_printmatrix(qh ferr, "center:", ¢er, 1, dim); if (qh IStracing >= 5) { qh_printpoints(qh ferr, "points", simplex); diff --git a/3rdparty/qhull/global.c b/3rdparty/qhull/global.c index 0328fea7b..9a810019a 100644 --- a/3rdparty/qhull/global.c +++ b/3rdparty/qhull/global.c @@ -11,9 +11,9 @@ see qhull_a.h for internal functions - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/global.c#17 $$Change: 2066 $ - $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/global.c#16 $$Change: 3037 $ + $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $ */ #include "qhull_a.h" @@ -29,7 +29,7 @@ qhT qh_qh; /* all global variables. #endif /*---------------------------------- + >--------------------------------- qh_version version string by year and date @@ -41,14 +41,14 @@ qhT qh_qh; /* all global variables. 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 + check that CMakeLists.txt @version is the same as qh_version2 change year: Copying.txt check download size recompile user_eg.c, rbox.c, libqhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c, testqset.c */ -const char qh_version[]= "2015.2 2016/01/18"; -const char qh_version2[]= "qhull 7.2.0 (2015.2 2016/01/18)"; +const char qh_version[]= "2020.2 2020/08/31"; +const char qh_version2[]= "qhull 8.0.2 (2020.2 2020/08/31)"; /*--------------------------------- @@ -86,18 +86,18 @@ void qh_appendprint(qh_PRINT format) { void qh_checkflags(char *command, char *hiddenflags) { char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */ char key, opt, prevopt; - char chkkey[]= " "; - char chkopt[]= " "; - char chkopt2[]= " "; + char chkkey[]= " "; /* check one character options ('s') */ + char chkopt[]= " "; /* check two character options ('Ta') */ + char chkopt2[]= " "; /* check three character options ('Q12') */ boolT waserr= False; if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') { - qh_fprintf(qh ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags); - qh_errexit(qh_ERRinput, NULL, NULL); + qh_fprintf(qh ferr, 6026, "qhull internal error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"\n", hiddenflags); + qh_errexit(qh_ERRqhull, NULL, NULL); } if (strpbrk(hiddenflags, ",\n\r\t")) { - qh_fprintf(qh ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags); - qh_errexit(qh_ERRinput, NULL, NULL); + qh_fprintf(qh ferr, 6027, "qhull internal error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"\n", hiddenflags); + qh_errexit(qh_ERRqhull, NULL, NULL); } while (*s && !isspace(*s)) /* skip program name */ s++; @@ -108,8 +108,8 @@ void qh_checkflags(char *command, char *hiddenflags) { s++; if (!*s) break; - key = *s++; - chkerr = NULL; + key= *s++; + chkerr= NULL; if (key == 'T' && (*s == 'I' || *s == 'O')) { /* TI or TO 'file name' */ s= qh_skipfilename(++s); continue; @@ -136,9 +136,16 @@ void qh_checkflags(char *command, char *hiddenflags) { } }else if (key == 'Q' && isdigit(opt) && prevopt != 'b' && (prevopt == ' ' || islower(prevopt))) { - chkopt[2]= opt; - if (strstr(hiddenflags, chkopt)) - chkerr= chkopt; + if (isdigit(*s)) { /* Q12 */ + chkopt2[2]= opt; + chkopt2[3]= *s++; + if (strstr(hiddenflags, chkopt2)) + chkerr= chkopt2; + }else { + chkopt[2]= opt; + if (strstr(hiddenflags, chkopt)) + chkerr= chkopt; + } }else { qh_strtod(s-1, &t); if (s < t) @@ -150,7 +157,7 @@ void qh_checkflags(char *command, char *hiddenflags) { if (chkerr) { *chkerr= '\''; chkerr[strlen(chkerr)-1]= '\''; - qh_fprintf(qh ferr, 6029, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr); + qh_fprintf(qh ferr, 6029, "qhull option error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr); waserr= True; } } @@ -159,9 +166,9 @@ void qh_checkflags(char *command, char *hiddenflags) { } /* checkflags */ /*--------------------------------- + >-------------------------------- - qh_clear_outputflags() + qh_clear_outputflags( ) Clear output flags for QhullPoints */ void qh_clear_outputflags(void) { @@ -277,20 +284,21 @@ void qh_freebuffers(void) { trace5((qh ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n")); /* allocated by qh_initqhull_buffers */ - qh_memfree(qh NEARzero, qh hull_dim * sizeof(realT)); - qh_memfree(qh lower_threshold, (qh input_dim+1) * sizeof(realT)); - qh_memfree(qh upper_threshold, (qh input_dim+1) * sizeof(realT)); - qh_memfree(qh lower_bound, (qh input_dim+1) * sizeof(realT)); - qh_memfree(qh upper_bound, (qh input_dim+1) * sizeof(realT)); - qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * sizeof(coordT)); - qh_memfree(qh gm_row, (qh hull_dim+1) * sizeof(coordT *)); + qh_setfree(&qh other_points); + qh_setfree(&qh del_vertices); + qh_setfree(&qh coplanarfacetset); + qh_memfree(qh NEARzero, qh hull_dim * (int)sizeof(realT)); + qh_memfree(qh lower_threshold, (qh input_dim+1) * (int)sizeof(realT)); + qh_memfree(qh upper_threshold, (qh input_dim+1) * (int)sizeof(realT)); + qh_memfree(qh lower_bound, (qh input_dim+1) * (int)sizeof(realT)); + qh_memfree(qh upper_bound, (qh input_dim+1) * (int)sizeof(realT)); + qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * (int)sizeof(coordT)); + qh_memfree(qh gm_row, (qh hull_dim+1) * (int)sizeof(coordT *)); qh NEARzero= qh lower_threshold= qh upper_threshold= NULL; qh lower_bound= qh upper_bound= NULL; qh gm_matrix= NULL; qh gm_row= NULL; - qh_setfree(&qh other_points); - qh_setfree(&qh del_vertices); - qh_setfree(&qh coplanarfacetset); + if (qh line) /* allocated by qh_readinput, freed if no error */ qh_free(qh line); if (qh half_space) @@ -327,22 +335,37 @@ void qh_freebuffers(void) { 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 merge sets free temporary sets */ void qh_freebuild(boolT allmem) { - facetT *facet; - vertexT *vertex; - ridgeT *ridge, **ridgep; + facetT *facet, *previousfacet= NULL; + vertexT *vertex, *previousvertex= NULL; + ridgeT *ridge, **ridgep, *previousridge= NULL; mergeT *merge, **mergep; + int newsize; + boolT freeall; + /* free qhT global sets first, includes references from qh_buildhull */ + trace5((qh ferr, 5004, "qh_freebuild: free global sets\n")); + FOREACHmerge_(qh facet_mergeset) /* usually empty */ + qh_memfree(merge, (int)sizeof(mergeT)); + FOREACHmerge_(qh degen_mergeset) /* usually empty */ + qh_memfree(merge, (int)sizeof(mergeT)); + FOREACHmerge_(qh vertex_mergeset) /* usually empty */ + qh_memfree(merge, (int)sizeof(mergeT)); + qh facet_mergeset= NULL; /* temp set freed by qh_settempfree_all */ + qh degen_mergeset= NULL; /* temp set freed by qh_settempfree_all */ + qh vertex_mergeset= NULL; /* temp set freed by qh_settempfree_all */ + qh_setfree(&(qh hash_table)); + trace5((qh ferr, 5003, "qh_freebuild: free temporary sets (qh_settempfree_all)\n")); + qh_settempfree_all(); trace1((qh ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n")); if (qh del_vertices) qh_settruncate(qh del_vertices, 0); @@ -351,9 +374,12 @@ void qh_freebuild(boolT allmem) { if (vertex->next) qh_delvertex(vertex); else { - qh_memfree(vertex, (int)sizeof(vertexT)); + qh_memfree(vertex, (int)sizeof(vertexT)); /* sentinel */ qh newvertex_list= qh vertex_list= NULL; + break; } + previousvertex= vertex; /* in case of memory fault */ + QHULL_UNUSED(previousvertex) } }else if (qh VERTEXneighbors) { FORALLvertices @@ -366,22 +392,18 @@ void qh_freebuild(boolT allmem) { FOREACHridge_(facet->ridges) ridge->seen= False; } - FORALLfacets { - if (facet->visible) { + while ((facet= qh facet_list)) { + if (!facet->newfacet || !qh NEWtentative || qh_setsize(facet->ridges) > 1) { /* skip tentative horizon ridges */ + trace4((qh ferr, 4095, "qh_freebuild: delete the previously-seen ridges of f%d\n", facet->id)); FOREACHridge_(facet->ridges) { - if (!otherfacet_(ridge, facet)->visible) - ridge->seen= True; /* an unattached ridge */ + if (ridge->seen) + qh_delridge(ridge); + else + ridge->seen= True; + previousridge= ridge; /* in case of memory fault */ + QHULL_UNUSED(previousridge) } } - } - while ((facet= qh facet_list)) { - FOREACHridge_(facet->ridges) { - if (ridge->seen) { - qh_setfree(&(ridge->vertices)); - qh_memfree(ridge, (int)sizeof(ridgeT)); - }else - ridge->seen= True; - } qh_setfree(&(facet->outsideset)); qh_setfree(&(facet->coplanarset)); qh_setfree(&(facet->neighbors)); @@ -393,26 +415,26 @@ void qh_freebuild(boolT allmem) { qh_memfree(facet, (int)sizeof(facetT)); qh visible_list= qh newfacet_list= qh facet_list= NULL; } + previousfacet= facet; /* in case of memory fault */ + QHULL_UNUSED(previousfacet) } }else { + freeall= True; + if (qh_setlarger_quick(qh hull_dim + 1, &newsize)) + freeall= False; FORALLfacets { qh_setfreelong(&(facet->outsideset)); qh_setfreelong(&(facet->coplanarset)); - if (!facet->simplicial) { + if (!facet->simplicial || freeall) { qh_setfreelong(&(facet->neighbors)); qh_setfreelong(&(facet->ridges)); qh_setfreelong(&(facet->vertices)); } } } - qh_setfree(&(qh hash_table)); + /* qh internal constants */ qh_memfree(qh interior_point, qh normal_size); qh interior_point= NULL; - FOREACHmerge_(qh facet_mergeset) /* usually empty */ - qh_memfree(merge, (int)sizeof(mergeT)); - qh facet_mergeset= NULL; /* temp set */ - qh degen_mergeset= NULL; /* temp set */ - qh_settempfree_all(); } /* freebuild */ /*-NOerrexit + calls qh_exit() if qh.NOerrexit returns: sets qh.qhull_command to command if needed notes: - ignores first word (e.g., "qhull d") + ignores first word (e.g., 'qhull' in "qhull d") use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces see: @@ -614,14 +636,19 @@ void qh_init_qhull_command(int argc, char *argv[]) { */ void qh_initflags(char *command) { int k, i, lastproject; - char *s= command, *t, *prev_s, *start, key; + char *s= command, *t, *prev_s, *start, key, *lastwarning= NULL; boolT isgeom= False, wasproject; realT r; - if(qh NOerrexit){/* without this comment, segfault in gcc 4.4.0 mingw32 */ - qh_fprintf(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(qh NOerrexit){ + qh_fprintf(qh ferr, 6245, "qhull internal error (qh_initflags): qh.NOerrexit was not cleared before calling qh_initflags(). It should be cleared after setjmp(). Exit qhull.\n"); + qh_exit(qh_ERRqhull); } +#ifdef qh_RANDOMdist + qh RANDOMfactor= qh_RANDOMdist; + qh_option("Random-qh_RANDOMdist", NULL, &qh RANDOMfactor); + qh RANDOMdist= True; +#endif 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'; @@ -630,6 +657,8 @@ void qh_initflags(char *command) { while (*s && !isspace(*s)) /* skip program name */ s++; } + if (qh_QHpointer) + qh_option("qh_QHpointer", NULL, NULL); while (*s) { while (*s && isspace(*s)) s++; @@ -677,9 +706,10 @@ void qh_initflags(char *command) { qh DELAUNAY= True; break; case 'A': - if (!isdigit(*s) && *s != '.' && *s != '-') - qh_fprintf(qh ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'. Ignored.\n"); - else { + if (!isdigit(*s) && *s != '.' && *s != '-') { + qh_fprintf(qh ferr, 7002, "qhull input warning: no maximum cosine angle given for option 'An'. A1.0 is coplanar\n"); + lastwarning= s-1; + }else { if (*s == '-') { qh premerge_cos= -qh_strtod(s, &s); qh_option("Angle-premerge-", NULL, &qh premerge_cos); @@ -693,9 +723,10 @@ void qh_initflags(char *command) { } break; case 'C': - if (!isdigit(*s) && *s != '.' && *s != '-') - qh_fprintf(qh ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'. Ignored.\n"); - else { + if (!isdigit(*s) && *s != '.' && *s != '-') { + qh_fprintf(qh ferr, 7003, "qhull input warning: no centrum radius given for option 'Cn'\n"); + lastwarning= s-1; + }else { if (*s == '-') { qh premerge_centrum= -qh_strtod(s, &s); qh_option("Centrum-premerge-", NULL, &qh premerge_centrum); @@ -709,11 +740,13 @@ void qh_initflags(char *command) { } break; case 'E': - if (*s == '-') - qh_fprintf(qh ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n"); - else if (!isdigit(*s)) - qh_fprintf(qh ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'. Ignored.\n"); - else { + if (*s == '-') { + qh_fprintf(qh ferr, 6363, "qhull option error: expecting a positive number for maximum roundoff 'En'. Got '%s'\n", s-1); + qh_errexit(qh_ERRinput, NULL, NULL); + }else if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7005, "qhull option warning: no maximum roundoff given for option 'En'\n"); + lastwarning= s-1; + }else { qh DISTround= qh_strtod(s, &s); qh_option("Distance-roundoff", NULL, &qh DISTround); qh SETroundoff= True; @@ -727,14 +760,16 @@ void qh_initflags(char *command) { if (*t && !isspace(*t)) { if (*t == ',') t++; - else - qh_fprintf(qh ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n"); + else { + qh_fprintf(qh ferr, 6364, "qhull option error: expecting 'Hn,n,n,...' for feasible point of halfspace intersection. Got '%s'\n", start-1); + qh_errexit(qh_ERRinput, NULL, NULL); + } } s= t; qh_strtod(s, &t); } if (start < t) { - if (!(qh feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) { + if (!(qh feasible_string= (char *)calloc((size_t)(t-start+1), (size_t)1))) { qh_fprintf(qh ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n"); qh_errexit(qh_ERRmem, NULL, NULL); } @@ -745,36 +780,41 @@ void qh_initflags(char *command) { qh_option("Halfspace", NULL, NULL); break; case 'R': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'. Ignored\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7007, "qhull option warning: missing random perturbation for option 'Rn'\n"); + lastwarning= s-1; + }else { qh RANDOMfactor= qh_strtod(s, &s); - qh_option("Random_perturb", NULL, &qh RANDOMfactor); + qh_option("Random-perturb", NULL, &qh RANDOMfactor); qh RANDOMdist= True; } break; case 'V': - if (!isdigit(*s) && *s != '-') - qh_fprintf(qh ferr, 7008, "qhull warning: missing visible distance for option 'Vn'. Ignored\n"); - else { + if (!isdigit(*s) && *s != '-') { + qh_fprintf(qh ferr, 7008, "qhull option warning: missing visible distance for option 'Vn'\n"); + lastwarning= s-1; + }else { qh MINvisible= qh_strtod(s, &s); qh_option("Visible", NULL, &qh MINvisible); } break; case 'U': - if (!isdigit(*s) && *s != '-') - qh_fprintf(qh ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'. Ignored\n"); - else { + if (!isdigit(*s) && *s != '-') { + qh_fprintf(qh ferr, 7009, "qhull option warning: missing coplanar distance for option 'Un'\n"); + lastwarning= s-1; + }else { qh MAXcoplanar= qh_strtod(s, &s); qh_option("U-coplanar", NULL, &qh MAXcoplanar); } break; case 'W': - if (*s == '-') - qh_fprintf(qh ferr, 7010, "qhull warning: negative outside width for option 'Wn'. Ignored.\n"); - else if (!isdigit(*s)) - qh_fprintf(qh ferr, 7011, "qhull warning: missing outside width for option 'Wn'. Ignored\n"); - else { + if (*s == '-') { + qh_fprintf(qh ferr, 6365, "qhull option error: expecting a positive number for outside width 'Wn'. Got '%s'\n", s-1); + qh_errexit(qh_ERRinput, NULL, NULL); + }else if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7011, "qhull option warning: missing outside width for option 'Wn'\n"); + lastwarning= s-1; + }else { qh MINoutside= qh_strtod(s, &s); qh_option("W-outside", NULL, &qh MINoutside); qh APPROXhull= True; @@ -887,7 +927,8 @@ void qh_initflags(char *command) { break; default: s--; - qh_fprintf(qh ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]); + qh_fprintf(qh ferr, 7012, "qhull option warning: unknown 'F' output option 'F%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; while (*++s && !isspace(*s)); break; } @@ -939,19 +980,23 @@ void qh_initflags(char *command) { qh PRINTspheres= True; break; case 'D': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n"); - else { - if (qh DROPdim >= 0) - qh_fprintf(qh ferr, 7013, "qhull warning: can only drop one dimension. Previous 'GD%d' ignored\n", + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7004, "qhull option warning: missing dimension for option 'GDn'\n"); + lastwarning= s-2; + }else { + if (qh DROPdim >= 0) { + qh_fprintf(qh ferr, 7013, "qhull option warning: can only drop one dimension. Previous 'GD%d' ignored\n", qh DROPdim); + lastwarning= s-2; + } qh DROPdim= qh_strtol(s, &s); qh_option("GDrop-dim", &qh DROPdim, NULL); } break; default: s--; - qh_fprintf(qh ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]); + qh_fprintf(qh ferr, 7014, "qhull option warning: unknown 'G' geomview option 'G%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; while (*++s && !isspace(*s)); break; } @@ -990,34 +1035,38 @@ void qh_initflags(char *command) { qh PRINTprecision= False; break; case 'A': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7006, "qhull option warning: missing facet count for keep area option 'PAn'\n"); + lastwarning= s-2; + }else { qh KEEParea= qh_strtol(s, &s); qh_option("PArea-keep", &qh KEEParea, NULL); qh GETarea= True; } break; case 'F': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7010, "qhull option warning: missing facet area for option 'PFn'\n"); + lastwarning= s-2; + }else { qh KEEPminArea= qh_strtod(s, &s); qh_option("PFacet-area-keep", NULL, &qh KEEPminArea); qh GETarea= True; } break; case 'M': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7090, "qhull option warning: missing merge count for option 'PMn'\n"); + lastwarning= s-2; + }else { qh KEEPmerge= qh_strtol(s, &s); qh_option("PMerge-keep", &qh KEEPmerge, NULL); } break; default: s--; - qh_fprintf(qh ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]); + qh_fprintf(qh ferr, 7015, "qhull option warning: unknown 'P' print option 'P%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; while (*++s && !isspace(*s)); break; } @@ -1027,6 +1076,10 @@ void qh_initflags(char *command) { lastproject= -1; while (*s && !isspace(*s)) { switch (*s++) { + case 'a': + qh_option("Qallow-short", NULL, NULL); + qh ALLOWshort= True; + break; case 'b': case 'B': /* handled by qh_initthresholds */ key= s[-1]; if (key == 'b' && *s == 'B') { @@ -1114,9 +1167,10 @@ void qh_initflags(char *command) { break; case 'T': qh_option("QTestPoints", NULL, NULL); - if (!isdigit(*s)) - qh_fprintf(qh ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7091, "qhull option warning: missing number of test points for option 'QTn'\n"); + lastwarning= s-2; + }else { qh TESTpoints= qh_strtol(s, &s); qh_option("QTestPoints", &qh TESTpoints, NULL); } @@ -1143,8 +1197,8 @@ void qh_initflags(char *command) { break; case '1': if (!isdigit(*s)) { - qh_option("Q1-no-angle-sort", NULL, NULL); - qh ANGLEmerge= False; + qh_option("Q1-angle-merge", NULL, NULL); + qh ANGLEmerge= True; break; } switch (*s++) { @@ -1158,12 +1212,26 @@ void qh_initflags(char *command) { qh TRIangulate= True; break; case '2': - qh_option("Q12-no-wide-dup", NULL, NULL); - qh NOwide= True; + qh_option("Q12-allow-wide", NULL, NULL); + qh ALLOWwide= True; + break; + case '4': +#ifndef qh_NOmerge + qh_option("Q14-merge-pinched-vertices", NULL, NULL); + qh MERGEpinched= True; +#else + /* ignore 'Q14' for q_benchmark testing of difficult cases for Qhull */ + qh_fprintf(qh ferr, 7099, "qhull option warning: option 'Q14-merge-pinched' disabled due to qh_NOmerge\n"); +#endif + break; + case '7': + qh_option("Q15-check-duplicates", NULL, NULL); + qh CHECKduplicates= True; break; default: s--; - qh_fprintf(qh ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]); + qh_fprintf(qh ferr, 7016, "qhull option warning: unknown 'Q' qhull option 'Q1%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; while (*++s && !isspace(*s)); break; } @@ -1172,14 +1240,16 @@ void qh_initflags(char *command) { qh_option("Q2-no-merge-independent", NULL, NULL); qh MERGEindependent= False; goto LABELcheckdigit; - break; /* no warnings */ + break; /* no gcc warnings */ case '3': qh_option("Q3-no-merge-vertices", NULL, NULL); qh MERGEvertices= False; LABELcheckdigit: - if (isdigit(*s)) - qh_fprintf(qh ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit. '%c' skipped.\n", - *s++); + if (isdigit(*s)) { + qh_fprintf(qh ferr, 7017, "qhull option warning: can not follow '1', '2', or '3' with a digit. 'Q%c%c' skipped\n", *(s-1), *s); + lastwarning= s-2; + s++; + } break; case '4': qh_option("Q4-avoid-old-into-new", NULL, NULL); @@ -1207,11 +1277,13 @@ void qh_initflags(char *command) { break; case 'G': i= qh_strtol(s, &t); - if (qh GOODpoint) - qh_fprintf(qh ferr, 7018, "qhull warning: good point already defined for option 'QGn'. Ignored\n"); - else if (s == t) - qh_fprintf(qh ferr, 7019, "qhull warning: missing good point id for option 'QGn'. Ignored\n"); - else if (i < 0 || *s == '-') { + if (qh GOODpoint) { + qh_fprintf(qh ferr, 7018, "qhull option warning: good point already defined for option 'QGn'. Ignored\n"); + lastwarning= s-2; + }else if (s == t) { + qh_fprintf(qh ferr, 7019, "qhull option warning: missing good point id for option 'QGn'. Ignored\n"); + lastwarning= s-2; + }else if (i < 0 || *s == '-') { qh GOODpoint= i-1; qh_option("QGood-if-dont-see-point", &i, NULL); }else { @@ -1229,9 +1301,10 @@ void qh_initflags(char *command) { } break; case 'R': - if (!isdigit(*s) && *s != '-') - qh_fprintf(qh ferr, 7020, "qhull warning: missing random seed for option 'QRn'. Ignored\n"); - else { + if (!isdigit(*s) && *s != '-') { + qh_fprintf(qh ferr, 7020, "qhull option warning: missing random seed for option 'QRn'\n"); + lastwarning= s-2; + }else { qh ROTATErandom= i= qh_strtol(s, &s); if (i > 0) qh_option("QRotate-id", &i, NULL ); @@ -1241,11 +1314,13 @@ void qh_initflags(char *command) { break; case 'V': i= qh_strtol(s, &t); - if (qh GOODvertex) - qh_fprintf(qh ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n"); - else if (s == t) - qh_fprintf(qh ferr, 7022, "qhull warning: no good point id given for option 'QVn'. Ignored\n"); - else if (i < 0) { + if (qh GOODvertex) { + qh_fprintf(qh ferr, 7021, "qhull option warning: good vertex already defined for option 'QVn'. Ignored\n"); + lastwarning= s-2; + }else if (s == t) { + qh_fprintf(qh ferr, 7022, "qhull option warning: no good point id given for option 'QVn'. Ignored\n"); + lastwarning= s-2; + }else if (i < 0) { qh GOODvertex= i - 1; qh_option("QV-good-facets-not-point", &i, NULL); }else { @@ -1254,9 +1329,14 @@ void qh_initflags(char *command) { } s= t; break; + case 'w': + qh_option("Qwarn-allow", NULL, NULL); + qh ALLOWwarning= True; + break; default: s--; - qh_fprintf(qh ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]); + qh_fprintf(qh ferr, 7023, "qhull option warning: unknown 'Q' qhull option 'Q%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; while (*++s && !isspace(*s)); break; } @@ -1275,6 +1355,10 @@ void qh_initflags(char *command) { qh_option("Tcheck-frequently", NULL, NULL); qh CHECKfrequently= True; break; + case 'f': + qh_option("Tflush", NULL, NULL); + qh FLUSHprint= True; + break; case 's': qh_option("Tstatistics", NULL, NULL); qh PRINTstatistics= True; @@ -1288,9 +1372,10 @@ void qh_initflags(char *command) { /* The C++ interface captures the output in qh_fprint_qhull() */ qh_option("Tz-stdout", NULL, NULL); qh USEstdout= True; - }else if (!qh fout) - qh_fprintf(qh ferr, 7024, "qhull warning: output file undefined(stdout). Option 'Tz' ignored.\n"); - else { + }else if (!qh fout) { + qh_fprintf(qh ferr, 7024, "qhull option warning: output file undefined(stdout). Option 'Tz' ignored.\n"); + lastwarning= s-2; + }else { qh_option("Tz-stdout", NULL, NULL); qh USEstdout= True; qh ferr= qh fout; @@ -1298,26 +1383,26 @@ void qh_initflags(char *command) { } break; case 'C': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'. Ignored\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7025, "qhull option warning: missing point id for cone for trace option 'TCn'\n"); + lastwarning= s-2; + }else { i= qh_strtol(s, &s); qh_option("TCone-stop", &i, NULL); qh STOPcone= i + 1; } break; case 'F': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'. Ignored\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7026, "qhull option warning: missing frequency count for trace option 'TFn'\n"); + lastwarning= s-2; + }else { qh REPORTfreq= qh_strtol(s, &s); qh_option("TFacet-log", &qh REPORTfreq, NULL); qh REPORTfreq2= qh REPORTfreq/2; /* for tracemerging() */ } break; case 'I': - if (!isspace(*s)) - qh_fprintf(qh ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s); while (isspace(*s)) s++; t= qh_skipfilename(s); @@ -1327,7 +1412,7 @@ void qh_initflags(char *command) { qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ s= t; if (!freopen(filename, "r", stdin)) { - qh_fprintf(qh ferr, 6041, "qhull error: could not open file \"%s\".", filename); + qh_fprintf(qh ferr, 6041, "qhull option error: cannot open 'TI' file \"%s\"\n", filename); qh_errexit(qh_ERRinput, NULL, NULL); }else { qh_option("TInput-file", NULL, NULL); @@ -1336,56 +1421,81 @@ void qh_initflags(char *command) { } break; case 'O': - if (!isspace(*s)) - qh_fprintf(qh ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s); - while (isspace(*s)) - s++; - t= qh_skipfilename(s); - { - char filename[qh_FILENAMElen]; - - qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ - s= t; - if (!qh fout) { - qh_fprintf(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 ferr, 6044, "qhull error: could not open file \"%s\".", filename); - qh_errexit(qh_ERRinput, NULL, NULL); - }else { - qh_option("TOutput-file", NULL, NULL); + while (isspace(*s)) + s++; + t= qh_skipfilename(s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + if (!qh fout) { + qh_fprintf(qh ferr, 7092, "qhull option warning: qh.fout was not set by caller of qh_initflags. Cannot use option 'TO' to redirect output. Ignoring option 'TO'\n"); + lastwarning= s-2; + }else if (!freopen(filename, "w", qh fout)) { + qh_fprintf(qh ferr, 6044, "qhull option error: cannot open file \"%s\" for writing as option 'TO'. It is already in use or read-only\n", filename); + qh_errexit(qh_ERRinput, NULL, NULL); + }else { + qh_option("TOutput-file", NULL, NULL); qh_option(filename, NULL, NULL); } + s= t; } break; + case 'A': + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7093, "qhull option warning: missing count of added points for trace option 'TAn'\n"); + lastwarning= s-2; + }else { + i= qh_strtol(s, &t); + qh STOPadd= i + 1; + qh_option("TA-stop-add", &i, NULL); + } + s= t; + break; case 'P': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 7029, "qhull warning: missing point id for trace option 'TPn'. Ignored\n"); - else { + if (*s == '-') { + if (s[1] == '1' && !isdigit(s[2])) { + s += 2; + qh TRACEpoint= qh_IDunknown; /* qh_buildhull done */ + qh_option("Trace-point", &qh TRACEpoint, NULL); + }else { + qh_fprintf(qh ferr, 7100, "qhull option warning: negative point id for trace option 'TPn'. Expecting 'TP-1' for tracing after qh_buildhull and qh_postmerge\n"); + lastwarning= s-2; + while (isdigit(*(++s))) + ; /* skip digits */ + } + }else if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7029, "qhull option warning: missing point id or -1 for trace option 'TPn'\n"); + lastwarning= s-2; + }else { qh TRACEpoint= qh_strtol(s, &s); qh_option("Trace-point", &qh TRACEpoint, NULL); } break; case 'M': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'. Ignored\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7030, "qhull option warning: missing merge id for trace option 'TMn'\n"); + lastwarning= s-2; + }else { qh TRACEmerge= qh_strtol(s, &s); qh_option("Trace-merge", &qh TRACEmerge, NULL); } break; case 'R': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'. Ignored\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7031, "qhull option warning: missing rerun count for trace option 'TRn'\n"); + lastwarning= s-2; + }else { qh RERUN= qh_strtol(s, &s); qh_option("TRerun", &qh RERUN, NULL); } break; case 'V': i= qh_strtol(s, &t); - if (s == t) - qh_fprintf(qh ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'. Ignored\n"); - else if (i < 0) { + if (s == t) { + qh_fprintf(qh ferr, 7032, "qhull option warning: missing furthest point id for trace option 'TVn'\n"); + lastwarning= s-2; + }else if (i < 0) { qh STOPpoint= i - 1; qh_option("TV-stop-before-point", &i, NULL); }else { @@ -1395,37 +1505,51 @@ void qh_initflags(char *command) { s= t; break; case 'W': - if (!isdigit(*s)) - qh_fprintf(qh ferr, 7033, "qhull warning: missing max width for trace option 'TWn'. Ignored\n"); - else { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7033, "qhull option warning: missing max width for trace option 'TWn'\n"); + lastwarning= s-2; + }else { qh TRACEdist= (realT) qh_strtod(s, &s); qh_option("TWide-trace", NULL, &qh TRACEdist); } break; default: s--; - qh_fprintf(qh ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]); + qh_fprintf(qh ferr, 7034, "qhull option warning: unknown 'T' trace option 'T%c', skip to next space\n", (int)s[0]); + lastwarning= s-2; while (*++s && !isspace(*s)); break; } } break; default: - qh_fprintf(qh ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1], - (int)s[-1]); + qh_fprintf(qh ferr, 7094, "qhull option warning: unknown option '%c'(%x)\n", + (int)s[-1], (int)s[-1]); + lastwarning= s-2; break; } if (s-1 == prev_s && *s && !isspace(*s)) { - qh_fprintf(qh ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n", - (int)*prev_s, (int)*prev_s); + qh_fprintf(qh ferr, 7036, "qhull option warning: missing space after option '%c'(%x), reserved for sub-options, ignoring '%c' options to next space\n", + (int)*prev_s, (int)*prev_s, (int)*prev_s); + lastwarning= s-1; while (*s && !isspace(*s)) s++; } } - if (qh STOPcone && qh JOGGLEmax < REALmax/2) - qh_fprintf(qh ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n"); - if (isgeom && !qh FORCEoutput && qh PRINTout[1]) - qh_fprintf(qh ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n"); + if (qh STOPcone && qh JOGGLEmax < REALmax/2) { + qh_fprintf(qh ferr, 7078, "qhull option warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n"); + lastwarning= command; + } + if (isgeom && !qh FORCEoutput && qh PRINTout[1]) { + qh_fprintf(qh ferr, 7037, "qhull option warning: additional output formats ('Fc',etc.) are not compatible with Geomview ('G'). Use option 'Po' to override\n"); + lastwarning= command; + } + if (lastwarning && !qh ALLOWwarning) { + qh_fprintf(qh ferr, 6035, "qhull option error: see previous warnings, use 'Qw' to override: '%s' (last offset %d)\n", + command, (int)(lastwarning-command)); + qh_errexit(qh_ERRinput, NULL, NULL); + } + trace4((qh ferr, 4093, "qh_initflags: option flags initialized\n")); /* set derived values in qh_initqhull_globals */ } /* initflags */ @@ -1433,7 +1557,7 @@ void qh_initflags(char *command) { /*--------------------------------- - qh_initqhull_buffers() + qh_initqhull_buffers( ) initialize global memory buffers notes: @@ -1442,25 +1566,25 @@ void qh_initflags(char *command) { void qh_initqhull_buffers(void) { int k; - qh TEMPsize= (qhmem.LASTsize - sizeof(setT))/SETelemsize; + qh TEMPsize= (qhmem.LASTsize - (int)sizeof(setT))/SETelemsize; if (qh TEMPsize <= 0 || qh TEMPsize > qhmem.LASTsize) qh TEMPsize= 8; /* e.g., if qh_NOmem */ qh other_points= qh_setnew(qh TEMPsize); qh del_vertices= qh_setnew(qh TEMPsize); qh coplanarfacetset= qh_setnew(qh TEMPsize); - qh NEARzero= (realT *)qh_memalloc(qh hull_dim * sizeof(realT)); - qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); - qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); - qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); - qh upper_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + qh NEARzero= (realT *)qh_memalloc(qh hull_dim * (int)sizeof(realT)); + qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * (int)sizeof(realT)); + qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * (int)sizeof(realT)); + qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * (int)sizeof(realT)); + qh upper_bound= (realT *)qh_memalloc((qh input_dim+1) * (int)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 hull_dim+1) * qh hull_dim * sizeof(coordT)); - qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * sizeof(coordT *)); + qh gm_matrix= (coordT *)qh_memalloc((qh hull_dim+1) * qh hull_dim * (int)sizeof(coordT)); + qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * (int)sizeof(coordT *)); } /* initqhull_buffers */ /*- qh_POINTSmax) { + qh_fprintf(qh ferr, 6412, "qhull input error (qh_initqhull_globals): expecting between 1 and %d points. Got %d %d-d points\n", + qh_POINTSmax, numpoints, dim); + qh_errexit(qh_ERRinput, NULL, NULL); + /* same error message in qh_readpoints */ + } qh POINTSmalloc= ismalloc; qh first_point= points; qh num_points= numpoints; @@ -1513,23 +1642,25 @@ void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc qh_option("_pre-merge", NULL, NULL); }else { qh MERGEexact= True; - qh_option("Qxact_merge", NULL, NULL); + qh_option("Qxact-merge", NULL, NULL); } }else if (qh MERGEexact) qh MERGING= True; + if (qh NOpremerge && (qh MERGEexact || qh PREmerge)) + qh_fprintf(qh ferr, 7095, "qhull option warning: 'Q0-no-premerge' ignored due to exact merge ('Qx') or pre-merge ('C-n' or 'A-n')\n"); if (!qh NOpremerge && qh JOGGLEmax > REALmax/2) { #ifdef qh_NOmerge qh JOGGLEmax= 0.0; #endif } - if (qh TRIangulate && qh JOGGLEmax < REALmax/2 && qh PRINTprecision) - qh_fprintf(qh ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output. Triangulated output('Qt') does nothing.\n"); + if (qh TRIangulate && qh JOGGLEmax < REALmax/2 && !qh PREmerge && !qh POSTmerge && qh PRINTprecision) + qh_fprintf(qh ferr, 7038, "qhull option warning: joggle ('QJ') produces simplicial output (i.e., triangles in 2-D). Unless merging is requested, option 'Qt' has no effect\n"); if (qh JOGGLEmax < REALmax/2 && qh DELAUNAY && !qh SCALEinput && !qh SCALElast) { qh SCALElast= True; qh_option("Qbbound-last-qj", NULL, NULL); } if (qh MERGING && !qh POSTmerge && qh premerge_cos > REALmax/2 - && qh premerge_centrum == 0) { + && qh premerge_centrum == 0.0) { qh ZEROcentrum= True; qh ZEROall_ok= True; qh_option("_zero-centrum", NULL, NULL); @@ -1539,7 +1670,7 @@ void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc REALepsilon); #ifdef qh_NOmerge if (qh MERGING) { - qh_fprintf(qh ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n"); + qh_fprintf(qh ferr, 6045, "qhull option error: merging not installed (qh_NOmerge) for 'Qx', 'Cn' or 'An')\n"); qh_errexit(qh_ERRinput, NULL, NULL); } #endif @@ -1547,21 +1678,34 @@ void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc qh KEEPinside= True; qh_option("Qinterior-keep", NULL, NULL); } + if (qh VORONOI && !qh DELAUNAY) { + qh_fprintf(qh ferr, 6038, "qhull internal error (qh_initqhull_globals): if qh.VORONOI is set, qh.DELAUNAY must be set. Qhull constructs the Delaunay triangulation in order to compute the Voronoi diagram\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } if (qh DELAUNAY && qh HALFspace) { - qh_fprintf(qh ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n"); + qh_fprintf(qh ferr, 6046, "qhull option error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n"); qh_errexit(qh_ERRinput, NULL, NULL); + /* same error message in qh_readpoints */ } if (!qh DELAUNAY && (qh UPPERdelaunay || qh ATinfinity)) { - qh_fprintf(qh ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n"); + qh_fprintf(qh ferr, 6047, "qhull option error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n"); qh_errexit(qh_ERRinput, NULL, NULL); } if (qh UPPERdelaunay && qh ATinfinity) { - qh_fprintf(qh ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n"); + qh_fprintf(qh ferr, 6048, "qhull option error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n"); qh_errexit(qh_ERRinput, NULL, NULL); } + if (qh MERGEpinched && qh ONLYgood) { + qh_fprintf(qh ferr, 6362, "qhull option error: can not use merge-pinched-vertices ('Q14') with good-facets-only ('Qg')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh MERGEpinched && qh hull_dim == 2) { + trace2((qh ferr, 2108, "qh_initqhull_globals: disable qh.MERGEpinched for 2-d. It has no effect")) + qh MERGEpinched= False; + } if (qh SCALElast && !qh DELAUNAY && qh PRINTprecision) - qh_fprintf(qh ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n"); - qh DOcheckmax= (!qh SKIPcheckmax && qh MERGING ); + qh_fprintf(qh ferr, 7040, "qhull option warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n"); + qh DOcheckmax= (!qh SKIPcheckmax && (qh MERGING || qh APPROXhull)); qh KEEPnearinside= (qh DOcheckmax && !(qh KEEPinside && qh KEEPcoplanar) && !qh NOnearinside); if (qh MERGING) @@ -1569,7 +1713,7 @@ void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc else if (qh VORONOI) qh CENTERtype= qh_ASvoronoi; if (qh TESTvneighbors && !qh MERGING) { - qh_fprintf(qh ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n"); + qh_fprintf(qh ferr, 6049, "qhull option error: test vertex neighbors('Qv') needs a merge option\n"); qh_errexit(qh_ERRinput, NULL ,NULL); } if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) { @@ -1587,10 +1731,10 @@ void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc for (k=2, factorial=1.0; k < qh hull_dim; k++) factorial *= k; qh AREAfactor= 1.0 / factorial; - trace2((qh ferr, 2005, "qh_initqhull_globals: initialize globals. dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n", - dim, numpoints, ismalloc, qh PROJECTinput, qh hull_dim)); - qh normal_size= qh hull_dim * sizeof(coordT); - qh center_size= qh normal_size - sizeof(coordT); + trace2((qh ferr, 2005, "qh_initqhull_globals: initialize globals. input_dim %d, numpoints %d, malloc? %d, projected %d to hull_dim %d\n", + qh input_dim, numpoints, ismalloc, qh PROJECTinput, qh hull_dim)); + qh normal_size= qh hull_dim * (int)sizeof(coordT); + qh center_size= qh normal_size - (int)sizeof(coordT); pointsneeded= qh hull_dim+1; if (qh hull_dim > qh_DIMmergeVertex) { qh MERGEvertices= False; @@ -1599,17 +1743,20 @@ void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc if (qh GOODpoint) pointsneeded++; #ifdef qh_NOtrace - if (qh IStracing) { - qh_fprintf(qh ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)"); - qh_errexit(qh_ERRqhull, NULL, NULL); + if (qh IStracing || qh TRACEmerge || qh TRACEpoint != qh_IDnone || qh TRACEdist < REALmax/2) { + qh_fprintf(qh ferr, 6051, "qhull option error: tracing is not installed (qh_NOtrace in user.h). Trace options 'Tn', 'TMn', 'TPn' and 'TWn' mostly removed. Continue with 'Qw' (allow warning)\n"); + if (!qh ALLOWwarning) + qh_errexit(qh_ERRinput, NULL, NULL); } #endif if (qh RERUN > 1) { qh TRACElastrun= qh IStracing; /* qh_build_withrestart duplicates next conditional */ - if (qh IStracing != -1) + if (qh IStracing && qh IStracing != -1) { + qh_fprintf(qh ferr, 8162, "qh_initqhull_globals: trace last of TR%d runs at level %d\n", qh RERUN, qh IStracing); qh IStracing= 0; - }else if (qh TRACEpoint != qh_IDunknown || qh TRACEdist < REALmax/2 || qh TRACEmerge) { - qh TRACElevel= (qh IStracing? qh IStracing : 3); + } + }else if (qh TRACEpoint != qh_IDnone || qh TRACEdist < REALmax/2 || qh TRACEmerge) { + qh TRACElevel= (qh IStracing ? qh IStracing : 3); qh IStracing= 0; } if (qh ROTATErandom == 0 || qh ROTATErandom == -1) { @@ -1633,20 +1780,17 @@ void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc randr += randi; if (randi > qh_RANDOMmax) { qh_fprintf(qh ferr, 8036, "\ -qhull configuration error (qh_RANDOMmax in user.h):\n\ - random integer %d > qh_RANDOMmax(%.8g)\n", +qhull configuration error (qh_RANDOMmax in user.h): random integer %d > qh_RANDOMmax (%.8g)\n", randi, qh_RANDOMmax); qh_errexit(qh_ERRinput, NULL, NULL); } } qh_RANDOMseed_(seed); - randr = randr/1000; + randr= randr/1000; if (randr < qh_RANDOMmax * 0.1 || randr > qh_RANDOMmax * 0.9) qh_fprintf(qh ferr, 8037, "\ -qhull configuration warning (qh_RANDOMmax in user.h):\n\ - average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\ - Is qh_RANDOMmax (%.2g) wrong?\n", +qhull configuration warning (qh_RANDOMmax in user.h): average of 1000 random integers (%.2g) is much different than expected (%.2g). 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; @@ -1666,12 +1810,12 @@ qhull configuration warning (qh_RANDOMmax in user.h):\n\ /*--------------------------------- - qh_initqhull_mem( ) + qh_initqhull_mem( ) initialize mem.c for qhull qh.hull_dim and qh.normal_size determine some of the allocation sizes if qh.MERGING, includes ridgeT - calls qh_user_memsizes() to add up to 10 additional sizes for quick allocation + calls qh_user_memsizes (user.c) to add up to 10 additional sizes for quick allocation (see numsizes below) returns: @@ -1687,14 +1831,14 @@ void qh_initqhull_mem(void) { numsizes= 8+10; qh_meminitbuffers(qh IStracing, qh_MEMalign, numsizes, - qh_MEMbufsize,qh_MEMinitbuf); + qh_MEMbufsize, qh_MEMinitbuf); qh_memsize((int)sizeof(vertexT)); if (qh MERGING) { qh_memsize((int)sizeof(ridgeT)); qh_memsize((int)sizeof(mergeT)); } qh_memsize((int)sizeof(facetT)); - i= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; /* ridge.vertices */ + i= (int)sizeof(setT) + (qh hull_dim - 1) * SETelemsize; /* ridge.vertices */ qh_memsize(i); qh_memsize(qh normal_size); /* normal */ i += SETelemsize; /* facet.vertices, .ridges, .neighbors */ @@ -1725,7 +1869,7 @@ void qh_initqhull_outputflags(void) { trace3((qh ferr, 3024, "qh_initqhull_outputflags: %s\n", qh qhull_command)); if (!(qh PRINTgood || qh PRINTneighbors)) { - if (qh KEEParea || qh KEEPminArea < REALmax/2 || qh KEEPmerge || qh DELAUNAY + if (qh DELAUNAY || qh KEEParea || qh KEEPminArea < REALmax/2 || qh KEEPmerge || (!qh ONLYgood && (qh GOODvertex || qh GOODpoint))) { qh PRINTgood= True; qh_option("Pgood", NULL, NULL); @@ -1733,11 +1877,11 @@ void qh_initqhull_outputflags(void) { } if (qh PRINTtransparent) { if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) { - qh_fprintf(qh ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n"); + qh_fprintf(qh ferr, 6215, "qhull option error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n"); qh_errexit(qh_ERRinput, NULL, NULL); } - qh DROPdim = 3; - qh PRINTridges = True; + qh DROPdim= 3; + qh PRINTridges= True; } for (i=qh_PRINTEND; i--; ) { if (qh PRINTout[i] == qh_PRINTgeom) @@ -1749,13 +1893,13 @@ void qh_initqhull_outputflags(void) { else if (qh PRINTout[i] == qh_PRINTpointnearest) printcoplanar= True; else if (qh PRINTout[i] == qh_PRINTpointintersect && !qh HALFspace) { - qh_fprintf(qh ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n"); + qh_fprintf(qh ferr, 6053, "qhull option error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n"); qh_errexit(qh_ERRinput, NULL, NULL); }else if (qh PRINTout[i] == qh_PRINTtriangles && (qh HALFspace || qh VORONOI)) { - qh_fprintf(qh ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n"); + qh_fprintf(qh ferr, 6054, "qhull option error: option 'Ft' is not available for Voronoi vertices ('v') or halfspace intersection ('H')\n"); qh_errexit(qh_ERRinput, NULL, NULL); }else if (qh PRINTout[i] == qh_PRINTcentrums && qh VORONOI) { - qh_fprintf(qh ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n"); + qh_fprintf(qh ferr, 6055, "qhull option error: option 'FC' is not available for Voronoi vertices('v')\n"); qh_errexit(qh_ERRinput, NULL, NULL); }else if (qh PRINTout[i] == qh_PRINTvertices) { if (qh VORONOI) @@ -1766,31 +1910,30 @@ void qh_initqhull_outputflags(void) { } if (printcoplanar && qh DELAUNAY && qh JOGGLEmax < REALmax/2) { if (qh PRINTprecision) - qh_fprintf(qh ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n"); + qh_fprintf(qh ferr, 7041, "qhull option warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n"); } if (printmath && (qh hull_dim > 3 || qh VORONOI)) { - qh_fprintf(qh ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n"); + qh_fprintf(qh ferr, 6056, "qhull option error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n"); qh_errexit(qh_ERRinput, NULL, NULL); } if (printgeom) { if (qh hull_dim > 4) { - qh_fprintf(qh ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n"); + qh_fprintf(qh ferr, 6057, "qhull option error: Geomview output is only available for 2-d, 3-d and 4-d\n"); qh_errexit(qh_ERRinput, NULL, NULL); } if (qh PRINTnoplanes && !(qh PRINTcoplanar + qh PRINTcentrums + qh PRINTdots + qh PRINTspheres + qh DOintersections + qh PRINTridges)) { - qh_fprintf(qh ferr, 6058, "qhull input error: no output specified for Geomview\n"); + qh_fprintf(qh ferr, 6058, "qhull option error: no output specified for Geomview\n"); qh_errexit(qh_ERRinput, NULL, NULL); } if (qh VORONOI && (qh hull_dim > 3 || qh DROPdim >= 0)) { - qh_fprintf(qh ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n"); + qh_fprintf(qh ferr, 6059, "qhull option error: Geomview output for Voronoi diagrams only for 2-d\n"); qh_errexit(qh_ERRinput, NULL, NULL); } /* can not warn about furthest-site Geomview output: no lower_threshold */ if (qh hull_dim == 4 && qh DROPdim == -1 && (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) { - qh_fprintf(qh ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\ -available for 4-d output(ignored). Could use 'GDn' instead.\n"); + qh_fprintf(qh ferr, 7042, "qhull option warning: coplanars, vertices, and centrums output not available for 4-d output(ignored). Could use 'GDn' instead.\n"); qh PRINTcoplanar= qh PRINTspheres= qh PRINTcentrums= False; } } @@ -1799,7 +1942,7 @@ available for 4-d output(ignored). Could use 'GDn' instead.\n"); if (qh QHULLfinished) { qh_fprintf(qh ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n"); }else { - qh KEEPcoplanar = True; + qh KEEPcoplanar= True; qh_option("Qcoplanar", NULL, NULL); } } @@ -1809,7 +1952,7 @@ available for 4-d output(ignored). Could use 'GDn' instead.\n"); if (qh DROPdim < qh hull_dim) { qh PRINTdim--; if (!printgeom || qh hull_dim < 3) - qh_fprintf(qh ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh DROPdim); + qh_fprintf(qh ferr, 7043, "qhull option 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) { @@ -1863,17 +2006,22 @@ void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) { #else memset((char *)&qh_qh, 0, sizeof(qhT)); #endif - qh ANGLEmerge= True; + qh NOerrexit= True; qh DROPdim= -1; qh ferr= errfile; qh fin= infile; qh fout= outfile; qh furthest_id= qh_IDunknown; +#ifndef qh_NOmerge qh JOGGLEmax= REALmax; - qh KEEPminArea = REALmax; +#else + qh JOGGLEmax= 0.0; /* Joggle ('QJ') if qh_NOmerge */ +#endif + qh KEEPminArea= REALmax; qh last_low= REALmax; qh last_high= REALmax; qh last_newhigh= REALmax; + qh lastcpu= 0.0; qh max_outside= 0.0; qh max_vertex= 0.0; qh MAXabs_coord= 0.0; @@ -1896,9 +2044,10 @@ void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) { 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 */ + qh TRACEpoint= qh_IDnone; /* recompile to trace a point, or use 'TPn' */ + qh tracefacet_id= UINT_MAX; /* recompile to trace a facet, set to UINT_MAX when done, see userprintf.c/qh_fprintf */ + qh traceridge_id= UINT_MAX; /* recompile to trace a ridge, set to UINT_MAX when done, see userprintf.c/qh_fprintf */ + qh tracevertex_id= UINT_MAX; /* recompile to trace a vertex, set to UINT_MAX when done, see userprintf.c/qh_fprintf */ seed= (int)time(&timedata); qh_RANDOMseed_(seed); qh run_id= qh_RANDOMint; @@ -1932,6 +2081,7 @@ void qh_initthresholds(char *command) { realT value; int idx, maxdim, k; char *s= command; /* non-const due to strtol */ + char *lastoption, *lastwarning= NULL; char key; maxdim= qh input_dim; @@ -1941,26 +2091,29 @@ void qh_initthresholds(char *command) { if (*s == '-') s++; if (*s == 'P') { - s++; + lastoption= s++; while (*s && !isspace(key= *s++)) { if (key == 'd' || key == 'D') { if (!isdigit(*s)) { - qh_fprintf(qh ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n", + qh_fprintf(qh ferr, 7044, "qhull option warning: no dimension given for Print option 'P%c' at: %s. Ignored\n", key, s-1); + lastwarning= lastoption; continue; } idx= qh_strtol(s, &s); if (idx >= qh hull_dim) { - qh_fprintf(qh ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n", + qh_fprintf(qh ferr, 7045, "qhull option warning: dimension %d for Print option 'P%c' is >= %d. Ignored\n", idx, key, qh hull_dim); + lastwarning= lastoption; continue; } if (*s == ':') { s++; value= qh_strtod(s, &s); if (fabs((double)value) > 1.0) { - qh_fprintf(qh ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1. Ignored\n", + qh_fprintf(qh ferr, 7046, "qhull option warning: value %2.4g for Print option 'P%c' is > +1 or < -1. Ignored\n", value, key); + lastwarning= lastoption; continue; } }else @@ -1972,7 +2125,7 @@ void qh_initthresholds(char *command) { } } }else if (*s == 'Q') { - s++; + lastoption= s++; while (*s && !isspace(key= *s++)) { if (key == 'b' && *s == 'B') { s++; @@ -1984,14 +2137,16 @@ void qh_initthresholds(char *command) { s++; else if (key == 'b' || key == 'B') { if (!isdigit(*s)) { - qh_fprintf(qh ferr, 7047, "qhull warning: no dimension given for Qhull option %c. Ignored\n", + qh_fprintf(qh ferr, 7047, "qhull option warning: no dimension given for Qhull option 'Q%c'\n", key); + lastwarning= lastoption; continue; } idx= qh_strtol(s, &s); if (idx >= maxdim) { - qh_fprintf(qh ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n", + qh_fprintf(qh ferr, 7048, "qhull option warning: dimension %d for Qhull option 'Q%c' is >= %d. Ignored\n", idx, key, maxdim); + lastwarning= lastoption; continue; } if (*s == ':') { @@ -2025,6 +2180,11 @@ void qh_initthresholds(char *command) { }else if (qh upper_threshold[k] < REALmax/2) qh GOODthreshold= True; } + if (lastwarning && !qh ALLOWwarning) { + qh_fprintf(qh ferr, 6036, "qhull option error: see previous warnings, use 'Qw' to override: '%s' (last offset %d)\n", + command, (int)(lastwarning-command)); + qh_errexit(qh_ERRinput, NULL, NULL); + } } /* initthresholds */ /*- sizeof(buf)-30-30) { + qh_fprintf(qh ferr, 6408, "qhull internal error (qh_option): option (%d chars) has more than %d chars. May overflow temporary buffer. Option '%s'\n", + (int)strlen(option), (int)sizeof(buf)-30-30, option); + qh_errexit(qh_ERRqhull, NULL, NULL); + } 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); + buflen= (int)strlen(buf); /* WARN64 */ + qh qhull_optionlen += buflen; + remainder= (int)(sizeof(qh qhull_options) - strlen(qh qhull_options)) - 1; /* WARN64 */ + maximize_(remainder, 0); + if (qh qhull_optionlen >= qh_OPTIONline && remainder > 0) { + strncat(qh qhull_options, "\n", (unsigned int)remainder); + --remainder; + qh qhull_optionlen= buflen; + } + if (buflen > remainder) { + trace1((qh ferr, 1058, "qh_option: option would overflow qh.qhull_options. Truncated '%s'\n", buf)); + } + strncat(qh qhull_options, buf, (unsigned int)remainder); } /* option */ #if qh_QHpointer diff --git a/3rdparty/qhull/io.c b/3rdparty/qhull/io.c index 401987ec0..beed156ad 100644 --- a/3rdparty/qhull/io.c +++ b/3rdparty/qhull/io.c @@ -13,9 +13,9 @@ unix.c and user.c are the only callers of io.c functions This allows the user to avoid loading io.o from qhull.a - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/io.c#5 $$Change: 2064 $ - $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/io.c#11 $$Change: 2965 $ + $DateTime: 2020/06/04 15:37:41 $$Author: bbarber $ */ #include "qhull_a.h" @@ -25,10 +25,11 @@ /*--------------------------------- - qh_produce_output() - qh_produce_output2() + qh_produce_output( ) + qh_produce_output2( ) prints out the result of qhull in desired format - qh_produce_output2() does not call qh_prepare_output() + qh_produce_output2 does not call qh_prepare_output + qh_checkpolygon is valid for qh_prepare_output if qh.GETarea computes and prints area and volume qh.PRINTout[] is an array of output formats @@ -52,12 +53,15 @@ void qh_produce_output(void) { void qh_produce_output2(void) { int i, tempsize= qh_setsize(qhmem.tempstack), d_1; + fflush(NULL); if (qh PRINTsummary) qh_printsummary(qh ferr); else if (qh PRINTout[0] == qh_PRINTnone) qh_printsummary(qh fout); for (i=0; i < qh_PRINTEND; i++) qh_printfacets(qh fout, qh PRINTout[i], qh facet_list, NULL, !qh_ALL); + fflush(NULL); + qh_allstatistics(); if (qh PRINTprecision && !qh MERGING && (qh JOGGLEmax > REALmax/2 || qh RERUN)) qh_printstats(qh ferr, qhstat precision, NULL); @@ -66,7 +70,7 @@ void qh_produce_output2(void) { if (qh PRINTstatistics) { qh_printstatistics(qh ferr, ""); qh_memstatistics(qh ferr); - d_1= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; + d_1= (int)sizeof(setT) + (qh hull_dim - 1) * SETelemsize; qh_fprintf(qh ferr, 8040, "\ size in bytes: merge %d ridge %d vertex %d facet %d\n\ normal %d ridge vertices %d facet vertices or neighbors %d\n", @@ -88,7 +92,7 @@ void qh_produce_output2(void) { print facet by id, for debugging */ -void qh_dfacet(unsigned id) { +void qh_dfacet(unsigned int id) { facetT *facet; FORALLfacets { @@ -106,7 +110,7 @@ void qh_dfacet(unsigned id) { qh_dvertex( id ) print vertex by id, for debugging */ -void qh_dvertex(unsigned id) { +void qh_dvertex(unsigned int id) { vertexT *vertex; FORALLvertices { @@ -138,18 +142,6 @@ int qh_compare_facetarea(const void *p1, const void *p2) { return -1; } /* compare_facetarea */ -/*--------------------------------- - - qh_compare_facetmerge( p1, p2 ) - used by qsort() to order facets by number of merges -*/ -int qh_compare_facetmerge(const void *p1, const void *p2) { - const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); - - return(a->nummerge - b->nummerge); -} /* compare_facetvisit */ - /*--------------------------------- @@ -160,26 +152,27 @@ 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; + if (!(i= (int)a->visitid)) + i= (int)(0 - a->id); /* sign distinguishes id from visitid */ + if (!(j= (int)b->visitid)) + j= (int)(0 - b->id); return(i - j); } /* compare_facetvisit */ /*--------------------------------- + >-------------------------------- - qh_compare_vertexpoint( p1, p2 ) - used by qsort() to order vertices by point id + qh_compare_nummerge( p1, p2 ) + used by qsort() to order facets by number of merges - Not used. Not available in libqhull_r.h since qh_pointid depends on qh +notes: + called by qh_markkeep ('PMerge-keep') */ -int qh_compare_vertexpoint(const void *p1, const void *p2) { - const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); +int qh_compare_nummerge(const void *p1, const void *p2) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); - return((qh_pointid(a->point) > qh_pointid(b->point)?1:-1)); -} /* compare_vertexpoint */ + return(a->nummerge - b->nummerge); +} /* compare_nummerge */ /*--------------------------------- @@ -197,7 +190,7 @@ void qh_copyfilename(char *filename, int size, const char* source, int length) { qh_fprintf(qh ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source); qh_errexit(qh_ERRinput, NULL, NULL); } - strncpy(filename, source, length); + strncpy(filename, source, (size_t)length); filename[length]= '\0'; if (c == '\'' || c == '"') { char *s= filename + 1; @@ -217,7 +210,7 @@ void qh_copyfilename(char *filename, int size, const char* source, int length) { /*--------------------------------- - qh_countfacets( facetlist, facets, printall, + qh_countfacets(facetlist, facets, printall, numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars ) count good facets for printing and set visitid if allfacets, ignores qh_skipfacet() @@ -251,7 +244,7 @@ void qh_countfacets(facetT *facetlist, setT *facets, boolT printall, || (!printall && qh_skipfacet(facet))) facet->visitid= 0; else { - facet->visitid= ++numfacets; + facet->visitid= (unsigned int)(++numfacets); totneighbors += qh_setsize(facet->neighbors); if (facet->simplicial) { numsimplicial++; @@ -269,7 +262,7 @@ void qh_countfacets(facetT *facetlist, setT *facets, boolT printall, || (!printall && qh_skipfacet(facet))) facet->visitid= 0; else { - facet->visitid= ++numfacets; + facet->visitid= (unsigned int)(++numfacets); totneighbors += qh_setsize(facet->neighbors); if (facet->simplicial){ numsimplicial++; @@ -281,7 +274,7 @@ void qh_countfacets(facetT *facetlist, setT *facets, boolT printall, numcoplanars += qh_setsize(facet->coplanarset); } } - qh visit_id += numfacets+1; + qh visit_id += (unsigned int)numfacets + 1; *numfacetsp= numfacets; *numsimplicialp= numsimplicial; *totneighborsp= totneighbors; @@ -390,6 +383,7 @@ pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *off normal= gmcoord; qh_sethyperplane_gauss(dim, qh gm_row, point0, True, normal, &offset, &nearzero); + /* nearzero is true for axis-parallel hyperplanes (e.g., a bounding box). Should detect degenerate hyperplanes. See 'Tv' check following */ if (qh GOODvertexp == vertexA->point) inpoint= vertexA->point; else @@ -426,7 +420,7 @@ pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *off else angle= angle - 1.0; if (angle < 0.0) - angle -= angle; + angle= -angle; trace4((qh ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n", pointid, pointidA, angle, nearzero)); if (nearzero) { @@ -595,6 +589,7 @@ setT *qh_detvridge3(vertexT *atvertex, vertexT *vertex) { if printvridge, calls printvridge( fp, vertex, vertexA, centers) fp== any pointer (assumes FILE*) + fp may be NULL for QhullQh::qh_fprintf which calls appendQhullMessage 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 @@ -669,7 +664,7 @@ int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT totridges++; trace4((qh ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n", count, qh_pointid(atvertex->point), qh_pointid(vertex->point))); - if (printvridge && fp) { + if (printvridge) { if (inorder && qh hull_dim == 3+1) /* 3-d Voronoi diagram */ centers= qh_detvridge3(atvertex, vertex); else @@ -725,7 +720,7 @@ int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RID qh_clearcenters(qh_ASvoronoi); qh_vertexneighbors(); - maximize_(qh visit_id, (unsigned) qh num_facets); + maximize_(qh visit_id, (unsigned int)qh num_facets); FORALLfacets { facet->visitid= 0; facet->seen= False; @@ -733,7 +728,7 @@ int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RID } FORALLfacets { if (facet->upperdelaunay == isUpper) - facet->visitid= numcenters++; + facet->visitid= (unsigned int)(numcenters++); } FORALLvertices vertex->seen= False; @@ -869,13 +864,17 @@ void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane) { >-------------------------------- qh_markkeep( facetlist ) - mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea + restrict good facets for qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea ignores visible facets (!part of convex hull) returns: may clear facet->good recomputes qh.num_good + notes: + only called by qh_prepare_output after qh_findgood_all + does not throw errors except memory/corruption of qset.c + design: get set of good facets if qh.KEEParea @@ -913,7 +912,7 @@ void qh_markkeep(facetT *facetlist) { } if (qh KEEPmerge) { qsort(SETaddr_(facets, facetT), (size_t)size, - sizeof(facetT *), qh_compare_facetmerge); + sizeof(facetT *), qh_compare_nummerge); if ((count= size - qh KEEPmerge) > 0) { FOREACHfacet_(facets) { facet->good= False; @@ -974,7 +973,7 @@ setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isL if (qh ATinfinity) SETelem_(vertices, qh num_points-1)= NULL; qh visit_id++; - maximize_(qh visit_id, (unsigned) qh num_facets); + maximize_(qh visit_id, (unsigned int)qh num_facets); FORALLfacet_(facetlist) { if (printall || !qh_skipfacet(facet)) { if (!facet->upperdelaunay) { @@ -1002,11 +1001,11 @@ setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isL numcenters++; /* qh_INFINITE */ FORALLfacet_(facetlist) { if (printall || !qh_skipfacet(facet)) - facet->visitid= numcenters++; + facet->visitid= (unsigned int)(numcenters++); } FOREACHfacet_(facets) { if (printall || !qh_skipfacet(facet)) - facet->visitid= numcenters++; + facet->visitid= (unsigned int)(numcenters++); } *isLowerp= isLower; *numcentersp= numcenters; @@ -1018,43 +1017,75 @@ setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isL >-------------------------------- qh_order_vertexneighbors( vertex ) - order facet neighbors of a 2-d or 3-d vertex by adjacency + order facet neighbors of vertex by 2-d (orientation), 3-d (adjacency), or n-d (f.visitid,id) notes: - does not orient the neighbors - - design: + error if qh_vertexneighbors not called beforehand + only 2-d orients the neighbors + for 4-d and higher + set or clear f.visitid for qh_compare_facetvisit + for example, use qh_markvoronoi (e.g., qh_printvornoi) or qh_countfacets (e.g., qh_printvneighbors) + + design (2-d): + see qh_printextremes_2d + design (3-d): 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 + design (n-d): + qsort by f.visitid, or f.facetid (qh_compare_facetvisit) + facet_id is negated (sorted before visit_id facets) */ void qh_order_vertexneighbors(vertexT *vertex) { setT *newset; - facetT *facet, *neighbor, **neighborp; + facetT *facet, *facetA, *facetB, *neighbor, **neighborp; + vertexT *vertexA; + int numneighbors; - trace4((qh ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id)); - newset= qh_settemp(qh_setsize(vertex->neighbors)); - facet= (facetT*)qh_setdellast(vertex->neighbors); - qh_setappend(&newset, facet); - while (qh_setsize(vertex->neighbors)) { - FOREACHneighbor_(vertex) { - if (qh_setin(facet->neighbors, neighbor)) { - qh_setdel(vertex->neighbors, neighbor); - qh_setappend(&newset, neighbor); - facet= neighbor; - break; - } + trace4((qh ferr, 4018, "qh_order_vertexneighbors: order facet neighbors of v%d by 2-d (orientation), 3-d (adjacency), or n-d (f.visitid,id)\n", vertex->id)); + if (!qh VERTEXneighbors) { + qh_fprintf(qh ferr, 6428, "qhull internal error (qh_order_vertexneighbors): call qh_vertexneighbors before calling qh_order_vertexneighbors\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (qh hull_dim == 2) { + facetA= SETfirstt_(vertex->neighbors, facetT); + if (facetA->toporient ^ qh_ORIENTclock) + vertexA= SETfirstt_(facetA->vertices, vertexT); + else + vertexA= SETsecondt_(facetA->vertices, vertexT); + if (vertexA!=vertex) { + facetB= SETsecondt_(vertex->neighbors, facetT); + SETfirst_(vertex->neighbors)= facetB; + SETsecond_(vertex->neighbors)= facetA; } - if (!neighbor) { - qh_fprintf(qh ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n", - vertex->id, facet->id); - qh_errexit(qh_ERRqhull, facet, NULL); + }else if (qh hull_dim == 3) { + newset= qh_settemp(qh_setsize(vertex->neighbors)); + facet= (facetT *)qh_setdellast(vertex->neighbors); + qh_setappend(&newset, facet); + while (qh_setsize(vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (qh_setin(facet->neighbors, neighbor)) { + qh_setdel(vertex->neighbors, neighbor); + qh_setappend(&newset, neighbor); + facet= neighbor; + break; + } + } + if (!neighbor) { + qh_fprintf(qh ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n", + vertex->id, facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } } + qh_setfree(&vertex->neighbors); + qh_settemppop(); + vertex->neighbors= newset; + }else { /* qh.hull_dim >= 4 */ + numneighbors= qh_setsize(vertex->neighbors); + qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors, + sizeof(facetT *), qh_compare_facetvisit); } - qh_setfree(&vertex->neighbors); - qh_settemppop(); - vertex->neighbors= newset; } /* order_vertexneighbors */ /*-good notes + called by qh_produce_output, qh_new_qhull, Qhull.outputQhull except for PRINTstatistics, no-op if previously called with same options */ void qh_prepare_output(void) { @@ -1232,11 +1264,11 @@ void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall) { case qh_PRINTpointintersect: if (!qh feasible_point) { qh_fprintf(qh ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh feasible_point\n"); - qh_errexit( qh_ERRinput, NULL, NULL); + qh_errexit(qh_ERRinput, NULL, NULL); } if (facet->offset > 0) goto LABELprintinfinite; - point= coordp= (coordT*)qh_memalloc(qh normal_size); + point= coordp= (coordT *)qh_memalloc(qh normal_size); normp= facet->normal; feasiblep= qh feasible_point; if (facet->offset < -qh MINdenom) { @@ -1288,7 +1320,7 @@ void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall) { /*--------------------------------- - qh_printbegin( ) + qh_printbegin( ) prints header for all output formats returns: @@ -1475,8 +1507,8 @@ void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, b break; case qh_PRINTincidences: if (qh VORONOI && qh PRINTprecision) - qh_fprintf(qh ferr, 7053, "qhull warning: writing Delaunay. Use 'p' or 'o' for Voronoi centers\n"); - qh printoutvar= qh vertex_id; /* centrum id for non-simplicial facets */ + qh_fprintf(qh ferr, 7053, "qhull warning: input sites of Delaunay regions (option 'i'). Use option 'p' or 'o' for Voronoi centers. Disable warning with option 'Pp'\n"); + qh printoutvar= (int)qh vertex_id; /* centrum id for 4-d+, non-simplicial facets */ if (qh hull_dim <= 3) qh_fprintf(fp, 9050, "%d\n", numfacets); else @@ -1519,7 +1551,7 @@ void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, b case qh_PRINTtriangles: if (qh VORONOI) goto LABELnoformat; - num = qh hull_dim; + num= qh hull_dim; if (format == qh_PRINToff || qh hull_dim == 2) qh_fprintf(fp, 9060, "%d\n%d %d %d\n", num, qh num_points+qh_setsize(qh other_points), numfacets, totneighbors/2); @@ -1871,7 +1903,7 @@ void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printa nextfacet= SETsecondt_(facet->neighbors, facetT); } if (facet->visitid == qh visit_id) { - qh_fprintf(qh ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n", + qh_fprintf(qh ferr, 6218, "qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n", facet->id, nextfacet->id); qh_errexit2(qh_ERRqhull, facet, nextfacet); } @@ -2137,7 +2169,7 @@ void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offs /*--------------------------------- - qh_printfacet3geom_simplicial( ) + qh_printfacet3geom_simplicial( ) print Geomview OFF for a 3-d simplicial facet. notes: @@ -2271,7 +2303,7 @@ void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format) { /*--------------------------------- - qh_printfacet4geom_nonsimplicial( ) + qh_printfacet4geom_nonsimplicial( ) print Geomview 4OFF file for a 4d nonsimplicial facet prints all ridges to unvisited neighbors (qh.visit_id) if qh.DROPdim @@ -2472,17 +2504,23 @@ void qh_printfacetheader(FILE *fp, facetT *facet) { if (facet->visible) qh_fprintf(fp, 9143, " visible"); if (facet->newfacet) - qh_fprintf(fp, 9144, " new"); + qh_fprintf(fp, 9144, " newfacet"); if (facet->tested) qh_fprintf(fp, 9145, " tested"); if (!facet->good) qh_fprintf(fp, 9146, " notG"); - if (facet->seen) + if (facet->seen && qh IStracing) qh_fprintf(fp, 9147, " seen"); - if (facet->coplanar) - qh_fprintf(fp, 9148, " coplanar"); + if (facet->seen2 && qh IStracing) + qh_fprintf(fp, 9418, " seen2"); + if (facet->isarea) + qh_fprintf(fp, 9419, " isarea"); + if (facet->coplanarhorizon) + qh_fprintf(fp, 9148, " coplanarhorizon"); if (facet->mergehorizon) qh_fprintf(fp, 9149, " mergehorizon"); + if (facet->cycledone) + qh_fprintf(fp, 9420, " cycledone"); if (facet->keepcentrum) qh_fprintf(fp, 9150, " keepcentrum"); if (facet->dupridge) @@ -2514,18 +2552,20 @@ void qh_printfacetheader(FILE *fp, facetT *facet) { qh_fprintf(fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id); }else if (facet->f.newcycle) qh_fprintf(fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id); - if (facet->nummerge) + if (facet->nummerge == qh_MAXnummerge) + qh_fprintf(fp, 9427, " - merges: %dmax\n", qh_MAXnummerge); + else if (facet->nummerge) qh_fprintf(fp, 9165, " - merges: %d\n", facet->nummerge); qh_printpointid(fp, " - normal: ", qh hull_dim, facet->normal, qh_IDunknown); qh_fprintf(fp, 9166, " - offset: %10.7g\n", facet->offset); if (qh CENTERtype == qh_ASvoronoi || facet->center) qh_printcenter(fp, qh_PRINTfacets, " - center: ", facet); #if qh_MAXoutside - if (facet->maxoutside > qh DISTround) + if (facet->maxoutside > qh DISTround) /* initial value */ qh_fprintf(fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside); #endif if (!SETempty_(facet->outsideset)) { - furthest= (pointT*)qh_setlast(facet->outsideset); + furthest= (pointT *)qh_setlast(facet->outsideset); if (qh_setsize(facet->outsideset) < 6) { qh_fprintf(fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(furthest)); FOREACHpoint_(facet->outsideset) @@ -2541,7 +2581,7 @@ void qh_printfacetheader(FILE *fp, facetT *facet) { #endif } if (!SETempty_(facet->coplanarset)) { - furthest= (pointT*)qh_setlast(facet->coplanarset); + furthest= (pointT *)qh_setlast(facet->coplanarset); if (qh_setsize(facet->coplanarset) < 6) { qh_fprintf(fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(furthest)); FOREACHpoint_(facet->coplanarset) @@ -2560,9 +2600,9 @@ void qh_printfacetheader(FILE *fp, facetT *facet) { qh_fprintf(fp, 9174, " - neighboring facets:"); FOREACHneighbor_(facet) { if (neighbor == qh_MERGEridge) - qh_fprintf(fp, 9175, " MERGE"); + qh_fprintf(fp, 9175, " MERGEridge"); else if (neighbor == qh_DUPLICATEridge) - qh_fprintf(fp, 9176, " DUP"); + qh_fprintf(fp, 9176, " DUPLICATEridge"); else qh_fprintf(fp, 9177, " f%d", neighbor->id); } @@ -2587,10 +2627,10 @@ void qh_printfacetridges(FILE *fp, facetT *facet) { facetT *neighbor, **neighborp; ridgeT *ridge, **ridgep; int numridges= 0; - + int n; if (facet->visible && qh NEWfacets) { - qh_fprintf(fp, 9179, " - ridges(ids may be garbage):"); + qh_fprintf(fp, 9179, " - ridges (tentative ids):"); FOREACHridge_(facet->ridges) qh_fprintf(fp, 9180, " r%d", ridge->id); qh_fprintf(fp, 9181, "\n"); @@ -2609,7 +2649,7 @@ void qh_printfacetridges(FILE *fp, facetT *facet) { }else { FOREACHneighbor_(facet) { FOREACHridge_(facet->ridges) { - if (otherfacet_(ridge,facet) == neighbor) { + if (otherfacet_(ridge, facet) == neighbor && !ridge->seen) { ridge->seen= True; qh_printridge(fp, ridge); numridges++; @@ -2617,12 +2657,17 @@ void qh_printfacetridges(FILE *fp, facetT *facet) { } } } - if (numridges != qh_setsize(facet->ridges)) { + n= qh_setsize(facet->ridges); + if (n == 1 && facet->newfacet && qh NEWtentative) { + qh_fprintf(fp, 9411, " - horizon ridge to visible facet\n"); + } + if (numridges != n) { qh_fprintf(fp, 9183, " - all ridges:"); FOREACHridge_(facet->ridges) qh_fprintf(fp, 9184, " r%d", ridge->id); - qh_fprintf(fp, 9185, "\n"); + qh_fprintf(fp, 9185, "\n"); } + /* non-3d ridges w/o non-simplicial neighbors */ FOREACHridge_(facet->ridges) { if (!ridge->seen) qh_printridge(fp, ridge); @@ -2817,7 +2862,7 @@ void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *fac facetB= NULL; facets= qh_settemp(2*(qh_setsize(facetA->neighbors)+1)); qh visit_id++; - for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) { + for (facet=facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) { if (facet->visitid != qh visit_id) { facet->visitid= qh visit_id; qh_setappend(&facets, facet); @@ -2850,9 +2895,9 @@ void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *fac Same as QhullPoint's printPoint */ void qh_printpoint(FILE *fp, const char *string, pointT *point) { - int id= qh_pointid( point); + int id= qh_pointid(point); - qh_printpointid( fp, string, qh hull_dim, point, id); + qh_printpointid(fp, string, qh hull_dim, point, id); } /* printpoint */ void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id) { @@ -3024,6 +3069,14 @@ void qh_printridge(FILE *fp, ridgeT *ridge) { qh_fprintf(fp, 9223, " tested"); if (ridge->nonconvex) qh_fprintf(fp, 9224, " nonconvex"); + if (ridge->mergevertex) + qh_fprintf(fp, 9421, " mergevertex"); + if (ridge->mergevertex2) + qh_fprintf(fp, 9422, " mergevertex2"); + if (ridge->simplicialtop) + qh_fprintf(fp, 9425, " simplicialtop"); + if (ridge->simplicialbot) + qh_fprintf(fp, 9423, " simplicialbot"); qh_fprintf(fp, 9225, "\n"); qh_printvertices(fp, " vertices:", ridge->vertices); if (ridge->top && ridge->bottom) @@ -3153,8 +3206,8 @@ void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets innerouter= qh_RIDGEouter; printvridge= qh_printvnorm; }else { - qh_fprintf(qh ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format); - qh_errexit(qh_ERRinput, NULL, NULL); + qh_fprintf(qh ferr, 6219, "qhull internal error (qh_printvdiagram): unknown print format %d.\n", format); + qh_errexit(qh_ERRqhull, NULL, NULL); } vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters); totcount= qh_printvdiagram2(NULL, NULL, vertices, innerouter, False); @@ -3242,7 +3295,13 @@ void qh_printvertex(FILE *fp, vertexT *vertex) { if (vertex->deleted) qh_fprintf(fp, 9237, " deleted"); if (vertex->delridge) - qh_fprintf(fp, 9238, " ridgedeleted"); + qh_fprintf(fp, 9238, " delridge"); + if (vertex->newfacet) + qh_fprintf(fp, 9415, " newfacet"); + if (vertex->seen && qh IStracing) + qh_fprintf(fp, 9416, " seen"); + if (vertex->seen2 && qh IStracing) + qh_fprintf(fp, 9417, " seen2"); qh_fprintf(fp, 9239, "\n"); if (vertex->neighbors) { qh_fprintf(fp, 9240, " neighbors:"); @@ -3341,11 +3400,7 @@ void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printal if (vertex) { numneighbors= qh_setsize(vertex->neighbors); qh_fprintf(fp, 9249, "%d", numneighbors); - if (qh hull_dim == 3) - qh_order_vertexneighbors(vertex); - else if (qh hull_dim >= 4) - qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors, - sizeof(facetT *), qh_compare_facetvisit); + qh_order_vertexneighbors(vertex); FOREACHneighbor_(vertex) qh_fprintf(fp, 9250, " %d", neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id); @@ -3390,13 +3445,13 @@ void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, setT *vertices; vertexT *vertex; boolT isLower; - unsigned int numfacets= (unsigned int) qh num_facets; + unsigned int numfacets= (unsigned int)qh num_facets; vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters); FOREACHvertex_i_(vertices) { if (vertex) { numvertices++; - numneighbors = numinf = 0; + numneighbors= numinf= 0; FOREACHneighbor_(vertex) { if (neighbor->visitid == 0) numinf= 1; @@ -3441,12 +3496,7 @@ void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, numneighbors= 0; numinf=0; if (vertex) { - if (qh hull_dim == 3) - qh_order_vertexneighbors(vertex); - else if (qh hull_dim >= 4) - qsort(SETaddr_(vertex->neighbors, facetT), - (size_t)qh_setsize(vertex->neighbors), - sizeof(facetT *), qh_compare_facetvisit); + qh_order_vertexneighbors(vertex); FOREACHneighbor_(vertex) { if (neighbor->visitid == 0) numinf= 1; @@ -3601,7 +3651,7 @@ int qh_readfeasible(int dim, const char *curline) { } if (qh feasible_string) qh_fprintf(qh ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n"); - if (!(qh feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) { + if (!(qh feasible_point= (coordT *)qh_malloc((size_t)dim * sizeof(coordT)))) { qh_fprintf(qh ferr, 6071, "qhull error: insufficient memory for feasible point\n"); qh_errexit(qh_ERRmem, NULL, NULL); } @@ -3668,7 +3718,7 @@ int qh_readfeasible(int dim, const char *curline) { 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 + QH11012 FIX: qh_readpoints needs rewriting, too long */ coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) { coordT *points, *coords, *infinity= NULL; @@ -3741,11 +3791,22 @@ coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) { numinput= tempi; } if (diminput < 2) { - qh_fprintf(qh ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n", + qh_fprintf(qh ferr, 6220, "qhull input error: dimension %d (first or smaller number) should be at least 2\n", diminput); qh_errexit(qh_ERRinput, NULL, NULL); } - if (isdelaunay) { + if (numinput < 1 || numinput > qh_POINTSmax) { + qh_fprintf(qh ferr, 6411, "qhull input error: expecting between 1 and %d points. Got %d %d-d points\n", + qh_POINTSmax, numinput, diminput); + qh_errexit(qh_ERRinput, NULL, NULL); + /* same error message in qh_initqhull_globals */ + } + + if (isdelaunay && qh HALFspace) { + qh_fprintf(qh ferr, 6037, "qhull option error (qh_readpoints): can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + /* otherwise corrupted memory allocations, same error message as in qh_initqhull_globals */ + }else if (isdelaunay) { qh PROJECTdelaunay= False; if (qh CDDinput) *dimension= diminput; @@ -3758,13 +3819,13 @@ coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) { *dimension= diminput - 1; *numpoints= numinput; if (diminput < 3) { - qh_fprintf(qh ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n", + qh_fprintf(qh ferr, 6221, "qhull input error: dimension %d (first number, includes offset) should be at least 3 for halfspaces\n", diminput); qh_errexit(qh_ERRinput, NULL, NULL); } if (dimfeasible) { if (dimfeasible != *dimension) { - qh_fprintf(qh ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n", + qh_fprintf(qh ferr, 6222, "qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n", dimfeasible, diminput); qh_errexit(qh_ERRinput, NULL, NULL); } @@ -3777,9 +3838,9 @@ coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) { *dimension= diminput; *numpoints= numinput; } - qh normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */ + qh normal_size= *dimension * (int)sizeof(coordT); /* for tracing with qh_printpoint */ if (qh HALFspace) { - qh half_space= coordp= (coordT*)qh_malloc(qh normal_size + sizeof(coordT)); + qh half_space= coordp= (coordT *)qh_malloc((size_t)qh normal_size + sizeof(coordT)); if (qh CDDinput) { offsetp= qh half_space; normalp= offsetp + 1; @@ -3790,10 +3851,10 @@ coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) { } qh maxline= diminput * (qh_REALdigits + 5); maximize_(qh maxline, 500); - qh line= (char*)qh_malloc((qh maxline+1) * sizeof(char)); + qh line= (char *)qh_malloc((size_t)(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)); + (coordT *)qh_malloc((size_t)((*numpoints)*(*dimension))*sizeof(coordT)); if (!coords || !qh line || (qh HALFspace && !qh half_space)) { qh_fprintf(qh ferr, 6076, "qhull error: insufficient memory to read %d points\n", numinput); @@ -3894,19 +3955,29 @@ coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) { } isfirst= False; } + if (qh rbox_command[0]) + qh rbox_command[strlen(qh rbox_command)-1]= '\0'; /* remove \n, previous qh_errexit's display command as two lines */ if (tokcount != maxcount) { newnum= fmin_(numinput, tokcount/diminput); - qh_fprintf(qh ferr, 7073,"\ -qhull warning: instead of %d %d-dimensional points, input contains\n\ -%d points and %d extra coordinates. Line %d is the first\npoint", - numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint); + if (qh ALLOWshort) + qh_fprintf(qh ferr, 7073, "qhull warning: instead of %d points in %d-d, input contains %d points and %d extra coordinates.\n", + numinput, diminput, tokcount/diminput, tokcount % diminput); + else + qh_fprintf(qh ferr, 6410, "qhull error: instead of %d points in %d-d, input contains %d points and %d extra coordinates.\n", + numinput, diminput, tokcount/diminput, tokcount % diminput); if (firsttext) - qh_fprintf(qh ferr, 8051, ", line %d is the first comment", firsttext); + qh_fprintf(qh ferr, 8051, " Line %d is the first comment.\n", firsttext); + qh_fprintf(qh ferr, 8033, " Line %d is the first point.\n", firstpoint); if (firstshort) - qh_fprintf(qh ferr, 8052, ", line %d is the first short\nline", firstshort); + qh_fprintf(qh ferr, 8052, " Line %d is the first short line.\n", firstshort); if (firstlong) - qh_fprintf(qh ferr, 8053, ", line %d is the first long line", firstlong); - qh_fprintf(qh ferr, 8054, ". Continue with %d points.\n", newnum); + qh_fprintf(qh ferr, 8053, " Line %d is the first long line.\n", firstlong); + if (qh ALLOWshort) + qh_fprintf(qh ferr, 8054, " Continuing with %d points.\n", newnum); + else { + qh_fprintf(qh ferr, 8077, " Override with option 'Qa' (allow-short)\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } numinput= newnum; if (isdelaunay && qh ATinfinity) { for (k= tokcount % diminput; k--; ) @@ -3918,20 +3989,18 @@ qhull warning: instead of %d %d-dimensional points, input contains\n\ } } if (isdelaunay && qh ATinfinity) { - for (k= (*dimension) -1; k--; ) + for (k= (*dimension) - 1; k--; ) infinity[k] /= numinput; if (coords == infinity) coords += (*dimension) -1; else { - for (k=0; k < (*dimension) -1; k++) + for (k=0; k < (*dimension) - 1; k++) *(coords++)= infinity[k]; } *(coords++)= maxboloid * 1.1; } - if (qh rbox_command[0]) { - qh rbox_command[strlen(qh rbox_command)-1]= '\0'; - if (!strcmp(qh rbox_command, "./rbox D4")) - qh_fprintf(qh ferr, 8055, "\n\ + if (!strcmp(qh rbox_command, "./rbox D4")) + qh_fprintf(qh ferr, 8055, "\n\ This is the qhull test case. If any errors or core dumps occur,\n\ recompile qhull with 'make new'. If errors still occur, there is\n\ an incompatibility. You should try a different compiler. You can also\n\ @@ -3939,7 +4008,6 @@ 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) { @@ -3970,12 +4038,10 @@ void qh_setfeasible(int dim) { coordT *coords, value; if (!(s= qh feasible_string)) { - qh_fprintf(qh ferr, 6223, "\ -qhull input error: halfspace intersection needs a feasible point.\n\ -Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n"); + qh_fprintf(qh ferr, 6223, "qhull input error: halfspace intersection needs a feasible point. Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n"); qh_errexit(qh_ERRinput, NULL, NULL); } - if (!(qh feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) { + if (!(qh feasible_point= (pointT *)qh_malloc((size_t)dim * sizeof(coordT)))) { qh_fprintf(qh ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n"); qh_errexit(qh_ERRmem, NULL, NULL); } diff --git a/3rdparty/qhull/io.h b/3rdparty/qhull/io.h index eca0369d3..d08c35c21 100644 --- a/3rdparty/qhull/io.h +++ b/3rdparty/qhull/io.h @@ -6,9 +6,9 @@ see README, libqhull.h and io.c - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/io.h#1 $$Change: 1981 $ - $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/io.h#3 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #ifndef qhDEFio @@ -60,7 +60,7 @@ */ typedef enum { - qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter + qh_RIDGEall= 0, qh_RIDGEinner, qh_RIDGEouter } qh_RIDGE; @@ -77,12 +77,11 @@ typedef void (*printvridgeT)(FILE *fp, vertexT *vertex, vertexT *vertexA, setT * /*============== -prototypes in alphabetical order =========*/ -void qh_dfacet(unsigned id); -void qh_dvertex(unsigned id); +void qh_dfacet(unsigned int id); +void qh_dvertex(unsigned int id); int qh_compare_facetarea(const void *p1, const void *p2); -int qh_compare_facetmerge(const void *p1, const void *p2); int qh_compare_facetvisit(const void *p1, const void *p2); -int qh_compare_vertexpoint(const void *p1, const void *p2); /* not used, not in libqhull_r.h */ +int qh_compare_nummerge(const void *p1, const void *p2); void qh_copyfilename(char *filename, int size, const char* source, int length); void qh_countfacets(facetT *facetlist, setT *facets, boolT printall, int *numfacetsp, int *numsimplicialp, int *totneighborsp, @@ -127,8 +126,8 @@ void qh_printfacetridges(FILE *fp, facetT *facet); void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2, setT *vertices, realT color[3]); -void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]); +void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); void qh_printpoint(FILE *fp, const char *string, pointT *point); void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id); void qh_printpoint3(FILE *fp, pointT *point); diff --git a/3rdparty/qhull/libqhull.c b/3rdparty/qhull/libqhull.c index 7696a8a9f..97b7f05fa 100644 --- a/3rdparty/qhull/libqhull.c +++ b/3rdparty/qhull/libqhull.c @@ -10,9 +10,9 @@ see qhull_a.h for internal functions - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/libqhull.c#3 $$Change: 2047 $ - $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/libqhull.c#13 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #include "qhull_a.h" @@ -22,7 +22,7 @@ /*--------------------------------- - qh_qhull() + qh_qhull( ) compute DIM3 convex hull of qh.num_points starting at qh.first_point qh contains all global options and variables @@ -67,29 +67,36 @@ void qh_qhull(void) { qh_initbuild(); qh_buildhull(); } - if (!qh STOPpoint && !qh STOPcone) { + if (!qh STOPadd && !qh STOPcone && !qh STOPpoint) { if (qh ZEROall_ok && !qh TESTvneighbors && qh MERGEexact) - qh_checkzero( qh_ALL); + qh_checkzero(qh_ALL); if (qh ZEROall_ok && !qh TESTvneighbors && !qh WAScoplanar) { trace2((qh ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points. Post-merging and check of maxout not needed.\n")); qh DOcheckmax= False; }else { + qh_initmergesets(/* qh.facet_mergeset,degen_mergeset,vertex_mergeset */); if (qh MERGEexact || (qh hull_dim > qh_DIMreduceBuild && qh PREmerge)) qh_postmerge("First post-merge", qh premerge_centrum, qh premerge_cos, - (qh POSTmerge ? False : qh TESTvneighbors)); + (qh POSTmerge ? False : qh TESTvneighbors)); /* calls qh_reducevertices */ else if (!qh POSTmerge && qh TESTvneighbors) qh_postmerge("For testing vertex neighbors", qh premerge_centrum, - qh premerge_cos, True); + qh premerge_cos, True); /* calls qh_test_vneighbors */ if (qh POSTmerge) qh_postmerge("For post-merging", qh postmerge_centrum, qh postmerge_cos, qh TESTvneighbors); - if (qh visible_list == qh facet_list) { /* i.e., merging done */ + if (qh visible_list == qh facet_list) { /* qh_postmerge was called */ qh findbestnew= True; - qh_partitionvisible(/*qh.visible_list*/ !qh_ALL, &numoutside); + qh_partitionvisible(!qh_ALL, &numoutside /* qh.visible_list */); qh findbestnew= False; - qh_deletevisible(/*qh.visible_list*/); - qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + qh_deletevisible(/* qh.visible_list */); /* stops at first !f.visible */ + qh_resetlists(False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); } + qh_all_vertexmerges(-1, NULL, NULL); + qh_freemergesets(); + } + if (qh TRACEpoint == qh_IDunknown && qh TRACElevel > qh IStracing) { + qh IStracing= qh TRACElevel; + qh_fprintf(qh ferr, 2112, "qh_qhull: finished qh_buildhull and qh_postmerge, start tracing (TP-1)\n"); } if (qh DOcheckmax){ if (qh REPORTfreq) { @@ -102,7 +109,7 @@ void qh_qhull(void) { qh_nearcoplanar(); } if (qh_setsize(qhmem.tempstack) != 0) { - qh_fprintf(qh ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n", + qh_fprintf(qh ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d) at end of Qhull\n", qh_setsize(qhmem.tempstack)); qh_errexit(qh_ERRqhull, NULL, NULL); } @@ -128,21 +135,24 @@ void qh_qhull(void) { returns: returns False if user requested an early termination - qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined + 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: + called from qh_initbuild, qh_buildhull, and qh_addpoint + tail recursive call if merged a pinchedvertex due to a duplicated ridge + no more than qh.num_vertices calls (QH6296) 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 + if called from a user application after qh_qhull and 'QJ' (joggle), + facet merging for precision problems is disabled by default design: + exit if qh.STOPadd vertices 'TAn' add point to other_points if needed if checkdist if point not above facet @@ -150,11 +160,9 @@ void qh_qhull(void) { 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 + build cone of new facets to the horizon + exit if build cone fails due to qh.ONLYgood + tail recursive call if build cone fails due to pinched vertices exit if STOPcone requested merge non-convex new facets if merge found, many merges, or 'Qf' @@ -166,12 +174,11 @@ void qh_qhull(void) { reset working lists of facets and vertices */ boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) { - int goodvisible, goodhorizon; - vertexT *vertex; - facetT *newfacet; - realT dist, newbalance, pbalance; + realT dist, pbalance; + facetT *replacefacet, *newfacet; + vertexT *apex; boolT isoutside= False; - int numpart, numpoints, numnew, firstnew; + int numpart, numpoints, goodvisible, goodhorizon, apexpointid; qh maxoutdone= False; if (qh_pointid(furthest) == qh_IDunknown) @@ -180,6 +187,7 @@ boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) { qh_fprintf(qh ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n"); qh_errexit(qh_ERRqhull, NULL, NULL); } + qh_detmaxoutside(); if (checkdist) { facet= qh_findbest(furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper, &dist, &isoutside, &numpart); @@ -187,7 +195,7 @@ boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) { if (!isoutside) { zinc_(Znotmax); /* last point of outsideset is no longer furthest. */ facet->notfurthest= True; - qh_partitioncoplanar(furthest, facet, &dist); + qh_partitioncoplanar(furthest, facet, &dist, qh findbestnew); return True; } } @@ -197,44 +205,47 @@ boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) { return False; } qh_findhorizon(furthest, facet, &goodvisible, &goodhorizon); - if (qh ONLYgood && !(goodvisible+goodhorizon) && !qh GOODclosest) { + if (qh ONLYgood && !qh GOODclosest && !(goodvisible+goodhorizon)) { zinc_(Znotgood); facet->notfurthest= True; /* last point of outsideset is no longer furthest. This is ok - since all points of the outside are likely to be bad */ - qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + since all points of the outside are likely to be bad */ + qh_resetlists(False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); return True; } - zzinc_(Zprocessed); - firstnew= qh facet_id; - vertex= qh_makenewfacets(furthest /*visible_list, attaches if !ONLYgood */); - qh_makenewplanes(/* newfacet_list */); - numnew= qh facet_id - firstnew; - newbalance= numnew - (realT) (qh num_facets-qh num_visible) - * qh hull_dim/qh num_vertices; - wadd_(Wnewbalance, newbalance); - wadd_(Wnewbalance2, newbalance * newbalance); - if (qh ONLYgood - && !qh_findgood(qh newfacet_list, goodhorizon) && !qh GOODclosest) { - FORALLnew_facets - qh_delfacet(newfacet); - qh_delvertex(vertex); - qh_resetlists(True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); - zinc_(Znotgoodnew); - facet->notfurthest= True; - return True; + apex= qh_buildcone(furthest, facet, goodhorizon, &replacefacet); + /* qh.newfacet_list, visible_list, newvertex_list */ + if (!apex) { + if (qh ONLYgood) + return True; /* ignore this furthest point, a good new facet was not found */ + if (replacefacet) { + if (qh retry_addpoint++ >= qh num_vertices) { + qh_fprintf(qh ferr, 6296, "qhull internal error (qh_addpoint): infinite loop (%d retries) of merging pinched vertices due to dupridge for point p%d, facet f%d, and %d vertices\n", + qh retry_addpoint, qh_pointid(furthest), facet->id, qh num_vertices); + qh_errexit(qh_ERRqhull, facet, NULL); + } + /* retry qh_addpoint after resolving a dupridge via qh_merge_pinchedvertices */ + return qh_addpoint(furthest, replacefacet, True /* checkdisk */); + } + qh retry_addpoint= 0; + return True; /* ignore this furthest point, resolved a dupridge by making furthest a coplanar point */ } - if (qh ONLYgood) - qh_attachnewfacets(/*visible_list*/); - qh_matchnewfacets(); - qh_updatevertices(); + if (qh retry_addpoint) { + zinc_(Zretryadd); + zadd_(Zretryaddtot, qh retry_addpoint); + zmax_(Zretryaddmax, qh retry_addpoint); + qh retry_addpoint= 0; + } + apexpointid= qh_pointid(apex->point); + zzinc_(Zprocessed); if (qh STOPcone && qh furthest_id == qh STOPcone-1) { facet->notfurthest= True; return False; /* visible_list etc. still defined */ } qh findbestnew= False; if (qh PREmerge || qh MERGEexact) { - qh_premerge(vertex, qh premerge_centrum, qh premerge_cos); + qh_initmergesets(/* qh.facet_mergeset,degen_mergeset,vertex_mergeset */); + qh_premerge(apexpointid, qh premerge_centrum, qh premerge_cos /* qh.newfacet_list */); if (qh_USEfindbestnew) qh findbestnew= True; else { @@ -247,7 +258,9 @@ boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) { } }else if (qh BESToutside) qh findbestnew= True; - qh_partitionvisible(/*qh.visible_list*/ !qh_ALL, &numpoints); + if (qh IStracing >= 4) + qh_checkpolygon(qh visible_list); + qh_partitionvisible(!qh_ALL, &numpoints /* qh.visible_list */); qh findbestnew= False; qh findbest_notsharp= False; zinc_(Zpbalance); @@ -255,57 +268,68 @@ boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) { * (qh num_points - qh num_vertices)/qh num_vertices; wadd_(Wpbalance, pbalance); wadd_(Wpbalance2, pbalance * pbalance); - qh_deletevisible(/*qh.visible_list*/); + qh_deletevisible(/* qh.visible_list */); zmax_(Zmaxvertex, qh num_vertices); qh NEWfacets= False; if (qh IStracing >= 4) { - if (qh num_facets < 2000) + if (qh num_facets < 200) qh_printlists(); qh_printfacetlist(qh newfacet_list, NULL, True); qh_checkpolygon(qh facet_list); }else if (qh CHECKfrequently) { - if (qh num_facets < 50) + if (qh num_facets < 1000) qh_checkpolygon(qh facet_list); else qh_checkpolygon(qh newfacet_list); } - if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1) + if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1 && qh_setsize(qh vertex_mergeset) > 0) return False; - qh_resetlists(True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + qh_resetlists(True, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + if (qh facet_mergeset) { + /* vertex merges occur after facet merges (qh_premerge) and qh_resetlists */ + qh_all_vertexmerges(apexpointid, NULL, NULL); + qh_freemergesets(); + } /* qh_triangulate(); to test qh.TRInormals */ - trace2((qh ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n", - qh_pointid(furthest), numnew, newbalance, pbalance)); + if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1) + return False; + trace2((qh ferr, 2056, "qh_addpoint: added p%d to convex hull with point balance %2.2g\n", + qh_pointid(furthest), pbalance)); return True; } /* addpoint */ /*--------------------------------- - qh_build_withrestart() + qh_build_withrestart( ) 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() + it may be moved by qh_joggleinput */ void qh_build_withrestart(void) { int restart; + vertexT *vertex, **vertexp; qh ALLOWrestart= True; while (True) { restart= setjmp(qh restartexit); /* simple statement for CRAY J916 */ - if (restart) { /* only from qh_precision() */ + if (restart) { /* only from qh_joggle_restart() */ + qh last_errcode= qh_ERRnone; zzinc_(Zretry); wmax_(Wretrymax, qh JOGGLEmax); /* QH7078 warns about using 'TCn' with 'QJn' */ qh STOPcone= qh_IDunknown; /* if break from joggle, prevents normal output */ + FOREACHvertex_(qh del_vertices) { + if (vertex->point && !vertex->partitioned) + vertex->partitioned= True; /* avoid error in qh_freebuild -> qh_delvertex */ + } } if (!qh RERUN && qh JOGGLEmax < REALmax/2) { if (qh build_cnt > qh_JOGGLEmaxretry) { - qh_fprintf(qh ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\ - with joggled input. Increase joggle above 'QJ%2.2g'\n\ - or modify qh_JOGGLE... parameters in user.h\n", + qh_fprintf(qh ferr, 6229, "qhull input error: %d attempts to construct a convex hull with joggled input. Increase joggle above 'QJ%2.2g' or modify qh_JOGGLE... parameters in user.h\n", qh build_cnt, qh JOGGLEmax); - qh_errexit(qh_ERRqhull, NULL, NULL); + qh_errexit(qh_ERRinput, NULL, NULL); } if (qh build_cnt && !restart) break; @@ -317,13 +341,13 @@ void qh_build_withrestart(void) { if (!qh qhull_optionsiz) qh qhull_optionsiz= (int)strlen(qh qhull_options); /* WARN64 */ else { - qh qhull_options [qh qhull_optionsiz]= '\0'; + qh qhull_options[qh qhull_optionsiz]= '\0'; qh qhull_optionlen= qh_OPTIONline; /* starts a new line */ } qh_option("_run", &qh build_cnt, NULL); if (qh build_cnt == qh RERUN) { qh IStracing= qh TRACElastrun; /* duplicated from qh_initqhull_globals */ - if (qh TRACEpoint != qh_IDunknown || qh TRACEdist < REALmax/2 || qh TRACEmerge) { + if (qh TRACEpoint != qh_IDnone || qh TRACEdist < REALmax/2 || qh TRACEmerge) { qh TRACElevel= (qh IStracing? qh IStracing : 3); qh IStracing= 0; } @@ -339,10 +363,211 @@ void qh_build_withrestart(void) { qh ALLOWrestart= False; } /* qh_build_withrestart */ +/*--------------------------------- + + qh_buildcone( furthest, facet, goodhorizon, &replacefacet ) + build cone of new facets from furthest to the horizon + goodhorizon is count of good, horizon facets from qh_find_horizon + + returns: + returns apex of cone with qh.newfacet_list and qh.first_newfacet (f.id) + returns NULL if qh.ONLYgood and no good facets + returns NULL and retryfacet if merging pinched vertices will resolve a dupridge + a horizon vertex was nearly adjacent to another vertex + will retry qh_addpoint + returns NULL if resolve a dupridge by making furthest a coplanar point + furthest was nearly adjacent to an existing vertex + updates qh.degen_mergeset (MRGridge) if resolve a dupridge by merging facets + updates qh.newfacet_list, visible_list, newvertex_list + updates qh.facet_list, vertex_list, num_facets, num_vertices + + notes: + called by qh_addpoint + see qh_triangulate, it triangulates non-simplicial facets in post-processing + + design: + make new facets for point to horizon + compute balance statistics + make hyperplanes for point + exit if qh.ONLYgood and not good (qh_buildcone_onlygood) + match neighboring new facets + if dupridges + exit if !qh.IGNOREpinched and dupridge resolved by coplanar furthest + retry qh_buildcone if !qh.IGNOREpinched and dupridge resolved by qh_buildcone_mergepinched + otherwise dupridges resolved by merging facets + update vertex neighbors and delete interior vertices +*/ +vertexT *qh_buildcone(pointT *furthest, facetT *facet, int goodhorizon, facetT **retryfacet) { + vertexT *apex; + realT newbalance; + int numnew; + + *retryfacet= NULL; + qh first_newfacet= qh facet_id; + qh NEWtentative= (qh MERGEpinched || qh ONLYgood); /* cleared by qh_attachnewfacets or qh_resetlists */ + apex= qh_makenewfacets(furthest /* qh.newfacet_list visible_list, attaches new facets if !qh.NEWtentative */); + numnew= (int)(qh facet_id - qh first_newfacet); + newbalance= numnew - (realT)(qh num_facets - qh num_visible) * qh hull_dim / qh num_vertices; + /* newbalance statistics updated below if the new facets are accepted */ + if (qh ONLYgood) { /* qh.MERGEpinched is false by QH6362 */ + if (!qh_buildcone_onlygood(apex, goodhorizon /* qh.newfacet_list */)) { + facet->notfurthest= True; + return NULL; + } + }else if(qh MERGEpinched) { +#ifndef qh_NOmerge + if (qh_buildcone_mergepinched(apex, facet, retryfacet /* qh.newfacet_list */)) + return NULL; +#else + qh_fprintf(qh ferr, 6375, "qhull option error (qh_buildcone): option 'Q14' (qh.MERGEpinched) is not available due to qh_NOmerge\n"); + qh_errexit(qh_ERRinput, NULL, NULL); +#endif + }else { + /* qh_makenewfacets attached new facets to the horizon */ + qh_matchnewfacets(); /* ignore returned value. qh_forcedmerges will merge dupridges if any */ + qh_makenewplanes(/* qh.newfacet_list */); + qh_update_vertexneighbors_cone(); + } + wadd_(Wnewbalance, newbalance); + wadd_(Wnewbalance2, newbalance * newbalance); + trace2((qh ferr, 2067, "qh_buildcone: created %d newfacets for p%d(v%d) new facet balance %2.2g\n", + numnew, qh_pointid(furthest), apex->id, newbalance)); + return apex; +} /* buildcone */ + +#ifndef qh_NOmerge +/*--------------------------------- + + qh_buildcone_mergepinched( apex, facet, maxdupdist, &retryfacet ) + build cone of new facets from furthest to the horizon + maxdupdist>0.0 for merging dupridges (qh_matchdupridge) + + returns: + returns True if merged a pinched vertex and deleted the cone of new facets + if retryfacet is set + a dupridge was resolved by qh_merge_pinchedvertices + retry qh_addpoint + otherwise + apex/furthest was partitioned as a coplanar point + ignore this furthest point + returns False if no dupridges or if dupridges will be resolved by MRGridge + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + + notes: + only called from qh_buildcone with qh.MERGEpinched + + design: + match neighboring new facets + if matching detected dupridges with a wide merge (qh_RATIOtrypinched) + if pinched vertices (i.e., nearly adjacent) + delete the cone of new facets + delete the apex and reset the facet lists + if coplanar, pinched apex + partition the apex as a coplanar point + else + repeatedly merge the nearest pair of pinched vertices and subsequent facet merges + return True + otherwise + MRGridge are better than vertex merge, but may report an error + attach new facets + make hyperplanes for point + update vertex neighbors and delete interior vertices +*/ +boolT qh_buildcone_mergepinched(vertexT *apex, facetT *facet, facetT **retryfacet) { + facetT *newfacet, *nextfacet; + pointT *apexpoint; + coordT maxdupdist; + int apexpointid; + boolT iscoplanar; + + *retryfacet= NULL; + maxdupdist= qh_matchnewfacets(); + if (maxdupdist > qh_RATIOtrypinched * qh ONEmerge) { /* one or more dupridges with a wide merge */ + if (qh IStracing >= 4 && qh num_facets < 1000) + qh_printlists(); + qh_initmergesets(/* qh.facet_mergeset,degen_mergeset,vertex_mergeset */); + if (qh_getpinchedmerges(apex, maxdupdist, &iscoplanar /* qh.newfacet_list, qh.vertex_mergeset */)) { + for (newfacet=qh newfacet_list; newfacet && newfacet->next; newfacet= nextfacet) { + nextfacet= newfacet->next; + qh_delfacet(newfacet); + } + apexpoint= apex->point; + apexpointid= qh_pointid(apexpoint); + qh_delvertex(apex); + qh_resetlists(False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + if (iscoplanar) { + zinc_(Zpinchedapex); + facet->notfurthest= True; + qh_partitioncoplanar(apexpoint, facet, NULL, qh findbestnew); + }else { + qh_all_vertexmerges(apexpointid, facet, retryfacet); + } + qh_freemergesets(); /* errors if not empty */ + return True; + } + /* MRGridge are better than vertex merge, but may report an error */ + qh_freemergesets(); + } + qh_attachnewfacets(/* qh.visible_list */); + qh_makenewplanes(/* qh.newfacet_list */); + qh_update_vertexneighbors_cone(); + return False; +} /* buildcone_mergepinched */ +#endif /* !qh_NOmerge */ + +/*--------------------------------- + + qh_buildcone_onlygood( apex, goodhorizon ) + build cone of good, new facets from apex and its qh.newfacet_list to the horizon + goodhorizon is count of good, horizon facets from qh_find_horizon + + returns: + False if a f.good facet or a qh.GOODclosest facet is not found + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + + notes: + called from qh_buildcone + QH11030 FIX: Review effect of qh.GOODclosest on qh_buildcone_onlygood ('Qg'). qh_findgood preserves old value if didn't find a good facet. See qh_findgood_all for disabling + + design: + make hyperplanes for point + if qh_findgood fails to find a f.good facet or a qh.GOODclosest facet + delete cone of new facets + return NULL (ignores apex) + else + attach cone to horizon + match neighboring new facets +*/ +boolT qh_buildcone_onlygood(vertexT *apex, int goodhorizon) { + facetT *newfacet, *nextfacet; + + qh_makenewplanes(/* qh.newfacet_list */); + if(qh_findgood(qh newfacet_list, goodhorizon) == 0) { + if (!qh GOODclosest) { + for (newfacet=qh newfacet_list; newfacet && newfacet->next; newfacet= nextfacet) { + nextfacet= newfacet->next; + qh_delfacet(newfacet); + } + qh_delvertex(apex); + qh_resetlists(False /*no stats*/, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + zinc_(Znotgoodnew); + /* !good outside points dropped from hull */ + return False; + } + } + qh_attachnewfacets(/* qh.visible_list */); + qh_matchnewfacets(); /* ignore returned value. qh_forcedmerges will merge dupridges if any */ + qh_update_vertexneighbors_cone(); + return True; +} /* buildcone_onlygood */ + /*--------------------------------- - qh_buildhull() + qh_buildhull( ) construct a convex hull by adding outside points one at a time returns: @@ -354,7 +579,7 @@ void qh_build_withrestart(void) { design: check visible facet and newfacet flags - check newlist vertex flags and qh.STOPcone/STOPpoint + check newfacet 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 @@ -376,7 +601,7 @@ void qh_buildhull(void) { } } FORALLvertices { - if (vertex->newlist) { + if (vertex->newfacet) { qh_fprintf(qh ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n", vertex->id); qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex); @@ -393,11 +618,15 @@ void qh_buildhull(void) { qh facet_next= qh facet_list; /* advance facet when processed */ while ((furthest= qh_nextfurthest(&facet))) { qh num_outside--; /* if ONLYmax, furthest may not be outside */ + if (qh STOPadd>0 && (qh num_vertices - qh hull_dim - 1 >= qh STOPadd - 1)) { + trace1((qh ferr, 1059, "qh_buildhull: stop after adding %d vertices\n", qh STOPadd-1)); + return; + } if (!qh_addpoint(furthest, facet, qh ONLYmax)) break; } if (qh NARROWhull) /* move points from outsideset to coplanarset */ - qh_outcoplanar( /* facet_list */ ); + qh_outcoplanar(/* facet_list */ ); if (qh num_outside && !furthest) { qh_fprintf(qh ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh num_outside); qh_errexit(qh_ERRqhull, NULL, NULL); @@ -414,7 +643,7 @@ void qh_buildhull(void) { if !furthest, prints progress message returns: - tracks progress with qh.lastreport + tracks progress with qh.lastreport, lastcpu, lastfacets, lastmerges, lastplanes, lastdist updates qh.furthest_id (-3 if furthest is NULL) also resets visit_id, vertext_visit on wrap around @@ -434,7 +663,7 @@ void qh_buildhull(void) { */ void qh_buildtracing(pointT *furthest, facetT *facet) { realT dist= 0; - float cpu; + double cpu; int total, furthestid; time_t timedata; struct tm *tp; @@ -445,8 +674,8 @@ void qh_buildtracing(pointT *furthest, facetT *facet) { if (!furthest) { time(&timedata); tp= localtime(&timedata); - cpu= (float)qh_CPUclock - (float)qh hulltime; - cpu /= (float)qh_SECticks; + cpu= (double)qh_CPUclock - (double)qh hulltime; + cpu /= (double)qh_SECticks; total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); qh_fprintf(qh ferr, 8118, "\n\ At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\ @@ -456,19 +685,22 @@ At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n return; } furthestid= qh_pointid(furthest); +#ifndef qh_NOtrace if (qh TRACEpoint == furthestid) { + trace1((qh ferr, 1053, "qh_buildtracing: start trace T%d for point TP%d above facet f%d\n", qh TRACElevel, furthestid, facet->id)); qh IStracing= qh TRACElevel; qhmem.IStracing= qh TRACElevel; - }else if (qh TRACEpoint != qh_IDunknown && qh TRACEdist < REALmax/2) { + }else if (qh TRACEpoint != qh_IDnone && qh TRACEdist < REALmax/2) { qh IStracing= 0; qhmem.IStracing= 0; } - if (qh REPORTfreq && (qh facet_id-1 > qh lastreport+qh REPORTfreq)) { +#endif + if (qh REPORTfreq && (qh facet_id-1 > qh lastreport + (unsigned int)qh REPORTfreq)) { qh lastreport= qh facet_id-1; time(&timedata); tp= localtime(&timedata); - cpu= (float)qh_CPUclock - (float)qh hulltime; - cpu /= (float)qh_SECticks; + cpu= (double)qh_CPUclock - (double)qh hulltime; + cpu /= (double)qh_SECticks; total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); zinc_(Zdistio); qh_distplane(furthest, facet, &dist); @@ -480,23 +712,36 @@ At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n 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; + cpu= (double)qh_CPUclock - (double)qh hulltime; + cpu /= (double)qh_SECticks; qh_distplane(furthest, facet, &dist); - qh_fprintf(qh ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs. Previous was p%d.\n", - furthestid, qh vertex_id, qh num_facets, dist, - getid_(facet), qh num_outside+1, cpu, qh furthest_id); + qh_fprintf(qh ferr, 1049, "qh_addpoint: add p%d(v%d) %2.2g above f%d to hull of %d facets, %d merges, %d outside at %4.4g CPU secs. Previous p%d(v%d) delta %4.4g CPU, %d facets, %d merges, %d hyperplanes, %d distplanes, %d retries\n", + furthestid, qh vertex_id, dist, getid_(facet), qh num_facets, zzval_(Ztotmerge), qh num_outside+1, cpu, qh furthest_id, qh vertex_id - 1, + cpu - qh lastcpu, qh num_facets - qh lastfacets, zzval_(Ztotmerge) - qh lastmerges, zzval_(Zsetplane) - qh lastplanes, zzval_(Zdistplane) - qh lastdist, qh retry_addpoint); + qh lastcpu= cpu; + qh lastfacets= qh num_facets; + qh lastmerges= zzval_(Ztotmerge); + qh lastplanes= zzval_(Zsetplane); + qh lastdist= zzval_(Zdistplane); } zmax_(Zvisit2max, (int)qh visit_id/2); - if (qh visit_id > (unsigned) INT_MAX) { /* 31 bits */ + if (qh visit_id > (unsigned int) INT_MAX) { /* 31 bits */ zinc_(Zvisit); + if (!qh_checklists(qh facet_list)) { + qh_fprintf(qh ferr, 6370, "qhull internal error: qh_checklists failed on reset of qh.visit_id %u\n", qh visit_id); + qh_errexit(qh_ERRqhull, NULL, NULL); + } qh visit_id= 0; FORALLfacets facet->visitid= 0; } zmax_(Zvvisit2max, (int)qh vertex_visit/2); - if (qh vertex_visit > (unsigned) INT_MAX) { /* 31 bits */ + if (qh vertex_visit > (unsigned int) INT_MAX) { /* 31 bits */ zinc_(Zvvisit); + if (qh visit_id && !qh_checklists(qh facet_list)) { + qh_fprintf(qh ferr, 6371, "qhull internal error: qh_checklists failed on reset of qh.vertex_visit %u\n", qh vertex_visit); + qh_errexit(qh_ERRqhull, NULL, NULL); + } qh vertex_visit= 0; FORALLvertices vertex->visitid= 0; @@ -519,7 +764,9 @@ At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n normally use qh_errexit() in user.c(reports a facet and a ridge) */ void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet) { - + qh tracefacet= NULL; /* avoid infinite recursion through qh_fprintf */ + qh traceridge= NULL; + qh tracevertex= NULL; qh_errprint("ERRONEOUS", facet, otherfacet, NULL, NULL); qh_errexit(exitcode, NULL, NULL); } /* errexit2 */ @@ -558,7 +805,7 @@ void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhor int numhorizon= 0, coplanar= 0; realT dist; - trace1((qh ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(point),facet->id)); + trace1((qh ferr, 1040, "qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(point),facet->id)); *goodvisible= *goodhorizon= 0; zinc_(Ztotvisible); qh_removefacet(facet); /* visible_list at end of qh facet_list */ @@ -574,7 +821,11 @@ void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhor qh visit_id++; FORALLvisible_facets { if (visible->tricoplanar && !qh TRInormals) { - qh_fprintf(qh ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_fprintf(qh ferr, 6230, "qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh_ERRqhull, visible, NULL); + } + if (qh_setsize(visible->neighbors) == 0) { + qh_fprintf(qh ferr, 6295, "qhull internal error (qh_findhorizon): visible facet f%d does not have neighbors\n", visible->id); qh_errexit(qh_ERRqhull, visible, NULL); } visible->visitid= qh visit_id; @@ -596,10 +847,10 @@ void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhor if (qh IStracing >=4) qh_errprint("visible", neighbor, NULL, NULL, NULL); }else { - if (dist > - qh MAXcoplanar) { - neighbor->coplanar= True; + if (dist >= -qh MAXcoplanar) { + neighbor->coplanarhorizon= True; zzinc_(Zcoplanarhorizon); - qh_precision("coplanar horizon"); + qh_joggle_restart("coplanar horizon"); coplanar++; if (qh MERGING) { if (dist > 0) { @@ -614,7 +865,7 @@ void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhor trace2((qh ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh MINvisible(%2.7g)\n", qh_pointid(point), neighbor->id, dist, qh MINvisible)); }else - neighbor->coplanar= False; + neighbor->coplanarhorizon= False; zinc_(Ztothorizon); numhorizon++; if (neighbor->good) @@ -625,18 +876,36 @@ void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhor } } if (!numhorizon) { - qh_precision("empty horizon"); - qh_fprintf(qh ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\ -QhullPoint p%d was above all facets.\n", qh_pointid(point)); - qh_printfacetlist(qh facet_list, NULL, True); - qh_errexit(qh_ERRprec, NULL, NULL); + qh_joggle_restart("empty horizon"); + qh_fprintf(qh ferr, 6168, "qhull topology error (qh_findhorizon): empty horizon for p%d. It was above all facets.\n", qh_pointid(point)); + if (qh num_facets < 100) { + qh_printfacetlist(qh facet_list, NULL, True); + } + qh_errexit(qh_ERRtopology, NULL, NULL); } trace1((qh ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n", numhorizon, *goodhorizon, qh num_visible, *goodvisible, coplanar)); - if (qh IStracing >= 4 && qh num_facets < 50) + if (qh IStracing >= 4 && qh num_facets < 100) qh_printlists(); } /* findhorizon */ +/*--------------------------------- + + qh_joggle_restart( reason ) + if joggle ('QJn') and not merging, restart on precision and topology errors +*/ +void qh_joggle_restart(const char *reason) { + + if (qh JOGGLEmax < REALmax/2) { + if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) { + trace0((qh ferr, 26, "qh_joggle_restart: qhull restart because of %s\n", reason)); + /* May be called repeatedly if qh ALLOWrestart */ + longjmp(qh restartexit, qh_ERRprec); + } + } +} /* qh_joggle_restart */ + /*--------------------------------- @@ -661,11 +930,17 @@ QhullPoint p%d was above all facets.\n", qh_pointid(point)); */ pointT *qh_nextfurthest(facetT **visible) { facetT *facet; - int size, idx; + int size, idx, loopcount= 0; realT randr, dist; pointT *furthest; while ((facet= qh facet_next) != qh facet_tail) { + if (!facet || loopcount++ > qh num_facets) { + qh_fprintf(qh ferr, 6406, "qhull internal error (qh_nextfurthest): null facet or infinite loop detected for qh.facet_next f%d facet_tail f%d\n", + getid_(facet), getid_(qh facet_tail)); + qh_printlists(); + qh_errexit2(qh_ERRqhull, facet, qh facet_tail); + } if (!facet->outsideset) { qh facet_next= facet->next; continue; @@ -679,7 +954,7 @@ pointT *qh_nextfurthest(facetT **visible) { if (qh NARROWhull) { if (facet->notfurthest) qh_furthestout(facet); - furthest= (pointT*)qh_setlast(facet->outsideset); + furthest= (pointT *)qh_setlast(facet->outsideset); #if qh_COMPUTEfurthest qh_distplane(furthest, facet, &dist); zinc_(Zcomputefurthest); @@ -697,21 +972,22 @@ pointT *qh_nextfurthest(facetT **visible) { facet= qh facet_next; } *visible= facet; - return((pointT*)qh_setdellast(facet->outsideset)); + return ((pointT *)qh_setdellast(facet->outsideset)); } if (qh RANDOMoutside) { - int outcoplanar = 0; + int outcoplanar= 0; if (qh NARROWhull) { FORALLfacets { if (facet == qh facet_next) break; if (facet->outsideset) - outcoplanar += qh_setsize( facet->outsideset); + outcoplanar += qh_setsize(facet->outsideset); } } randr= qh_RANDOMint; randr= randr/(qh_RANDOMmax+1); - idx= (int)floor((qh num_outside - outcoplanar) * randr); + randr= floor((qh num_outside - outcoplanar) * randr); + idx= (int)randr; FORALLfacet_(qh facet_next) { if (facet->outsideset) { SETreturnsize_(facet->outsideset, size); @@ -719,7 +995,7 @@ pointT *qh_nextfurthest(facetT **visible) { qh_setfree(&facet->outsideset); else if (size > idx) { *visible= facet; - return((pointT*)qh_setdelnth(facet->outsideset, idx)); + return ((pointT *)qh_setdelnth(facet->outsideset, idx)); }else idx -= size; } @@ -729,7 +1005,7 @@ pointT *qh_nextfurthest(facetT **visible) { qh_errexit(qh_ERRqhull, NULL, NULL); }else { /* VIRTUALmemory */ facet= qh facet_tail->previous; - if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) { + if (!(furthest= (pointT *)qh_setdellast(facet->outsideset))) { if (facet->outsideset) qh_setfree(&facet->outsideset); qh_removefacet(facet); @@ -841,7 +1117,7 @@ void qh_partitionall(setT *vertices, pointT *points, int numpoints){ } } /* if !qh BESToutside, pointset contains points not assigned to outsideset */ - if (qh BESToutside || qh MERGING || qh KEEPcoplanar || qh KEEPinside) { + if (qh BESToutside || qh MERGING || qh KEEPcoplanar || qh KEEPinside || qh KEEPnearinside) { qh findbestnew= True; FOREACHpoint_i_(pointset) { if (point) @@ -860,18 +1136,19 @@ void qh_partitionall(setT *vertices, pointT *points, int numpoints){ /*--------------------------------- - qh_partitioncoplanar( point, facet, dist ) + qh_partitioncoplanar( point, facet, dist, allnew ) 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, + if allnew (qh.findbestnew) searches new facets instead of using qh_findbest() returns: qh.max_ouside updated if qh.KEEPcoplanar or qh.KEEPinside point assigned to best coplanarset + qh.repart_facetid==0 (for detecting infinite recursion via qh_partitionpoint) notes: facet->maxoutside is updated at end by qh_check_maxout @@ -893,16 +1170,18 @@ void qh_partitionall(setT *vertices, pointT *points, int numpoints){ else update qh.max_outside */ -void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) { +void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist, boolT allnew) { facetT *bestfacet; pointT *oldfurthest; - realT bestdist, dist2= 0, angle; - int numpart= 0, oldfindbest; - boolT isoutside; + realT bestdist, angle, nearest, dist2= 0.0; + int numpart= 0; + boolT isoutside, oldfindbest, repartition= False; + trace4((qh ferr, 4090, "qh_partitioncoplanar: partition coplanar point p%d starting with f%d dist? %2.2g, allnew? %d, gh.repart_facetid f%d\n", + qh_pointid(point), facet->id, (dist ? *dist : 0.0), allnew, qh repart_facetid)); qh WAScoplanar= True; if (!dist) { - if (qh findbestnew) + if (allnew) bestfacet= qh_findbestnew(point, facet, &bestdist, qh_ALL, &isoutside, &numpart); else bestfacet= qh_findbest(point, facet, qh_ALL, !qh_ISnewfacets, qh DELAUNAY, @@ -913,14 +1192,16 @@ void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) { if (qh KEEPnearinside) { if (bestdist < -qh NEARinside) { zinc_(Zcoplanarinside); - trace4((qh ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n", - qh_pointid(point), bestfacet->id, bestdist, qh findbestnew)); + trace4((qh ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g allnew? %d\n", + qh_pointid(point), bestfacet->id, bestdist, allnew)); + qh repart_facetid= 0; return; } }else if (bestdist < -qh MAXcoplanar) { - trace4((qh ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n", - qh_pointid(point), bestfacet->id, bestdist, qh findbestnew)); + trace4((qh ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g allnew? %d\n", + qh_pointid(point), bestfacet->id, bestdist, allnew)); zinc_(Zcoplanarinside); + qh repart_facetid= 0; return; } } @@ -928,32 +1209,72 @@ void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) { bestfacet= facet; bestdist= *dist; } + if(bestfacet->visible){ + qh_fprintf(qh ferr, 6405, "qhull internal error (qh_partitioncoplanar): cannot partition coplanar p%d of f%d into visible facet f%d\n", + qh_pointid(point), facet->id, bestfacet->id); + qh_errexit2(qh_ERRqhull, facet, bestfacet); + } if (bestdist > qh max_outside) { - if (!dist && facet != bestfacet) { + if (!dist && facet != bestfacet) { /* can't be recursive from qh_partitionpoint since facet != bestfacet */ zinc_(Zpartangle); angle= qh_getangle(facet->normal, bestfacet->normal); if (angle < 0) { + nearest= qh_vertex_bestdist(bestfacet->vertices); /* typically due to deleted vertex and coplanar facets, e.g., - RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */ - zinc_(Zpartflip); - trace2((qh ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d. It is above flipped facet f%d dist %2.2g\n", - qh_pointid(point), facet->id, bestfacet->id, bestdist)); - oldfindbest= qh findbestnew; - qh findbestnew= False; - qh_partitionpoint(point, bestfacet); - qh findbestnew= oldfindbest; - return; + RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */ + zinc_(Zpartcorner); + trace2((qh ferr, 2058, "qh_partitioncoplanar: repartition coplanar point p%d from f%d as an outside point above corner facet f%d dist %2.2g with angle %2.2g\n", + qh_pointid(point), facet->id, bestfacet->id, bestdist, angle)); + repartition= True; + } + } + if (!repartition) { + if (bestdist > qh MAXoutside * qh_RATIOcoplanaroutside) { + nearest= qh_vertex_bestdist(bestfacet->vertices); + if (facet->id == bestfacet->id) { + if (facet->id == qh repart_facetid) { + qh_fprintf(qh ferr, 6404, "Qhull internal error (qh_partitioncoplanar): infinite loop due to recursive call to qh_partitionpoint. Repartition point p%d from f%d as a outside point dist %2.2g nearest vertices %2.2g\n", + qh_pointid(point), facet->id, bestdist, nearest); + qh_errexit(qh_ERRqhull, facet, NULL); + } + qh repart_facetid= facet->id; /* reset after call to qh_partitionpoint */ + } + if (point == qh coplanar_apex) { + /* otherwise may loop indefinitely, the point is well above a facet, yet near a vertex */ + qh_fprintf(qh ferr, 6425, "Qhull topology error (qh_partitioncoplanar): can not repartition coplanar point p%d from f%d as outside point above f%d. It previously failed to form a cone of facets, dist %2.2g, nearest vertices %2.2g\n", + qh_pointid(point), facet->id, bestfacet->id, bestdist, nearest); + qh_errexit(qh_ERRtopology, facet, NULL); + } + if (nearest < 2 * qh MAXoutside * qh_RATIOcoplanaroutside) { + zinc_(Zparttwisted); + qh_fprintf(qh ferr, 7085, "Qhull precision warning: repartition coplanar point p%d from f%d as an outside point above twisted facet f%d dist %2.2g nearest vertices %2.2g\n", + qh_pointid(point), facet->id, bestfacet->id, bestdist, nearest); + }else { + zinc_(Zparthidden); + qh_fprintf(qh ferr, 7086, "Qhull precision warning: repartition coplanar point p%d from f%d as an outside point above hidden facet f%d dist %2.2g nearest vertices %2.2g\n", + qh_pointid(point), facet->id, bestfacet->id, bestdist, nearest); + } + repartition= True; } } + if (repartition) { + oldfindbest= qh findbestnew; + qh findbestnew= False; + qh_partitionpoint(point, bestfacet); + qh findbestnew= oldfindbest; + qh repart_facetid= 0; + return; + } + qh repart_facetid= 0; qh max_outside= bestdist; - if (bestdist > qh TRACEdist) { - qh_fprintf(qh ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n", + if (bestdist > qh TRACEdist || qh IStracing >= 3) { + qh_fprintf(qh ferr, 3041, "qh_partitioncoplanar: == p%d from f%d increases qh.max_outside to %2.2g of f%d last p%d\n", qh_pointid(point), facet->id, bestdist, bestfacet->id, qh furthest_id); qh_errprint("DISTANT", facet, bestfacet, NULL, NULL); } } if (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside) { - oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset); + oldfurthest= (pointT *)qh_setlast(bestfacet->coplanarset); if (oldfurthest) { zinc_(Zcomputefurthest); qh_distplane(oldfurthest, bestfacet, &dist2); @@ -963,7 +1284,7 @@ void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) { else qh_setappend2ndlast(&bestfacet->coplanarset, point); } - trace4((qh ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n", + trace4((qh ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d (or inside) dist %2.2g\n", qh_pointid(point), bestfacet->id, bestdist)); } /* partitioncoplanar */ @@ -1003,13 +1324,10 @@ void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) { partition as coplanar point into bestfacet */ void qh_partitionpoint(pointT *point, facetT *facet) { - realT bestdist; - boolT isoutside; + realT bestdist, previousdist; + boolT isoutside, isnewoutside= False; facetT *bestfacet; int numpart; -#if qh_COMPUTEfurthest - realT dist; -#endif if (qh findbestnew) bestfacet= qh_findbestnew(point, facet, &bestdist, qh BESToutside, &isoutside, &numpart); @@ -1018,9 +1336,14 @@ void qh_partitionpoint(pointT *point, facetT *facet) { &bestdist, &isoutside, &numpart); zinc_(Ztotpartition); zzadd_(Zpartition, numpart); + if(bestfacet->visible){ + qh_fprintf(qh ferr, 6293, "qhull internal error (qh_partitionpoint): cannot partition p%d of f%d into visible facet f%d\n", + qh_pointid(point), facet->id, bestfacet->id); + qh_errexit2(qh_ERRqhull, facet, bestfacet); + } if (qh NARROWhull) { if (qh DELAUNAY && !isoutside && bestdist >= -qh MAXcoplanar) - qh_precision("nearly incident point(narrow hull)"); + qh_joggle_restart("nearly incident point (narrow hull)"); if (qh KEEPnearinside) { if (bestdist >= -qh NEARinside) isoutside= True; @@ -1030,53 +1353,67 @@ void qh_partitionpoint(pointT *point, facetT *facet) { if (isoutside) { if (!bestfacet->outsideset - || !qh_setlast(bestfacet->outsideset)) { + || !qh_setlast(bestfacet->outsideset)) { /* empty outside set */ qh_setappend(&(bestfacet->outsideset), point); - if (!bestfacet->newfacet) { - qh_removefacet(bestfacet); /* make sure it's after qh facet_next */ - qh_appendfacet(bestfacet); - } + if (!qh NARROWhull || bestdist > qh MINoutside) + isnewoutside= True; #if !qh_COMPUTEfurthest bestfacet->furthestdist= bestdist; #endif }else { #if qh_COMPUTEfurthest zinc_(Zcomputefurthest); - qh_distplane(oldfurthest, bestfacet, &dist); - if (dist < bestdist) + qh_distplane(oldfurthest, bestfacet, &previousdist); + if (previousdist < bestdist) qh_setappend(&(bestfacet->outsideset), point); else qh_setappend2ndlast(&(bestfacet->outsideset), point); #else - if (bestfacet->furthestdist < bestdist) { + previousdist= bestfacet->furthestdist; + if (previousdist < bestdist) { qh_setappend(&(bestfacet->outsideset), point); bestfacet->furthestdist= bestdist; + if (qh NARROWhull && previousdist < qh MINoutside && bestdist >= qh MINoutside) + isnewoutside= True; }else qh_setappend2ndlast(&(bestfacet->outsideset), point); #endif } + if (isnewoutside && qh facet_next != bestfacet) { + if (bestfacet->newfacet) { + if (qh facet_next->newfacet) + qh facet_next= qh newfacet_list; /* make sure it's after qh.facet_next */ + }else { + qh_removefacet(bestfacet); /* make sure it's after qh.facet_next */ + qh_appendfacet(bestfacet); + if(qh newfacet_list){ + bestfacet->newfacet= True; + } + } + } qh num_outside++; - trace4((qh ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n", - qh_pointid(point), bestfacet->id, bestfacet->newfacet)); + trace4((qh ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d newfacet? %d, newoutside? %d (or narrowhull)\n", + qh_pointid(point), bestfacet->id, bestfacet->newfacet, isnewoutside)); }else if (qh DELAUNAY || bestdist >= -qh MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */ - zzinc_(Zcoplanarpart); if (qh DELAUNAY) - qh_precision("nearly incident point"); + qh_joggle_restart("nearly incident point"); + /* allow coplanar points with joggle, may be interior */ + zzinc_(Zcoplanarpart); if ((qh KEEPcoplanar + qh KEEPnearinside) || bestdist > qh max_outside) - qh_partitioncoplanar(point, bestfacet, &bestdist); + qh_partitioncoplanar(point, bestfacet, &bestdist, qh findbestnew); else { trace4((qh ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n", qh_pointid(point), bestfacet->id)); } - }else if (qh KEEPnearinside && bestdist > -qh NEARinside) { + }else if (qh KEEPnearinside && bestdist >= -qh NEARinside) { zinc_(Zpartnear); - qh_partitioncoplanar(point, bestfacet, &bestdist); + qh_partitioncoplanar(point, bestfacet, &bestdist, qh findbestnew); }else { zinc_(Zpartinside); trace4((qh ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n", qh_pointid(point), bestfacet->id, bestdist)); if (qh KEEPinside) - qh_partitioncoplanar(point, bestfacet, &bestdist); + qh_partitioncoplanar(point, bestfacet, &bestdist, qh findbestnew); } } /* partitionpoint */ @@ -1084,18 +1421,18 @@ void qh_partitionpoint(pointT *point, facetT *facet) { >-------------------------------- qh_partitionvisible( allpoints, numoutside ) - partitions points in visible facets to qh.newfacet_list - qh.visible_list= visible facets - for visible facets - 1st neighbor (if any) points to a horizon facet or a new facet - if allpoints(!used), - repartitions coplanar points + partitions outside points in visible facets (qh.visible_list) to qh.newfacet_list + if keeping coplanar/near-inside/inside points + partitions coplanar points; repartitions if 'allpoints' (not used) + 1st neighbor (if any) of visible facets points to a horizon facet or a new facet returns: updates outside sets and coplanar sets of qh.newfacet_list updates qh.num_outside (count of outside points) + does not truncate f.outsideset, f.coplanarset, or qh.del_vertices (see qh_deletevisible) notes: + called by qh_qhull, qh_addpoint, and qh_all_vertexmerges qh.findbest_notsharp should be clear (extra work if set) design: @@ -1114,31 +1451,26 @@ void qh_partitionpoint(pointT *point, facetT *facet) { else partition vertex into coplanar sets of new facets */ -void qh_partitionvisible(/*qh.visible_list*/ boolT allpoints, int *numoutside) { +void qh_partitionvisible(boolT allpoints, int *numoutside /* qh.visible_list */) { facetT *visible, *newfacet; pointT *point, **pointp; - int coplanar=0, size; - unsigned count; + int delsize, coplanar=0, size; vertexT *vertex, **vertexp; + trace3((qh ferr, 3042, "qh_partitionvisible: partition outside and coplanar points of visible and merged facets f%d into new facets f%d\n", + qh visible_list->id, qh newfacet_list->id)); if (qh ONLYmax) maximize_(qh MINoutside, qh max_vertex); *numoutside= 0; FORALLvisible_facets { if (!visible->outsideset && !visible->coplanarset) continue; - newfacet= visible->f.replace; - count= 0; - while (newfacet && newfacet->visible) { - newfacet= newfacet->f.replace; - if (count++ > qh facet_id) - qh_infiniteloop(visible); - } + newfacet= qh_getreplacement(visible); if (!newfacet) newfacet= qh newfacet_list; - if (newfacet == qh facet_tail) { - qh_fprintf(qh ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n"); - qh_errexit(qh_ERRprec, NULL, NULL); + if (!newfacet->next) { + qh_fprintf(qh ferr, 6170, "qhull topology error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n"); + qh_errexit(qh_ERRtopology, NULL, NULL); } if (visible->outsideset) { size= qh_setsize(visible->outsideset); @@ -1154,40 +1486,33 @@ void qh_partitionvisible(/*qh.visible_list*/ boolT allpoints, int *numoutside) { if (allpoints) /* not used */ qh_partitionpoint(point, newfacet); else - qh_partitioncoplanar(point, newfacet, NULL); + qh_partitioncoplanar(point, newfacet, NULL, qh findbestnew); } } } - FOREACHvertex_(qh del_vertices) { - if (vertex->point) { - if (allpoints) /* not used */ - qh_partitionpoint(vertex->point, qh newfacet_list); - else - qh_partitioncoplanar(vertex->point, qh newfacet_list, NULL); + delsize= qh_setsize(qh del_vertices); + if (delsize > 0) { + trace3((qh ferr, 3049, "qh_partitionvisible: partition %d deleted vertices as coplanar? %d points into new facets f%d\n", + delsize, !allpoints, qh newfacet_list->id)); + FOREACHvertex_(qh del_vertices) { + if (vertex->point && !vertex->partitioned) { + if (!qh newfacet_list || qh newfacet_list == qh facet_tail) { + qh_fprintf(qh ferr, 6284, "qhull internal error (qh_partitionvisible): all new facets deleted or none defined. Can not partition deleted v%d.\n", vertex->id); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (allpoints) /* not used */ + /* [apr'2019] infinite loop if vertex recreates the same facets from the same horizon + e.g., qh_partitionpoint if qh.DELAUNAY with qh.MERGEindependent for all mergetype, ../eg/qtest.sh t427764 '1000 s W1e-13 D3' 'd' */ + qh_partitionpoint(vertex->point, qh newfacet_list); + else + qh_partitioncoplanar(vertex->point, qh newfacet_list, NULL, qh_ALL); /* search all new facets */ + vertex->partitioned= True; + } } } - trace1((qh ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar)); + trace1((qh ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets, %d points from coplanarsets, and %d deleted vertices\n", *numoutside, coplanar, delsize)); } /* partitionvisible */ - - -/*--------------------------------- - - qh_precision( reason ) - restart on precision errors if not merging and if 'QJn' -*/ -void qh_precision(const char *reason) { - - if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) { - if (qh JOGGLEmax < REALmax/2) { - trace0((qh ferr, 26, "qh_precision: qhull restart because of %s\n", reason)); - /* May be called repeatedly if qh->ALLOWrestart */ - longjmp(qh restartexit, qh_ERRprec); - } - } -} /* qh_precision */ - /*--------------------------------- @@ -1197,6 +1522,7 @@ void qh_precision(const char *reason) { notes: not in io.c so that user_eg.c can prevent io.c from loading qh_printsummary and qh_countfacets must match counts + updates qh.facet_visit to detect infinite loop design: determine number of points, vertices, and coplanar points @@ -1204,20 +1530,40 @@ void qh_precision(const char *reason) { */ void qh_printsummary(FILE *fp) { realT ratio, outerplane, innerplane; - float cpu; - int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0; - int goodused; + double cpu; + int size, id, nummerged, numpinched, numvertices, numcoplanars= 0, nonsimplicial=0, numdelaunay= 0; facetT *facet; const char *s; int numdel= zzval_(Zdelvertextot); int numtricoplanars= 0; + boolT goodused; size= qh num_points + qh_setsize(qh other_points); numvertices= qh num_vertices - qh_setsize(qh del_vertices); id= qh_pointid(qh GOODpointp); + if (!qh_checklists(qh facet_list) && !qh ERREXITcalled) { + qh_fprintf(fp, 6372, "qhull internal error: qh_checklists failed at qh_printsummary\n"); + if (qh num_facets < 4000) + qh_printlists(); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (qh DELAUNAY && qh ERREXITcalled) { + /* update f.good and determine qh.num_good as in qh_findgood_all */ + FORALLfacets { + if (facet->visible) + facet->good= False; /* will be deleted */ + else if (facet->good) { + if (facet->normal && !qh_inthresholds(facet->normal, NULL)) + facet->good= False; + else + numdelaunay++; + } + } + qh num_good= numdelaunay; + } FORALLfacets { if (facet->coplanarset) - numcoplanars += qh_setsize( facet->coplanarset); + numcoplanars += qh_setsize(facet->coplanarset); if (facet->good) { if (facet->simplicial) { if (facet->keepcentrum && facet->tricoplanar) @@ -1228,14 +1574,19 @@ void qh_printsummary(FILE *fp) { } if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id) size--; - if (qh STOPcone || qh STOPpoint) - qh_fprintf(fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'."); - if (qh UPPERdelaunay) - goodused= qh GOODvertex + qh GOODpoint + qh SPLITthresholds; - else if (qh DELAUNAY) - goodused= qh GOODvertex + qh GOODpoint + qh GOODthreshold; - else - goodused= qh num_good; + if (qh STOPadd || qh STOPcone || qh STOPpoint) + qh_fprintf(fp, 9288, "\nEarly exit due to 'TAn', 'TVn', 'TCn', 'TRn', or precision error with 'QJn'."); + goodused= False; + if (qh ERREXITcalled) + ; /* qh_findgood_all not called */ + else if (qh UPPERdelaunay) { + if (qh GOODvertex || qh GOODpoint || qh SPLITthresholds) + goodused= True; + }else if (qh DELAUNAY) { + if (qh GOODvertex || qh GOODpoint || qh GOODthreshold) + goodused= True; + }else if (qh num_good > 0 || qh GOODthreshold) + goodused= True; nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); if (qh VORONOI) { if (qh UPPERdelaunay) @@ -1347,14 +1698,16 @@ Convex hull of %d points in %d-d:\n\n", size, qh hull_dim); #endif if (nummerged) { qh_fprintf(fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+ - zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+ - zzval_(Zdistzero)); - qh_fprintf(fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart)); + zzval_(Zcentrumtests)+zzval_(Zvertextests)+zzval_(Zdistcheck)+zzval_(Zdistzero)); + qh_fprintf(fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart)+zzval_(Zdistconvex)); qh_fprintf(fp, 9332," Number of merged facets: %d\n", nummerged); } + numpinched= zzval_(Zpinchduplicate) + zzval_(Zpinchedvertex); + if (numpinched) + qh_fprintf(fp, 9375," Number of merged pinched vertices: %d\n", numpinched); if (!qh RANDOMoutside && qh QHULLfinished) { - cpu= (float)qh hulltime; - cpu /= (float)qh_SECticks; + cpu= (double)qh hulltime; + cpu /= (double)qh_SECticks; wval_(Wcpu)= cpu; qh_fprintf(fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu); } @@ -1378,8 +1731,7 @@ Convex hull of %d points in %d-d:\n\n", size, qh hull_dim); if (qh MERGING) { qh_outerinner(NULL, &outerplane, &innerplane); if (outerplane > 2 * qh DISTround) { - qh_fprintf(fp, 9339, " Maximum distance of %spoint above facet: %2.2g", - (qh QHULLfinished ? "" : "merged "), outerplane); + qh_fprintf(fp, 9339, " Maximum distance of point above facet: %2.2g", 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) @@ -1388,8 +1740,7 @@ Convex hull of %d points in %d-d:\n\n", size, qh hull_dim); qh_fprintf(fp, 9341, "\n"); } if (innerplane < -2 * qh DISTround) { - qh_fprintf(fp, 9342, " Maximum distance of %svertex below facet: %2.2g", - (qh QHULLfinished ? "" : "merged "), innerplane); + qh_fprintf(fp, 9342, " Maximum distance of vertex below facet: %2.2g", innerplane); ratio= -innerplane/(qh ONEmerge+qh DISTround); if (ratio > 0.05 && qh JOGGLEmax > REALmax/2) qh_fprintf(fp, 9343, " (%.1fx)\n", ratio); diff --git a/3rdparty/qhull/libqhull.h b/3rdparty/qhull/libqhull.h index 677085808..90c0519bb 100644 --- a/3rdparty/qhull/libqhull.h +++ b/3rdparty/qhull/libqhull.h @@ -6,9 +6,9 @@ see qh-qhull.htm, qhull_a.h - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/libqhull.h#7 $$Change: 2066 $ - $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/libqhull.h#11 $$Change: 2958 $ + $DateTime: 2020/05/26 16:17:49 $$Author: bbarber $ NOTE: access to qh_qh is via the 'qh' macro. This allows qh_qh to be either a pointer or a structure. An example @@ -31,19 +31,20 @@ /*=========================== -included files ==============*/ -/* user_r.h first for QHULL_CRTDBG */ -#include "user.h" /* user definable constants (e.g., qh_QHpointer) */ +/* user.h first for QHULL_CRTDBG */ +#include "user.h" /* user definable constants (e.g., realT). */ -#include "mem.h" /* Needed qhT in libqhull_r.h. Here for compatibility */ +#include "mem.h" /* Needed for qhT in libqhull_r.h. Here for compatibility */ #include "qset.h" /* Needed for QHULL_LIB_CHECK */ -/* include stat_r.h after defining boolT. Needed for qhT in libqhull_r.h. Here for compatibility and statT */ +/* include stat.h after defining boolT. Needed for qhT in libqhull_r.h. Here for compatibility */ #include #include +#include #include #include -#if __MWERKS__ && __POWERPC__ +#if defined(__MWERKS__) && defined(__POWERPC__) #include #include #include @@ -51,7 +52,7 @@ #ifndef __STDC__ #ifndef __cplusplus -#if !_MSC_VER +#if !defined(_MSC_VER) #error Neither __STDC__ nor __cplusplus is defined. Please use strict ANSI C or C++ to compile #error Qhull. You may need to turn off compiler extensions in your project configuration. If #error your compiler is a standard C compiler, you can delete this warning from libqhull.h @@ -96,7 +97,7 @@ extern const char qh_version2[]; /* defined in global.c */ #define pointT coordT typedef enum { - qh_IDnone = -3, qh_IDinterior = -2, qh_IDunknown = -1 + qh_IDnone= -3, qh_IDinterior= -2, qh_IDunknown= -1 } qh_pointT; @@ -130,7 +131,7 @@ qh_pointT; #define qh_False 0 #define qh_True 1 -#include "stat.h" /* after define of boolT */ +#include "stat.h" /* needs boolT */ /*---------------------------------- @@ -140,7 +141,7 @@ qh_pointT; */ typedef enum { - qh_ASnone = 0, /* If not MERGING and not VORONOI */ + 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) */ } @@ -178,24 +179,28 @@ typedef enum {qh_PRINTnone= 0, 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 */ +#define qh_NOupper True /* argument for qh_findbest */ +#define qh_IScheckmax True /* argument for qh_findbesthorizon */ +#define qh_ISnewfacets True /* argument for qh_findbest */ +#define qh_RESETvisible True /* argument for qh_resetlists */ /*---------------------------------- - qh_ERR - Qhull exit codes, for indicating errors - See: MSG_ERROR and MSG_WARNING [user.h] + qh_ERR... + Qhull exit status codes, for indicating errors + See: MSG_ERROR (6000) and MSG_WARNING (7000) [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_ERRsingular 2 /* singular input data, calls qh_printhelp_singular */ +#define qh_ERRprec 3 /* precision error, calls qh_printhelp_degenerate */ #define qh_ERRmem 4 /* insufficient memory, matches mem.h */ -#define qh_ERRqhull 5 /* internal error detected, matches mem.h */ +#define qh_ERRqhull 5 /* internal error detected, matches mem.h, calls qh_printhelp_internal */ +#define qh_ERRother 6 /* other error detected */ +#define qh_ERRtopology 7 /* topology error, maybe due to nearly adjacent vertices, calls qh_printhelp_topology */ +#define qh_ERRwide 8 /* wide facet error, maybe due to nearly adjacent vertices, calls qh_printhelp_wide */ +#define qh_ERRdebug 9 /* qh_errexit from debugging code */ /*---------------------------------- @@ -204,23 +209,24 @@ qh_FILEstderr Fake stderr to distinguish error output from normal output For C++ interface. Must redefine qh_fprintf_qhull */ -#define qh_FILEstderr ((FILE*)1) +#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 - DEFsetT is likewise defined in - mem.h and qset.h + + DEFsetT is likewise defined in mem.h and qset.h */ typedef struct vertexT vertexT; typedef struct ridgeT ridgeT; typedef struct facetT facetT; +typedef struct qhT qhT; #ifndef DEFsetT #define DEFsetT 1 -typedef struct setT setT; /* defined in qset.h */ +typedef struct setT setT; /* defined in qset.h */ #endif /*-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, + realT area; /* area of facet, only in io.c if f.isarea */ + facetT *replace; /* replacement facet for qh.NEWfacets with f.visible + NULL if qh_mergedegen_redundant, interior, or !NEWfacets */ + 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() */ @@ -290,34 +297,42 @@ struct facetT { /* 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 */ + facetT *previous; /* previous facet in the facet_list or NULL, for C++ interface */ + facetT *next; /* next facet in the facet_list or facet_tail */ 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*/ + if simplicial, 1st vertex was apex/furthest + qh_reduce_vertices removes extraneous vertices via qh_remove_extravertices + if f.visible, vertices may be on qh.del_vertices */ + setT *ridges; /* explicit ridges for nonsimplicial facets or nonsimplicial neighbors. + For simplicial facets, neighbors define the ridges + qh_makeridges() converts simplicial facets by creating ridges prior to merging + If qh.NEWtentative, new facets have horizon ridge, but not vice versa + if f.visible && qh.NEWfacets, ridges is empty */ + setT *neighbors; /* neighbors of the facet. Neighbors may be f.visible + If simplicial, the kth neighbor is opposite the kth vertex and the + first neighbor is the horizon facet for the first vertex. + dupridges marked by qh_DUPLICATEridge (0x01) and qh_MERGEridge (0x02) + if f.visible && qh.NEWfacets, neighbors is empty */ setT *outsideset; /* set of points outside this facet if non-empty, last point is furthest - if NARROWhull, includes coplanars for partitioning*/ + if NARROWhull, includes coplanars (less than qh.MINoutside) for partitioning*/ setT *coplanarset; /* set of points coplanar with this facet - > qh.min_vertex and <= facet->max_outside + >= 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, + unsigned int visitid; /* visit_id, for visiting all neighbors, all uses are independent */ - unsigned id; /* unique identifier from qh.facet_id */ - unsigned nummerge:9; /* number of merges */ -#define qh_MAXnummerge 511 /* 2^9-1, 32 flags total, see "flags:" in io.c */ + unsigned int id; /* unique identifier from qh.facet_id, 1..qh.facet_id, 0 is sentinel, printed as 'f%d' */ + unsigned int nummerge:9; /* number of merges */ +#define qh_MAXnummerge 511 /* 2^9-1 */ + /* 23 flags (at most 23 due to nummerge), printed by "flags:" in io.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 newfacet:1; /* True if facet on qh.newfacet_list (new/qh.first_newfacet 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 */ @@ -326,25 +341,29 @@ struct facetT { 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*/ + flagT notfurthest:1; /* True if last point of outsideset is not furthest */ /*-------- flags primarily for output ---------*/ flagT good:1; /* True if a facet marked good for output */ flagT isarea:1; /* True if facet->f.area is defined */ /*-------- flags for merging ------------------*/ - flagT dupridge:1; /* True if duplicate ridge in facet */ - flagT mergeridge:1; /* True if facet or neighbor contains a qh_MERGEridge - ->normal defined (also defined for mergeridge2) */ - flagT mergeridge2:1; /* True if neighbor contains a qh_MERGEridge (mark_dupridges */ - flagT coplanar:1; /* True if horizon facet is coplanar at last use */ - flagT mergehorizon:1; /* True if will merge into horizon (->coplanar) */ + flagT dupridge:1; /* True if facet has one or more dupridge in a new facet (qh_matchneighbor), + a dupridge has a subridge shared by more than one new facet */ + flagT mergeridge:1; /* True if facet or neighbor has a qh_MERGEridge (qh_mark_dupridges) + ->normal defined for mergeridge and mergeridge2 */ + flagT mergeridge2:1; /* True if neighbor has a qh_MERGEridge (qh_mark_dupridges) */ + flagT coplanarhorizon:1; /* True if horizon facet is coplanar at last use */ + flagT mergehorizon:1; /* True if will merge into horizon (its first neighbor w/ f.coplanarhorizon). */ 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 keepcentrum:1; /* True if keep old centrum after a merge, or marks owner for ->tricoplanar + Set by qh_updatetested if more than qh_MAXnewcentrum extra vertices + Set by qh_mergefacet if |maxdist| > qh.WIDEfacet */ 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) */ + flagT redundant:1; /* True if facet is redundant (degen_mergeset) + Maybe merge degenerate and redundant to gain another flag */ }; @@ -371,13 +390,21 @@ struct facetT { 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 */ + facetT *top; /* top facet for this ridge */ + facetT *bottom; /* bottom facet for this ridge + ridge oriented by odd/even vertex order and top/bottom */ + unsigned int id; /* unique identifier. Same size as vertex_id, printed as 'r%d' */ flagT seen:1; /* used to perform operations only once */ - flagT tested:1; /* True when ridge is tested for convexity */ + flagT tested:1; /* True when ridge is tested for convexity by centrum or opposite vertices */ flagT nonconvex:1; /* True if getmergeset detected a non-convex neighbor only one ridge between neighbors may have nonconvex */ + flagT mergevertex:1; /* True if pending qh_appendvertexmerge due to + qh_maybe_duplicateridge or qh_maybe_duplicateridges + disables check for duplicate vertices in qh_checkfacet */ + flagT mergevertex2:1; /* True if qh_drop_mergevertex of MRGvertices, printed but not used */ + flagT simplicialtop:1; /* True if top was simplicial (original vertices) */ + flagT simplicialbot:1; /* True if bottom was simplicial (original vertices) + use qh_test_centrum_merge if top and bot, need to retest since centrum may change */ }; /*---------------------------------- - qh - all global variables for qhull are in qh, qhmem, and qhstat + qhT + All global variables for qhull are in qhT, qhmemT, and qhstatT notes: qhmem is defined in mem.h, qhstat is defined in stat.h, qhrbox is defined in rboxpoints.h @@ -430,15 +464,15 @@ struct vertexT { QHULL_LIB_CHECK checks that a program and the corresponding qhull library were built with the same type of header files. -*/ -typedef struct qhT qhT; + QHULL_LIB_TYPE is QHULL_NON_REENTRANT, QHULL_QH_POINTER, or QHULL_REENTRANT +*/ #define QHULL_NON_REENTRANT 0 #define QHULL_QH_POINTER 1 #define QHULL_REENTRANT 2 -#if qh_QHpointer_dllimport +#ifdef qh_QHpointer_dllimport #define qh qh_qh-> __declspec(dllimport) extern qhT *qh_qh; /* allocated in global.c */ #define QHULL_LIB_TYPE QHULL_QH_POINTER @@ -448,7 +482,7 @@ __declspec(dllimport) extern qhT *qh_qh; /* allocated in global.c */ extern qhT *qh_qh; /* allocated in global.c */ #define QHULL_LIB_TYPE QHULL_QH_POINTER -#elif qh_dllimport +#elif defined(qh_dllimport) #define qh qh_qh. __declspec(dllimport) extern qhT qh_qh; /* allocated in global.c */ #define QHULL_LIB_TYPE QHULL_NON_REENTRANT @@ -475,9 +509,12 @@ struct qhT { copied into qh by qh_setflags(). qh-quick.htm#options defines the flags. */ boolT ALLpoints; /* true 'Qs' if search all points for initial simplex */ - boolT ANGLEmerge; /* true 'Qa' if sort potential merges by angle */ + boolT ALLOWshort; /* true 'Qa' allow input with fewer or more points than coordinates */ + boolT ALLOWwarning; /* true 'Qw' if allow option warnings */ + boolT ALLOWwide; /* true 'Q12' if allow wide facets and wide dupridges, c.f. qh_WIDEmaxoutside */ + boolT ANGLEmerge; /* true 'Q1' if sort potential merges by type/angle instead of type/distance */ boolT APPROXhull; /* true 'Wn' if MINoutside set */ - realT MINoutside; /* 'Wn' min. distance for an outside point */ + realT MINoutside; /* Minimum distance for an outside point ('Wn' or 2*qh.MINvisible) */ 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 */ @@ -485,22 +522,25 @@ struct qhT { 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 CHECKduplicates; /* true 'Q15' if qh_maybe_duplicateridges after each qh_mergefacet */ 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 DELAUNAY; /* true 'd' or 'v' if computing DELAUNAY triangulation */ boolT DOintersections; /* true 'Gh' if print hyperplane intersections */ int DROPdim; /* drops dim 'GDn' for 4-d -> 3-d output */ + boolT FLUSHprint; /* true 'Tf' if flush after qh_fprintf for segfaults */ boolT FORCEoutput; /* true 'Po' if forcing output despite degeneracies */ - int GOODpoint; /* 1+n for 'QGn', good facet if visible/not(-) from point n*/ + int GOODpoint; /* 'QGn' or 'QG-n' (n+1, n-1), good facet if visible from point n (or not) */ pointT *GOODpointp; /* the actual point */ - boolT GOODthreshold; /* true if qh.lower_threshold/upper_threshold defined + boolT GOODthreshold; /* true 'Pd/PD' if qh.lower_threshold/upper_threshold defined + set if qh.UPPERdelaunay (qh_initbuild) false if qh.SPLITthreshold */ - int GOODvertex; /* 1+n, good facet if vertex for point n */ + int GOODvertex; /* 'QVn' or 'QV-n' (n+1, n-1), good facet if vertex for point n (or not) */ 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 IStracing; /* 'Tn' 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 @@ -508,28 +548,31 @@ struct qhT { 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 */ + int MAXwide; /* 'QWn' max ratio for wide facet, otherwise error unless Q12-allow-wide */ + boolT MERGEexact; /* true 'Qx' if exact merges (concave, degen, dupridge, flipped) + tested by qh_checkzero and qh_test_*_merge */ + boolT MERGEindependent; /* true if merging independent sets of coplanar facets. 'Q2' disables */ 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 */ + boolT MERGEpinched; /* true 'Q14' if merging pinched vertices due to dupridge */ + boolT MERGEvertices; /* true if merging redundant vertices, 'Q3' disables or qh.hull_dim > qh_DIMmergeVertex */ 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 NOnearinside; /* true 'Q8' if ignore near-inside points when partitioning, qh_check_points may fail */ 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) */ + 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 PRINTgood; /* true 'Pg' if printing good facets + PGood set if 'd', 'PAn', 'PFn', 'PMn', 'QGn', 'QG-n', 'QVn', or 'QV-n' */ 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 */ @@ -545,23 +588,25 @@ struct qhT { 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 */ + boolT RANDOMdist; /* true 'Rn' 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 */ + boolT RANDOMoutside; /* true 'Qr' if select a random outside point */ + int REPORTfreq; /* 'TFn' 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 */ + int ROTATErandom; /* 'QRn' n<-1 random seed, n==-1 time is seed, n==0 random rotation by time, n>0 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 SETroundoff; /* true 'En' if qh.DISTround is predefined */ + boolT SKIPcheckmax; /* true 'Q5' if skip qh_check_maxout, qh_check_points may fail */ boolT SKIPconvex; /* true 'Q6' if skip convexity testing during pre-merge */ - boolT SPLITthresholds; /* true if upper_/lower_threshold defines a region + boolT SPLITthresholds; /* true 'Pd/PD' if upper_/lower_threshold defines a region + else qh.GOODthresholds + set if qh.DELAUNAY (qh_initbuild) used only for printing (!for qh.ONLYgood) */ + int STOPadd; /* 'TAn' 1+n for stop after adding n vertices */ 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(-) @@ -570,7 +615,7 @@ struct qhT { 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 */ + int TRACEpoint; /* 'TPn' start tracing when point n is a vertex, use qh_IDunknown (-1) after qh_buildhull and qh_postmerge */ 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 */ @@ -579,11 +624,11 @@ struct qhT { 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 */ + boolT VORONOI; /* true 'v' if computing Voronoi diagram, also sets qh.DELAUNAY */ /*--------input constants ---------*/ realT AREAfactor; /* 1/(hull_dim-1)! for converting det's to area */ - boolT DOcheckmax; /* true if calling qh_check_maxout (qh_initqhull_globals) */ + boolT DOcheckmax; /* true if calling qh_check_maxout (!qh.SKIPcheckmax && qh.MERGING) */ char *feasible_string; /* feasible point 'Hn,n,n' for halfspace intersection */ coordT *feasible_point; /* as coordinates, both malloc'd */ boolT GETarea; /* true 'Fa', 'FA', 'FS', 'PAn', 'PFn' if compute facet area/Voronoi volume in io.c */ @@ -604,10 +649,10 @@ struct qhT { 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 */ + boolT ZEROcentrum; /* true if 'C-0' or 'C-0 Qx' and not post-merging or 'A-n'. 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 */ + if qh.DELAUNAY, default is 0.0 for upper envelope (qh_initbuild) */ 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 @@ -620,16 +665,18 @@ struct qhT { precision constants for Qhull notes: - qh_detroundoff() computes the maximum roundoff error for distance + qh_detroundoff [geom2.c] 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 centrum_radius; /* max centrum radius for convexity ('Cn' + 2*qh.DISTround) */ realT cos_max; /* max cosine for convexity (roundoff added) */ - realT DISTround; /* max round off error for distances, 'E' overrides qh_distround() */ + realT DISTround; /* max round off error for distances, qh.SETroundoff ('En') overrides qh_distround */ realT MAXabs_coord; /* max absolute coordinate */ realT MAXlastcoord; /* max last coordinate for qh_scalelast */ + realT MAXoutside; /* max target for qh.max_outside/f.maxoutside, base for qh_RATIO... + recomputed at qh_addpoint, unrelated to qh_MAXoutside */ realT MAXsumcoord; /* max sum of coordinates */ realT MAXwidth; /* max rectilinear width of point coordinates */ realT MINdenom_1; /* min. abs. value for 1/x */ @@ -637,7 +684,6 @@ struct qhT { 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 */ @@ -645,6 +691,7 @@ struct qhT { 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 */ + boolT NARROWhull; /* set in qh_initialhull if angle < qh_MAXnarrow */ /*---------------------------------- @@ -654,16 +701,16 @@ struct qhT { */ 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 */ + 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_start2 */ - FILE *fout; /* pointer to output file */ - FILE *ferr; /* pointer to error file */ + char jmpXtra2[40]; /* extra bytes in case jmp_buf is defined wrong by compiler*/ + FILE * fin; /* pointer to input file, init by qh_initqhull_start2 */ + 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) */ + 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) */ /*---------------------------------- @@ -675,31 +722,40 @@ struct qhT { qh_resetlists() */ facetT *facet_list; /* first facet */ - facetT *facet_tail; /* end of facet_list (dummy facet) */ + facetT *facet_tail; /* end of facet_list (dummy facet with id 0 and next==NULL) */ 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 *newfacet_list; /* list of new facets to end of facet_list + qh_postmerge sets newfacet_list to facet_list */ facetT *visible_list; /* list of visible facets preceding newfacet_list, - facet->visible set */ + end of visible list if !facet->visible, same as newfacet_list + qh_findhorizon sets visible_list at end of facet_list + qh_willdelete prepends to visible_list + qh_triangulate appends mirror facets to visible_list at end of facet_list + qh_postmerge sets visible_list to facet_list + qh_deletevisible deletes the visible facets */ 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) */ + unsigned int tracefacet_id; /* set at init, then can print whenever */ + facetT *tracefacet; /* set in newfacet/mergefacet, undone in delfacet and qh_errexit */ + unsigned int traceridge_id; /* set at init, then can print whenever */ + ridgeT *traceridge; /* set in newridge, undone in delridge, errexit, errexit2, makenew_nonsimplicial, mergecycle_ridges */ + unsigned int tracevertex_id; /* set at buildtracing, can print whenever */ + vertexT *tracevertex; /* set in newvertex, undone in delvertex and qh_errexit */ + vertexT *vertex_list; /* list of all vertices, to vertex_tail */ + vertexT *vertex_tail; /* end of vertex_list (dummy vertex with ID 0, next NULL) */ vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail - all vertices have 'newlist' set */ + all vertices have 'newfacet' 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() */ + int num_good; /* number of good facets (after qh_findgood_all or qh_markkeep) */ + unsigned int facet_id; /* ID of next, new facet from newfacet() */ + unsigned int ridge_id; /* ID of next, new ridge from newridge() */ + unsigned int vertex_id; /* ID of next, new vertex from newvertex() */ + unsigned int first_newfacet; /* ID of first_newfacet for qh_buildcone, or 0 if none */ /*---------------------------------- @@ -710,15 +766,18 @@ struct qhT { 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 */ + /* use 'unsigned long' to avoid wrap-around errors */ + boolT ALLOWrestart; /* true if qh_joggle_restart 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 */ + int last_errcode; /* last errcode from qh_fprintf, reset in qh_build_withrestart */ facetT *GOODclosest; /* closest facet to GOODthreshold in qh_findgood */ + pointT *coplanar_apex; /* last apex declared a coplanar point by qh_getpinchedmerges, prevents infinite loop */ 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 isRenameVertex; /* true during qh_merge_pinchedvertices, disables duplicate ridge vertices in qh_checkfacet */ + realT JOGGLEmax; /* set 'QJn' if randomly joggle input. 'QJ'/'QJ0.0' sets default (qh_detjoggle) */ 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 @@ -731,22 +790,26 @@ struct qhT { 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 */ + from qh_makecone/qh_attachnewfacets to qh_resetlists */ + boolT NEWtentative; /* true while new facets are tentative due to !qh.IGNOREpinched or qh.ONLYgood + from qh_makecone to qh_attachnewfacets */ 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 */ + boolT NOerrexit; /* true if qh.errexit is not available, cleared after setjmp. See qh.ERREXITcalled */ 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 */ + unsigned int repart_facetid; /* previous facetid to prevent recursive qh_partitioncoplanar+qh_partitionpoint */ + int retry_addpoint; /* number of retries of qh_addpoint due to merging pinched vertices */ 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 (qh_check_maxout) */ + boolT ZEROall_ok; /* True if qh_checkzero always succeeds */ /*---------------------------------- @@ -757,11 +820,12 @@ struct qhT { */ setT *facet_mergeset; /* temporary set of merges to be done */ setT *degen_mergeset; /* temporary set of degenerate and redundant merges */ + setT *vertex_mergeset; /* temporary set of vertex 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 */ + facets. v.deleted is set for checkfacet */ /*---------------------------------- @@ -773,7 +837,7 @@ struct qhT { 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 *half_space; /* malloc'd input array for halfspace (qh.normal_size+coordT) */ coordT *temp_malloc; /* malloc'd input array for points */ /*-id) : qh_IDunknown) @@ -845,6 +915,8 @@ struct qhT { FORALLpoints { ... } assign 'point' to each point in qh.first_point, qh.num_points + notes: + declare: coordT *point, *pointtemp; */ @@ -853,13 +925,13 @@ struct qhT { /*---------------------------------- - FORALLpoint_( points, num) { ... } + FORALLpoint_(points, num) { ... } assign 'point' to each point in points array of num points declare: coordT *point, *pointtemp; */ -#define FORALLpoint_(points, num) for (point= (points), \ +#define FORALLpoint_(points, num) for (point=(points), \ pointtemp= (points)+qh hull_dim*(num); point < pointtemp; point += qh hull_dim) /*-next;vertex= vertex->next) @@ -885,6 +957,9 @@ struct qhT { declare: facetT *facet, **facetp; + notes: + assumes set is not modified + see: FOREACHsetelement_ */ @@ -902,6 +977,9 @@ struct qhT { declare: facetT *neighbor, **neighborp; + notes: + assumes set is not modified + see: FOREACHsetelement_ */ @@ -916,6 +994,9 @@ struct qhT { declare: pointT *point, **pointp; + notes: + assumes set is not modified + see: FOREACHsetelement_ */ @@ -930,6 +1011,9 @@ struct qhT { declare: ridgeT *ridge, **ridgep; + notes: + assumes set is not modified + see: FOREACHsetelement_ */ @@ -944,6 +1028,9 @@ struct qhT { declare: vertexT *vertex, **vertexp; + notes: + assumes set is not modified + see: FOREACHsetelement_ */ @@ -970,15 +1057,13 @@ struct qhT { FOREACHneighbor_i_( facet ) { ... } assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors - FOREACHneighbor_i_( vertex ) { ... } - assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors - declare: facetT *neighbor; int neighbor_n, neighbor_i; - see: - FOREACHsetelement_i_ + notes: + see FOREACHsetelement_i_ + for facet neighbors of vertex, need to define a new macro */ #define FOREACHneighbor_i_(facet) FOREACHsetelement_i_(facetT, facet->neighbors, neighbor) @@ -1025,12 +1110,13 @@ struct qhT { see: FOREACHsetelement_i_ */ -#define FOREACHvertex_i_(vertices) FOREACHsetelement_i_(vertexT, vertices,vertex) +#define FOREACHvertex_i_(vertices) FOREACHsetelement_i_(vertexT, vertices, vertex) /********* -libqhull.c prototypes (duplicated from qhull_a.h) **********************/ void qh_qhull(void); boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist); +void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet); void qh_printsummary(FILE *fp); /********* -user.c prototypes (alphabetical) **********************/ @@ -1041,8 +1127,11 @@ int qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc, char *qhull_cmd, FILE *outfile, FILE *errfile); void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall); void qh_printhelp_degenerate(FILE *fp); +void qh_printhelp_internal(FILE *fp); void qh_printhelp_narrowhull(FILE *fp, realT minangle); void qh_printhelp_singular(FILE *fp); +void qh_printhelp_topology(FILE *fp); +void qh_printhelp_wide(FILE *fp); void qh_user_memsizes(void); /********* -usermem.c prototypes (alphabetical) **********************/ @@ -1101,8 +1190,8 @@ qhT *qh_save_qhull(void); /***** -io.c prototypes (duplicated from io.h) ***********************/ -void qh_dfacet(unsigned id); -void qh_dvertex(unsigned id); +void qh_dfacet(unsigned int id); +void qh_dvertex(unsigned int id); void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); void qh_produce_output(void); coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc); @@ -1122,11 +1211,11 @@ facetT *qh_findbestfacet(pointT *point, boolT bestoutside, realT *bestdist, boolT *isoutside); vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp); pointT *qh_point(int id); -setT *qh_pointfacet(void /*qh.facet_list*/); +setT *qh_pointfacet(void /* qh.facet_list */); int qh_pointid(pointT *point); -setT *qh_pointvertex(void /*qh.facet_list*/); +setT *qh_pointvertex(void /* qh.facet_list */); void qh_setvoronoi_all(void); -void qh_triangulate(void /*qh.facet_list*/); +void qh_triangulate(void /* qh.facet_list */); /********* -rboxlib.c prototypes **********************/ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command); diff --git a/3rdparty/qhull/mem.c b/3rdparty/qhull/mem.c index db72bb4e1..a4259dfdf 100644 --- a/3rdparty/qhull/mem.c +++ b/3rdparty/qhull/mem.c @@ -29,9 +29,9 @@ qh-mem.htm and mem.h global.c (qh_initbuffers) for an example of using mem.c - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/mem.c#7 $$Change: 2065 $ - $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/mem.c#4 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #include "user.h" /* for QHULL_CRTDBG */ @@ -152,14 +152,14 @@ void *qh_memalloc(int insize) { *((void **)newbuffer)= qhmem.curbuffer; /* prepend newbuffer to curbuffer list. newbuffer!=0 by QH6080 */ qhmem.curbuffer= newbuffer; - size= (sizeof(void **) + qhmem.ALIGNmask) & ~qhmem.ALIGNmask; + size= ((int)sizeof(void **) + qhmem.ALIGNmask) & ~qhmem.ALIGNmask; qhmem.freemem= (void *)((char *)newbuffer+size); qhmem.freesize= bufsize - size; qhmem.totbuffer += bufsize - size; /* easier to check */ /* Periodically test totbuffer. It matches at beginning and exit of every call */ - n = qhmem.totshort + qhmem.totfree + qhmem.totdropped + qhmem.freesize - outsize; + n= qhmem.totshort + qhmem.totfree + qhmem.totdropped + qhmem.freesize - outsize; if (qhmem.totbuffer != n) { - qh_fprintf(qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qhmem.totbuffer, n); + qh_fprintf(qhmem.ferr, 6212, "qhull internal error (qh_memalloc): short totbuffer %d != totshort+totfree... %d\n", qhmem.totbuffer, n); qh_errexit(qhmem_ERRmem, NULL, NULL); } } @@ -205,7 +205,8 @@ void qh_memcheck(void) { void *object; if (qhmem.ferr == 0 || qhmem.IStracing < 0 || qhmem.IStracing > 10 || (((qhmem.ALIGNmask+1) & qhmem.ALIGNmask) != 0)) { - qh_fprintf_stderr(6244, "qh_memcheck error: either qhmem is overwritten or qhmem is not initialized. Call qh_meminit() or qh_new_qhull() before calling qh_mem routines. ferr 0x%x IsTracing %d ALIGNmask 0x%x", qhmem.ferr, qhmem.IStracing, qhmem.ALIGNmask); + qh_fprintf_stderr(6244, "qhull internal error (qh_memcheck): either qhmem is overwritten or qhmem is not initialized. Call qh_meminit or qh_new_qhull before calling qh_mem routines. ferr 0x%x, IsTracing %d, ALIGNmask 0x%x\n", + qhmem.ferr, qhmem.IStracing, qhmem.ALIGNmask); qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ } if (qhmem.IStracing != 0) @@ -217,7 +218,7 @@ void qh_memcheck(void) { totfree += qhmem.sizetable[i] * count; } if (totfree != qhmem.totfree) { - qh_fprintf(qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qhmem.totfree, totfree); + qh_fprintf(qhmem.ferr, 6211, "qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qhmem.totfree, totfree); qh_errexit(qhmem_ERRqhull, NULL, NULL); } if (qhmem.IStracing != 0) @@ -281,6 +282,11 @@ void qh_memfree(void *object, int insize) { 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_ALL) instead. + see: qh_freeqhull(allMem) qh_memtotal(curlong, totlong, curshort, totshort, maxlong, totbuffer); @@ -291,7 +297,7 @@ void qh_memfreeshort(int *curlong, int *totlong) { *curlong= qhmem.cntlong - qhmem.freelong; *totlong= qhmem.totlong; - for (buffer= qhmem.curbuffer; buffer; buffer= nextbuffer) { + for (buffer=qhmem.curbuffer; buffer; buffer= nextbuffer) { nextbuffer= *((void **) buffer); qh_free(buffer); } @@ -311,7 +317,7 @@ void qh_memfreeshort(int *curlong, int *totlong) { >--------------------------------- qh_meminit( ferr ) - initialize qhmem and test sizeof( void*) + initialize qhmem and test sizeof(void *) Does not throw errors. qh_exit on failure */ void qh_meminit(FILE *ferr) { @@ -321,13 +327,13 @@ void qh_meminit(FILE *ferr) { qhmem.ferr= ferr; else qhmem.ferr= stderr; - if (sizeof(void*) < sizeof(int)) { - qh_fprintf(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)); + if (sizeof(void *) < sizeof(int)) { + qh_fprintf(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(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() */ + if (sizeof(void *) > sizeof(ptr_intT)) { + qh_fprintf(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(); } /* meminit */ @@ -367,7 +373,7 @@ void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, /*--------------------------------- - qh_memsetup() + qh_memsetup( ) set up memory after running memsize() */ void qh_memsetup(void) { @@ -380,7 +386,7 @@ void qh_memsetup(void) { qhmem.LASTsize, qhmem.BUFsize, qhmem.BUFinit); qh_errexit(qhmem_ERRmem, NULL, NULL); } - if (!(qhmem.indextable= (int *)qh_malloc((qhmem.LASTsize+1) * sizeof(int)))) { + if (!(qhmem.indextable= (int *)qh_malloc((size_t)(qhmem.LASTsize+1) * sizeof(int)))) { qh_fprintf(qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n"); qh_errexit(qhmem_ERRmem, NULL, NULL); } @@ -405,10 +411,12 @@ void qh_memsize(int size) { int k; if (qhmem.LASTsize) { - qh_fprintf(qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n"); + qh_fprintf(qhmem.ferr, 6089, "qhull internal error (qh_memsize): qh_memsize called after qh_memsetup\n"); qh_errexit(qhmem_ERRqhull, NULL, NULL); } size= (size + qhmem.ALIGNmask) & ~qhmem.ALIGNmask; + if (qhmem.IStracing >= 3) + qh_fprintf(qhmem.ferr, 3078, "qh_memsize: quick memory of %d bytes\n", size); for (k=qhmem.TABLEsize; k--; ) { if (qhmem.sizetable[k] == size) return; @@ -416,7 +424,7 @@ void qh_memsize(int size) { if (qhmem.TABLEsize < qhmem.NUMsizes) qhmem.sizetable[qhmem.TABLEsize++]= size; else - qh_fprintf(qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qhmem.NUMsizes); + qh_fprintf(qhmem.ferr, 7060, "qhull warning (qh_memsize): free list table has room for only %d sizes\n", qhmem.NUMsizes); } /* memsize */ @@ -456,7 +464,7 @@ void qh_memstatistics(FILE *fp) { qhmem.totbuffer, qhmem.BUFsize, qhmem.BUFinit); if (qhmem.cntlarger) { qh_fprintf(fp, 9279, "%7d calls to qh_setlarger\n%7.2g average copy size\n", - qhmem.cntlarger, ((float)qhmem.totlarger)/(float)qhmem.cntlarger); + qhmem.cntlarger, ((double)qhmem.totlarger)/(double)qhmem.cntlarger); qh_fprintf(fp, 9280, " freelists(bytes->count):"); } for (i=0; i < qhmem.TABLEsize; i++) { @@ -496,6 +504,9 @@ void *qh_memalloc(int insize) { return object; } +void qh_memcheck(void) { +} + void qh_memfree(void *object, int insize) { if (!object) @@ -520,8 +531,8 @@ void qh_meminit(FILE *ferr) { qhmem.ferr= ferr; else qhmem.ferr= stderr; - if (sizeof(void*) < sizeof(int)) { - qh_fprintf(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)); + if (sizeof(void *) < sizeof(int)) { + qh_fprintf(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(qhmem_ERRqhull, NULL, NULL); } } @@ -532,11 +543,9 @@ void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, } void qh_memsetup(void) { - } void qh_memsize(int size) { - } void qh_memstatistics(FILE *fp) { @@ -563,7 +572,7 @@ void qh_memstatistics(FILE *fp) { Returns the total current bytes of long and short allocations Returns the current count of long and short allocations Returns the maximum long memory and total short buffer (minus one link per buffer) - Does not error (UsingLibQhull.cpp) + Does not error (for deprecated UsingLibQhull.cpp in libqhullpcpp) */ void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) { *totlong= qhmem.totlong; diff --git a/3rdparty/qhull/mem.h b/3rdparty/qhull/mem.h index 453f319df..06a14145b 100644 --- a/3rdparty/qhull/mem.h +++ b/3rdparty/qhull/mem.h @@ -11,9 +11,9 @@ and qh_errexit(qhmem_ERRqhull, NULL, NULL) otherwise - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/mem.h#2 $$Change: 2062 $ - $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/mem.h#2 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #ifndef qhDEFmem @@ -33,7 +33,7 @@ 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 + #define qh_NOmem */ /*-neighbors not set until the first merge occurs - Copyright (c) 1993-2015 C.B. Barber. - $Id: //main/2015/qhull/src/libqhull/merge.c#4 $$Change: 2064 $ - $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ + Copyright (c) 1993-2020 C.B. Barber. + $Id: //main/2019/qhull/src/libqhull/merge.c#12 $$Change: 2958 $ + $DateTime: 2020/05/26 16:17:49 $$Author: bbarber $ */ #include "qhull_a.h" #ifndef qh_NOmerge +/* MRGnone, etc. */ +const char *mergetypes[]= { + "none", + "coplanar", + "anglecoplanar", + "concave", + "concavecoplanar", + "twisted", + "flip", + "dupridge", + "subridge", + "vertices", + "degen", + "redundant", + "mirror", + "coplanarhorizon", +}; + /*===== functions(alphabetical after premerge and postmerge) ======*/ /*--------------------------------- - qh_premerge( apex, maxcentrum ) - pre-merge nonconvex facets in qh.newfacet_list for apex + qh_premerge( apexpointid, maxcentrum ) + pre-merge nonconvex facets in qh.newfacet_list for apexpointid maxcentrum defines coplanar and concave (qh_test_appendmerge) returns: deleted facets added to qh.visible_list with facet->visible set notes: + only called by qh_addpoint uses globals, qh.MERGEexact, qh.PREmerge design: - mark duplicate ridges in qh.newfacet_list + mark dupridges in qh.newfacet_list merge facet cycles in qh.newfacet_list - merge duplicate ridges and concave facets in qh.newfacet_list + merge dupridges and concave facets in qh.newfacet_list check merged facet cycles for degenerate and redundant facets merge degenerate and redundant facets collect coplanar and concave facets merge concave, coplanar, degenerate, and redundant facets */ -void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) { +void qh_premerge(int apexpointid, realT maxcentrum, realT maxangle /* qh.newfacet_list */) { boolT othermerge= False; - facetT *newfacet; if (qh ZEROcentrum && qh_checkzero(!qh_ALL)) return; - trace2((qh ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n", - maxcentrum, maxangle, apex->id, getid_(qh newfacet_list))); - if (qh IStracing >= 4 && qh num_facets < 50) + trace2((qh ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %4.4g for apex p%d newfacet_list f%d\n", + maxcentrum, maxangle, apexpointid, getid_(qh newfacet_list))); + if (qh IStracing >= 4 && qh num_facets < 100) qh_printlists(); qh centrum_radius= maxcentrum; qh cos_max= maxangle; - qh degen_mergeset= qh_settemp(qh TEMPsize); - qh facet_mergeset= qh_settemp(qh TEMPsize); if (qh hull_dim >=3) { - qh_mark_dupridges(qh newfacet_list); /* facet_mergeset */ + qh_mark_dupridges(qh newfacet_list, qh_ALL); /* facet_mergeset */ qh_mergecycle_all(qh newfacet_list, &othermerge); qh_forcedmerges(&othermerge /* qh.facet_mergeset */); - FORALLnew_facets { /* test samecycle merges */ - if (!newfacet->simplicial && !newfacet->mergeridge) - qh_degen_redundant_neighbors(newfacet, NULL); - } - if (qh_merge_degenredundant()) - othermerge= True; }else /* qh.hull_dim == 2 */ qh_mergecycle_all(qh newfacet_list, &othermerge); qh_flippedmerges(qh newfacet_list, &othermerge); @@ -86,8 +96,6 @@ void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) { qh_getmergeset_initial(qh newfacet_list); qh_all_merges(othermerge, False); } - qh_settempfree(&qh facet_mergeset); - qh_settempfree(&qh degen_mergeset); } /* premerge */ /*-newmerge set qh.newvertext_list to qh.vertex_list add all vertices to qh.newvertex_list - if a pre-merge occured + if a pre-merge occurred set vertex->delridge {will retest the ridge} if qh.MERGEexact call qh_reducevertices() @@ -145,35 +155,29 @@ void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, qh centrum_radius= maxcentrum; qh cos_max= maxangle; qh POSTmerging= True; - qh degen_mergeset= qh_settemp(qh TEMPsize); - qh facet_mergeset= qh_settemp(qh TEMPsize); - if (qh visible_list != qh facet_list) { /* first call */ + if (qh visible_list != qh facet_list) { /* first call due to qh_buildhull, multiple calls if qh.POSTmerge */ qh NEWfacets= True; qh visible_list= qh newfacet_list= qh facet_list; - FORALLnew_facets { + FORALLnew_facets { /* all facets are new facets for qh_postmerge */ newfacet->newfacet= True; if (!newfacet->simplicial) - newfacet->newmerge= True; + newfacet->newmerge= True; /* test f.vertices for 'delridge'. 'newmerge' was cleared at end of qh_all_merges */ zinc_(Zpostfacets); } qh newvertex_list= qh vertex_list; FORALLvertices - vertex->newlist= True; - if (qh VERTEXneighbors) { /* a merge has occurred */ - FORALLvertices - vertex->delridge= True; /* test for redundant, needed? */ - if (qh MERGEexact) { - if (qh hull_dim <= qh_DIMreduceBuild) - qh_reducevertices(); /* was skipped during pre-merging */ - } + vertex->newfacet= True; + if (qh VERTEXneighbors) { /* a merge has occurred */ + if (qh MERGEexact && qh hull_dim <= qh_DIMreduceBuild) + qh_reducevertices(); /* qh_all_merges did not call qh_reducevertices for v.delridge */ } if (!qh PREmerge && !qh MERGEexact) qh_flippedmerges(qh newfacet_list, &othermerges); } qh_getmergeset_initial(qh newfacet_list); - qh_all_merges(False, vneighbors); - qh_settempfree(&qh facet_mergeset); - qh_settempfree(&qh degen_mergeset); + qh_all_merges(False, vneighbors); /* calls qh_reducevertices before exiting */ + FORALLnew_facets + newfacet->newmerge= False; /* Was True if no vertex in f.vertices was 'delridge' */ } /* post_merge */ /*- 0 || qh_setsize(qh degen_mergeset) > 0) { + if (qh_setsize(qh degen_mergeset) > 0) { + numdegenredun += qh_merge_degenredundant(); + wasmerge= True; + } + while ((merge= (mergeT *)qh_setdellast(qh facet_mergeset))) { facet1= merge->facet1; facet2= merge->facet2; - mergetype= merge->type; - qh_memfree_(merge, (int)sizeof(mergeT), freelistp); - if (facet1->visible || facet2->visible) /*deleted facet*/ + vertex= merge->vertex1; /* not used for qh.facet_mergeset*/ + mergetype= merge->mergetype; + angle= merge->angle; + distance= merge->distance; + qh_memfree_(merge, (int)sizeof(mergeT), freelistp); /* 'merge' is invalid */ + if (facet1->visible || facet2->visible) { + trace3((qh ferr, 3045, "qh_all_merges: drop merge of f%d (del? %d) into f%d (del? %d) mergetype %d, dist %4.4g, angle %4.4g. One or both facets is deleted\n", + facet1->id, facet1->visible, facet2->id, facet2->visible, mergetype, distance, angle)); continue; - if ((facet1->newfacet && !facet1->tested) - || (facet2->newfacet && !facet2->tested)) { - if (qh MERGEindependent && mergetype <= MRGanglecoplanar) - continue; /* perform independent sets of merges */ + }else if (mergetype == MRGcoplanar || mergetype == MRGanglecoplanar) { + if (qh MERGEindependent) { + if ((!facet1->tested && facet1->newfacet) + || (!facet2->tested && facet2->newfacet)) { + trace3((qh ferr, 3064, "qh_all_merges: drop merge of f%d (tested? %d) into f%d (tested? %d) mergetype %d, dist %2.2g, angle %4.4g. Merge independent sets of coplanar merges\n", + facet1->id, facet1->visible, facet2->id, facet2->visible, mergetype, distance, angle)); + continue; + } + } } - qh_merge_nonconvex(facet1, facet2, mergetype); - numdegenredun += qh_merge_degenredundant(); + trace3((qh ferr, 3047, "qh_all_merges: merge f%d and f%d type %d dist %2.2g angle %4.4g\n", + facet1->id, facet2->id, mergetype, distance, angle)); + if (mergetype == MRGtwisted) + qh_merge_twisted(facet1, facet2); + else + qh_merge_nonconvex(facet1, facet2, mergetype); numnewmerges++; + numdegenredun += qh_merge_degenredundant(); wasmerge= True; if (mergetype == MRGconcave) numconcave++; - else /* MRGcoplanar or MRGanglecoplanar */ + else if (mergetype == MRGconcavecoplanar) + numconcavecoplanar++; + else if (mergetype == MRGtwisted) + numtwisted++; + else if (mergetype == MRGcoplanar || mergetype == MRGanglecoplanar) numcoplanar++; - } /* while setdellast */ + else { + qh_fprintf(qh ferr, 6394, "qhull internal error (qh_all_merges): expecting concave, coplanar, or twisted merge. Got merge f%d f%d v%d mergetype %d\n", + getid_(facet1), getid_(facet2), getid_(vertex), mergetype); + qh_errexit2(qh_ERRqhull, facet1, facet2); + } + } /* while qh_setdellast */ if (qh POSTmerging && qh hull_dim <= qh_DIMreduceBuild && numnewmerges > qh_MAXnewmerges) { numnewmerges= 0; + wasmerge= othermerge= False; qh_reducevertices(); /* otherwise large post merges too slow */ } - qh_getmergeset(qh newfacet_list); /* facet_mergeset */ - } /* while mergeset */ - if (qh VERTEXneighbors) { + qh_getmergeset(qh newfacet_list); /* qh.facet_mergeset */ + } /* while facet_mergeset or degen_mergeset */ + if (qh VERTEXneighbors) { /* at least one merge */ 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; + if (qh POSTmerging && qh hull_dim >= 4) { isreduce= True; + }else if (qh POSTmerging || !qh MERGEexact) { + if ((wasmerge || othermerge) && qh hull_dim > 2 && qh hull_dim <= qh_DIMreduceBuild) + isreduce= True; } if (isreduce) { + wasmerge= othermerge= False; if (qh_reducevertices()) { qh_getmergeset(qh newfacet_list); /* facet_mergeset */ continue; @@ -277,6 +313,12 @@ void qh_all_merges(boolT othermerge, boolT vneighbors) { continue; break; } /* while (True) */ + if (wasmerge || othermerge) { + trace3((qh ferr, 3033, "qh_all_merges: skip qh_reducevertices due to post-merging, no qh.VERTEXneighbors (%d), or hull_dim %d ==2 or >%d\n", qh VERTEXneighbors, qh hull_dim, qh_DIMreduceBuild)) + FORALLnew_facets { + newfacet->newmerge= False; + } + } if (qh CHECKfrequently && !qh MERGEexact) { qh old_randomdist= qh RANDOMdist; qh RANDOMdist= False; @@ -284,27 +326,79 @@ void qh_all_merges(boolT othermerge, boolT vneighbors) { /* qh_checkconnect(); [this is slow and it changes the facet order] */ qh RANDOMdist= qh old_randomdist; } - trace1((qh ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n", - numcoplanar, numconcave, numdegenredun)); - if (qh IStracing >= 4 && qh num_facets < 50) + trace1((qh ferr, 1009, "qh_all_merges: merged %d coplanar %d concave %d concavecoplanar %d twisted facets and %d degen or redundant facets.\n", + numcoplanar, numconcave, numconcavecoplanar, numtwisted, numdegenredun)); + if (qh IStracing >= 4 && qh num_facets < 500) qh_printlists(); } /* all_merges */ +/*--------------------------------- + + qh_all_vertexmerges( apexpointid, facet, &retryfacet ) + merge vertices in qh.vertex_mergeset and subsequent merges + + returns: + returns retryfacet for facet (if defined) + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + mergesets are empty + if merges, resets facet lists + + notes: + called from qh_qhull, qh_addpoint, and qh_buildcone_mergepinched + vertex merges occur after facet merges and qh_resetlists + + design: + while merges in vertex_mergeset (MRGvertices) + merge a pair of pinched vertices + update vertex neighbors + merge non-convex and degenerate facets and check for ridges with duplicate vertices + partition outside points of deleted, "visible" facets +*/ +void qh_all_vertexmerges(int apexpointid, facetT *facet, facetT **retryfacet) { + int numpoints; /* ignore count of partitioned points. Used by qh_addpoint for Zpbalance */ + + if (retryfacet) + *retryfacet= facet; + while (qh_setsize(qh vertex_mergeset) > 0) { + trace1((qh ferr, 1057, "qh_all_vertexmerges: starting to merge %d vertex merges for apex p%d facet f%d\n", + qh_setsize(qh vertex_mergeset), apexpointid, getid_(facet))); + if (qh IStracing >= 4 && qh num_facets < 1000) + qh_printlists(); + qh_merge_pinchedvertices(apexpointid /* qh.vertex_mergeset, visible_list, newvertex_list, newfacet_list */); + qh_update_vertexneighbors(); /* update neighbors of qh.newvertex_list from qh_newvertices for deleted facets on qh.visible_list */ + /* test ridges and merge non-convex facets */ + qh_getmergeset(qh newfacet_list); + qh_all_merges(True, False); /* calls qh_reducevertices */ + if (qh CHECKfrequently) + qh_checkpolygon(qh facet_list); + qh_partitionvisible(!qh_ALL, &numpoints /* qh.visible_list qh.del_vertices*/); + if (retryfacet) + *retryfacet= qh_getreplacement(*retryfacet); + qh_deletevisible(/* qh.visible_list qh.del_vertices*/); + qh_resetlists(False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + if (qh IStracing >= 4 && qh num_facets < 1000) { + qh_printlists(); + qh_checkpolygon(qh facet_list); + } + } +} /* all_vertexmerges */ /*--------------------------------- - qh_appendmergeset( facet, neighbor, mergetype, angle ) + qh_appendmergeset( facet, vertex, neighbor, mergetype, dist, angle ) appends an entry to qh.facet_mergeset or qh.degen_mergeset - - angle ignored if NULL or !qh.ANGLEmerge + if 'dist' is unknown, set it to 0.0 + if 'angle' is unknown, set it to 1.0 (coplanar) returns: merge appended to facet_mergeset or degen_mergeset sets ->degenerate or ->redundant if degen_mergeset - see: - qh_test_appendmerge() + notes: + caller collects statistics and/or caller of qh_mergefacet + see: qh_test_appendmerge() design: allocate merge entry @@ -313,44 +407,75 @@ void qh_all_merges(boolT othermerge, boolT vneighbors) { 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 + prepend to qh.degen_mergeset (merged last) else if redundant merge append to qh.degen_mergeset */ -void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) { +void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, coordT dist, realT angle) { mergeT *merge, *lastmerge; void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + const char *mergename; - if (facet->redundant) + if ((facet->redundant && mergetype != MRGmirror) || neighbor->redundant) { + trace3((qh ferr, 3051, "qh_appendmergeset: f%d is already redundant (%d) or f%d is already redundant (%d). Ignore merge f%d and f%d type %d\n", + facet->id, facet->redundant, neighbor->id, neighbor->redundant, facet->id, neighbor->id, mergetype)); return; - if (facet->degenerate && mergetype == MRGdegen) + } + if (facet->degenerate && mergetype == MRGdegen) { + trace3((qh ferr, 3077, "qh_appendmergeset: f%d is already degenerate. Ignore merge f%d type %d (MRGdegen)\n", + facet->id, facet->id, mergetype)); return; + } + if (!qh facet_mergeset || !qh degen_mergeset) { + qh_fprintf(qh ferr, 6403, "qhull internal error (qh_appendmergeset): expecting temp set defined for qh.facet_mergeset (0x%x) and qh.degen_mergeset (0x%x). Got NULL\n", + qh facet_mergeset, qh degen_mergeset); + /* otherwise qh_setappend creates a new set that is not freed by qh_freebuild() */ + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (neighbor->flipped && !facet->flipped) { + if (mergetype != MRGdupridge) { + qh_fprintf(qh ferr, 6355, "qhull internal error (qh_appendmergeset): except for MRGdupridge, cannot merge a non-flipped facet f%d into flipped f%d, mergetype %d, dist %4.4g\n", + facet->id, neighbor->id, mergetype, dist); + qh_errexit(qh_ERRqhull, NULL, NULL); + }else { + trace2((qh ferr, 2106, "qh_appendmergeset: dupridge will merge a non-flipped facet f%d into flipped f%d, dist %4.4g\n", + facet->id, neighbor->id, dist)); + } + } qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT); + merge->angle= angle; + merge->distance= dist; merge->facet1= facet; merge->facet2= neighbor; - merge->type= mergetype; - if (angle && qh ANGLEmerge) - merge->angle= *angle; + merge->vertex1= NULL; + merge->vertex2= NULL; + merge->ridge1= NULL; + merge->ridge2= NULL; + merge->mergetype= mergetype; + if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *)) + mergename= mergetypes[mergetype]; + else + mergename= mergetypes[MRGnone]; if (mergetype < MRGdegen) qh_setappend(&(qh facet_mergeset), merge); else if (mergetype == MRGdegen) { facet->degenerate= True; - if (!(lastmerge= (mergeT*)qh_setlast(qh degen_mergeset)) - || lastmerge->type == MRGdegen) + if (!(lastmerge= (mergeT *)qh_setlast(qh degen_mergeset)) + || lastmerge->mergetype == MRGdegen) qh_setappend(&(qh degen_mergeset), merge); else - qh_setaddnth(&(qh degen_mergeset), 0, merge); + qh_setaddnth(&(qh degen_mergeset), 0, merge); /* merged last */ }else if (mergetype == MRGredundant) { facet->redundant= True; qh_setappend(&(qh degen_mergeset), merge); }else /* mergetype == MRGmirror */ { if (facet->redundant || neighbor->redundant) { - qh_fprintf(qh ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n", + qh_fprintf(qh ferr, 6092, "qhull internal error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet (i.e., 'redundant')\n", facet->id, neighbor->id); qh_errexit2(qh_ERRqhull, facet, neighbor); } if (!qh_setequal(facet->vertices, neighbor->vertices)) { - qh_fprintf(qh ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n", + qh_fprintf(qh ferr, 6093, "qhull internal error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n", facet->id, neighbor->id); qh_errexit2(qh_ERRqhull, facet, neighbor); } @@ -358,9 +483,68 @@ void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, rea neighbor->redundant= True; qh_setappend(&(qh degen_mergeset), merge); } + if (merge->mergetype >= MRGdegen) { + trace3((qh ferr, 3044, "qh_appendmergeset: append merge f%d and f%d type %d (%s) to qh.degen_mergeset (size %d)\n", + merge->facet1->id, merge->facet2->id, merge->mergetype, mergename, qh_setsize(qh degen_mergeset))); + }else { + trace3((qh ferr, 3027, "qh_appendmergeset: append merge f%d and f%d type %d (%s) dist %2.2g angle %4.4g to qh.facet_mergeset (size %d)\n", + merge->facet1->id, merge->facet2->id, merge->mergetype, mergename, merge->distance, merge->angle, qh_setsize(qh facet_mergeset))); + } } /* appendmergeset */ +/*--------------------------------- + + qh_appendvertexmerge( vertex, vertex2, mergetype, distance, ridge1, ridge2 ) + appends a vertex merge to qh.vertex_mergeset + MRGsubridge includes two ridges (from MRGdupridge) + MRGvertices includes two ridges + + notes: + called by qh_getpinchedmerges for MRGsubridge + called by qh_maybe_duplicateridge and qh_maybe_duplicateridges for MRGvertices + only way to add a vertex merge to qh.vertex_mergeset + checked by qh_next_vertexmerge +*/ +void qh_appendvertexmerge(vertexT *vertex, vertexT *destination, mergeType mergetype, realT distance, ridgeT *ridge1, ridgeT *ridge2) { + mergeT *merge; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + const char *mergename; + + if (!qh vertex_mergeset) { + qh_fprintf(qh ferr, 6387, "qhull internal error (qh_appendvertexmerge): expecting temp set defined for qh.vertex_mergeset (0x%x). Got NULL\n", + qh vertex_mergeset); + /* otherwise qh_setappend creates a new set that is not freed by qh_freebuild() */ + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT); + merge->angle= qh_ANGLEnone; + merge->distance= distance; + merge->facet1= NULL; + merge->facet2= NULL; + merge->vertex1= vertex; + merge->vertex2= destination; + merge->ridge1= ridge1; + merge->ridge2= ridge2; + merge->mergetype= mergetype; + if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *)) + mergename= mergetypes[mergetype]; + else + mergename= mergetypes[MRGnone]; + if (mergetype == MRGvertices) { + if (!ridge1 || !ridge2 || ridge1 == ridge2) { + qh_fprintf(qh ferr, 6106, "qhull internal error (qh_appendvertexmerge): expecting two distinct ridges for MRGvertices. Got r%d r%d\n", + getid_(ridge1), getid_(ridge2)); + qh_errexit(qh_ERRqhull, NULL, ridge1); + } + } + qh_setappend(&(qh vertex_mergeset), merge); + trace3((qh ferr, 3034, "qh_appendvertexmerge: append merge v%d into v%d r%d r%d dist %2.2g type %d (%s)\n", + vertex->id, destination->id, getid_(ridge1), getid_(ridge2), distance, merge->mergetype, mergename)); +} /* appendvertexmerge */ + + /*--------------------------------- @@ -404,10 +588,60 @@ setT *qh_basevertices(facetT *samecycle) { return vertices; } /* basevertices */ +/*--------------------------------- + + qh_check_dupridge( facet1, dist1, facet2, dist2 ) + Check dupridge 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 dupridge with the minimum distance between vertices + Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x) + + notes: + only called from qh_forcedmerges +*/ +void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2) { + vertexT *vertex, **vertexp, *vertexA, **vertexAp; + realT dist, innerplane, mergedist, outerplane, prevdist, ratio, vertexratio; + realT minvertex= REALmax; + + mergedist= fmin_(dist1, dist2); + qh_outerinner(NULL, &outerplane, &innerplane); /* ratio from qh_printsummary */ + FOREACHvertex_(facet1->vertices) { /* The dupridge 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); + /* Not quite correct. A facet may have a dupridge and another pair of nearly adjacent vertices. */ + } + } + } + prevdist= fmax_(outerplane, innerplane); + maximize_(prevdist, qh ONEmerge + qh DISTround); + maximize_(prevdist, qh MINoutside + qh DISTround); + ratio= mergedist/prevdist; + vertexratio= minvertex/prevdist; + trace0((qh ferr, 16, "qh_check_dupridge: dupridge between f%d and f%d (vertex dist %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 ferr, 6271, "qhull topology error (qh_check_dupridge): wide merge (%.1fx wider) due to dupridge between f%d and f%d (vertex dist %2.2g), merge dist %2.2g, while processing p%d\n- Allow error with option 'Q12'\n", + ratio, facet1->id, facet2->id, minvertex, mergedist, qh furthest_id); + if (vertexratio < qh_WIDEpinched) + qh_fprintf(qh ferr, 8145, "- Experimental option merge-pinched-vertices ('Q14') may avoid this error. It merges nearly adjacent vertices.\n"); + if (qh DELAUNAY) + qh_fprintf(qh ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n"); + if (!qh ALLOWwide) + qh_errexit2(qh_ERRwide, facet1, facet2); + } +} /* check_dupridge */ + /*--------------------------------- - qh_checkconnect() + qh_checkconnect( ) check that new facets are connected new facets are on qh.newfacet_list @@ -441,7 +675,7 @@ void qh_checkconnect(void /* qh.newfacet_list */) { FORALLnew_facets { if (newfacet->visitid == qh visit_id) break; - qh_fprintf(qh ferr, 6094, "qhull error: f%d is not attached to the new facets\n", + qh_fprintf(qh ferr, 6094, "qhull internal error (qh_checkconnect): f%d is not attached to the new facets\n", newfacet->id); errfacet= newfacet; } @@ -449,6 +683,68 @@ void qh_checkconnect(void /* qh.newfacet_list */) { qh_errexit(qh_ERRqhull, errfacet, NULL); } /* checkconnect */ +/*--------------------------------- + + qh_checkdelfacet( facet, mergeset ) + check that mergeset does not reference facet + +*/ +void qh_checkdelfacet(facetT *facet, setT *mergeset) { + mergeT *merge, **mergep; + + FOREACHmerge_(mergeset) { + if (merge->facet1 == facet || merge->facet2 == facet) { + qh_fprintf(qh ferr, 6390, "qhull internal error (qh_checkdelfacet): cannot delete f%d. It is referenced by merge f%d f%d mergetype %d\n", + facet->id, merge->facet1->id, getid_(merge->facet2), merge->mergetype); + qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2); + } + } +} /* checkdelfacet */ + +/*--------------------------------- + + qh_checkdelridge( ) + check that qh_delridge_merge is not needed for deleted ridges + + notes: + called from qh_mergecycle, qh_makenewfacets, qh_attachnewfacets + errors if qh.vertex_mergeset is non-empty + errors if any visible or new facet has a ridge with r.nonconvex set + assumes that vertex.delfacet is not needed +*/ +void qh_checkdelridge(void /* qh.visible_facets, vertex_mergeset */) { + facetT *newfacet, *visible; + ridgeT *ridge, **ridgep; + + if (!SETempty_(qh vertex_mergeset)) { + qh_fprintf(qh ferr, 6382, "qhull internal error (qh_checkdelridge): expecting empty qh.vertex_mergeset in order to avoid calling qh_delridge_merge. Got %d merges\n", qh_setsize(qh vertex_mergeset)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + + FORALLnew_facets { + FOREACHridge_(newfacet->ridges) { + if (ridge->nonconvex) { + qh_fprintf(qh ferr, 6313, "qhull internal error (qh_checkdelridge): unexpected 'nonconvex' flag for ridge r%d in newfacet f%d. Otherwise need to call qh_delridge_merge\n", + ridge->id, newfacet->id); + qh_errexit(qh_ERRqhull, newfacet, ridge); + } + } + } + + FORALLvisible_facets { + FOREACHridge_(visible->ridges) { + if (ridge->nonconvex) { + qh_fprintf(qh ferr, 6385, "qhull internal error (qh_checkdelridge): unexpected 'nonconvex' flag for ridge r%d in visible facet f%d. Otherwise need to call qh_delridge_merge\n", + ridge->id, visible->id); + qh_errexit(qh_ERRqhull, visible, ridge); + } + } + } +} /* checkdelridge */ + + /*--------------------------------- @@ -472,6 +768,7 @@ void qh_checkconnect(void /* qh.newfacet_list */) { clears qh.ZEROall_ok if any problems or coplanar facets notes: + called by qh_premerge (qh.CHECKzero, 'C-0') and qh_qhull ('Qx') uses qh.vertex_visit horizon facets may define multiple new facets @@ -486,9 +783,9 @@ void qh_checkconnect(void /* qh.newfacet_list */) { test the other vertices in the facet's horizon facet */ boolT qh_checkzero(boolT testall) { - facetT *facet, *neighbor, **neighborp; + facetT *facet, *neighbor; facetT *horizon, *facetlist; - int neighbor_i; + int neighbor_i, neighbor_n; vertexT *vertex, **vertexp; realT dist; @@ -510,19 +807,17 @@ boolT qh_checkzero(boolT testall) { } FORALLfacet_(facetlist) { qh vertex_visit++; - neighbor_i= 0; horizon= NULL; - FOREACHneighbor_(facet) { + FOREACHneighbor_i_(facet) { if (!neighbor_i && !testall) { horizon= neighbor; - neighbor_i++; continue; /* horizon facet tested in qh_findhorizon */ } - vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT); + vertex= SETelemt_(facet->vertices, neighbor_i, vertexT); vertex->visitid= qh vertex_visit; zzinc_(Zdistzero); qh_distplane(vertex->point, neighbor, &dist); - if (dist >= -qh DISTround) { + if (dist >= -2 * qh DISTround) { /* need 2x for qh_distround and 'Rn' for qh_checkconvex, same as qh.premerge_centrum */ qh ZEROall_ok= False; if (!qh MERGEexact || testall || dist > qh DISTround) goto LABELnonconvex; @@ -533,10 +828,10 @@ boolT qh_checkzero(boolT testall) { if (vertex->visitid != qh vertex_visit) { zzinc_(Zdistzero); qh_distplane(vertex->point, facet, &dist); - if (dist >= -qh DISTround) { + if (dist >= -2 * qh DISTround) { qh ZEROall_ok= False; if (!qh MERGEexact || dist > qh DISTround) - goto LABELnonconvex; + goto LABELnonconvexhorizon; } break; } @@ -545,55 +840,84 @@ boolT qh_checkzero(boolT testall) { } trace2((qh ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall, (qh MERGEexact && !testall) ? - "not concave, flipped, or duplicate ridged" : "clearly convex")); + "not concave, flipped, or dupridge" : "clearly convex")); return True; LABELproblem: qh ZEROall_ok= False; - trace2((qh ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n", - facet->id)); + trace2((qh ferr, 2013, "qh_checkzero: qh_premerge is needed. New facet f%d or its horizon f%d is non-simplicial, flipped, dupridge, or mergehorizon\n", + facet->id, horizon->id)); return False; LABELnonconvex: trace2((qh ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n", facet->id, neighbor->id, vertex->id, dist)); return False; + + LABELnonconvexhorizon: + trace2((qh ferr, 2060, "qh_checkzero: facet f%d and horizon f%d are not clearly convex. v%d dist %.2g\n", + facet->id, horizon->id, vertex->id, dist)); + return False; } /* checkzero */ /*--------------------------------- + >-------------------------------- + + qh_compare_anglemerge( mergeA, mergeB ) + used by qsort() to order qh.facet_mergeset by mergetype and angle (qh.ANGLEmerge, 'Q1') + lower numbered mergetypes done first (MRGcoplanar before MRGconcave) - qh_compareangle( angle1, angle2 ) - used by qsort() to order merges by angle + notes: + qh_all_merges processes qh.facet_mergeset by qh_setdellast + [mar'19] evaluated various options with eg/q_benchmark and merging of pinched vertices (Q14) */ -int qh_compareangle(const void *p1, const void *p2) { +int qh_compare_anglemerge(const void *p1, const void *p2) { const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); - return((a->angle > b->angle) ? 1 : -1); -} /* compareangle */ + if (a->mergetype != b->mergetype) + return (a->mergetype < b->mergetype ? 1 : -1); /* select MRGcoplanar (1) before MRGconcave (3) */ + else + return (a->angle > b->angle ? 1 : -1); /* select coplanar merge (1.0) before sharp merge (-0.5) */ +} /* compare_anglemerge */ /*--------------------------------- + >-------------------------------- - qh_comparemerge( merge1, merge2 ) - used by qsort() to order merges + qh_compare_facetmerge( mergeA, mergeB ) + used by qsort() to order merges by mergetype, first merge, first + lower numbered mergetypes done first (MRGcoplanar before MRGconcave) + if same merge type, flat merges are first + + notes: + qh_all_merges processes qh.facet_mergeset by qh_setdellast + [mar'19] evaluated various options with eg/q_benchmark and merging of pinched vertices (Q14) */ -int qh_comparemerge(const void *p1, const void *p2) { +int qh_compare_facetmerge(const void *p1, const void *p2) { const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); - return(a->type - b->type); -} /* comparemerge */ + if (a->mergetype != b->mergetype) + return (a->mergetype < b->mergetype ? 1 : -1); /* select MRGcoplanar (1) before MRGconcave (3) */ + else if (a->mergetype == MRGanglecoplanar) + return (a->angle > b->angle ? 1 : -1); /* if MRGanglecoplanar, select coplanar merge (1.0) before sharp merge (-0.5) */ + else + return (a->distance < b->distance ? 1 : -1); /* select flat (0.0) merge before wide (1e-10) merge */ +} /* compare_facetmerge */ /*--------------------------------- - qh_comparevisit( vertex1, vertex2 ) + qh_comparevisit( vertexA, vertexB ) used by qsort() to order vertices by their visitid + + notes: + only called by qh_find_newvertex */ int qh_comparevisit(const void *p1, const void *p2) { const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); - return(a->visitid - b->visitid); + if (a->visitid > b->visitid) + return 1; + return -1; } /* comparevisit */ /*-top; otherfacet= atridge->bottom; + atridge->nonconvex= False; FOREACHridge_(facet->ridges) { - if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) { - ridge->nonconvex= True; - trace4((qh ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n", - atridge->id, ridge->id)); - break; + if (otherfacet == ridge->top || otherfacet == ridge->bottom) { + if (ridge != atridge) { + ridge->nonconvex= True; + trace4((qh ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d between f%d and f%d\n", + atridge->id, ridge->id, facet->id, otherfacet->id)); + break; + } } } } /* copynonconvex */ @@ -630,15 +957,14 @@ void qh_copynonconvex(ridgeT *atridge) { >-------------------------------- qh_degen_redundant_facet( facet ) - check facet for degen. or redundancy + check for a degenerate (too few neighbors) or redundant (subset of vertices) facet notes: + called at end of qh_mergefacet, qh_renamevertex, and qh_reducevertices 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() + see: qh_test_redundant_neighbors, qh_maydropneighbor design: test for redundant neighbor @@ -648,9 +974,20 @@ void qh_degen_redundant_facet(facetT *facet) { vertexT *vertex, **vertexp; facetT *neighbor, **neighborp; - trace4((qh ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n", + trace3((qh ferr, 3028, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n", facet->id)); + if (facet->flipped) { + trace2((qh ferr, 3074, "qh_degen_redundant_facet: f%d is flipped, will merge later\n", facet->id)); + return; + } FOREACHneighbor_(facet) { + if (neighbor->flipped) /* disallow merge of non-flipped into flipped, neighbor will be merged later */ + continue; + if (neighbor->visible) { + qh_fprintf(qh ferr, 6357, "qhull internal error (qh_degen_redundant_facet): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + qh_errexit2(qh_ERRqhull, facet, neighbor); + } qh vertex_visit++; FOREACHvertex_(neighbor->vertices) vertex->visitid= qh vertex_visit; @@ -659,86 +996,82 @@ void qh_degen_redundant_facet(facetT *facet) { break; } if (!vertex) { - qh_appendmergeset(facet, neighbor, MRGredundant, NULL); trace2((qh ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id)); + qh_appendmergeset(facet, neighbor, MRGredundant, 0.0, 1.0); return; } } if (qh_setsize(facet->neighbors) < qh hull_dim) { - qh_appendmergeset(facet, facet, MRGdegen, NULL); - trace2((qh ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id)); + qh_appendmergeset(facet, facet, MRGdegen, 0.0, 1.0); + trace2((qh ferr, 2016, "qh_degen_redundant_facet: f%d is degenerate.\n", facet->id)); } } /* degen_redundant_facet */ /*--------------------------------- + >-------------------------------- - qh_degen_redundant_neighbors( facet, delfacet, ) - append degenerate and redundant neighbors to facet_mergeset - if delfacet, - only checks neighbors of both delfacet and facet - also checks current facet for degeneracy + qh_delridge_merge( ridge ) + delete ridge due to a merge 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() + only called by merge.c (qh_mergeridges, qh_renameridgevertex) + ridges also freed in qh_freeqhull and qh_mergecycle_ridges design: - test for degenerate facet - test for redundant neighbor - test for degenerate neighbor + if needed, moves ridge.nonconvex to another ridge + sets vertex.delridge for qh_reducevertices + deletes ridge from qh.vertex_mergeset + deletes ridge from its neighboring facets + frees up its memory */ -void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet) { +void qh_delridge_merge(ridgeT *ridge) { vertexT *vertex, **vertexp; - facetT *neighbor, **neighborp; - int size; + mergeT *merge; + int merge_i, merge_n; - trace4((qh ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n", - facet->id, getid_(delfacet))); - if ((size= qh_setsize(facet->neighbors)) < qh hull_dim) { - qh_appendmergeset(facet, facet, MRGdegen, NULL); - trace2((qh ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size)); - } - if (!delfacet) - delfacet= facet; - qh vertex_visit++; - FOREACHvertex_(facet->vertices) - vertex->visitid= qh vertex_visit; - FOREACHneighbor_(delfacet) { - /* uses early out instead of checking vertex count */ - if (neighbor == facet) - continue; - FOREACHvertex_(neighbor->vertices) { - if (vertex->visitid != qh vertex_visit) - break; - } - if (!vertex) { - qh_appendmergeset(neighbor, facet, MRGredundant, NULL); - trace2((qh ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id)); - } - } - FOREACHneighbor_(delfacet) { /* redundant merges occur first */ - if (neighbor == facet) - continue; - if ((size= qh_setsize(neighbor->neighbors)) < qh hull_dim) { - qh_appendmergeset(neighbor, neighbor, MRGdegen, NULL); - trace2((qh ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id)); + trace3((qh ferr, 3036, "qh_delridge_merge: delete ridge r%d between f%d and f%d\n", + ridge->id, ridge->top->id, ridge->bottom->id)); + if (ridge->nonconvex) + qh_copynonconvex(ridge); + FOREACHvertex_(ridge->vertices) + vertex->delridge= True; + FOREACHmerge_i_(qh vertex_mergeset) { + if (merge->ridge1 == ridge || merge->ridge2 == ridge) { + trace3((qh ferr, 3029, "qh_delridge_merge: drop merge of v%d into v%d (dist %2.2g r%d r%d) due to deleted, duplicated ridge r%d\n", + merge->vertex1->id, merge->vertex2->id, merge->distance, merge->ridge1->id, merge->ridge2->id, ridge->id)); + if (merge->ridge1 == ridge) + merge->ridge2->mergevertex= False; + else + merge->ridge1->mergevertex= False; + qh_setdelnth(qh vertex_mergeset, merge_i); + merge_i--; merge_n--; /* next merge after deleted */ } } -} /* degen_redundant_neighbors */ + qh_setdel(ridge->top->ridges, ridge); + qh_setdel(ridge->bottom->ridges, ridge); + qh_delridge(ridge); +} /* delridge_merge */ + + +/*--------------------------------- + + qh_drop_mergevertex( merge ) + clear mergevertex flags for ridges of a vertex merge +*/ +void qh_drop_mergevertex(mergeT *merge) +{ + if (merge->mergetype == MRGvertices) { + merge->ridge1->mergevertex= False; + merge->ridge1->mergevertex2= True; + merge->ridge2->mergevertex= False; + merge->ridge2->mergevertex2= True; + trace3((qh ferr, 3032, "qh_drop_mergevertex: unset mergevertex for r%d and r%d due to dropped vertex merge v%d to v%d. Sets mergevertex2\n", + merge->ridge1->id, merge->ridge2->id, merge->vertex1->id, merge->vertex2->id)); + } +} /* drop_mergevertex */ /*--------------------------------- @@ -750,11 +1083,14 @@ void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet) { returns: newvertex or NULL - each ridge includes both vertex and oldvertex - vertices sorted by number of deleted ridges + each ridge includes both newvertex and oldvertex + vertices without oldvertex sorted by number of deleted ridges + qh.vertex_visit updated + sets v.seen notes: - modifies vertex->visitid + called by qh_redundant_vertex due to vertex->delridge and qh_rename_sharedvertex + sets vertex->visitid to 0..setsize() for vertices new vertex is in one of the ridges renaming will not cause a duplicate ridge renaming will minimize the number of deleted ridges @@ -762,13 +1098,13 @@ void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet) { design: for each vertex in vertices - set vertex->visitid to number of references in ridges + set vertex->visitid to number of ridges remove unvisited vertices set qh.vertex_visit above all possible values - sort vertices by number of references in ridges + sort vertices by number of ridges (minimize ridges that need renaming 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 + find the first vertex that would not cause a duplicate ridge after a rename */ vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) { vertexT *vertex, **vertexp; @@ -776,6 +1112,7 @@ vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) { ridgeT *ridge, **ridgep; int size, hashsize; int hash; + unsigned int maxvisit; #ifndef qh_NOtrace if (qh IStracing >= 4) { @@ -788,11 +1125,19 @@ vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) { qh_fprintf(qh ferr, 8066, "\n"); } #endif - FOREACHvertex_(vertices) - vertex->visitid= 0; FOREACHridge_(ridges) { FOREACHvertex_(ridge->vertices) - vertex->visitid++; + vertex->seen= False; + } + FOREACHvertex_(vertices) { + vertex->visitid= 0; /* v.visitid will be number of ridges */ + vertex->seen= True; + } + FOREACHridge_(ridges) { + FOREACHvertex_(ridge->vertices) { + if (vertex->seen) + vertex->visitid++; + } } FOREACHvertex_(vertices) { if (!vertex->visitid) { @@ -800,7 +1145,8 @@ vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) { vertexp--; /* repeat since deleted this vertex */ } } - qh vertex_visit += (unsigned int)qh_setsize(ridges); + maxvisit= (unsigned int)qh_setsize(ridges); + maximize_(qh vertex_visit, maxvisit); if (!qh_setsize(vertices)) { trace4((qh ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n", oldvertex->id)); @@ -819,10 +1165,10 @@ vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) { FOREACHridge_(ridges) qh_hashridge(qh hash_table, hashsize, ridge, oldvertex); FOREACHvertex_(vertices) { - newridges= qh_vertexridges(vertex); + newridges= qh_vertexridges(vertex, !qh_ALL); FOREACHridge_(newridges) { if (qh_hashridge_find(qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) { - zinc_(Zdupridge); + zinc_(Zvertexridge); break; } } @@ -836,31 +1182,163 @@ vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) { vertex->id, oldvertex->id, qh_setsize(vertices), qh_setsize(ridges))); }else { zinc_(Zfindfail); - trace0((qh ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n", + trace0((qh ferr, 14, "qh_find_newvertex: no vertex for renaming v%d (all duplicated ridges) during p%d\n", oldvertex->id, qh furthest_id)); } qh_setfree(&qh hash_table); return vertex; } /* find_newvertex */ +/*--------------------------------- + + qh_findbest_pinchedvertex( merge, apex, nearestp, distp ) + Determine the best pinched vertex to rename as its nearest neighboring vertex + Renaming will remove a duplicate MRGdupridge in newfacet_list + + returns: + pinched vertex (either apex or subridge), nearest vertex (subridge or neighbor vertex), and the distance between them + + notes: + only called by qh_getpinchedmerges + assumes qh.VERTEXneighbors + see qh_findbest_ridgevertex + + design: + if the facets have the same vertices + return the nearest vertex pair + else + the subridge is the intersection of the two new facets minus the apex + the subridge consists of qh.hull_dim-2 horizon vertices + the subridge is also a matched ridge for the new facets (its duplicate) + determine the nearest vertex to the apex + determine the nearest pair of subridge vertices + for each vertex in the subridge + determine the nearest neighbor vertex (not in the subridge) +*/ +vertexT *qh_findbest_pinchedvertex(mergeT *merge, vertexT *apex, vertexT **nearestp, coordT *distp /* qh.newfacet_list */) { + vertexT *vertex, **vertexp, *vertexA, **vertexAp; + vertexT *bestvertex= NULL, *bestpinched= NULL; + setT *subridge, *maybepinched; + coordT dist, bestdist= REALmax; + coordT pincheddist= (qh ONEmerge+qh DISTround)*qh_RATIOpinchedsubridge; + + if (!merge->facet1->simplicial || !merge->facet2->simplicial) { + qh_fprintf(qh ferr, 6351, "qhull internal error (qh_findbest_pinchedvertex): expecting merge of adjacent, simplicial new facets. f%d or f%d is not simplicial\n", + merge->facet1->id, merge->facet2->id); + qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2); + } + subridge= qh_vertexintersect_new(merge->facet1->vertices, merge->facet2->vertices); /* new setT. No error_exit() */ + if (qh_setsize(subridge) == qh hull_dim) { /* duplicate vertices */ + bestdist= qh_vertex_bestdist2(subridge, &bestvertex, &bestpinched); + if(bestvertex == apex) { + bestvertex= bestpinched; + bestpinched= apex; + } + }else { + qh_setdel(subridge, apex); + if (qh_setsize(subridge) != qh hull_dim - 2) { + qh_fprintf(qh ferr, 6409, "qhull internal error (qh_findbest_pinchedvertex): expecting subridge of qh.hull_dim-2 vertices for the intersection of new facets f%d and f%d minus their apex. Got %d vertices\n", + merge->facet1->id, merge->facet2->id, qh_setsize(subridge)); + qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2); + } + FOREACHvertex_(subridge) { + dist= qh_pointdist(vertex->point, apex->point, qh hull_dim); + if (dist < bestdist) { + bestpinched= apex; + bestvertex= vertex; + bestdist= dist; + } + } + if (bestdist > pincheddist) { + FOREACHvertex_(subridge) { + FOREACHvertexA_(subridge) { + if (vertexA->id > vertex->id) { /* once per vertex pair, do not compare addresses */ + dist= qh_pointdist(vertexA->point, vertex->point, qh hull_dim); + if (dist < bestdist) { + bestpinched= vertexA; + bestvertex= vertex; + bestdist= dist; + } + } + } + } + } + if (bestdist > pincheddist) { + FOREACHvertexA_(subridge) { + maybepinched= qh_neighbor_vertices(vertexA, subridge); /* subridge and apex tested above */ + FOREACHvertex_(maybepinched) { + dist= qh_pointdist(vertex->point, vertexA->point, qh hull_dim); + if (dist < bestdist) { + bestvertex= vertex; + bestpinched= vertexA; + bestdist= dist; + } + } + qh_settempfree(&maybepinched); + } + } + } + *distp= bestdist; + qh_setfree(&subridge); /* qh_err_exit not called since allocated */ + if (!bestvertex) { /* should never happen if qh.hull_dim > 2 */ + qh_fprintf(qh ferr, 6274, "qhull internal error (qh_findbest_pinchedvertex): did not find best vertex for subridge of dupridge between f%d and f%d, while processing p%d\n", merge->facet1->id, merge->facet2->id, qh furthest_id); + qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2); + } + *nearestp= bestvertex; + trace2((qh ferr, 2061, "qh_findbest_pinchedvertex: best pinched p%d(v%d) and vertex p%d(v%d) are closest (%2.2g) for duplicate subridge between f%d and f%d\n", + qh_pointid(bestpinched->point), bestpinched->id, qh_pointid(bestvertex->point), bestvertex->id, bestdist, merge->facet1->id, merge->facet2->id)); + return bestpinched; +} /* findbest_pinchedvertex */ + +/*--------------------------------- + + qh_findbest_ridgevertex( ridge, pinchedp, distp ) + Determine the best vertex/pinched-vertex to merge for ridges with the same vertices + + returns: + vertex, pinched vertex, and the distance between them + + notes: + assumes qh.hull_dim>=3 + see qh_findbest_pinchedvertex + +*/ +vertexT *qh_findbest_ridgevertex(ridgeT *ridge, vertexT **pinchedp, coordT *distp) { + vertexT *bestvertex; + + *distp= qh_vertex_bestdist2(ridge->vertices, &bestvertex, pinchedp); + trace4((qh ferr, 4069, "qh_findbest_ridgevertex: best pinched p%d(v%d) and vertex p%d(v%d) are closest (%2.2g) for duplicated ridge r%d (same vertices) between f%d and f%d\n", + qh_pointid((*pinchedp)->point), (*pinchedp)->id, qh_pointid(bestvertex->point), bestvertex->id, *distp, ridge->id, ridge->top->id, ridge->bottom->id)); + return bestvertex; +} /* findbest_ridgevertex */ + /*--------------------------------- - qh_findbest_test( testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist ) + qh_findbest_test( testcentrum, facet, neighbor, &bestfacet, &dist, &mindist, &maxdist ) test neighbor of facet for qh_findbestneighbor() if testcentrum, tests centrum (assumes it is defined) else tests vertices + initially *bestfacet==NULL and *dist==REALmax returns: if a better facet (i.e., vertices/centrum of facet closer to neighbor) updates bestfacet, dist, mindist, and maxdist + + notes: + called by qh_findbestneighbor + ignores pairs of flipped facets, unless that's all there is */ void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor, facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) { realT dist, mindist, maxdist; + if (facet->flipped && neighbor->flipped && *bestfacet && !(*bestfacet)->flipped) + return; /* do not merge flipped into flipped facets */ if (testcentrum) { zzinc_(Zbestdist); qh_distplane(facet->center, neighbor, &dist); @@ -917,7 +1395,7 @@ facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT int size= qh_setsize(facet->vertices); if(qh CENTERtype==qh_ASvoronoi){ - qh_fprintf(qh ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id); + qh_fprintf(qh ferr, 6272, "qhull internal error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id); qh_errexit(qh_ERRqhull, facet, NULL); } *distp= REALmax; @@ -944,7 +1422,6 @@ facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT } if (!bestfacet) { qh_fprintf(qh ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id); - qh_errexit(qh_ERRqhull, facet, NULL); } if (testcentrum) @@ -984,44 +1461,50 @@ void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) { realT dist, mindist, maxdist; mergeT *merge, **mergep; setT *othermerges; - int nummerge=0; + int nummerge= 0, numdegen= 0; trace4((qh ferr, 4024, "qh_flippedmerges: begin\n")); FORALLfacet_(facetlist) { if (facet->flipped && !facet->visible) - qh_appendmergeset(facet, facet, MRGflip, NULL); + qh_appendmergeset(facet, facet, MRGflip, 0.0, 1.0); + } + othermerges= qh_settemppop(); + if(othermerges != qh facet_mergeset) { + qh_fprintf(qh ferr, 6392, "qhull internal error (qh_flippedmerges): facet_mergeset (%d merges) not at top of tempstack (%d merges)\n", + qh_setsize(qh facet_mergeset), qh_setsize(othermerges)); + qh_errexit(qh_ERRqhull, NULL, NULL); } - othermerges= qh_settemppop(); /* was facet_mergeset */ qh facet_mergeset= qh_settemp(qh TEMPsize); qh_settemppush(othermerges); FOREACHmerge_(othermerges) { facet1= merge->facet1; - if (merge->type != MRGflip || facet1->visible) + if (merge->mergetype != MRGflip || facet1->visible) continue; if (qh TRACEmerge-1 == zzval_(Ztotmerge)) qhmem.IStracing= qh IStracing= qh TRACElevel; neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist); trace0((qh ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n", facet1->id, neighbor->id, dist, qh furthest_id)); - qh_mergefacet(facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex); + qh_mergefacet(facet1, neighbor, merge->mergetype, &mindist, &maxdist, !qh_MERGEapex); nummerge++; if (qh PRINTstatistics) { zinc_(Zflipped); wadd_(Wflippedtot, dist); wmax_(Wflippedmax, dist); } - qh_merge_degenredundant(); } FOREACHmerge_(othermerges) { if (merge->facet1->visible || merge->facet2->visible) - qh_memfree(merge, (int)sizeof(mergeT)); + qh_memfree(merge, (int)sizeof(mergeT)); /* invalidates merge and othermerges */ else qh_setappend(&qh facet_mergeset, merge); } qh_settempfree(&othermerges); + numdegen += qh_merge_degenredundant(); /* somewhat better here than after each flipped merge -- qtest.sh 10 '500 C1,2e-13 D4' 'd Qbb' */ if (nummerge) *wasmerge= True; - trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge)); + trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped and %d degenredundant facets into a good neighbor\n", + nummerge, numdegen)); } /* flippedmerges */ @@ -1029,16 +1512,19 @@ void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) { >-------------------------------- qh_forcedmerges( wasmerge ) - merge duplicated ridges + merge dupridges + calls qh_check_dupridge to report an error on wide merges + assumes qh_settemppop is qh.facet_mergeset returns: - removes all duplicate ridges on facet_mergeset + removes all dupridges 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, + called by qh_premerge + dupridges 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 @@ -1046,54 +1532,75 @@ void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) { keep it in case of change design: - for each duplicate ridge + for each dupridge find current facets by chasing f.replace links - check for wide merge due to duplicate ridge + check for wide merge due to dupridge determine best direction for facet merge one facet into the other - remove duplicate ridges from qh.facet_mergeset + remove dupridges from qh.facet_mergeset */ void qh_forcedmerges(boolT *wasmerge) { - facetT *facet1, *facet2; + facetT *facet1, *facet2, *merging, *merged, *newfacet; mergeT *merge, **mergep; - realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2; + realT dist, mindist, maxdist, dist2, mindist2, maxdist2; setT *othermerges; - int nummerge=0, numflip=0; + int nummerge=0, numflip=0, numdegen= 0; + boolT wasdupridge= False; if (qh TRACEmerge-1 == zzval_(Ztotmerge)) qhmem.IStracing= qh IStracing= qh TRACElevel; - trace4((qh ferr, 4025, "qh_forcedmerges: begin\n")); + trace3((qh ferr, 3054, "qh_forcedmerges: merge dupridges\n")); othermerges= qh_settemppop(); /* was facet_mergeset */ + if (qh facet_mergeset != othermerges ) { + qh_fprintf(qh ferr, 6279, "qhull internal error (qh_forcedmerges): qh_settemppop (size %d) is not qh facet_mergeset (size %d)\n", + qh_setsize(othermerges), qh_setsize(qh facet_mergeset)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } qh facet_mergeset= qh_settemp(qh TEMPsize); qh_settemppush(othermerges); FOREACHmerge_(othermerges) { - if (merge->type != MRGridge) + if (merge->mergetype != MRGdupridge) continue; + wasdupridge= True; if (qh TRACEmerge-1 == zzval_(Ztotmerge)) 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 */ + facet1= qh_getreplacement(merge->facet1); /* must exist, no qh_merge_degenredunant */ + facet2= qh_getreplacement(merge->facet2); /* previously merged facet, if any */ if (facet1 == facet2) continue; if (!qh_setin(facet2->neighbors, facet1)) { - qh_fprintf(qh ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n", + qh_fprintf(qh ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a dupridge but as f%d and f%d they are no longer neighbors\n", merge->facet1->id, merge->facet2->id, facet1->id, facet2->id); qh_errexit2(qh_ERRqhull, facet1, facet2); } - dist1= qh_getdistance(facet1, facet2, &mindist1, &maxdist1); + dist= qh_getdistance(facet1, facet2, &mindist, &maxdist); dist2= qh_getdistance(facet2, facet1, &mindist2, &maxdist2); - qh_check_dupridge(facet1, dist1, facet2, dist2); - if (dist1 < dist2) - qh_mergefacet(facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex); - else { - qh_mergefacet(facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex); - dist1= dist2; - facet1= facet2; + qh_check_dupridge(facet1, dist, facet2, dist2); + if (dist < dist2) { + if (facet2->flipped && !facet1->flipped && dist2 < qh_WIDEdupridge*(qh ONEmerge+qh DISTround)) { /* prefer merge of flipped facet */ + merging= facet2; + merged= facet1; + dist= dist2; + mindist= mindist2; + maxdist= maxdist2; + }else { + merging= facet1; + merged= facet2; + } + }else { + if (facet1->flipped && !facet2->flipped && dist < qh_WIDEdupridge*(qh ONEmerge+qh DISTround)) { /* prefer merge of flipped facet */ + merging= facet1; + merged= facet2; + }else { + merging= facet2; + merged= facet1; + dist= dist2; + mindist= mindist2; + maxdist= maxdist2; + } } + qh_mergefacet(merging, merged, merge->mergetype, &mindist, &maxdist, !qh_MERGEapex); + numdegen += qh_merge_degenredundant(); /* better here than at end -- qtest.sh 10 '500 C1,2e-13 D4' 'd Qbb' */ if (facet1->flipped) { zinc_(Zmergeflipdup); numflip++; @@ -1101,43 +1608,83 @@ void qh_forcedmerges(boolT *wasmerge) { nummerge++; if (qh PRINTstatistics) { zinc_(Zduplicate); - wadd_(Wduplicatetot, dist1); - wmax_(Wduplicatemax, dist1); + wadd_(Wduplicatetot, dist); + wmax_(Wduplicatemax, dist); } } FOREACHmerge_(othermerges) { - if (merge->type == MRGridge) - qh_memfree(merge, (int)sizeof(mergeT)); + if (merge->mergetype == MRGdupridge) + qh_memfree(merge, (int)sizeof(mergeT)); /* invalidates merge and othermerges */ else qh_setappend(&qh facet_mergeset, merge); } qh_settempfree(&othermerges); - if (nummerge) + if (wasdupridge) { + FORALLnew_facets { + if (newfacet->dupridge) { + newfacet->dupridge= False; + newfacet->mergeridge= False; + newfacet->mergeridge2= False; + if (qh_setsize(newfacet->neighbors) < qh hull_dim) { /* not tested for MRGdupridge */ + qh_appendmergeset(newfacet, newfacet, MRGdegen, 0.0, 1.0); + trace2((qh ferr, 2107, "qh_forcedmerges: dupridge f%d is degenerate with fewer than %d neighbors\n", + newfacet->id, qh hull_dim)); + } + } + } + numdegen += qh_merge_degenredundant(); + } + if (nummerge || numflip) { *wasmerge= True; - trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n", - nummerge, numflip)); + trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets, %d flipped facets, and %d degenredundant facets across dupridges\n", + nummerge, numflip, numdegen)); + } } /* forcedmerges */ /*--------------------------------- - - qh_getmergeset( facetlist ) - determines nonconvex facets on facetlist - tests !tested ridges and nonconvex ridges of !tested facets + >-------------------------------- - returns: - returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged - all ridges tested + qh_freemergesets( ) + free the merge sets 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 + matches qh_initmergesets +*/ +void qh_freemergesets(void) { - see: - qh_getmergeset_initial() + if (!qh facet_mergeset || !qh degen_mergeset || !qh vertex_mergeset) { + qh_fprintf(qh ferr, 6388, "qhull internal error (qh_freemergesets): expecting mergesets. Got a NULL mergeset, qh.facet_mergeset (0x%x), qh.degen_mergeset (0x%x), qh.vertex_mergeset (0x%x)\n", + qh facet_mergeset, qh degen_mergeset, qh vertex_mergeset); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (!SETempty_(qh facet_mergeset) || !SETempty_(qh degen_mergeset) || !SETempty_(qh vertex_mergeset)) { + qh_fprintf(qh ferr, 6389, "qhull internal error (qh_freemergesets): expecting empty mergesets. Got qh.facet_mergeset (%d merges), qh.degen_mergeset (%d merges), qh.vertex_mergeset (%d merges)\n", + qh_setsize(qh facet_mergeset), qh_setsize(qh degen_mergeset), qh_setsize(qh vertex_mergeset)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_settempfree(&qh facet_mergeset); + qh_settempfree(&qh vertex_mergeset); + qh_settempfree(&qh degen_mergeset); +} /* freemergesets */ + +/*--------------------------------- + + qh_getmergeset( facetlist ) + determines nonconvex facets on facetlist + tests !tested ridges and nonconvex ridges of !tested facets + + returns: + returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged + all ridges tested + + notes: + facetlist is qh.facet_newlist, use qh_getmergeset_initial for all facets + 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 design: for each facet on facetlist @@ -1146,12 +1693,13 @@ void qh_forcedmerges(boolT *wasmerge) { test ridge for convexity if non-convex append ridge to qh.facet_mergeset - sort qh.facet_mergeset by angle + sort qh.facet_mergeset by mergetype and angle or distance */ void qh_getmergeset(facetT *facetlist) { facetT *facet, *neighbor, **neighborp; ridgeT *ridge, **ridgep; int nummerges; + boolT simplicial; nummerges= qh_setsize(qh facet_mergeset); trace4((qh ferr, 4026, "qh_getmergeset: started.\n")); @@ -1160,31 +1708,36 @@ void qh_getmergeset(facetT *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; + /* facet must be non-simplicial due to merge to qh.facet_newlist */ FOREACHridge_(facet->ridges) { if (ridge->tested && !ridge->nonconvex) continue; - /* if tested & nonconvex, need to append merge */ + /* if r.tested & r.nonconvex, need to retest and append merge */ neighbor= otherfacet_(ridge, facet); - if (neighbor->seen) { + if (neighbor->seen) { /* another ridge for this facet-neighbor pair was already tested in this loop */ ridge->tested= True; - ridge->nonconvex= False; + ridge->nonconvex= False; /* only one ridge is marked nonconvex per facet-neighbor pair */ }else if (neighbor->visitid != qh visit_id) { - ridge->tested= True; + neighbor->seen= True; ridge->nonconvex= False; - neighbor->seen= True; /* only one ridge is marked nonconvex */ - if (qh_test_appendmerge(facet, neighbor)) + simplicial= False; + if (ridge->simplicialbot && ridge->simplicialtop) + simplicial= True; + if (qh_test_appendmerge(facet, neighbor, simplicial)) ridge->nonconvex= True; + ridge->tested= True; } } + facet->tested= True; } nummerges= qh_setsize(qh facet_mergeset); if (qh ANGLEmerge) - qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle); + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_anglemerge); else - qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge); + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_facetmerge); + nummerges += qh_setsize(qh degen_mergeset); if (qh POSTmerging) { zadd_(Zmergesettot2, nummerges); }else { @@ -1208,9 +1761,7 @@ void qh_getmergeset(facetT *facetlist) { notes: uses visit_id, assumes ridge->nonconvex is False - - see: - qh_getmergeset() + see qh_getmergeset design: for each facet on facetlist @@ -1219,20 +1770,23 @@ void qh_getmergeset(facetT *facetlist) { if non-convex append merge to qh.facet_mergeset mark one of the ridges as nonconvex - sort qh.facet_mergeset by angle + sort qh.facet_mergeset by mergetype and angle or distance */ void qh_getmergeset_initial(facetT *facetlist) { facetT *facet, *neighbor, **neighborp; ridgeT *ridge, **ridgep; int nummerges; + boolT simplicial; qh visit_id++; FORALLfacet_(facetlist) { facet->visitid= qh visit_id; - facet->tested= True; FOREACHneighbor_(facet) { if (neighbor->visitid != qh visit_id) { - if (qh_test_appendmerge(facet, neighbor)) { + simplicial= False; /* ignores r.simplicialtop/simplicialbot. Need to test horizon facets */ + if (facet->simplicial && neighbor->simplicial) + simplicial= True; + if (qh_test_appendmerge(facet, neighbor, simplicial)) { FOREACHridge_(neighbor->ridges) { if (facet == otherfacet_(ridge, neighbor)) { ridge->nonconvex= True; @@ -1242,14 +1796,16 @@ void qh_getmergeset_initial(facetT *facetlist) { } } } + facet->tested= True; FOREACHridge_(facet->ridges) ridge->tested= True; } nummerges= qh_setsize(qh facet_mergeset); if (qh ANGLEmerge) - qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle); + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_anglemerge); else - qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge); + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_facetmerge); + nummerges += qh_setsize(qh degen_mergeset); if (qh POSTmerging) { zadd_(Zmergeinittot2, nummerges); }else { @@ -1259,6 +1815,130 @@ void qh_getmergeset_initial(facetT *facetlist) { trace2((qh ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges)); } /* getmergeset_initial */ +/*--------------------------------- + + qh_getpinchedmerges( apex, maxdist, iscoplanar ) + get pinched merges for dupridges in qh.facet_mergeset + qh.NEWtentative==True + qh.newfacet_list with apex + qh.horizon_list is attached to qh.visible_list instead of qh.newfacet_list + maxdist for vertex-facet of a dupridge + qh.facet_mergeset is empty + qh.vertex_mergeset is a temporary set + + returns: + False if nearest vertex would increase facet width by more than maxdist or qh_WIDEpinched + True and iscoplanar, if the pinched vertex is the apex (i.e., make the apex a coplanar point) + True and !iscoplanar, if should merge a pinched vertex of a dupridge + qh.vertex_mergeset contains one or more MRGsubridge with a pinched vertex and a nearby, neighboring vertex + qh.facet_mergeset is empty + + notes: + called by qh_buildcone_mergepinched + hull_dim >= 3 + a pinched vertex is in a dupridge and the horizon + selects the pinched vertex that is closest to its neighbor + + design: + for each dupridge + determine the best pinched vertex to be merged into a neighboring vertex + if merging the pinched vertex would produce a wide merge (qh_WIDEpinched) + ignore pinched vertex with a warning, and use qh_merge_degenredundant instead + else + append the pinched vertex to vertex_mergeset for merging +*/ +boolT qh_getpinchedmerges(vertexT *apex, coordT maxdupdist, boolT *iscoplanar /* qh.newfacet_list, qh.vertex_mergeset */) { + mergeT *merge, **mergep, *bestmerge= NULL; + vertexT *nearest, *pinched, *bestvertex= NULL, *bestpinched= NULL; + boolT result; + coordT dist, prevdist, bestdist= REALmax/(qh_RATIOcoplanarapex+1.0); /* allow *3.0 */ + realT ratio; + + trace2((qh ferr, 2062, "qh_getpinchedmerges: try to merge pinched vertices for dupridges in new facets with apex p%d(v%d) max dupdist %2.2g\n", + qh_pointid(apex->point), apex->id, maxdupdist)); + *iscoplanar= False; + prevdist= fmax_(qh ONEmerge + qh DISTround, qh MINoutside + qh DISTround); + maximize_(prevdist, qh max_outside); + maximize_(prevdist, -qh min_vertex); + qh_mark_dupridges(qh newfacet_list, !qh_ALL); /* qh.facet_mergeset, creates ridges */ + /* qh_mark_dupridges is called a second time in qh_premerge */ + FOREACHmerge_(qh facet_mergeset) { /* read-only */ + if (merge->mergetype != MRGdupridge) { + qh_fprintf(qh ferr, 6393, "qhull internal error (qh_getpinchedmerges): expecting MRGdupridge from qh_mark_dupridges. Got merge f%d f%d type %d\n", + getid_(merge->facet1), getid_(merge->facet2), merge->mergetype); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + /* dist is distance between vertices */ + pinched= qh_findbest_pinchedvertex(merge, apex, &nearest, &dist /* qh.newfacet_list */); + if (pinched == apex && dist < qh_RATIOcoplanarapex*bestdist) { /* prefer coplanar apex since it always works */ + bestdist= dist/qh_RATIOcoplanarapex; + bestmerge= merge; + bestpinched= pinched; + bestvertex= nearest; + }else if (dist < bestdist) { + bestdist= dist; + bestmerge= merge; + bestpinched= pinched; + bestvertex= nearest; + } + } + result= False; + if (bestmerge && bestdist < maxdupdist) { + ratio= bestdist / prevdist; + if (ratio > qh_WIDEpinched) { + if (bestmerge->facet1->mergehorizon || bestmerge->facet2->mergehorizon) { /* e.g., rbox 175 C3,2e-13 t1539182828 | qhull d */ + trace1((qh ferr, 1051, "qh_getpinchedmerges: dupridge (MRGdupridge) of coplanar horizon would produce a wide merge (%.0fx) due to pinched vertices v%d and v%d (dist %2.2g) for f%d and f%d. qh_mergecycle_all will merge one or both facets\n", + ratio, bestpinched->id, bestvertex->id, bestdist, bestmerge->facet1->id, bestmerge->facet2->id)); + }else { + qh_fprintf(qh ferr, 7081, "qhull precision warning (qh_getpinchedmerges): pinched vertices v%d and v%d (dist %2.2g, %.0fx) would produce a wide merge for f%d and f%d. Will merge dupridge instead\n", + bestpinched->id, bestvertex->id, bestdist, ratio, bestmerge->facet1->id, bestmerge->facet2->id); + } + }else { + if (bestpinched == apex) { + trace2((qh ferr, 2063, "qh_getpinchedmerges: will make the apex a coplanar point. apex p%d(v%d) is the nearest vertex to v%d on dupridge. Dist %2.2g\n", + qh_pointid(apex->point), apex->id, bestvertex->id, bestdist*qh_RATIOcoplanarapex)); + qh coplanar_apex= apex->point; + *iscoplanar= True; + result= True; + }else if (qh_setin(bestmerge->facet1->vertices, bestpinched) != qh_setin(bestmerge->facet2->vertices, bestpinched)) { /* pinched in one facet but not the other facet */ + trace2((qh ferr, 2064, "qh_getpinchedmerges: will merge new facets to resolve dupridge between f%d and f%d with pinched v%d and v%d\n", + bestmerge->facet1->id, bestmerge->facet2->id, bestpinched->id, bestvertex->id)); + qh_appendvertexmerge(bestpinched, bestvertex, MRGsubridge, bestdist, NULL, NULL); + result= True; + }else { + trace2((qh ferr, 2065, "qh_getpinchedmerges: will merge pinched v%d into v%d to resolve dupridge between f%d and f%d\n", + bestpinched->id, bestvertex->id, bestmerge->facet1->id, bestmerge->facet2->id)); + qh_appendvertexmerge(bestpinched, bestvertex, MRGsubridge, bestdist, NULL, NULL); + result= True; + } + } + } + /* delete MRGdupridge, qh_mark_dupridges is called a second time in qh_premerge */ + while ((merge= (mergeT *)qh_setdellast(qh facet_mergeset))) + qh_memfree(merge, (int)sizeof(mergeT)); + return result; +}/* getpinchedmerges */ + +/*--------------------------------- + + qh_hasmerge( mergeset, mergetype, facetA, facetB ) + True if mergeset has mergetype for facetA and facetB +*/ +boolT qh_hasmerge(setT *mergeset, mergeType type, facetT *facetA, facetT *facetB) { + mergeT *merge, **mergep; + + FOREACHmerge_(mergeset) { + if (merge->mergetype == type) { + if (merge->facet1 == facetA && merge->facet2 == facetB) + return True; + if (merge->facet1 == facetB && merge->facet2 == facetA) + return True; + } + } + return False; +}/* hasmerge */ /*--------------------------------- @@ -1341,6 +2021,28 @@ ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, } /* hashridge_find */ +/*--------------------------------- + + qh_initmergesets( ) + initialize the merge sets + if 'all', include qh.degen_mergeset + + notes: + matches qh_freemergesets +*/ +void qh_initmergesets(void /* qh.facet_mergeset,degen_mergeset,vertex_mergeset */) { + + if (qh facet_mergeset || qh degen_mergeset || qh vertex_mergeset) { + qh_fprintf(qh ferr, 6386, "qhull internal error (qh_initmergesets): expecting NULL mergesets. Got qh.facet_mergeset (0x%x), qh.degen_mergeset (0x%x), qh.vertex_mergeset (0x%x)\n", + qh facet_mergeset, qh degen_mergeset, qh vertex_mergeset); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh degen_mergeset= qh_settemp(qh TEMPsize); + qh vertex_mergeset= qh_settemp(qh TEMPsize); + qh facet_mergeset= qh_settemp(qh TEMPsize); /* last temporary set for qh_forcedmerges */ +} /* initmergesets */ + /*--------------------------------- @@ -1350,6 +2052,7 @@ ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, returns: facet with ridges and without qh_MERGEridge ->simplicial is False + if facet was tested, new ridges are tested notes: allows qh_MERGEridge flag @@ -1358,6 +2061,7 @@ ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, see: qh_mergecycle_ridges() + qh_rename_adjacentvertex for qh_merge_pinchedvertices design: look for qh_MERGEridge neighbors @@ -1365,7 +2069,7 @@ ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, 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) + delete qh_MERGEridge flags (previously processed by qh_mark_dupridges) */ void qh_makeridges(facetT *facet) { facetT *neighbor, **neighborp; @@ -1392,26 +2096,40 @@ void qh_makeridges(facetT *facet) { ridge= qh_newridge(); ridge->vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim, neighbor_i, 0); - toporient= facet->toporient ^ (neighbor_i & 0x1); + toporient= (boolT)(facet->toporient ^ (neighbor_i & 0x1)); if (toporient) { ridge->top= facet; ridge->bottom= neighbor; + ridge->simplicialtop= True; + ridge->simplicialbot= neighbor->simplicial; }else { ridge->top= neighbor; ridge->bottom= facet; + ridge->simplicialtop= neighbor->simplicial; + ridge->simplicialbot= True; } + if (facet->tested && !mergeridge) + ridge->tested= True; #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; + ridge->simplicialtop= True; + ridge->simplicialbot= neighbor->simplicial; }else { ridge->top= facet; ridge->bottom= neighbor; + ridge->simplicialtop= neighbor->simplicial; + ridge->simplicialbot= True; } #endif qh_setappend(&(facet->ridges), ridge); + trace5((qh ferr, 5005, "makeridges: appended r%d to ridges for f%d. Next is ridges for neighbor f%d\n", + ridge->id, facet->id, neighbor->id)); qh_setappend(&(neighbor->ridges), ridge); + if (qh ridge_id == qh traceridge_id) + qh traceridge= ridge; } } if (mergeridge) { @@ -1424,46 +2142,64 @@ void qh_makeridges(facetT *facet) { /*--------------------------------- - qh_mark_dupridges( facetlist ) + qh_mark_dupridges( facetlist, allmerges ) add duplicated ridges to qh.facet_mergeset - facet->dupridge is true + facet-dupridge is true if it contains a subridge shared by more than one new facet + for each such facet, one has a neighbor marked qh_MERGEridge + allmerges is true if merging dupridges + allmerges is false if merging pinched vertices followed by retry addpoint + qh_mark_dupridges will be called again if pinched vertices not found 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 + dupridges on qh.facet_mergeset (MRGdupridge) + f.mergeridge and f.mergeridge2 set for facet + f.mergeridge set for neighbor + if allmerges is true + make ridges for facets with dupridges as marked by qh_MERGEridge and both sides facet->dupridge + removes qh_MERGEridge from neighbor sets notes: - duplicate ridges occur when the horizon is pinched, + called by qh_premerge and qh_getpinchedmerges + dupridges are due to duplicate subridges i.e. a subridge occurs in more than two horizon ridges. - could rename vertices that pinch the horizon (thus removing subridge) + i.e., a ridge has more than two neighboring facets + dupridges occur in at least two cases + 1) a pinched horizon with nearly adjacent vertices -> merge the vertices (qh_getpinchedmerges) + 2) more than one newfacet for a horizon face -> merge coplanar facets (qh_premerge) + qh_matchdupridge previously identified the furthest apart pair of facets to retain + they must have a matching subridge and the same orientation + only way to set facet->mergeridge and mergeridge2 uses qh.visit_id design: for all facets on facetlist - if facet contains a duplicate ridge + if facet contains a dupridge 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 + if neighbor contains a dupridge and the back link is qh_MERGEridge - append duplicate ridge to qh.facet_mergeset - for each duplicate ridge + append dupridge to qh.facet_mergeset + exit if !allmerges for repeating qh_mark_dupridges later + for each dupridge make ridge sets in preparation for merging remove qh_MERGEridge from neighbor set - for each duplicate ridge + for each dupridge restore the missing neighbor from the neighbor set that was qh_MERGEridge add the missing ridge for this neighbor */ -void qh_mark_dupridges(facetT *facetlist) { +void qh_mark_dupridges(facetT *facetlist, boolT allmerges) { facetT *facet, *neighbor, **neighborp; int nummerge=0; mergeT *merge, **mergep; - - trace4((qh ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n")); + trace4((qh ferr, 4028, "qh_mark_dupridges: identify dupridges in facetlist f%d, allmerges? %d\n", + facetlist->id, allmerges)); + FORALLfacet_(facetlist) { /* not necessary for first call */ + facet->mergeridge2= False; + facet->mergeridge= False; + } FORALLfacet_(facetlist) { if (facet->dupridge) { FOREACHneighbor_(facet) { @@ -1471,43 +2207,198 @@ void qh_mark_dupridges(facetT *facetlist) { facet->mergeridge= True; continue; } - if (neighbor->dupridge - && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */ - qh_appendmergeset(facet, neighbor, MRGridge, NULL); - facet->mergeridge2= True; - facet->mergeridge= True; - nummerge++; + if (neighbor->dupridge) { + if (!qh_setin(neighbor->neighbors, facet)) { /* i.e., it is qh_MERGEridge, neighbors are distinct */ + qh_appendmergeset(facet, neighbor, MRGdupridge, 0.0, 1.0); + facet->mergeridge2= True; + facet->mergeridge= True; + nummerge++; + }else if (qh_setequal(facet->vertices, neighbor->vertices)) { /* neighbors are the same except for horizon and qh_MERGEridge, see QH7085 */ + trace3((qh ferr, 3043, "qh_mark_dupridges): dupridge due to duplicate vertices for subridges f%d and f%d\n", + facet->id, neighbor->id)); + qh_appendmergeset(facet, neighbor, MRGdupridge, 0.0, 1.0); + facet->mergeridge2= True; + facet->mergeridge= True; + nummerge++; + break; /* same for all neighbors */ + } } } } } if (!nummerge) return; - FORALLfacet_(facetlist) { /* gets rid of qh_MERGEridge */ + if (!allmerges) { + trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges (MRGdupridge) for qh_getpinchedmerges\n", nummerge)); + return; + } + trace1((qh ferr, 1048, "qh_mark_dupridges: found %d duplicated ridges (MRGdupridge) for qh_premerge. Prepare facets for merging\n", nummerge)); + /* make ridges in preparation for merging */ + FORALLfacet_(facetlist) { if (facet->mergeridge && !facet->mergeridge2) qh_makeridges(facet); } + trace3((qh ferr, 3075, "qh_mark_dupridges: restore missing neighbors and ridges due to qh_MERGEridge\n")); FOREACHmerge_(qh facet_mergeset) { /* restore the missing neighbors */ - if (merge->type == MRGridge) { - qh_setappend(&merge->facet2->neighbors, merge->facet1); + if (merge->mergetype == MRGdupridge) { /* only between simplicial facets */ + if (merge->facet2->mergeridge2 && qh_setin(merge->facet2->neighbors, merge->facet1)) { + /* Due to duplicate or multiple subridges, e.g., ../eg/qtest.sh t712682 '200 s W1e-13 C1,1e-13 D5' 'd' + merge->facet1: - neighboring facets: f27779 f59186 f59186 f59186 MERGEridge f59186 + merge->facet2: - neighboring facets: f27779 f59100 f59100 f59100 f59100 f59100 + or, ../eg/qtest.sh 100 '500 s W1e-13 C1,1e-13 D4' 'd' + both facets will be degenerate after merge, consider for special case handling + */ + qh_fprintf(qh ferr, 6361, "qhull topological error (qh_mark_dupridges): multiple dupridges for f%d and f%d, including reverse\n", + merge->facet1->id, merge->facet2->id); + qh_errexit2(qh_ERRtopology, merge->facet1, merge->facet2); + }else + qh_setappend(&merge->facet2->neighbors, merge->facet1); qh_makeridges(merge->facet1); /* and the missing ridges */ } } - trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n", - nummerge)); } /* mark_dupridges */ +/*--------------------------------- + + qh_maybe_duplicateridge( ridge ) + add MRGvertices if neighboring facet has another ridge with the same vertices + + returns: + adds rename requests to qh.vertex_mergeset + + notes: + called by qh_renamevertex + nop if 2-D + expensive test + Duplicate ridges may lead to new facets with same vertex set (QH7084), will try merging vertices + same as qh_maybe_duplicateridges + + design: + for the two neighbors + if non-simplicial + for each ridge with the same first and last vertices (max id and min id) + if the remaining vertices are the same + get the closest pair of vertices + add to vertex_mergeset for merging +*/ +void qh_maybe_duplicateridge(ridgeT *ridgeA) { + ridgeT *ridge, **ridgep; + vertexT *vertex, *pinched; + facetT *neighbor; + coordT dist; + int i, k, last= qh hull_dim-2; + + if (qh hull_dim < 3 ) + return; + + for (neighbor= ridgeA->top, i=0; i<2; neighbor= ridgeA->bottom, i++) { + if (!neighbor->simplicial && neighbor->nummerge > 0) { /* skip degenerate neighbors with both new and old vertices that will be merged */ + FOREACHridge_(neighbor->ridges) { + if (ridge != ridgeA && SETfirst_(ridge->vertices) == SETfirst_(ridgeA->vertices)) { + if (SETelem_(ridge->vertices, last) == SETelem_(ridgeA->vertices, last)) { + for (k=1; kvertices, k) != SETelem_(ridgeA->vertices, k)) + break; + } + if (k == last) { + vertex= qh_findbest_ridgevertex(ridge, &pinched, &dist); + trace2((qh ferr, 2069, "qh_maybe_duplicateridge: will merge v%d into v%d (dist %2.2g) due to duplicate ridges r%d/r%d with the same vertices. mergevertex set\n", + pinched->id, vertex->id, dist, ridgeA->id, ridge->id, ridgeA->top->id, ridgeA->bottom->id, ridge->top->id, ridge->bottom->id)); + qh_appendvertexmerge(pinched, vertex, MRGvertices, dist, ridgeA, ridge); + ridge->mergevertex= True; /* disables check for duplicate vertices in qh_checkfacet */ + ridgeA->mergevertex= True; + } + } + } + } + } + } +} /* maybe_duplicateridge */ + +/*--------------------------------- + + qh_maybe_duplicateridges( facet ) + if Q15, add MRGvertices if facet has ridges with the same vertices + + returns: + adds rename requests to qh.vertex_mergeset + + notes: + called at end of qh_mergefacet and qh_mergecycle_all + only enabled if qh.CHECKduplicates ('Q15') and 3-D or more + expensive test, not worth it + same as qh_maybe_duplicateridge + + design: + for all ridge pairs in facet + if the same first and last vertices (max id and min id) + if the remaining vertices are the same + get the closest pair of vertices + add to vertex_mergeset for merging +*/ +void qh_maybe_duplicateridges(facetT *facet) { + facetT *otherfacet; + ridgeT *ridge, *ridge2; + vertexT *vertex, *pinched; + coordT dist; + int ridge_i, ridge_n, i, k, last_v= qh hull_dim-2; + + if (qh hull_dim < 3 || !qh CHECKduplicates) + return; + + FOREACHridge_i_(facet->ridges) { + otherfacet= otherfacet_(ridge, facet); + if (otherfacet->degenerate || otherfacet->redundant || otherfacet->dupridge || otherfacet->flipped) /* will merge */ + continue; + for (i=ridge_i+1; i < ridge_n; i++) { + ridge2= SETelemt_(facet->ridges, i, ridgeT); + otherfacet= otherfacet_(ridge2, facet); + if (otherfacet->degenerate || otherfacet->redundant || otherfacet->dupridge || otherfacet->flipped) /* will merge */ + continue; + /* optimize qh_setequal(ridge->vertices, ridge2->vertices) */ + if (SETelem_(ridge->vertices, last_v) == SETelem_(ridge2->vertices, last_v)) { /* SETfirst is likely to be the same */ + if (SETfirst_(ridge->vertices) == SETfirst_(ridge2->vertices)) { + for (k=1; kvertices, k) != SETelem_(ridge2->vertices, k)) + break; + } + if (k == last_v) { + vertex= qh_findbest_ridgevertex(ridge, &pinched, &dist); + if (ridge->top == ridge2->bottom && ridge->bottom == ridge2->top) { + /* proof that ridges may have opposite orientation */ + trace2((qh ferr, 2088, "qh_maybe_duplicateridges: will merge v%d into v%d (dist %2.2g) due to opposite oriented ridges r%d/r%d for f%d and f%d\n", + pinched->id, vertex->id, dist, ridge->id, ridge2->id, ridge->top->id, ridge->bottom->id)); + }else { + trace2((qh ferr, 2083, "qh_maybe_duplicateridges: will merge v%d into v%d (dist %2.2g) due to duplicate ridges with the same vertices r%d/r%d in merged facet f%d\n", + pinched->id, vertex->id, dist, ridge->id, ridge2->id, facet->id)); + } + qh_appendvertexmerge(pinched, vertex, MRGvertices, dist, ridge, ridge2); + ridge->mergevertex= True; /* disables check for duplicate vertices in qh_checkfacet */ + ridge2->mergevertex= True; + } + } + } + } + } +} /* maybe_duplicateridges */ + /*--------------------------------- qh_maydropneighbor( facet ) - drop neighbor relationship if no ridge between facet and neighbor + drop neighbor relationship if ridge was deleted between a non-simplicial facet and its neighbors returns: - neighbor sets updated - appends degenerate facets to qh.facet_mergeset + for deleted ridges + ridges made for simplicial neighbors + neighbor sets updated + appends degenerate facets to qh.facet_mergeset notes: + called by qh_renamevertex + assumes neighbors do not include qh_MERGEridge (qh_makeridges) won't cause redundant facets since vertex inclusion is the same may drop vertex and neighbor if no ridge uses qh.visit_id @@ -1515,7 +2406,7 @@ void qh_mark_dupridges(facetT *facetlist) { design: visit all neighbors with ridges for each unvisited neighbor of facet - delete neighbor and facet from the neighbor sets + delete neighbor and facet from the non-simplicial neighbor sets if neighbor becomes degenerate append neighbor to qh.degen_mergeset if facet is degenerate @@ -1523,34 +2414,48 @@ void qh_mark_dupridges(facetT *facetlist) { */ void qh_maydropneighbor(facetT *facet) { ridgeT *ridge, **ridgep; - realT angledegen= qh_ANGLEdegen; facetT *neighbor, **neighborp; qh visit_id++; trace4((qh ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n", facet->id)); + if (facet->simplicial) { + qh_fprintf(qh ferr, 6278, "qhull internal error (qh_maydropneighbor): not valid for simplicial f%d while adding furthest p%d\n", + facet->id, qh furthest_id); + qh_errexit(qh_ERRqhull, facet, NULL); + } FOREACHridge_(facet->ridges) { ridge->top->visitid= qh visit_id; ridge->bottom->visitid= qh visit_id; } FOREACHneighbor_(facet) { + if (neighbor->visible) { + qh_fprintf(qh ferr, 6358, "qhull internal error (qh_maydropneighbor): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + qh_errexit2(qh_ERRqhull, facet, neighbor); + } if (neighbor->visitid != qh visit_id) { - trace0((qh ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n", + trace2((qh ferr, 2104, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors while adding furthest p%d\n", facet->id, neighbor->id, qh furthest_id)); + if (neighbor->simplicial) { + qh_fprintf(qh ferr, 6280, "qhull internal error (qh_maydropneighbor): not valid for simplicial neighbor f%d of f%d while adding furthest p%d\n", + neighbor->id, facet->id, qh furthest_id); + qh_errexit2(qh_ERRqhull, neighbor, facet); + } zinc_(Zdropneighbor); - qh_setdel(facet->neighbors, neighbor); - neighborp--; /* repeat, deleted a neighbor */ qh_setdel(neighbor->neighbors, facet); if (qh_setsize(neighbor->neighbors) < qh hull_dim) { zinc_(Zdropdegen); - qh_appendmergeset(neighbor, neighbor, MRGdegen, &angledegen); + qh_appendmergeset(neighbor, neighbor, MRGdegen, 0.0, qh_ANGLEnone); trace2((qh ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id)); } + qh_setdel(facet->neighbors, neighbor); + neighborp--; /* repeat, deleted a neighbor */ } } if (qh_setsize(facet->neighbors) < qh hull_dim) { zinc_(Zdropdegen); - qh_appendmergeset(facet, facet, MRGdegen, &angledegen); + qh_appendmergeset(facet, facet, MRGdegen, 0.0, qh_ANGLEnone); trace2((qh ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id)); } } /* maydropneighbor */ @@ -1559,9 +2464,9 @@ void qh_maydropneighbor(facetT *facet) { /*--------------------------------- - qh_merge_degenredundant() + qh_merge_degenredundant( ) merge all degenerate and redundant facets - qh.degen_mergeset contains merges from qh_degen_redundant_neighbors() + qh.degen_mergeset contains merges from qh_test_degen_neighbors, qh_test_redundant_neighbors, and qh_degen_redundant_facet returns: number of merges performed @@ -1572,6 +2477,7 @@ void qh_maydropneighbor(facetT *facet) { notes: redundant merges happen before degenerate ones merging and renaming vertices can result in degen/redundant facets + check for coplanar and convex neighbors afterwards design: for each merge on qh.degen_mergeset @@ -1584,17 +2490,21 @@ void qh_maydropneighbor(facetT *facet) { int qh_merge_degenredundant(void) { int size; mergeT *merge; - facetT *bestneighbor, *facet1, *facet2; + facetT *bestneighbor, *facet1, *facet2, *facet3; realT dist, mindist, maxdist; vertexT *vertex, **vertexp; int nummerges= 0; mergeType mergetype; + setT *mergedfacets; - while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) { + trace2((qh ferr, 2095, "qh_merge_degenredundant: merge %d degenerate, redundant, and mirror facets\n", + qh_setsize(qh degen_mergeset))); + mergedfacets= qh_settemp(qh TEMPsize); + while ((merge= (mergeT *)qh_setdellast(qh degen_mergeset))) { facet1= merge->facet1; facet2= merge->facet2; - mergetype= merge->type; - qh_memfree(merge, (int)sizeof(mergeT)); + mergetype= merge->mergetype; + qh_memfree(merge, (int)sizeof(mergeT)); /* 'merge' is invalidated */ if (facet1->visible) continue; facet1->degenerate= False; @@ -1602,25 +2512,23 @@ int qh_merge_degenredundant(void) { if (qh TRACEmerge-1 == zzval_(Ztotmerge)) qhmem.IStracing= qh IStracing= qh TRACElevel; if (mergetype == MRGredundant) { - zinc_(Zneighbor); - while (facet2->visible) { - if (!facet2->f.replace) { - qh_fprintf(qh ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n", - facet1->id, facet2->id); + zinc_(Zredundant); + facet3= qh_getreplacement(facet2); /* the same facet if !facet2.visible */ + if (!facet3) { + qh_fprintf(qh ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d is redundant but visible f%d has no replacement\n", + facet1->id, getid_(facet2)); qh_errexit2(qh_ERRqhull, facet1, facet2); - } - facet2= facet2->f.replace; } - if (facet1 == facet2) { - qh_degen_redundant_facet(facet1); /* in case of others */ + qh_setunique(&mergedfacets, facet3); + if (facet1 == facet3) { continue; } - trace2((qh ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n", - facet1->id, facet2->id)); - qh_mergefacet(facet1, facet2, NULL, NULL, !qh_MERGEapex); + trace2((qh ferr, 2025, "qh_merge_degenredundant: merge redundant f%d into f%d (arg f%d)\n", + facet1->id, facet3->id, facet2->id)); + qh_mergefacet(facet1, facet3, mergetype, NULL, NULL, !qh_MERGEapex); /* merge distance is already accounted for */ nummerges++; - }else { /* mergetype == MRGdegen, other merges may have fixed */ + }else { /* mergetype == MRGdegen or MRGmirror, other merges may have fixed */ if (!(size= qh_setsize(facet1->neighbors))) { zinc_(Zdelfacetdup); trace2((qh ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id)); @@ -1640,7 +2548,7 @@ int qh_merge_degenredundant(void) { bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist); trace2((qh ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n", facet1->id, size, bestneighbor->id, dist)); - qh_mergefacet(facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + qh_mergefacet(facet1, bestneighbor, mergetype, &mindist, &maxdist, !qh_MERGEapex); nummerges++; if (qh PRINTstatistics) { zinc_(Zdegen); @@ -1650,6 +2558,7 @@ int qh_merge_degenredundant(void) { } /* else, another merge fixed the degeneracy and redundancy tested */ } } + qh_settempfree(&mergedfacets); return nummerges; } /* merge_degenredundant */ @@ -1663,6 +2572,9 @@ int qh_merge_degenredundant(void) { returns: merges one of the facets into the best neighbor + notes: + mergetype is MRGcoplanar..MRGconvex + design: if one of the facets is a new facet prefer merging new facet into old facet @@ -1671,9 +2583,14 @@ int qh_merge_degenredundant(void) { update the statistics */ void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) { - facetT *bestfacet, *bestneighbor, *neighbor; + facetT *bestfacet, *bestneighbor, *neighbor, *merging, *merged; realT dist, dist2, mindist, mindist2, maxdist, maxdist2; + if (mergetype < MRGcoplanar || mergetype > MRGconcavecoplanar) { + qh_fprintf(qh ferr, 6398, "qhull internal error (qh_merge_nonconvex): expecting mergetype MRGcoplanar..MRGconcavecoplanar. Got merge f%d and f%d type %d\n", + facet1->id, facet2->id, mergetype); + qh_errexit2(qh_ERRqhull, facet1, facet2); + } if (qh TRACEmerge-1 == zzval_(Ztotmerge)) qhmem.IStracing= qh IStracing= qh TRACElevel; trace3((qh ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n", @@ -1688,7 +2605,8 @@ void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) { bestneighbor= qh_findbestneighbor(bestfacet, &dist, &mindist, &maxdist); neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2); if (dist < dist2) { - qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + merging= bestfacet; + merged= bestneighbor; }else if (qh AVOIDold && !facet2->newfacet && ((mindist >= -qh MAXcoplanar && maxdist <= qh max_outside) || dist * 1.5 < dist2)) { @@ -1697,11 +2615,17 @@ void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) { wmax_(Wavoidoldmax, dist); trace2((qh ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n", facet2->id, dist2, facet1->id, dist2)); - qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + merging= bestfacet; + merged= bestneighbor; }else { - qh_mergefacet(facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex); + merging= facet2; + merged= neighbor; dist= dist2; + mindist= mindist2; + maxdist= maxdist2; } + qh_mergefacet(merging, merged, mergetype, &mindist, &maxdist, !qh_MERGEapex); + /* caller merges qh_degenredundant */ if (qh PRINTstatistics) { if (mergetype == MRGanglecoplanar) { zinc_(Zacoplanar); @@ -1711,6 +2635,10 @@ void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) { zinc_(Zconcave); wadd_(Wconcavetot, dist); wmax_(Wconcavemax, dist); + }else if (mergetype == MRGconcavecoplanar) { + zinc_(Zconcavecoplanar); + wadd_(Wconcavecoplanartot, dist); + wmax_(Wconcavecoplanarmax, dist); }else { /* MRGcoplanar */ zinc_(Zcoplanar); wadd_(Wcoplanartot, dist); @@ -1719,6 +2647,137 @@ void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) { } } /* merge_nonconvex */ +/*--------------------------------- + + qh_merge_pinchedvertices( apex ) + merge pinched vertices in qh.vertex_mergeset to avoid qh_forcedmerges of dupridges + + notes: + only called by qh_all_vertexmerges + hull_dim >= 3 + + design: + make vertex neighbors if necessary + for each pinched vertex + determine the ridges for the pinched vertex (make ridges as needed) + merge the pinched vertex into the horizon vertex + merge the degenerate and redundant facets that result + check and resolve new dupridges +*/ +void qh_merge_pinchedvertices(int apexpointid /* qh.newfacet_list */) { + mergeT *merge, *mergeA, **mergeAp; + vertexT *vertex, *vertex2; + realT dist; + boolT firstmerge= True; + + qh_vertexneighbors(); + if (qh visible_list || qh newfacet_list || qh newvertex_list) { + qh_fprintf(qh ferr, 6402, "qhull internal error (qh_merge_pinchedvertices): qh.visible_list (f%d), newfacet_list (f%d), or newvertex_list (v%d) not empty\n", + getid_(qh visible_list), getid_(qh newfacet_list), getid_(qh newvertex_list)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh visible_list= qh newfacet_list= qh facet_tail; + qh newvertex_list= qh vertex_tail; + qh isRenameVertex= True; /* disable duplicate ridge vertices check in qh_checkfacet */ + while ((merge= qh_next_vertexmerge(/* qh.vertex_mergeset */))) { /* only one at a time from qh_getpinchedmerges */ + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + if (merge->mergetype == MRGsubridge) { + zzinc_(Zpinchedvertex); + trace1((qh ferr, 1050, "qh_merge_pinchedvertices: merge one of %d pinched vertices before adding apex p%d. Try to resolve duplicate ridges in newfacets\n", + qh_setsize(qh vertex_mergeset)+1, apexpointid)); + qh_remove_mergetype(qh vertex_mergeset, MRGsubridge); + }else { + zzinc_(Zpinchduplicate); + if (firstmerge) + trace1((qh ferr, 1056, "qh_merge_pinchedvertices: merge %d pinched vertices from dupridges in merged facets, apex p%d\n", + qh_setsize(qh vertex_mergeset)+1, apexpointid)); + firstmerge= False; + } + vertex= merge->vertex1; + vertex2= merge->vertex2; + dist= merge->distance; + qh_memfree(merge, (int)sizeof(mergeT)); /* merge is invalidated */ + qh_rename_adjacentvertex(vertex, vertex2, dist); +#ifndef qh_NOtrace + if (qh IStracing >= 2) { + FOREACHmergeA_(qh degen_mergeset) { + if (mergeA->mergetype== MRGdegen) { + qh_fprintf(qh ferr, 2072, "qh_merge_pinchedvertices: merge degenerate f%d into an adjacent facet\n", mergeA->facet1->id); + }else { + qh_fprintf(qh ferr, 2084, "qh_merge_pinchedvertices: merge f%d into f%d mergeType %d\n", mergeA->facet1->id, mergeA->facet2->id, mergeA->mergetype); + } + } + } +#endif + qh_merge_degenredundant(); /* simplicial facets with both old and new vertices */ + } + qh isRenameVertex= False; +}/* merge_pinchedvertices */ + +/*--------------------------------- + + qh_merge_twisted( facet1, facet2 ) + remove twisted ridge between facet1 into facet2 or report error + + returns: + merges one of the facets into the best neighbor + + notes: + a twisted ridge has opposite vertices that are convex and concave + + design: + find best neighbors for both facets + error if wide merge + merge the nearest facet into its best neighbor + update statistics +*/ +void qh_merge_twisted(facetT *facet1, facetT *facet2) { + facetT *neighbor2, *neighbor, *merging, *merged; + vertexT *bestvertex, *bestpinched; + realT dist, dist2, mindist, mindist2, maxdist, maxdist2, mintwisted, bestdist; + + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + trace3((qh ferr, 3050, "qh_merge_twisted: merge #%d for twisted f%d and f%d\n", + zzval_(Ztotmerge) + 1, facet1->id, facet2->id)); + /* twisted */ + neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist); + neighbor2= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2); + mintwisted= qh_RATIOtwisted * qh ONEmerge; + maximize_(mintwisted, facet1->maxoutside); + maximize_(mintwisted, facet2->maxoutside); + if (dist > mintwisted && dist2 > mintwisted) { + bestdist= qh_vertex_bestdist2(facet1->vertices, &bestvertex, &bestpinched); + if (bestdist > mintwisted) { + qh_fprintf(qh ferr, 6417, "qhull precision error (qh_merge_twisted): twisted facet f%d does not contain pinched vertices. Too wide to merge into neighbor. mindist %2.2g maxdist %2.2g vertexdist %2.2g maxpinched %2.2g neighbor f%d mindist %2.2g maxdist %2.2g\n", + facet1->id, mindist, maxdist, bestdist, mintwisted, facet2->id, mindist2, maxdist2); + }else { + qh_fprintf(qh ferr, 6418, "qhull precision error (qh_merge_twisted): twisted facet f%d with pinched vertices. Could merge vertices, but too wide to merge into neighbor. mindist %2.2g maxdist %2.2g vertexdist %2.2g neighbor f%d mindist %2.2g maxdist %2.2g\n", + facet1->id, mindist, maxdist, bestdist, facet2->id, mindist2, maxdist2); + } + qh_errexit2(qh_ERRwide, facet1, facet2); + } + if (dist < dist2) { + merging= facet1; + merged= neighbor; + }else { + /* ignores qh.AVOIDold ('Q4') */ + merging= facet2; + merged= neighbor2; + dist= dist2; + mindist= mindist2; + maxdist= maxdist2; + } + qh_mergefacet(merging, merged, MRGtwisted, &mindist, &maxdist, !qh_MERGEapex); + /* caller merges qh_degenredundant */ + zinc_(Ztwisted); + wadd_(Wtwistedtot, dist); + wmax_(Wtwistedmax, dist); +} /* merge_twisted */ + /*--------------------------------- @@ -1733,9 +2792,9 @@ void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) { 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 + notes: + only called by qh_mergecycle_all for multiple, same cycle facets + see qh_mergefacet design: make vertex neighbors if necessary @@ -1755,16 +2814,6 @@ void qh_mergecycle(facetT *samecycle, facetT *newfacet) { facetT *same; #endif - if (newfacet->tricoplanar) { - if (!qh TRInormals) { - qh_fprintf(qh ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n"); - qh_errexit(qh_ERRqhull, newfacet, NULL); - } - newfacet->tricoplanar= False; - newfacet->keepcentrum= False; - } - if (!qh VERTEXneighbors) - qh_vertexneighbors(); zzinc_(Ztotmerge); if (qh REPORTfreq2 && qh POSTmerging) { if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2) @@ -1791,6 +2840,18 @@ void qh_mergecycle(facetT *samecycle, facetT *newfacet) { if (qh IStracing >=4) qh_errprint("MERGING CYCLE", samecycle, newfacet, NULL, NULL); #endif /* !qh_NOtrace */ + if (newfacet->tricoplanar) { + if (!qh TRInormals) { + qh_fprintf(qh ferr, 6224, "qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh_ERRqhull, newfacet, NULL); + } + newfacet->tricoplanar= False; + newfacet->keepcentrum= False; + } + if (qh CHECKfrequently) + qh_checkdelridge(); + if (!qh VERTEXneighbors) + qh_vertexneighbors(); apex= SETfirstt_(samecycle->vertices, vertexT); qh_makeridges(newfacet); qh_mergecycle_neighbors(samecycle, newfacet); @@ -1801,7 +2862,7 @@ void qh_mergecycle(facetT *samecycle, facetT *newfacet) { if (!newfacet->newfacet) qh_newvertices(newfacet->vertices); qh_mergecycle_facets(samecycle, newfacet); - qh_tracemerge(samecycle, newfacet); + qh_tracemerge(samecycle, newfacet, MRGcoplanarhorizon); /* check for degen_redundant_neighbors after qh_forcedmerges() */ if (traceonce) { qh_fprintf(qh ferr, 8072, "qh_mergecycle: end of trace facet\n"); @@ -1823,37 +2884,40 @@ void qh_mergecycle(facetT *samecycle, facetT *newfacet) { deleted vertices on qh.del_vertices sets wasmerge if any merge - see: + notes: + called by qh_premerge calls qh_mergecycle for multiple, same cycle facets design: for each facet on facetlist - skip facets with duplicate ridges and normals + skip facets with dupridges 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 + remove facets with dupridges from samecycle merge samecycle into horizon (deletes facets from facetlist) */ void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) { - facetT *facet, *same, *prev, *horizon; + facetT *facet, *same, *prev, *horizon, *newfacet; facetT *samecycle= NULL, *nextfacet, *nextsame; vertexT *apex, *vertex, **vertexp; - int cycles=0, total=0, facets, nummerge; + int cycles=0, total=0, facets, nummerge, numdegen= 0; - trace2((qh ferr, 2031, "qh_mergecycle_all: begin\n")); - for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) { + trace2((qh ferr, 2031, "qh_mergecycle_all: merge new facets into coplanar horizon facets. Bulk merge a cycle of facets with the same horizon facet\n")); + for (facet=facetlist; facet && (nextfacet= facet->next); facet= nextfacet) { if (facet->normal) continue; if (!facet->mergehorizon) { - qh_fprintf(qh ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id); + qh_fprintf(qh ferr, 6225, "qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id); qh_errexit(qh_ERRqhull, facet, NULL); } horizon= SETfirstt_(facet->neighbors, facetT); if (facet->f.samecycle == facet) { + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; zinc_(Zonehorizon); /* merge distance done in qh_findhorizon */ apex= SETfirstt_(facet->vertices, vertexT); @@ -1862,7 +2926,7 @@ void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) { vertex->delridge= True; } horizon->f.newcycle= NULL; - qh_mergefacet(facet, horizon, NULL, NULL, qh_MERGEapex); + qh_mergefacet(facet, horizon, MRGcoplanarhorizon, NULL, NULL, qh_MERGEapex); }else { samecycle= facet; facets= 0; @@ -1889,7 +2953,7 @@ void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) { if (nummerge > qh_MAXnummerge) horizon->nummerge= qh_MAXnummerge; else - horizon->nummerge= (short unsigned int)nummerge; + horizon->nummerge= (short unsigned int)nummerge; /* limited to 9 bits by qh_MAXnummerge, -Wconversion */ zzinc_(Zcyclehorizon); total += facets; zzadd_(Zcyclefacettot, facets); @@ -1897,9 +2961,20 @@ void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) { } cycles++; } - if (cycles) + if (cycles) { + FORALLnew_facets { + /* qh_maybe_duplicateridges postponed since qh_mergecycle_ridges deletes ridges without calling qh_delridge_merge */ + if (newfacet->coplanarhorizon) { + qh_test_redundant_neighbors(newfacet); + qh_maybe_duplicateridges(newfacet); + newfacet->coplanarhorizon= False; + } + } + numdegen += qh_merge_degenredundant(); *wasmerge= True; - trace1((qh ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles)); + trace1((qh ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons and %d degenredundant facets\n", + cycles, numdegen)); + } } /* mergecycle_all */ /*-vertices)); qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); numold++; }else if (neighbor->visitid == samevisitid) { qh_setdel(neighbor->ridges, ridge); + if (qh traceridge == ridge) + qh traceridge= NULL; qh_setfree(&(ridge->vertices)); qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); numold++; @@ -2139,16 +3219,20 @@ void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet) { ridge= qh_newridge(); ridge->vertices= qh_setnew_delnthsorted(same->vertices, qh hull_dim, neighbor_i, 0); - toporient= same->toporient ^ (neighbor_i & 0x1); + toporient= (boolT)(same->toporient ^ (neighbor_i & 0x1)); if (toporient) { ridge->top= newfacet; ridge->bottom= neighbor; + ridge->simplicialbot= True; }else { ridge->top= neighbor; ridge->bottom= newfacet; + ridge->simplicialtop= True; } qh_setappend(&(newfacet->ridges), ridge); qh_setappend(&(neighbor->ridges), ridge); + if (qh ridge_id == qh traceridge_id) + qh traceridge= ridge; numnew++; } } @@ -2220,14 +3304,17 @@ void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) { /*--------------------------------- - qh_mergefacet( facet1, facet2, mindist, maxdist, mergeapex ) + qh_mergefacet( facet1, facet2, mergetype, mindist, maxdist, mergeapex ) merges facet1 into facet2 - mergeapex==qh_MERGEapex if merging new facet into coplanar horizon + mergeapex==qh_MERGEapex if merging new facet into coplanar horizon (optimizes qh_mergesimplex) returns: qh.max_outside and qh.min_vertex updated initializes vertex neighbors on first merge + note: + mergetype only used for logging and error reporting + returns: facet2 contains facet1's vertices, neighbors, and ridges facet2 moved to end of qh.facet_list @@ -2242,6 +3329,7 @@ void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) { adds neighboring facets to facet_mergeset if redundant or degenerate notes: + when done, tests facet1 and facet2 for degenerate or redundant neighbors and dupridges mindist/maxdist may be NULL (only if both NULL) traces merge if fmax_(maxdist,-mindist) > TRACEdist @@ -2263,19 +3351,30 @@ void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) { 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 + move facet2 to end of qh.newfacet_list + unless MRGcoplanarhorizon + test facet2 for redundant neighbors + test facet1 for degenerate neighbors + test for redundant facet2 + maybe test for duplicate ridges ('Q15') + move facet1 to qh.visible_list for later deletion */ -void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) { +void qh_mergefacet(facetT *facet1, facetT *facet2, mergeType mergetype, realT *mindist, realT *maxdist, boolT mergeapex) { boolT traceonce= False; vertexT *vertex, **vertexp; + realT mintwisted, vertexdist; + realT onemerge; int tracerestore=0, nummerge; + const char *mergename; + if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *)) + mergename= mergetypes[mergetype]; + else + mergename= mergetypes[MRGnone]; if (facet1->tricoplanar || facet2->tricoplanar) { if (!qh TRInormals) { - qh_fprintf(qh ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_fprintf(qh ferr, 6226, "qhull internal error (qh_mergefacet): merge f%d into f%d for mergetype %d (%s) does not work for tricoplanar facets. Use option 'Q11'\n", + facet1->id, facet2->id, mergetype, mergename); qh_errexit2(qh_ERRqhull, facet1, facet2); } if (facet2->tricoplanar) { @@ -2294,14 +3393,14 @@ void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdis tracerestore= 0; qh IStracing= qh TRACElevel; traceonce= True; - qh_fprintf(qh ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge), - fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh furthest_id); + qh_fprintf(qh ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d for mergetype %d (%s), last point was p%d\n", + zzval_(Ztotmerge), fmax_(-*mindist, *maxdist), facet1->id, facet2->id, mergetype, mergename, qh furthest_id); }else if (facet1 == qh tracefacet || facet2 == qh tracefacet) { tracerestore= qh IStracing; qh IStracing= 4; traceonce= True; - qh_fprintf(qh ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n", - zzval_(Ztotmerge), qh tracefacet_id, qh furthest_id); + qh_fprintf(qh ferr, 8076, "qh_mergefacet: ========= trace merge #%d for f%d into f%d for mergetype %d (%s), furthest is p%d\n", + zzval_(Ztotmerge), facet1->id, facet2->id, mergetype, mergename, qh furthest_id); } } if (qh IStracing >= 2) { @@ -2312,23 +3411,38 @@ void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdis mergemin= *mindist; mergemax= *maxdist; } - qh_fprintf(qh ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n", - zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax); + qh_fprintf(qh ferr, 2081, "qh_mergefacet: #%d merge f%d into f%d for merge for mergetype %d (%s), mindist= %2.2g, maxdist= %2.2g, max_outside %2.2g\n", + zzval_(Ztotmerge), facet1->id, facet2->id, mergetype, mergename, mergemin, mergemax, qh max_outside); } #endif /* !qh_NOtrace */ + if(!qh ALLOWwide && mindist) { + mintwisted= qh_WIDEmaxoutside * qh ONEmerge; /* same as qh_merge_twisted and qh_check_maxout (poly2) */ + maximize_(mintwisted, facet1->maxoutside); + maximize_(mintwisted, facet2->maxoutside); + if (*maxdist > mintwisted || -*mindist > mintwisted) { + vertexdist= qh_vertex_bestdist(facet1->vertices); + onemerge= qh ONEmerge + qh DISTround; + if (vertexdist > mintwisted) { + qh_fprintf(qh ferr, 6347, "qhull precision error (qh_mergefacet): wide merge for facet f%d into f%d for mergetype %d (%s). maxdist %2.2g (%.1fx) mindist %2.2g (%.1fx) vertexdist %2.2g Allow with 'Q12' (allow-wide)\n", + facet1->id, facet2->id, mergetype, mergename, *maxdist, *maxdist/onemerge, *mindist, -*mindist/onemerge, vertexdist); + }else { + qh_fprintf(qh ferr, 6348, "qhull precision error (qh_mergefacet): wide merge for pinched facet f%d into f%d for mergetype %d (%s). maxdist %2.2g (%.fx) mindist %2.2g (%.1fx) vertexdist %2.2g Allow with 'Q12' (allow-wide)\n", + facet1->id, facet2->id, mergetype, mergename, *maxdist, *maxdist/onemerge, *mindist, -*mindist/onemerge, vertexdist); + } + qh_errexit2(qh_ERRwide, facet1, facet2); + } + } if (facet1 == facet2 || facet1->visible || facet2->visible) { - qh_fprintf(qh ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n", - facet1->id, facet2->id); + qh_fprintf(qh ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet, mergetype %d (%s)\n", + facet1->id, facet2->id, mergetype, mergename); qh_errexit2(qh_ERRqhull, facet1, facet2); } if (qh num_facets - qh num_visible <= qh hull_dim + 1) { - qh_fprintf(qh ferr, 6227, "\n\ -qhull precision error: Only %d facets remain. Can not merge another\n\ -pair. The input is too degenerate or the convexity constraints are\n\ -too strong.\n", qh hull_dim+1); + qh_fprintf(qh ferr, 6227, "qhull topology error: Only %d facets remain. The input is too degenerate or the convexity constraints are too strong.\n", + qh hull_dim+1); if (qh hull_dim >= 5 && !qh MERGEexact) - qh_fprintf(qh ferr, 8079, "Option 'Qx' may avoid this problem.\n"); - qh_errexit(qh_ERRprec, NULL, NULL); + qh_fprintf(qh ferr, 8079, " Option 'Qx' may avoid this problem.\n"); + qh_errexit(qh_ERRtopology, NULL, NULL); } if (!qh VERTEXneighbors) qh_vertexneighbors(); @@ -2353,7 +3467,7 @@ too strong.\n", qh hull_dim+1); if (nummerge >= qh_MAXnummerge) facet2->nummerge= qh_MAXnummerge; else - facet2->nummerge= (short unsigned int)nummerge; + facet2->nummerge= (short unsigned int)nummerge; /* limited to 9 bits by qh_MAXnummerge, -Wconversion */ facet2->newmerge= True; facet2->dupridge= False; qh_updatetested(facet1, facet2); @@ -2374,25 +3488,34 @@ too strong.\n", qh hull_dim+1); if (!facet2->newfacet) qh_newvertices(facet2->vertices); } - if (!mergeapex) - qh_degen_redundant_neighbors(facet2, facet1); - if (facet2->coplanar || !facet2->newfacet) { + if (facet2->coplanarhorizon) { + zinc_(Zmergeintocoplanar); + }else if (!facet2->newfacet) { zinc_(Zmergeintohorizon); }else if (!facet1->newfacet && facet2->newfacet) { zinc_(Zmergehorizon); }else { zinc_(Zmergenew); } - qh_willdelete(facet1, facet2); qh_removefacet(facet2); /* append as a newfacet to end of qh facet_list */ qh_appendfacet(facet2); facet2->newfacet= True; facet2->tested= False; - qh_tracemerge(facet1, facet2); + qh_tracemerge(facet1, facet2, mergetype); if (traceonce) { qh_fprintf(qh ferr, 8080, "qh_mergefacet: end of wide tracing\n"); qh IStracing= tracerestore; } + if (mergetype != MRGcoplanarhorizon) { + trace3((qh ferr, 3076, "qh_mergefacet: check f%d and f%d for redundant and degenerate neighbors\n", + facet1->id, facet2->id)); + qh_test_redundant_neighbors(facet2); + qh_test_degen_neighbors(facet1); /* after qh_test_redundant_neighbors since MRGdegen more difficult than MRGredundant + and before qh_willdelete which clears facet1.neighbors */ + qh_degen_redundant_facet(facet2); /* may occur in qh_merge_pinchedvertices, e.g., rbox 175 C3,2e-13 D4 t1545228104 | qhull d */ + qh_maybe_duplicateridges(facet2); + } + qh_willdelete(facet1, facet2); } /* mergefacet */ @@ -2468,7 +3591,7 @@ void qh_mergefacet2d(facetT *facet1, facetT *facet2) { SETfirst_(facet2->neighbors)= neighborB; SETsecond_(facet2->neighbors)= neighborA; } - qh_makeridges(neighborB); + /* qh_makeridges not needed since neighborB is not degenerate */ qh_setreplace(neighborB->neighbors, facet1, facet2); trace4((qh ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n", vertexA->id, neighborB->id, facet1->id, facet2->id)); @@ -2481,13 +3604,15 @@ void qh_mergefacet2d(facetT *facet1, facetT *facet2) { qh_mergeneighbors( facet1, facet2 ) merges the neighbors of facet1 into facet2 - see: - qh_mergecycle_neighbors() + notes: + only called by qh_mergefacet + qh.hull_dim >= 3 + see qh_mergecycle_neighbors design: for each neighbor of facet1 if neighbor is also a neighbor of facet2 - if neighbor is simpilicial + if neighbor is simplicial make ridges for later deletion as a degenerate facet update its neighbor set else @@ -2544,23 +3669,24 @@ void qh_mergeneighbors(facetT *facet1, facetT *facet2) { */ void qh_mergeridges(facetT *facet1, facetT *facet2) { ridgeT *ridge, **ridgep; - vertexT *vertex, **vertexp; - trace4((qh ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n", + trace4((qh ferr, 4038, "qh_mergeridges: merge ridges of f%d into f%d\n", facet1->id, facet2->id)); FOREACHridge_(facet2->ridges) { if ((ridge->top == facet1) || (ridge->bottom == facet1)) { - FOREACHvertex_(ridge->vertices) - vertex->delridge= True; - qh_delridge(ridge); /* expensive in high-d, could rebuild */ - ridgep--; /*repeat*/ + /* ridge.nonconvex is irrelevant due to merge */ + qh_delridge_merge(ridge); /* expensive in high-d, could rebuild */ + ridgep--; /* deleted this ridge, repeat with next ridge*/ } } FOREACHridge_(facet1->ridges) { - if (ridge->top == facet1) + if (ridge->top == facet1) { ridge->top= facet2; - else + ridge->simplicialtop= False; + }else { /* ridge.bottom is facet1 */ ridge->bottom= facet2; + ridge->simplicialbot= False; + } qh_setappend(&(facet2->ridges), ridge); } } /* mergeridges */ @@ -2602,7 +3728,7 @@ void qh_mergeridges(facetT *facet1, facetT *facet2) { if apex rename facet1 to facet2 in its vertex neighbors else - delete facet1 from vertex neighors + delete facet1 from vertex neighbors if only in facet2 add vertex to qh.del_vertices for later deletion for each ridge of facet1 @@ -2610,20 +3736,21 @@ void qh_mergeridges(facetT *facet1, facetT *facet2) { append other ridges to facet2 after renaming facet to facet2 */ void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) { - vertexT *vertex, **vertexp, *apex; + vertexT *vertex, **vertexp, *opposite; ridgeT *ridge, **ridgep; - boolT issubset= False; - int vertex_i= -1, vertex_n; + boolT isnew= False; facetT *neighbor, **neighborp, *otherfacet; if (mergeapex) { + opposite= SETfirstt_(facet1->vertices, vertexT); /* apex is opposite facet2. It has the last vertex id */ + trace4((qh ferr, 4086, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n", + opposite->id, facet1->id, facet2->id)); if (!facet2->newfacet) - qh_newvertices(facet2->vertices); /* apex is new */ - apex= SETfirstt_(facet1->vertices, vertexT); - if (SETfirstt_(facet2->vertices, vertexT) != apex) - qh_setaddnth(&facet2->vertices, 0, apex); /* apex has last id */ - else - issubset= True; + qh_newvertices(facet2->vertices); /* apex, the first vertex, is already new */ + if (SETfirstt_(facet2->vertices, vertexT) != opposite) { + qh_setaddnth(&facet2->vertices, 0, opposite); + isnew= True; + } }else { zinc_(Zmergesimplex); FOREACHvertex_(facet1->vertices) @@ -2641,30 +3768,21 @@ void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) { if (!vertex->seen) break; /* must occur */ } - apex= vertex; - trace4((qh ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n", - apex->id, facet1->id, facet2->id)); - FOREACHvertex_i_(facet2->vertices) { - if (vertex->id < apex->id) { - break; - }else if (vertex->id == apex->id) { - issubset= True; - break; - } - } - if (!issubset) - qh_setaddnth(&facet2->vertices, vertex_i, apex); + opposite= vertex; + trace4((qh ferr, 4039, "qh_mergesimplex: merge opposite v%d of f%d into facet f%d\n", + opposite->id, facet1->id, facet2->id)); + isnew= qh_addfacetvertex(facet2, opposite); if (!facet2->newfacet) qh_newvertices(facet2->vertices); - else if (!apex->newlist) { - qh_removevertex(apex); - qh_appendvertex(apex); + else if (!opposite->newfacet) { + qh_removevertex(opposite); + qh_appendvertex(opposite); } } trace4((qh ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n", facet1->id)); FOREACHvertex_(facet1->vertices) { - if (vertex == apex && !issubset) + if (vertex == opposite && isnew) qh_setreplace(vertex->neighbors, facet1, facet2); else { qh_setdel(vertex->neighbors, facet1); @@ -2680,11 +3798,17 @@ void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) { FOREACHridge_(facet1->ridges) { otherfacet= otherfacet_(ridge, facet1); if (otherfacet == facet2) { - qh_setdel(facet2->ridges, ridge); - qh_setfree(&(ridge->vertices)); - qh_memfree(ridge, (int)sizeof(ridgeT)); - qh_setdel(facet2->neighbors, facet1); + /* ridge.nonconvex is irrelevant due to merge */ + qh_delridge_merge(ridge); /* expensive in high-d, could rebuild */ + ridgep--; /* deleted this ridge, repeat with next ridge*/ + qh_setdel(facet2->neighbors, facet1); /* a simplicial facet may have duplicate neighbors, need to delete each one */ + }else if (otherfacet->dupridge && !qh_setin(otherfacet->neighbors, facet1)) { + qh_fprintf(qh ferr, 6356, "qhull topology error (qh_mergesimplex): f%d is a dupridge of f%d, cannot merge f%d into f%d\n", + facet1->id, otherfacet->id, facet1->id, facet2->id); + qh_errexit2(qh_ERRqhull, facet1, otherfacet); }else { + trace4((qh ferr, 4059, "qh_mergesimplex: move r%d with f%d to f%d, new neighbor? %d, maybe horizon? %d\n", + ridge->id, otherfacet->id, facet2->id, (otherfacet->visitid != qh visit_id), (SETfirstt_(otherfacet->neighbors, facetT) == facet1))); qh_setappend(&facet2->ridges, ridge); if (otherfacet->visitid != qh visit_id) { qh_setappend(&facet2->neighbors, otherfacet); @@ -2693,22 +3817,26 @@ void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) { }else { if (otherfacet->simplicial) /* is degen, needs ridges */ qh_makeridges(otherfacet); - if (SETfirstt_(otherfacet->neighbors, facetT) != facet1) - qh_setdel(otherfacet->neighbors, facet1); - else { /*keep newfacet->neighbors->horizon*/ + if (SETfirstt_(otherfacet->neighbors, facetT) == facet1) { + /* keep new, otherfacet->neighbors->horizon */ qh_setdel(otherfacet->neighbors, facet2); qh_setreplace(otherfacet->neighbors, facet1, facet2); + }else { + /* facet2 is already a neighbor of otherfacet, by f.visitid */ + qh_setdel(otherfacet->neighbors, facet1); } } - if (ridge->top == facet1) /* wait until after qh_makeridges */ + if (ridge->top == facet1) { /* wait until after qh_makeridges */ ridge->top= facet2; - else + ridge->simplicialtop= False; + }else { ridge->bottom= facet2; + ridge->simplicialbot= False; + } } } - SETfirst_(facet1->ridges)= NULL; /* it will be deleted */ - trace3((qh ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n", - facet1->id, getid_(apex), facet2->id)); + trace3((qh ferr, 3006, "qh_mergesimplex: merged simplex f%d v%d into facet f%d\n", + facet1->id, opposite->id, facet2->id)); } /* mergesimplex */ /*-id, facet2->id)); if (qh tracevertex) { - qh_fprintf(qh ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n", + qh_fprintf(qh ferr, 8081, "qh_mergevertex_neighbors: of f%d into f%d at furthest p%d f0= %p\n", facet1->id, facet2->id, qh furthest_id, qh tracevertex->neighbors->e[0].p); qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex); } @@ -2828,6 +3956,8 @@ void qh_mergevertices(setT *vertices1, setT **vertices2) { NULL if empty set notes: + only called by qh_redundant_vertex for qh_reducevertices + so f.vertices does not contain extraneous vertices that are not in f.ridges used for renaming vertices design: @@ -2875,32 +4005,251 @@ setT *qh_neighbor_intersections(vertexT *vertex) { } /* neighbor_intersections */ /*--------------------------------- + >-------------------------------- - qh_newvertices( vertices ) - add vertices to end of qh.vertex_list (marks as new vertices) + qh_neighbor_vertices( vertex ) + return neighboring vertices for a vertex (not in subridge) + assumes vertices have full vertex->neighbors returns: - vertices on qh.newvertex_list - vertex->newlist set + temporary set of vertices + + notes: + updates qh.visit_id and qh.vertex_visit + similar to qh_vertexridges + */ -void qh_newvertices(setT *vertices) { +setT *qh_neighbor_vertices(vertexT *vertexA, setT *subridge) { + facetT *neighbor, **neighborp; vertexT *vertex, **vertexp; + setT *vertices= qh_settemp(qh TEMPsize); - FOREACHvertex_(vertices) { - if (!vertex->newlist) { - qh_removevertex(vertex); + qh visit_id++; + FOREACHneighbor_(vertexA) + neighbor->visitid= qh visit_id; + qh vertex_visit++; + vertexA->visitid= qh vertex_visit; + FOREACHvertex_(subridge) { + vertex->visitid= qh vertex_visit; + } + FOREACHneighbor_(vertexA) { + if (*neighborp) /* no new ridges in last neighbor */ + qh_neighbor_vertices_facet(vertexA, neighbor, &vertices); + } + trace3((qh ferr, 3035, "qh_neighbor_vertices: %d non-subridge, vertex neighbors for v%d\n", + qh_setsize(vertices), vertexA->id)); + return vertices; +} /* neighbor_vertices */ + +/*--------------------------------- + + qh_neighbor_vertices_facet( vertex, facet, vertices ) + add neighboring vertices on ridges for vertex in facet + neighbor->visitid==qh.visit_id if it hasn't been visited + v.visitid==qh.vertex_visit if it is already in vertices + + returns: + vertices updated + sets facet->visitid to qh.visit_id-1 + + notes: + only called by qh_neighbor_vertices + similar to qh_vertexridges_facet + + design: + for each ridge of facet + if ridge of visited neighbor (i.e., unprocessed) + if vertex in ridge + append unprocessed vertices of ridge + mark facet processed +*/ +void qh_neighbor_vertices_facet(vertexT *vertexA, facetT *facet, setT **vertices) { + ridgeT *ridge, **ridgep; + facetT *neighbor; + vertexT *second, *last, *vertex, **vertexp; + int last_i= qh hull_dim-2, count= 0; + boolT isridge; + + if (facet->simplicial) { + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + qh_setappend(vertices, vertex); + count++; + } + } + }else { + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid == qh visit_id) { + isridge= False; + if (SETfirst_(ridge->vertices) == vertexA) { + isridge= True; + }else if (last_i > 2) { + second= SETsecondt_(ridge->vertices, vertexT); + last= SETelemt_(ridge->vertices, last_i, vertexT); + if (second->id >= vertexA->id && last->id <= vertexA->id) { /* vertices inverse sorted by id */ + if (second == vertexA || last == vertexA) + isridge= True; + else if (qh_setin(ridge->vertices, vertexA)) + isridge= True; + } + }else if (SETelem_(ridge->vertices, last_i) == vertexA) { + isridge= True; + }else if (last_i > 1 && SETsecond_(ridge->vertices) == vertexA) { + isridge= True; + } + if (isridge) { + FOREACHvertex_(ridge->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + qh_setappend(vertices, vertex); + count++; + } + } + } + } + } + } + facet->visitid= qh visit_id-1; + if (count) { + trace4((qh ferr, 4079, "qh_neighbor_vertices_facet: found %d vertex neighbors for v%d in f%d (simplicial? %d)\n", + count, vertexA->id, facet->id, facet->simplicial)); + } +} /* neighbor_vertices_facet */ + + +/*--------------------------------- + + qh_newvertices( vertices ) + add vertices to end of qh.vertex_list (marks as new vertices) + + returns: + vertices on qh.newvertex_list + vertex->newfacet set +*/ +void qh_newvertices(setT *vertices) { + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newfacet) { + qh_removevertex(vertex); qh_appendvertex(vertex); } } } /* newvertices */ +/*--------------------------------- + + qh_next_vertexmerge( ) + return next vertex merge from qh.vertex_mergeset + + returns: + vertex merge either MRGvertices or MRGsubridge + drops merges of deleted vertices + + notes: + called from qh_merge_pinchedvertices +*/ +mergeT *qh_next_vertexmerge(void /* qh.vertex_mergeset */) { + mergeT *merge; + int merge_i, merge_n, best_i= -1; + realT bestdist= REALmax; + + FOREACHmerge_i_(qh vertex_mergeset) { + if (!merge->vertex1 || !merge->vertex2) { + qh_fprintf(qh ferr, 6299, "qhull internal error (qh_next_vertexmerge): expecting two vertices for vertex merge. Got v%d v%d and optional f%d\n", + getid_(merge->vertex1), getid_(merge->vertex2), getid_(merge->facet1)); + qh_errexit(qh_ERRqhull, merge->facet1, NULL); + } + if (merge->vertex1->deleted || merge->vertex2->deleted) { + trace3((qh ferr, 3030, "qh_next_vertexmerge: drop merge of v%d (del? %d) into v%d (del? %d) due to deleted vertex of r%d and r%d\n", + merge->vertex1->id, merge->vertex1->deleted, merge->vertex2->id, merge->vertex2->deleted, getid_(merge->ridge1), getid_(merge->ridge2))); + qh_drop_mergevertex(merge); + qh_setdelnth(qh vertex_mergeset, merge_i); + merge_i--; merge_n--; + qh_memfree(merge, (int)sizeof(mergeT)); + }else if (merge->distance < bestdist) { + bestdist= merge->distance; + best_i= merge_i; + } + } + merge= NULL; + if (best_i >= 0) { + merge= SETelemt_(qh vertex_mergeset, best_i, mergeT); + if (bestdist/qh ONEmerge > qh_WIDEpinched) { + if (merge->mergetype==MRGvertices) { + if (merge->ridge1->top == merge->ridge2->bottom && merge->ridge1->bottom == merge->ridge2->top) + qh_fprintf(qh ferr, 6391, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve opposite oriented ridges r%d and r%d in f%d and f%d. Nearest v%d and v%d dist %2.2g (%.1fx)\n", + merge->ridge1->id, merge->ridge2->id, merge->ridge1->top->id, merge->ridge1->bottom->id, merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh ONEmerge); + else + qh_fprintf(qh ferr, 6381, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve duplicate ridges r%d and r%d. Nearest v%d and v%d dist %2.2g (%.1fx)\n", + merge->ridge1->id, merge->ridge2->id, merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh ONEmerge); + }else { + qh_fprintf(qh ferr, 6208, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve dupridge. Nearest v%d and v%d dist %2.2g (%.1fx)\n", + merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh ONEmerge); + } + /* it may be possible to find a different vertex, after other vertex merges have occurred */ + qh_errexit(qh_ERRtopology, NULL, merge->ridge1); + } + qh_setdelnth(qh vertex_mergeset, best_i); + } + return merge; +} /* next_vertexmerge */ + +/*--------------------------------- + + qh_opposite_horizonfacet( merge, opposite ) + return horizon facet for one of the merge facets, and its opposite vertex across the ridge + assumes either facet1 or facet2 of merge is 'mergehorizon' + assumes both facets are simplicial facets on qh.new_facetlist + + returns: + horizon facet and opposite vertex + + notes: + called by qh_getpinchedmerges +*/ +facetT *qh_opposite_horizonfacet(mergeT *merge, vertexT **opposite) { + facetT *facet, *horizon, *otherfacet; + int neighbor_i; + + if (!merge->facet1->simplicial || !merge->facet2->simplicial || (!merge->facet1->mergehorizon && !merge->facet2->mergehorizon)) { + qh_fprintf(qh ferr, 6273, "qhull internal error (qh_opposite_horizonfacet): expecting merge of simplicial facets, at least one of which is mergehorizon. Either simplicial or mergehorizon is wrong\n"); + qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2); + } + if (merge->facet1->mergehorizon) { + facet= merge->facet1; + otherfacet= merge->facet2; + }else { + facet= merge->facet2; + otherfacet= merge->facet1; + } + horizon= SETfirstt_(facet->neighbors, facetT); + neighbor_i= qh_setindex(otherfacet->neighbors, facet); + if (neighbor_i==-1) + neighbor_i= qh_setindex(otherfacet->neighbors, qh_MERGEridge); + if (neighbor_i==-1) { + qh_fprintf(qh ferr, 6238, "qhull internal error (qh_opposite_horizonfacet): merge facet f%d not connected to mergehorizon f%d\n", + otherfacet->id, facet->id); + qh_errexit2(qh_ERRqhull, otherfacet, facet); + } + *opposite= SETelemt_(otherfacet->vertices, neighbor_i, vertexT); + return horizon; +} /* opposite_horizonfacet */ + + /*--------------------------------- - qh_reducevertices() + qh_reducevertices( ) reduce extra vertices, shared vertices, and redundant vertices facet->newmerge is set if merged since last call + vertex->delridge is set if vertex was on a deleted ridge if !qh.MERGEvertices, only removes extra vertices returns: @@ -2909,18 +4258,23 @@ void qh_newvertices(setT *vertices) { clears facet->newmerge and vertex->delridge notes: + called by qh_all_merges and qh_postmerge ignored if 2-d design: merge any degenerate or redundant facets - for each newly merged facet - remove extra vertices - if qh.MERGEvertices + repeat until no more degenerate or redundant facets 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 + 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 + for each new, undeleted vertex + remove delridge flag + if vertex is redundant + merge degenerate or redundant facets */ boolT qh_reducevertices(void) { int numshare=0, numrename= 0; @@ -2930,14 +4284,21 @@ boolT qh_reducevertices(void) { if (qh hull_dim == 2) return False; + trace2((qh ferr, 2101, "qh_reducevertices: reduce extra vertices, shared vertices, and redundant vertices\n")); if (qh_merge_degenredundant()) degenredun= True; - LABELrestart: +LABELrestart: FORALLnew_facets { if (newfacet->newmerge) { if (!qh MERGEvertices) newfacet->newmerge= False; - qh_remove_extravertices(newfacet); + if (qh_remove_extravertices(newfacet)) { + qh_degen_redundant_facet(newfacet); + if (qh_merge_degenredundant()) { + degenredun= True; + goto LABELrestart; + } + } } } if (!qh MERGEvertices) @@ -2949,6 +4310,10 @@ boolT qh_reducevertices(void) { if (vertex->delridge) { if (qh_rename_sharedvertex(vertex, newfacet)) { numshare++; + if (qh_merge_degenredundant()) { + degenredun= True; + goto LABELrestart; + } vertexp--; /* repeat since deleted vertex */ } } @@ -2976,33 +4341,36 @@ boolT qh_reducevertices(void) { >-------------------------------- qh_redundant_vertex( vertex ) - detect and rename a redundant vertex - vertices have full vertex->neighbors + rename a redundant vertex if qh_find_newvertex succeeds + assumes vertices have full vertex->neighbors returns: - returns true if find a redundant vertex - deletes vertex(vertex->deleted) + if find a replacement vertex + returns new vertex + qh_renamevertex sets vertex->deleted for redundant vertex notes: - only needed if vertex->delridge and hull_dim >= 4 + only called by qh_reducevertices for 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 + if find a new vertex for vertex among these ridges and vertices rename vertex to the new vertex */ vertexT *qh_redundant_vertex(vertexT *vertex) { vertexT *newvertex= NULL; setT *vertices, *ridges; - trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id)); + trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d from a deleted ridge can be renamed\n", vertex->id)); if ((vertices= qh_neighbor_intersections(vertex))) { - ridges= qh_vertexridges(vertex); - if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) - qh_renamevertex(vertex, newvertex, ridges, NULL, NULL); + ridges= qh_vertexridges(vertex, !qh_ALL); + if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) { + zinc_(Zrenameall); + qh_renamevertex(vertex, newvertex, ridges, NULL, NULL); /* ridges invalidated */ + } qh_settempfree(&ridges); qh_settempfree(&vertices); } @@ -3017,12 +4385,18 @@ vertexT *qh_redundant_vertex(vertexT *vertex) { returns: returns True if it finds them + deletes facet from vertex neighbors + facet may be redundant (test with qh_degen_redundant) + + notes: + called by qh_renamevertex and qh_reducevertices + a merge (qh_reducevertices) or qh_renamevertex may drop all ridges for a vertex in a facet 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 + delete facet from vertex's neighbors unless vertex in another facet add vertex to qh.del_vertices for later deletion */ @@ -3031,7 +4405,10 @@ boolT qh_remove_extravertices(facetT *facet) { vertexT *vertex, **vertexp; boolT foundrem= False; - trace4((qh ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n", + if (facet->simplicial) { + return False; + } + trace4((qh ferr, 4043, "qh_remove_extravertices: test non-simplicial f%d for extra vertices\n", facet->id)); FOREACHvertex_(facet->vertices) vertex->seen= False; @@ -3058,6 +4435,127 @@ boolT qh_remove_extravertices(facetT *facet) { return foundrem; } /* remove_extravertices */ +/*--------------------------------- + + qh_remove_mergetype( mergeset, mergetype ) + Remove mergetype merges from mergeset + + notes: + Does not preserve order +*/ +void qh_remove_mergetype(setT *mergeset, mergeType type) { + mergeT *merge; + int merge_i, merge_n; + + FOREACHmerge_i_(mergeset) { + if (merge->mergetype == type) { + trace3((qh ferr, 3037, "qh_remove_mergetype: remove merge f%d f%d v%d v%d r%d r%d dist %2.2g type %d", + getid_(merge->facet1), getid_(merge->facet2), getid_(merge->vertex1), getid_(merge->vertex2), getid_(merge->ridge1), getid_(merge->ridge2), merge->distance, type)); + qh_setdelnth(mergeset, merge_i); + merge_i--; merge_n--; /* repeat with next merge */ + } + } +} /* remove_mergetype */ + +/*--------------------------------- + + qh_rename_adjacentvertex( oldvertex, newvertex ) + renames oldvertex as newvertex. Must be adjacent (i.e., in the same subridge) + no-op if either vertex is deleted + + notes: + called from qh_merge_pinchedvertices + + design: + for all neighbors of oldvertex + if simplicial, rename oldvertex to newvertex and drop if degenerate + if needed, add oldvertex neighbor to newvertex + determine ridges for oldvertex + rename oldvertex as newvertex in ridges (qh_renamevertex) +*/ +void qh_rename_adjacentvertex(vertexT *oldvertex, vertexT *newvertex, realT dist) { + setT *ridges; + facetT *neighbor, **neighborp, *maxfacet= NULL; + ridgeT *ridge, **ridgep; + boolT istrace= False; + int oldsize= qh_setsize(oldvertex->neighbors); + int newsize= qh_setsize(newvertex->neighbors); + coordT maxdist2= -REALmax, dist2; + + if (qh IStracing >= 4 || oldvertex->id == qh tracevertex_id || newvertex->id == qh tracevertex_id) { + istrace= True; + } + zzinc_(Ztotmerge); + if (istrace) { + qh_fprintf(qh ferr, 2071, "qh_rename_adjacentvertex: merge #%d rename v%d (%d neighbors) to v%d (%d neighbors) dist %2.2g\n", + zzval_(Ztotmerge), oldvertex->id, oldsize, newvertex->id, newsize, dist); + } + if (oldvertex->deleted || newvertex->deleted) { + if (istrace || qh IStracing >= 2) { + qh_fprintf(qh ferr, 2072, "qh_rename_adjacentvertex: ignore rename. Either v%d (%d) or v%d (%d) is deleted\n", + oldvertex->id, oldvertex->deleted, newvertex->id, newvertex->deleted); + } + return; + } + if (oldsize == 0 || newsize == 0) { + qh_fprintf(qh ferr, 2072, "qhull internal error (qh_rename_adjacentvertex): expecting neighbor facets for v%d and v%d. Got %d and %d neighbors resp.\n", + oldvertex->id, newvertex->id, oldsize, newsize); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + FOREACHneighbor_(oldvertex) { + if (neighbor->simplicial) { + if (qh_setin(neighbor->vertices, newvertex)) { + if (istrace || qh IStracing >= 2) { + qh_fprintf(qh ferr, 2070, "qh_rename_adjacentvertex: simplicial f%d contains old v%d and new v%d. Will be marked degenerate by qh_renamevertex\n", + neighbor->id, oldvertex->id, newvertex->id); + } + qh_makeridges(neighbor); /* no longer simplicial, nummerge==0, skipped by qh_maybe_duplicateridge */ + }else { + qh_replacefacetvertex(neighbor, oldvertex, newvertex); + qh_setunique(&newvertex->neighbors, neighbor); + qh_newvertices(neighbor->vertices); /* for qh_update_vertexneighbors of vertex neighbors */ + } + } + } + ridges= qh_vertexridges(oldvertex, qh_ALL); + if (istrace) { + FOREACHridge_(ridges) { + qh_printridge(qh ferr, ridge); + } + } + FOREACHneighbor_(oldvertex) { + if (!neighbor->simplicial){ + qh_addfacetvertex(neighbor, newvertex); + qh_setunique(&newvertex->neighbors, neighbor); + qh_newvertices(neighbor->vertices); /* for qh_update_vertexneighbors of vertex neighbors */ + if (qh newfacet_list == qh facet_tail) { + qh_removefacet(neighbor); /* add a neighbor to newfacet_list so that qh_partitionvisible has a newfacet */ + qh_appendfacet(neighbor); + neighbor->newfacet= True; + } + } + } + qh_renamevertex(oldvertex, newvertex, ridges, NULL, NULL); /* ridges invalidated */ + if (oldvertex->deleted && !oldvertex->partitioned) { + FOREACHneighbor_(newvertex) { + if (!neighbor->visible) { + qh_distplane(oldvertex->point, neighbor, &dist2); + if (dist2>maxdist2) { + maxdist2= dist2; + maxfacet= neighbor; + } + } + } + trace2((qh ferr, 2096, "qh_rename_adjacentvertex: partition old p%d(v%d) as a coplanar point for furthest f%d dist %2.2g. Maybe repartition later (QH0031)\n", + qh_pointid(oldvertex->point), oldvertex->id, maxfacet->id, maxdist2)) + qh_partitioncoplanar(oldvertex->point, maxfacet, NULL, !qh_ALL); /* faster with maxdist2, otherwise duplicates distance tests from maxdist2/dist2 */ + oldvertex->partitioned= True; + } + qh_settempfree(&ridges); +} /* rename_adjacentvertex */ + /*--------------------------------- @@ -3072,6 +4570,8 @@ boolT qh_remove_extravertices(facetT *facet) { updates vertex->neighbors notes: + only called by qh_reducevertices after qh_remove_extravertices + so f.vertices does not contain extraneous vertices a shared vertex for a facet is only in ridges to one neighbor this may undo a pinched facet @@ -3088,7 +4588,7 @@ boolT qh_remove_extravertices(facetT *facet) { vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) { facetT *neighbor, **neighborp, *neighborA= NULL; setT *vertices, *ridges; - vertexT *newvertex; + vertexT *newvertex= NULL; if (qh_setsize(vertex->neighbors) == 2) { neighborA= SETfirstt_(vertex->neighbors, facetT); @@ -3107,27 +4607,29 @@ vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) { neighborA= neighbor; } } - if (!neighborA) { - qh_fprintf(qh ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n", + } + if (!neighborA) { + qh_fprintf(qh ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n", vertex->id, facet->id); - qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex); - qh_errexit(qh_ERRqhull, NULL, NULL); - } + qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (neighborA) { /* avoid warning */ + /* the vertex is shared by facet and neighborA */ + ridges= qh_settemp(qh TEMPsize); + neighborA->visitid= ++qh visit_id; + qh_vertexridges_facet(vertex, facet, &ridges); + trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n", + qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id)); + zinc_(Zintersectnum); + vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices); + qh_setdel(vertices, vertex); + qh_settemppush(vertices); + if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) + qh_renamevertex(vertex, newvertex, ridges, facet, neighborA); /* ridges invalidated */ + qh_settempfree(&vertices); + qh_settempfree(&ridges); } - /* the vertex is shared by facet and neighborA */ - ridges= qh_settemp(qh TEMPsize); - neighborA->visitid= ++qh visit_id; - qh_vertexridges_facet(vertex, facet, &ridges); - trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n", - qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id)); - zinc_(Zintersectnum); - vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices); - qh_setdel(vertices, vertex); - qh_settemppush(vertices); - if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) - qh_renamevertex(vertex, newvertex, ridges, facet, neighborA); - qh_settempfree(&vertices); - qh_settempfree(&ridges); return newvertex; } /* rename_sharedvertex */ @@ -3138,6 +4640,12 @@ vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) { renames oldvertex as newvertex in ridge returns: + True if renames oldvertex + False if deleted the ridge + + notes: + called by qh_renamevertex + caller sets newvertex->delridge for deleted ridges (qh_reducevertices) design: delete oldvertex from ridge @@ -3148,12 +4656,17 @@ vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) { insert newvertex into the ridge adjust the ridge's orientation */ -void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) { +boolT qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) { int nth= 0, oldnth; facetT *temp; vertexT *vertex, **vertexp; oldnth= qh_setindex(ridge->vertices, oldvertex); + if (oldnth < 0) { + qh_fprintf(qh ferr, 6424, "qhull internal error (qh_renameridgevertex): oldvertex v%d not found in r%d. Cannot rename to v%d\n", + oldvertex->id, ridge->id, newvertex->id); + qh_errexit(qh_ERRqhull, NULL, ridge); + } qh_setdelnthsorted(ridge->vertices, oldnth); FOREACHvertex_(ridge->vertices) { if (vertex == newvertex) { @@ -3162,14 +4675,16 @@ void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) qh_copynonconvex(ridge); trace2((qh ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n", ridge->id, oldvertex->id, newvertex->id)); - qh_delridge(ridge); - return; + qh_delridge_merge(ridge); /* ridge.vertices deleted */ + return False; } if (vertex->id < newvertex->id) break; nth++; } qh_setaddnth(&ridge->vertices, nth, newvertex); + ridge->simplicialtop= False; + ridge->simplicialbot= False; if (abs(oldnth - nth)%2) { trace3((qh ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n", ridge->id)); @@ -3177,6 +4692,7 @@ void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) ridge->top= ridge->bottom; ridge->bottom= temp; } + return True; } /* renameridgevertex */ @@ -3184,21 +4700,27 @@ void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) >-------------------------------- qh_renamevertex( oldvertex, newvertex, ridges, oldfacet, neighborA ) - renames oldvertex as newvertex in ridges - gives oldfacet/neighborA if oldvertex is shared between two facets + renames oldvertex as newvertex in ridges of non-simplicial neighbors + set oldfacet/neighborA if oldvertex is shared between two facets (qh_rename_sharedvertex) + otherwise qh_redundant_vertex or qh_rename_adjacentvertex returns: - oldvertex may still exist afterwards - + if oldfacet and multiple neighbors, oldvertex may still exist afterwards + otherwise sets oldvertex->deleted for later deletion + one or more ridges maybe deleted + ridges is invalidated + merges may be added to degen_mergeset via qh_maydropneighbor or qh_degen_redundant_facet notes: - can not change neighbors of newvertex (since it's a subset) + qh_rename_sharedvertex can not change neighbors of newvertex (since it's a subset) + qh_redundant_vertex due to vertex->delridge for qh_reducevertices + qh_rename_adjacentvertex for complete renames design: for each ridge in ridges rename oldvertex to newvertex and delete degenerate ridges if oldfacet not defined - for each neighbor of oldvertex + for each non-simplicial neighbor of oldvertex delete oldvertex from neighbor's vertices remove extra vertices from neighbor add oldvertex to qh.del_vertices @@ -3207,29 +4729,56 @@ void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) 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 + delete oldfacet from old vertex's neighbors remove extra vertices (e.g., oldvertex) from neighborA */ void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) { facetT *neighbor, **neighborp; ridgeT *ridge, **ridgep; + int topsize, bottomsize; boolT istrace= False; +#ifndef qh_NOtrace if (qh IStracing >= 2 || oldvertex->id == qh tracevertex_id || - newvertex->id == qh tracevertex_id) + newvertex->id == qh tracevertex_id) { istrace= True; - FOREACHridge_(ridges) - qh_renameridgevertex(ridge, oldvertex, newvertex); + qh_fprintf(qh ferr, 2086, "qh_renamevertex: rename v%d to v%d in %d ridges with old f%d and neighbor f%d\n", + oldvertex->id, newvertex->id, qh_setsize(ridges), getid_(oldfacet), getid_(neighborA)); + } +#endif + FOREACHridge_(ridges) { + if (qh_renameridgevertex(ridge, oldvertex, newvertex)) { /* ridge is deleted if False, invalidating ridges */ + topsize= qh_setsize(ridge->top->vertices); + bottomsize= qh_setsize(ridge->bottom->vertices); + if (topsize < qh hull_dim || (topsize == qh hull_dim && !ridge->top->simplicial && qh_setin(ridge->top->vertices, newvertex))) { + trace4((qh ferr, 4070, "qh_renamevertex: ignore duplicate check for r%d. top f%d (size %d) will be degenerate after rename v%d to v%d\n", + ridge->id, ridge->top->id, topsize, oldvertex->id, newvertex->id)); + }else if (bottomsize < qh hull_dim || (bottomsize == qh hull_dim && !ridge->bottom->simplicial && qh_setin(ridge->bottom->vertices, newvertex))) { + trace4((qh ferr, 4071, "qh_renamevertex: ignore duplicate check for r%d. bottom f%d (size %d) will be degenerate after rename v%d to v%d\n", + ridge->id, ridge->bottom->id, bottomsize, oldvertex->id, newvertex->id)); + }else + qh_maybe_duplicateridge(ridge); + } + } if (!oldfacet) { - zinc_(Zrenameall); + /* stat Zrenameall or Zpinchduplicate */ if (istrace) - qh_fprintf(qh ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n", + qh_fprintf(qh ferr, 2087, "qh_renamevertex: renaming v%d to v%d in several facets for qh_redundant_vertex or MRGsubridge\n", oldvertex->id, newvertex->id); FOREACHneighbor_(oldvertex) { - qh_maydropneighbor(neighbor); - qh_setdelsorted(neighbor->vertices, oldvertex); - if (qh_remove_extravertices(neighbor)) - neighborp--; /* neighbor may be deleted */ + if (neighbor->simplicial) { + qh_degen_redundant_facet(neighbor); /* e.g., rbox 175 C3,2e-13 D4 t1545235541 | qhull d */ + }else { + if (istrace) + qh_fprintf(qh ferr, 4080, "qh_renamevertex: rename vertices in non-simplicial neighbor f%d of v%d\n", neighbor->id, oldvertex->id); + qh_maydropneighbor(neighbor); + qh_setdelsorted(neighbor->vertices, oldvertex); /* if degenerate, qh_degen_redundant_facet will add to mergeset */ + if (qh_remove_extravertices(neighbor)) + neighborp--; /* neighbor deleted from oldvertex neighborset */ + qh_degen_redundant_facet(neighbor); /* either direction may be redundant, faster if combine? */ + qh_test_redundant_neighbors(neighbor); + qh_test_degen_neighbors(neighbor); + } } if (!oldvertex->deleted) { oldvertex->deleted= True; @@ -3238,120 +4787,454 @@ void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facet }else if (qh_setsize(oldvertex->neighbors) == 2) { zinc_(Zrenameshare); if (istrace) - qh_fprintf(qh ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n", + qh_fprintf(qh ferr, 3039, "qh_renamevertex: renaming v%d to v%d in oldfacet f%d for qh_rename_sharedvertex\n", oldvertex->id, newvertex->id, oldfacet->id); - FOREACHneighbor_(oldvertex) + FOREACHneighbor_(oldvertex) { qh_setdelsorted(neighbor->vertices, oldvertex); + qh_degen_redundant_facet(neighbor); + } oldvertex->deleted= True; qh_setappend(&qh del_vertices, oldvertex); }else { zinc_(Zrenamepinch); - if (istrace || qh IStracing) - qh_fprintf(qh ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n", + if (istrace || qh IStracing >= 1) + qh_fprintf(qh ferr, 3040, "qh_renamevertex: renaming pinched v%d to v%d between f%d and f%d\n", oldvertex->id, newvertex->id, oldfacet->id, neighborA->id); qh_setdelsorted(oldfacet->vertices, oldvertex); qh_setdel(oldvertex->neighbors, oldfacet); - qh_remove_extravertices(neighborA); + if (qh_remove_extravertices(neighborA)) + qh_degen_redundant_facet(neighborA); } + if (oldfacet) + qh_degen_redundant_facet(oldfacet); } /* renamevertex */ - /*--------------------------------- - qh_test_appendmerge( facet, neighbor ) - tests facet/neighbor for convexity - appends to mergeset if non-convex + qh_test_appendmerge( facet, neighbor, simplicial ) + test convexity and append to qh.facet_mergeset if non-convex if pre-merging, - nop if qh.SKIPconvex, or qh.MERGEexact and coplanar + no-op if qh.SKIPconvex, or qh.MERGEexact and coplanar + if simplicial, assumes centrum test is valid (e.g., adjacent, simplicial new facets) returns: - true if appends facet/neighbor to mergeset + true if appends facet/neighbor to qh.facet_mergeset sets facet->center as needed does not change facet->seen + notes: + called from qh_getmergeset_initial, qh_getmergeset, and qh_test_vneighbors + must be at least as strong as qh_checkconvex (poly2.c) + assumes !f.flipped + design: - if qh.cos_max is defined + exit if qh.SKIPconvex ('Q0') and !qh.POSTmerging + if qh.cos_max ('An') is defined and merging coplanars if the angle between facet normals is too shallow append an angle-coplanar merge to qh.mergeset return True - make facet's centrum if needed - if facet's centrum is above the neighbor - set isconcave - else - if facet's centrum is not below the neighbor - set iscoplanar - make neighbor's centrum if needed - if neighbor's centrum is above the facet - set isconcave - else if neighbor's centrum is not below the facet - set iscoplanar - if isconcave or iscoplanar - get angle if needed - append concave or coplanar merge to qh.mergeset -*/ -boolT qh_test_appendmerge(facetT *facet, facetT *neighbor) { - realT dist, dist2= -REALmax, angle= -REALmax; - boolT isconcave= False, iscoplanar= False, okangle= False; + test convexity of facet and neighbor +*/ +boolT qh_test_appendmerge(facetT *facet, facetT *neighbor, boolT simplicial) { + realT angle= -REALmax; + boolT okangle= False; if (qh SKIPconvex && !qh POSTmerging) return False; - if ((!qh MERGEexact || qh POSTmerging) && qh cos_max < REALmax/2) { + if (qh cos_max < REALmax/2 && (!qh MERGEexact || qh POSTmerging)) { angle= qh_getangle(facet->normal, neighbor->normal); + okangle= True; zinc_(Zangletests); if (angle > qh cos_max) { zinc_(Zcoplanarangle); - qh_appendmergeset(facet, neighbor, MRGanglecoplanar, &angle); + qh_appendmergeset(facet, neighbor, MRGanglecoplanar, 0.0, angle); trace2((qh ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n", angle, facet->id, neighbor->id)); return True; - }else - okangle= True; + } } + if (simplicial || qh hull_dim <= 3) + return qh_test_centrum_merge(facet, neighbor, angle, okangle); + else + return qh_test_nonsimplicial_merge(facet, neighbor, angle, okangle); +} /* test_appendmerge */ + +/*--------------------------------- + + qh_test_centrum_merge( facet, neighbor, angle, okangle ) + test centrum convexity and append non-convex facets to qh.facet_mergeset + 'angle' is angle between facets if okangle is true, otherwise use 0.0 + + returns: + true if append facet/neighbor to qh.facet_mergeset + sets facet->center as needed + does not change facet->seen + + notes: + called from test_appendmerge if adjacent simplicial facets or 2-d/3-d + at least as strict as qh_checkconvex, including qh.DISTround ('En' and 'Rn') + + design: + make facet's centrum if needed + if facet's centrum is above the neighbor (qh.centrum_radius) + set isconcave + + if facet's centrum is not below the neighbor (-qh.centrum_radius) + 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 and merging coplanars + get angle if needed (qh.ANGLEmerge 'An') + append concave-coplanar, concave ,or coplanar merge to qh.mergeset +*/ +boolT qh_test_centrum_merge(facetT *facet, facetT *neighbor, realT angle, boolT okangle) { + coordT dist, dist2, mergedist; + boolT isconcave= False, iscoplanar= False; + if (!facet->center) facet->center= qh_getcentrum(facet); zzinc_(Zcentrumtests); qh_distplane(facet->center, neighbor, &dist); if (dist > qh centrum_radius) isconcave= True; - else { - if (dist > -qh centrum_radius) - iscoplanar= True; - if (!neighbor->center) - neighbor->center= qh_getcentrum(neighbor); - zzinc_(Zcentrumtests); - qh_distplane(neighbor->center, facet, &dist2); - if (dist2 > qh centrum_radius) - isconcave= True; - else if (!iscoplanar && dist2 > -qh centrum_radius) - iscoplanar= True; - } + else if (dist >= -qh centrum_radius) + iscoplanar= True; + if (!neighbor->center) + neighbor->center= qh_getcentrum(neighbor); + zzinc_(Zcentrumtests); + qh_distplane(neighbor->center, facet, &dist2); + if (dist2 > qh centrum_radius) + isconcave= True; + else if (!iscoplanar && dist2 >= -qh centrum_radius) + iscoplanar= True; if (!isconcave && (!iscoplanar || (qh MERGEexact && !qh POSTmerging))) return False; if (!okangle && qh ANGLEmerge) { angle= qh_getangle(facet->normal, neighbor->normal); zinc_(Zangletests); } - if (isconcave) { - zinc_(Zconcaveridge); - if (qh ANGLEmerge) - angle += qh_ANGLEconcave + 0.5; - qh_appendmergeset(facet, neighbor, MRGconcave, &angle); - trace0((qh ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n", + if (isconcave && iscoplanar) { + zinc_(Zconcavecoplanarridge); + if (dist > dist2) + qh_appendmergeset(facet, neighbor, MRGconcavecoplanar, dist, angle); + else + qh_appendmergeset(neighbor, facet, MRGconcavecoplanar, dist2, angle); + trace0((qh ferr, 36, "qh_test_centrum_merge: concave f%d to coplanar 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 if (isconcave) { + mergedist= fmax_(dist, dist2); + zinc_(Zconcaveridge); + qh_appendmergeset(facet, neighbor, MRGconcave, mergedist, angle); + trace0((qh ferr, 37, "qh_test_centrum_merge: 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 */ { + mergedist= fmin_(fabs_(dist), fabs_(dist2)); zinc_(Zcoplanarcentrum); - qh_appendmergeset(facet, neighbor, MRGcoplanar, &angle); - trace2((qh ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n", + qh_appendmergeset(facet, neighbor, MRGcoplanar, mergedist, angle); + trace2((qh ferr, 2097, "qh_test_centrum_merge: 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 */ +} /* test_centrum_merge */ + +/*--------------------------------- + + qh_test_degen_neighbors( facet ) + append degenerate neighbors to qh.degen_mergeset + + notes: + called at end of qh_mergefacet() and qh_renamevertex() + call after test_redundant_facet() since MRGredundant is less expensive then MRGdegen + a degenerate facet has fewer than hull_dim neighbors + see: qh_merge_degenredundant() + +*/ +void qh_test_degen_neighbors(facetT *facet) { + facetT *neighbor, **neighborp; + int size; + + trace4((qh ferr, 4073, "qh_test_degen_neighbors: test for degenerate neighbors of f%d\n", facet->id)); + FOREACHneighbor_(facet) { + if (neighbor->visible) { + qh_fprintf(qh ferr, 6359, "qhull internal error (qh_test_degen_neighbors): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + qh_errexit2(qh_ERRqhull, facet, neighbor); + } + if (neighbor->degenerate || neighbor->redundant || neighbor->dupridge) /* will merge or delete */ + continue; + /* merge flipped-degenerate facet before flipped facets */ + if ((size= qh_setsize(neighbor->neighbors)) < qh hull_dim) { + qh_appendmergeset(neighbor, neighbor, MRGdegen, 0.0, 1.0); + trace2((qh ferr, 2019, "qh_test_degen_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id)); + } + } +} /* test_degen_neighbors */ + + +/*--------------------------------- + + qh_test_nonsimplicial_merge( facet, neighbor, angle, okangle ) + test centrum and vertex convexity and append non-convex or redundant facets to qh.facet_mergeset + 'angle' is angle between facets if okangle is true, otherwise use 0.0 + skips coplanar merges if pre-merging with qh.MERGEexact ('Qx') + + returns: + true if appends facet/neighbor to qh.facet_mergeset + sets facet->center as needed + does not change facet->seen + + notes: + only called from test_appendmerge if a non-simplicial facet and at least 4-d + at least as strict as qh_checkconvex, including qh.DISTround ('En' and 'Rn') + centrums must be < -qh.centrum_radius + tests vertices as well as centrums since a facet may be twisted relative to its neighbor + + design: + set precision constants for maxoutside, clearlyconcave, minvertex, and coplanarcentrum + use maxoutside for coplanarcentrum if premerging with 'Qx' and qh_MAXcoplanarcentrum merges + otherwise use qh.centrum_radious for coplanarcentrum + make facet and neighbor centrums if needed + isconcave if a centrum is above neighbor (coplanarcentrum) + iscoplanar if a centrum is not below neighbor (-qh.centrum_radius) + maybeconvex if a centrum is clearly below neighbor (-clearyconvex) + return False if both centrums clearly below neighbor (-clearyconvex) + return MRGconcave if isconcave + + facets are neither clearly convex nor clearly concave + test vertices as well as centrums + if maybeconvex + determine mindist and maxdist for vertices of the other facet + maybe MRGredundant + otherwise + determine mindist and maxdist for vertices of either facet + maybe MRGredundant + maybeconvex if a vertex is clearly below neighbor (-clearconvex) + + vertices are concave if dist > clearlyconcave + vertices are twisted if dist > maxoutside (isconcave and maybeconvex) + return False if not concave and pre-merge of 'Qx' (qh.MERGEexact) + vertices are coplanar if dist in -minvertex..maxoutside + if !isconcave, vertices are coplanar if dist >= -qh.MAXcoplanar (n*qh.premerge_centrum) + + return False if neither concave nor coplanar + return MRGtwisted if isconcave and maybeconvex + return MRGconcavecoplanar if isconcave and isconvex + return MRGconcave if isconcave + return MRGcoplanar if iscoplanar +*/ +boolT qh_test_nonsimplicial_merge(facetT *facet, facetT *neighbor, realT angle, boolT okangle) { + coordT dist, mindist, maxdist, mindist2, maxdist2, dist2, maxoutside, clearlyconcave, minvertex, clearlyconvex, mergedist, coplanarcentrum; + boolT isconcave= False, iscoplanar= False, maybeconvex= False, isredundant= False; + vertexT *maxvertex= NULL, *maxvertex2= NULL; + + maxoutside= fmax_(neighbor->maxoutside, qh ONEmerge + qh DISTround); + maxoutside= fmax_(maxoutside, facet->maxoutside); + clearlyconcave= qh_RATIOconcavehorizon * maxoutside; + minvertex= fmax_(-qh min_vertex, qh MAXcoplanar); /* non-negative, not available per facet, not used for iscoplanar */ + clearlyconvex= qh_RATIOconvexmerge * minvertex; /* must be convex for MRGtwisted */ + if (qh MERGEexact && !qh POSTmerging && (facet->nummerge > qh_MAXcoplanarcentrum || neighbor->nummerge > qh_MAXcoplanarcentrum)) + coplanarcentrum= maxoutside; + else + coplanarcentrum= qh centrum_radius; + + if (!facet->center) + facet->center= qh_getcentrum(facet); + zzinc_(Zcentrumtests); + qh_distplane(facet->center, neighbor, &dist); + if (dist > coplanarcentrum) + isconcave= True; + else if (dist >= -qh centrum_radius) + iscoplanar= True; + else if (dist < -clearlyconvex) + maybeconvex= True; + if (!neighbor->center) + neighbor->center= qh_getcentrum(neighbor); + zzinc_(Zcentrumtests); + qh_distplane(neighbor->center, facet, &dist2); + if (dist2 > coplanarcentrum) + isconcave= True; + else if (dist2 >= -qh centrum_radius) + iscoplanar= True; + else if (dist2 < -clearlyconvex) { + if (maybeconvex) + return False; /* both centrums clearly convex */ + maybeconvex= True; + } + if (isconcave) { + if (!okangle && qh ANGLEmerge) { + angle= qh_getangle(facet->normal, neighbor->normal); + zinc_(Zangletests); + } + mergedist= fmax_(dist, dist2); + zinc_(Zconcaveridge); + qh_appendmergeset(facet, neighbor, MRGconcave, mergedist, angle); + trace0((qh ferr, 18, "qh_test_nonsimplicial_merge: concave centrum for f%d or 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)); + return True; + } + /* neither clearly convex nor clearly concave, test vertices as well as centrums */ + if (maybeconvex) { + if (dist < -clearlyconvex) { + maxdist= dist; /* facet centrum clearly convex, no need to test its vertex distance */ + mindist= dist; + maxvertex2= qh_furthestvertex(neighbor, facet, &maxdist2, &mindist2); + if (!maxvertex2) { + qh_appendmergeset(neighbor, facet, MRGredundant, maxdist2, qh_ANGLEnone); + isredundant= True; + } + }else { /* dist2 < -clearlyconvex */ + maxdist2= dist2; /* neighbor centrum clearly convex, no need to test its vertex distance */ + mindist2= dist2; + maxvertex= qh_furthestvertex(facet, neighbor, &maxdist, &mindist); + if (!maxvertex) { + qh_appendmergeset(facet, neighbor, MRGredundant, maxdist, qh_ANGLEnone); + isredundant= True; + } + } + }else { + maxvertex= qh_furthestvertex(facet, neighbor, &maxdist, &mindist); + if (maxvertex) { + maxvertex2= qh_furthestvertex(neighbor, facet, &maxdist2, &mindist2); + if (!maxvertex2) { + qh_appendmergeset(neighbor, facet, MRGredundant, maxdist2, qh_ANGLEnone); + isredundant= True; + }else if (mindist < -clearlyconvex || mindist2 < -clearlyconvex) + maybeconvex= True; + }else { /* !maxvertex */ + qh_appendmergeset(facet, neighbor, MRGredundant, maxdist, qh_ANGLEnone); + isredundant= True; + } + } + if (isredundant) { + zinc_(Zredundantmerge); + return True; + } + + if (maxdist > clearlyconcave || maxdist2 > clearlyconcave) + isconcave= True; + else if (maybeconvex) { + if (maxdist > maxoutside || maxdist2 > maxoutside) + isconcave= True; /* MRGtwisted */ + } + if (!isconcave && qh MERGEexact && !qh POSTmerging) + return False; + if (isconcave && !iscoplanar) { + if (maxdist < maxoutside && (-qh MAXcoplanar || (maxdist2 < maxoutside && mindist2 >= -qh MAXcoplanar))) + iscoplanar= True; /* MRGconcavecoplanar */ + }else if (!iscoplanar) { + if (mindist >= -qh MAXcoplanar || mindist2 >= -qh MAXcoplanar) + iscoplanar= True; /* MRGcoplanar */ + } + if (!isconcave && !iscoplanar) + return False; + if (!okangle && qh ANGLEmerge) { + angle= qh_getangle(facet->normal, neighbor->normal); + zinc_(Zangletests); + } + if (isconcave && maybeconvex) { + zinc_(Ztwistedridge); + if (maxdist > maxdist2) + qh_appendmergeset(facet, neighbor, MRGtwisted, maxdist, angle); + else + qh_appendmergeset(neighbor, facet, MRGtwisted, maxdist2, angle); + trace0((qh ferr, 27, "qh_test_nonsimplicial_merge: twisted concave f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n", + facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh furthest_id)); + }else if (isconcave && iscoplanar) { + zinc_(Zconcavecoplanarridge); + if (maxdist > maxdist2) + qh_appendmergeset(facet, neighbor, MRGconcavecoplanar, maxdist, angle); + else + qh_appendmergeset(neighbor, facet, MRGconcavecoplanar, maxdist2, angle); + trace0((qh ferr, 28, "qh_test_nonsimplicial_merge: concave coplanar f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n", + facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh furthest_id)); + }else if (isconcave) { + mergedist= fmax_(maxdist, maxdist2); + zinc_(Zconcaveridge); + qh_appendmergeset(facet, neighbor, MRGconcave, mergedist, angle); + trace0((qh ferr, 29, "qh_test_nonsimplicial_merge: concave f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n", + facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh furthest_id)); + }else /* iscoplanar */ { + mergedist= fmax_(fmax_(maxdist, maxdist2), fmax_(-mindist, -mindist2)); + zinc_(Zcoplanarcentrum); + qh_appendmergeset(facet, neighbor, MRGcoplanar, mergedist, angle); + trace2((qh ferr, 2099, "qh_test_nonsimplicial_merge: coplanar f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n", + facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh furthest_id)); + } + return True; +} /* test_nonsimplicial_merge */ + +/*--------------------------------- + + qh_test_redundant_neighbors( facet ) + append degenerate facet or its redundant neighbors to qh.degen_mergeset + + returns: + bumps vertex_visit + + notes: + called at end of qh_mergefacet(), qh_mergecycle_all(), and qh_renamevertex + call before qh_test_degen_neighbors (MRGdegen are more likely to cause problems) + a redundant neighbor's vertices is a subset of the facet's vertices + with pinched and flipped facets, a redundant neighbor may have a wildly different normal + + see qh_merge_degenredundant() and qh_-_facet() + + design: + if facet is degenerate + appends facet to degen_mergeset + else + appends redundant neighbors of facet to degen_mergeset +*/ +void qh_test_redundant_neighbors(facetT *facet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + int size; + + trace4((qh ferr, 4022, "qh_test_redundant_neighbors: test neighbors of f%d vertex_visit %d\n", + facet->id, qh vertex_visit+1)); + if ((size= qh_setsize(facet->neighbors)) < qh hull_dim) { + qh_appendmergeset(facet, facet, MRGdegen, 0.0, 1.0); + trace2((qh ferr, 2017, "qh_test_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size)); + }else { + qh vertex_visit++; + FOREACHvertex_(facet->vertices) + vertex->visitid= qh vertex_visit; + FOREACHneighbor_(facet) { + if (neighbor->visible) { + qh_fprintf(qh ferr, 6360, "qhull internal error (qh_test_redundant_neighbors): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + qh_errexit2(qh_ERRqhull, facet, neighbor); + } + if (neighbor->degenerate || neighbor->redundant || neighbor->dupridge) /* will merge or delete */ + continue; + if (facet->flipped && !neighbor->flipped) /* do not merge non-flipped into flipped */ + continue; + /* merge redundant-flipped facet first */ + /* uses early out instead of checking vertex count */ + FOREACHvertex_(neighbor->vertices) { + if (vertex->visitid != qh vertex_visit) + break; + } + if (!vertex) { + qh_appendmergeset(neighbor, facet, MRGredundant, 0.0, 1.0); + trace2((qh ferr, 2018, "qh_test_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id)); + } + } + } +} /* test_redundant_neighbors */ /*--------------------------------- - qh_test_vneighbors() + qh_test_vneighbors( ) test vertex neighbors for convexity tests all facets on qh.newfacet_list @@ -3360,6 +5243,7 @@ boolT qh_test_appendmerge(facetT *facet, facetT *neighbor) { initializes vertex neighbors if needed notes: + called by qh_all_merges from qh_postmerge if qh.TESTvneighbors ('Qv') assumes all facet neighbors have been tested this can be expensive this does not guarantee that a centrum is below all facets @@ -3392,7 +5276,7 @@ boolT qh_test_vneighbors(void /* qh.newfacet_list */) { FOREACHneighbor_(vertex) { if (neighbor->seen || neighbor->visitid == qh visit_id) continue; - if (qh_test_appendmerge(newfacet, neighbor)) + if (qh_test_appendmerge(newfacet, neighbor, False)) /* ignores optimization for simplicial ridges */ nummerges++; } } @@ -3409,14 +5293,20 @@ boolT qh_test_vneighbors(void /* qh.newfacet_list */) { qh_tracemerge( facet1, facet2 ) print trace message after merge */ -void qh_tracemerge(facetT *facet1, facetT *facet2) { +void qh_tracemerge(facetT *facet1, facetT *facet2, mergeType mergetype) { boolT waserror= False; + const char *mergename; #ifndef qh_NOtrace + if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *)) + mergename= mergetypes[mergetype]; + else + mergename= mergetypes[MRGnone]; if (qh IStracing >= 4) qh_errprint("MERGED", facet2, NULL, NULL, NULL); - if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newlist)) { - qh_fprintf(qh ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh furthest_id); + if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newfacet)) { + qh_fprintf(qh ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d into f%d type %d (%s), furthest p%d\n", + facet1->id, facet2->id, mergetype, mergename, qh furthest_id); if (facet2 != qh tracefacet) qh_errprint("TRACE", qh tracefacet, (qh tracevertex && qh tracevertex->neighbors) ? @@ -3428,25 +5318,25 @@ void qh_tracemerge(facetT *facet1, facetT *facet2) { qh_fprintf(qh ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n", qh furthest_id); else - qh_checkvertex(qh tracevertex); - } - if (qh tracefacet) { - qh_checkfacet(qh tracefacet, True, &waserror); - if (waserror) - qh_errexit(qh_ERRqhull, qh tracefacet, NULL); + qh_checkvertex(qh tracevertex, qh_ALL, &waserror); } + if (qh tracefacet && qh tracefacet->normal && !qh tracefacet->visible) + qh_checkfacet(qh tracefacet, True /* newmerge */, &waserror); #endif /* !qh_NOtrace */ if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */ - qh_checkfacet(facet2, True, &waserror); - if (waserror) - qh_errexit(qh_ERRqhull, NULL, NULL); + if (qh IStracing >= 4 && qh num_facets < 500) { + qh_printlists(); + } + qh_checkfacet(facet2, True /* newmerge */, &waserror); } + if (waserror) + qh_errexit(qh_ERRqhull, NULL, NULL); /* erroneous facet logged by qh_checkfacet */ } /* tracemerge */ /*--------------------------------- - qh_tracemerging() + qh_tracemerging( ) print trace message during POSTmerging returns: @@ -3471,10 +5361,10 @@ void qh_tracemerging(void) { cpu /= qh_SECticks; total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); qh_fprintf(qh ferr, 8087, "\n\ -At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\ - contains %d facets and %d vertices.\n", - tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, - total, qh num_facets - qh num_visible, +At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets with max_outside %2.2g, min_vertex %2.2g.\n\ + The hull contains %d facets and %d vertices.\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, total, qh max_outside, qh min_vertex, + qh num_facets - qh num_visible, qh num_vertices-qh_setsize(qh del_vertices)); } /* tracemerging */ @@ -3488,6 +5378,9 @@ At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\ deletes facet2->center unless it's already large if so, clears facet2->ridge->tested + notes: + only called by qh_mergefacet + design: clear facet2->tested clear ridge->tested for facet1's ridges @@ -3531,28 +5424,32 @@ void qh_updatetested(facetT *facet1, facetT *facet2) { /*--------------------------------- - qh_vertexridges( vertex ) + qh_vertexridges( vertex, allneighbors ) return temporary set of ridges adjacent to a vertex - vertex->neighbors defined + vertex->neighbors defined (qh_vertexneighbors) - ntoes: + notes: uses qh.visit_id does not include implicit ridges for simplicial facets + skips last neighbor, unless allneighbors. For new facets, the last neighbor shares ridges with adjacent neighbors + if the last neighbor is not simplicial, it will have ridges for its simplicial neighbors + Use allneighbors when a new cone is attached to an existing convex hull + similar to qh_neighbor_vertices design: for each neighbor of vertex add ridges that include the vertex to ridges */ -setT *qh_vertexridges(vertexT *vertex) { +setT *qh_vertexridges(vertexT *vertex, boolT allneighbors) { facetT *neighbor, **neighborp; setT *ridges= qh_settemp(qh TEMPsize); int size; - qh visit_id++; + qh visit_id += 2; /* visit_id for vertex neighbors, visit_id-1 for facets of visited ridges */ FOREACHneighbor_(vertex) neighbor->visitid= qh visit_id; FOREACHneighbor_(vertex) { - if (*neighborp) /* no new ridges in last neighbor */ + if (*neighborp || allneighbors) /* no new ridges in last neighbor */ qh_vertexridges_facet(vertex, neighbor, &ridges); } if (qh PRINTstatistics || qh IStracing) { @@ -3581,18 +5478,34 @@ setT *qh_vertexridges(vertexT *vertex) { for each ridge of facet if ridge of visited neighbor (i.e., unprocessed) if vertex in ridge - append ridge to vertex + append ridge mark facet processed */ void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges) { ridgeT *ridge, **ridgep; facetT *neighbor; + int last_i= qh hull_dim-2; + vertexT *second, *last; FOREACHridge_(facet->ridges) { neighbor= otherfacet_(ridge, facet); - if (neighbor->visitid == qh visit_id - && qh_setin(ridge->vertices, vertex)) - qh_setappend(ridges, ridge); + if (neighbor->visitid == qh visit_id) { + if (SETfirst_(ridge->vertices) == vertex) { + qh_setappend(ridges, ridge); + }else if (last_i > 2) { + second= SETsecondt_(ridge->vertices, vertexT); + last= SETelemt_(ridge->vertices, last_i, vertexT); + if (second->id >= vertex->id && last->id <= vertex->id) { /* vertices inverse sorted by id */ + if (second == vertex || last == vertex) + qh_setappend(ridges, ridge); + else if (qh_setin(ridge->vertices, vertex)) + qh_setappend(ridges, ridge); + } + }else if (SETelem_(ridge->vertices, last_i) == vertex + || (last_i > 1 && SETsecond_(ridge->vertices) == vertex)) { + qh_setappend(ridges, ridge); + } + } } facet->visitid= qh visit_id-1; } /* vertexridges_facet */ @@ -3601,28 +5514,68 @@ void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges) { >-------------------------------- qh_willdelete( facet, replace ) - moves facet to visible list + moves facet to visible list for qh_deletevisible sets facet->f.replace to replace (may be NULL) + clears f.ridges and f.neighbors -- no longer valid returns: bumps qh.num_visible */ void qh_willdelete(facetT *facet, facetT *replace) { + trace4((qh ferr, 4081, "qh_willdelete: move f%d to visible list, set its replacement as f%d, and clear f.neighbors and f.ridges\n", facet->id, getid_(replace))); + if (!qh visible_list && qh newfacet_list) { + qh_fprintf(qh ferr, 6378, "qhull internal error (qh_willdelete): expecting qh.visible_list at before qh.newfacet_list f%d. Got NULL\n", + qh newfacet_list->id); + qh_errexit2(qh_ERRqhull, NULL, NULL); + } qh_removefacet(facet); qh_prependfacet(facet, &qh visible_list); qh num_visible++; facet->visible= True; facet->f.replace= replace; + if (facet->ridges) + SETfirst_(facet->ridges)= NULL; + if (facet->neighbors) + SETfirst_(facet->neighbors)= NULL; } /* willdelete */ #else /* qh_NOmerge */ -void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) { + +void qh_all_vertexmerges(int apexpointid, facetT *facet, facetT **retryfacet) { + QHULL_UNUSED(apexpointid) + QHULL_UNUSED(facet) + QHULL_UNUSED(retryfacet) +} +void qh_premerge(int apexpointid, realT maxcentrum, realT maxangle) { + QHULL_UNUSED(apexpointid) + QHULL_UNUSED(maxcentrum) + QHULL_UNUSED(maxangle) } void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, boolT vneighbors) { + QHULL_UNUSED(reason) + QHULL_UNUSED(maxcentrum) + QHULL_UNUSED(maxangle) + QHULL_UNUSED(vneighbors) +} +void qh_checkdelfacet(facetT *facet, setT *mergeset) { + QHULL_UNUSED(facet) + QHULL_UNUSED(mergeset) +} +void qh_checkdelridge(void /* qh.visible_facets, vertex_mergeset */) { } boolT qh_checkzero(boolT testall) { - } + QHULL_UNUSED(testall) + + return True; +} +void qh_freemergesets(void) { +} +void qh_initmergesets(void) { +} +void qh_merge_pinchedvertices(int apexpointid /* qh.newfacet_list */) { + QHULL_UNUSED(apexpointid) +} #endif /* qh_NOmerge */ diff --git a/3rdparty/qhull/merge.h b/3rdparty/qhull/merge.h index 7f5ec3fb6..b3e1a4aaf 100644 --- a/3rdparty/qhull/merge.h +++ b/3rdparty/qhull/merge.h @@ -6,9 +6,9 @@ see qh-merge.htm and merge.c - Copyright (c) 1993-2015 C.B. Barber. - $Id: //main/2015/qhull/src/libqhull/merge.h#1 $$Change: 1981 $ - $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $ + Copyright (c) 1993-2020 C.B. Barber. + $Id: //main/2019/qhull/src/libqhull/merge.h#3 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #ifndef qhDEFmerge @@ -20,52 +20,44 @@ /*============ -constants- ==============*/ /*---------------------------------- + >--------------------------------- - qh_ANGLEredundant - indicates redundant merge in mergeT->angle + qh_ANGLEnone + indicates missing angle for mergeT->angle */ -#define qh_ANGLEredundant 6.0 - -/*---------------------------------- - - qh_ANGLEdegen - indicates degenerate facet in mergeT->angle -*/ -#define qh_ANGLEdegen 5.0 - -/*---------------------------------- - - qh_ANGLEconcave - offset to indicate concave facets in mergeT->angle - - notes: - concave facets are assigned the range of [2,4] in mergeT->angle - roundoff error may make the angle less than 2 -*/ -#define qh_ANGLEconcave 1.5 +#define qh_ANGLEnone 2.0 /*---------------------------------- MRG... (mergeType) indicates the type of a merge (mergeT->type) + MRGcoplanar...MRGtwisted set by qh_test_centrum_merge, qh_test_nonsimplicial_merge */ -typedef enum { /* in sort order for facet_mergeset */ +typedef enum { /* must match mergetypes[] */ 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) */ + /* MRGcoplanar..MRGtwisted go into qh.facet_mergeset for qh_all_merges + qh_compare_facetmerge selects lower mergetypes for merging first */ + MRGcoplanar, /* (1) centrum coplanar if centrum ('Cn') or vertex not clearly above or below neighbor */ + MRGanglecoplanar, /* (2) angle coplanar if angle ('An') is coplanar */ + MRGconcave, /* (3) concave ridge */ + MRGconcavecoplanar, /* (4) concave and coplanar ridge, one side concave, other side coplanar */ + MRGtwisted, /* (5) twisted ridge, both concave and convex, facet1 is wider */ + /* MRGflip go into qh.facet_mergeset for qh_flipped_merges */ + MRGflip, /* (6) flipped facet if qh.interior_point is above facet, w/ facet1 == facet2 */ + /* MRGdupridge go into qh.facet_mergeset for qh_forcedmerges */ + MRGdupridge, /* (7) dupridge if more than two neighbors. Set by qh_mark_dupridges for qh_MERGEridge */ + /* MRGsubridge and MRGvertices go into vertex_mergeset */ + MRGsubridge, /* (8) merge pinched vertex to remove the subridge of a MRGdupridge */ + MRGvertices, /* (9) merge pinched vertex to remove a facet's ridges with the same vertices */ + /* MRGdegen, MRGredundant, and MRGmirror go into qh.degen_mergeset */ + MRGdegen, /* (10) degenerate facet (!enough neighbors) facet1 == facet2 */ + MRGredundant, /* (11) redundant facet (vertex subset) */ /* merge_degenredundant assumes degen < redundant */ - MRGmirror, /* mirror facet from qh_triangulate */ + MRGmirror, /* (12) mirror facets: same vertices due to null facets in qh_triangulate + f.redundant for both facets*/ + /* MRGcoplanarhorizon for qh_mergecycle_all only */ + MRGcoplanarhorizon, /* (13) new facet coplanar with the horizon (qh_mergecycle_all) */ ENDmrg } mergeType; @@ -88,10 +80,16 @@ typedef enum { /* in sort order for facet_mergeset */ typedef struct mergeT mergeT; struct mergeT { /* initialize in qh_appendmergeset */ - realT angle; /* angle between normals of facet1 and facet2 */ + realT angle; /* cosine of angle between normals of facet1 and facet2, + null value and right angle is 0.0, coplanar is 1.0, narrow is -1.0 */ + realT distance; /* absolute value of distance between vertices, centrum and facet, or vertex and facet */ facetT *facet1; /* will merge facet1 into facet2 */ facetT *facet2; - mergeType type; + vertexT *vertex1; /* will merge vertext1 into vertex2 for MRGsubridge or MRGvertices */ + vertexT *vertex2; + ridgeT *ridge1; /* the duplicate ridges resolved by MRGvertices */ + ridgeT *ridge2; /* merge is deleted if either ridge is deleted (qh_delridge) */ + mergeType mergetype; }; @@ -106,50 +104,93 @@ struct mergeT { /* initialize in qh_appendmergeset */ notes: uses 'mergeT *merge, **mergep;' if qh_mergefacet(), - restart since qh.facet_mergeset may change + restart or use qh_setdellast() since qh.facet_mergeset may change see FOREACHsetelement_ */ -#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge) +#define FOREACHmerge_(merges) FOREACHsetelement_(mergeT, merges, merge) + +/*---------------------------------- + + FOREACHmergeA_( vertices ) { ... } + assign 'mergeA' to each merge in merges + + notes: + uses 'mergeT *mergeA, *mergeAp;' + see FOREACHsetelement_ +*/ +#define FOREACHmergeA_(merges) FOREACHsetelement_(mergeT, merges, mergeA) + +/*---------------------------------- + + FOREACHmerge_i_( vertices ) { ... } + assign 'merge' and 'merge_i' for each merge in mergeset + + declare: + mergeT *merge; + int merge_n, merge_i; + + see: + FOREACHsetelement_i_ +*/ +#define FOREACHmerge_i_(mergeset) FOREACHsetelement_i_(mergeT, mergeset, merge) /*============ prototypes in alphabetical order after pre/postmerge =======*/ -void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle); +void qh_premerge(int apexpointid, realT maxcentrum, realT maxangle); void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, boolT vneighbors); void qh_all_merges(boolT othermerge, boolT vneighbors); -void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle); -setT *qh_basevertices( facetT *samecycle); +void qh_all_vertexmerges(int apexpointid, facetT *facet, facetT **retryfacet); +void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, coordT dist, realT angle); +void qh_appendvertexmerge(vertexT *vertex, vertexT *destination, mergeType mergetype, realT distance, ridgeT *ridge1, ridgeT *ridge2); +setT *qh_basevertices(facetT *samecycle); +void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2); void qh_checkconnect(void /* qh.new_facets */); +void qh_checkdelfacet(facetT *facet, setT *mergeset); +void qh_checkdelridge(void /* qh.visible_facets, vertex_mergeset */); boolT qh_checkzero(boolT testall); -int qh_compareangle(const void *p1, const void *p2); -int qh_comparemerge(const void *p1, const void *p2); +int qh_compare_anglemerge(const void *p1, const void *p2); +int qh_compare_facetmerge(const void *p1, const void *p2); int qh_comparevisit(const void *p1, const void *p2); void qh_copynonconvex(ridgeT *atridge); void qh_degen_redundant_facet(facetT *facet); -void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet); +void qh_drop_mergevertex(mergeT *merge); +void qh_delridge_merge(ridgeT *ridge); vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges); +vertexT *qh_findbest_pinchedvertex(mergeT *merge, vertexT *apex, vertexT **pinchedp, realT *distp /* qh.newfacet_list */); +vertexT *qh_findbest_ridgevertex(ridgeT *ridge, vertexT **pinchedp, coordT *distp); void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor, facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp); facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp); void qh_flippedmerges(facetT *facetlist, boolT *wasmerge); -void qh_forcedmerges( boolT *wasmerge); +void qh_forcedmerges(boolT *wasmerge); +void qh_freemergesets(void); void qh_getmergeset(facetT *facetlist); void qh_getmergeset_initial(facetT *facetlist); +boolT qh_getpinchedmerges(vertexT *apex, coordT maxdupdist, boolT *iscoplanar /* qh.newfacet_list, vertex_mergeset */); +boolT qh_hasmerge(setT *mergeset, mergeType type, facetT *facetA, facetT *facetB); void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex); ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *vertex, vertexT *oldvertex, int *hashslot); +void qh_initmergesets(void); void qh_makeridges(facetT *facet); -void qh_mark_dupridges(facetT *facetlist); +void qh_mark_dupridges(facetT *facetlist, boolT allmerges); +void qh_maybe_duplicateridge(ridgeT *ridge); +void qh_maybe_duplicateridges(facetT *facet); void qh_maydropneighbor(facetT *facet); int qh_merge_degenredundant(void); -void qh_merge_nonconvex( facetT *facet1, facetT *facet2, mergeType mergetype); +void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype); +void qh_merge_pinchedvertices(int apexpointid /* qh.newfacet_list */); +void qh_merge_twisted(facetT *facet1, facetT *facet2); void qh_mergecycle(facetT *samecycle, facetT *newfacet); void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge); -void qh_mergecycle_facets( facetT *samecycle, facetT *newfacet); +void qh_mergecycle_facets(facetT *samecycle, facetT *newfacet); void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet); void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet); -void qh_mergecycle_vneighbors( facetT *samecycle, facetT *newfacet); -void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex); +void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet); +void qh_mergefacet(facetT *facet1, facetT *facet2, mergeType mergetype, realT *mindist, realT *maxdist, boolT mergeapex); void qh_mergefacet2d(facetT *facet1, facetT *facet2); void qh_mergeneighbors(facetT *facet1, facetT *facet2); void qh_mergeridges(facetT *facet1, facetT *facet2); @@ -158,20 +199,31 @@ void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2); void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2); void qh_mergevertices(setT *vertices1, setT **vertices); setT *qh_neighbor_intersections(vertexT *vertex); +setT *qh_neighbor_vertices(vertexT *vertex, setT *subridge); +void qh_neighbor_vertices_facet(vertexT *vertexA, facetT *facet, setT **vertices); void qh_newvertices(setT *vertices); +mergeT *qh_next_vertexmerge(void); +facetT *qh_opposite_horizonfacet(mergeT *merge, vertexT **vertex); boolT qh_reducevertices(void); vertexT *qh_redundant_vertex(vertexT *vertex); boolT qh_remove_extravertices(facetT *facet); +void qh_remove_mergetype(setT *mergeset, mergeType type); +void qh_rename_adjacentvertex(vertexT *oldvertex, vertexT *newvertex, realT dist); vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet); -void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex); +boolT qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex); void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA); -boolT qh_test_appendmerge(facetT *facet, facetT *neighbor); +boolT qh_test_appendmerge(facetT *facet, facetT *neighbor, boolT simplicial); +void qh_test_degen_neighbors(facetT *facet); +boolT qh_test_centrum_merge(facetT *facet, facetT *neighbor, realT angle, boolT okangle); +boolT qh_test_nonsimplicial_merge(facetT *facet, facetT *neighbor, realT angle, boolT okangle); +void qh_test_redundant_neighbors(facetT *facet); boolT qh_test_vneighbors(void /* qh.newfacet_list */); -void qh_tracemerge(facetT *facet1, facetT *facet2); +void qh_tracemerge(facetT *facet1, facetT *facet2, mergeType mergetype); void qh_tracemerging(void); -void qh_updatetested( facetT *facet1, facetT *facet2); -setT *qh_vertexridges(vertexT *vertex); +void qh_undo_newfacets(void); +void qh_updatetested(facetT *facet1, facetT *facet2); +setT *qh_vertexridges(vertexT *vertex, boolT allneighbors); void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges); void qh_willdelete(facetT *facet, facetT *replace); diff --git a/3rdparty/qhull/poly.c b/3rdparty/qhull/poly.c index b8db6a9ef..6fe15b8ae 100644 --- a/3rdparty/qhull/poly.c +++ b/3rdparty/qhull/poly.c @@ -9,9 +9,9 @@ infrequent code is in poly2.c (all but top 50 and their callers 12/3/95) - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/poly.c#3 $$Change: 2064 $ - $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/poly.c#7 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #include "qhull_a.h" @@ -38,8 +38,11 @@ void qh_appendfacet(facetT *facet) { facetT *tail= qh facet_tail; - if (tail == qh newfacet_list) + if (tail == qh newfacet_list) { qh newfacet_list= facet; + if (tail == qh visible_list) /* visible_list is at or before newfacet_list */ + qh visible_list= facet; + } if (tail == qh facet_next) qh facet_next= facet; facet->previous= tail->previous; @@ -61,7 +64,7 @@ void qh_appendfacet(facetT *facet) { appends vertex to end of qh.vertex_list, returns: - sets vertex->newlist + sets vertex->newfacet updates qh.vertex_list, newvertex_list increments qh.num_vertices @@ -74,7 +77,7 @@ void qh_appendvertex(vertexT *vertex) { if (tail == qh newvertex_list) qh newvertex_list= vertex; - vertex->newlist= True; + vertex->newfacet= True; vertex->previous= tail->previous; vertex->next= tail; if (tail->previous) @@ -83,7 +86,7 @@ void qh_appendvertex(vertexT *vertex) { qh vertex_list= vertex; tail->previous= vertex; qh num_vertices++; - trace4((qh ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id)); + trace4((qh ferr, 4045, "qh_appendvertex: append v%d to qh.newvertex_list and set v.newfacet\n", vertex->id)); } /* appendvertex */ @@ -93,9 +96,9 @@ void qh_appendvertex(vertexT *vertex) { qh_attachnewfacets( ) attach horizon facets to new facets in qh.newfacet_list newfacets have neighbor and ridge links to horizon but not vice versa - only needed for qh.ONLYgood returns: + clears qh.NEWtentative set qh.NEWfacets horizon facets linked to new facets ridges changed from visible facets to new facets @@ -103,6 +106,10 @@ void qh_appendvertex(vertexT *vertex) { qh.visible_list, no ridges valid facet->f.replace is a newfacet (if any) + notes: + used for qh.NEWtentative, otherwise see qh_makenew_nonsimplicial and qh_makenew_simplicial + qh_delridge_merge not needed (as tested by qh_checkdelridge) + design: delete interior ridges and neighbor sets by for each visible, non-simplicial facet @@ -118,7 +125,7 @@ void qh_appendvertex(vertexT *vertex) { 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 + else it is non-simplicial for all visible neighbors of the horizon facet link visible neighbor to new facet delete visible neighbor from horizon facet @@ -126,12 +133,14 @@ void qh_appendvertex(vertexT *vertex) { the first ridge of the new facet is the horizon ridge link the new facet into the horizon ridge */ -void qh_attachnewfacets(void /* qh.visible_list, newfacet_list */) { +void qh_attachnewfacets(void /* qh.visible_list, qh.newfacet_list */) { facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible; ridgeT *ridge, **ridgep; - qh NEWfacets= True; trace3((qh ferr, 3012, "qh_attachnewfacets: delete interior ridges\n")); + if (qh CHECKfrequently) { + qh_checkdelridge(); + } qh visit_id++; FORALLvisible_facets { visible->visitid= qh visit_id; @@ -142,13 +151,10 @@ void qh_attachnewfacets(void /* qh.visible_list, newfacet_list */) { || (!neighbor->visible && neighbor->simplicial)) { if (!neighbor->visible) /* delete ridge for simplicial horizon */ qh_setdel(neighbor->ridges, ridge); - qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */ - qh_memfree(ridge, (int)sizeof(ridgeT)); + qh_delridge(ridge); /* delete on second visit */ } } - SETfirst_(visible->ridges)= NULL; } - SETfirst_(visible->neighbors)= NULL; } trace1((qh ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n")); FORALLnew_facets { @@ -171,7 +177,7 @@ void qh_attachnewfacets(void /* qh.visible_list, newfacet_list */) { visible->f.replace= newfacet; qh_setreplace(horizon->neighbors, visible, newfacet); }else { - qh_fprintf(qh ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n", + qh_fprintf(qh ferr, 6102, "qhull internal error (qh_attachnewfacets): could not find visible facet for horizon f%d of newfacet f%d\n", horizon->id, newfacet->id); qh_errexit2(qh_ERRqhull, horizon, newfacet); } @@ -179,19 +185,29 @@ void qh_attachnewfacets(void /* qh.visible_list, newfacet_list */) { FOREACHneighbor_(horizon) { /* may hold for many new facets */ if (neighbor->visible) { neighbor->f.replace= newfacet; - qh_setdelnth(horizon->neighbors, - SETindex_(horizon->neighbors, neighbor)); + qh_setdelnth(horizon->neighbors, SETindex_(horizon->neighbors, neighbor)); neighborp--; /* repeat */ } } qh_setappend(&horizon->neighbors, newfacet); ridge= SETfirstt_(newfacet->ridges, ridgeT); - if (ridge->top == horizon) + if (ridge->top == horizon) { ridge->bottom= newfacet; - else + ridge->simplicialbot= True; + }else { ridge->top= newfacet; + ridge->simplicialtop= True; } + } } /* newfacets */ + trace4((qh ferr, 4094, "qh_attachnewfacets: clear f.ridges and f.neighbors for visible facets, may become invalid before qh_deletevisible\n")); + FORALLvisible_facets { + if (visible->ridges) + SETfirst_(visible->ridges)= NULL; + SETfirst_(visible->neighbors)= NULL; + } + qh NEWtentative= False; + qh NEWfacets= True; if (qh PRINTstatistics) { FORALLvisible_facets { if (!visible->f.replace) @@ -207,13 +223,16 @@ void qh_attachnewfacets(void /* qh.visible_list, newfacet_list */) { checks facet orientation to interior point if allerror set, - tests against qh.DISTround + tests against -qh.DISTround else - tests against 0 since tested against DISTround before + tests against 0.0 since tested against -qh.DISTround before returns: False if it flipped orientation (sets facet->flipped) distance if non-NULL + + notes: + called by qh_setfacetplane, qh_initialhull, and qh_checkflipped_all */ boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) { realT dist; @@ -224,12 +243,14 @@ boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) { qh_distplane(qh interior_point, facet, &dist); if (distp) *distp= dist; - if ((allerror && dist > -qh DISTround)|| (!allerror && dist >= 0.0)) { + if ((allerror && dist >= -qh DISTround) || (!allerror && dist > 0.0)) { facet->flipped= True; - zzinc_(Zflippedfacets); - trace0((qh ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n", - facet->id, dist, qh furthest_id)); - qh_precision("flipped facet"); + trace0((qh ferr, 19, "qh_checkflipped: facet f%d flipped, allerror? %d, distance= %6.12g during p%d\n", + facet->id, allerror, dist, qh furthest_id)); + if (qh num_facets > qh hull_dim+1) { /* qh_initialhull reverses orientation if !qh_checkflipped */ + zzinc_(Zflippedfacets); + qh_joggle_restart("flipped facet"); + } return False; } return True; @@ -242,12 +263,19 @@ boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) { removes facet from facet_list and frees up its memory notes: - assumes vertices and ridges already freed + assumes vertices and ridges already freed or referenced elsewhere */ void qh_delfacet(facetT *facet) { void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ - trace4((qh ferr, 4046, "qh_delfacet: delete f%d\n", facet->id)); + trace3((qh ferr, 3057, "qh_delfacet: delete f%d\n", facet->id)); + if (qh CHECKfrequently || qh VERIFYoutput) { + if (!qh NOerrexit) { + qh_checkdelfacet(facet, qh facet_mergeset); + qh_checkdelfacet(facet, qh degen_mergeset); + qh_checkdelfacet(facet, qh vertex_mergeset); + } + } if (facet == qh tracefacet) qh tracefacet= NULL; if (facet == qh GOODclosest) @@ -281,26 +309,29 @@ void qh_delfacet(facetT *facet) { returns: deletes each facet and removes from facetlist + deletes vertices on qh.del_vertices and ridges in qh.del_ridges at exit, qh.visible_list empty (== qh.newfacet_list) notes: - ridges already deleted + called by qh_all_vertexmerges, qh_addpoint, and qh_qhull + ridges already deleted or moved elsewhere + deleted vertices on qh.del_vertices horizon facets do not reference facets on qh.visible_list new facets in qh.newfacet_list uses qh.visit_id; */ -void qh_deletevisible(void /*qh.visible_list*/) { +void qh_deletevisible(void /* qh.visible_list */) { facetT *visible, *nextfacet; vertexT *vertex, **vertexp; int numvisible= 0, numdel= qh_setsize(qh del_vertices); trace1((qh ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n", qh num_visible, numdel)); - for (visible= qh visible_list; visible && visible->visible; + for (visible=qh visible_list; visible && visible->visible; visible= nextfacet) { /* deleting current */ nextfacet= visible->next; numvisible++; - qh_delfacet(visible); + qh_delfacet(visible); /* f.ridges deleted or moved elsewhere, deleted f.vertices on qh.del_vertices */ } if (numvisible != qh num_visible) { qh_fprintf(qh ferr, 6103, "qhull internal error (qh_deletevisible): qh num_visible %d is not number of visible facets %d\n", @@ -378,7 +409,7 @@ setT *qh_facetintersect(facetT *facetA, facetT *facetB, } } if (i >= dim || j >= dim) { - qh_fprintf(qh ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n", + qh_fprintf(qh ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in other's neighbors\n", facetA->id, facetB->id); qh_errexit2(qh_ERRqhull, facetA, facetB); } @@ -404,8 +435,8 @@ setT *qh_facetintersect(facetT *facetA, facetT *facetB, */ int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem) { void **elemp= SETelemaddr_(set, firstindex, void); - ptr_intT hash = 0, elem; - unsigned result; + ptr_intT hash= 0, elem; + unsigned int uresult; int i; #ifdef _MSC_VER /* Microsoft Visual C++ -- warn about 64-bit issues */ #pragma warning( push) /* WARN64 -- ptr_intT holds a 64-bit pointer */ @@ -453,15 +484,36 @@ int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem qh_fprintf(qh ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize); qh_errexit2(qh_ERRqhull, NULL, NULL); } - result= (unsigned)hash; - result %= (unsigned)hashsize; + uresult= (unsigned int)hash; + uresult %= (unsigned int)hashsize; /* result= 0; for debugging */ - return result; + return (int)uresult; #ifdef _MSC_VER #pragma warning( pop) #endif } /* gethash */ +/*--------------------------------- + + qh_getreplacement( visible ) + get replacement for visible facet + + returns: + valid facet from visible.replace (may be chained) +*/ +facetT *qh_getreplacement(facetT *visible) { + unsigned int count= 0; + + facetT *result= visible; + while (result && result->visible) { + result= result->f.replace; + if (count++ > qh facet_id) + qh_infiniteloop(visible); + } + return result; +} + /*--------------------------------- @@ -476,19 +528,20 @@ int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem newfacet->neighbor= horizon, but not vice versa newvertex_list updated with vertices */ -facetT *qh_makenewfacet(setT *vertices, boolT toporient,facetT *horizon) { +facetT *qh_makenewfacet(setT *vertices, boolT toporient, facetT *horizon) { facetT *newfacet; vertexT *vertex, **vertexp; FOREACHvertex_(vertices) { - if (!vertex->newlist) { + if (!vertex->newfacet) { qh_removevertex(vertex); qh_appendvertex(vertex); } } newfacet= qh_newfacet(); newfacet->vertices= vertices; - newfacet->toporient= (unsigned char)toporient; + if (toporient) + newfacet->toporient= True; if (horizon) qh_setappend(&(newfacet->neighbors), horizon); qh_appendfacet(newfacet); @@ -510,17 +563,20 @@ facetT *qh_makenewfacet(setT *vertices, boolT toporient,facetT *horizon) { notes: facet->f.samecycle is defined for facet->mergehorizon facets */ -void qh_makenewplanes(void /* newfacet_list */) { +void qh_makenewplanes(void /* qh.newfacet_list */) { facetT *newfacet; + trace4((qh ferr, 4074, "qh_makenewplanes: make new hyperplanes for facets on qh.newfacet_list f%d\n", + qh newfacet_list->id)); FORALLnew_facets { if (!newfacet->mergehorizon) - qh_setfacetplane(newfacet); + qh_setfacetplane(newfacet); /* updates Wnewvertexmax */ } if (qh JOGGLEmax < REALmax/2) minimize_(qh min_vertex, -wwval_(Wnewvertexmax)); } /* makenewplanes */ +#ifndef qh_NOmerge /*--------------------------------- @@ -529,18 +585,21 @@ void qh_makenewplanes(void /* newfacet_list */) { returns: first newfacet, bumps numnew as needed - attaches new facets if !qh.ONLYgood + attaches new facets if !qh NEWtentative marks ridge neighbors for simplicial visible - if (qh.ONLYgood) + if (qh.NEWtentative) ridges on newfacet, horizon, and visible else - ridge and neighbors between newfacet and horizon + ridge and neighbors between newfacet and horizon visible facet's ridges are deleted + visible facet's f.neighbors is empty notes: + called by qh_makenewfacets and qh_triangulatefacet qh.visit_id if visible has already been processed sets neighbor->seen for building f.samecycle assumes all 'seen' flags initially false + qh_delridge_merge not needed (as tested by qh_checkdelridge in qh_makenewfacets) design: for each ridge of visible facet @@ -559,21 +618,22 @@ void qh_makenewplanes(void /* newfacet_list */) { (deletes ridge if neighbor is simplicial) */ -#ifndef qh_NOmerge facetT *qh_makenew_nonsimplicial(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; + unsigned int ridgeid; FOREACHridge_(visible->ridges) { ridgeid= ridge->id; neighbor= otherfacet_(ridge, visible); if (neighbor->visible) { - if (!qh ONLYgood) { + if (!qh NEWtentative) { if (neighbor->visitid == qh visit_id) { + if (qh traceridge == ridge) + qh traceridge= NULL; qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); } @@ -585,7 +645,7 @@ facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { qh_setappend_set(&vertices, ridge->vertices); newfacet= qh_makenewfacet(vertices, toporient, neighbor); (*numnew)++; - if (neighbor->coplanar) { + if (neighbor->coplanarhorizon) { newfacet->mergehorizon= True; if (!neighbor->seen) { newfacet->f.samecycle= newfacet; @@ -596,7 +656,7 @@ facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { samecycle->f.samecycle= newfacet; } } - if (qh ONLYgood) { + if (qh NEWtentative) { if (!neighbor->simplicial) qh_setappend(&(newfacet->ridges), ridge); }else { /* qh_attachnewfacets */ @@ -611,27 +671,32 @@ facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { qh_setreplace(neighbor->neighbors, visible, newfacet); if (neighbor->simplicial) { qh_setdel(neighbor->ridges, ridge); - qh_setfree(&(ridge->vertices)); - qh_memfree(ridge, (int)sizeof(ridgeT)); + qh_delridge(ridge); }else { qh_setappend(&(newfacet->ridges), ridge); - if (toporient) + if (toporient) { ridge->top= newfacet; - else + ridge->simplicialtop= True; + }else { ridge->bottom= newfacet; + ridge->simplicialbot= True; + } } - trace4((qh ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n", - newfacet->id, apex->id, ridgeid, neighbor->id)); } + trace4((qh ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n", + newfacet->id, apex->id, ridgeid, neighbor->id)); } neighbor->seen= True; } /* for each ridge */ - if (!qh ONLYgood) - SETfirst_(visible->ridges)= NULL; return newfacet; } /* makenew_nonsimplicial */ + #else /* qh_NOmerge */ facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { + QHULL_UNUSED(visible) + QHULL_UNUSED(apex) + QHULL_UNUSED(numnew) + return NULL; } #endif /* qh_NOmerge */ @@ -643,7 +708,7 @@ facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { make new facets for simplicial visible facet and apex returns: - attaches new facets if (!qh.ONLYgood) + attaches new facets if !qh.NEWtentative neighbors between newfacet and horizon notes: @@ -674,13 +739,13 @@ facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) { toporient= (horizonskip & 0x1) ^ 0x1; newfacet= qh_makenewfacet(vertices, toporient, neighbor); (*numnew)++; - if (neighbor->coplanar && (qh PREmerge || qh MERGEexact)) { + if (neighbor->coplanarhorizon && (qh PREmerge || qh MERGEexact)) { #ifndef qh_NOmerge newfacet->f.samecycle= newfacet; newfacet->mergehorizon= True; #endif } - if (!qh ONLYgood) + if (!qh NEWtentative) SETelem_(neighbor->neighbors, horizonskip)= newfacet; trace4((qh ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n", newfacet->id, toporient, apex->id, neighbor->id, horizonskip, @@ -697,15 +762,16 @@ facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) { either match subridge of newfacet with neighbor or add to hash_table returns: - duplicate ridges are unmatched and marked by qh_DUPLICATEridge + matched ridges of newfacet, except for duplicate ridges + duplicate ridges marked by qh_DUPLICATEridge for qh_matchdupridge notes: + called by qh_matchnewfacets + assumes newfacet is simplicial 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 + see qh_matchdupridge (poly2.c) design: for each possible matching facet in qh.hash_table @@ -714,12 +780,39 @@ facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) { 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 + note: dupridge detected when a match 'f&d skip %d' has already been seen + need to mark all of the dupridges for qh_matchdupridge + indicate a duplicate ridge by qh_DUPLICATEridge and f.dupridge 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 + + state at "indicate a duplicate ridge": + newfacet@newskip= the argument + facet= the hashed facet@skip that has the same vertices as newfacet@newskip + same= true if matched vertices have the same orientation + matchfacet= neighbor at facet@skip + matchfacet=qh_DUPLICATEridge, matchfacet was previously detected as a dupridge of facet@skip + ismatch if 'vertex orientation (same) matches facet/newfacet orientation (toporient) + unknown facet will match later + + details at "indicate a duplicate ridge": + if !ismatch and matchfacet, + dupridge is between hashed facet@skip/matchfacet@matchskip and arg newfacet@newskip/unknown + set newfacet@newskip, facet@skip, and matchfacet@matchskip to qh_DUPLICATEridge + add newfacet and matchfacet to hash_table + if ismatch and matchfacet, + same as !ismatch and matchfacet -- it matches facet instead of matchfacet + if !ismatch and !matchfacet + dupridge between hashed facet@skip/unknown and arg newfacet@newskip/unknown + set newfacet@newskip and facet@skip to qh_DUPLICATEridge + add newfacet to hash_table + if ismatch and matchfacet==qh_DUPLICATEridge + dupridge with already duplicated hashed facet@skip and arg newfacet@newskip/unknown + set newfacet@newskip to qh_DUPLICATEridge + add newfacet to hash_table + facet's hyperplane already set */ void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcount) { boolT newfound= False; /* True, if new facet is already in hash chain */ @@ -733,7 +826,7 @@ void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcoun trace4((qh ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n", newfacet->id, newskip, hash, *hashcount)); zinc_(Zhashlookup); - for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT)); + for (scan=hash; (facet= SETelemt_(qh hash_table, scan, facetT)); scan= (++scan >= hashsize ? 0 : scan)) { if (facet == newfacet) { newfound= True; @@ -741,12 +834,12 @@ void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcoun } zinc_(Zhashtests); if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { - if (SETelem_(newfacet->vertices, newskip) == - SETelem_(facet->vertices, skip)) { - qh_precision("two facets with the same vertices"); - qh_fprintf(qh ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d. Can not force output.\n", - facet->id, newfacet->id); - qh_errexit2(qh_ERRprec, facet, newfacet); + if (SETelem_(newfacet->vertices, newskip) == SETelem_(facet->vertices, skip)) { + qh_joggle_restart("two new facets with the same vertices"); + /* duplicated for multiple skips, not easily avoided */ + qh_fprintf(qh ferr, 7084, "qhull topology warning (qh_matchneighbor): will merge vertices to undo new facets -- f%d and f%d have the same vertices (skip %d, skip %d) and same horizon ridges to f%d and f%d\n", + facet->id, newfacet->id, skip, newskip, SETfirstt_(facet->neighbors, facetT)->id, SETfirstt_(newfacet->neighbors, facetT)->id); + /* will rename a vertex (QH3053). The fault was duplicate ridges (same vertices) in different facets due to a previous rename. Expensive to detect beforehand */ } ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient))); matchfacet= SETelemt_(facet->neighbors, skip, facetT); @@ -759,35 +852,27 @@ void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcoun return; } if (!qh PREmerge && !qh MERGEexact) { - qh_precision("a ridge with more than two neighbors"); - qh_fprintf(qh ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue.\n", + qh_joggle_restart("a ridge with more than two neighbors"); + qh_fprintf(qh ferr, 6107, "qhull topology error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue due to no qh.PREmerge and no 'Qx' (MERGEexact)\n", facet->id, newfacet->id, getid_(matchfacet)); - qh_errexit2(qh_ERRprec, facet, newfacet); + qh_errexit2(qh_ERRtopology, facet, newfacet); } SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge; newfacet->dupridge= True; - if (!newfacet->normal) - qh_setfacetplane(newfacet); qh_addhash(newfacet, qh hash_table, hashsize, hash); (*hashcount)++; - if (!facet->normal) - qh_setfacetplane(facet); if (matchfacet != qh_DUPLICATEridge) { SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge; facet->dupridge= True; - if (!facet->normal) - qh_setfacetplane(facet); if (matchfacet) { matchskip= qh_setindex(matchfacet->neighbors, facet); if (matchskip<0) { - qh_fprintf(qh ferr, 6260, "qhull internal error (qh_matchneighbor): matchfacet f%d is in f%d neighbors but not vice versa. Can not continue.\n", + qh_fprintf(qh ferr, 6260, "qhull topology 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_ERRqhull, matchfacet, facet); + qh_errexit2(qh_ERRtopology, matchfacet, facet); } SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */ matchfacet->dupridge= True; - if (!matchfacet->normal) - qh_setfacetplane(matchfacet); qh_addhash(matchfacet, qh hash_table, hashsize, hash); *hashcount += 2; } @@ -810,41 +895,52 @@ void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcoun /*--------------------------------- - qh_matchnewfacets() - match newfacets in qh.newfacet_list to their newfacet neighbors + qh_matchnewfacets( ) + match new facets in qh.newfacet_list to their newfacet neighbors + all facets are simplicial 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 + if dupridges and merging + returns maxdupdist (>=0.0) from vertex to opposite facet sets facet->dupridge - missing neighbor links identifies extra ridges to be merging (qh_MERGEridge) + missing neighbor links identify dupridges to be merged (qh_DUPLICATEridge) + else + qh.newfacet_list with full neighbor sets + vertices for the nth neighbor match all but the nth vertex + if not merging and qh.FORCEoutput + for facets with normals (i.e., with dupridges) + sets facet->flippped for flipped normals, also prevents point partitioning notes: - newfacets already have neighbor[0] (horizon facet) + called by qh_buildcone* and qh_triangulate_facet + neighbor[0] of new facets is the horizon facet + if NEWtentative, new facets not attached to the horizon 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 + truncate neighbor sets to horizon facet 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 + try to match facets with the same coplanar horizon + if not all matched + for each new facet with a duplicate ridge + match it with a coplanar facet, or identify a pinched vertex + if not merging and qh.FORCEoutput + check for flipped facets */ -void qh_matchnewfacets(void /* qh.newfacet_list */) { +coordT qh_matchnewfacets(void /* qh.newfacet_list */) { int numnew=0, hashcount=0, newskip; facetT *newfacet, *neighbor; + coordT maxdupdist= 0.0, maxdist2; int dim= qh hull_dim, hashsize, neighbor_i, neighbor_n; setT *neighbors; #ifndef qh_NOtrace - int facet_i, facet_n, numfree= 0; + int facet_i, facet_n, numunused= 0; facetT *facet; #endif @@ -854,7 +950,7 @@ void qh_matchnewfacets(void /* qh.newfacet_list */) { { /* inline qh_setzero(newfacet->neighbors, 1, qh hull_dim); */ neighbors= newfacet->neighbors; neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/ - memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize); + memset((char *)SETelemaddr_(neighbors, 1, void), 0, (size_t)(dim * SETelemsize)); } } @@ -862,6 +958,11 @@ void qh_matchnewfacets(void /* qh.newfacet_list */) { but every ridge could be DUPLICATEridge */ hashsize= qh_setsize(qh hash_table); FORALLnew_facets { + if (!newfacet->simplicial) { + qh_fprintf(qh ferr, 6377, "qhull internal error (qh_matchnewfacets): expecting simplicial facets on qh.newfacet_list f%d for qh_matchneighbors, qh_matchneighbor, and qh_matchdupridge. Got non-simplicial f%d\n", + qh newfacet_list->id, newfacet->id); + qh_errexit2(qh_ERRqhull, newfacet, qh newfacet_list); + } for (newskip=1; newskip0 because hull_dim>1 and numnew>0 */ qh_matchneighbor(newfacet, newskip, hashsize, &hashcount); @@ -881,20 +982,23 @@ void qh_matchnewfacets(void /* qh.newfacet_list */) { break; } if (count != hashcount) { - qh_fprintf(qh ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n", + qh_fprintf(qh ferr, 6266, "qhull error (qh_matchnewfacets): after adding facet %d, hashcount %d != count %d\n", newfacet->id, hashcount, count); - qh_errexit(qh_ERRqhull, newfacet, NULL); + qh_errexit(qh_ERRdebug, newfacet, NULL); } } #endif /* end of trap code */ - } - if (hashcount) { - FORALLnew_facets { - if (newfacet->dupridge) { - FOREACHneighbor_i_(newfacet) { - if (neighbor == qh_DUPLICATEridge) { - qh_matchduplicates(newfacet, neighbor_i, hashsize, &hashcount); - /* this may report MERGEfacet */ + } /* end FORALLnew_facets */ + if (hashcount) { /* all neighbors matched, except for qh_DUPLICATEridge neighbors */ + qh_joggle_restart("ridge with multiple neighbors"); + if (hashcount) { + FORALLnew_facets { + if (newfacet->dupridge) { + FOREACHneighbor_i_(newfacet) { + if (neighbor == qh_DUPLICATEridge) { + maxdist2= qh_matchdupridge(newfacet, neighbor_i, hashsize, &hashcount); + maximize_(maxdupdist, maxdist2); + } } } } @@ -907,25 +1011,21 @@ void qh_matchnewfacets(void /* qh.newfacet_list */) { qh_errexit(qh_ERRqhull, NULL, NULL); } #ifndef qh_NOtrace - if (qh IStracing >= 2) { + if (qh IStracing >= 3) { FOREACHfacet_i_(qh hash_table) { if (!facet) - numfree++; + numunused++; } - qh_fprintf(qh ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries . hashsize %d\n", - numnew, numfree, qh_setsize(qh hash_table)); + qh_fprintf(qh ferr, 3063, "qh_matchnewfacets: maxdupdist %2.2g, new facets %d, unused hash entries %d, hashsize %d\n", + maxdupdist, numnew, numunused, qh_setsize(qh hash_table)); } #endif /* !qh_NOtrace */ qh_setfree(&qh hash_table); if (qh PREmerge || qh MERGEexact) { if (qh IStracing >= 4) qh_printfacetlist(qh newfacet_list, NULL, qh_ALL); - FORALLnew_facets { - if (newfacet->normal) - qh_checkflipped(newfacet, NULL, qh_ALL); - } - }else if (qh FORCEoutput) - qh_checkflipped_all(qh newfacet_list); /* prints warnings for flipped */ + } + return maxdupdist; } /* matchnewfacets */ @@ -938,10 +1038,11 @@ void qh_matchnewfacets(void /* qh.newfacet_list */) { returns: true if matched vertices - skip index for each set + skip index for skipB sets same iff vertices have the same orientation notes: + called by qh_matchneighbor and qh_matchdupridge assumes skipA is in A and both sets are the same size design: @@ -965,7 +1066,8 @@ boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA, }while (*(++elemAp)); if (!skipBp) skipBp= ++elemBp; - *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */ + *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB + verticesA and verticesB are the same size, otherwise trace4 may segfault */ *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */ trace4((qh ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n", skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same)); @@ -975,7 +1077,7 @@ boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA, /*--------------------------------- - qh_newfacet() + qh_newfacet( ) return a new facet returns: @@ -999,7 +1101,7 @@ facetT *qh_newfacet(void) { if (qh FORCEoutput && qh APPROXhull) facet->maxoutside= qh MINoutside; else - facet->maxoutside= qh DISTround; + facet->maxoutside= qh DISTround; /* same value as test for QH7082 */ #endif facet->simplicial= True; facet->good= True; @@ -1014,6 +1116,8 @@ facetT *qh_newfacet(void) { qh_newridge() return a new ridge + notes: + caller sets qh.traceridge */ ridgeT *qh_newridge(void) { ridgeT *ridge; @@ -1023,8 +1127,7 @@ ridgeT *qh_newridge(void) { memset((char *)ridge, (size_t)0, sizeof(ridgeT)); zinc_(Ztotridges); if (qh ridge_id == UINT_MAX) { - qh_fprintf(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"); + qh_fprintf(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 ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id)); @@ -1035,7 +1138,7 @@ qhull warning: more than 2^32 ridges. Qhull results are OK. Since the ridge ID /*--------------------------------- - qh_pointid( point ) + qh_pointid( point ) return id for a point, returns qh_IDnone(-3) if null, qh_IDinterior(-2) if interior, or qh_IDunknown(-1) if not known @@ -1082,7 +1185,7 @@ int qh_pointid(pointT *point) { qh_appendfacet */ void qh_removefacet(facetT *facet) { - facetT *next= facet->next, *previous= facet->previous; + facetT *next= facet->next, *previous= facet->previous; /* next is always defined */ if (facet == qh newfacet_list) qh newfacet_list= next; @@ -1098,7 +1201,7 @@ void qh_removefacet(facetT *facet) { qh facet_list->previous= NULL; } qh num_facets--; - trace4((qh ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id)); + trace4((qh ferr, 4057, "qh_removefacet: removed f%d from facet_list, newfacet_list, and visible_list\n", facet->id)); } /* removefacet */ @@ -1113,65 +1216,101 @@ void qh_removefacet(facetT *facet) { decrements qh.num_vertices */ void qh_removevertex(vertexT *vertex) { - vertexT *next= vertex->next, *previous= vertex->previous; + vertexT *next= vertex->next, *previous= vertex->previous; /* next is always defined */ + trace4((qh ferr, 4058, "qh_removevertex: remove v%d from qh.vertex_list\n", vertex->id)); 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= next; qh vertex_list->previous= NULL; } qh num_vertices--; - trace4((qh ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id)); } /* removevertex */ /*--------------------------------- + >-------------------------------- - qh_updatevertices() + qh_update_vertexneighbors( ) update vertex neighbors and delete interior vertices returns: - if qh.VERTEXneighbors, updates neighbors for each vertex + if qh.VERTEXneighbors, if qh.newvertex_list, - removes visible neighbors from vertex neighbors + 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 + if qh.visible_list + interior vertices added to qh.del_vertices for later partitioning as coplanar points + if not qh.VERTEXneighbors (not merging) + interior vertices of visible facets added to qh.del_vertices for later partitioning as coplanar points + + notes + [jan'19] split off qh_update_vertexneighbors_cone. Optimize the remaining cases in a future release + called by qh_triangulate_facet after triangulating a non-simplicial facet, followed by reset_lists + called by qh_triangulate after triangulating null and mirror facets + called by qh_all_vertexmerges after calling qh_merge_pinchedvertices 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 + for each vertex on newvertex_list (i.e., new vertices and vertices of new facets) + delete visible facets from vertex neighbors + for each new facet on newfacet_list + for each vertex of facet + append facet to vertex neighbors + for each visible facet on qh.visible_list + for each vertex of facet + if the vertex is not on a new facet and not itself deleted + if the vertex has a not-visible neighbor (due to merging) + remove the visible facet from the vertex's neighbors + otherwise + add the vertex to qh.del_vertices for later deletion + + if not qh.VERTEXneighbors (not merging) + for each vertex of a visible facet + if the vertex is not on a new facet and not itself deleted + add the vertex to qh.del_vertices for later deletion */ -void qh_updatevertices(void /*qh.newvertex_list, newfacet_list, visible_list*/) { +void qh_update_vertexneighbors(void /* qh.newvertex_list, newfacet_list, visible_list */) { facetT *newfacet= NULL, *neighbor, **neighborp, *visible; vertexT *vertex, **vertexp; + int neighborcount= 0; - trace3((qh ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n")); if (qh VERTEXneighbors) { + trace3((qh ferr, 3013, "qh_update_vertexneighbors: update v.neighbors for qh.newvertex_list (v%d) and qh.newfacet_list (f%d)\n", + getid_(qh newvertex_list), getid_(qh newfacet_list))); FORALLvertex_(qh newvertex_list) { + neighborcount= 0; FOREACHneighbor_(vertex) { - if (neighbor->visible) + if (neighbor->visible) { + neighborcount++; SETref_(neighbor)= NULL; + } + } + if (neighborcount) { + trace4((qh ferr, 4046, "qh_update_vertexneighbors: delete %d of %d vertex neighbors for v%d. Removes to-be-deleted, visible facets\n", + neighborcount, qh_setsize(vertex->neighbors), vertex->id)); + qh_setcompact(vertex->neighbors); } - qh_setcompact(vertex->neighbors); } FORALLnew_facets { - FOREACHvertex_(newfacet->vertices) - qh_setappend(&vertex->neighbors, newfacet); + if (qh first_newfacet && newfacet->id >= qh first_newfacet) { + FOREACHvertex_(newfacet->vertices) + qh_setappend(&vertex->neighbors, newfacet); + }else { /* called after qh_merge_pinchedvertices. In 7-D, many more neighbors than new facets. qh_setin is expensive */ + FOREACHvertex_(newfacet->vertices) + qh_setunique(&vertex->neighbors, newfacet); + } } + trace3((qh ferr, 3058, "qh_update_vertexneighbors: delete interior vertices for qh.visible_list (f%d)\n", + getid_(qh visible_list))); FORALLvisible_facets { FOREACHvertex_(visible->vertices) { - if (!vertex->newlist && !vertex->deleted) { + if (!vertex->newfacet && !vertex->deleted) { FOREACHneighbor_(vertex) { /* this can happen under merging */ if (!neighbor->visible) break; @@ -1181,25 +1320,128 @@ void qh_updatevertices(void /*qh.newvertex_list, newfacet_list, visible_list*/) else { vertex->deleted= True; qh_setappend(&qh del_vertices, vertex); - trace2((qh ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n", + trace2((qh ferr, 2041, "qh_update_vertexneighbors: delete interior vertex p%d(v%d) of visible f%d\n", qh_pointid(vertex->point), vertex->id, visible->id)); } } } } }else { /* !VERTEXneighbors */ + trace3((qh ferr, 3058, "qh_update_vertexneighbors: delete old vertices for qh.visible_list (f%d)\n", + getid_(qh visible_list))); FORALLvisible_facets { FOREACHvertex_(visible->vertices) { - if (!vertex->newlist && !vertex->deleted) { + if (!vertex->newfacet && !vertex->deleted) { vertex->deleted= True; qh_setappend(&qh del_vertices, vertex); - trace2((qh ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n", + trace2((qh ferr, 2042, "qh_update_vertexneighbors: will delete interior vertex p%d(v%d) of visible f%d\n", qh_pointid(vertex->point), vertex->id, visible->id)); } } } } -} /* updatevertices */ +} /* update_vertexneighbors */ + +/*--------------------------------- + + qh_update_vertexneighbors_cone( ) + update vertex neighbors for a cone of new facets and delete interior vertices + + returns: + if qh.VERTEXneighbors, + 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 as coplanar points + if not qh.VERTEXneighbors (not merging) + interior vertices of visible facets added to qh.del_vertices for later partitioning as coplanar points + + notes + called by qh_addpoint after create cone and before premerge + + design: + if qh.VERTEXneighbors + for each vertex on newvertex_list (i.e., new vertices and vertices of new facets) + delete visible facets from vertex neighbors + for each new facet on newfacet_list + for each vertex of facet + append facet to vertex neighbors + for each visible facet on qh.visible_list + for each vertex of facet + if the vertex is not on a new facet and not itself deleted + if the vertex has a not-visible neighbor (due to merging) + remove the visible facet from the vertex's neighbors + otherwise + add the vertex to qh.del_vertices for later deletion + + if not qh.VERTEXneighbors (not merging) + for each vertex of a visible facet + if the vertex is not on a new facet and not itself deleted + add the vertex to qh.del_vertices for later deletion +*/ +void qh_update_vertexneighbors_cone(void /* qh.newvertex_list, newfacet_list, visible_list */) { + facetT *newfacet= NULL, *neighbor, **neighborp, *visible; + vertexT *vertex, **vertexp; + int delcount= 0; + if (qh VERTEXneighbors) { + trace3((qh ferr, 3059, "qh_update_vertexneighbors_cone: update v.neighbors for qh.newvertex_list (v%d) and qh.newfacet_list (f%d)\n", + getid_(qh newvertex_list), getid_(qh newfacet_list))); + FORALLvertex_(qh newvertex_list) { + delcount= 0; + FOREACHneighbor_(vertex) { + if (neighbor->visible) { /* alternative design is a loop over visible facets, but needs qh_setdel() */ + delcount++; + qh_setdelnth(vertex->neighbors, SETindex_(vertex->neighbors, neighbor)); + neighborp--; /* repeat */ + } + } + if (delcount) { + trace4((qh ferr, 4021, "qh_update_vertexneighbors_cone: deleted %d visible vertexneighbors of v%d\n", + delcount, vertex->id)); + } + } + FORALLnew_facets { + FOREACHvertex_(newfacet->vertices) + qh_setappend(&vertex->neighbors, newfacet); + } + trace3((qh ferr, 3065, "qh_update_vertexneighbors_cone: delete interior vertices, if any, for qh.visible_list (f%d)\n", + getid_(qh visible_list))); + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newfacet && !vertex->deleted) { + FOREACHneighbor_(vertex) { /* this can happen under merging, qh_checkfacet QH4025 */ + if (!neighbor->visible) + break; + } + if (neighbor) + qh_setdel(vertex->neighbors, visible); + else { + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + trace2((qh ferr, 2102, "qh_update_vertexneighbors_cone: will delete interior vertex p%d(v%d) of visible f%d\n", + qh_pointid(vertex->point), vertex->id, visible->id)); + } + } + } + } + }else { /* !VERTEXneighbors */ + trace3((qh ferr, 3066, "qh_update_vertexneighbors_cone: delete interior vertices for qh.visible_list (f%d)\n", + getid_(qh visible_list))); + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newfacet && !vertex->deleted) { + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + trace2((qh ferr, 2059, "qh_update_vertexneighbors_cone: will delete interior vertex p%d(v%d) of visible f%d\n", + qh_pointid(vertex->point), vertex->id, visible->id)); + } + } + } + } +} /* update_vertexneighbors_cone */ diff --git a/3rdparty/qhull/poly.h b/3rdparty/qhull/poly.h index af8b42077..74d66532a 100644 --- a/3rdparty/qhull/poly.h +++ b/3rdparty/qhull/poly.h @@ -6,9 +6,9 @@ see qh-poly.htm, libqhull.h and poly.c - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/poly.h#3 $$Change: 2047 $ - $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/poly.h#5 $$Change: 2963 $ + $DateTime: 2020/06/03 19:31:01 $$Author: bbarber $ */ #ifndef qhDEFpoly @@ -21,7 +21,7 @@ /*---------------------------------- - ALGORITHMfault + qh_ALGORITHMfault use as argument to checkconvex() to report errors during buildhull */ #define qh_ALGORITHMfault 0 @@ -29,7 +29,7 @@ /*---------------------------------- - DATAfault + qh_DATAfault use as argument to checkconvex() to report errors during initialhull */ #define qh_DATAfault 1 @@ -37,22 +37,23 @@ /*---------------------------------- - DUPLICATEridge + qh_DUPLICATEridge special value for facet->neighbor to indicate a duplicate ridge notes: - set by matchneighbor, used by matchmatch and mark_dupridge + set by qh_matchneighbor for qh_matchdupridge */ #define qh_DUPLICATEridge (facetT *)1L /*---------------------------------- - MERGEridge flag in facet - special value for facet->neighbor to indicate a merged ridge + qh_MERGEridge flag in facet + special value for facet->neighbor to indicate a duplicate ridge that needs merging notes: - set by matchneighbor, used by matchmatch and mark_dupridge + set by qh_matchnewfacets..qh_matchdupridge from qh_DUPLICATEridge + used by qh_mark_dupridges to set facet->mergeridge, facet->mergeridge2 from facet->dupridge */ #define qh_MERGEridge (facetT *)2L @@ -74,7 +75,7 @@ see: FORALLfacets */ -#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next ) +#define FORALLfacet_( facetlist ) if (facetlist) for ( facet=(facetlist); facet && facet->next; facet= facet->next ) /*---------------------------------- @@ -86,7 +87,7 @@ uses 'facetT *newfacet;' at exit, newfacet==NULL */ -#define FORALLnew_facets for ( newfacet=qh newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next ) +#define FORALLnew_facets for ( newfacet=qh newfacet_list; newfacet && newfacet->next; newfacet=newfacet->next ) /*---------------------------------- @@ -212,16 +213,17 @@ void qh_appendvertex(vertexT *vertex); void qh_attachnewfacets(void /* qh.visible_list, qh.newfacet_list */); boolT qh_checkflipped(facetT *facet, realT *dist, boolT allerror); void qh_delfacet(facetT *facet); -void qh_deletevisible(void /*qh.visible_list, qh.horizon_list*/); +void qh_deletevisible(void /* qh.visible_list, qh.horizon_list */); setT *qh_facetintersect(facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra); int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem); +facetT *qh_getreplacement(facetT *visible); facetT *qh_makenewfacet(setT *vertices, boolT toporient, facetT *facet); -void qh_makenewplanes(void /* newfacet_list */); +void qh_makenewplanes(void /* qh.newfacet_list */); facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew); facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew); void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcount); -void qh_matchnewfacets(void); +coordT qh_matchnewfacets(void); boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA, setT *verticesB, int *skipB, boolT *same); facetT *qh_newfacet(void); @@ -229,23 +231,25 @@ ridgeT *qh_newridge(void); int qh_pointid(pointT *point); void qh_removefacet(facetT *facet); void qh_removevertex(vertexT *vertex); -void qh_updatevertices(void); +void qh_update_vertexneighbors(void); +void qh_update_vertexneighbors_cone(void); /*========== -prototypes poly2.c in alphabetical order ===========*/ -void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash); +boolT qh_addfacetvertex(facetT *facet, vertexT *newvertex); +void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash); void qh_check_bestdist(void); -void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2); void qh_check_maxout(void); void qh_check_output(void); -void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2); +void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2, int *errcount); void qh_check_points(void); void qh_checkconvex(facetT *facetlist, int fault); void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp); void qh_checkflipped_all(facetT *facetlist); +boolT qh_checklists(facetT *facetlist); void qh_checkpolygon(facetT *facetlist); -void qh_checkvertex(vertexT *vertex); +void qh_checkvertex(vertexT *vertex, boolT allchecks, boolT *waserrorp); void qh_clearcenters(qh_CENTER type); void qh_createsimplex(setT *vertices); void qh_delridge(ridgeT *ridge); @@ -254,7 +258,7 @@ setT *qh_facet3vertex(facetT *facet); facetT *qh_findbestfacet(pointT *point, boolT bestoutside, realT *bestdist, boolT *isoutside); facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart); -facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, +facetT *qh_findfacet_all(pointT *point, boolT noupper, realT *bestdist, boolT *isoutside, int *numpart); int qh_findgood(facetT *facetlist, int goodhorizon); void qh_findgood_all(facetT *facetlist); @@ -265,32 +269,34 @@ void qh_initbuild(void); void qh_initialhull(setT *vertices); setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints); vertexT *qh_isvertex(pointT *point, setT *vertices); -vertexT *qh_makenewfacets(pointT *point /*horizon_list, visible_list*/); -void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount); +vertexT *qh_makenewfacets(pointT *point /* qh.horizon_list, visible_list */); +coordT qh_matchdupridge(facetT *atfacet, int atskip, int hashsize, int *hashcount); void qh_nearcoplanar(void /* qh.facet_list */); vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp); int qh_newhashtable(int newsize); vertexT *qh_newvertex(pointT *point); +facetT *qh_nextfacet2d(facetT *facet, vertexT **nextvertexp); ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp); -void qh_outcoplanar(void /* facet_list */); +vertexT *qh_opposite_vertex(facetT *facetA, facetT *neighbor); +void qh_outcoplanar(void /* qh.facet_list */); pointT *qh_point(int id); void qh_point_add(setT *set, pointT *point, void *elem); -setT *qh_pointfacet(void /*qh.facet_list*/); -setT *qh_pointvertex(void /*qh.facet_list*/); +setT *qh_pointfacet(void /* qh.facet_list */); +setT *qh_pointvertex(void /* qh.facet_list */); void qh_prependfacet(facetT *facet, facetT **facetlist); void qh_printhashtable(FILE *fp); void qh_printlists(void); -void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list qh.newfacet_list qh.visible_list*/); +void qh_replacefacetvertex(facetT *facet, vertexT *oldvertex, vertexT *newvertex); +void qh_resetlists(boolT stats, boolT resetVisible /* qh.newvertex_list qh.newfacet_list qh.visible_list */); void qh_setvoronoi_all(void); -void qh_triangulate(void /*qh.facet_list*/); +void qh_triangulate(void /* qh.facet_list */); void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex); void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB); void qh_triangulate_mirror(facetT *facetA, facetT *facetB); void qh_triangulate_null(facetT *facetA); void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB); setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB); -void qh_vertexneighbors(void /*qh.facet_list*/); +void qh_vertexneighbors(void /* qh.facet_list */); boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB); - #endif /* qhDEFpoly */ diff --git a/3rdparty/qhull/poly2.c b/3rdparty/qhull/poly2.c index f6dc15c20..4a207b860 100644 --- a/3rdparty/qhull/poly2.c +++ b/3rdparty/qhull/poly2.c @@ -2,28 +2,59 @@ >-------------------------------- poly2.c - implements polygons and simplices + implements polygons and simplicies see qh-poly.htm, poly.h and libqhull.h frequently used code is in poly.c - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/poly2.c#11 $$Change: 2069 $ - $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/poly2.c#16 $$Change: 2963 $ + $DateTime: 2020/06/03 19:31:01 $$Author: bbarber $ */ #include "qhull_a.h" /*======== functions in alphabetical order ==========*/ +/*--------------------------------- + + qh_addfacetvertex( facet, newvertex ) + add newvertex to facet.vertices if not already there + vertices are inverse sorted by vertex->id + + returns: + True if new vertex for facet + + notes: + see qh_replacefacetvertex +*/ +boolT qh_addfacetvertex(facetT *facet, vertexT *newvertex) { + vertexT *vertex; + int vertex_i= 0, vertex_n; + boolT isnew= True; + + FOREACHvertex_i_(facet->vertices) { + if (vertex->id < newvertex->id) { + break; + }else if (vertex->id == newvertex->id) { + isnew= False; + break; + } + } + if (isnew) + qh_setaddnth(&facet->vertices, vertex_i, newvertex); + return isnew; +} /* addfacetvertex */ + /*--------------------------------- 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) { +void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash) { int scan; void *elem; @@ -40,7 +71,7 @@ void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash) { /*--------------------------------- - qh_check_bestdist() + qh_check_bestdist( ) check that all points are within max_outside of the nearest facet if qh.ONLYgood, ignores !good facets @@ -78,7 +109,7 @@ void qh_check_bestdist(void) { maxoutside += qh DISTround; /* one more qh.DISTround for check computation */ trace1((qh ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside)); - facets= qh_pointfacet(/*qh.facet_list*/); + facets= qh_pointfacet(/* qh.facet_list */); if (!qh_QUICKhelp && qh PRINTprecision) qh_fprintf(qh ferr, 8091, "\n\ qhull output completed. Verifying that %d points are\n\ @@ -101,12 +132,12 @@ below %2.2g of the nearest %sfacet.\n", maximize_(maxdist, dist); if (dist > maxoutside) { if (qh ONLYgood && !bestfacet->good - && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist)) - && dist > maxoutside)) + && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) notgood++; else { waserror= True; - qh_fprintf(qh ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n", + qh_fprintf(qh ferr, 6109, "qhull precision error (qh_check_bestdist): 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; @@ -122,7 +153,7 @@ below %2.2g of the nearest %sfacet.\n", a lens-shaped component, these points were not verified. Use\n\ options 'Qci Tv' to verify all points.\n", notverified); if (maxdist > qh outside_err) { - qh_fprintf(qh ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n", + qh_fprintf(qh ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value is qh.outside_err (%6.2g)\n", maxdist, qh outside_err); qh_errexit2(qh_ERRprec, errfacet1, errfacet2); }else if (waserror && qh outside_err > REALmax/2) @@ -131,61 +162,11 @@ options 'Qci Tv' to verify all points.\n", notverified); trace0((qh ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist)); } /* check_bestdist */ -/*--------------------------------- - - qh_check_dupridge(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(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(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 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 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 ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n"); - if(minvertex > qh_WIDEduplicate*prevdist) - qh_fprintf(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_ERRqhull, facet1, facet2); - } -} /* check_dupridge */ -#endif - /*--------------------------------- - qh_check_maxout() + qh_check_maxout( ) updates qh.max_outside by checking all points against bestfacet if qh.ONLYgood, ignores !good facets @@ -197,9 +178,9 @@ void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2) removes inside/coplanar points from coplanarset as needed notes: - defines coplanar as min_vertex instead of MAXcoplanar + defines coplanar as qh.min_vertex instead of qh.MAXcoplanar may not need to check near-inside points because of qh.MAXcoplanar - and qh.KEEPnearinside (before it was -DISTround) + and qh.KEEPnearinside (before it was -qh.DISTround) see also: qh_check_bestdist() @@ -214,78 +195,140 @@ void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2) (updates outer planes) remove near-inside points from coplanar sets */ -#ifndef qh_NOmerge void qh_check_maxout(void) { - facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist; - realT dist, maxoutside, minvertex, old_maxoutside; - pointT *point; + facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist, *maxbestfacet= NULL, *minfacet, *maxfacet, *maxpointfacet; + realT dist, maxoutside, mindist, nearest; + realT maxoutside_base, minvertex_base; + pointT *point, *maxpoint= NULL; int numpart= 0, facet_i, facet_n, notgood= 0; setT *facets, *vertices; - vertexT *vertex; + vertexT *vertex, *minvertex; - trace1((qh ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n")); - maxoutside= minvertex= 0; + trace1((qh ferr, 1022, "qh_check_maxout: check and update qh.min_vertex %2.2g and qh.max_outside %2.2g\n", qh min_vertex, qh max_outside)); + minvertex_base= fmin_(qh min_vertex, -(qh ONEmerge+qh DISTround)); + maxoutside= mindist= 0.0; + minvertex= qh vertex_list; + maxfacet= minfacet= maxpointfacet= qh facet_list; if (qh VERTEXneighbors && (qh PRINTsummary || qh KEEPinside || qh KEEPcoplanar - || qh TRACElevel || qh PRINTstatistics + || qh TRACElevel || qh PRINTstatistics || qh VERIFYoutput || qh CHECKfrequently || qh PRINTout[0] == qh_PRINTsummary || qh PRINTout[0] == qh_PRINTnone)) { - trace1((qh ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n")); - vertices= qh_pointvertex(/*qh.facet_list*/); + trace1((qh ferr, 1023, "qh_check_maxout: determine actual minvertex\n")); + vertices= qh_pointvertex(/* qh.facet_list */); FORALLvertices { FOREACHneighbor_(vertex) { zinc_(Zdistvertex); /* distance also computed by main loop below */ qh_distplane(vertex->point, neighbor, &dist); - minimize_(minvertex, dist); + if (dist < mindist) { + if (qh min_vertex/minvertex_base > qh_WIDEmaxoutside && (qh PRINTprecision || !qh ALLOWwide)) { + nearest= qh_vertex_bestdist(neighbor->vertices); + /* should be caught in qh_mergefacet */ + qh_fprintf(qh ferr, 7083, "Qhull precision warning: in post-processing (qh_check_maxout) p%d(v%d) is %2.2g below f%d nearest vertices %2.2g\n", + qh_pointid(vertex->point), vertex->id, dist, neighbor->id, nearest); + } + mindist= dist; + minvertex= vertex; + minfacet= neighbor; + } +#ifndef qh_NOtrace if (-dist > qh TRACEdist || dist > qh TRACEdist - || neighbor == qh tracefacet || vertex == qh tracevertex) - qh_fprintf(qh ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n", - qh_pointid(vertex->point), vertex->id, dist, neighbor->id); + || neighbor == qh tracefacet || vertex == qh tracevertex) { + nearest= qh_vertex_bestdist(neighbor->vertices); + qh_fprintf(qh ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d nearest vertices %2.2g\n", + qh_pointid(vertex->point), vertex->id, dist, neighbor->id, nearest); + } +#endif } } if (qh MERGING) { wmin_(Wminvertex, qh min_vertex); } - qh min_vertex= minvertex; + qh min_vertex= mindist; qh_settempfree(&vertices); } - facets= qh_pointfacet(/*qh.facet_list*/); - do { - old_maxoutside= fmax_(qh max_outside, maxoutside); - FOREACHfacet_i_(facets) { /* for each point with facet assignment */ - if (facet) { - point= qh_point(facet_i); - if (point == qh GOODpointp) - continue; - zzinc_(Ztotcheck); - qh_distplane(point, facet, &dist); - numpart++; - bestfacet= qh_findbesthorizon(qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart); - if (bestfacet && dist > maxoutside) { - if (qh ONLYgood && !bestfacet->good - && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist)) - && dist > maxoutside)) - notgood++; - else - maxoutside= dist; + trace1((qh ferr, 1055, "qh_check_maxout: determine actual maxoutside\n")); + maxoutside_base= fmax_(qh max_outside, qh ONEmerge+qh DISTround); + /* maxoutside_base is same as qh.MAXoutside without qh.MINoutside (qh_detmaxoutside) */ + facets= qh_pointfacet(/* qh.facet_list */); + FOREACHfacet_i_(facets) { /* for each point with facet assignment */ + if (facet) { + point= qh_point(facet_i); + if (point == qh GOODpointp) + continue; + zzinc_(Ztotcheck); + qh_distplane(point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart); + if (bestfacet && dist >= maxoutside) { + if (qh ONLYgood && !bestfacet->good + && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) { + notgood++; + }else if (dist/maxoutside_base > qh_WIDEmaxoutside && (qh PRINTprecision || !qh ALLOWwide)) { + nearest= qh_vertex_bestdist(bestfacet->vertices); + if (nearest < fmax_(qh ONEmerge, qh max_outside) * qh_RATIOcoplanaroutside * 2) { + qh_fprintf(qh ferr, 7087, "Qhull precision warning: in post-processing (qh_check_maxout) p%d for f%d is %2.2g above twisted facet f%d nearest vertices %2.2g\n", + qh_pointid(point), facet->id, dist, bestfacet->id, nearest); + }else { + qh_fprintf(qh ferr, 7088, "Qhull precision warning: in post-processing (qh_check_maxout) p%d for f%d is %2.2g above hidden facet f%d nearest vertices %2.2g\n", + qh_pointid(point), facet->id, dist, bestfacet->id, nearest); + } + maxbestfacet= bestfacet; } - if (dist > qh TRACEdist || (bestfacet && bestfacet == qh tracefacet)) - qh_fprintf(qh ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n", - qh_pointid(point), dist, (bestfacet ? bestfacet->id : UINT_MAX)); + maxoutside= dist; + maxfacet= bestfacet; + maxpoint= point; + maxpointfacet= facet; } + if (dist > qh TRACEdist || (bestfacet && bestfacet == qh tracefacet)) + qh_fprintf(qh ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n", + qh_pointid(point), dist, (bestfacet ? 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(&facets); wval_(Wmaxout)= maxoutside - qh max_outside; wmax_(Wmaxoutside, qh max_outside); - qh max_outside= maxoutside; - qh_nearcoplanar(/*qh.facet_list*/); + if (!qh APPROXhull && maxoutside > qh DISTround) { /* initial value for f.maxoutside */ + FORALLfacets { + if (maxoutside < facet->maxoutside) { + if (!qh KEEPcoplanar) { + maxoutside= facet->maxoutside; + }else if (maxoutside + qh DISTround < facet->maxoutside) { /* maxoutside is computed distance, e.g., rbox 100 s D3 t1547136913 | qhull R1e-3 Tcv Qc */ + qh_fprintf(qh ferr, 7082, "Qhull precision warning (qh_check_maxout): f%d.maxoutside (%4.4g) is greater than computed qh.max_outside (%2.2g) + qh.DISTround (%2.2g). It should be less than or equal\n", + facet->id, facet->maxoutside, maxoutside, qh DISTround); + } + } + } + } + qh max_outside= maxoutside; + qh_nearcoplanar(/* qh.facet_list */); qh maxoutdone= True; - trace1((qh ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n", - maxoutside, qh min_vertex, notgood)); + trace1((qh ferr, 1024, "qh_check_maxout: p%d(v%d) is qh.min_vertex %2.2g below facet f%d. Point p%d for f%d is qh.max_outside %2.2g above f%d. %d points are outside of not-good facets\n", + qh_pointid(minvertex->point), minvertex->id, qh min_vertex, minfacet->id, qh_pointid(maxpoint), maxpointfacet->id, qh max_outside, maxfacet->id, notgood)); + if(!qh ALLOWwide) { + if (maxoutside/maxoutside_base > qh_WIDEmaxoutside) { + qh_fprintf(qh ferr, 6297, "Qhull precision error (qh_check_maxout): large increase in qh.max_outside during post-processing dist %2.2g (%.1fx). See warning QH0032/QH0033. Allow with 'Q12' (allow-wide) and 'Pp'\n", + maxoutside, maxoutside/maxoutside_base); + qh_errexit(qh_ERRwide, maxbestfacet, NULL); + }else if (!qh APPROXhull && maxoutside_base > (qh ONEmerge * qh_WIDEmaxoutside2)) { + if (maxoutside > (qh ONEmerge * qh_WIDEmaxoutside2)) { /* wide facets may have been deleted */ + qh_fprintf(qh ferr, 6298, "Qhull precision error (qh_check_maxout): a facet merge, vertex merge, vertex, or coplanar point produced a wide facet %2.2g (%.1fx). Trace with option 'TWn' to identify the merge. Allow with 'Q12' (allow-wide)\n", + maxoutside_base, maxoutside_base/(qh ONEmerge + qh DISTround)); + qh_errexit(qh_ERRwide, maxbestfacet, NULL); + } + }else if (qh min_vertex/minvertex_base > qh_WIDEmaxoutside) { + qh_fprintf(qh ferr, 6354, "Qhull precision error (qh_check_maxout): large increase in qh.min_vertex during post-processing dist %2.2g (%.1fx). See warning QH7083. Allow with 'Q12' (allow-wide) and 'Pp'\n", + qh min_vertex, qh min_vertex/minvertex_base); + qh_errexit(qh_ERRwide, minfacet, NULL); + }else if (minvertex_base < -(qh ONEmerge * qh_WIDEmaxoutside2)) { + if (qh min_vertex < -(qh ONEmerge * qh_WIDEmaxoutside2)) { /* wide facets may have been deleted */ + qh_fprintf(qh ferr, 6380, "Qhull precision error (qh_check_maxout): a facet or vertex merge produced a wide facet: v%d below f%d distance %2.2g (%.1fx). Trace with option 'TWn' to identify the merge. Allow with 'Q12' (allow-wide)\n", + minvertex->id, minfacet->id, mindist, -qh min_vertex/(qh ONEmerge + qh DISTround)); + qh_errexit(qh_ERRwide, minfacet, NULL); + } + } + } } /* check_maxout */ #else /* qh_NOmerge */ void qh_check_maxout(void) { @@ -295,16 +338,16 @@ void qh_check_maxout(void) { /*--------------------------------- - qh_check_output() + qh_check_output( ) performs the checks at the end of qhull algorithm - Maybe called after voronoi output. Will recompute otherwise centrums are Voronoi centers instead + Maybe called after Voronoi output. If so, it recomputes centrums since they are Voronoi centers instead. */ void qh_check_output(void) { int i; if (qh STOPcone) return; - if (qh VERIFYoutput | qh IStracing | qh CHECKfrequently) { + if (qh VERIFYoutput || qh IStracing || qh CHECKfrequently) { qh_checkpolygon(qh facet_list); qh_checkflipped_all(qh facet_list); qh_checkconvex(qh facet_list, qh_ALGORITHMfault); @@ -319,35 +362,43 @@ void qh_check_output(void) { /*--------------------------------- - qh_check_point( point, facet, maxoutside, maxdist, errfacet1, errfacet2 ) + qh_check_point( point, facet, maxoutside, maxdist, errfacet1, errfacet2, errcount ) check that point is less than maxoutside from facet + + notes: + only called from qh_checkpoints + reports up to qh_MAXcheckpoint-1 errors per facet */ -void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) { - realT dist; +void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2, int *errcount) { + realT dist, nearest; /* occurs after statistics reported */ qh_distplane(point, facet, &dist); + maximize_(*maxdist, dist); if (dist > *maxoutside) { + (*errcount)++; if (*errfacet1 != facet) { *errfacet2= *errfacet1; *errfacet1= facet; } - qh_fprintf(qh ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n", - qh_pointid(point), facet->id, dist, *maxoutside); + if (*errcount < qh_MAXcheckpoint) { + nearest= qh_vertex_bestdist(facet->vertices); + qh_fprintf(qh ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g nearest vertices %2.2g\n", + qh_pointid(point), facet->id, dist, *maxoutside, nearest); + } } - maximize_(*maxdist, dist); } /* qh_check_point */ /*--------------------------------- - qh_check_points() + qh_check_points( ) checks that all points are inside all facets notes: if many points and qh_check_maxout not called (i.e., !qh.MERGING), - calls qh_findbesthorizon (seldom done). + calls qh_findbesthorizon via qh_check_bestdist (seldom done). ignores flipped facets maxoutside includes 2 qh.DISTrounds one qh.DISTround for the computed distances in qh_check_points @@ -366,6 +417,7 @@ void qh_check_points(void) { facetT *facet, *errfacet1= NULL, *errfacet2= NULL; realT total, maxoutside, maxdist= -REALmax; pointT *point, **pointp, *pointtemp; + int errcount; boolT testouter; maxoutside= qh_maxouter(); @@ -379,8 +431,7 @@ void qh_check_points(void) { total= (float)qh num_facets * (float)qh num_points; if (total >= qh_VERIFYdirect && !qh maxoutdone) { if (!qh_QUICKhelp && qh SKIPcheckmax && qh MERGING) - qh_fprintf(qh ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\ -Verify may report that a point is outside of a facet.\n"); + qh_fprintf(qh ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po'). Verify may report that a point is outside of a facet.\n"); qh_check_bestdist(); }else { if (qh_MAXoutside && qh maxoutdone) @@ -389,12 +440,9 @@ Verify may report that a point is outside of a facet.\n"); testouter= False; if (!qh_QUICKhelp) { if (qh MERGEexact) - qh_fprintf(qh ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point\n\ -is outside of a facet. See qh-optq.htm#Qx\n"); + qh_fprintf(qh ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point is outside of a facet. See qh-optq.htm#Qx\n"); else if (qh SKIPcheckmax || qh NOnearinside) - qh_fprintf(qh ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\ -near-inside points ('Q8'). Verify may report that a point is outside\n\ -of a facet.\n"); + qh_fprintf(qh ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of near-inside points ('Q8'). Verify may report that a point is outside of a facet.\n"); } if (qh PRINTprecision) { if (testouter) @@ -415,29 +463,36 @@ all %sfacets. Will make %2.0f distance computations.\n", continue; if (!facet->normal) { qh_fprintf(qh ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id); + if (!errfacet1) + errfacet1= facet; continue; } if (testouter) { #if qh_MAXoutside - maxoutside= facet->maxoutside + 2* qh DISTround; + maxoutside= facet->maxoutside + 2 * qh DISTround; /* one DISTround to actual point and another to computed point */ #endif } + errcount= 0; FORALLpoints { if (point != qh GOODpointp) - qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2); + qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2, &errcount); } FOREACHpoint_(qh other_points) { if (point != qh GOODpointp) - qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2); + qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2, &errcount); + } + if (errcount >= qh_MAXcheckpoint) { + qh_fprintf(qh ferr, 6422, "qhull precision error (qh_check_points): %d additional points outside facet f%d, maxdist= %6.8g\n", + errcount-qh_MAXcheckpoint+1, facet->id, maxdist); } } if (maxdist > qh outside_err) { qh_fprintf(qh ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n", maxdist, qh outside_err ); - qh_errexit2( qh_ERRprec, errfacet1, errfacet2 ); + qh_errexit2(qh_ERRprec, errfacet1, errfacet2 ); }else if (errfacet1 && qh outside_err > REALmax/2) - qh_errexit2( qh_ERRprec, errfacet1, errfacet2 ); + qh_errexit2(qh_ERRprec, errfacet1, errfacet2 ); /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */ trace0((qh ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist)); } @@ -449,103 +504,133 @@ all %sfacets. Will make %2.0f distance computations.\n", qh_checkconvex( facetlist, fault ) check that each ridge in facetlist is convex - fault = qh_DATAfault if reporting errors + fault = qh_DATAfault if reporting errors from qh_initialhull with qh.ZEROcentrum = qh_ALGORITHMfault otherwise returns: counts Zconcaveridges and Zcoplanarridges - errors if concaveridge or if merging an coplanar ridge + errors if !qh.FORCEoutput ('Fo') and concaveridge or if merging a coplanar ridge + overwrites Voronoi centers if set by qh_setvoronoi_all/qh_ASvoronoi - note: + notes: + called by qh_initial_hull, qh_check_output, qh_all_merges ('Tc'), qh_build_withrestart ('QJ') + does not test f.tricoplanar facets (qh_triangulate) + must be no stronger than qh_test_appendmerge if not merging, - tests vertices for neighboring simplicial facets - else if ZEROcentrum, - tests vertices for neighboring simplicial facets - else - tests centrums of neighboring facets + tests vertices for neighboring simplicial facets < -qh.DISTround + else if ZEROcentrum and simplicial facet, + tests vertices for neighboring simplicial facets < 0.0 + tests centrums of neighboring nonsimplicial facets < 0.0 + else if ZEROcentrum + tests centrums of neighboring facets < 0.0 + else + tests centrums of neighboring facets < -qh.DISTround ('En' 'Rn') + Does not test against -qh.centrum_radius since repeated computations may have different round-off errors (e.g., 'Rn') design: for all facets report flipped facets if ZEROcentrum and simplicial neighbors - test vertices for neighboring simplicial facets + test vertices against neighbor else - test centrum against all neighbors + test centrum against neighbor */ void qh_checkconvex(facetT *facetlist, int fault) { facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL; vertexT *vertex; realT dist; pointT *centrum; - boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial; - int neighbor_i; + boolT waserror= False, centrum_warning= False, tempcentrum= False, first_nonsimplicial= False, tested_simplicial, allsimplicial; + int neighbor_i, neighbor_n; - trace1((qh ferr, 1026, "qh_checkconvex: check all ridges are convex\n")); + if (qh ZEROcentrum) { + trace1((qh ferr, 1064, "qh_checkconvex: check that facets are not-flipped and for qh.ZEROcentrum that simplicial vertices are below their neighbor (dist<0.0)\n")); + first_nonsimplicial= True; + }else if (!qh MERGING) { + trace1((qh ferr, 1026, "qh_checkconvex: check that facets are not-flipped and that simplicial vertices are convex by qh.DISTround ('En', 'Rn')\n")); + first_nonsimplicial= True; + }else + trace1((qh ferr, 1062, "qh_checkconvex: check that facets are not-flipped and that their centrums are convex by qh.DISTround ('En', 'Rn') \n")); if (!qh RERUN) { zzval_(Zconcaveridges)= 0; zzval_(Zcoplanarridges)= 0; } FORALLfacet_(facetlist) { if (facet->flipped) { - qh_precision("flipped facet"); - qh_fprintf(qh ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n", + qh_joggle_restart("flipped facet"); /* also tested by qh_checkflipped */ + qh_fprintf(qh ferr, 6113, "qhull precision error: f%d is flipped (interior point is outside)\n", facet->id); errfacet1= facet; waserror= True; continue; } - if (qh MERGING && (!qh ZEROcentrum || !facet->simplicial || facet->tricoplanar)) + if (facet->tricoplanar) + continue; + if (qh MERGING && (!qh ZEROcentrum || !facet->simplicial)) { allsimplicial= False; - else { + tested_simplicial= False; + }else { allsimplicial= True; - neighbor_i= 0; - FOREACHneighbor_(facet) { - vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT); - if (!neighbor->simplicial || neighbor->tricoplanar) { + tested_simplicial= True; + FOREACHneighbor_i_(facet) { + if (neighbor->tricoplanar) + continue; + if (!neighbor->simplicial) { allsimplicial= False; continue; } + vertex= SETelemt_(facet->vertices, neighbor_i, vertexT); qh_distplane(vertex->point, neighbor, &dist); - if (dist > -qh DISTround) { + if (dist >= -qh DISTround) { if (fault == qh_DATAfault) { - qh_precision("coplanar or concave ridge"); - qh_fprintf(qh ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist); - qh_errexit(qh_ERRsingular, NULL, NULL); + qh_joggle_restart("non-convex initial simplex"); + if (dist > qh DISTround) + qh_fprintf(qh ferr, 6114, "qhull precision error: initial simplex is not convex, since p%d(v%d) is %6.4g above opposite f%d\n", + qh_pointid(vertex->point), vertex->id, dist, neighbor->id); + else + qh_fprintf(qh ferr, 6379, "qhull precision error: initial simplex is not convex, since p%d(v%d) is within roundoff of opposite facet f%d (dist %6.4g)\n", + qh_pointid(vertex->point), vertex->id, neighbor->id, dist); + qh_errexit(qh_ERRsingular, neighbor, NULL); } if (dist > qh DISTround) { zzinc_(Zconcaveridges); - qh_precision("concave ridge"); - qh_fprintf(qh ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n", - facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist); + qh_joggle_restart("concave ridge"); + qh_fprintf(qh ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above f%d\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, neighbor->id); errfacet1= facet; errfacet2= neighbor; waserror= True; }else if (qh ZEROcentrum) { - if (dist > 0) { /* qh_checkzero checks that dist < - qh DISTround */ + if (dist > 0.0) { /* qh_checkzero checked convex (dist < (- 2*qh DISTround)), computation may differ e.g. 'Rn' */ zzinc_(Zcoplanarridges); - qh_precision("coplanar ridge"); - qh_fprintf(qh ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n", - facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist); + qh_joggle_restart("coplanar ridge"); + qh_fprintf(qh ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above or coplanar with f%d with qh.ZEROcentrum\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, neighbor->id); errfacet1= facet; errfacet2= neighbor; waserror= True; } }else { zzinc_(Zcoplanarridges); - qh_precision("coplanar ridge"); - trace0((qh ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n", - facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, qh furthest_id)); + qh_joggle_restart("coplanar ridge"); + trace0((qh ferr, 22, "qhull precision error: f%d is coplanar to f%d, since p%d(v%d) is within %6.4g of f%d, during p%d\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, neighbor->id, qh furthest_id)); } } } } if (!allsimplicial) { + if (first_nonsimplicial) { + trace1((qh ferr, 1063, "qh_checkconvex: starting with f%d, also check that centrums of non-simplicial ridges are below their neighbors (dist<0.0)\n", + facet->id)); + first_nonsimplicial= False; + } if (qh CENTERtype == qh_AScentrum) { if (!facet->center) facet->center= qh_getcentrum(facet); centrum= facet->center; }else { - if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) { + if (!centrum_warning && !facet->simplicial) { /* recomputed centrum correct for simplicial facets */ centrum_warning= True; qh_fprintf(qh ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n"); } @@ -553,15 +638,15 @@ void qh_checkconvex(facetT *facetlist, int fault) { tempcentrum= True; } FOREACHneighbor_(facet) { - if (qh ZEROcentrum && facet->simplicial && neighbor->simplicial) + if (neighbor->simplicial && tested_simplicial) /* tested above since f.simplicial */ continue; - if (facet->tricoplanar || neighbor->tricoplanar) + if (neighbor->tricoplanar) continue; zzinc_(Zdistconvex); qh_distplane(centrum, neighbor, &dist); if (dist > qh DISTround) { zzinc_(Zconcaveridges); - qh_precision("concave ridge"); + qh_joggle_restart("concave ridge"); qh_fprintf(qh ferr, 6117, "qhull precision error: f%d is concave to f%d. Centrum of f%d is %6.4g above f%d\n", facet->id, neighbor->id, facet->id, dist, neighbor->id); errfacet1= facet; @@ -570,7 +655,7 @@ void qh_checkconvex(facetT *facetlist, int fault) { }else if (dist >= 0.0) { /* if arithmetic always rounds the same, can test against centrum radius instead */ zzinc_(Zcoplanarridges); - qh_precision("coplanar ridge"); + qh_joggle_restart("coplanar ridge"); qh_fprintf(qh ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d. Centrum of f%d is %6.4g above f%d\n", facet->id, neighbor->id, facet->id, dist, neighbor->id); errfacet1= facet; @@ -613,6 +698,7 @@ void qh_checkconvex(facetT *facetlist, int fault) { all ridges have distinct vertex sets notes: + called by qh_tracemerge and qh_checkpolygon uses neighbor->seen design: @@ -628,22 +714,53 @@ void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { facetT *neighbor, **neighborp, *errother=NULL; ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2; vertexT *vertex, **vertexp; - unsigned previousid= INT_MAX; + unsigned int previousid= INT_MAX; int numneighbors, numvertices, numridges=0, numRvertices=0; boolT waserror= False; - int skipA, skipB, ridge_i, ridge_n, i; + int skipA, skipB, ridge_i, ridge_n, i, last_v= qh hull_dim-2; setT *intersection; - if (facet->visible) { - qh_fprintf(qh ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n", + trace4((qh ferr, 4088, "qh_checkfacet: check f%d newmerge? %d\n", facet->id, newmerge)); + if (facet->id >= qh facet_id) { + qh_fprintf(qh ferr, 6414, "qhull internal error (qh_checkfacet): unknown facet id f%d >= qh.facet_id (%d)\n", facet->id, qh facet_id); + waserror= True; + } + if (facet->visitid > qh visit_id) { + qh_fprintf(qh ferr, 6415, "qhull internal error (qh_checkfacet): expecting f%d.visitid <= qh.visit_id (%d). Got visitid %d\n", facet->id, qh visit_id, facet->visitid); + waserror= True; + } + if (facet->visible && !qh NEWtentative) { + qh_fprintf(qh ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on qh.visible_list\n", facet->id); qh_errexit(qh_ERRqhull, facet, NULL); } + if (facet->redundant && !facet->visible && qh_setsize(qh degen_mergeset)==0) { + qh_fprintf(qh ferr, 6399, "qhull internal error (qh_checkfacet): redundant facet f%d not on qh.visible_list\n", + facet->id); + waserror= True; + } + if (facet->degenerate && !facet->visible && qh_setsize(qh degen_mergeset)==0) { + qh_fprintf(qh ferr, 6400, "qhull internal error (qh_checkfacet): degenerate facet f%d is not on qh.visible_list and qh.degen_mergeset is empty\n", + facet->id); + waserror= True; + } if (!facet->normal) { - qh_fprintf(qh ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n", + qh_fprintf(qh ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n", facet->id); waserror= True; } + if (!facet->newfacet) { + if (facet->dupridge) { + qh_fprintf(qh ferr, 6349, "qhull internal error (qh_checkfacet): f%d is 'dupridge' but it is not a newfacet on qh.newfacet_list f%d\n", + facet->id, getid_(qh newfacet_list)); + waserror= True; + } + if (facet->newmerge) { + qh_fprintf(qh ferr, 6383, "qhull internal error (qh_checkfacet): f%d is 'newmerge' but it is not a newfacet on qh.newfacet_list f%d. Missing call to qh_reducevertices\n", + facet->id, getid_(qh newfacet_list)); + waserror= True; + } + } qh_setcheck(facet->vertices, "vertices for f", facet->id); qh_setcheck(facet->ridges, "ridges for f", facet->id); qh_setcheck(facet->outsideset, "outsideset for f", facet->id); @@ -694,9 +811,15 @@ void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { } FOREACHneighbor_(facet) { if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) { - qh_fprintf(qh ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id); + qh_fprintf(qh ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGEridge or DUPLICATEridge neighbor\n", facet->id); qh_errexit(qh_ERRqhull, facet, NULL); } + if (neighbor->visible) { + qh_fprintf(qh ferr, 6401, "qhull internal error (qh_checkfacet): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + errother= neighbor; + waserror= True; + } neighbor->seen= True; } FOREACHneighbor_(facet) { @@ -741,6 +864,14 @@ void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { errridge= ridge; waserror= True; } + if (!facet->newfacet && !neighbor->newfacet) { + if ((!ridge->tested) | ridge->nonconvex | ridge->mergevertex) { + qh_fprintf(qh ferr, 6384, "qhull internal error (qh_checkfacet): ridge r%d is nonconvex (%d), mergevertex (%d) or not tested (%d) for facet f%d, neighbor f%d\n", + ridge->id, ridge->nonconvex, ridge->mergevertex, ridge->tested, facet->id, neighbor->id); + errridge= ridge; + waserror= True; + } + } } if (!facet->simplicial) { FOREACHneighbor_(facet) { @@ -773,15 +904,16 @@ void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { if (!newmerge) { FOREACHvertex_(intersection) { if (!vertex->seen2) { - if (qh IStracing >=3 || !qh MERGING) { - qh_fprintf(qh ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\ - not in a ridge. This is ok under merging. Last point was p%d\n", + if (!qh MERGING) { + qh_fprintf(qh ferr, 6420, "qhull topology error (qh_checkfacet): vertex v%d in f%d intersect f%d but not in a ridge. Last point was p%d\n", vertex->id, facet->id, neighbor->id, qh furthest_id); - if (!qh FORCEoutput && !qh MERGING) { + if (!qh FORCEoutput) { qh_errprint("ERRONEOUS", facet, neighbor, NULL, vertex); - if (!qh MERGING) - qh_errexit(qh_ERRqhull, NULL, NULL); + qh_errexit(qh_ERRtopology, NULL, NULL); } + }else { + trace4((qh ferr, 4025, "qh_checkfacet: vertex v%d in f%d intersect f%d but not in a ridge. Repaired by qh_remove_extravertices in qh_reducevertices\n", + vertex->id, facet->id, neighbor->id)); } } } @@ -790,7 +922,7 @@ void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { } }else { /* simplicial */ FOREACHneighbor_(facet) { - if (neighbor->simplicial) { + if (neighbor->simplicial && !facet->degenerate && !neighbor->degenerate) { 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)) { @@ -802,15 +934,21 @@ void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { } } } - if (qh hull_dim < 5 && (qh IStracing > 2 || qh CHECKfrequently)) { - FOREACHridge_i_(facet->ridges) { /* expensive */ - for (i=ridge_i+1; i < ridge_n; i++) { - ridge2= SETelemt_(facet->ridges, i, ridgeT); - if (qh_setequal(ridge->vertices, ridge2->vertices)) { - qh_fprintf(qh ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n", - ridge->id, ridge2->id); - errridge= ridge; - waserror= True; + if (!newmerge && qh CHECKduplicates && qh hull_dim < 5 && (qh IStracing > 2 || qh CHECKfrequently)) { + FOREACHridge_i_(facet->ridges) { /* expensive, if was merge and qh_maybe_duplicateridges hasn't been called yet */ + if (!ridge->mergevertex) { + for (i=ridge_i+1; i < ridge_n; i++) { + ridge2= SETelemt_(facet->ridges, i, ridgeT); + if (SETelem_(ridge->vertices, last_v) == SETelem_(ridge2->vertices, last_v)) { /* SETfirst is likely to be the same */ + if (SETfirst_(ridge->vertices) == SETfirst_(ridge2->vertices)) { + if (qh_setequal(ridge->vertices, ridge2->vertices)) { + qh_fprintf(qh ferr, 6294, "qhull internal error (qh_checkfacet): ridges r%d and r%d (f%d) have the same vertices\n", /* same as duplicate ridge */ + ridge->id, ridge2->id, facet->id); + errridge= ridge; + waserror= True; + } + } + } } } } @@ -821,12 +959,14 @@ void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { } } /* checkfacet */ - /*--------------------------------- qh_checkflipped_all( facetlist ) checks orientation of facets in list against interior point + + notes: + called by qh_checkoutput */ void qh_checkflipped_all(facetT *facetlist) { facetT *facet; @@ -848,11 +988,183 @@ void qh_checkflipped_all(facetT *facetlist) { if (waserror) { qh_fprintf(qh ferr, 8101, "\n\ A flipped facet occurs when its distance to the interior point is\n\ -greater than %2.2g, the maximum roundoff error.\n", -qh DISTround); +greater than or equal to %2.2g, the maximum roundoff error.\n", -qh DISTround); qh_errexit(qh_ERRprec, NULL, NULL); } } /* checkflipped_all */ +/*--------------------------------- + + qh_checklists( facetlist ) + Check and repair facetlist and qh.vertex_list for infinite loops or overwritten facets + Checks that qh.newvertex_list is on qh.vertex_list + if facetlist is qh.facet_list + Checks that qh.visible_list and qh.newfacet_list are on qh.facet_list + Updates qh.facetvisit and qh.vertexvisit + + returns: + True if no errors found + If false, repairs erroneous lists to prevent infinite loops by FORALL macros + + notes: + called by qh_buildtracing, qh_checkpolygon, qh_collectstatistics, qh_printfacetlist, qh_printsummary + not called by qh_printlists + + design: + if facetlist + check qh.facet_tail + for each facet + check for infinite loop or overwritten facet + check previous facet + if facetlist is qh.facet_list + check qh.next_facet, qh.visible_list and qh.newfacet_list + if vertexlist + check qh.vertex_tail + for each vertex + check for infinite loop or overwritten vertex + check previous vertex + check qh.newvertex_list +*/ +boolT qh_checklists(facetT *facetlist) { + facetT *facet, *errorfacet= NULL, *errorfacet2= NULL, *previousfacet; + vertexT *vertex, *vertexlist, *previousvertex, *errorvertex= NULL; + boolT waserror= False, newseen= False, nextseen= False, newvertexseen= False, visibleseen= False; + + if (facetlist == qh newfacet_list || facetlist == qh visible_list) { + vertexlist= qh vertex_list; + previousvertex= NULL; + trace2((qh ferr, 2110, "qh_checklists: check qh.%s_list f%d and qh.vertex_list v%d\n", + (facetlist == qh newfacet_list ? "newfacet" : "visible"), facetlist->id, getid_(vertexlist))); + }else { + vertexlist= qh vertex_list; + previousvertex= NULL; + trace2((qh ferr, 2111, "qh_checklists: check %slist f%d and qh.vertex_list v%d\n", + (facetlist == qh facet_list ? "qh.facet_" : "facet"), getid_(facetlist), getid_(vertexlist))); + } + if (facetlist) { + if (qh facet_tail == NULL || qh facet_tail->id != 0 || qh facet_tail->next != NULL) { + qh_fprintf(qh ferr, 6397, "qhull internal error (qh_checklists): either qh.facet_tail f%d is NULL, or its id is not 0, or its next is not NULL\n", + getid_(qh facet_tail)); + qh_errexit(qh_ERRqhull, qh facet_tail, NULL); + } + previousfacet= (facetlist == qh facet_list ? NULL : facetlist->previous); + qh visit_id++; + FORALLfacet_(facetlist) { + if (facet->visitid >= qh visit_id || facet->id >= qh facet_id) { + waserror= True; + errorfacet= facet; + errorfacet2= previousfacet; + if (facet->visitid == qh visit_id) + qh_fprintf(qh ferr, 6039, "qhull internal error (qh_checklists): f%d already in facetlist causing an infinite loop ... f%d > f%d ... > f%d > f%d. Truncate facetlist at f%d\n", + facet->id, facet->id, facet->next->id, getid_(previousfacet), facet->id, getid_(previousfacet)); + else + qh_fprintf(qh ferr, 6350, "qhull internal error (qh_checklists): unknown or overwritten facet f%d, either id >= qh.facet_id (%d) or f.visitid %u > qh.visit_id %u. Facetlist terminated at previous facet f%d\n", + facet->id, qh facet_id, facet->visitid, qh visit_id, getid_(previousfacet)); + if (previousfacet) + previousfacet->next= qh facet_tail; + else + facetlist= qh facet_tail; + break; + } + facet->visitid= qh visit_id; + if (facet->previous != previousfacet) { + qh_fprintf(qh ferr, 6416, "qhull internal error (qh_checklists): expecting f%d.previous == f%d. Got f%d\n", + facet->id, getid_(previousfacet), getid_(facet->previous)); + waserror= True; + errorfacet= facet; + errorfacet2= facet->previous; + } + previousfacet= facet; + if (facetlist == qh facet_list) { + if (facet == qh visible_list) { + if(newseen){ + qh_fprintf(qh ferr, 6285, "qhull internal error (qh_checklists): qh.visible_list f%d is after qh.newfacet_list f%d. It should be at, before, or NULL\n", + facet->id, getid_(qh newfacet_list)); + waserror= True; + errorfacet= facet; + errorfacet2= qh newfacet_list; + } + visibleseen= True; + } + if (facet == qh newfacet_list) + newseen= True; + if (facet == qh facet_next) + nextseen= True; + } + } + if (facetlist == qh facet_list) { + if (!nextseen && qh facet_next && qh facet_next->next) { + qh_fprintf(qh ferr, 6369, "qhull internal error (qh_checklists): qh.facet_next f%d for qh_addpoint is not on qh.facet_list f%d\n", + qh facet_next->id, facetlist->id); + waserror= True; + errorfacet= qh facet_next; + errorfacet2= facetlist; + } + if (!newseen && qh newfacet_list && qh newfacet_list->next) { + qh_fprintf(qh ferr, 6286, "qhull internal error (qh_checklists): qh.newfacet_list f%d is not on qh.facet_list f%d\n", + qh newfacet_list->id, facetlist->id); + waserror= True; + errorfacet= qh newfacet_list; + errorfacet2= facetlist; + } + if (!visibleseen && qh visible_list && qh visible_list->next) { + qh_fprintf(qh ferr, 6138, "qhull internal error (qh_checklists): qh.visible_list f%d is not on qh.facet_list f%d\n", + qh visible_list->id, facetlist->id); + waserror= True; + errorfacet= qh visible_list; + errorfacet2= facetlist; + } + } + } + if (vertexlist) { + if (qh vertex_tail == NULL || qh vertex_tail->id != 0 || qh vertex_tail->next != NULL) { + qh_fprintf(qh ferr, 6366, "qhull internal error (qh_checklists): either qh.vertex_tail v%d is NULL, or its id is not 0, or its next is not NULL\n", + getid_(qh vertex_tail)); + qh_errprint("ERRONEOUS", errorfacet, errorfacet2, NULL, qh vertex_tail); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh vertex_visit++; + FORALLvertex_(vertexlist) { + if (vertex->visitid >= qh vertex_visit || vertex->id >= qh vertex_id) { + waserror= True; + errorvertex= vertex; + if (vertex->visitid == qh visit_id) + qh_fprintf(qh ferr, 6367, "qhull internal error (qh_checklists): v%d already in vertexlist causing an infinite loop ... v%d > v%d ... > v%d > v%d. Truncate vertexlist at v%d\n", + vertex->id, vertex->id, vertex->next->id, getid_(previousvertex), vertex->id, getid_(previousvertex)); + else + qh_fprintf(qh ferr, 6368, "qhull internal error (qh_checklists): unknown or overwritten vertex v%d, either id >= qh.vertex_id (%d) or v.visitid %u > qh.visit_id %u. vertexlist terminated at previous vertex v%d\n", + vertex->id, qh vertex_id, vertex->visitid, qh visit_id, getid_(previousvertex)); + if (previousvertex) + previousvertex->next= qh vertex_tail; + else + vertexlist= qh vertex_tail; + break; + } + vertex->visitid= qh vertex_visit; + if (vertex->previous != previousvertex) { + qh_fprintf(qh ferr, 6427, "qhull internal error (qh_checklists): expecting v%d.previous == v%d. Got v%d\n", + vertex->id, previousvertex, getid_(vertex->previous)); + waserror= True; + errorvertex= vertex; + } + previousvertex= vertex; + if(vertex == qh newvertex_list) + newvertexseen= True; + } + if(!newvertexseen && qh newvertex_list && qh newvertex_list->next) { + qh_fprintf(qh ferr, 6287, "qhull internal error (qh_checklists): new vertex list v%d is not on vertex list\n", qh newvertex_list->id); + waserror= True; + errorvertex= qh newvertex_list; + } + } + if (waserror) { + qh_errprint("ERRONEOUS", errorfacet, errorfacet2, NULL, errorvertex); + return False; + } + return True; +} /* checklists */ + /*--------------------------------- @@ -860,68 +1172,116 @@ greater than %2.2g, the maximum roundoff error.\n", -qh DISTround); checks the correctness of the structure notes: - call with either qh.facet_list or qh.newfacet_list + called by qh_addpoint, qh_all_vertexmerge, qh_check_output, qh_initialhull, qh_prepare_output, qh_triangulate + call with qh.facet_list or qh.newfacet_list or another list checks num_facets and num_vertices if qh.facet_list design: + check and repair lists for infinite loop for each facet - checks facet and outside set - initializes vertexlist + check f.newfacet and f.visible + check facet and outside set if qh.NEWtentative and not f.newfacet, or not f.visible + initializes vertexlist for qh.facet_list or qh.newfacet_list + for each vertex + check vertex + check v.newfacet for each facet - checks vertex set - if checking all facets(qh.facetlist) + count f.ridges + check and count f.vertices + if checking qh.facet_list check facet count if qh.VERTEXneighbors - check vertex neighbors and count + check and count v.neighbors for all vertices + check v.neighbors count and report possible causes of mismatch + check that facets are in their v.neighbors check vertex count */ void qh_checkpolygon(facetT *facetlist) { - facetT *facet; + facetT *facet, *neighbor, **neighborp; + facetT *errorfacet= NULL, *errorfacet2= NULL; vertexT *vertex, **vertexp, *vertexlist; int numfacets= 0, numvertices= 0, numridges= 0; - int totvneighbors= 0, totvertices= 0; - boolT waserror= False, nextseen= False, visibleseen= False; + int totvneighbors= 0, totfacetvertices= 0; + boolT waserror= False, newseen= False, newvertexseen= False, nextseen= False, visibleseen= False; + boolT checkfacet; - trace1((qh ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id)); + trace1((qh ferr, 1027, "qh_checkpolygon: check all facets from f%d, qh.NEWtentative? %d\n", facetlist->id, qh NEWtentative)); + if (!qh_checklists(facetlist)) { + waserror= True; + qh_fprintf(qh ferr, 6374, "qhull internal error: qh_checklists failed in qh_checkpolygon\n"); + if (qh num_facets < 4000) + qh_printlists(); + } if (facetlist != qh facet_list || qh ONLYgood) - nextseen= True; + nextseen= True; /* allow f.outsideset */ FORALLfacet_(facetlist) { if (facet == qh visible_list) visibleseen= True; - if (!facet->visible) { + if (facet == qh newfacet_list) + newseen= True; + if (facet->newfacet && !newseen && !visibleseen) { + qh_fprintf(qh ferr, 6289, "qhull internal error (qh_checkpolygon): f%d is 'newfacet' but it is not on qh.newfacet_list f%d or visible_list f%d\n", facet->id, getid_(qh newfacet_list), getid_(qh visible_list)); + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (!facet->newfacet && newseen) { + qh_fprintf(qh ferr, 6292, "qhull internal error (qh_checkpolygon): f%d is on qh.newfacet_list f%d but it is not 'newfacet'\n", facet->id, getid_(qh newfacet_list)); + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (facet->visible != (visibleseen & !newseen)) { + if(facet->visible) + qh_fprintf(qh ferr, 6290, "qhull internal error (qh_checkpolygon): f%d is 'visible' but it is not on qh.visible_list f%d\n", facet->id, getid_(qh visible_list)); + else + qh_fprintf(qh ferr, 6291, "qhull internal error (qh_checkpolygon): f%d is on qh.visible_list f%d but it is not 'visible'\n", facet->id, qh newfacet_list->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (qh NEWtentative) { + checkfacet= !facet->newfacet; + }else { + checkfacet= !facet->visible; + } + if(checkfacet) { if (!nextseen) { - if (facet == qh facet_next) + if (facet == qh facet_next) /* previous facets do not have outsideset */ nextseen= True; else if (qh_setsize(facet->outsideset)) { if (!qh NARROWhull #if !qh_COMPUTEfurthest - || facet->furthestdist >= qh MINoutside + || facet->furthestdist >= qh MINoutside #endif - ) { - qh_fprintf(qh ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh facet_next\n", - facet->id); - qh_errexit(qh_ERRqhull, facet, NULL); + ) { + qh_fprintf(qh ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh.facet_next f%d\n", + facet->id, getid_(qh facet_next)); + qh_errexit2(qh_ERRqhull, facet, qh facet_next); } } } numfacets++; qh_checkfacet(facet, False, &waserror); + }else if (facet->visible && qh NEWfacets) { + if (!SETempty_(facet->neighbors) || !SETempty_(facet->ridges)) { + qh_fprintf(qh ferr, 6376, "qhull internal error (qh_checkpolygon): expecting empty f.neighbors and f.ridges for visible facet f%d. Got %d neighbors and %d ridges\n", + facet->id, qh_setsize(facet->neighbors), qh_setsize(facet->ridges)); + qh_errexit(qh_ERRqhull, facet, NULL); + } } } - if (qh visible_list && !visibleseen && facetlist == qh facet_list) { - qh_fprintf(qh ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh visible_list->id); - qh_printlists(); - qh_errexit(qh_ERRqhull, qh visible_list, NULL); - } - if (facetlist == qh facet_list) + if (facetlist == qh facet_list) { vertexlist= qh vertex_list; - else if (facetlist == qh newfacet_list) + }else if (facetlist == qh newfacet_list) { vertexlist= qh newvertex_list; - else + }else { vertexlist= NULL; + } FORALLvertex_(vertexlist) { + qh_checkvertex(vertex, !qh_ALL, &waserror); + if(vertex == qh newvertex_list) + newvertexseen= True; vertex->seen= False; vertex->visitid= 0; + if(vertex->newfacet && !newvertexseen && !vertex->deleted) { + qh_fprintf(qh ferr, 6288, "qhull internal error (qh_checkpolygon): v%d is 'newfacet' but it is not on new vertex list v%d\n", vertex->id, getid_(qh newvertex_list)); + qh_errexit(qh_ERRqhull, qh visible_list, NULL); + } } FORALLfacet_(facetlist) { if (facet->visible) @@ -953,17 +1313,56 @@ void qh_checkpolygon(facetT *facetlist) { qh vertex_visit++; if (qh VERTEXneighbors) { FORALLvertices { + if (!vertex->neighbors) { + qh_fprintf(qh ferr, 6407, "qhull internal error (qh_checkpolygon): missing vertex neighbors for v%d\n", vertex->id); + waserror= True; + } qh_setcheck(vertex->neighbors, "neighbors for v", vertex->id); if (vertex->deleted) continue; totvneighbors += qh_setsize(vertex->neighbors); } - FORALLfacet_(facetlist) - totvertices += qh_setsize(facet->vertices); - if (totvneighbors != totvertices) { - qh_fprintf(qh ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent. Totvneighbors %d, totvertices %d\n", - totvneighbors, totvertices); + FORALLfacet_(facetlist) { + if (!facet->visible) + totfacetvertices += qh_setsize(facet->vertices); + } + if (totvneighbors != totfacetvertices) { + qh_fprintf(qh ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent (tot_vneighbors %d != tot_facetvertices %d). Maybe duplicate or missing vertex\n", + totvneighbors, totfacetvertices); waserror= True; + FORALLvertices { + if (vertex->deleted) + continue; + qh visit_id++; + FOREACHneighbor_(vertex) { + if (neighbor->visitid==qh visit_id) { + qh_fprintf(qh ferr, 6275, "qhull internal error (qh_checkpolygon): facet f%d occurs twice in neighbors of vertex v%d\n", + neighbor->id, vertex->id); + errorfacet2= errorfacet; + errorfacet= neighbor; + } + neighbor->visitid= qh visit_id; + if (!qh_setin(neighbor->vertices, vertex)) { + qh_fprintf(qh ferr, 6276, "qhull internal error (qh_checkpolygon): facet f%d is a neighbor of vertex v%d but v%d is not a vertex of f%d\n", + neighbor->id, vertex->id, vertex->id, neighbor->id); + errorfacet2= errorfacet; + errorfacet= neighbor; + } + } + } + FORALLfacet_(facetlist){ + if (!facet->visible) { + /* vertices are inverse sorted and are unlikely to be duplicated */ + FOREACHvertex_(facet->vertices){ + if (!qh_setin(vertex->neighbors, facet)) { + qh_fprintf(qh ferr, 6277, "qhull internal error (qh_checkpolygon): v%d is a vertex of facet f%d but f%d is not a neighbor of v%d\n", + vertex->id, facet->id, facet->id, vertex->id); + errorfacet2= errorfacet; + errorfacet= facet; + } + } + } + } } } if (numvertices != qh num_vertices - qh_setsize(qh del_vertices)) { @@ -977,28 +1376,31 @@ void qh_checkpolygon(facetT *facetlist) { waserror= True; } if (qh hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) { - qh_fprintf(qh ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\ - A vertex appears twice in a edge list. May occur during merging.", - numvertices, numfacets, numridges/2); + qh_fprintf(qh ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2. A vertex appears twice in a edge list. May occur during merging.\n", + numvertices, numfacets, numridges/2); /* occurs if lots of merging and a vertex ends up twice in an edge list. e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */ } } if (waserror) - qh_errexit(qh_ERRqhull, NULL, NULL); + qh_errexit2(qh_ERRqhull, errorfacet, errorfacet2); } /* checkpolygon */ /*--------------------------------- - qh_checkvertex( vertex ) + qh_checkvertex( vertex, allchecks, &waserror ) check vertex for consistency - checks vertex->neighbors + if allchecks, checks vertex->neighbors + + returns: + sets waserror if any error occurs notes: - neighbors checked efficiently in checkpolygon + called by qh_tracemerge and qh_checkpolygon + neighbors checked efficiently in qh_checkpolygon */ -void qh_checkvertex(vertexT *vertex) { +void qh_checkvertex(vertexT *vertex, boolT allchecks, boolT *waserrorp) { boolT waserror= False; facetT *neighbor, **neighborp, *errfacet=NULL; @@ -1007,10 +1409,14 @@ void qh_checkvertex(vertexT *vertex) { waserror= True; } if (vertex->id >= qh vertex_id) { - qh_fprintf(qh ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id); + qh_fprintf(qh ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id v%d >= qh.vertex_id (%d)\n", vertex->id, qh vertex_id); waserror= True; } - if (!waserror && !vertex->deleted) { + if (vertex->visitid > qh vertex_visit) { + qh_fprintf(qh ferr, 6413, "qhull internal error (qh_checkvertex): expecting v%d.visitid <= qh.vertex_visit (%d). Got visitid %d\n", vertex->id, qh vertex_visit, vertex->visitid); + waserror= True; + } + if (allchecks && !waserror && !vertex->deleted) { if (qh_setsize(vertex->neighbors)) { FOREACHneighbor_(vertex) { if (!qh_setin(neighbor->vertices, vertex)) { @@ -1023,7 +1429,9 @@ void qh_checkvertex(vertexT *vertex) { } if (waserror) { qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex); - qh_errexit(qh_ERRqhull, errfacet, NULL); + if (errfacet) + qh_errexit(qh_ERRqhull, errfacet, NULL); + *waserrorp= True; } } /* checkvertex */ @@ -1069,31 +1477,28 @@ void qh_clearcenters(qh_CENTER type) { returns: initializes qh.facet_list to the simplex - initializes qh.newfacet_list, .facet_tail - initializes qh.vertex_list, .newvertex_list, .vertex_tail + + notes: + only called by qh_initialhull design: - initializes lists for each vertex create a new facet for each new facet create its neighbor set */ -void qh_createsimplex(setT *vertices) { +void qh_createsimplex(setT *vertices /* qh.facet_list */) { facetT *facet= NULL, *newfacet; boolT toporient= True; int vertex_i, vertex_n, nth; setT *newfacets= qh_settemp(qh hull_dim+1); vertexT *vertex; - qh facet_list= qh newfacet_list= qh facet_tail= qh_newfacet(); - qh num_facets= qh num_vertices= qh num_visible= 0; - qh vertex_list= qh newvertex_list= qh vertex_tail= qh_newvertex(NULL); FOREACHvertex_i_(vertices) { newfacet= qh_newfacet(); - newfacet->vertices= qh_setnew_delnthsorted(vertices, vertex_n, - vertex_i, 0); - newfacet->toporient= (unsigned char)toporient; + newfacet->vertices= qh_setnew_delnthsorted(vertices, vertex_n, vertex_i, 0); + if (toporient) + newfacet->toporient= True; qh_appendfacet(newfacet); newfacet->newfacet= True; qh_appendvertex(vertex); @@ -1116,23 +1521,19 @@ void qh_createsimplex(setT *vertices) { >-------------------------------- qh_delridge( ridge ) - deletes ridge from data structures it belongs to - frees up its memory + delete a ridge's vertices and frees its memory notes: - in merge.c, caller sets vertex->delridge for each vertex - ridges also freed in qh_freeqhull + assumes r.top->ridges and r.bottom->ridges have been updated */ void qh_delridge(ridgeT *ridge) { - void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ - qh_setdel(ridge->top->ridges, ridge); - qh_setdel(ridge->bottom->ridges, ridge); + if (ridge == qh traceridge) + qh traceridge= NULL; qh_setfree(&(ridge->vertices)); - qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); + qh_memfree(ridge, (int)sizeof(ridgeT)); } /* delridge */ - /*--------------------------------- @@ -1145,6 +1546,11 @@ void qh_delridge(ridgeT *ridge) { */ void qh_delvertex(vertexT *vertex) { + if (vertex->deleted && !vertex->partitioned && !qh NOerrexit) { + qh_fprintf(qh ferr, 6395, "qhull internal error (qh_delvertex): vertex v%d was deleted but it was not partitioned as a coplanar point\n", + vertex->id); + qh_errexit(qh_ERRqhull, NULL, NULL); + } if (vertex == qh tracevertex) qh tracevertex= NULL; qh_removevertex(vertex); @@ -1156,7 +1562,7 @@ void qh_delvertex(vertexT *vertex) { /*--------------------------------- - qh_facet3vertex( ) + qh_facet3vertex( ) return temporary set of 3-d vertices in qh_ORIENTclock order design: @@ -1223,9 +1629,13 @@ setT *qh_facet3vertex(facetT *facet) { isoutside set if outside of facet notes: + Distance is measured by distance to the facet's hyperplane. For + Delaunay facets, this is not the same as the containing facet. It may + be an adjacent facet or a different tricoplanar facet. See + locate a facet with qh_findbestfacet() + For tricoplanar facets, this finds one of the tricoplanar facets closest - to the point. For Delaunay triangulations, the point may be inside a - different tricoplanar facet. See locate a facet with qh_findbestfacet() + to the point. If inside, qh_findbestfacet performs an exhaustive search this may be too conservative. Sometimes it is clearly required. @@ -1245,7 +1655,7 @@ facetT *qh_findbestfacet(pointT *point, boolT bestoutside, bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */, bestdist, isoutside, &totpart); if (*bestdist < -qh DISTround) { - bestfacet= qh_findfacet_all(point, bestdist, isoutside, &numpart); + bestfacet= qh_findfacet_all(point, !qh_NOupper, bestdist, isoutside, &numpart); totpart += numpart; if ((isoutside && *isoutside && bestoutside) || (isoutside && !*isoutside && bestfacet->upperdelaunay)) { @@ -1256,7 +1666,7 @@ facetT *qh_findbestfacet(pointT *point, boolT bestoutside, } } trace3((qh ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n", - bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart)); + bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart)); return bestfacet; } /* findbestfacet */ @@ -1271,8 +1681,8 @@ facetT *qh_findbestfacet(pointT *point, boolT bestoutside, 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 + if Delaunay and inside, point is outside of circumsphere of bestfacet */ facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) { @@ -1316,7 +1726,7 @@ facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, in trace3((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(point, &bestdist, &isoutside, numpart); + bestfacet= qh_findfacet_all(point, qh_NOupper, &bestdist, &isoutside, numpart); } *bestdistp= bestdist; trace3((qh ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n", @@ -1327,8 +1737,9 @@ facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, in /*--------------------------------- - qh_findfacet_all( point, bestdist, isoutside, numpart ) + qh_findfacet_all( point, noupper, bestdist, isoutside, numpart ) exhaustive search for facet below a point + ignore flipped and visible facets, f.normal==NULL, and if noupper, f.upperdelaunay facets for Delaunay triangulations, Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed @@ -1343,9 +1754,10 @@ facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, in number of distance tests notes: - primarily for library users, rarely used by Qhull + called by qh_findbestlower if all neighbors are flipped or upper Delaunay (QH3025) + primarily for library users (qh_findbestfacet), rarely used by Qhull */ -facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, +facetT *qh_findfacet_all(pointT *point, boolT noupper, realT *bestdist, boolT *isoutside, int *numpart) { facetT *bestfacet= NULL, *facet; realT dist; @@ -1354,7 +1766,9 @@ facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, *bestdist= -REALmax; *isoutside= False; FORALLfacets { - if (facet->flipped || !facet->normal) + if (facet->flipped || !facet->normal || facet->visible) + continue; + if (noupper && facet->upperdelaunay) continue; totpart++; qh_distplane(point, facet, &dist); @@ -1368,8 +1782,8 @@ facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, } } *numpart= totpart; - trace3((qh ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n", - getid_(bestfacet), *bestdist, *isoutside, totpart)); + trace3((qh ferr, 3016, "qh_findfacet_all: p%d, noupper? %d, f%d, dist %2.2g, isoutside %d, totpart %d\n", + qh_pointid(point), noupper, getid_(bestfacet), *bestdist, *isoutside, totpart)); return bestfacet; } /* findfacet_all */ @@ -1377,17 +1791,17 @@ facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, >-------------------------------- qh_findgood( facetlist, goodhorizon ) - identify good facets for qh.PRINTgood - if qh.GOODvertex>0 + identify good facets for qh.PRINTgood and qh_buildcone_onlygood + goodhorizon is count of good, horizon facets from qh_find_horizon, otherwise 0 from qh_findgood_all + if not qh.MERGING and 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 + selects facet with closest angle to thresholds sets GOODclosest returns: @@ -1396,14 +1810,16 @@ facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, may update qh.GOODclosest notes: - qh_findgood_all further reduces the good region + called from qh_initbuild, qh_buildcone_onlygood, and qh_findgood_all + qh_findgood_all (called from qh_prepare_output) 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 + if not merging, clear good facets that fail qh.GOODvertex ('QVn', but not 'QV-n') + clear good facets that fail qh.GOODpoint ('QGn' or 'QG-n') + clear good facets that fail qh.GOODthreshold + if !goodhorizon and !find f.good, + sets GOODclosest to facet with closest angle to thresholds */ int qh_findgood(facetT *facetlist, int goodhorizon) { facetT *facet, *bestfacet= NULL; @@ -1416,7 +1832,7 @@ int qh_findgood(facetT *facetlist, int goodhorizon) { } if (qh GOODvertex>0 && !qh MERGING) { FORALLfacet_(facetlist) { - if (!qh_isvertex(qh GOODvertexp, facet->vertices)) { + if (facet->good && !qh_isvertex(qh GOODvertexp, facet->vertices)) { facet->good= False; numgood--; } @@ -1447,7 +1863,7 @@ int qh_findgood(facetT *facetlist, int goodhorizon) { } } } - if (!numgood && (!goodhorizon || qh GOODclosest)) { + if (numgood == 0 && (goodhorizon == 0 || qh GOODclosest)) { if (qh GOODclosest) { if (qh GOODclosest->visible) qh GOODclosest= NULL; @@ -1457,7 +1873,7 @@ int qh_findgood(facetT *facetlist, int goodhorizon) { bestfacet= qh GOODclosest; } } - if (bestfacet && bestfacet != qh GOODclosest) { + if (bestfacet && bestfacet != qh GOODclosest) { /* numgood == 0 */ if (qh GOODclosest) qh GOODclosest->good= False; qh GOODclosest= bestfacet; @@ -1473,8 +1889,8 @@ int qh_findgood(facetT *facetlist, int goodhorizon) { } } zadd_(Zgoodfacet, numgood); - trace2((qh ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n", - numgood, goodhorizon)); + trace2((qh ferr, 2045, "qh_findgood: found %d good facets with %d good horizon and qh.GOODclosest f%d\n", + numgood, goodhorizon, getid_(qh GOODclosest))); if (!numgood && qh GOODvertex>0 && !qh MERGING) return goodhorizon; return numgood; @@ -1488,7 +1904,7 @@ int qh_findgood(facetT *facetlist, int goodhorizon) { 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 + if qh.SPLITthresholds (e.g., qh.DELAUNAY) facet->normal matches threshold, or if none, the closest one calls qh_findgood nop if good not used @@ -1498,12 +1914,14 @@ int qh_findgood(facetT *facetlist, int goodhorizon) { sets qh.num_good notes: - this is like qh_findgood but more restrictive + called by qh_prepare_output and qh_printneighborhood + unless qh.ONLYgood, calls qh_findgood first design: uses qh_findgood to mark good facets - marks facets for qh.GOODvertex - marks facets for qh.SPLITthreholds + clear f.good for failed qh.GOODvertex + clear f.good for failed qh.SPLITthreholds + if no more good facets, select best of qh.SPLITthresholds */ void qh_findgood_all(facetT *facetlist) { facetT *facet, *bestfacet=NULL; @@ -1521,7 +1939,7 @@ void qh_findgood_all(facetT *facetlist) { } if (qh GOODvertex <0 || (qh GOODvertex > 0 && qh MERGING)) { FORALLfacet_(facetlist) { - if (facet->good && ((qh GOODvertex > 0) ^ !!qh_isvertex(qh GOODvertexp, facet->vertices))) { + if (facet->good && ((qh GOODvertex > 0) ^ !!qh_isvertex(qh GOODvertexp, facet->vertices))) { /* convert to bool */ if (!--numgood) { if (qh ONLYgood) { qh_fprintf(qh ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n", @@ -1555,11 +1973,17 @@ void qh_findgood_all(facetT *facetlist) { if (!numgood && bestfacet) { bestfacet->good= True; numgood++; - trace0((qh ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n", + trace0((qh ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to split thresholds\n", bestfacet->id, bestangle)); return; } } + if (numgood == 1 && !qh PRINTgood && qh GOODclosest && qh GOODclosest->good) { + trace2((qh ferr, 2109, "qh_findgood_all: undo selection of qh.GOODclosest f%d since it would fail qh_inthresholds in qh_skipfacet\n", + qh GOODclosest->id)); + qh GOODclosest->good= False; + numgood= 0; + } qh num_good= numgood; trace0((qh ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n", numgood, startgood)); @@ -1583,7 +2007,7 @@ void qh_furthestnext(void /* qh.facet_list */) { if (facet->outsideset) { #if qh_COMPUTEfurthest pointT *furthest; - furthest= (pointT*)qh_setlast(facet->outsideset); + furthest= (pointT *)qh_setlast(facet->outsideset); zinc_(Zcomputefurthest); qh_distplane(furthest, facet, &dist); #else @@ -1651,7 +2075,7 @@ void qh_furthestout(facetT *facet) { */ void qh_infiniteloop(facetT *facet) { - qh_fprintf(qh ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n"); + qh_fprintf(qh ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected. If visible, f.replace. If newfacet, f.samecycle\n"); qh_errexit(qh_ERRqhull, facet, NULL); } /* qh_infiniteloop */ @@ -1673,6 +2097,9 @@ void qh_infiniteloop(facetT *facet) { 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') + initialize qh.newfacet_list, qh.facet_tail + initialize qh.vertex_list, qh.newvertex_list, qh.vertex_tail + determine initial vertices build initial simplex partition input points into facets of initial simplex set up lists @@ -1680,15 +2107,24 @@ void qh_infiniteloop(facetT *facet) { check consistency add qh.GOODvertex if defined */ -void qh_initbuild( void) { +void qh_initbuild(void) { setT *maxpoints, *vertices; facetT *facet; int i, numpart; realT dist; boolT isoutside; + if (qh PRINTstatistics) { + qh_fprintf(qh ferr, 9350, "qhull %s Statistics: %s | %s\n", + qh_version, qh rbox_command, qh qhull_command); + fflush(NULL); + } qh furthest_id= qh_IDunknown; qh lastreport= 0; + qh lastfacets= 0; + qh lastmerges= 0; + qh lastplanes= 0; + qh lastdist= 0; qh facet_id= qh vertex_id= qh ridge_id= 0; qh visit_id= qh vertex_visit= 0; qh maxoutdone= False; @@ -1704,17 +2140,16 @@ void qh_initbuild( void) { if ((qh GOODpoint && (qh GOODpointp < qh first_point /* also catches !GOODpointp */ || qh GOODpointp > qh_point(qh num_points-1))) - || (qh GOODvertex - && (qh GOODvertexp < qh first_point /* also catches !GOODvertexp */ - || qh GOODvertexp > qh_point(qh num_points-1)))) { + || (qh GOODvertex + && (qh GOODvertexp < qh first_point /* also catches !GOODvertexp */ + || qh GOODvertexp > qh_point(qh num_points-1)))) { qh_fprintf(qh ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n", qh num_points-1); qh_errexit(qh_ERRinput, NULL, NULL); } maxpoints= qh_maxmin(qh first_point, qh num_points, qh hull_dim); if (qh SCALElast) - qh_scalelast(qh first_point, qh num_points, qh hull_dim, - qh MINlastcoord, qh MAXlastcoord, qh MAXwidth); + qh_scalelast(qh first_point, qh num_points, qh hull_dim, qh MINlastcoord, qh MAXlastcoord, qh MAXabs_coord); qh_detroundoff(); if (qh DELAUNAY && qh upper_threshold[qh hull_dim-1] > REALmax/2 && qh lower_threshold[qh hull_dim-1] < -REALmax/2) { @@ -1735,21 +2170,25 @@ void qh_initbuild( void) { } } } + trace4((qh ferr, 4091, "qh_initbuild: create sentinels for qh.facet_tail and qh.vertex_tail\n")); + qh facet_list= qh newfacet_list= qh facet_tail= qh_newfacet(); + qh num_facets= qh num_vertices= qh num_visible= 0; + qh vertex_list= qh newvertex_list= qh vertex_tail= qh_newvertex(NULL); vertices= qh_initialvertices(qh hull_dim, maxpoints, qh first_point, qh num_points); qh_initialhull(vertices); /* initial qh facet_list */ qh_partitionall(vertices, qh first_point, qh num_points); if (qh PRINToptions1st || qh TRACElevel || qh IStracing) { if (qh TRACElevel || qh IStracing) - qh_fprintf(qh ferr, 8103, "\nTrace level %d for %s | %s\n", - qh IStracing ? qh IStracing : qh TRACElevel, qh rbox_command, qh qhull_command); + qh_fprintf(qh ferr, 8103, "\nTrace level T%d, IStracing %d, point TP%d, merge TM%d, dist TW%2.2g, qh.tracefacet_id %d, traceridge_id %d, tracevertex_id %d, last qh.RERUN %d, %s | %s\n", + qh TRACElevel, qh IStracing, qh TRACEpoint, qh TRACEmerge, qh TRACEdist, qh tracefacet_id, qh traceridge_id, qh tracevertex_id, qh TRACElastrun, qh rbox_command, qh qhull_command); qh_fprintf(qh ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options); } - qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + qh_resetlists(False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); qh facet_next= qh facet_list; qh_furthestnext(/* qh.facet_list */); if (qh PREmerge) { qh cos_max= qh premerge_cos; - qh centrum_radius= qh premerge_centrum; + qh centrum_radius= qh premerge_centrum; /* overwritten by qh_premerge */ } if (qh ONLYgood) { if (qh GOODvertex > 0 && qh MERGING) { @@ -1758,12 +2197,11 @@ void qh_initbuild( void) { } if (!(qh GOODthreshold || qh GOODpoint || (!qh MERGEexact && !qh PREmerge && qh GOODvertexp))) { - qh_fprintf(qh ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\ -good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n"); + qh_fprintf(qh ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n"); qh_errexit(qh_ERRinput, NULL, NULL); } if (qh GOODvertex > 0 && !qh MERGING /* matches qh_partitionall */ - && !qh_isvertex(qh GOODvertexp, vertices)) { + && !qh_isvertex(qh GOODvertexp, vertices)) { facet= qh_findbestnew(qh GOODvertexp, qh facet_list, &dist, !qh_ALL, &isoutside, &numpart); zadd_(Zdistgood, numpart); @@ -1791,6 +2229,9 @@ good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n"); qh_initialhull( vertices ) constructs the initial hull as a DIM3 simplex of vertices + notes: + only called by qh_initbuild + design: creates a simplex (initializes lists) determines orientation of simplex @@ -1801,49 +2242,68 @@ good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n"); */ void qh_initialhull(setT *vertices) { facetT *facet, *firstfacet, *neighbor, **neighborp; - realT dist, angle, minangle= REALmax; -#ifndef qh_NOtrace - int k; -#endif + realT angle, minangle= REALmax, dist; - qh_createsimplex(vertices); /* qh.facet_list */ + qh_createsimplex(vertices /* qh.facet_list */); qh_resetlists(False, qh_RESETvisible); qh facet_next= qh facet_list; /* advance facet when processed */ qh interior_point= qh_getcenter(vertices); + if (qh IStracing) { + qh_fprintf(qh ferr, 8105, "qh_initialhull: "); + qh_printpoint(qh ferr, "qh.interior_point", qh interior_point); + } firstfacet= qh facet_list; - qh_setfacetplane(firstfacet); - zinc_(Znumvisibility); /* needs to be in printsummary */ + qh_setfacetplane(firstfacet); /* qh_joggle_restart if flipped */ + if (firstfacet->flipped) { + trace1((qh ferr, 1065, "qh_initialhull: ignore f%d flipped. Test qh.interior_point (p-2) for clearly flipped\n", firstfacet->id)); + firstfacet->flipped= False; + } + zzinc_(Zdistcheck); qh_distplane(qh interior_point, firstfacet, &dist); - if (dist > 0) { + if (dist > qh DISTround) { /* clearly flipped */ + trace1((qh ferr, 1060, "qh_initialhull: initial orientation incorrect, qh.interior_point is %2.2g from f%d. Reversing orientation of all facets\n", + dist, firstfacet->id)); FORALLfacets facet->toporient ^= (unsigned char)True; + qh_setfacetplane(firstfacet); } - FORALLfacets - qh_setfacetplane(facet); FORALLfacets { - if (!qh_checkflipped(facet, NULL, qh_ALL)) {/* due to axis-parallel facet */ - trace1((qh ferr, 1031, "qh_initialhull: initial orientation incorrect. Correct all facets\n")); + if (facet != firstfacet) + qh_setfacetplane(facet); /* qh_joggle_restart if flipped */ + } + FORALLfacets { + if (facet->flipped) { + trace1((qh ferr, 1066, "qh_initialhull: ignore f%d flipped. Test qh.interior_point (p-2) for clearly flipped\n", facet->id)); facet->flipped= False; - FORALLfacets { + } + zzinc_(Zdistcheck); + qh_distplane(qh interior_point, facet, &dist); /* duplicates qh_setfacetplane */ + if (dist > qh DISTround) { /* clearly flipped, due to axis-parallel facet or coplanar firstfacet */ + trace1((qh ferr, 1031, "qh_initialhull: initial orientation incorrect, qh.interior_point is %2.2g from f%d. Either axis-parallel facet or coplanar firstfacet f%d. Force outside orientation of all facets\n")); + FORALLfacets { /* reuse facet, then 'break' */ + facet->flipped= False; facet->toporient ^= (unsigned char)True; - qh_orientoutside(facet); + qh_orientoutside(facet); /* force outside orientation for f.normal */ } break; } } FORALLfacets { - if (!qh_checkflipped(facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */ + if (!qh_checkflipped(facet, NULL, qh_ALL)) { if (qh DELAUNAY && ! qh ATinfinity) { + qh_joggle_restart("initial Delaunay cocircular or cospherical"); if (qh UPPERdelaunay) - qh_fprintf(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"); + qh_fprintf(qh ferr, 6240, "Qhull precision error: initial Delaunay input sites are cocircular or cospherical. Option 'Qs' searches all points. Use option 'QJ' to joggle the input, otherwise cannot compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n"); else - qh_fprintf(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_fprintf(qh ferr, 6239, "Qhull precision error: initial Delaunay input sites are cocircular or cospherical. Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points; it adds a point \"at infinity\". Alternatively use option 'QJ' to joggle the input. Use option 'Qs' to search all points for the initial simplex.\n"); + qh_printvertexlist(qh ferr, "\ninput sites with last coordinate projected to a paraboloid\n", qh facet_list, NULL, qh_ALL); qh_errexit(qh_ERRinput, NULL, NULL); + }else { + qh_joggle_restart("initial simplex is flat"); + qh_fprintf(qh ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n", + facet->id); + qh_errexit(qh_ERRsingular, NULL, NULL); /* calls qh_printhelp_singular */ } - qh_precision("initial simplex is flat"); - qh_fprintf(qh ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n", - facet->id); - qh_errexit(qh_ERRsingular, NULL, NULL); /* calls qh_printhelp_singular */ } FOREACHneighbor_(facet) { angle= qh_getangle(facet->normal, neighbor->normal); @@ -1860,15 +2320,10 @@ void qh_initialhull(setT *vertices) { } zzval_(Zprocessed)= qh hull_dim+1; qh_checkpolygon(qh facet_list); - qh_checkconvex(qh facet_list, qh_DATAfault); -#ifndef qh_NOtrace + qh_checkconvex(qh facet_list, qh_DATAfault); if (qh IStracing >= 1) { - qh_fprintf(qh ferr, 8105, "qh_initialhull: simplex constructed, interior point:"); - for (k=0; k < qh hull_dim; k++) - qh_fprintf(qh ferr, 8106, " %6.4g", qh interior_point[k]); - qh_fprintf(qh ferr, 8107, "\n"); + qh_fprintf(qh ferr, 8105, "qh_initialhull: simplex constructed\n"); } -#endif } /* initialhull */ /*-= dim) /* qh_maxsimplex for last point */ + break; + if (point_i & 0x1) { /* first try up to dim, max. coord. points */ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ qh_detsimplex(point, simplex, k, &nearzero); if (nearzero) qh_setappend(&tested, point); else { qh_setappend(&simplex, point); - if (++k == dim) /* use search for last point */ - break; + k++; } } } } - while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) { - if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ - qh_detsimplex(point, simplex, k, &nearzero); - if (nearzero) - qh_setappend(&tested, point); - else { - qh_setappend(&simplex, point); - k++; + FOREACHpoint_i_(maxpoints) { + if (k >= dim) /* qh_maxsimplex for last point */ + break; + if ((point_i & 0x1) == 0) { /* then test min. coord points */ + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(&tested, point); + else { + qh_setappend(&simplex, point); + k++; + } } } } + /* remove tested points from maxpoints */ + FOREACHpoint_i_(maxpoints) { + if (qh_setin(simplex, point) || qh_setin(tested, point)) + SETelem_(maxpoints, point_i)= NULL; + } + qh_setcompact(maxpoints); idx= 0; - while (k != dim && (point= qh_point(idx++))) { + while (k < dim && (point= qh_point(idx++))) { if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ qh_detsimplex(point, simplex, k, &nearzero); if (!nearzero){ @@ -1954,7 +2421,7 @@ setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints } qh_settempfree(&tested); qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex); - }else + }else /* qh.hull_dim < qh_INITIALmax */ qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex); FOREACHpoint_(simplex) qh_setaddnth(&vertices, 0, qh_newvertex(point)); /* descending order */ @@ -1989,10 +2456,11 @@ vertexT *qh_isvertex(pointT *point, setT *vertices) { make new facets from point and qh.visible_list returns: + apex (point) of the new facets qh.newfacet_list= list of new facets with hyperplanes and ->newfacet - qh.newvertex_list= list of vertices in new facets with ->newlist set + qh.newvertex_list= list of vertices in new facets with ->newfacet set - if (qh.ONLYgood) + if (qh.NEWtentative) newfacets reference horizon facets, but not vice versa ridges reference non-simplicial horizon ridges, but not vice versa does not change existing facets @@ -2004,9 +2472,9 @@ vertexT *qh_isvertex(pointT *point, setT *vertices) { see also: qh_makenewplanes() -- make hyperplanes for facets - qh_attachnewfacets() -- attachnewfacets if not done here(qh ONLYgood) + qh_attachnewfacets() -- attachnewfacets if not done here qh NEWtentative qh_matchnewfacets() -- match up neighbors - qh_updatevertices() -- update vertex neighbors and delvertices + qh_update_vertexneighbors() -- update vertex neighbors and delvertices qh_deletevisible() -- delete visible facets qh_checkpolygon() --check the result qh_triangulate() -- triangulate a non-simplicial facet @@ -2017,18 +2485,19 @@ vertexT *qh_isvertex(pointT *point, setT *vertices) { update its f.replace clear its neighbor set */ -vertexT *qh_makenewfacets(pointT *point /*visible_list*/) { +vertexT *qh_makenewfacets(pointT *point /* qh.visible_list */) { facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp; vertexT *apex; int numnew=0; + if (qh CHECKfrequently) { + qh_checkdelridge(); + } qh newfacet_list= qh facet_tail; qh newvertex_list= qh vertex_tail; apex= qh_newvertex(point); qh_appendvertex(apex); qh visit_id++; - if (!qh ONLYgood) - qh NEWfacets= True; FORALLvisible_facets { FOREACHneighbor_(visible) neighbor->seen= False; @@ -2038,139 +2507,262 @@ vertexT *qh_makenewfacets(pointT *point /*visible_list*/) { } if (visible->simplicial) newfacet= qh_makenew_simplicial(visible, apex, &numnew); - if (!qh ONLYgood) { + if (!qh NEWtentative) { if (newfacet2) /* newfacet is null if all ridges defined */ newfacet= newfacet2; if (newfacet) visible->f.replace= newfacet; else zinc_(Zinsidevisible); + if (visible->ridges) /* ridges and neighbors are no longer valid for visible facet */ + SETfirst_(visible->ridges)= NULL; SETfirst_(visible->neighbors)= NULL; } } - trace1((qh ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n", - numnew, qh_pointid(point))); + if (!qh NEWtentative) + qh NEWfacets= True; + trace1((qh ferr, 1032, "qh_makenewfacets: created %d new facets f%d..f%d from point p%d to horizon\n", + numnew, qh first_newfacet, qh facet_id-1, qh_pointid(point))); if (qh IStracing >= 4) qh_printfacetlist(qh newfacet_list, NULL, qh_ALL); return apex; } /* makenewfacets */ +#ifndef qh_NOmerge /*--------------------------------- + >-------------------------------- - qh_matchduplicates( atfacet, atskip, hashsize, hashcount ) - match duplicate ridges in qh.hash_table for atfacet/atskip + qh_matchdupridge( 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) + vertex-facet distance (>0.0) for qh_MERGEridge ridge updates hashcount + set newfacet, facet, matchfacet's hyperplane (removes from mergecycle of coplanarhorizon facets) see also: qh_matchneighbor notes: + only called by qh_matchnewfacets for qh_buildcone and qh_triangulate_facet + assumes atfacet is simplicial + assumes atfacet->neighbors @ atskip == qh_DUPLICATEridge + usually keeps ridge with the widest merge + both MRGdupridge and MRGflipped are required merges -- rbox 100 C1,2e-13 D4 t1 | qhull d Qbb + can merge flipped f11842 skip 3 into f11862 skip 2 and vice versa (forced by goodmatch/goodmatch2) + blocks -- cannot merge f11862 skip 2 and f11863 skip2 (the widest merge) + must block -- can merge f11843 skip 3 into f11842 flipped skip 3, but not vice versa + can merge f11843 skip 3 into f11863 skip 2, but not vice versa + working/unused.h: [jan'19] Dropped qh_matchdupridge_coplanarhorizon, it was the same or slightly worse. Complex addition, rarely occurs 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 + if it is a matching facet with the same orientation 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 + if it is a matching facet with the same orientation and pass 1 test if this is a better match if pass 1, make best match (it will not be merged) + set newfacet, facet, matchfacet's hyperplane (removes from mergecycle of coplanarhorizon facets) + */ -#ifndef qh_NOmerge -void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) { - boolT same, ismatch; +coordT qh_matchdupridge(facetT *atfacet, int atskip, int hashsize, int *hashcount) { + boolT same, ismatch, isduplicate= False; 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; + facetT *facet, *newfacet, *nextfacet; + facetT *maxmatch= NULL, *maxmatch2= NULL, *goodmatch= NULL, *goodmatch2= NULL; + int skip, newskip, nextskip= 0, makematch; + int maxskip= 0, maxskip2= 0, goodskip= 0, goodskip2= 0; + coordT maxdist= -REALmax, maxdist2= 0.0, dupdist, dupdist2, low, high, maxgood, gooddist= 0.0; + maxgood= qh_WIDEdupridge * (qh ONEmerge + qh DISTround); hash= qh_gethash(hashsize, atfacet->vertices, qh hull_dim, 1, SETelem_(atfacet->vertices, atskip)); - trace2((qh ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n", + trace2((qh ferr, 2046, "qh_matchdupridge: find dupridge matches for f%d skip %d hash %d hashcount %d\n", atfacet->id, atskip, hash, *hashcount)); - for (makematch= 0; makematch < 2; makematch++) { + for (makematch=0; makematch < 2; makematch++) { /* makematch is false on the first pass and 1 on the second */ qh visit_id++; - for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) { + for (newfacet=atfacet, newskip=atskip; newfacet; newfacet= nextfacet, newskip= nextskip) { zinc_(Zhashlookup); - nextfacet= NULL; + nextfacet= NULL; /* exit when ismatch found */ newfacet->visitid= qh visit_id; - for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT)); + for (scan=hash; (facet= SETelemt_(qh hash_table, scan, facetT)); scan= (++scan >= hashsize ? 0 : scan)) { if (!facet->dupridge || facet->visitid == qh visit_id) continue; zinc_(Zhashtests); if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + if (SETelem_(newfacet->vertices, newskip) == SETelem_(facet->vertices, skip)) { + trace3((qh ferr, 3053, "qh_matchdupridge: duplicate ridge due to duplicate facets (f%d skip %d and f%d skip %d) previously reported as QH7084. Maximize dupdist to force vertex merge\n", + newfacet->id, newskip, facet->id, skip)); + isduplicate= True; + } ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient)); if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) { - if (!makematch) { - qh_fprintf(qh ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n", - facet->id, skip, newfacet->id, newskip, hash); - qh_errexit2(qh_ERRqhull, facet, newfacet); - } - }else if (ismatch && makematch) { - if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) { - SETelem_(facet->neighbors, skip)= newfacet; - if (newfacet->tricoplanar) - SETelem_(newfacet->neighbors, newskip)= facet; - else - SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge; - *hashcount -= 2; /* removed two unmatched facets */ - trace4((qh ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n", - facet->id, skip, newfacet->id, newskip)); - } - }else if (ismatch) { - mindist= qh_getdistance(facet, newfacet, &low, &high); - dist2= qh_getdistance(newfacet, facet, &low, &high); - minimize_(mindist, dist2); - if (mindist > maxdist) { - maxdist= mindist; - maxmatch= facet; - maxskip= skip; - maxmatch2= newfacet; - maxskip2= newskip; + if (!makematch) { /* occurs if many merges, e.g., rbox 100 W0 C2,1e-13 D6 t1546872462 | qhull C0 Qt Tcv */ + qh_fprintf(qh ferr, 6155, "qhull topology error (qh_matchdupridge): missing qh_DUPLICATEridge at f%d skip %d for new f%d skip %d hash %d ismatch %d. Set by qh_matchneighbor\n", + facet->id, skip, newfacet->id, newskip, hash, ismatch); + qh_errexit2(qh_ERRtopology, facet, newfacet); } - trace3((qh ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n", - facet->id, skip, newfacet->id, newskip, mindist, - maxmatch->id, maxmatch2->id)); - }else { /* !ismatch */ + }else if (!ismatch) { nextfacet= facet; nextskip= skip; + }else if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) { + if (makematch) { + if (newfacet->tricoplanar) { + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= facet; + *hashcount -= 2; /* removed two unmatched facets */ + trace2((qh ferr, 2075, "qh_matchdupridge: allow tricoplanar dupridge for new f%d skip %d and f%d skip %d\n", + newfacet->id, newskip, facet->id, skip)); + }else if (goodmatch && goodmatch2) { + SETelem_(goodmatch2->neighbors, goodskip2)= qh_MERGEridge; /* undo selection of goodmatch */ + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= facet; + *hashcount -= 2; /* removed two unmatched facets */ + trace2((qh ferr, 2105, "qh_matchdupridge: make good forced merge of dupridge f%d skip %d into f%d skip %d, keep new f%d skip %d and f%d skip %d, dist %4.4g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, newfacet->id, newskip, facet->id, skip, gooddist)); + goodmatch2= NULL; + }else { + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge; /* resolved by qh_mark_dupridges */ + *hashcount -= 2; /* removed two unmatched facets */ + trace3((qh ferr, 3073, "qh_matchdupridge: make forced merge of dupridge for new f%d skip %d and f%d skip %d, maxdist %4.4g in qh_forcedmerges\n", + newfacet->id, newskip, facet->id, skip, maxdist2)); + } + }else { /* !makematch */ + if (!facet->normal) + qh_setfacetplane(facet); /* qh_mergecycle will ignore 'mergehorizon' facets with normals, too many cases otherwise */ + if (!newfacet->normal) + qh_setfacetplane(newfacet); + dupdist= qh_getdistance(facet, newfacet, &low, &high); /* ignore low/high */ + dupdist2= qh_getdistance(newfacet, facet, &low, &high); + if (isduplicate) { + goodmatch= NULL; + minimize_(dupdist, dupdist2); + maxdist= dupdist; + maxdist2= REALmax/2; + maxmatch= facet; + maxskip= skip; + maxmatch2= newfacet; + maxskip2= newskip; + break; /* force maxmatch */ + }else if (facet->flipped && !newfacet->flipped && dupdist < maxgood) { + if (!goodmatch || !goodmatch->flipped || dupdist < gooddist) { + goodmatch= facet; + goodskip= skip; + goodmatch2= newfacet; + goodskip2= newskip; + gooddist= dupdist; + trace3((qh ferr, 3070, "qh_matchdupridge: try good dupridge flipped f%d skip %d into new f%d skip %d at dist %2.2g otherdist %2.2g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, gooddist, dupdist2)); + } + }else if (newfacet->flipped && !facet->flipped && dupdist2 < maxgood) { + if (!goodmatch || !goodmatch->flipped || dupdist2 < gooddist) { + goodmatch= newfacet; + goodskip= newskip; + goodmatch2= facet; + goodskip2= skip; + gooddist= dupdist2; + trace3((qh ferr, 3071, "qh_matchdupridge: try good dupridge flipped new f%d skip %d into f%d skip %d at dist %2.2g otherdist %2.2g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, gooddist, dupdist)); + } + }else if (dupdist < maxgood && (!newfacet->flipped || facet->flipped)) { /* disallow not-flipped->flipped */ + if (!goodmatch || (!goodmatch->flipped && dupdist < gooddist)) { + goodmatch= facet; + goodskip= skip; + goodmatch2= newfacet; + goodskip2= newskip; + gooddist= dupdist; + trace3((qh ferr, 3072, "qh_matchdupridge: try good dupridge f%d skip %d into new f%d skip %d at dist %2.2g otherdist %2.2g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, gooddist, dupdist2)); + } + }else if (dupdist2 < maxgood && (!facet->flipped || newfacet->flipped)) { /* disallow not-flipped->flipped */ + if (!goodmatch || (!goodmatch->flipped && dupdist2 < gooddist)) { + goodmatch= newfacet; + goodskip= newskip; + goodmatch2= facet; + goodskip2= skip; + gooddist= dupdist2; + trace3((qh ferr, 3018, "qh_matchdupridge: try good dupridge new f%d skip %d into f%d skip %d at dist %2.2g otherdist %2.2g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, gooddist, dupdist)); + } + }else if (!goodmatch) { /* otherwise match the furthest apart facets */ + if (!newfacet->flipped || facet->flipped) { + minimize_(dupdist, dupdist2); + } + if (dupdist > maxdist) { /* could keep !flipped->flipped, but probably lost anyway */ + maxdist2= maxdist; + maxdist= dupdist; + maxmatch= facet; + maxskip= skip; + maxmatch2= newfacet; + maxskip2= newskip; + trace3((qh ferr, 3055, "qh_matchdupridge: try furthest dupridge f%d skip %d new f%d skip %d at dist %2.2g\n", + maxmatch->id, maxskip, maxmatch2->id, maxskip2, maxdist)); + }else if (dupdist > maxdist2) + maxdist2= dupdist; + } + } } } - if (makematch && !facet - && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) { - qh_fprintf(qh ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n", - newfacet->id, newskip, hash); - qh_errexit(qh_ERRqhull, newfacet, NULL); - } + } /* end of foreach entry in qh.hash_table starting at 'hash' */ + if (makematch && SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) { + qh_fprintf(qh ferr, 6156, "qhull internal error (qh_matchdupridge): no MERGEridge match for dupridge new f%d skip %d at hash %d..%d\n", + newfacet->id, newskip, hash, scan); + qh_errexit(qh_ERRqhull, newfacet, NULL); } - } /* end of for each new facet at hash */ + } /* end of foreach newfacet at 'hash' */ if (!makematch) { - if (!maxmatch) { - qh_fprintf(qh ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n", - atfacet->id, atskip, hash); + if (!maxmatch && !goodmatch) { + qh_fprintf(qh ferr, 6157, "qhull internal error (qh_matchdupridge): no maximum or good match for dupridge new f%d skip %d at hash %d..%d\n", + atfacet->id, atskip, hash, scan); qh_errexit(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 ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n", - maxmatch->id, maxskip, maxmatch2->id, maxskip2)); - qh_precision("ridge with multiple neighbors"); - if (qh IStracing >= 4) - qh_errprint("DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL); + if (goodmatch) { + SETelem_(goodmatch->neighbors, goodskip)= goodmatch2; + SETelem_(goodmatch2->neighbors, goodskip2)= goodmatch; + *hashcount -= 2; /* removed two unmatched facets */ + if (goodmatch->flipped) { + if (!goodmatch2->flipped) { + zzinc_(Zflipridge); + }else { + zzinc_(Zflipridge2); + /* qh_joggle_restart called by qh_matchneighbor if qh_DUPLICATEridge */ + } + } + /* previously traced */ + }else { + SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; /* maxmatch!=NULL by QH6157 */ + SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch; + *hashcount -= 2; /* removed two unmatched facets */ + zzinc_(Zmultiridge); + /* qh_joggle_restart called by qh_matchneighbor if qh_DUPLICATEridge */ + trace0((qh ferr, 25, "qh_matchdupridge: keep dupridge f%d skip %d and f%d skip %d, dist %4.4g\n", + maxmatch2->id, maxskip2, maxmatch->id, maxskip, maxdist)); + } } } -} /* matchduplicates */ + if (goodmatch) + return gooddist; + return maxdist2; +} /* matchdupridge */ + +#else /* qh_NOmerge */ +coordT qh_matchdupridge(facetT *atfacet, int atskip, int hashsize, int *hashcount) { + QHULL_UNUSED(atfacet) + QHULL_UNUSED(atskip) + QHULL_UNUSED(hashsize) + QHULL_UNUSED(hashcount) + + return 0.0; +} +#endif /* qh_NOmerge */ /*--------------------------------- @@ -2188,7 +2780,8 @@ void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcoun notes: used for qh.PREmerge and qh.JOGGLEmax - must agree with computation of qh.NEARcoplanar in qh_detroundoff() + must agree with computation of qh.NEARcoplanar in qh_detroundoff + design: if not keeping coplanar or inside points free all coplanar sets @@ -2204,7 +2797,7 @@ void qh_nearcoplanar(void /* qh.facet_list */) { if (!qh KEEPcoplanar && !qh KEEPinside) { FORALLfacets { if (facet->coplanarset) - qh_setfree( &facet->coplanarset); + qh_setfree(&facet->coplanarset); } }else if (!qh KEEPcoplanar || !qh KEEPinside) { qh_outerinner(NULL, NULL, &innerplane); @@ -2334,8 +2927,8 @@ vertexT *qh_newvertex(pointT *point) { memset((char *) vertex, (size_t)0, sizeof(vertexT)); if (qh vertex_id == UINT_MAX) { qh_memfree(vertex, (int)sizeof(vertexT)); - qh_fprintf(qh ferr, 6159, "qhull error: more than 2^32 vertices. vertexT.id field overflows. Vertices would not be sorted correctly.\n"); - qh_errexit(qh_ERRqhull, NULL, NULL); + qh_fprintf(qh ferr, 6159, "qhull error: 2^32 or more vertices. vertexT.id field overflows. Vertices would not be sorted correctly.\n"); + qh_errexit(qh_ERRother, NULL, NULL); } if (qh vertex_id == qh tracevertex_id) qh tracevertex= vertex; @@ -2346,10 +2939,38 @@ vertexT *qh_newvertex(pointT *point) { return(vertex); } /* newvertex */ +/*--------------------------------- + + qh_nextfacet2d( facet, &nextvertex ) + return next facet and vertex for a 2d facet in qh_ORIENTclock order + returns NULL on error + + notes: + in qh_ORIENTclock order (default counter-clockwise) + nextvertex is in between the two facets + does not use qhT or qh_errexit [QhullFacet.cpp] + + design: + see io.c/qh_printextremes_2d +*/ +facetT *qh_nextfacet2d(facetT *facet, vertexT **nextvertexp) { + facetT *nextfacet; + + if (facet->toporient ^ qh_ORIENTclock) { + *nextvertexp= SETfirstt_(facet->vertices, vertexT); + nextfacet= SETfirstt_(facet->neighbors, facetT); + }else { + *nextvertexp= SETsecondt_(facet->vertices, vertexT); + nextfacet= SETsecondt_(facet->neighbors, facetT); + } + return nextfacet; +} /* nextfacet2d */ + /*--------------------------------- - qh_nextridge3d( atridge, facet, vertex ) + qh_nextridge3d( atridge, facet, &vertex ) return next ridge and vertex for a 3d facet returns NULL on error [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qh_qh. @@ -2391,14 +3012,34 @@ ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) { } return NULL; } /* nextridge3d */ -#else /* qh_NOmerge */ -void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) { -} -ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) { - return NULL; -} -#endif /* qh_NOmerge */ +/*--------------------------------- + + qh_opposite_vertex( facetA, neighbor ) + return the opposite vertex in facetA to neighbor + +*/ +vertexT *qh_opposite_vertex(facetT *facetA, facetT *neighbor) { + vertexT *opposite= NULL; + facetT *facet; + int facet_i, facet_n; + + if (facetA->simplicial) { + FOREACHfacet_i_(facetA->neighbors) { + if (facet == neighbor) { + opposite= SETelemt_(facetA->vertices, facet_i, vertexT); + break; + } + } + } + if (!opposite) { + qh_fprintf(qh ferr, 6396, "qhull internal error (qh_opposite_vertex): opposite vertex in facet f%d to neighbor f%d is not defined. Either is facet is not simplicial or neighbor not found\n", + facetA->id, neighbor->id); + qh_errexit2(qh_ERRqhull, facetA, neighbor); + } + return opposite; +} /* opposite_vertex */ /*--------------------------------- @@ -2426,7 +3067,7 @@ void qh_outcoplanar(void /* facet_list */) { if (qh KEEPcoplanar || qh KEEPnearinside) { qh_distplane(point, facet, &dist); zinc_(Zpartition); - qh_partitioncoplanar(point, facet, &dist); + qh_partitioncoplanar(point, facet, &dist, qh findbestnew); } } qh_setfree(&facet->outsideset); @@ -2440,7 +3081,7 @@ void qh_outcoplanar(void /* facet_list */) { return point for a point id, or NULL if unknown alternative code: - return((pointT *)((unsigned long)qh.first_point + return((pointT *)((unsigned long)qh.first_point + (unsigned long)((id)*qh.normal_size))); */ pointT *qh_point(int id) { @@ -2475,7 +3116,7 @@ void qh_point_add(setT *set, pointT *point, void *elem) { qh_fprintf(qh ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n", point, id); else if (id >= size) { - qh_fprintf(qh ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n", + qh_fprintf(qh ferr, 6160, "qhull internal error (point_add): point p%d is out of bounds(%d)\n", id, size); qh_errexit(qh_ERRqhull, NULL, NULL); }else @@ -2489,9 +3130,12 @@ void qh_point_add(setT *set, pointT *point, void *elem) { qh_pointfacet() return temporary set of facet for each point the set is indexed by point id + at most one facet per point, arbitrary selection notes: - vertices assigned to one of the facets + each point is assigned to at most one of vertices, coplanarset, or outsideset + unassigned points are interior points or + vertices assigned to one of its facets coplanarset assigned to the facet outside set assigned to the facet NULL if no facet for point (inside) @@ -2507,7 +3151,7 @@ void qh_point_add(setT *set, pointT *point, void *elem) { add each coplanar point add each outside point */ -setT *qh_pointfacet(void /*qh.facet_list*/) { +setT *qh_pointfacet(void /* qh.facet_list */) { int numpoints= qh num_points + qh_setsize(qh other_points); setT *facets; facetT *facet; @@ -2535,7 +3179,7 @@ setT *qh_pointfacet(void /*qh.facet_list*/) { /*--------------------------------- - qh_pointvertex( ) + qh_pointvertex( ) return temporary set of vertices indexed by point id entry is NULL if no vertex for a point this will include qh.GOODpointp @@ -2544,7 +3188,7 @@ setT *qh_pointfacet(void /*qh.facet_list*/) { FOREACHvertex_i_(vertices) { ... } SETelem_(vertices, i) */ -setT *qh_pointvertex(void /*qh.facet_list*/) { +setT *qh_pointvertex(void /* qh.facet_list */) { int numpoints= qh num_points + qh_setsize(qh other_points); setT *vertices; vertexT *vertex; @@ -2574,7 +3218,6 @@ setT *qh_pointvertex(void /*qh.facet_list*/) { void qh_prependfacet(facetT *facet, facetT **facetlist) { facetT *prevfacet, *list; - trace4((qh ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n", facet->id, getid_(*facetlist))); if (!*facetlist) @@ -2641,29 +3284,30 @@ void qh_printhashtable(FILE *fp) { } } /* printhashtable */ - /*--------------------------------- - qh_printlists( fp ) - print out facet and vertex list for debugging (without 'f/v' tags) + qh_printlists( ) + print out facet and vertex lists for debugging (without 'f/v' tags) + + notes: + not in I/O to avoid bringing io.c in */ void qh_printlists(void) { facetT *facet; vertexT *vertex; int count= 0; - qh_fprintf(qh ferr, 8108, "qh_printlists: facets:"); - FORALLfacets { + qh_fprintf(qh ferr, 3062, "qh_printlists: max_outside %2.2g all facets:", qh max_outside); + FORALLfacets{ if (++count % 100 == 0) qh_fprintf(qh ferr, 8109, "\n "); qh_fprintf(qh ferr, 8110, " %d", facet->id); } - qh_fprintf(qh ferr, 8111, "\n new facets %d visible facets %d next facet for qh_addpoint %d\n vertices(new %d):", - getid_(qh newfacet_list), getid_(qh visible_list), getid_(qh facet_next), - getid_(qh newvertex_list)); - count = 0; - FORALLvertices { + qh_fprintf(qh ferr, 8111, "\n qh.visible_list f%d, newfacet_list f%d, facet_next f%d for qh_addpoint\n qh.newvertex_list v%d all vertices:", + getid_(qh visible_list), getid_(qh newfacet_list), getid_(qh facet_next), getid_(qh newvertex_list)); + count= 0; + FORALLvertices{ if (++count % 100 == 0) qh_fprintf(qh ferr, 8112, "\n "); qh_fprintf(qh ferr, 8113, " %d", vertex->id); @@ -2671,22 +3315,84 @@ void qh_printlists(void) { qh_fprintf(qh ferr, 8114, "\n"); } /* printlists */ +/*--------------------------------- + + qh_replacefacetvertex( facet, oldvertex, newvertex ) + replace oldvertex with newvertex in f.vertices + vertices are inverse sorted by vertex->id + + returns: + toporient is flipped if an odd parity, position change + + notes: + for simplicial facets in qh_rename_adjacentvertex + see qh_addfacetvertex +*/ +void qh_replacefacetvertex(facetT *facet, vertexT *oldvertex, vertexT *newvertex) { + vertexT *vertex; + facetT *neighbor; + int vertex_i, vertex_n= 0; + int old_i= -1, new_i= -1; + + trace3((qh ferr, 3038, "qh_replacefacetvertex: replace v%d with v%d in f%d\n", oldvertex->id, newvertex->id, facet->id)); + if (!facet->simplicial) { + qh_fprintf(qh ferr, 6283, "qhull internal error (qh_replacefacetvertex): f%d is not simplicial\n", facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + FOREACHvertex_i_(facet->vertices) { + if (new_i == -1 && vertex->id < newvertex->id) { + new_i= vertex_i; + }else if (vertex->id == newvertex->id) { + qh_fprintf(qh ferr, 6281, "qhull internal error (qh_replacefacetvertex): f%d already contains new v%d\n", facet->id, newvertex->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (vertex->id == oldvertex->id) { + old_i= vertex_i; + } + } + if (old_i == -1) { + qh_fprintf(qh ferr, 6282, "qhull internal error (qh_replacefacetvertex): f%d does not contain old v%d\n", facet->id, oldvertex->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (new_i == -1) { + new_i= vertex_n; + } + if (old_i < new_i) + new_i--; + if ((old_i & 0x1) != (new_i & 0x1)) + facet->toporient ^= 1; + qh_setdelnthsorted(facet->vertices, old_i); + qh_setaddnth(&facet->vertices, new_i, newvertex); + neighbor= SETelemt_(facet->neighbors, old_i, facetT); + qh_setdelnthsorted(facet->neighbors, old_i); + qh_setaddnth(&facet->neighbors, new_i, neighbor); +} /* replacefacetvertex */ + /*--------------------------------- qh_resetlists( stats, qh_RESETvisible ) - reset newvertex_list, newfacet_list, visible_list + reset newvertex_list, newfacet_list, visible_list, NEWfacets, NEWtentative if stats, maintains statistics + if resetVisible, + visible_list is restored to facet_list + otherwise, f.visible/f.replace is retained returns: - visible_list is empty if qh_deletevisible was called + newvertex_list, newfacet_list, visible_list are NULL + + notes: + To delete visible facets, call qh_deletevisible before qh_resetlists */ -void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_list visible_list*/) { +void qh_resetlists(boolT stats, boolT resetVisible /* qh.newvertex_list newfacet_list visible_list */) { vertexT *vertex; facetT *newfacet, *visible; int totnew=0, totver=0; + trace2((qh ferr, 2066, "qh_resetlists: reset newvertex_list v%d, newfacet_list f%d, visible_list f%d, facet_list f%d next f%d vertex_list v%d -- NEWfacets? %d, NEWtentative? %d, stats? %d\n", + getid_(qh newvertex_list), getid_(qh newfacet_list), getid_(qh visible_list), getid_(qh facet_list), getid_(qh facet_next), getid_(qh vertex_list), qh NEWfacets, qh NEWtentative, stats)); if (stats) { FORALLvertex_(qh newvertex_list) totver++; @@ -2698,10 +3404,13 @@ void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_ zmax_(Znewfacetmax, totnew); } FORALLvertex_(qh newvertex_list) - vertex->newlist= False; + vertex->newfacet= False; qh newvertex_list= NULL; - FORALLnew_facets + qh first_newfacet= 0; + FORALLnew_facets { newfacet->newfacet= False; + newfacet->dupridge= False; + } qh newfacet_list= NULL; if (resetVisible) { FORALLvisible_facets { @@ -2710,14 +3419,15 @@ void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_ } qh num_visible= 0; } - qh visible_list= NULL; /* may still have visible facets via qh_triangulate */ + qh visible_list= NULL; qh NEWfacets= False; + qh NEWtentative= False; } /* resetlists */ /*--------------------------------- - qh_setvoronoi_all() + qh_setvoronoi_all( ) compute Voronoi centers for all facets includes upperDelaunay facets if qh.UPPERdelaunay ('Qu') @@ -2725,8 +3435,7 @@ void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_ facet->center is the Voronoi center notes: - this is unused/untested code - please email bradb@shore.net if this works ok for you + unused/untested code: please email bradb@shore.net if this works ok for you use: FORALLvertices {...} to locate the vertex for a point. @@ -2747,7 +3456,6 @@ void qh_setvoronoi_all(void) { } /* setvoronoi_all */ #ifndef qh_NOmerge - /*--------------------------------- @@ -2759,20 +3467,25 @@ void qh_setvoronoi_all(void) { returns: all facets simplicial each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc. + resets qh.newfacet_list and visible_list notes: - call after qh_check_output since may switch to Voronoi centers + called by qh_prepare_output and user_eg2.c + call after qh_check_output since may switch to Voronoi centers, and qh_checkconvex skips f.tricoplanar facets Output may overwrite ->f.triowner with ->f.area + while running, 'triangulated_facet_list' is a list of + one non-simplicial facet followed by its 'f.tricoplanar' triangulated facets + See qh_buildcone */ -void qh_triangulate(void /*qh.facet_list*/) { +void qh_triangulate(void /* qh.facet_list */) { facetT *facet, *nextfacet, *owner; - int onlygood= qh ONLYgood; - facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL; + facetT *neighbor, *visible= NULL, *facet1, *facet2, *triangulated_facet_list= NULL; facetT *orig_neighbor= NULL, *otherfacet; - vertexT *new_vertex_list= NULL; + vertexT *triangulated_vertex_list= NULL; mergeT *merge; mergeType mergetype; int neighbor_i, neighbor_n; + boolT onlygood= qh ONLYgood; if (qh hasTriangulation) return; @@ -2785,57 +3498,57 @@ void qh_triangulate(void /*qh.facet_list*/) { } qh ONLYgood= False; /* for makenew_nonsimplicial */ qh visit_id++; - qh NEWfacets= True; - qh degen_mergeset= qh_settemp(qh TEMPsize); + qh_initmergesets(/* qh.facet_mergeset,degen_mergeset,vertex_mergeset */); qh newvertex_list= qh vertex_tail; - for (facet= qh facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */ + for (facet=qh facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */ nextfacet= facet->next; if (facet->visible || facet->simplicial) continue; /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */ - if (!new_facet_list) - new_facet_list= facet; /* will be moved to end */ - qh_triangulate_facet(facet, &new_vertex_list); + if (!triangulated_facet_list) + triangulated_facet_list= facet; /* will be first triangulated facet */ + qh_triangulate_facet(facet, &triangulated_vertex_list); /* qh_resetlists ! */ } - trace2((qh ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list))); - for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */ + /* qh_checkpolygon invalid due to f.visible without qh.visible_list */ + trace2((qh ferr, 2047, "qh_triangulate: delete null facets from facetlist f%d. A null facet has the same first (apex) and second vertices\n", getid_(triangulated_facet_list))); + for (facet=triangulated_facet_list; facet && facet->next; facet= nextfacet) { nextfacet= facet->next; if (facet->visible) continue; if (facet->ridges) { if (qh_setsize(facet->ridges) > 0) { - qh_fprintf(qh ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id); + qh_fprintf(qh ferr, 6161, "qhull internal error (qh_triangulate): ridges still defined for f%d\n", facet->id); qh_errexit(qh_ERRqhull, facet, NULL); } qh_setfree(&facet->ridges); } if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) { zinc_(Ztrinull); - qh_triangulate_null(facet); + qh_triangulate_null(facet); /* will delete facet */ } } - trace2((qh ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh degen_mergeset))); + trace2((qh ferr, 2048, "qh_triangulate: delete %d or more mirrored facets. Mirrored facets have the same vertices due to a null facet\n", qh_setsize(qh degen_mergeset))); qh visible_list= qh facet_tail; - while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) { + while ((merge= (mergeT *)qh_setdellast(qh degen_mergeset))) { facet1= merge->facet1; facet2= merge->facet2; - mergetype= merge->type; + mergetype= merge->mergetype; qh_memfree(merge, (int)sizeof(mergeT)); if (mergetype == MRGmirror) { zinc_(Ztrimirror); - qh_triangulate_mirror(facet1, facet2); + qh_triangulate_mirror(facet1, facet2); /* will delete both facets */ } } - qh_settempfree(&qh degen_mergeset); - trace2((qh ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list))); - qh newvertex_list= new_vertex_list; /* all vertices of new facets */ + qh_freemergesets(); + trace2((qh ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(triangulated_vertex_list))); + qh newvertex_list= triangulated_vertex_list; /* all vertices of triangulated facets */ qh visible_list= NULL; - qh_updatevertices(/*qh.newvertex_list, empty newfacet_list and visible_list*/); - qh_resetlists(False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/); + qh_update_vertexneighbors(/* qh.newvertex_list, empty newfacet_list and visible_list */); + qh_resetlists(False, !qh_RESETvisible /* qh.newvertex_list, empty newfacet_list and visible_list */); - trace2((qh ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list))); + trace2((qh ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(triangulated_facet_list))); trace2((qh ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n")); - FORALLfacet_(new_facet_list) { + FORALLfacet_(triangulated_facet_list) { if (facet->tricoplanar && !facet->visible) { FOREACHneighbor_i_(facet) { if (neighbor_i == 0) { /* first iteration */ @@ -2857,11 +3570,13 @@ void qh_triangulate(void /*qh.facet_list*/) { } } } - + if (qh IStracing >= 4) + qh_printlists(); trace2((qh ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n")); owner= NULL; visible= NULL; - for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */ + for (facet=triangulated_facet_list; facet && facet->next; facet= nextfacet) { + /* deleting facets, triangulated_facet_list is no longer valid */ nextfacet= facet->next; if (facet->visible) { if (facet->tricoplanar) { /* a null or mirrored facet */ @@ -2870,7 +3585,7 @@ void qh_triangulate(void /*qh.facet_list*/) { }else { /* a non-simplicial facet followed by its tricoplanars */ if (visible && !owner) { /* RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */ - trace2((qh ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n", + trace2((qh ferr, 2053, "qh_triangulate: delete f%d. All tricoplanar facets degenerate for non-simplicial facet\n", visible->id)); qh_delfacet(visible); qh num_visible--; @@ -2880,7 +3595,7 @@ void qh_triangulate(void /*qh.facet_list*/) { } }else if (facet->tricoplanar) { if (facet->f.triowner != visible || visible==NULL) { - qh_fprintf(qh ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible)); + qh_fprintf(qh ferr, 6162, "qhull internal error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible)); qh_errexit2(qh_ERRqhull, facet, visible); } if (owner) @@ -2901,6 +3616,7 @@ void qh_triangulate(void /*qh.facet_list*/) { qh num_visible--; } } + facet->degenerate= False; /* reset f.degenerate set by qh_triangulate*/ } if (visible && !owner) { trace2((qh ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n", @@ -2908,7 +3624,6 @@ void qh_triangulate(void /*qh.facet_list*/) { qh_delfacet(visible); qh num_visible--; } - qh NEWfacets= False; qh ONLYgood= onlygood; /* restore value */ if (qh CHECKfrequently) qh_checkpolygon(qh facet_list); @@ -2919,7 +3634,7 @@ void qh_triangulate(void /*qh.facet_list*/) { /*--------------------------------- - qh_triangulate_facet(qh, facetA, &firstVertex ) + qh_triangulate_facet( facetA, &firstVertex ) triangulate a non-simplicial facet if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center returns: @@ -2959,22 +3674,22 @@ void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex) { trace3((qh ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id)); + qh first_newfacet= qh facet_id; if (qh IStracing >= 4) qh_printfacet(qh ferr, facetA); FOREACHneighbor_(facetA) { neighbor->seen= False; - neighbor->coplanar= False; + neighbor->coplanarhorizon= False; } if (qh CENTERtype == qh_ASvoronoi && !facetA->center /* matches upperdelaunay in qh_setfacetplane() */ - && fabs_(facetA->normal[qh hull_dim -1]) >= qh ANGLEround * qh_ZEROdelaunay) { + && fabs_(facetA->normal[qh hull_dim -1]) >= qh ANGLEround * qh_ZEROdelaunay) { facetA->center= qh_facetcenter(facetA->vertices); } - qh_willdelete(facetA, NULL); - qh newfacet_list= qh facet_tail; + qh visible_list= qh newfacet_list= qh facet_tail; facetA->visitid= qh visit_id; apex= SETfirstt_(facetA->vertices, vertexT); qh_makenew_nonsimplicial(facetA, apex, &numnew); - SETfirst_(facetA->neighbors)= NULL; + qh_willdelete(facetA, NULL); FORALLnew_facets { newfacet->tricoplanar= True; newfacet->f.trivisible= facetA; @@ -2984,14 +3699,14 @@ void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex) { if (qh TRInormals) { /* 'Q11' triangulate duplicates ->normal and ->center */ newfacet->keepcentrum= True; if(facetA->normal){ - newfacet->normal= qh_memalloc(qh normal_size); - memcpy((char *)newfacet->normal, facetA->normal, qh normal_size); + newfacet->normal= (double *)qh_memalloc(qh normal_size); + memcpy((char *)newfacet->normal, facetA->normal, (size_t)qh normal_size); } if (qh CENTERtype == qh_AScentrum) newfacet->center= qh_getcentrum(newfacet); else if (qh CENTERtype == qh_ASvoronoi && facetA->center){ - newfacet->center= qh_memalloc(qh center_size); - memcpy((char *)newfacet->center, facetA->center, qh center_size); + newfacet->center= (double *)qh_memalloc(qh center_size); + memcpy((char *)newfacet->center, facetA->center, (size_t)qh center_size); } }else { newfacet->keepcentrum= False; @@ -3004,42 +3719,46 @@ void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex) { newfacet->maxoutside= facetA->maxoutside; #endif } - qh_matchnewfacets(/*qh.newfacet_list*/); + qh_matchnewfacets(/* qh.newfacet_list */); /* ignore returned value, maxdupdist */ zinc_(Ztricoplanar); zadd_(Ztricoplanartot, numnew); zmax_(Ztricoplanarmax, numnew); - qh visible_list= NULL; if (!(*first_vertex)) (*first_vertex)= qh newvertex_list; qh newvertex_list= NULL; - qh_updatevertices(/*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/); - qh_resetlists(False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/); + qh visible_list= NULL; + /* only update v.neighbors for qh.newfacet_list. qh.visible_list and qh.newvertex_list are NULL */ + qh_update_vertexneighbors(/* qh.newfacet_list */); + qh_resetlists(False, !qh_RESETvisible /* qh.newfacet_list */); } /* triangulate_facet */ /*--------------------------------- qh_triangulate_link(oldfacetA, facetA, oldfacetB, facetB) - relink facetA to facetB via oldfacets + relink facetA to facetB via null oldfacetA or mirrored oldfacetA and oldfacetB 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 + if neighbors are already linked, will merge as MRGmirror (qh.degen_mergeset, 4-d and up) */ void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) { int errmirror= False; - trace3((qh ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n", - oldfacetA->id, oldfacetB->id, facetA->id, facetB->id)); + if (oldfacetA == oldfacetB) { + trace3((qh ferr, 3052, "qh_triangulate_link: relink neighbors f%d and f%d of null facet f%d\n", + facetA->id, facetB->id, oldfacetA->id)); + }else { + trace3((qh ferr, 3021, "qh_triangulate_link: relink neighbors f%d and f%d of mirrored facets f%d and f%d\n", + facetA->id, facetB->id, oldfacetA->id, oldfacetB->id)); + } if (qh_setin(facetA->neighbors, facetB)) { if (!qh_setin(facetB->neighbors, facetA)) errmirror= True; - else - qh_appendmergeset(facetA, facetB, MRGmirror, NULL); + else if (!facetA->redundant || !facetB->redundant || !qh_hasmerge(qh degen_mergeset, MRGmirror, facetA, facetB)) + qh_appendmergeset(facetA, facetB, MRGmirror, 0.0, 1.0); }else if (qh_setin(facetB->neighbors, facetA)) errmirror= True; if (errmirror) { - qh_fprintf(qh ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n", + qh_fprintf(qh ferr, 6163, "qhull internal error (qh_triangulate_link): neighbors f%d and f%d do not match for null facet or mirrored facets f%d and f%d\n", facetA->id, facetB->id, oldfacetA->id, oldfacetB->id); qh_errexit2(qh_ERRqhull, facetA, facetB); } @@ -3051,7 +3770,7 @@ void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, f >-------------------------------- qh_triangulate_mirror(facetA, facetB) - delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror + delete two mirrored facets identified by qh_triangulate_null() and itself 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 @@ -3061,12 +3780,18 @@ void qh_triangulate_mirror(facetT *facetA, facetT *facetB) { facetT *neighbor, *neighborB; int neighbor_i, neighbor_n; - trace3((qh ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n", + trace3((qh ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d and link their neighbors\n", facetA->id, facetB->id)); FOREACHneighbor_i_(facetA) { neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT); - if (neighbor == neighborB) + if (neighbor == facetB && neighborB == facetA) continue; /* occurs twice */ + else if (neighbor->redundant && neighborB->redundant) { /* also mirrored facets (D5+) */ + if (qh_hasmerge(qh degen_mergeset, MRGmirror, neighbor, neighborB)) + continue; + } + if (neighbor->visible && neighborB->visible) /* previously deleted as mirrored facets */ + continue; qh_triangulate_link(facetA, neighbor, facetB, neighborB); } qh_willdelete(facetA, NULL); @@ -3080,11 +3805,11 @@ void qh_triangulate_mirror(facetT *facetA, facetT *facetB) { 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 + adds facetA to ->visible for deletion after qh_update_vertexneighbors 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 + if they are already neighbors, the opposing neighbors will be merged (MRGmirror) */ void qh_triangulate_null(facetT *facetA) { facetT *neighbor, *otherfacet; @@ -3101,10 +3826,10 @@ void qh_triangulate(void) { } #endif /* qh_NOmerge */ - /*--------------------------------- - qh_vertexintersect( vertexsetA, vertexsetB ) + qh_vertexintersect( verticesA, verticesB ) intersects two vertex sets (inverse id ordered) vertexsetA is a temporary set at the top of qhmem.tempstack @@ -3112,9 +3837,12 @@ void qh_triangulate(void) { replaces vertexsetA with the intersection notes: - could overwrite vertexsetA if currently too slow + only called by qh_neighbor_intersections + if !qh.QHULLfinished, non-simplicial facets may have f.vertices with extraneous vertices + cleaned by qh_remove_extravertices in qh_reduce_vertices + could optimize by overwriting vertexsetA */ -void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB) { +void qh_vertexintersect(setT **vertexsetA, setT *vertexsetB) { setT *intersection; intersection= qh_vertexintersect_new(*vertexsetA, vertexsetB); @@ -3126,13 +3854,18 @@ void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB) { /*--------------------------------- - qh_vertexintersect_new( ) + qh_vertexintersect_new( verticesA, verticesB ) intersects two vertex sets (inverse id ordered) returns: a new set + + notes: + called by qh_checkfacet, qh_vertexintersect, qh_rename_sharedvertex, qh_findbest_pinchedvertex, qh_neighbor_intersections + if !qh.QHULLfinished, non-simplicial facets may have f.vertices with extraneous vertices + cleaned by qh_remove_extravertices in qh_reduce_vertices */ -setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) { +setT *qh_vertexintersect_new(setT *vertexsetA, setT *vertexsetB) { setT *intersection= qh_setnew(qh hull_dim - 1); vertexT **vertexA= SETaddr_(vertexsetA, vertexT); vertexT **vertexB= SETaddr_(vertexsetB, vertexT); @@ -3154,7 +3887,7 @@ setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) { /*--------------------------------- - qh_vertexneighbors() + qh_vertexneighbors( ) for each vertex in qh.facet_list, determine its neighboring facets @@ -3171,13 +3904,13 @@ setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) { for each vertex append facet to vertex->neighbors */ -void qh_vertexneighbors(void /*qh.facet_list*/) { +void qh_vertexneighbors(void /* qh.facet_list */) { facetT *facet; vertexT *vertex, **vertexp; if (qh VERTEXneighbors) return; - trace1((qh ferr, 1035, "qh_vertexneighbors: determing neighboring facets for each vertex\n")); + trace1((qh ferr, 1035, "qh_vertexneighbors: determining neighboring facets for each vertex\n")); qh vertex_visit++; FORALLfacets { if (facet->visible) diff --git a/3rdparty/qhull/qhull_a.h b/3rdparty/qhull/qhull_a.h index 729b72327..40a3fa002 100644 --- a/3rdparty/qhull/qhull_a.h +++ b/3rdparty/qhull/qhull_a.h @@ -3,7 +3,6 @@ qhull_a.h all header files for compiling qhull with non-reentrant code - included before C++ headers for user_r.h:QHULL_CRTDBG see qh-qhull.htm @@ -13,9 +12,9 @@ defines internal functions for libqhull.c global.c - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/qhull_a.h#4 $$Change: 2064 $ - $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/qhull_a.h#2 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ Notes: grep for ((" and (" to catch fprintf("lkasdjf"); full parens around (x?y:z) @@ -25,7 +24,7 @@ #ifndef qhDEFqhulla #define qhDEFqhulla 1 -#include "libqhull.h" /* Includes user_r.h and data types */ +#include "libqhull.h" /* Includes user.h and data types */ #include "stat.h" #include "random.h" @@ -113,17 +112,20 @@ inline void qhullUnused(T &x) { (void)x; } void qh_qhull(void); boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist); +void qh_build_withrestart(void); +vertexT *qh_buildcone(pointT *furthest, facetT *facet, int goodhorizon, facetT **retryfacet); +boolT qh_buildcone_mergepinched(vertexT *apex, facetT *facet, facetT **retryfacet); +boolT qh_buildcone_onlygood(vertexT *apex, int goodhorizon); void qh_buildhull(void); void qh_buildtracing(pointT *furthest, facetT *facet); -void qh_build_withrestart(void); void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet); void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible,int *goodhorizon); pointT *qh_nextfurthest(facetT **visible); void qh_partitionall(setT *vertices, pointT *points,int npoints); -void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist); +void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist, boolT allnew); void qh_partitionpoint(pointT *point, facetT *facet); void qh_partitionvisible(boolT allpoints, int *numpoints); -void qh_precision(const char *reason); +void qh_joggle_restart(const char *reason); void qh_printsummary(FILE *fp); /***** -global.c internal prototypes (alphabetical) ***********************/ diff --git a/3rdparty/qhull/qset.c b/3rdparty/qhull/qset.c index a969252a7..6926afc91 100644 --- a/3rdparty/qhull/qset.c +++ b/3rdparty/qhull/qset.c @@ -11,12 +11,14 @@ 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/qset.c#3 $$Change: 2062 $ - $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ + Do not reference 'qh' since it brings in qhT unnecessarily + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/qset.c#7 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ -#include "user.h" /* for QHULL_CRTDBG */ +#include "libqhull.h" /* for qhT and QHULL_CRTDBG */ #include "qset.h" #include "mem.h" #include @@ -44,7 +46,7 @@ void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ); /*---------------------------------- - qh_setaddnth( setp, nth, newelem) + qh_setaddnth( setp, nth, newelem ) adds newelem as n'th element of sorted or unsorted *setp notes: @@ -116,7 +118,7 @@ void qh_setaddsorted(setT **setp, void *newelem) { /*--------------------------------- - qh_setappend( setp, newelem) + qh_setappend( setp, newelem ) append newelem to *setp notes: @@ -148,7 +150,7 @@ void qh_setappend(setT **setp, void *newelem) { /*--------------------------------- - qh_setappend_set( setp, setA) + qh_setappend_set( setp, setA ) appends setA to *setp notes: @@ -231,7 +233,7 @@ void qh_setappend2ndlast(setT **setp, void *newelem) { design: checks that maxsize, actual size, and NULL terminator agree */ -void qh_setcheck(setT *set, const char *tname, unsigned id) { +void qh_setcheck(setT *set, const char *tname, unsigned int id) { int maxsize, size; int waserr= 0; @@ -283,7 +285,7 @@ void qh_setcompact(setT *set) { destp= elemp= firstp= SETaddr_(set, void); endp= destp + size; while (1) { - if (!(*destp++ = *elemp++)) { + if (!(*destp++= *elemp++)) { destp--; if (elemp > endp) break; @@ -324,7 +326,7 @@ setT *qh_setcopy(setT *set, int extra) { /*--------------------------------- - qh_setdel( set, oldelem ) + qh_setdel(set, oldelem ) delete oldelem from an unsorted set returns: @@ -367,7 +369,7 @@ void *qh_setdel(setT *set, void *oldelem) { /*--------------------------------- - qh_setdellast( set) + qh_setdellast( set ) return last element of set or NULL notes: @@ -428,7 +430,7 @@ void *qh_setdelnth(setT *set, int nth) { sizep= SETsizeaddr_(set); if ((sizep->i--)==0) /* if was a full set */ - sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ if (nth < 0 || nth >= sizep->i) { qh_fprintf(qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth); qh_setprint(qhmem.ferr, "", set); @@ -600,7 +602,7 @@ int qh_setequal(setT *setA, setT *setB) { return 1; elemAp= SETaddr_(setA, void); elemBp= SETaddr_(setB, void); - if (!memcmp((char *)elemAp, (char *)elemBp, sizeA*SETelemsize)) + if (!memcmp((char *)elemAp, (char *)elemBp, (size_t)(sizeA * SETelemsize))) return 1; return 0; } /* setequal */ @@ -718,7 +720,7 @@ void qh_setfree(setT **setp) { void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ if (*setp) { - size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + size= (int)sizeof(setT) + ((*setp)->maxsize)*SETelemsize; if (size <= qhmem.LASTsize) { qh_memfree_(*setp, size, freelistp); }else @@ -771,7 +773,7 @@ void qh_setfreelong(setT **setp) { int size; if (*setp) { - size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + size= (int)sizeof(setT) + ((*setp)->maxsize)*SETelemsize; if (size > qhmem.LASTsize) { qh_memfree(*setp, size); *setp= NULL; @@ -806,7 +808,7 @@ int qh_setin(setT *set, void *setelem) { /*--------------------------------- - qh_setindex( set, atelem ) + qh_setindex(set, atelem ) returns the index of atelem in set. returns -1, if not in set or maxsize wrong @@ -843,7 +845,13 @@ int qh_setindex(setT *set, void *atelem) { returns a larger set that contains all elements of *oldsetp notes: - the set is at least twice as large + if long memory, + the new set is 2x larger + if qhmem.LASTsize is between 1.5x and 2x + the new set is qhmem.LASTsize + otherwise use quick memory, + the new set is 2x larger, rounded up to next qh_memsize + if temp set, updates qhmem.tempstack design: @@ -853,22 +861,23 @@ int qh_setindex(setT *set, void *atelem) { deletes the old set */ void qh_setlarger(setT **oldsetp) { - int size= 1; + int setsize= 1, newsize; setT *newset, *set, **setp, *oldset; setelemT *sizep; setelemT *newp, *oldp; if (*oldsetp) { oldset= *oldsetp; - SETreturnsize_(oldset, size); + SETreturnsize_(oldset, setsize); qhmem.cntlarger++; - qhmem.totlarger += size+1; - newset= qh_setnew(2 * size); + qhmem.totlarger += setsize+1; + qh_setlarger_quick(setsize, &newsize); + newset= qh_setnew(newsize); oldp= (setelemT *)SETaddr_(oldset, void); newp= (setelemT *)SETaddr_(newset, void); - memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize); + memcpy((char *)newp, (char *)oldp, (size_t)(setsize+1) * SETelemsize); sizep= SETsizeaddr_(newset); - sizep->i= size+1; + sizep->i= setsize+1; FOREACHset_((setT *)qhmem.tempstack) { if (set == oldset) *(setp-1)= newset; @@ -880,6 +889,39 @@ void qh_setlarger(setT **oldsetp) { } /* setlarger */ +/*--------------------------------- + + qh_setlarger_quick( setsize, newsize ) + determine newsize for setsize + returns True if newsize fits in quick memory + + design: + if 2x fits into quick memory + return True, 2x + if x+4 does not fit into quick memory + return False, 2x + if x+x/3 fits into quick memory + return True, the last quick set + otherwise + return False, 2x +*/ +int qh_setlarger_quick(int setsize, int *newsize) { + int lastquickset; + + *newsize= 2 * setsize; + lastquickset= (qhmem.LASTsize - (int)sizeof(setT)) / SETelemsize; /* matches size computation in qh_setnew */ + if (*newsize <= lastquickset) + return 1; + if (setsize + 4 > lastquickset) + return 0; + if (setsize + setsize/3 <= lastquickset) { + *newsize= lastquickset; + return 1; + } + return 0; +} /* setlarger_quick */ + /*--------------------------------- @@ -929,7 +971,7 @@ setT *qh_setnew(int setsize) { if (!setsize) setsize++; - size= sizeof(setT) + setsize * SETelemsize; + size= (int)sizeof(setT) + setsize * SETelemsize; /* setT includes NULL terminator, see qh.LASTquickset */ if (size>0 && size <= qhmem.LASTsize) { qh_memalloc_(size, freelistp, set, setT); #ifndef qh_NOmem @@ -938,7 +980,7 @@ setT *qh_setnew(int setsize) { setsize += (sizereceived - size)/SETelemsize; #endif }else - set= (setT*)qh_memalloc(size); + set= (setT *)qh_memalloc(size); set->maxsize= setsize; set->e[setsize].i= 1; set->e[0].p= NULL; @@ -1104,6 +1146,7 @@ void qh_setreplace(setT *set, void *oldelem, void *newelem) { errors if set's maxsize is incorrect same as SETreturnsize_(set) same code for qh_setsize [qset.c] and QhullSetBase::count + if first element is NULL, SETempty_() is True but qh_setsize may be greater than 0 design: determine actual size of set from maxsize @@ -1132,7 +1175,7 @@ int qh_setsize(setT *set) { >-------------------------------- qh_settemp( setsize ) - return a stacked, temporary set of upto setsize elements + return a stacked, temporary set of up to setsize elements notes: use settempfree or settempfree_all to release from qhmem.tempstack @@ -1190,7 +1233,7 @@ void qh_settempfree(setT **set) { /*--------------------------------- - qh_settempfree_all( ) + qh_settempfree_all( ) free all temporary sets in qhmem.tempstack design: @@ -1209,7 +1252,7 @@ void qh_settempfree_all(void) { /*--------------------------------- - qh_settemppop( ) + qh_settemppop( ) pop and return temporary set from qhmem.tempstack notes: @@ -1221,7 +1264,7 @@ void qh_settempfree_all(void) { setT *qh_settemppop(void) { setT *stackedset; - stackedset= (setT*)qh_setdellast(qhmem.tempstack); + stackedset= (setT *)qh_setdellast(qhmem.tempstack); if (!stackedset) { qh_fprintf(qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n"); qh_errexit(qhmem_ERRqhull, NULL, NULL); diff --git a/3rdparty/qhull/qset.h b/3rdparty/qhull/qset.h index 7e4e7d14f..0948d2ff8 100644 --- a/3rdparty/qhull/qset.h +++ b/3rdparty/qhull/qset.h @@ -16,9 +16,9 @@ - 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/qset.h#2 $$Change: 2062 $ - $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/qset.h#4 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #ifndef qhDEFset @@ -77,7 +77,7 @@ structure for set of n elements: typedef union setelemT setelemT; union setelemT { void *p; - int i; /* integer used for e[maxSize] */ + int i; /* integer used for e[maxSize] */ }; struct setT { @@ -123,10 +123,11 @@ struct setT { variable is NULL at end of loop example: - #define FOREACHfacet_( facets ) FOREACHsetelement_( facetT, facets, facet ) + #define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet) notes: use FOREACHsetelement_i_() if need index or include NULLs + assumes set is not modified WARNING: nested loops can't use the same variable (define another FOREACH) @@ -159,7 +160,7 @@ struct setT { variable==NULL and variable_i==variable_n example: - #define FOREACHfacet_i_( facets ) FOREACHsetelement_i_( facetT, facets, facet ) + #define FOREACHfacet_i_(facets) FOREACHsetelement_i_(facetT, facets, facet) WARNING: nested loops can't use the same variable (define another FOREACH) @@ -194,7 +195,7 @@ struct setT { variable is NULL example: - #define FOREACHvertexreverse_( vertices ) FOREACHsetelementreverse_( vertexT, vertices, vertex ) + #define FOREACHvertexreverse_(vertices) FOREACHsetelementreverse_(vertexT, vertices, vertex) notes: use FOREACHsetelementreverse12_() to reverse first two elements @@ -226,7 +227,7 @@ struct setT { variable is NULL at end of loop example - #define FOREACHvertexreverse12_( vertices ) FOREACHsetelementreverse12_( vertexT, vertices, vertex ) + #define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex) notes: WARNING: needs braces if nested inside another FOREACH @@ -261,6 +262,7 @@ struct setT { FOREACHelem_(set) { notes: + assumes set is not modified WARNING: needs braces if nested inside another FOREACH */ #define FOREACHelem_(set) FOREACHsetelement_(void, set, elem) @@ -339,7 +341,7 @@ struct setT { notes: assumes that n is valid [0..size] and that set is defined */ -#define SETelemt_(set, n, type) ((type*)((set)->e[n].p)) +#define SETelemt_(set, n, type) ((type *)((set)->e[n].p)) /*----------------------------------------- @@ -368,7 +370,7 @@ struct setT { return first element of set as a type */ -#define SETfirstt_(set, type) ((type*)((set)->e[0].p)) +#define SETfirstt_(set, type) ((type *)((set)->e[0].p)) /*----------------------------------------- @@ -385,7 +387,7 @@ struct setT { SETsecondt_(set, type) return second element of set as a type */ -#define SETsecondt_(set, type) ((type*)((set)->e[1].p)) +#define SETsecondt_(set, type) ((type *)((set)->e[1].p)) /*----------------------------------------- @@ -411,10 +413,11 @@ struct setT { >---------------------------------------- SETempty_(set) - return true(1) if set is empty + return true(1) if set is empty (i.e., FOREACHsetelement_ is empty) notes: set may be NULL + qh_setsize may be non-zero if first element is NULL */ #define SETempty_(set) (!set || (SETfirst_(set) ? 0 : 1)) @@ -452,7 +455,7 @@ void qh_setaddnth(setT **setp, int nth, void *newelem); void qh_setappend(setT **setp, void *elem); void qh_setappend_set(setT **setp, setT *setA); void qh_setappend2ndlast(setT **setp, void *elem); -void qh_setcheck(setT *set, const char *tname, unsigned id); +void qh_setcheck(setT *set, const char *tname, unsigned int id); void qh_setcompact(setT *set); setT *qh_setcopy(setT *set, int extra); void *qh_setdel(setT *set, void *elem); @@ -460,17 +463,18 @@ void *qh_setdellast(setT *set); void *qh_setdelnth(setT *set, int nth); void *qh_setdelnthsorted(setT *set, int nth); void *qh_setdelsorted(setT *set, void *newelem); -setT *qh_setduplicate( setT *set, int elemsize); +setT *qh_setduplicate(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(setT **set); -void qh_setfree2( setT **setp, int elemsize); +void qh_setfree2(setT **setp, int elemsize); void qh_setfreelong(setT **set); int qh_setin(setT *set, void *setelem); int qh_setindex(setT *set, void *setelem); void qh_setlarger(setT **setp); +int qh_setlarger_quick(int setsize, int *newsize); void *qh_setlast(setT *set); setT *qh_setnew(int size); setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend); @@ -486,5 +490,4 @@ void qh_settruncate(setT *set, int size); int qh_setunique(setT **set, void *elem); void qh_setzero(setT *set, int idx, int size); - #endif /* qhDEFset */ diff --git a/3rdparty/qhull/random.c b/3rdparty/qhull/random.c index 176d697ae..2e12b2a98 100644 --- a/3rdparty/qhull/random.c +++ b/3rdparty/qhull/random.c @@ -21,22 +21,21 @@ #endif /*--------------------------------- + >-------------------------------- - qh_argv_to_command( argc, argv, command, max_size ) + qh_argv_to_command( argc, argv, command, max_size ) build command from argc/argv max_size is at least - returns: + returns: a space-delimited string of options (just as typed) returns false if max_size is too short - notes: + notes: silently removes makes option string easy to input and output - matches qh_argv_to_command_size() - + matches qh_argv_to_command_size argc may be 0 */ int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) { @@ -81,9 +80,10 @@ int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) { *t= '\0'; }else if (remaining < 0) { goto error_argv; - }else + }else { strcat(command, " "); strcat(command, s); + } } return 1; @@ -92,18 +92,20 @@ int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) { } /* argv_to_command */ /*--------------------------------- + >-------------------------------- -qh_argv_to_command_size( argc, argv ) + qh_argv_to_command_size( argc, argv ) return size to allocate for qh_argv_to_command() -notes: + notes: + only called from rbox with qh_errexit not enabled + caller should report error if returned size is less than 1 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 count= 1; /* null-terminator if argc==0 */ int i; char *s; @@ -147,24 +149,24 @@ int qh_last_random= 1; /* define as global variable instead of using qh */ #define qh_rand_q 127773 /* m div a */ #define qh_rand_r 2836 /* m mod a */ -int qh_rand( void) { +int qh_rand(void) { int lo, hi, test; - int seed = qh_last_random; + 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; + 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 */ + /* seed= seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax; for testing */ + /* seed= qh_RANDOMmax; for testing */ return seed; } /* rand */ -void qh_srand( int seed) { +void qh_srand(int seed) { if (seed < 1) qh_last_random= 1; else if (seed >= qh_rand_m) @@ -192,14 +194,14 @@ realT qh_randomfactor(realT scale, realT offset) { /*--------------------------------- -qh_randommatrix( buffer, dim, rows ) - generate a random dim X dim matrix in range [-1,1] - assumes buffer is [dim+1, dim] + qh_randommatrix( buffer, dim, rows ) + generate a random dim X dim matrix in range [-1,1] + assumes buffer is [dim+1, dim] -returns: - sets buffer to random numbers - sets rows to rows of buffer - sets row[dim] as scratch row + returns: + sets buffer to random numbers + sets rows to rows of buffer + sets row[dim] as scratch row */ void qh_randommatrix(realT *buffer, int dim, realT **rows) { int i, k; diff --git a/3rdparty/qhull/random.h b/3rdparty/qhull/random.h index 0c6896b76..0a0e7b400 100644 --- a/3rdparty/qhull/random.h +++ b/3rdparty/qhull/random.h @@ -6,9 +6,9 @@ see qh-geom.htm and random.c - Copyright (c) 1993-2015 The Geometry Center. - $Id: //main/2015/qhull/src/libqhull/random.h#2 $$Change: 2026 $ - $DateTime: 2015/11/07 22:44:39 $$Author: bbarber $ + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull/random.h#2 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ */ #ifndef qhDEFrandom @@ -18,11 +18,10 @@ /*============= prototypes in alphabetical order ======= */ - int qh_argv_to_command(int argc, char *argv[], char* command, int max_size); int qh_argv_to_command_size(int argc, char *argv[]); -int qh_rand( void); -void qh_srand( int seed); +int qh_rand(void); +void qh_srand(int seed); realT qh_randomfactor(realT scale, realT offset); void qh_randommatrix(realT *buffer, int dim, realT **row); int qh_strtol(const char *s, char **endp); diff --git a/3rdparty/qhull/rboxlib.c b/3rdparty/qhull/rboxlib.c index f945133fa..6a9f314c3 100644 --- a/3rdparty/qhull/rboxlib.c +++ b/3rdparty/qhull/rboxlib.c @@ -33,18 +33,19 @@ #define PI 3.1415926535897932384 /* ------------------------------ prototypes ----------------*/ -int qh_roundi( double a); -void qh_out1( double a); -void qh_out2n( double a, double b); -void qh_out3n( double a, double b, double c); +int qh_roundi(double a); +void qh_out1(double a); +void qh_out2n(double a, double b); +void qh_out3n(double a, double b, double c); void qh_outcoord(int iscdd, double *coord, int dim); void qh_outcoincident(int coincidentpoints, double radius, int iscdd, double *coord, int dim); +void qh_rboxpoints2(char* rbox_command, double **simplex); void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ); void qh_free(void *mem); void *qh_malloc(size_t size); -int qh_rand( void); -void qh_srand( int seed); +int qh_rand(void); +void qh_srand(int seed); /* ------------------------------ globals -------------------*/ @@ -79,6 +80,7 @@ rboxT rbox; notes: To avoid using stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user.c) + Split out qh_rboxpoints2() to avoid -Wclobbered design: Straight line code (consider defining a struct and functions): @@ -88,6 +90,35 @@ rboxT rbox; Generate the points */ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { + int exitcode; + double *simplex; + + if (rbox_inuse) { + qh_fprintf_stderr(6188, "rbox error: rbox in use by another process. Please lock calls to rbox or use libqhull_r/rboxlib_r.c\n"); + return qh_ERRqhull; + } + rbox_inuse= True; + rbox.ferr= ferr; + rbox.fout= fout; + + simplex= NULL; + exitcode= setjmp(rbox.errexit); + if (exitcode) { + /* same code for error exit and normal return. qh.NOerrexit is set */ + if (simplex) + qh_free(simplex); + rbox_inuse= False; + return exitcode; + } + qh_rboxpoints2(rbox_command, &simplex); + /* same code for error exit and normal return */ + if (simplex) + qh_free(simplex); + rbox_inuse= False; + return qh_ERRnone; +} /* rboxpoints */ + +void qh_rboxpoints2(char* rbox_command, double **simplex) { int i,j,k; int gendim; int coincidentcount=0, coincidenttotal=0, coincidentpoints=0; @@ -99,36 +130,18 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { int isbox=0, issimplex=0, issimplex2=0, ismesh=0; double width=0.0, gap=0.0, radius=0.0, coincidentradius=0.0; double coord[MAXdim], offset, meshm=3.0, meshn=4.0, meshr=5.0; - double *coordp, *simplex= NULL, *simplexp; + double *coordp, *simplexp; int nthroot, mult[MAXdim]; - double norm, factor, randr, rangap, lensangle=0, lensbase=1; + double norm, factor, randr, rangap, tempr, lensangle=0, lensbase=1; double anglediff, angle, x, y, cube=0.0, diamond=0.0; double box= qh_DEFAULTbox; /* scale all numbers before output */ double randmax= qh_RANDOMmax; - char command[200], seedbuf[200]; - char *s= command, *t, *first_point= NULL; + char command[250], seedbuf[50]; + char *s=command, *t, *first_point=NULL; time_t timedata; - int exitcode; - - if (rbox_inuse) { - qh_fprintf_rbox(rbox.ferr, 6188, "rbox error: rbox in use by another process. Please lock calls to rbox.\n"); - return qh_ERRqhull; - } - rbox_inuse= True; - rbox.ferr= ferr; - rbox.fout= fout; - - exitcode= setjmp(rbox.errexit); - if (exitcode) { - /* same code for error exit and normal return. qh.NOerrexit is set */ - if (simplex) - qh_free(simplex); - rbox_inuse= False; - return exitcode; - } *command= '\0'; - strncat(command, rbox_command, sizeof(command)-strlen(command)-1); + strncat(command, rbox_command, sizeof(command)-sizeof(seedbuf)-strlen(command)-1); while (*s && !isspace(*s)) /* skip program name */ s++; @@ -225,7 +238,7 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { dim= qh_strtol(s, &s); if (dim < 1 || dim > MAXdim) { - qh_fprintf_rbox(rbox.ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim); + qh_fprintf_rbox(rbox.ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)\n", dim, MAXdim); qh_errexit_rbox(qh_ERRinput); } break; @@ -267,7 +280,7 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { break; case 'P': if (!first_point) - first_point= s-1; + first_point= s - 1; addpoints++; while (*s && !isspace(*s)) /* read points later */ s++; @@ -284,11 +297,11 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { isaxis= 1; break; default: - qh_fprintf_rbox(rbox.ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s); + qh_fprintf_rbox(rbox.ferr, 6352, "rbox error: unknown flag at '%s'.\nExecute 'rbox' without arguments for documentation.\n", s - 1); qh_errexit_rbox(qh_ERRinput); } if (*s && !isspace(*s)) { - qh_fprintf_rbox(rbox.ferr, 7071, "rbox error: missing space between flags at %s.\n", s); + qh_fprintf_rbox(rbox.ferr, 6353, "rbox error: missing space between flags at %s.\n", s); qh_errexit_rbox(qh_ERRinput); } } @@ -297,7 +310,8 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { if (rbox.isinteger && !isbox) box= qh_DEFAULTzbox; if (addcube) { - cubesize= (int)floor(ldexp(1.0,dim)+0.5); + tempr= floor(ldexp(1.0,dim)+0.5); + cubesize= (int)tempr; if (cube == 0.0) cube= box; }else @@ -344,6 +358,11 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { qh_fprintf_rbox(rbox.ferr, 6270, "rbox error: 'Cn,r,m' requested n coincident points for each of m points. Either there is no points or m (%d) is greater than the number of points (%d).\n", coincidenttotal, numpoints); qh_errexit_rbox(qh_ERRinput); } + if (coincidentpoints > 0 && isregular) { + qh_fprintf_rbox(rbox.ferr, 6423, "rbox error: 'Cn,r,m' is not implemented for regular points ('r')\n"); + qh_errexit_rbox(qh_ERRinput); + } + if (coincidenttotal == 0) coincidenttotal= numpoints; @@ -382,7 +401,7 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { }else if (israndom) { seed= (int)time(&timedata); sprintf(seedbuf, " t%d", seed); /* appends an extra t, not worth removing */ - strncat(command, seedbuf, sizeof(command)-strlen(command)-1); + strncat(command, seedbuf, sizeof(command) - strlen(command) - 1); t= strstr(command, " t "); if (t) strcpy(t+1, t+3); /* remove " t " */ @@ -407,9 +426,9 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { while (s && *s) { /* 'P' */ count= 0; if (iscdd) - qh_out1( 1.0); + qh_out1(1.0); while (*++s) { - qh_out1( qh_strtod(s, &s)); + qh_out1(qh_strtod(s, &s)); count++; if (isspace(*s) || !*s) break; @@ -420,7 +439,7 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { } if (count < dim) { for (k=dim-count; k--; ) - qh_out1( 0.0); + qh_out1(0.0); }else if (count > dim) { qh_fprintf_rbox(rbox.ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n", count, dim, s); @@ -436,11 +455,11 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { /* ============= simplex distribution =============== */ if (issimplex+issimplex2) { - if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) { + if (!(*simplex= (double *)qh_malloc( (size_t)(dim * (dim+1)) * sizeof(double)))) { qh_fprintf_rbox(rbox.ferr, 6196, "rbox error: insufficient memory for simplex\n"); qh_errexit_rbox(qh_ERRmem); /* qh_ERRmem */ } - simplexp= simplex; + simplexp= *simplex; if (isregular) { for (i=0; i=0; k--) { if (j & ( 1 << k)) - qh_out1( cube); + qh_out1(cube); else - qh_out1( -cube); + qh_out1(-cube); } qh_fprintf_rbox(rbox.fout, 9400, "\n"); } @@ -763,14 +779,14 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { if (adddiamond) { for (j=0; j=0; k--) { if (j/2 != k) - qh_out1( 0.0); + qh_out1(0.0); else if (j & 0x1) - qh_out1( diamond); + qh_out1(diamond); else - qh_out1( -diamond); + qh_out1(-diamond); } qh_fprintf_rbox(rbox.fout, 9401, "\n"); } @@ -778,17 +794,12 @@ int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) { if (iscdd) qh_fprintf_rbox(rbox.fout, 9402, "end\nhull\n"); - - /* same code for error exit and normal return */ - qh_free(simplex); - rbox_inuse= False; - return qh_ERRnone; -} /* rboxpoints */ +} /* rboxpoints2 */ /*------------------------------------------------ outxxx - output functions for qh_rboxpoints */ -int qh_roundi( double a) { +int qh_roundi(double a) { if (a < 0.0) { if (a - 0.5 < INT_MIN) { qh_fprintf_rbox(rbox.ferr, 6200, "rbox input error: negative coordinate %2.2g is too large. Reduce 'Bn'\n", a); @@ -807,12 +818,12 @@ int qh_roundi( double a) { void qh_out1(double a) { if (rbox.isinteger) - qh_fprintf_rbox(rbox.fout, 9403, "%d ", qh_roundi( a+rbox.out_offset)); + qh_fprintf_rbox(rbox.fout, 9403, "%d ", qh_roundi(a+rbox.out_offset)); else qh_fprintf_rbox(rbox.fout, 9404, qh_REAL_1, a+rbox.out_offset); } /* qh_out1 */ -void qh_out2n( double a, double b) { +void qh_out2n(double a, double b) { if (rbox.isinteger) qh_fprintf_rbox(rbox.fout, 9405, "%d %d\n", qh_roundi(a+rbox.out_offset), qh_roundi(b+rbox.out_offset)); @@ -820,7 +831,7 @@ void qh_out2n( double a, double b) { qh_fprintf_rbox(rbox.fout, 9406, qh_REAL_2n, a+rbox.out_offset, b+rbox.out_offset); } /* qh_out2n */ -void qh_out3n( double a, double b, double c) { +void qh_out3n(double a, double b, double c) { if (rbox.isinteger) qh_fprintf_rbox(rbox.fout, 9407, "%d %d %d\n", qh_roundi(a+rbox.out_offset), qh_roundi(b+rbox.out_offset), qh_roundi(c+rbox.out_offset)); @@ -833,7 +844,7 @@ void qh_outcoord(int iscdd, double *coord, int dim) { int k; if (iscdd) - qh_out1( 1.0); + qh_out1(1.0); for (k=0; k < dim; k++) qh_out1(*(p++)); qh_fprintf_rbox(rbox.fout, 9396, "\n"); @@ -845,10 +856,10 @@ void qh_outcoincident(int coincidentpoints, double radius, int iscdd, double *co int i,k; double randmax= qh_RANDOMmax; - for (i= 0; i