123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284 |
- /*<html><pre> -<a href="qh-geom_r.htm"
- >-------------------------------</a><a name="TOP">-</a>
- geom_r.c
- geometric routines of qhull
- see qh-geom_r.htm and geom_r.h
- Copyright (c) 1993-2020 The Geometry Center.
- $Id: //main/2019/qhull/src/libqhull_r/geom_r.c#5 $$Change: 2953 $
- $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
- infrequent code goes into geom2_r.c
- */
- #include "qhull_ra.h"
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="distplane">-</a>
- qh_distplane(qh, point, facet, dist )
- return distance from point to facet
- returns:
- dist
- if qh.RANDOMdist, joggles result
- notes:
- dist > 0 if point is above facet (i.e., outside)
- does not error (for qh_sortfacets, qh_outerinner)
- 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_r.c
- qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies
- */
- void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist) {
- coordT *normal= facet->normal, *coordp, randr;
- int k;
- switch (qh->hull_dim){
- case 2:
- *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];
- break;
- case 3:
- *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
- break;
- case 4:
- *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
- break;
- case 5:
- *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
- break;
- case 6:
- *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];
- break;
- case 7:
- *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];
- break;
- case 8:
- *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];
- break;
- default:
- *dist= facet->offset;
- coordp= point;
- for (k=qh->hull_dim; k--; )
- *dist += *coordp++ * *normal++;
- break;
- }
- zzinc_(Zdistplane);
- if (!qh->RANDOMdist && qh->IStracing < 4)
- return;
- if (qh->RANDOMdist) {
- randr= qh_RANDOMint;
- *dist += (2.0 * randr / qh_RANDOMmax - 1.0) *
- qh->RANDOMfactor * qh->MAXabs_coord;
- }
- #ifndef qh_NOtrace
- if (qh->IStracing >= 4) {
- qh_fprintf(qh, qh->ferr, 8001, "qh_distplane: ");
- qh_fprintf(qh, qh->ferr, 8002, qh_REAL_1, *dist);
- qh_fprintf(qh, qh->ferr, 8003, "from p%d to f%d\n", qh_pointid(qh, point), facet->id);
- }
- #endif
- return;
- } /* distplane */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="findbest">-</a>
- qh_findbest(qh, point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
- find facet that is furthest below a point
- for upperDelaunay facets
- returns facet only if !qh_NOupper and clearly above
- input:
- starts search at 'startfacet' (can not be flipped)
- if !bestoutside(qh_ALL), stops at qh.MINoutside
- returns:
- best facet (reports error if NULL)
- early out if isoutside defined and bestdist > qh.MINoutside
- dist is distance to facet
- isoutside is true if point is outside of facet
- numpart counts the number of distance tests
- see also:
- qh_findbestnew()
- notes:
- If merging (testhorizon), searches horizon facets of coplanar best facets because
- after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
- avoid calls to distplane, function calls, and real number operations.
- caller traces result
- Optimized for outside points. Tried recording a search set for qh_findhorizon.
- Made code more complicated.
- when called by qh_partitionvisible():
- indicated by qh_ISnewfacets
- qh.newfacet_list is list of simplicial, new facets
- qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
- qh.bestfacet_notsharp set if qh_sharpnewfacets returns False
- when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
- qh_check_bestdist(), qh_addpoint()
- indicated by !qh_ISnewfacets
- returns best facet in neighborhood of given facet
- this is best facet overall if dist >= -qh.MAXcoplanar
- or hull has at least a "spherical" curvature
- design:
- initialize and test for early exit
- repeat while there are better facets
- for each neighbor of facet
- exit if outside facet found
- test for better facet
- if point is inside and partitioning
- test for new facets with a "sharp" intersection
- if so, future calls go to qh_findbestnew()
- test horizon facets
- */
- facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
- boolT bestoutside, boolT isnewfacets, boolT noupper,
- realT *dist, boolT *isoutside, int *numpart) {
- realT bestdist= -REALmax/2 /* avoid underflow */;
- facetT *facet, *neighbor, **neighborp;
- facetT *bestfacet= NULL, *lastfacet= NULL;
- int oldtrace= qh->IStracing;
- unsigned int visitid= ++qh->visit_id;
- int numpartnew=0;
- boolT testhorizon= True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
- zinc_(Zfindbest);
- #ifndef qh_NOtrace
- if (qh->IStracing >= 4 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {
- if (qh->TRACElevel > qh->IStracing)
- qh->IStracing= qh->TRACElevel;
- qh_fprintf(qh, qh->ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g,",
- qh_pointid(qh, point), startfacet->id, isnewfacets, bestoutside, qh->MINoutside);
- qh_fprintf(qh, qh->ferr, 8005, " testhorizon? %d, noupper? %d,", testhorizon, noupper);
- qh_fprintf(qh, qh->ferr, 8006, " Last qh_addpoint p%d,", qh->furthest_id);
- qh_fprintf(qh, 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 before testing its neighbors */
- *numpart= 1;
- qh_distplane(qh, point, startfacet, dist); /* this code is duplicated below */
- if (!bestoutside && *dist >= qh->MINoutside
- && (!startfacet->upperdelaunay || !noupper)) {
- bestfacet= startfacet;
- goto LABELreturn_best;
- }
- bestdist= *dist;
- if (!startfacet->upperdelaunay) {
- bestfacet= startfacet;
- }
- }else
- *numpart= 0;
- startfacet->visitid= visitid;
- facet= startfacet;
- while (facet) {
- trace4((qh, qh->ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n",
- facet->id, bestdist, getid_(bestfacet)));
- lastfacet= facet;
- FOREACHneighbor_(facet) {
- if (!neighbor->newfacet && isnewfacets)
- continue;
- if (neighbor->visitid == visitid)
- continue;
- neighbor->visitid= visitid;
- if (!neighbor->flipped) { /* code duplicated above */
- (*numpart)++;
- qh_distplane(qh, point, neighbor, dist);
- if (*dist > bestdist) {
- if (!bestoutside && *dist >= qh->MINoutside
- && (!neighbor->upperdelaunay || !noupper)) {
- bestfacet= neighbor;
- goto LABELreturn_best;
- }
- if (!neighbor->upperdelaunay) {
- bestfacet= neighbor;
- bestdist= *dist;
- break; /* switch to neighbor */
- }else if (!bestfacet) {
- bestdist= *dist;
- break; /* switch to neighbor */
- }
- } /* end of *dist>bestdist */
- } /* end of !flipped */
- } /* end of FOREACHneighbor */
- facet= neighbor; /* non-NULL only if *dist>bestdist */
- } /* end of while facet (directed search) */
- if (isnewfacets) {
- if (!bestfacet) { /* startfacet is upperdelaunay (or flipped) w/o !flipped newfacet neighbors */
- bestdist= -REALmax/2;
- bestfacet= qh_findbestnew(qh, point, qh->newfacet_list, &bestdist, bestoutside, isoutside, &numpartnew);
- testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
- }else if (!qh->findbest_notsharp && bestdist < -qh->DISTround) {
- if (qh_sharpnewfacets(qh)) {
- /* seldom used, qh_findbestnew will retest all facets */
- zinc_(Zfindnewsharp);
- bestfacet= qh_findbestnew(qh, point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);
- testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
- qh->findbestnew= True;
- }else
- qh->findbest_notsharp= True;
- }
- }
- if (!bestfacet)
- bestfacet= qh_findbestlower(qh, lastfacet, point, &bestdist, numpart); /* lastfacet is non-NULL because startfacet is non-NULL */
- if (testhorizon) /* qh_findbestnew not called */
- bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
- *dist= bestdist;
- if (isoutside && bestdist < qh->MINoutside)
- *isoutside= False;
- LABELreturn_best:
- zadd_(Zfindbesttot, *numpart);
- zmax_(Zfindbestmax, *numpart);
- (*numpart) += numpartnew;
- qh->IStracing= oldtrace;
- return bestfacet;
- } /* findbest */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="findbesthorizon">-</a>
- qh_findbesthorizon(qh, qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
- search coplanar and better horizon facets from startfacet/bestdist
- ischeckmax turns off statistics and minsearch update
- all arguments must be initialized, 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 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
- when called by qh_check_maxout() (qh_IScheckmax)
- startfacet must be closest to the point
- Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum
- even though other facets are below the point.
- updates facet->maxoutside for good, visited facets
- may return NULL
- searchdist is qh.max_outside + 2 * DISTround
- + max( MINvisible('Vn'), MAXcoplanar('Un'));
- This setting is a guess. It must be at least max_outside + 2*DISTround
- because a facet may have a geometric neighbor across a vertex
- design:
- for each horizon facet of coplanar best facets
- continue if clearly inside
- unless upperdelaunay or clearly outside
- update best facet
- */
- facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {
- facetT *bestfacet= startfacet;
- realT dist;
- facetT *neighbor, **neighborp, *facet;
- facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */
- int numpartinit= *numpart, coplanarfacetset_size, 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);
- }else {
- #if qh_MAXoutside
- if ((!qh->ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)
- startfacet->maxoutside= *bestdist;
- #endif
- }
- 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) {
- numfacet++;
- is_5x_minsearch= (ischeckmax && facet->nummerge > 10 && qh_setsize(qh, 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, 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, is_5x_minsearch, searchdist));
- FOREACHneighbor_(facet) {
- if (neighbor->visitid == visitid)
- continue;
- neighbor->visitid= visitid;
- if (!neighbor->flipped) { /* neighbors of flipped facets always searched via nextfacet */
- qh_distplane(qh, 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)) {
- if (!ischeckmax) {
- minsearch= dist - searchdist;
- if (dist > *bestdist + searchdist) {
- zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */
- 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; /* 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, need to search neighbor */
- if (nextfacet) {
- numcoplanar++;
- if (!coplanarfacetset_size++) {
- SETfirst_(qh->coplanarfacetset)= nextfacet;
- SETtruncate_(qh->coplanarfacetset, 1);
- }else
- qh_setappend(qh, &qh->coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv
- and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */
- }
- nextfacet= neighbor;
- } /* end of EACHneighbor */
- facet= nextfacet;
- if (facet)
- nextfacet= NULL;
- else if (!coplanarfacetset_size)
- break;
- else if (!--coplanarfacetset_size) {
- facet= SETfirstt_(qh->coplanarfacetset, facetT);
- SETtruncate_(qh->coplanarfacetset, 0);
- }else
- facet= (facetT *)qh_setdellast(qh->coplanarfacetset);
- } /* while True, i.e., "for each facet in qh.coplanarfacetset" */
- if (!ischeckmax) {
- zadd_(Zfindhorizontot, *numpart - numpartinit);
- zmax_(Zfindhorizonmax, *numpart - numpartinit);
- if (newbest)
- zinc_(Znewbesthorizon);
- }
- trace4((qh, qh->ferr, 4003, "qh_findbesthorizon: p%d, newbest? %d, bestfacet f%d, bestdist %2.2g, numfacet %d, coplanarfacets %d, numdist %d\n",
- qh_pointid(qh, point), newbest, getid_(bestfacet), *bestdist, numfacet, numcoplanar, *numpart - numpartinit));
- return bestfacet;
- } /* findbesthorizon */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="findbestnew">-</a>
- qh_findbestnew(qh, point, startfacet, dist, isoutside, numpart )
- find best newfacet for point
- searches all of qh.newfacet_list starting at startfacet
- searches horizon facets of coplanar best newfacets
- searches all facets if startfacet == qh.facet_list
- returns:
- best new or horizon facet that is not upperdelaunay
- early out if isoutside and not 'Qf'
- dist is distance to facet
- isoutside is true if point is outside of facet
- numpart is number of distance tests
- notes:
- Always used for merged new facets (see qh_USEfindbestnew)
- Avoids upperdelaunay facet unless (isoutside and outside)
- Uses qh.visit_id, qh.coplanarfacetset.
- If share visit_id with qh_findbest, coplanarfacetset is incorrect.
- If merging (testhorizon), searches horizon facets of coplanar best facets because
- a point maybe coplanar to the bestfacet, below its horizon facet,
- and above a horizon facet of a coplanar newfacet. For example,
- rbox 1000 s Z1 G1e-13 | qhull
- rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc
- qh_findbestnew() used if
- qh_sharpnewfacets -- newfacets contains a sharp angle
- if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)
- see also:
- qh_partitionall() and qh_findbest()
- design:
- for each new facet starting from startfacet
- test distance from point to facet
- return facet if clearly outside
- unless upperdelaunay and a lowerdelaunay exists
- update best facet
- test horizon facets
- */
- facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,
- realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
- realT bestdist= -REALmax/2;
- facetT *bestfacet= NULL, *facet;
- int oldtrace= qh->IStracing, i;
- unsigned int visitid= ++qh->visit_id;
- realT distoutside= 0.0;
- boolT isdistoutside; /* True if distoutside is defined */
- boolT testhorizon= True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
- if (!startfacet || !startfacet->next) {
- if (qh->MERGING) {
- qh_fprintf(qh, 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, qh_ERRtopology, NULL, NULL);
- }else {
- qh_fprintf(qh, qh->ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
- qh->furthest_id);
- qh_errexit(qh, qh_ERRqhull, NULL, NULL);
- }
- }
- zinc_(Zfindnew);
- if (qh->BESToutside || bestoutside)
- isdistoutside= False;
- else {
- isdistoutside= True;
- distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user_r.h */
- }
- if (isoutside)
- *isoutside= True;
- *numpart= 0;
- #ifndef qh_NOtrace
- if (qh->IStracing >= 4 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {
- if (qh->TRACElevel > qh->IStracing)
- qh->IStracing= qh->TRACElevel;
- qh_fprintf(qh, qh->ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g,",
- qh_pointid(qh, point), startfacet->id, isdistoutside, distoutside);
- qh_fprintf(qh, 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, 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) {
- if (facet == startfacet && i)
- break;
- facet->visitid= visitid;
- if (!facet->flipped) {
- qh_distplane(qh, point, facet, dist);
- (*numpart)++;
- if (*dist > bestdist) {
- if (!facet->upperdelaunay || *dist >= qh->MINoutside) {
- bestfacet= facet;
- if (isdistoutside && *dist >= distoutside)
- goto LABELreturn_bestnew;
- bestdist= *dist;
- }
- }
- } /* end of !flipped */
- } /* FORALLfacet from startfacet or qh->newfacet_list */
- }
- if (testhorizon || !bestfacet) /* testhorizon is always True. Keep the same code as qh_findbest */
- bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,
- !qh_NOupper, &bestdist, numpart);
- *dist= bestdist;
- if (isoutside && *dist < qh->MINoutside)
- *isoutside= False;
- LABELreturn_bestnew:
- zadd_(Zfindnewtot, *numpart);
- zmax_(Zfindnewmax, *numpart);
- trace4((qh, qh->ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g for p%d f%d bestoutside? %d \n",
- getid_(bestfacet), *dist, qh_pointid(qh, point), startfacet->id, bestoutside));
- qh->IStracing= oldtrace;
- return bestfacet;
- } /* findbestnew */
- /* ============ hyperplane functions -- keep code together [?] ============ */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="backnormal">-</a>
- qh_backnormal(qh, rows, numrow, numcol, sign, normal, nearzero )
- given an upper-triangular rows array and a sign,
- solve for normal equation x using back substitution over rows U
- returns:
- normal= x
- if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),
- if fails on last row
- this means that the hyperplane intersects [0,..,1]
- sets last coordinate of normal to sign
- otherwise
- sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
- sets nearzero
- notes:
- assumes numrow == numcol-1
- see Golub & van Loan, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting"
- solves Ux=b where Ax=b and PA=LU
- b= [0,...,0,sign or 0] (sign is either -1 or +1)
- last row of A= [0,...,0,1]
- 1) Ly=Pb == y=b since P only permutes the 0's of b
- design:
- for each row from end
- perform back substitution
- if near zero
- use qh_divzero for division
- if zero divide and not last row
- set tail of normal to 0
- */
- void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign,
- coordT *normal, boolT *nearzero) {
- int i, j;
- coordT *normalp, *normal_tail, *ai, *ak;
- realT diagonal;
- boolT waszero;
- int zerocol= -1;
- normalp= normal + numcol - 1;
- *normalp--= (sign ? -1.0 : 1.0);
- for (i=numrow; i--; ) {
- *normalp= 0.0;
- ai= rows[i] + i + 1;
- ak= normalp+1;
- for (j=i+1; j < numcol; j++)
- *normalp -= *ai++ * *ak++;
- diagonal= (rows[i])[i];
- if (fabs_(diagonal) > qh->MINdenom_2)
- *(normalp--) /= diagonal;
- else {
- waszero= False;
- *normalp= qh_divzero(*normalp, diagonal, qh->MINdenom_1_2, &waszero);
- if (waszero) {
- zerocol= i;
- *(normalp--)= (sign ? -1.0 : 1.0);
- for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
- *normal_tail= 0.0;
- }else
- normalp--;
- }
- }
- if (zerocol != -1) {
- *nearzero= True;
- trace4((qh, qh->ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
- zzinc_(Zback0);
- qh_joggle_restart(qh, "zero diagonal on back substitution");
- }
- } /* backnormal */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="gausselim">-</a>
- qh_gausselim(qh, rows, numrow, numcol, sign )
- Gaussian elimination with partial pivoting
- returns:
- rows is upper triangular (includes row exchanges)
- flips sign for each row exchange
- sets nearzero if pivot[k] < qh.NEARzero[k], else clears it
- notes:
- if nearzero, the determinant's sign may be incorrect.
- assumes numrow <= numcol
- design:
- for each row
- determine pivot and exchange rows if necessary
- test for near zero
- perform gaussian elimination step
- */
- void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
- realT *ai, *ak, *rowp, *pivotrow;
- realT n, pivot, pivot_abs= 0.0, temp;
- int i, j, k, pivoti, flip=0;
- *nearzero= False;
- for (k=0; k < numrow; k++) {
- pivot_abs= fabs_((rows[k])[k]);
- pivoti= k;
- for (i=k+1; i < numrow; i++) {
- if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
- pivot_abs= temp;
- pivoti= i;
- }
- }
- if (pivoti != k) {
- rowp= rows[pivoti];
- rows[pivoti]= rows[k];
- rows[k]= rowp;
- *sign ^= 1;
- flip ^= 1;
- }
- if (pivot_abs <= qh->NEARzero[k]) {
- *nearzero= True;
- if (pivot_abs == 0.0) { /* remainder of column == 0 */
- #ifndef qh_NOtrace
- if (qh->IStracing >= 4) {
- qh_fprintf(qh, qh->ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh->DISTround);
- qh_printmatrix(qh, qh->ferr, "Matrix:", rows, numrow, numcol);
- }
- #endif
- zzinc_(Zgauss0);
- qh_joggle_restart(qh, "zero pivot for Gaussian elimination");
- goto LABELnextcol;
- }
- }
- pivotrow= rows[k] + k;
- pivot= *pivotrow++; /* signed value of pivot, and remainder of row */
- for (i=k+1; i < numrow; i++) {
- ai= rows[i] + k;
- ak= pivotrow;
- n= (*ai++)/pivot; /* divzero() not needed since |pivot| >= |*ai| */
- for (j= numcol - (k+1); j--; )
- *ai++ -= n * *ak++;
- }
- LABELnextcol:
- ;
- }
- wmin_(Wmindenom, pivot_abs); /* last pivot element */
- if (qh->IStracing >= 5)
- qh_printmatrix(qh, qh->ferr, "qh_gausselem: result", rows, numrow, numcol);
- } /* gausselim */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="getangle">-</a>
- qh_getangle(qh, vect1, vect2 )
- returns the dot product of two vectors
- if qh.RANDOMdist, joggles result
- notes:
- the angle may be > 1.0 or < -1.0 because of roundoff errors
- */
- realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2) {
- realT angle= 0, randr;
- int k;
- for (k=qh->hull_dim; k--; )
- angle += *vect1++ * *vect2++;
- if (qh->RANDOMdist) {
- randr= qh_RANDOMint;
- angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
- qh->RANDOMfactor;
- }
- trace4((qh, qh->ferr, 4006, "qh_getangle: %4.4g\n", angle));
- return(angle);
- } /* getangle */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="getcenter">-</a>
- qh_getcenter(qh, vertices )
- returns arithmetic center of a set of vertices as a new point
- notes:
- allocates point array for center
- */
- pointT *qh_getcenter(qhT *qh, setT *vertices) {
- int k;
- pointT *center, *coord;
- vertexT *vertex, **vertexp;
- int count= qh_setsize(qh, vertices);
- if (count < 2) {
- qh_fprintf(qh, qh->ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
- qh_errexit(qh, qh_ERRqhull, NULL, NULL);
- }
- center= (pointT *)qh_memalloc(qh, qh->normal_size);
- for (k=0; k < qh->hull_dim; k++) {
- coord= center+k;
- *coord= 0.0;
- FOREACHvertex_(vertices)
- *coord += vertex->point[k];
- *coord /= count; /* count>=2 by QH6003 */
- }
- return(center);
- } /* getcenter */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="getcentrum">-</a>
- qh_getcentrum(qh, facet )
- returns the centrum for a facet as a new point
- notes:
- allocates the centrum
- */
- pointT *qh_getcentrum(qhT *qh, facetT *facet) {
- realT dist;
- pointT *centrum, *point;
- point= qh_getcenter(qh, facet->vertices);
- zzinc_(Zcentrumtests);
- qh_distplane(qh, point, facet, &dist);
- centrum= qh_projectpoint(qh, point, facet, dist);
- qh_memfree(qh, point, qh->normal_size);
- trace4((qh, qh->ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
- facet->id, qh_setsize(qh, facet->vertices), dist));
- return centrum;
- } /* getcentrum */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="getdistance">-</a>
- qh_getdistance(qh, facet, neighbor, mindist, maxdist )
- returns the min and max distance to neighbor of non-neighbor vertices in facet
- returns:
- the max absolute value
- design:
- for each vertex of facet that is not in neighbor
- test the distance from vertex to neighbor
- */
- coordT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, coordT *mindist, coordT *maxdist) {
- vertexT *vertex, **vertexp;
- coordT dist, maxd, mind;
- FOREACHvertex_(facet->vertices)
- vertex->seen= False;
- FOREACHvertex_(neighbor->vertices)
- vertex->seen= True;
- mind= 0.0;
- maxd= 0.0;
- FOREACHvertex_(facet->vertices) {
- if (!vertex->seen) {
- zzinc_(Zbestdist);
- qh_distplane(qh, vertex->point, neighbor, &dist);
- if (dist < mind)
- mind= dist;
- else if (dist > maxd)
- maxd= dist;
- }
- }
- *mindist= mind;
- *maxdist= maxd;
- mind= -mind;
- if (maxd > mind)
- return maxd;
- else
- return mind;
- } /* getdistance */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="normalize">-</a>
- qh_normalize(qh, normal, dim, toporient )
- normalize a vector and report if too small
- does not use min norm
- see:
- qh_normalize2
- */
- void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient) {
- qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
- } /* normalize */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="normalize2">-</a>
- qh_normalize2(qh, normal, dim, toporient, minnorm, ismin )
- normalize a vector and report if too small
- qh.MINdenom/MINdenom1 are the upper limits for divide overflow
- returns:
- normalized vector
- flips sign if !toporient
- if minnorm non-NULL,
- sets ismin if normal < minnorm
- notes:
- if zero norm
- sets all elements to sqrt(1.0/dim)
- if divide by zero (divzero())
- sets largest element to +/-1
- bumps Znearlysingular
- design:
- computes norm
- test for minnorm
- if not near zero
- normalizes normal
- else if zero norm
- sets normal to standard value
- else
- uses qh_divzero to normalize
- if nearzero
- sets norm to direction of maximum value
- */
- void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,
- realT *minnorm, boolT *ismin) {
- int k;
- realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
- boolT zerodiv;
- norm1= normal+1;
- norm2= normal+2;
- norm3= normal+3;
- if (dim == 2)
- norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
- else if (dim == 3)
- norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
- else if (dim == 4) {
- norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
- + (*norm3)*(*norm3));
- }else if (dim > 4) {
- norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
- + (*norm3)*(*norm3);
- for (k=dim-4, colp=normal+4; k--; colp++)
- norm += (*colp) * (*colp);
- norm= sqrt(norm);
- }
- if (minnorm) {
- if (norm < *minnorm)
- *ismin= True;
- else
- *ismin= False;
- }
- wmin_(Wmindenom, norm);
- if (norm > qh->MINdenom) {
- if (!toporient)
- norm= -norm;
- *normal /= norm;
- *norm1 /= norm;
- if (dim == 2)
- ; /* all done */
- else if (dim == 3)
- *norm2 /= norm;
- else if (dim == 4) {
- *norm2 /= norm;
- *norm3 /= norm;
- }else if (dim >4) {
- *norm2 /= norm;
- *norm3 /= norm;
- for (k=dim-4, colp=normal+4; k--; )
- *colp++ /= norm;
- }
- }else if (norm == 0.0) {
- temp= sqrt(1.0/dim);
- for (k=dim, colp=normal; k--; )
- *colp++= temp;
- }else {
- if (!toporient)
- norm= -norm;
- for (k=dim, colp=normal; k--; colp++) { /* k used below */
- temp= qh_divzero(*colp, norm, qh->MINdenom_1, &zerodiv);
- if (!zerodiv)
- *colp= temp;
- else {
- maxp= qh_maxabsval(normal, dim);
- temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
- for (k=dim, colp=normal; k--; colp++)
- *colp= 0.0;
- *maxp= temp;
- zzinc_(Znearlysingular);
- /* qh_joggle_restart ignored for Znearlysingular, normal part of qh_sethyperplane_gauss */
- trace0((qh, qh->ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
- norm, qh->furthest_id));
- return;
- }
- }
- }
- } /* normalize */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="projectpoint">-</a>
- qh_projectpoint(qh, point, facet, dist )
- project point onto a facet by dist
- returns:
- returns a new point
- notes:
- if dist= distplane(point,facet)
- this projects point to hyperplane
- assumes qh_memfree_() is valid for normal_size
- */
- pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist) {
- pointT *newpoint, *np, *normal;
- int normsize= qh->normal_size;
- int k;
- void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
- qh_memalloc_(qh, normsize, freelistp, newpoint, pointT);
- np= newpoint;
- normal= facet->normal;
- for (k=qh->hull_dim; k--; )
- *(np++)= *point++ - dist * *normal++;
- return(newpoint);
- } /* projectpoint */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="setfacetplane">-</a>
- qh_setfacetplane(qh, facet )
- sets the hyperplane for a facet
- if qh.RANDOMdist, joggles hyperplane
- notes:
- uses global buffers qh.gm_matrix and qh.gm_row
- overwrites facet->normal if already defined
- updates Wnewvertex if PRINTstatistics
- sets facet->upperdelaunay if upper envelope of Delaunay triangulation
- design:
- copy vertex coordinates to qh.gm_matrix/gm_row
- compute determinate
- if nearzero
- recompute determinate with gaussian elimination
- if nearzero
- force outside orientation by testing interior point
- */
- void qh_setfacetplane(qhT *qh, facetT *facet) {
- pointT *point;
- vertexT *vertex, **vertexp;
- int normsize= qh->normal_size;
- int k,i, oldtrace= 0;
- realT dist;
- void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
- coordT *coord, *gmcoord;
- pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
- boolT nearzero= False;
- zzinc_(Zsetplane);
- if (!facet->normal)
- qh_memalloc_(qh, normsize, freelistp, facet->normal, coordT);
- #ifndef qh_NOtrace
- if (facet == qh->tracefacet) {
- oldtrace= qh->IStracing;
- qh->IStracing= 5;
- qh_fprintf(qh, qh->ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);
- qh_fprintf(qh, qh->ferr, 8013, " Last point added to hull was p%d.", qh->furthest_id);
- if (zzval_(Ztotmerge))
- qh_fprintf(qh, qh->ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge));
- qh_fprintf(qh, qh->ferr, 8015, "\n\nCurrent summary is:\n");
- qh_printsummary(qh, qh->ferr);
- }
- #endif
- if (qh->hull_dim <= 4) {
- i= 0;
- if (qh->RANDOMdist) {
- gmcoord= qh->gm_matrix;
- FOREACHvertex_(facet->vertices) {
- qh->gm_row[i++]= gmcoord;
- coord= vertex->point;
- for (k=qh->hull_dim; k--; )
- *(gmcoord++)= *coord++ * qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);
- }
- }else {
- FOREACHvertex_(facet->vertices)
- qh->gm_row[i++]= vertex->point;
- }
- qh_sethyperplane_det(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,
- facet->normal, &facet->offset, &nearzero);
- }
- if (qh->hull_dim > 4 || nearzero) {
- i= 0;
- gmcoord= qh->gm_matrix;
- FOREACHvertex_(facet->vertices) {
- if (vertex->point != point0) {
- qh->gm_row[i++]= gmcoord;
- coord= vertex->point;
- point= point0;
- for (k=qh->hull_dim; k--; )
- *(gmcoord++)= *coord++ - *point++;
- }
- }
- qh->gm_row[i]= gmcoord; /* for areasimplex */
- if (qh->RANDOMdist) {
- gmcoord= qh->gm_matrix;
- for (i=qh->hull_dim-1; i--; ) {
- for (k=qh->hull_dim; k--; )
- *(gmcoord++) *= qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);
- }
- }
- qh_sethyperplane_gauss(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,
- facet->normal, &facet->offset, &nearzero);
- if (nearzero) {
- if (qh_orientoutside(qh, facet)) {
- trace0((qh, qh->ferr, 2, "qh_setfacetplane: flipped orientation 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
- 0 0 0 1 0
- 0 1 0 0 0
- 1 0 0 0 0
- norm= 0.38 0.38 -0.76 0.38 0
- has a determinate of 1, but g.e. after subtracting pt. 0 has
- 0's in the diagonal, even with full pivoting. It does work
- if you subtract pt. 4 instead. */
- }
- }
- }
- facet->upperdelaunay= False;
- if (qh->DELAUNAY) {
- if (qh->UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */
- if (facet->normal[qh->hull_dim -1] >= qh->ANGLEround * qh_ZEROdelaunay)
- facet->upperdelaunay= True;
- }else {
- if (facet->normal[qh->hull_dim -1] > -qh->ANGLEround * qh_ZEROdelaunay)
- facet->upperdelaunay= True;
- }
- }
- if (qh->PRINTstatistics || qh->IStracing || qh->TRACElevel || qh->JOGGLEmax < REALmax) {
- qh->old_randomdist= qh->RANDOMdist;
- qh->RANDOMdist= False;
- FOREACHvertex_(facet->vertices) {
- if (vertex->point != point0) {
- boolT istrace= False;
- zinc_(Zdiststat);
- qh_distplane(qh, vertex->point, facet, &dist);
- dist= fabs_(dist);
- zinc_(Znewvertex);
- wadd_(Wnewvertex, dist);
- if (dist > wwval_(Wnewvertexmax)) {
- wwval_(Wnewvertexmax)= dist;
- if (dist > qh->max_outside) {
- qh->max_outside= dist; /* used by qh_maxouter(qh) */
- if (dist > qh->TRACEdist)
- istrace= True;
- }
- }else if (-dist > qh->TRACEdist)
- istrace= True;
- if (istrace) {
- qh_fprintf(qh, qh->ferr, 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(qh, vertex->point), vertex->id, dist, facet->id, qh->furthest_id);
- qh_errprint(qh, "DISTANT", facet, NULL, NULL, NULL);
- }
- }
- }
- qh->RANDOMdist= qh->old_randomdist;
- }
- #ifndef qh_NOtrace
- if (qh->IStracing >= 4) {
- qh_fprintf(qh, qh->ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ",
- facet->id, facet->offset);
- for (k=0; k < qh->hull_dim; k++)
- qh_fprintf(qh, qh->ferr, 8018, "%2.2g ", facet->normal[k]);
- qh_fprintf(qh, qh->ferr, 8019, "\n");
- }
- #endif
- qh_checkflipped(qh, facet, NULL, qh_ALL);
- if (facet == qh->tracefacet) {
- qh->IStracing= oldtrace;
- qh_printfacet(qh, qh->ferr, facet);
- }
- } /* setfacetplane */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="sethyperplane_det">-</a>
- qh_sethyperplane_det(qh, dim, rows, point0, toporient, normal, offset, nearzero )
- given dim X dim array indexed by rows[], one row per point,
- toporient(flips all signs),
- and point0 (any row)
- set normalized hyperplane equation from oriented simplex
- returns:
- normal (normalized)
- offset (places point0 on the hyperplane)
- sets nearzero if hyperplane not through points
- notes:
- only defined for dim == 2..4
- rows[] is not modified
- solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
- see Bower & Woodworth, A programmer's geometry, Butterworths 1983.
- derivation of 3-d minnorm
- Goal: all vertices V_i within qh.one_merge of hyperplane
- Plan: exactly translate the facet so that V_0 is the origin
- exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
- exactly rotate the effective perturbation to only effect n_0
- this introduces a factor of sqrt(3)
- n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
- Let M_d be the max coordinate difference
- Let M_a be the greater of M_d and the max abs. coordinate
- Let u be machine roundoff and distround be max error for distance computation
- The max error for n_0 is sqrt(3) u M_a M_d / norm. n_1 is approx. 1 and n_2 is approx. 0
- The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm. Offset=0 at origin
- Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
- Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d
- derivation of 4-d minnorm
- same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
- [if two vertices fixed on x-axis, can rotate the other two in yzw.]
- n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
- [all other terms contain at least two factors nearly zero.]
- The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
- Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
- Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
- */
- void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,
- boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
- realT maxround, dist;
- int i;
- pointT *point;
- if (dim == 2) {
- normal[0]= dY(1,0);
- normal[1]= dX(0,1);
- qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
- *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
- *nearzero= False; /* since nearzero norm => incident points */
- }else if (dim == 3) {
- normal[0]= det2_(dY(2,0), dZ(2,0),
- dY(1,0), dZ(1,0));
- normal[1]= det2_(dX(1,0), dZ(1,0),
- dX(2,0), dZ(2,0));
- normal[2]= det2_(dX(2,0), dY(2,0),
- dX(1,0), dY(1,0));
- qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
- *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
- + point0[2]*normal[2]);
- maxround= qh->DISTround;
- for (i=dim; i--; ) {
- point= rows[i];
- if (point != point0) {
- dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
- + point[2]*normal[2]);
- if (dist > maxround || dist < -maxround) {
- *nearzero= True;
- break;
- }
- }
- }
- }else if (dim == 4) {
- normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),
- dY(1,0), dZ(1,0), dW(1,0),
- dY(3,0), dZ(3,0), dW(3,0));
- normal[1]= det3_(dX(2,0), dZ(2,0), dW(2,0),
- dX(1,0), dZ(1,0), dW(1,0),
- dX(3,0), dZ(3,0), dW(3,0));
- normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),
- dX(1,0), dY(1,0), dW(1,0),
- dX(3,0), dY(3,0), dW(3,0));
- normal[3]= det3_(dX(2,0), dY(2,0), dZ(2,0),
- dX(1,0), dY(1,0), dZ(1,0),
- dX(3,0), dY(3,0), dZ(3,0));
- qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
- *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
- + point0[2]*normal[2] + point0[3]*normal[3]);
- maxround= qh->DISTround;
- for (i=dim; i--; ) {
- point= rows[i];
- if (point != point0) {
- dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
- + point[2]*normal[2] + point[3]*normal[3]);
- if (dist > maxround || dist < -maxround) {
- *nearzero= True;
- break;
- }
- }
- }
- }
- if (*nearzero) {
- zzinc_(Zminnorm);
- /* qh_joggle_restart not needed, will call qh_sethyperplane_gauss instead */
- trace0((qh, qh->ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d, use qh_sethyperplane_gauss instead.\n", qh->furthest_id));
- }
- } /* sethyperplane_det */
- /*-<a href="qh-geom_r.htm#TOC"
- >-------------------------------</a><a name="sethyperplane_gauss">-</a>
- qh_sethyperplane_gauss(qh, dim, rows, point0, toporient, normal, offset, nearzero )
- given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
- set normalized hyperplane equation from oriented simplex
- returns:
- normal (normalized)
- offset (places point0 on the hyperplane)
- notes:
- if nearzero
- orientation may be incorrect because of incorrect sign flips in gausselim
- solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
- or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
- i.e., N is normal to the hyperplane, and the unnormalized
- distance to [0 .. 1] is either 1 or 0
- design:
- perform gaussian elimination
- flip sign for negative values
- perform back substitution
- normalize result
- compute offset
- */
- void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
- boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
- coordT *pointcoord, *normalcoef;
- int k;
- boolT sign= toporient, nearzero2= False;
- qh_gausselim(qh, rows, dim-1, dim, &sign, nearzero);
- for (k=dim-1; k--; ) {
- if ((rows[k])[k] < 0)
- sign ^= 1;
- }
- if (*nearzero) {
- zzinc_(Znearlysingular);
- /* qh_joggle_restart ignored for Znearlysingular, normal part of qh_sethyperplane_gauss */
- trace0((qh, qh->ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh->furthest_id));
- qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
- }else {
- qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
- if (nearzero2) {
- zzinc_(Znearlysingular);
- trace0((qh, qh->ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh->furthest_id));
- }
- }
- if (nearzero2)
- *nearzero= True;
- qh_normalize2(qh, normal, dim, True, NULL, NULL);
- pointcoord= point0;
- normalcoef= normal;
- *offset= -(*pointcoord++ * *normalcoef++);
- for (k=dim-1; k--; )
- *offset -= *pointcoord++ * *normalcoef++;
- } /* sethyperplane_gauss */
|