ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/trunk/src/QuickHull/geom.c
Revision: 1138
Committed: Tue May 29 22:51:00 2007 UTC (17 years, 11 months ago) by chuckv
Content type: text/plain
File size: 40665 byte(s)
Log Message:
Addded QuickHull to cvs.

File Contents

# User Rev Content
1 chuckv 1138 /*<html><pre> -<a href="qh-geom.htm"
2     >-------------------------------</a><a name="TOP">-</a>
3    
4     geom.c
5     geometric routines of qhull
6    
7     see qh-geom.htm and geom.h
8    
9     copyright (c) 1993-2003 The Geometry Center
10    
11     infrequent code goes into geom2.c
12     */
13    
14     #include "QuickHull/qhull_a.h"
15    
16     /*-<a href="qh-geom.htm#TOC"
17     >-------------------------------</a><a name="distplane">-</a>
18    
19     qh_distplane( point, facet, dist )
20     return distance from point to facet
21    
22     returns:
23     dist
24     if qh.RANDOMdist, joggles result
25    
26     notes:
27     dist > 0 if point is above facet (i.e., outside)
28     does not error (for sortfacets)
29    
30     see:
31     qh_distnorm in geom2.c
32     */
33     void qh_distplane (pointT *point, facetT *facet, realT *dist) {
34     coordT *normal= facet->normal, *coordp, randr;
35     int k;
36    
37     switch(qh hull_dim){
38     case 2:
39     *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];
40     break;
41     case 3:
42     *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
43     break;
44     case 4:
45     *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
46     break;
47     case 5:
48     *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
49     break;
50     case 6:
51     *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];
52     break;
53     case 7:
54     *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];
55     break;
56     case 8:
57     *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];
58     break;
59     default:
60     *dist= facet->offset;
61     coordp= point;
62     for (k= qh hull_dim; k--; )
63     *dist += *coordp++ * *normal++;
64     break;
65     }
66     zinc_(Zdistplane);
67     if (!qh RANDOMdist && qh IStracing < 4)
68     return;
69     if (qh RANDOMdist) {
70     randr= qh_RANDOMint;
71     *dist += (2.0 * randr / qh_RANDOMmax - 1.0) *
72     qh RANDOMfactor * qh MAXabs_coord;
73     }
74     if (qh IStracing >= 4) {
75     fprintf (qh ferr, "qh_distplane: ");
76     fprintf (qh ferr, qh_REAL_1, *dist);
77     fprintf (qh ferr, "from p%d to f%d\n", qh_pointid(point), facet->id);
78     }
79     return;
80     } /* distplane */
81    
82    
83     /*-<a href="qh-geom.htm#TOC"
84     >-------------------------------</a><a name="findbest">-</a>
85    
86     qh_findbest( point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
87     find facet that is furthest below a point
88     for upperDelaunay facets
89     returns facet only if !qh_NOupper and clearly above
90    
91     input:
92     starts search at 'startfacet' (can not be flipped)
93     if !bestoutside (qh_ALL), stops at qh.MINoutside
94    
95     returns:
96     best facet (reports error if NULL)
97     early out if isoutside defined and bestdist > qh.MINoutside
98     dist is distance to facet
99     isoutside is true if point is outside of facet
100     numpart counts the number of distance tests
101    
102     see also:
103     qh_findbestnew()
104    
105     notes:
106     If merging (testhorizon), searches horizon facets of coplanar best facets because
107     after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
108     avoid calls to distplane, function calls, and real number operations.
109     caller traces result
110     Optimized for outside points. Tried recording a search set for qh_findhorizon.
111     Made code more complicated.
112    
113     when called by qh_partitionvisible():
114     indicated by qh_ISnewfacets
115     qh.newfacet_list is list of simplicial, new facets
116     qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
117     qh.bestfacet_notsharp set if qh_sharpnewfacets returns False
118    
119     when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
120     qh_check_bestdist(), qh_addpoint()
121     indicated by !qh_ISnewfacets
122     returns best facet in neighborhood of given facet
123     this is best facet overall if dist > - qh.MAXcoplanar
124     or hull has at least a "spherical" curvature
125    
126     design:
127     initialize and test for early exit
128     repeat while there are better facets
129     for each neighbor of facet
130     exit if outside facet found
131     test for better facet
132     if point is inside and partitioning
133     test for new facets with a "sharp" intersection
134     if so, future calls go to qh_findbestnew()
135     test horizon facets
136     */
137     facetT *qh_findbest (pointT *point, facetT *startfacet,
138     boolT bestoutside, boolT isnewfacets, boolT noupper,
139     realT *dist, boolT *isoutside, int *numpart) {
140     realT bestdist= -REALmax/2 /* avoid underflow */;
141     facetT *facet, *neighbor, **neighborp;
142     facetT *bestfacet= NULL, *lastfacet= NULL;
143     int oldtrace= qh IStracing;
144     unsigned int visitid= ++qh visit_id;
145     int numpartnew=0;
146     boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
147    
148     zinc_(Zfindbest);
149     if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid (point))) {
150     if (qh TRACElevel > qh IStracing)
151     qh IStracing= qh TRACElevel;
152     fprintf (qh ferr, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",
153     qh_pointid(point), startfacet->id, isnewfacets, bestoutside, qh MINoutside);
154     fprintf(qh ferr, " testhorizon? %d noupper? %d", testhorizon, noupper);
155     fprintf (qh ferr, " Last point added was p%d.", qh furthest_id);
156     fprintf(qh ferr, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh max_outside);
157     }
158     if (isoutside)
159     *isoutside= True;
160     if (!startfacet->flipped) { /* test startfacet */
161     *numpart= 1;
162     qh_distplane (point, startfacet, dist); /* this code is duplicated below */
163     if (!bestoutside && *dist >= qh MINoutside
164     && (!startfacet->upperdelaunay || !noupper)) {
165     bestfacet= startfacet;
166     goto LABELreturn_best;
167     }
168     bestdist= *dist;
169     if (!startfacet->upperdelaunay) {
170     bestfacet= startfacet;
171     }
172     }else
173     *numpart= 0;
174     startfacet->visitid= visitid;
175     facet= startfacet;
176     while (facet) {
177     trace4((qh ferr, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n", facet->id, bestdist, getid_(bestfacet)));
178     lastfacet= facet;
179     FOREACHneighbor_(facet) {
180     if (!neighbor->newfacet && isnewfacets)
181     continue;
182     if (neighbor->visitid == visitid)
183     continue;
184     neighbor->visitid= visitid;
185     if (!neighbor->flipped) { /* code duplicated above */
186     (*numpart)++;
187     qh_distplane (point, neighbor, dist);
188     if (*dist > bestdist) {
189     if (!bestoutside && *dist >= qh MINoutside
190     && (!neighbor->upperdelaunay || !noupper)) {
191     bestfacet= neighbor;
192     goto LABELreturn_best;
193     }
194     if (!neighbor->upperdelaunay) {
195     bestfacet= neighbor;
196     bestdist= *dist;
197     break; /* switch to neighbor */
198     }else if (!bestfacet) {
199     bestdist= *dist;
200     break; /* switch to neighbor */
201     }
202     } /* end of *dist>bestdist */
203     } /* end of !flipped */
204     } /* end of FOREACHneighbor */
205     facet= neighbor; /* non-NULL only if *dist>bestdist */
206     } /* end of while facet (directed search) */
207     if (isnewfacets) {
208     if (!bestfacet) {
209     bestdist= -REALmax/2;
210     bestfacet= qh_findbestnew (point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew);
211     testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
212     }else if (!qh findbest_notsharp && bestdist < - qh DISTround) {
213     if (qh_sharpnewfacets()) {
214     /* seldom used, qh_findbestnew will retest all facets */
215     zinc_(Zfindnewsharp);
216     bestfacet= qh_findbestnew (point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);
217     testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
218     qh findbestnew= True;
219     }else
220     qh findbest_notsharp= True;
221     }
222     }
223     if (!bestfacet)
224     bestfacet= qh_findbestlower (lastfacet, point, &bestdist, numpart);
225     if (testhorizon)
226     bestfacet= qh_findbesthorizon (!qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
227     *dist= bestdist;
228     if (isoutside && bestdist < qh MINoutside)
229     *isoutside= False;
230     LABELreturn_best:
231     zadd_(Zfindbesttot, *numpart);
232     zmax_(Zfindbestmax, *numpart);
233     (*numpart) += numpartnew;
234     qh IStracing= oldtrace;
235     return bestfacet;
236     } /* findbest */
237    
238    
239     /*-<a href="qh-geom.htm#TOC"
240     >-------------------------------</a><a name="findbesthorizon">-</a>
241    
242     qh_findbesthorizon( qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
243     search coplanar and better horizon facets from startfacet/bestdist
244     ischeckmax turns off statistics and minsearch update
245     all arguments must be initialized
246     returns (ischeckmax):
247     best facet
248     returns (!ischeckmax):
249     best facet that is not upperdelaunay
250     allows upperdelaunay that is clearly outside
251     returns:
252     bestdist is distance to bestfacet
253     numpart -- updates number of distance tests
254    
255     notes:
256     no early out -- use qh_findbest() or qh_findbestnew()
257     Searches coplanar or better horizon facets
258    
259     when called by qh_check_maxout() (qh_IScheckmax)
260     startfacet must be closest to the point
261     Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum
262     even though other facets are below the point.
263     updates facet->maxoutside for good, visited facets
264     may return NULL
265    
266     searchdist is qh.max_outside + 2 * DISTround
267     + max( MINvisible('Vn'), MAXcoplanar('Un'));
268     This setting is a guess. It must be at least max_outside + 2*DISTround
269     because a facet may have a geometric neighbor across a vertex
270    
271     design:
272     for each horizon facet of coplanar best facets
273     continue if clearly inside
274     unless upperdelaunay or clearly outside
275     update best facet
276     */
277     facetT *qh_findbesthorizon (boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {
278     facetT *bestfacet= startfacet;
279     realT dist;
280     facetT *neighbor, **neighborp, *facet;
281     facetT *nextfacet= NULL; /* optimize last facet of coplanarset */
282     int numpartinit= *numpart, coplanarset_size;
283     unsigned int visitid= ++qh visit_id;
284     boolT newbest= False; /* for tracing */
285     realT minsearch, searchdist; /* skip facets that are too far from point */
286    
287     if (!ischeckmax) {
288     zinc_(Zfindhorizon);
289     }else {
290     #if qh_MAXoutside
291     if ((!qh ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)
292     startfacet->maxoutside= *bestdist;
293     #endif
294     }
295     searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */
296     minsearch= *bestdist - searchdist;
297     if (ischeckmax) {
298     /* Always check coplanar facets. Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */
299     minimize_(minsearch, -searchdist);
300     }
301     coplanarset_size= 0;
302     facet= startfacet;
303     while (True) {
304     trace4((qh ferr, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n", facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper, minsearch, searchdist));
305     FOREACHneighbor_(facet) {
306     if (neighbor->visitid == visitid)
307     continue;
308     neighbor->visitid= visitid;
309     if (!neighbor->flipped) {
310     qh_distplane (point, neighbor, &dist);
311     (*numpart)++;
312     if (dist > *bestdist) {
313     if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh MINoutside)) {
314     bestfacet= neighbor;
315     *bestdist= dist;
316     newbest= True;
317     if (!ischeckmax) {
318     minsearch= dist - searchdist;
319     if (dist > *bestdist + searchdist) {
320     zinc_(Zfindjump); /* everything in qh.coplanarset at least searchdist below */
321     coplanarset_size= 0;
322     }
323     }
324     }
325     }else if (dist < minsearch)
326     continue; /* if ischeckmax, dist can't be positive */
327     #if qh_MAXoutside
328     if (ischeckmax && dist > neighbor->maxoutside)
329     neighbor->maxoutside= dist;
330     #endif
331     } /* end of !flipped */
332     if (nextfacet) {
333     if (!coplanarset_size++) {
334     SETfirst_(qh coplanarset)= nextfacet;
335     SETtruncate_(qh coplanarset, 1);
336     }else
337     qh_setappend (&qh coplanarset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv
338     and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */
339     }
340     nextfacet= neighbor;
341     } /* end of EACHneighbor */
342     facet= nextfacet;
343     if (facet)
344     nextfacet= NULL;
345     else if (!coplanarset_size)
346     break;
347     else if (!--coplanarset_size) {
348     facet= SETfirst_(qh coplanarset);
349     SETtruncate_(qh coplanarset, 0);
350     }else
351     facet= (facetT*)qh_setdellast (qh coplanarset);
352     } /* while True, for each facet in qh.coplanarset */
353     if (!ischeckmax) {
354     zadd_(Zfindhorizontot, *numpart - numpartinit);
355     zmax_(Zfindhorizonmax, *numpart - numpartinit);
356     if (newbest)
357     zinc_(Zparthorizon);
358     }
359     trace4((qh ferr, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));
360     return bestfacet;
361     } /* findbesthorizon */
362    
363     /*-<a href="qh-geom.htm#TOC"
364     >-------------------------------</a><a name="findbestnew">-</a>
365    
366     qh_findbestnew( point, startfacet, dist, isoutside, numpart )
367     find best newfacet for point
368     searches all of qh.newfacet_list starting at startfacet
369     searches horizon facets of coplanar best newfacets
370     searches all facets if startfacet == qh.facet_list
371     returns:
372     best new or horizon facet that is not upperdelaunay
373     early out if isoutside and not 'Qf'
374     dist is distance to facet
375     isoutside is true if point is outside of facet
376     numpart is number of distance tests
377    
378     notes:
379     Always used for merged new facets (see qh_USEfindbestnew)
380     Avoids upperdelaunay facet unless (isoutside and outside)
381    
382     Uses qh.visit_id, qh.coplanarset.
383     If share visit_id with qh_findbest, coplanarset is incorrect.
384    
385     If merging (testhorizon), searches horizon facets of coplanar best facets because
386     a point maybe coplanar to the bestfacet, below its horizon facet,
387     and above a horizon facet of a coplanar newfacet. For example,
388     rbox 1000 s Z1 G1e-13 | qhull
389     rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc
390    
391     qh_findbestnew() used if
392     qh_sharpnewfacets -- newfacets contains a sharp angle
393     if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)
394    
395     see also:
396     qh_partitionall() and qh_findbest()
397    
398     design:
399     for each new facet starting from startfacet
400     test distance from point to facet
401     return facet if clearly outside
402     unless upperdelaunay and a lowerdelaunay exists
403     update best facet
404     test horizon facets
405     */
406     facetT *qh_findbestnew (pointT *point, facetT *startfacet,
407     realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
408     realT bestdist= -REALmax/2;
409     facetT *bestfacet= NULL, *facet;
410     int oldtrace= qh IStracing, i;
411     unsigned int visitid= ++qh visit_id;
412     realT distoutside= 0.0;
413     boolT isdistoutside; /* True if distoutside is defined */
414     boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
415    
416     if (!startfacet) {
417     if (qh MERGING)
418     fprintf(qh ferr, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n");
419     else
420     fprintf(qh ferr, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
421     qh furthest_id);
422     qh_errexit (qh_ERRqhull, NULL, NULL);
423     }
424     zinc_(Zfindnew);
425     if (qh BESToutside || bestoutside)
426     isdistoutside= False;
427     else {
428     isdistoutside= True;
429     distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
430     }
431     if (isoutside)
432     *isoutside= True;
433     *numpart= 0;
434     if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid (point))) {
435     if (qh TRACElevel > qh IStracing)
436     qh IStracing= qh TRACElevel;
437     fprintf(qh ferr, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",
438     qh_pointid(point), startfacet->id, isdistoutside, distoutside);
439     fprintf(qh ferr, " Last point added p%d visitid %d.", qh furthest_id, visitid);
440     fprintf(qh ferr, " Last merge was #%d.\n", zzval_(Ztotmerge));
441     }
442     /* visit all new facets starting with startfacet, maybe qh facet_list */
443     for (i= 0, facet= startfacet; i < 2; i++, facet= qh newfacet_list) {
444     FORALLfacet_(facet) {
445     if (facet == startfacet && i)
446     break;
447     facet->visitid= visitid;
448     if (!facet->flipped) {
449     qh_distplane (point, facet, dist);
450     (*numpart)++;
451     if (*dist > bestdist) {
452     if (!facet->upperdelaunay || *dist >= qh MINoutside) {
453     bestfacet= facet;
454     if (isdistoutside && *dist >= distoutside)
455     goto LABELreturn_bestnew;
456     bestdist= *dist;
457     }
458     }
459     } /* end of !flipped */
460     } /* FORALLfacet from startfacet or qh newfacet_list */
461     }
462     if (testhorizon || !bestfacet)
463     bestfacet= qh_findbesthorizon (!qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,
464     !qh_NOupper, &bestdist, numpart);
465     *dist= bestdist;
466     if (isoutside && *dist < qh MINoutside)
467     *isoutside= False;
468     LABELreturn_bestnew:
469     zadd_(Zfindnewtot, *numpart);
470     zmax_(Zfindnewmax, *numpart);
471     trace4((qh ferr, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist));
472     qh IStracing= oldtrace;
473     return bestfacet;
474     } /* findbestnew */
475    
476     /* ============ hyperplane functions -- keep code together [?] ============ */
477    
478     /*-<a href="qh-geom.htm#TOC"
479     >-------------------------------</a><a name="backnormal">-</a>
480    
481     qh_backnormal( rows, numrow, numcol, sign, normal, nearzero )
482     given an upper-triangular rows array and a sign,
483     solve for normal equation x using back substitution over rows U
484    
485     returns:
486     normal= x
487    
488     if will not be able to divzero() when normalized (qh.MINdenom_2 and qh.MINdenom_1_2),
489     if fails on last row
490     this means that the hyperplane intersects [0,..,1]
491     sets last coordinate of normal to sign
492     otherwise
493     sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
494     sets nearzero
495    
496     notes:
497     assumes numrow == numcol-1
498    
499     see Golub & van Loan 4.4-9 for back substitution
500    
501     solves Ux=b where Ax=b and PA=LU
502     b= [0,...,0,sign or 0] (sign is either -1 or +1)
503     last row of A= [0,...,0,1]
504    
505     1) Ly=Pb == y=b since P only permutes the 0's of b
506    
507     design:
508     for each row from end
509     perform back substitution
510     if near zero
511     please use qh_divzero for division
512     if zero divide and not last row
513     set tail of normal to 0
514     */
515     void qh_backnormal (realT **rows, int numrow, int numcol, boolT sign,
516     coordT *normal, boolT *nearzero) {
517     int i, j;
518     coordT *normalp, *normal_tail, *ai, *ak;
519     realT diagonal;
520     boolT waszero;
521     int zerocol= -1;
522    
523     normalp= normal + numcol - 1;
524     *normalp--= (sign ? -1.0 : 1.0);
525     for(i= numrow; i--; ) {
526     *normalp= 0.0;
527     ai= rows[i] + i + 1;
528     ak= normalp+1;
529     for(j= i+1; j < numcol; j++)
530     *normalp -= *ai++ * *ak++;
531     diagonal= (rows[i])[i];
532     if (fabs_(diagonal) > qh MINdenom_2)
533     *(normalp--) /= diagonal;
534     else {
535     waszero= False;
536     *normalp= qh_divzero (*normalp, diagonal, qh MINdenom_1_2, &waszero);
537     if (waszero) {
538     zerocol= i;
539     *(normalp--)= (sign ? -1.0 : 1.0);
540     for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
541     *normal_tail= 0.0;
542     }else
543     normalp--;
544     }
545     }
546     if (zerocol != -1) {
547     zzinc_(Zback0);
548     *nearzero= True;
549     trace4((qh ferr, "qh_backnormal: zero diagonal at column %d.\n", i));
550     qh_precision ("zero diagonal on back substitution");
551     }
552     } /* backnormal */
553    
554     /*-<a href="qh-geom.htm#TOC"
555     >-------------------------------</a><a name="gausselim">-</a>
556    
557     qh_gausselim( rows, numrow, numcol, sign )
558     Gaussian elimination with partial pivoting
559    
560     returns:
561     rows is upper triangular (includes row exchanges)
562     flips sign for each row exchange
563     sets nearzero if pivot[k] < qh.NEARzero[k], else clears it
564    
565     notes:
566     if nearzero, the determinant's sign may be incorrect.
567     assumes numrow <= numcol
568    
569     design:
570     for each row
571     determine pivot and exchange rows if necessary
572     test for near zero
573     perform gaussian elimination step
574     */
575     void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
576     realT *ai, *ak, *rowp, *pivotrow;
577     realT n, pivot, pivot_abs= 0.0, temp;
578     int i, j, k, pivoti, flip=0;
579    
580     *nearzero= False;
581     for(k= 0; k < numrow; k++) {
582     pivot_abs= fabs_((rows[k])[k]);
583     pivoti= k;
584     for(i= k+1; i < numrow; i++) {
585     if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
586     pivot_abs= temp;
587     pivoti= i;
588     }
589     }
590     if (pivoti != k) {
591     rowp= rows[pivoti];
592     rows[pivoti]= rows[k];
593     rows[k]= rowp;
594     *sign ^= 1;
595     flip ^= 1;
596     }
597     if (pivot_abs <= qh NEARzero[k]) {
598     *nearzero= True;
599     if (pivot_abs == 0.0) { /* remainder of column == 0 */
600     if (qh IStracing >= 4) {
601     fprintf (qh ferr, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh DISTround);
602     qh_printmatrix (qh ferr, "Matrix:", rows, numrow, numcol);
603     }
604     zzinc_(Zgauss0);
605     qh_precision ("zero pivot for Gaussian elimination");
606     goto LABELnextcol;
607     }
608     }
609     pivotrow= rows[k] + k;
610     pivot= *pivotrow++; /* signed value of pivot, and remainder of row */
611     for(i= k+1; i < numrow; i++) {
612     ai= rows[i] + k;
613     ak= pivotrow;
614     n= (*ai++)/pivot; /* divzero() not needed since |pivot| >= |*ai| */
615     for(j= numcol - (k+1); j--; )
616     *ai++ -= n * *ak++;
617     }
618     LABELnextcol:
619     ;
620     }
621     wmin_(Wmindenom, pivot_abs); /* last pivot element */
622     if (qh IStracing >= 5)
623     qh_printmatrix (qh ferr, "qh_gausselem: result", rows, numrow, numcol);
624     } /* gausselim */
625    
626    
627     /*-<a href="qh-geom.htm#TOC"
628     >-------------------------------</a><a name="getangle">-</a>
629    
630     qh_getangle( vect1, vect2 )
631     returns the dot product of two vectors
632     if qh.RANDOMdist, joggles result
633    
634     notes:
635     the angle may be > 1.0 or < -1.0 because of roundoff errors
636    
637     */
638     realT qh_getangle(pointT *vect1, pointT *vect2) {
639     realT angle= 0, randr;
640     int k;
641    
642     for(k= qh hull_dim; k--; )
643     angle += *vect1++ * *vect2++;
644     if (qh RANDOMdist) {
645     randr= qh_RANDOMint;
646     angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
647     qh RANDOMfactor;
648     }
649     trace4((qh ferr, "qh_getangle: %2.2g\n", angle));
650     return(angle);
651     } /* getangle */
652    
653    
654     /*-<a href="qh-geom.htm#TOC"
655     >-------------------------------</a><a name="getcenter">-</a>
656    
657     qh_getcenter( vertices )
658     returns arithmetic center of a set of vertices as a new point
659    
660     notes:
661     allocates point array for center
662     */
663     pointT *qh_getcenter(setT *vertices) {
664     int k;
665     pointT *center, *coord;
666     vertexT *vertex, **vertexp;
667     int count= qh_setsize(vertices);
668    
669     if (count < 2) {
670     fprintf (qh ferr, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
671     qh_errexit (qh_ERRqhull, NULL, NULL);
672     }
673     center= (pointT *)qh_memalloc(qh normal_size);
674     for (k=0; k < qh hull_dim; k++) {
675     coord= center+k;
676     *coord= 0.0;
677     FOREACHvertex_(vertices)
678     *coord += vertex->point[k];
679     *coord /= count;
680     }
681     return(center);
682     } /* getcenter */
683    
684    
685     /*-<a href="qh-geom.htm#TOC"
686     >-------------------------------</a><a name="getcentrum">-</a>
687    
688     qh_getcentrum( facet )
689     returns the centrum for a facet as a new point
690    
691     notes:
692     allocates the centrum
693     */
694     pointT *qh_getcentrum(facetT *facet) {
695     realT dist;
696     pointT *centrum, *point;
697    
698     point= qh_getcenter(facet->vertices);
699     zzinc_(Zcentrumtests);
700     qh_distplane (point, facet, &dist);
701     centrum= qh_projectpoint(point, facet, dist);
702     qh_memfree(point, qh normal_size);
703     trace4((qh ferr, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n", facet->id, qh_setsize(facet->vertices), dist));
704     return centrum;
705     } /* getcentrum */
706    
707    
708     /*-<a href="qh-geom.htm#TOC"
709     >-------------------------------</a><a name="getdistance">-</a>
710    
711     qh_getdistance( facet, neighbor, mindist, maxdist )
712     returns the maxdist and mindist distance of any vertex from neighbor
713    
714     returns:
715     the max absolute value
716    
717     design:
718     for each vertex of facet that is not in neighbor
719     test the distance from vertex to neighbor
720     */
721     realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) {
722     vertexT *vertex, **vertexp;
723     realT dist, maxd, mind;
724    
725     FOREACHvertex_(facet->vertices)
726     vertex->seen= False;
727     FOREACHvertex_(neighbor->vertices)
728     vertex->seen= True;
729     mind= 0.0;
730     maxd= 0.0;
731     FOREACHvertex_(facet->vertices) {
732     if (!vertex->seen) {
733     zzinc_(Zbestdist);
734     qh_distplane(vertex->point, neighbor, &dist);
735     if (dist < mind)
736     mind= dist;
737     else if (dist > maxd)
738     maxd= dist;
739     }
740     }
741     *mindist= mind;
742     *maxdist= maxd;
743     mind= -mind;
744     if (maxd > mind)
745     return maxd;
746     else
747     return mind;
748     } /* getdistance */
749    
750    
751     /*-<a href="qh-geom.htm#TOC"
752     >-------------------------------</a><a name="normalize">-</a>
753    
754     qh_normalize( normal, dim, toporient )
755     normalize a vector and report if too small
756     does not use min norm
757    
758     see:
759     qh_normalize2
760     */
761     void qh_normalize (coordT *normal, int dim, boolT toporient) {
762     qh_normalize2( normal, dim, toporient, NULL, NULL);
763     } /* normalize */
764    
765     /*-<a href="qh-geom.htm#TOC"
766     >-------------------------------</a><a name="normalize2">-</a>
767    
768     qh_normalize2( normal, dim, toporient, minnorm, ismin )
769     normalize a vector and report if too small
770     qh.MINdenom/MINdenom1 are the upper limits for divide overflow
771    
772     returns:
773     normalized vector
774     flips sign if !toporient
775     if minnorm non-NULL,
776     sets ismin if normal < minnorm
777    
778     notes:
779     if zero norm
780     sets all elements to sqrt(1.0/dim)
781     if divide by zero (divzero ())
782     sets largest element to +/-1
783     bumps Znearlysingular
784    
785     design:
786     computes norm
787     test for minnorm
788     if not near zero
789     normalizes normal
790     else if zero norm
791     sets normal to standard value
792     else
793     uses qh_divzero to normalize
794     if nearzero
795     sets norm to direction of maximum value
796     */
797     void qh_normalize2 (coordT *normal, int dim, boolT toporient,
798     realT *minnorm, boolT *ismin) {
799     int k;
800     realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
801     boolT zerodiv;
802    
803     norm1= normal+1;
804     norm2= normal+2;
805     norm3= normal+3;
806     if (dim == 2)
807     norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
808     else if (dim == 3)
809     norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
810     else if (dim == 4) {
811     norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
812     + (*norm3)*(*norm3));
813     }else if (dim > 4) {
814     norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
815     + (*norm3)*(*norm3);
816     for (k= dim-4, colp= normal+4; k--; colp++)
817     norm += (*colp) * (*colp);
818     norm= sqrt(norm);
819     }
820     if (minnorm) {
821     if (norm < *minnorm)
822     *ismin= True;
823     else
824     *ismin= False;
825     }
826     wmin_(Wmindenom, norm);
827     if (norm > qh MINdenom) {
828     if (!toporient)
829     norm= -norm;
830     *normal /= norm;
831     *norm1 /= norm;
832     if (dim == 2)
833     ; /* all done */
834     else if (dim == 3)
835     *norm2 /= norm;
836     else if (dim == 4) {
837     *norm2 /= norm;
838     *norm3 /= norm;
839     }else if (dim >4) {
840     *norm2 /= norm;
841     *norm3 /= norm;
842     for (k= dim-4, colp= normal+4; k--; )
843     *colp++ /= norm;
844     }
845     }else if (norm == 0.0) {
846     temp= sqrt (1.0/dim);
847     for (k= dim, colp= normal; k--; )
848     *colp++ = temp;
849     }else {
850     if (!toporient)
851     norm= -norm;
852     for (k= dim, colp= normal; k--; colp++) { /* k used below */
853     temp= qh_divzero (*colp, norm, qh MINdenom_1, &zerodiv);
854     if (!zerodiv)
855     *colp= temp;
856     else {
857     maxp= qh_maxabsval(normal, dim);
858     temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
859     for (k= dim, colp= normal; k--; colp++)
860     *colp= 0.0;
861     *maxp= temp;
862     zzinc_(Znearlysingular);
863     trace0((qh ferr, "qh_normalize: norm=%2.2g too small during p%d\n", norm, qh furthest_id));
864     return;
865     }
866     }
867     }
868     } /* normalize */
869    
870    
871     /*-<a href="qh-geom.htm#TOC"
872     >-------------------------------</a><a name="projectpoint">-</a>
873    
874     qh_projectpoint( point, facet, dist )
875     project point onto a facet by dist
876    
877     returns:
878     returns a new point
879    
880     notes:
881     if dist= distplane(point,facet)
882     this projects point to hyperplane
883     assumes qh_memfree\_() is valid for normal_size
884     */
885     pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist) {
886     pointT *newpoint, *np, *normal;
887     int normsize= qh normal_size,k;
888     void **freelistp; /* used !qh_NOmem */
889    
890     qh_memalloc_(normsize, freelistp, newpoint, pointT);
891     np= newpoint;
892     normal= facet->normal;
893     for(k= qh hull_dim; k--; )
894     *(np++)= *point++ - dist * *normal++;
895     return(newpoint);
896     } /* projectpoint */
897    
898    
899     /*-<a href="qh-geom.htm#TOC"
900     >-------------------------------</a><a name="setfacetplane">-</a>
901    
902     qh_setfacetplane( facet )
903     sets the hyperplane for a facet
904     if qh.RANDOMdist, joggles hyperplane
905    
906     notes:
907     uses global buffers qh.gm_matrix and qh.gm_row
908     overwrites facet->normal if already defined
909     updates Wnewvertex if PRINTstatistics
910     sets facet->upperdelaunay if upper envelope of Delaunay triangulation
911    
912     design:
913     copy vertex coordinates to qh.gm_matrix/gm_row
914     compute determinate
915     if nearzero
916     recompute determinate with gaussian elimination
917     if nearzero
918     force outside orientation by testing interior point
919     */
920     void qh_setfacetplane(facetT *facet) {
921     pointT *point;
922     vertexT *vertex, **vertexp;
923     int k,i, normsize= qh normal_size, oldtrace= 0;
924     realT dist;
925     void **freelistp; /* used !qh_NOmem */
926     coordT *coord, *gmcoord;
927     pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
928     boolT nearzero= False;
929    
930     zzinc_(Zsetplane);
931     if (!facet->normal)
932     qh_memalloc_(normsize, freelistp, facet->normal, coordT);
933     if (facet == qh tracefacet) {
934     oldtrace= qh IStracing;
935     qh IStracing= 5;
936     fprintf (qh ferr, "qh_setfacetplane: facet f%d created.\n", facet->id);
937     fprintf (qh ferr, " Last point added to hull was p%d.", qh furthest_id);
938     if (zzval_(Ztotmerge))
939     fprintf(qh ferr, " Last merge was #%d.", zzval_(Ztotmerge));
940     fprintf (qh ferr, "\n\nCurrent summary is:\n");
941     qh_printsummary (qh ferr);
942     }
943     if (qh hull_dim <= 4) {
944     i= 0;
945     if (qh RANDOMdist) {
946     gmcoord= qh gm_matrix;
947     FOREACHvertex_(facet->vertices) {
948     qh gm_row[i++]= gmcoord;
949     coord= vertex->point;
950     for (k= qh hull_dim; k--; )
951     *(gmcoord++)= *coord++ * qh_randomfactor();
952     }
953     }else {
954     FOREACHvertex_(facet->vertices)
955     qh gm_row[i++]= vertex->point;
956     }
957     qh_sethyperplane_det(qh hull_dim, qh gm_row, point0, facet->toporient,
958     facet->normal, &facet->offset, &nearzero);
959     }
960     if (qh hull_dim > 4 || nearzero) {
961     i= 0;
962     gmcoord= qh gm_matrix;
963     FOREACHvertex_(facet->vertices) {
964     if (vertex->point != point0) {
965     qh gm_row[i++]= gmcoord;
966     coord= vertex->point;
967     point= point0;
968     for(k= qh hull_dim; k--; )
969     *(gmcoord++)= *coord++ - *point++;
970     }
971     }
972     qh gm_row[i]= gmcoord; /* for areasimplex */
973     if (qh RANDOMdist) {
974     gmcoord= qh gm_matrix;
975     for (i= qh hull_dim-1; i--; ) {
976     for (k= qh hull_dim; k--; )
977     *(gmcoord++) *= qh_randomfactor();
978     }
979     }
980     qh_sethyperplane_gauss(qh hull_dim, qh gm_row, point0, facet->toporient,
981     facet->normal, &facet->offset, &nearzero);
982     if (nearzero) {
983     if (qh_orientoutside (facet)) {
984     trace0((qh ferr, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh furthest_id));
985     /* this is part of using Gaussian Elimination. For example in 5-d
986     1 1 1 1 0
987     1 1 1 1 1
988     0 0 0 1 0
989     0 1 0 0 0
990     1 0 0 0 0
991     norm= 0.38 0.38 -0.76 0.38 0
992     has a determinate of 1, but g.e. after subtracting pt. 0 has
993     0's in the diagonal, even with full pivoting. It does work
994     if you subtract pt. 4 instead. */
995     }
996     }
997     }
998     facet->upperdelaunay= False;
999     if (qh DELAUNAY) {
1000     if (qh UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */
1001     if (facet->normal[qh hull_dim -1] >= qh ANGLEround * qh_ZEROdelaunay)
1002     facet->upperdelaunay= True;
1003     }else {
1004     if (facet->normal[qh hull_dim -1] > -qh ANGLEround * qh_ZEROdelaunay)
1005     facet->upperdelaunay= True;
1006     }
1007     }
1008     if (qh PRINTstatistics || qh IStracing || qh TRACElevel || qh JOGGLEmax < REALmax) {
1009     qh old_randomdist= qh RANDOMdist;
1010     qh RANDOMdist= False;
1011     FOREACHvertex_(facet->vertices) {
1012     if (vertex->point != point0) {
1013     boolT istrace= False;
1014     zinc_(Zdiststat);
1015     qh_distplane(vertex->point, facet, &dist);
1016     dist= fabs_(dist);
1017     zinc_(Znewvertex);
1018     wadd_(Wnewvertex, dist);
1019     if (dist > wwval_(Wnewvertexmax)) {
1020     wwval_(Wnewvertexmax)= dist;
1021     if (dist > qh max_outside) {
1022     qh max_outside= dist; /* used by qh_maxouter() */
1023     if (dist > qh TRACEdist)
1024     istrace= True;
1025     }
1026     }else if (-dist > qh TRACEdist)
1027     istrace= True;
1028     if (istrace) {
1029     fprintf (qh ferr, "qh_setfacetplane: ====== vertex p%d (v%d) increases max_outside to %2.2g for new facet f%d last p%d\n",
1030     qh_pointid(vertex->point), vertex->id, dist, facet->id, qh furthest_id);
1031     qh_errprint ("DISTANT", facet, NULL, NULL, NULL);
1032     }
1033     }
1034     }
1035     qh RANDOMdist= qh old_randomdist;
1036     }
1037     if (qh IStracing >= 3) {
1038     fprintf (qh ferr, "qh_setfacetplane: f%d offset %2.2g normal: ",
1039     facet->id, facet->offset);
1040     for (k=0; k < qh hull_dim; k++)
1041     fprintf (qh ferr, "%2.2g ", facet->normal[k]);
1042     fprintf (qh ferr, "\n");
1043     }
1044     if (facet == qh tracefacet)
1045     qh IStracing= oldtrace;
1046     } /* setfacetplane */
1047    
1048    
1049     /*-<a href="qh-geom.htm#TOC"
1050     >-------------------------------</a><a name="sethyperplane_det">-</a>
1051    
1052     qh_sethyperplane_det( dim, rows, point0, toporient, normal, offset, nearzero )
1053     given dim X dim array indexed by rows[], one row per point,
1054     toporient (flips all signs),
1055     and point0 (any row)
1056     set normalized hyperplane equation from oriented simplex
1057    
1058     returns:
1059     normal (normalized)
1060     offset (places point0 on the hyperplane)
1061     sets nearzero if hyperplane not through points
1062    
1063     notes:
1064     only defined for dim == 2..4
1065     rows[] is not modified
1066     solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
1067     see Bower & Woodworth, A programmer's geometry, Butterworths 1983.
1068    
1069     derivation of 3-d minnorm
1070     Goal: all vertices V_i within qh.one_merge of hyperplane
1071     Plan: exactly translate the facet so that V_0 is the origin
1072     exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
1073     exactly rotate the effective perturbation to only effect n_0
1074     this introduces a factor of sqrt(3)
1075     n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
1076     Let M_d be the max coordinate difference
1077     Let M_a be the greater of M_d and the max abs. coordinate
1078     Let u be machine roundoff and distround be max error for distance computation
1079     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
1080     The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm. Offset=0 at origin
1081     Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
1082     Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d
1083    
1084     derivation of 4-d minnorm
1085     same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
1086     [if two vertices fixed on x-axis, can rotate the other two in yzw.]
1087     n_0 = det3(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
1088     [all other terms contain at least two factors nearly zero.]
1089     The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
1090     Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
1091     Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
1092     */
1093     void qh_sethyperplane_det (int dim, coordT **rows, coordT *point0,
1094     boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
1095     realT maxround, dist;
1096     int i;
1097     pointT *point;
1098    
1099    
1100     if (dim == 2) {
1101     normal[0]= dY(1,0);
1102     normal[1]= dX(0,1);
1103     qh_normalize2 (normal, dim, toporient, NULL, NULL);
1104     *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
1105     *nearzero= False; /* since nearzero norm => incident points */
1106     }else if (dim == 3) {
1107     normal[0]= det2_(dY(2,0), dZ(2,0), dY(1,0), dZ(1,0));
1108     normal[1]= det2_(dX(1,0), dZ(1,0), dX(2,0), dZ(2,0));
1109     normal[2]= det2_(dX(2,0), dY(2,0), dX(1,0), dY(1,0));
1110     qh_normalize2 (normal, dim, toporient, NULL, NULL);
1111     *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
1112     + point0[2]*normal[2]);
1113     maxround= qh DISTround;
1114     for (i=dim; i--; ) {
1115     point= rows[i];
1116     if (point != point0) {
1117     dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
1118     + point[2]*normal[2]);
1119     if (dist > maxround || dist < -maxround) {
1120     *nearzero= True;
1121     break;
1122     }
1123     }
1124     }
1125     }else if (dim == 4) {
1126     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));
1127     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));
1128     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));
1129     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));
1130     qh_normalize2 (normal, dim, toporient, NULL, NULL);
1131     *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
1132     + point0[2]*normal[2] + point0[3]*normal[3]);
1133     maxround= qh DISTround;
1134     for (i=dim; i--; ) {
1135     point= rows[i];
1136     if (point != point0) {
1137     dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
1138     + point[2]*normal[2] + point[3]*normal[3]);
1139     if (dist > maxround || dist < -maxround) {
1140     *nearzero= True;
1141     break;
1142     }
1143     }
1144     }
1145     }
1146     if (*nearzero) {
1147     zzinc_(Zminnorm);
1148     trace0((qh ferr, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id));
1149     zzinc_(Znearlysingular);
1150     }
1151     } /* sethyperplane_det */
1152    
1153    
1154     /*-<a href="qh-geom.htm#TOC"
1155     >-------------------------------</a><a name="sethyperplane_gauss">-</a>
1156    
1157     qh_sethyperplane_gauss( dim, rows, point0, toporient, normal, offset, nearzero )
1158     given (dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
1159     set normalized hyperplane equation from oriented simplex
1160    
1161     returns:
1162     normal (normalized)
1163     offset (places point0 on the hyperplane)
1164    
1165     notes:
1166     if nearzero
1167     orientation may be incorrect because of incorrect sign flips in gausselim
1168     solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
1169     or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
1170     i.e., N is normal to the hyperplane, and the unnormalized
1171     distance to [0 .. 1] is either 1 or 0
1172    
1173     design:
1174     perform gaussian elimination
1175     flip sign for negative values
1176     perform back substitution
1177     normalize result
1178     compute offset
1179     */
1180     void qh_sethyperplane_gauss (int dim, coordT **rows, pointT *point0,
1181     boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
1182     coordT *pointcoord, *normalcoef;
1183     int k;
1184     boolT sign= toporient, nearzero2= False;
1185    
1186     qh_gausselim(rows, dim-1, dim, &sign, nearzero);
1187     for(k= dim-1; k--; ) {
1188     if ((rows[k])[k] < 0)
1189     sign ^= 1;
1190     }
1191     if (*nearzero) {
1192     zzinc_(Znearlysingular);
1193     trace0((qh ferr, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh furthest_id));
1194     qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
1195     }else {
1196     qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
1197     if (nearzero2) {
1198     zzinc_(Znearlysingular);
1199     trace0((qh ferr, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh furthest_id));
1200     }
1201     }
1202     if (nearzero2)
1203     *nearzero= True;
1204     qh_normalize2(normal, dim, True, NULL, NULL);
1205     pointcoord= point0;
1206     normalcoef= normal;
1207     *offset= -(*pointcoord++ * *normalcoef++);
1208     for(k= dim-1; k--; )
1209     *offset -= *pointcoord++ * *normalcoef++;
1210     } /* sethyperplane_gauss */
1211    
1212    
1213