ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/trunk/src/QuickHull/merge.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: 119797 byte(s)
Log Message:
Addded QuickHull to cvs.

File Contents

# User Rev Content
1 chuckv 1138 /*<html><pre> -<a href="qh-merge.htm#TOC"
2     >-------------------------------</a><a name="TOP">-</a>
3    
4     merge.c
5     merges non-convex facets
6    
7     see qh-merge.htm and merge.h
8    
9     other modules call qh_premerge() and qh_postmerge()
10    
11     the user may call qh_postmerge() to perform additional merges.
12    
13     To remove deleted facets and vertices (qhull() in qhull.c):
14     qh_partitionvisible (!qh_ALL, &numoutside); // visible_list, newfacet_list
15     qh_deletevisible (); // qh.visible_list
16     qh_resetlists (False, qh_RESETvisible); // qh.visible_list newvertex_list newfacet_list
17    
18     assumes qh.CENTERtype= centrum
19    
20     merges occur in qh_mergefacet and in qh_mergecycle
21     vertex->neighbors not set until the first merge occurs
22    
23     copyright (c) 1993-2003 The Geometry Center
24     */
25    
26     #include "QuickHull/qhull_a.h"
27    
28     #ifndef qh_NOmerge
29    
30     /*===== functions (alphabetical after premerge and postmerge) ======*/
31    
32     /*-<a href="qh-merge.htm#TOC"
33     >-------------------------------</a><a name="premerge">-</a>
34    
35     qh_premerge( apex, maxcentrum )
36     pre-merge nonconvex facets in qh.newfacet_list for apex
37     maxcentrum defines coplanar and concave (qh_test_appendmerge)
38    
39     returns:
40     deleted facets added to qh.visible_list with facet->visible set
41    
42     notes:
43     uses globals, qh.MERGEexact, qh.PREmerge
44    
45     design:
46     mark duplicate ridges in qh.newfacet_list
47     merge facet cycles in qh.newfacet_list
48     merge duplicate ridges and concave facets in qh.newfacet_list
49     check merged facet cycles for degenerate and redundant facets
50     merge degenerate and redundant facets
51     collect coplanar and concave facets
52     merge concave, coplanar, degenerate, and redundant facets
53     */
54     void qh_premerge (vertexT *apex, realT maxcentrum, realT maxangle) {
55     boolT othermerge= False;
56     facetT *newfacet;
57    
58     if (qh ZEROcentrum && qh_checkzero(!qh_ALL))
59     return;
60     trace2((qh ferr, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n",
61     maxcentrum, maxangle, apex->id, getid_(qh newfacet_list)));
62     if (qh IStracing >= 4 && qh num_facets < 50)
63     qh_printlists();
64     qh centrum_radius= maxcentrum;
65     qh cos_max= maxangle;
66     qh degen_mergeset= qh_settemp (qh TEMPsize);
67     qh facet_mergeset= qh_settemp (qh TEMPsize);
68     if (qh hull_dim >=3) {
69     qh_mark_dupridges (qh newfacet_list); /* facet_mergeset */
70     qh_mergecycle_all (qh newfacet_list, &othermerge);
71     qh_forcedmerges (&othermerge /* qh facet_mergeset */);
72     FORALLnew_facets { /* test samecycle merges */
73     if (!newfacet->simplicial && !newfacet->mergeridge)
74     qh_degen_redundant_neighbors (newfacet, NULL);
75     }
76     if (qh_merge_degenredundant())
77     othermerge= True;
78     }else /* qh hull_dim == 2 */
79     qh_mergecycle_all (qh newfacet_list, &othermerge);
80     qh_flippedmerges (qh newfacet_list, &othermerge);
81     if (!qh MERGEexact || zzval_(Ztotmerge)) {
82     zinc_(Zpremergetot);
83     qh POSTmerging= False;
84     qh_getmergeset_initial (qh newfacet_list);
85     qh_all_merges (othermerge, False);
86     }
87     qh_settempfree(&qh facet_mergeset);
88     qh_settempfree(&qh degen_mergeset);
89     } /* premerge */
90    
91     /*-<a href="qh-merge.htm#TOC"
92     >-------------------------------</a><a name="postmerge">-</a>
93    
94     qh_postmerge( reason, maxcentrum, maxangle, vneighbors )
95     post-merge nonconvex facets as defined by maxcentrum and maxangle
96     'reason' is for reporting progress
97     if vneighbors,
98     calls qh_test_vneighbors at end of qh_all_merge
99     if firstmerge,
100     calls qh_reducevertices before qh_getmergeset
101    
102     returns:
103     if first call (qh.visible_list != qh.facet_list),
104     builds qh.facet_newlist, qh.newvertex_list
105     deleted facets added to qh.visible_list with facet->visible
106     qh.visible_list == qh.facet_list
107    
108     notes:
109    
110    
111     design:
112     if first call
113     set qh.visible_list and qh.newfacet_list to qh.facet_list
114     add all facets to qh.newfacet_list
115     mark non-simplicial facets, facet->newmerge
116     set qh.newvertext_list to qh.vertex_list
117     add all vertices to qh.newvertex_list
118     if a pre-merge occured
119     set vertex->delridge {will retest the ridge}
120     if qh.MERGEexact
121     call qh_reducevertices()
122     if no pre-merging
123     merge flipped facets
124     determine non-convex facets
125     merge all non-convex facets
126     */
127     void qh_postmerge (char *reason, realT maxcentrum, realT maxangle,
128     boolT vneighbors) {
129     facetT *newfacet;
130     boolT othermerges= False;
131     vertexT *vertex;
132    
133     if (qh REPORTfreq || qh IStracing) {
134     qh_buildtracing (NULL, NULL);
135     qh_printsummary (qh ferr);
136     if (qh PRINTstatistics)
137     qh_printallstatistics (qh ferr, "reason");
138     fprintf (qh ferr, "\n%s with 'C%.2g' and 'A%.2g'\n",
139     reason, maxcentrum, maxangle);
140     }
141     trace2((qh ferr, "qh_postmerge: postmerge. test vneighbors? %d\n",
142     vneighbors));
143     qh centrum_radius= maxcentrum;
144     qh cos_max= maxangle;
145     qh POSTmerging= True;
146     qh degen_mergeset= qh_settemp (qh TEMPsize);
147     qh facet_mergeset= qh_settemp (qh TEMPsize);
148     if (qh visible_list != qh facet_list) { /* first call */
149     qh NEWfacets= True;
150     qh visible_list= qh newfacet_list= qh facet_list;
151     FORALLnew_facets {
152     newfacet->newfacet= True;
153     if (!newfacet->simplicial)
154     newfacet->newmerge= True;
155     zinc_(Zpostfacets);
156     }
157     qh newvertex_list= qh vertex_list;
158     FORALLvertices
159     vertex->newlist= True;
160     if (qh VERTEXneighbors) { /* a merge has occurred */
161     FORALLvertices
162     vertex->delridge= True; /* test for redundant, needed? */
163     if (qh MERGEexact) {
164     if (qh hull_dim <= qh_DIMreduceBuild)
165     qh_reducevertices(); /* was skipped during pre-merging */
166     }
167     }
168     if (!qh PREmerge && !qh MERGEexact)
169     qh_flippedmerges (qh newfacet_list, &othermerges);
170     }
171     qh_getmergeset_initial (qh newfacet_list);
172     qh_all_merges (False, vneighbors);
173     qh_settempfree(&qh facet_mergeset);
174     qh_settempfree(&qh degen_mergeset);
175     } /* post_merge */
176    
177     /*-<a href="qh-merge.htm#TOC"
178     >-------------------------------</a><a name="all_merges">-</a>
179    
180     qh_all_merges( othermerge, vneighbors )
181     merge all non-convex facets
182    
183     set othermerge if already merged facets (for qh_reducevertices)
184     if vneighbors
185     tests vertex neighbors for convexity at end
186     qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list
187     qh.degen_mergeset is defined
188     if qh.MERGEexact && !qh.POSTmerging,
189     does not merge coplanar facets
190    
191     returns:
192     deleted facets added to qh.visible_list with facet->visible
193     deleted vertices added qh.delvertex_list with vertex->delvertex
194    
195     notes:
196     unless !qh.MERGEindependent,
197     merges facets in independent sets
198     uses qh.newfacet_list as argument since merges call qh_removefacet()
199    
200     design:
201     while merges occur
202     for each merge in qh.facet_mergeset
203     unless one of the facets was already merged in this pass
204     merge the facets
205     test merged facets for additional merges
206     add merges to qh.facet_mergeset
207     if vertices record neighboring facets
208     rename redundant vertices
209     update qh.facet_mergeset
210     if vneighbors ??
211     tests vertex neighbors for convexity at end
212     */
213     void qh_all_merges (boolT othermerge, boolT vneighbors) {
214     facetT *facet1, *facet2;
215     mergeT *merge;
216     boolT wasmerge= True, isreduce;
217     void **freelistp; /* used !qh_NOmem */
218     vertexT *vertex;
219     mergeType mergetype;
220     int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0;
221    
222     trace2((qh ferr, "qh_all_merges: starting to merge facets beginning from f%d\n",
223     getid_(qh newfacet_list)));
224     while (True) {
225     wasmerge= False;
226     while (qh_setsize (qh facet_mergeset)) {
227     while ((merge= (mergeT*)qh_setdellast(qh facet_mergeset))) {
228     facet1= merge->facet1;
229     facet2= merge->facet2;
230     mergetype= merge->type;
231     qh_memfree_(merge, sizeof(mergeT), freelistp);
232     if (facet1->visible || facet2->visible) /*deleted facet*/
233     continue;
234     if ((facet1->newfacet && !facet1->tested)
235     || (facet2->newfacet && !facet2->tested)) {
236     if (qh MERGEindependent && mergetype <= MRGanglecoplanar)
237     continue; /* perform independent sets of merges */
238     }
239     qh_merge_nonconvex (facet1, facet2, mergetype);
240     numdegenredun += qh_merge_degenredundant();
241     numnewmerges++;
242     wasmerge= True;
243     if (mergetype == MRGconcave)
244     numconcave++;
245     else /* MRGcoplanar or MRGanglecoplanar */
246     numcoplanar++;
247     } /* while setdellast */
248     if (qh POSTmerging && qh hull_dim <= qh_DIMreduceBuild
249     && numnewmerges > qh_MAXnewmerges) {
250     numnewmerges= 0;
251     qh_reducevertices(); /* otherwise large post merges too slow */
252     }
253     qh_getmergeset (qh newfacet_list); /* facet_mergeset */
254     } /* while mergeset */
255     if (qh VERTEXneighbors) {
256     isreduce= False;
257     if (qh hull_dim >=4 && qh POSTmerging) {
258     FORALLvertices
259     vertex->delridge= True;
260     isreduce= True;
261     }
262     if ((wasmerge || othermerge) && (!qh MERGEexact || qh POSTmerging)
263     && qh hull_dim <= qh_DIMreduceBuild) {
264     othermerge= False;
265     isreduce= True;
266     }
267     if (isreduce) {
268     if (qh_reducevertices()) {
269     qh_getmergeset (qh newfacet_list); /* facet_mergeset */
270     continue;
271     }
272     }
273     }
274     if (vneighbors && qh_test_vneighbors(/* qh newfacet_list */))
275     continue;
276     break;
277     } /* while (True) */
278     if (qh CHECKfrequently && !qh MERGEexact) {
279     qh old_randomdist= qh RANDOMdist;
280     qh RANDOMdist= False;
281     qh_checkconvex (qh newfacet_list, qh_ALGORITHMfault);
282     /* qh_checkconnect (); [this is slow and it changes the facet order] */
283     qh RANDOMdist= qh old_randomdist;
284     }
285     trace1((qh ferr, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n",
286     numcoplanar, numconcave, numdegenredun));
287     if (qh IStracing >= 4 && qh num_facets < 50)
288     qh_printlists ();
289     } /* all_merges */
290    
291    
292     /*-<a href="qh-merge.htm#TOC"
293     >-------------------------------</a><a name="appendmergeset">-</a>
294    
295     qh_appendmergeset( facet, neighbor, mergetype, angle )
296     appends an entry to qh.facet_mergeset or qh.degen_mergeset
297    
298     angle ignored if NULL or !qh.ANGLEmerge
299    
300     returns:
301     merge appended to facet_mergeset or degen_mergeset
302     sets ->degenerate or ->redundant if degen_mergeset
303    
304     see:
305     qh_test_appendmerge()
306    
307     design:
308     allocate merge entry
309     if regular merge
310     append to qh.facet_mergeset
311     else if degenerate merge and qh.facet_mergeset is all degenerate
312     append to qh.degen_mergeset
313     else if degenerate merge
314     prepend to qh.degen_mergeset
315     else if redundant merge
316     append to qh.degen_mergeset
317     */
318     void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) {
319     mergeT *merge, *lastmerge;
320     void **freelistp; /* used !qh_NOmem */
321    
322     if (facet->redundant)
323     return;
324     if (facet->degenerate && mergetype == MRGdegen)
325     return;
326     qh_memalloc_(sizeof(mergeT), freelistp, merge, mergeT);
327     merge->facet1= facet;
328     merge->facet2= neighbor;
329     merge->type= mergetype;
330     if (angle && qh ANGLEmerge)
331     merge->angle= *angle;
332     if (mergetype < MRGdegen)
333     qh_setappend (&(qh facet_mergeset), merge);
334     else if (mergetype == MRGdegen) {
335     facet->degenerate= True;
336     if (!(lastmerge= (mergeT*)qh_setlast (qh degen_mergeset))
337     || lastmerge->type == MRGdegen)
338     qh_setappend (&(qh degen_mergeset), merge);
339     else
340     qh_setaddnth (&(qh degen_mergeset), 0, merge);
341     }else if (mergetype == MRGredundant) {
342     facet->redundant= True;
343     qh_setappend (&(qh degen_mergeset), merge);
344     }else /* mergetype == MRGmirror */ {
345     if (facet->redundant || neighbor->redundant) {
346     fprintf(qh ferr, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n",
347     facet->id, neighbor->id);
348     qh_errexit2 (qh_ERRqhull, facet, neighbor);
349     }
350     if (!qh_setequal (facet->vertices, neighbor->vertices)) {
351     fprintf(qh ferr, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
352     facet->id, neighbor->id);
353     qh_errexit2 (qh_ERRqhull, facet, neighbor);
354     }
355     facet->redundant= True;
356     neighbor->redundant= True;
357     qh_setappend (&(qh degen_mergeset), merge);
358     }
359     } /* appendmergeset */
360    
361    
362     /*-<a href="qh-merge.htm#TOC"
363     >-------------------------------</a><a name="basevertices">-</a>
364    
365     qh_basevertices( samecycle )
366     return temporary set of base vertices for samecycle
367     samecycle is first facet in the cycle
368     assumes apex is SETfirst_( samecycle->vertices )
369    
370     returns:
371     vertices (settemp)
372     all ->seen are cleared
373    
374     notes:
375     uses qh_vertex_visit;
376    
377     design:
378     for each facet in samecycle
379     for each unseen vertex in facet->vertices
380     append to result
381     */
382     setT *qh_basevertices (facetT *samecycle) {
383     facetT *same;
384     vertexT *apex, *vertex, **vertexp;
385     setT *vertices= qh_settemp (qh TEMPsize);
386    
387     apex= SETfirstt_(samecycle->vertices, vertexT);
388     apex->visitid= ++qh vertex_visit;
389     FORALLsame_cycle_(samecycle) {
390     if (same->mergeridge)
391     continue;
392     FOREACHvertex_(same->vertices) {
393     if (vertex->visitid != qh vertex_visit) {
394     qh_setappend (&vertices, vertex);
395     vertex->visitid= qh vertex_visit;
396     vertex->seen= False;
397     }
398     }
399     }
400     trace4((qh ferr, "qh_basevertices: found %d vertices\n",
401     qh_setsize (vertices)));
402     return vertices;
403     } /* basevertices */
404    
405     /*-<a href="qh-merge.htm#TOC"
406     >-------------------------------</a><a name="checkconnect">-</a>
407    
408     qh_checkconnect()
409     check that new facets are connected
410     new facets are on qh.newfacet_list
411    
412     notes:
413     this is slow and it changes the order of the facets
414     uses qh.visit_id
415    
416     design:
417     move first new facet to end of qh.facet_list
418     for all newly appended facets
419     append unvisited neighbors to end of qh.facet_list
420     for all new facets
421     report error if unvisited
422     */
423     void qh_checkconnect (void /* qh newfacet_list */) {
424     facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;
425    
426     facet= qh newfacet_list;
427     qh_removefacet (facet);
428     qh_appendfacet (facet);
429     facet->visitid= ++qh visit_id;
430     FORALLfacet_(facet) {
431     FOREACHneighbor_(facet) {
432     if (neighbor->visitid != qh visit_id) {
433     qh_removefacet (neighbor);
434     qh_appendfacet (neighbor);
435     neighbor->visitid= qh visit_id;
436     }
437     }
438     }
439     FORALLnew_facets {
440     if (newfacet->visitid == qh visit_id)
441     break;
442     fprintf(qh ferr, "qhull error: f%d is not attached to the new facets\n",
443     newfacet->id);
444     errfacet= newfacet;
445     }
446     if (errfacet)
447     qh_errexit (qh_ERRqhull, errfacet, NULL);
448     } /* checkconnect */
449    
450     /*-<a href="qh-merge.htm#TOC"
451     >-------------------------------</a><a name="checkzero">-</a>
452    
453     qh_checkzero( testall )
454     check that facets are clearly convex for qh.DISTround with qh.MERGEexact
455    
456     if testall,
457     test all facets for qh.MERGEexact post-merging
458     else
459     test qh.newfacet_list
460    
461     if qh.MERGEexact,
462     allows coplanar ridges
463     skips convexity test while qh.ZEROall_ok
464    
465     returns:
466     True if all facets !flipped, !dupridge, normal
467     if all horizon facets are simplicial
468     if all vertices are clearly below neighbor
469     if all opposite vertices of horizon are below
470     clears qh.ZEROall_ok if any problems or coplanar facets
471    
472     notes:
473     uses qh.vertex_visit
474     horizon facets may define multiple new facets
475    
476     design:
477     for all facets in qh.newfacet_list or qh.facet_list
478     check for flagged faults (flipped, etc.)
479     for all facets in qh.newfacet_list or qh.facet_list
480     for each neighbor of facet
481     skip horizon facets for qh.newfacet_list
482     test the opposite vertex
483     if qh.newfacet_list
484     test the other vertices in the facet's horizon facet
485     */
486     boolT qh_checkzero (boolT testall) {
487     facetT *facet, *neighbor, **neighborp;
488     facetT *horizon, *facetlist;
489     int neighbor_i;
490     vertexT *vertex, **vertexp;
491     realT dist;
492    
493     if (testall)
494     facetlist= qh facet_list;
495     else {
496     facetlist= qh newfacet_list;
497     FORALLfacet_(facetlist) {
498     horizon= SETfirstt_(facet->neighbors, facetT);
499     if (!horizon->simplicial)
500     goto LABELproblem;
501     if (facet->flipped || facet->dupridge || !facet->normal)
502     goto LABELproblem;
503     }
504     if (qh MERGEexact && qh ZEROall_ok) {
505     trace2((qh ferr, "qh_checkzero: skip convexity check until first pre-merge\n"));
506     return True;
507     }
508     }
509     FORALLfacet_(facetlist) {
510     qh vertex_visit++;
511     neighbor_i= 0;
512     horizon= NULL;
513     FOREACHneighbor_(facet) {
514     if (!neighbor_i && !testall) {
515     horizon= neighbor;
516     neighbor_i++;
517     continue; /* horizon facet tested in qh_findhorizon */
518     }
519     vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
520     vertex->visitid= qh vertex_visit;
521     zzinc_(Zdistzero);
522     qh_distplane (vertex->point, neighbor, &dist);
523     if (dist >= -qh DISTround) {
524     qh ZEROall_ok= False;
525     if (!qh MERGEexact || testall || dist > qh DISTround)
526     goto LABELnonconvex;
527     }
528     }
529     if (!testall) {
530     FOREACHvertex_(horizon->vertices) {
531     if (vertex->visitid != qh vertex_visit) {
532     zzinc_(Zdistzero);
533     qh_distplane (vertex->point, facet, &dist);
534     if (dist >= -qh DISTround) {
535     qh ZEROall_ok= False;
536     if (!qh MERGEexact || dist > qh DISTround)
537     goto LABELnonconvex;
538     }
539     break;
540     }
541     }
542     }
543     }
544     trace2((qh ferr, "qh_checkzero: testall %d, facets are %s\n", testall,
545     (qh MERGEexact && !testall) ?
546     "not concave, flipped, or duplicate ridged" : "clearly convex"));
547     return True;
548    
549     LABELproblem:
550     qh ZEROall_ok= False;
551     trace2((qh ferr, "qh_checkzero: facet f%d needs pre-merging\n",
552     facet->id));
553     return False;
554    
555     LABELnonconvex:
556     trace2((qh ferr, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n",
557     facet->id, neighbor->id, vertex->id, dist));
558     return False;
559     } /* checkzero */
560    
561     /*-<a href="qh-merge.htm#TOC"
562     >-------------------------------</a><a name="compareangle">-</a>
563    
564     qh_compareangle( angle1, angle2 )
565     used by qsort() to order merges by angle
566     */
567     int qh_compareangle(const void *p1, const void *p2) {
568     mergeT *a= *((mergeT **)p1), *b= *((mergeT **)p2);
569    
570     return ((a->angle > b->angle) ? 1 : -1);
571     } /* compareangle */
572    
573     /*-<a href="qh-merge.htm#TOC"
574     >-------------------------------</a><a name="comparemerge">-</a>
575    
576     qh_comparemerge( merge1, merge2 )
577     used by qsort() to order merges
578     */
579     int qh_comparemerge(const void *p1, const void *p2) {
580     mergeT *a= *((mergeT **)p1), *b= *((mergeT **)p2);
581    
582     return (a->type - b->type);
583     } /* comparemerge */
584    
585     /*-<a href="qh-merge.htm#TOC"
586     >-------------------------------</a><a name="comparevisit">-</a>
587    
588     qh_comparevisit( vertex1, vertex2 )
589     used by qsort() to order vertices by their visitid
590     */
591     int qh_comparevisit (const void *p1, const void *p2) {
592     vertexT *a= *((vertexT **)p1), *b= *((vertexT **)p2);
593    
594     return (a->visitid - b->visitid);
595     } /* comparevisit */
596    
597     /*-<a href="qh-merge.htm#TOC"
598     >-------------------------------</a><a name="copynonconvex">-</a>
599    
600     qh_copynonconvex( atridge )
601     set non-convex flag on other ridges (if any) between same neighbors
602    
603     notes:
604     may be faster if use smaller ridge set
605    
606     design:
607     for each ridge of atridge's top facet
608     if ridge shares the same neighbor
609     set nonconvex flag
610     */
611     void qh_copynonconvex (ridgeT *atridge) {
612     facetT *facet, *otherfacet;
613     ridgeT *ridge, **ridgep;
614    
615     facet= atridge->top;
616     otherfacet= atridge->bottom;
617     FOREACHridge_(facet->ridges) {
618     if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) {
619     ridge->nonconvex= True;
620     trace4((qh ferr, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",
621     atridge->id, ridge->id));
622     break;
623     }
624     }
625     } /* copynonconvex */
626    
627     /*-<a href="qh-merge.htm#TOC"
628     >-------------------------------</a><a name="degen_redundant_facet">-</a>
629    
630     qh_degen_redundant_facet( facet )
631     check facet for degen. or redundancy
632    
633     notes:
634     bumps vertex_visit
635     called if a facet was redundant but no longer is (qh_merge_degenredundant)
636     qh_appendmergeset() only appends first reference to facet (i.e., redundant)
637    
638     see:
639     qh_degen_redundant_neighbors()
640    
641     design:
642     test for redundant neighbor
643     test for degenerate facet
644     */
645     void qh_degen_redundant_facet (facetT *facet) {
646     vertexT *vertex, **vertexp;
647     facetT *neighbor, **neighborp;
648    
649     trace4((qh ferr, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",
650     facet->id));
651     FOREACHneighbor_(facet) {
652     qh vertex_visit++;
653     FOREACHvertex_(neighbor->vertices)
654     vertex->visitid= qh vertex_visit;
655     FOREACHvertex_(facet->vertices) {
656     if (vertex->visitid != qh vertex_visit)
657     break;
658     }
659     if (!vertex) {
660     qh_appendmergeset (facet, neighbor, MRGredundant, NULL);
661     trace2((qh ferr, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id));
662     return;
663     }
664     }
665     if (qh_setsize (facet->neighbors) < qh hull_dim) {
666     qh_appendmergeset (facet, facet, MRGdegen, NULL);
667     trace2((qh ferr, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));
668     }
669     } /* degen_redundant_facet */
670    
671    
672     /*-<a href="qh-merge.htm#TOC"
673     >-------------------------------</a><a name="degen_redundant_neighbors">-</a>
674    
675     qh_degen_redundant_neighbors( facet, delfacet, )
676     append degenerate and redundant neighbors to facet_mergeset
677     if delfacet,
678     only checks neighbors of both delfacet and facet
679     also checks current facet for degeneracy
680    
681     notes:
682     bumps vertex_visit
683     called for each qh_mergefacet() and qh_mergecycle()
684     merge and statistics occur in merge_nonconvex
685     qh_appendmergeset() only appends first reference to facet (i.e., redundant)
686     it appends redundant facets after degenerate ones
687    
688     a degenerate facet has fewer than hull_dim neighbors
689     a redundant facet's vertices is a subset of its neighbor's vertices
690     tests for redundant merges first (appendmergeset is nop for others)
691     in a merge, only needs to test neighbors of merged facet
692    
693     see:
694     qh_merge_degenredundant() and qh_degen_redundant_facet()
695    
696     design:
697     test for degenerate facet
698     test for redundant neighbor
699     test for degenerate neighbor
700     */
701     void qh_degen_redundant_neighbors (facetT *facet, facetT *delfacet) {
702     vertexT *vertex, **vertexp;
703     facetT *neighbor, **neighborp;
704     int size;
705    
706     trace4((qh ferr, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n",
707     facet->id, getid_(delfacet)));
708     if ((size= qh_setsize (facet->neighbors)) < qh hull_dim) {
709     qh_appendmergeset (facet, facet, MRGdegen, NULL);
710     trace2((qh ferr, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));
711     }
712     if (!delfacet)
713     delfacet= facet;
714     qh vertex_visit++;
715     FOREACHvertex_(facet->vertices)
716     vertex->visitid= qh vertex_visit;
717     FOREACHneighbor_(delfacet) {
718     /* uses early out instead of checking vertex count */
719     if (neighbor == facet)
720     continue;
721     FOREACHvertex_(neighbor->vertices) {
722     if (vertex->visitid != qh vertex_visit)
723     break;
724     }
725     if (!vertex) {
726     qh_appendmergeset (neighbor, facet, MRGredundant, NULL);
727     trace2((qh ferr, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id));
728     }
729     }
730     FOREACHneighbor_(delfacet) { /* redundant merges occur first */
731     if (neighbor == facet)
732     continue;
733     if ((size= qh_setsize (neighbor->neighbors)) < qh hull_dim) {
734     qh_appendmergeset (neighbor, neighbor, MRGdegen, NULL);
735     trace2((qh ferr, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id));
736     }
737     }
738     } /* degen_redundant_neighbors */
739    
740    
741     /*-<a href="qh-merge.htm#TOC"
742     >-------------------------------</a><a name="find_newvertex">-</a>
743    
744     qh_find_newvertex( oldvertex, vertices, ridges )
745     locate new vertex for renaming old vertex
746     vertices is a set of possible new vertices
747     vertices sorted by number of deleted ridges
748    
749     returns:
750     newvertex or NULL
751     each ridge includes both vertex and oldvertex
752     vertices sorted by number of deleted ridges
753    
754     notes:
755     modifies vertex->visitid
756     new vertex is in one of the ridges
757     renaming will not cause a duplicate ridge
758     renaming will minimize the number of deleted ridges
759     newvertex may not be adjacent in the dual (though unlikely)
760    
761     design:
762     for each vertex in vertices
763     set vertex->visitid to number of references in ridges
764     remove unvisited vertices
765     set qh.vertex_visit above all possible values
766     sort vertices by number of references in ridges
767     add each ridge to qh.hash_table
768     for each vertex in vertices
769     look for a vertex that would not cause a duplicate ridge after a rename
770     */
771     vertexT *qh_find_newvertex (vertexT *oldvertex, setT *vertices, setT *ridges) {
772     vertexT *vertex, **vertexp;
773     setT *newridges;
774     ridgeT *ridge, **ridgep;
775     int size, hashsize;
776     int hash;
777    
778     #ifndef qh_NOtrace
779     if (qh IStracing >= 4) {
780     fprintf (qh ferr, "qh_find_newvertex: find new vertex for v%d from ",
781     oldvertex->id);
782     FOREACHvertex_(vertices)
783     fprintf (qh ferr, "v%d ", vertex->id);
784     FOREACHridge_(ridges)
785     fprintf (qh ferr, "r%d ", ridge->id);
786     fprintf (qh ferr, "\n");
787     }
788     #endif
789     FOREACHvertex_(vertices)
790     vertex->visitid= 0;
791     FOREACHridge_(ridges) {
792     FOREACHvertex_(ridge->vertices)
793     vertex->visitid++;
794     }
795     FOREACHvertex_(vertices) {
796     if (!vertex->visitid) {
797     qh_setdelnth (vertices, SETindex_(vertices,vertex));
798     vertexp--; /* repeat since deleted this vertex */
799     }
800     }
801     qh vertex_visit += qh_setsize (ridges);
802     if (!qh_setsize (vertices)) {
803     trace4((qh ferr, "qh_find_newvertex: vertices not in ridges for v%d\n",
804     oldvertex->id));
805     return NULL;
806     }
807     qsort (SETaddr_(vertices, vertexT), qh_setsize (vertices),
808     sizeof (vertexT *), qh_comparevisit);
809     /* can now use qh vertex_visit */
810     if (qh PRINTstatistics) {
811     size= qh_setsize (vertices);
812     zinc_(Zintersect);
813     zadd_(Zintersecttot, size);
814     zmax_(Zintersectmax, size);
815     }
816     hashsize= qh_newhashtable (qh_setsize (ridges));
817     FOREACHridge_(ridges)
818     qh_hashridge (qh hash_table, hashsize, ridge, oldvertex);
819     FOREACHvertex_(vertices) {
820     newridges= qh_vertexridges (vertex);
821     FOREACHridge_(newridges) {
822     if (qh_hashridge_find (qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
823     zinc_(Zdupridge);
824     break;
825     }
826     }
827     qh_settempfree (&newridges);
828     if (!ridge)
829     break; /* found a rename */
830     }
831     if (vertex) {
832     /* counted in qh_renamevertex */
833     trace2((qh ferr, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
834     vertex->id, oldvertex->id, qh_setsize (vertices), qh_setsize (ridges)));
835     }else {
836     zinc_(Zfindfail);
837     trace0((qh ferr, "qh_find_newvertex: no vertex for renaming v%d (all duplicated ridges) during p%d\n",
838     oldvertex->id, qh furthest_id));
839     }
840     qh_setfree (&qh hash_table);
841     return vertex;
842     } /* find_newvertex */
843    
844     /*-<a href="qh-merge.htm#TOC"
845     >-------------------------------</a><a name="findbest_test">-</a>
846    
847     qh_findbest_test( testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist )
848     test neighbor of facet for qh_findbestneighbor()
849     if testcentrum,
850     tests centrum (assumes it is defined)
851     else
852     tests vertices
853    
854     returns:
855     if a better facet (i.e., vertices/centrum of facet closer to neighbor)
856     updates bestfacet, dist, mindist, and maxdist
857     */
858     void qh_findbest_test (boolT testcentrum, facetT *facet, facetT *neighbor,
859     facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
860     realT dist, mindist, maxdist;
861    
862     if (testcentrum) {
863     zzinc_(Zbestdist);
864     qh_distplane(facet->center, neighbor, &dist);
865     dist *= qh hull_dim; /* estimate furthest vertex */
866     if (dist < 0) {
867     maxdist= 0;
868     mindist= dist;
869     dist= -dist;
870     }else
871     maxdist= dist;
872     }else
873     dist= qh_getdistance (facet, neighbor, &mindist, &maxdist);
874     if (dist < *distp) {
875     *bestfacet= neighbor;
876     *mindistp= mindist;
877     *maxdistp= maxdist;
878     *distp= dist;
879     }
880     } /* findbest_test */
881    
882     /*-<a href="qh-merge.htm#TOC"
883     >-------------------------------</a><a name="findbestneighbor">-</a>
884    
885     qh_findbestneighbor( facet, dist, mindist, maxdist )
886     finds best neighbor (least dist) of a facet for merging
887    
888     returns:
889     returns min and max distances and their max absolute value
890    
891     notes:
892     avoids merging old into new
893     assumes ridge->nonconvex only set on one ridge between a pair of facets
894     could use an early out predicate but not worth it
895    
896     design:
897     if a large facet
898     will test centrum
899     else
900     will test vertices
901     if a large facet
902     test nonconvex neighbors for best merge
903     else
904     test all neighbors for the best merge
905     if testing centrum
906     get distance information
907     */
908     facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
909     facetT *neighbor, **neighborp, *bestfacet= NULL;
910     ridgeT *ridge, **ridgep;
911     boolT nonconvex= True, testcentrum= False;
912     int size= qh_setsize (facet->vertices);
913    
914     *distp= REALmax;
915     if (size > qh_BESTcentrum2 * qh hull_dim + qh_BESTcentrum) {
916     testcentrum= True;
917     zinc_(Zbestcentrum);
918     if (!facet->center)
919     facet->center= qh_getcentrum (facet);
920     }
921     if (size > qh hull_dim + qh_BESTnonconvex) {
922     FOREACHridge_(facet->ridges) {
923     if (ridge->nonconvex) {
924     neighbor= otherfacet_(ridge, facet);
925     qh_findbest_test (testcentrum, facet, neighbor,
926     &bestfacet, distp, mindistp, maxdistp);
927     }
928     }
929     }
930     if (!bestfacet) {
931     nonconvex= False;
932     FOREACHneighbor_(facet)
933     qh_findbest_test (testcentrum, facet, neighbor,
934     &bestfacet, distp, mindistp, maxdistp);
935     }
936     if (!bestfacet) {
937     fprintf (qh ferr, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
938    
939     qh_errexit (qh_ERRqhull, facet, NULL);
940     }
941     if (testcentrum)
942     qh_getdistance (facet, bestfacet, mindistp, maxdistp);
943     trace3((qh ferr, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
944     bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
945     return(bestfacet);
946     } /* findbestneighbor */
947    
948    
949     /*-<a href="qh-merge.htm#TOC"
950     >-------------------------------</a><a name="flippedmerges">-</a>
951    
952     qh_flippedmerges( facetlist, wasmerge )
953     merge flipped facets into best neighbor
954     assumes qh.facet_mergeset at top of temporary stack
955    
956     returns:
957     no flipped facets on facetlist
958     sets wasmerge if merge occurred
959     degen/redundant merges passed through
960    
961     notes:
962     othermerges not needed since qh.facet_mergeset is empty before & after
963     keep it in case of change
964    
965     design:
966     append flipped facets to qh.facetmergeset
967     for each flipped merge
968     find best neighbor
969     merge facet into neighbor
970     merge degenerate and redundant facets
971     remove flipped merges from qh.facet_mergeset
972     */
973     void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) {
974     facetT *facet, *neighbor, *facet1;
975     realT dist, mindist, maxdist;
976     mergeT *merge, **mergep;
977     setT *othermerges;
978     int nummerge=0;
979    
980     trace4((qh ferr, "qh_flippedmerges: begin\n"));
981     FORALLfacet_(facetlist) {
982     if (facet->flipped && !facet->visible)
983     qh_appendmergeset (facet, facet, MRGflip, NULL);
984     }
985     othermerges= qh_settemppop(); /* was facet_mergeset */
986     qh facet_mergeset= qh_settemp (qh TEMPsize);
987     qh_settemppush (othermerges);
988     FOREACHmerge_(othermerges) {
989     facet1= merge->facet1;
990     if (merge->type != MRGflip || facet1->visible)
991     continue;
992     if (qh TRACEmerge-1 == zzval_(Ztotmerge))
993     qhmem.IStracing= qh IStracing= qh TRACElevel;
994     neighbor= qh_findbestneighbor (facet1, &dist, &mindist, &maxdist);
995     trace0((qh ferr, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
996     facet1->id, neighbor->id, dist, qh furthest_id));
997     qh_mergefacet (facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);
998     nummerge++;
999     if (qh PRINTstatistics) {
1000     zinc_(Zflipped);
1001     wadd_(Wflippedtot, dist);
1002     wmax_(Wflippedmax, dist);
1003     }
1004     qh_merge_degenredundant();
1005     }
1006     FOREACHmerge_(othermerges) {
1007     if (merge->facet1->visible || merge->facet2->visible)
1008     qh_memfree (merge, sizeof(mergeT));
1009     else
1010     qh_setappend (&qh facet_mergeset, merge);
1011     }
1012     qh_settempfree (&othermerges);
1013     if (nummerge)
1014     *wasmerge= True;
1015     trace1((qh ferr, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));
1016     } /* flippedmerges */
1017    
1018    
1019     /*-<a href="qh-merge.htm#TOC"
1020     >-------------------------------</a><a name="forcedmerges">-</a>
1021    
1022     qh_forcedmerges( wasmerge )
1023     merge duplicated ridges
1024    
1025     returns:
1026     removes all duplicate ridges on facet_mergeset
1027     wasmerge set if merge
1028     qh.facet_mergeset may include non-forced merges (none for now)
1029     qh.degen_mergeset includes degen/redun merges
1030    
1031     notes:
1032     duplicate ridges occur when the horizon is pinched,
1033     i.e. a subridge occurs in more than two horizon ridges.
1034     could rename vertices that pinch the horizon
1035     assumes qh_merge_degenredundant() has not be called
1036     othermerges isn't needed since facet_mergeset is empty afterwards
1037     keep it in case of change
1038    
1039     design:
1040     for each duplicate ridge
1041     find current facets by chasing f.replace links
1042     determine best direction for facet
1043     merge one facet into the other
1044     remove duplicate ridges from qh.facet_mergeset
1045     */
1046     void qh_forcedmerges(boolT *wasmerge) {
1047     facetT *facet1, *facet2;
1048     mergeT *merge, **mergep;
1049     realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2;
1050     setT *othermerges;
1051     int nummerge=0, numflip=0;
1052    
1053     if (qh TRACEmerge-1 == zzval_(Ztotmerge))
1054     qhmem.IStracing= qh IStracing= qh TRACElevel;
1055     trace4((qh ferr, "qh_forcedmerges: begin\n"));
1056     othermerges= qh_settemppop(); /* was facet_mergeset */
1057     qh facet_mergeset= qh_settemp (qh TEMPsize);
1058     qh_settemppush (othermerges);
1059     FOREACHmerge_(othermerges) {
1060     if (merge->type != MRGridge)
1061     continue;
1062     facet1= merge->facet1;
1063     facet2= merge->facet2;
1064     while (facet1->visible) /* must exist, no qh_merge_degenredunant */
1065     facet1= facet1->f.replace; /* previously merged facet */
1066     while (facet2->visible)
1067     facet2= facet2->f.replace; /* previously merged facet */
1068     if (facet1 == facet2)
1069     continue;
1070     if (!qh_setin (facet2->neighbors, facet1)) {
1071     fprintf (qh ferr, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n",
1072     merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);
1073     qh_errexit2 (qh_ERRqhull, facet1, facet2);
1074     }
1075     if (qh TRACEmerge-1 == zzval_(Ztotmerge))
1076     qhmem.IStracing= qh IStracing= qh TRACElevel;
1077     dist1= qh_getdistance (facet1, facet2, &mindist1, &maxdist1);
1078     dist2= qh_getdistance (facet2, facet1, &mindist2, &maxdist2);
1079     trace0((qh ferr, "qh_forcedmerges: duplicate ridge between f%d and f%d, dist %2.2g and reverse dist %2.2g during p%d\n",
1080     facet1->id, facet2->id, dist1, dist2, qh furthest_id));
1081     if (dist1 < dist2)
1082     qh_mergefacet (facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);
1083     else {
1084     qh_mergefacet (facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex);
1085     dist1= dist2;
1086     facet1= facet2;
1087     }
1088     if (facet1->flipped) {
1089     zinc_(Zmergeflipdup);
1090     numflip++;
1091     }else
1092     nummerge++;
1093     if (qh PRINTstatistics) {
1094     zinc_(Zduplicate);
1095     wadd_(Wduplicatetot, dist1);
1096     wmax_(Wduplicatemax, dist1);
1097     }
1098     }
1099     FOREACHmerge_(othermerges) {
1100     if (merge->type == MRGridge)
1101     qh_memfree (merge, sizeof(mergeT));
1102     else
1103     qh_setappend (&qh facet_mergeset, merge);
1104     }
1105     qh_settempfree (&othermerges);
1106     if (nummerge)
1107     *wasmerge= True;
1108     trace1((qh ferr, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",
1109     nummerge, numflip));
1110     } /* forcedmerges */
1111    
1112    
1113     /*-<a href="qh-merge.htm#TOC"
1114     >-------------------------------</a><a name="getmergeset">-</a>
1115    
1116     qh_getmergeset( facetlist )
1117     determines nonconvex facets on facetlist
1118     tests !tested ridges and nonconvex ridges of !tested facets
1119    
1120     returns:
1121     returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
1122     all ridges tested
1123    
1124     notes:
1125     assumes no nonconvex ridges with both facets tested
1126     uses facet->tested/ridge->tested to prevent duplicate tests
1127     can not limit tests to modified ridges since the centrum changed
1128     uses qh.visit_id
1129    
1130     see:
1131     qh_getmergeset_initial()
1132    
1133     design:
1134     for each facet on facetlist
1135     for each ridge of facet
1136     if untested ridge
1137     test ridge for convexity
1138     if non-convex
1139     append ridge to qh.facet_mergeset
1140     sort qh.facet_mergeset by angle
1141     */
1142     void qh_getmergeset(facetT *facetlist) {
1143     facetT *facet, *neighbor, **neighborp;
1144     ridgeT *ridge, **ridgep;
1145     int nummerges;
1146    
1147     nummerges= qh_setsize (qh facet_mergeset);
1148     trace4((qh ferr, "qh_getmergeset: started.\n"));
1149     qh visit_id++;
1150     FORALLfacet_(facetlist) {
1151     if (facet->tested)
1152     continue;
1153     facet->visitid= qh visit_id;
1154     facet->tested= True; /* must be non-simplicial due to merge */
1155     FOREACHneighbor_(facet)
1156     neighbor->seen= False;
1157     FOREACHridge_(facet->ridges) {
1158     if (ridge->tested && !ridge->nonconvex)
1159     continue;
1160     /* if tested & nonconvex, need to append merge */
1161     neighbor= otherfacet_(ridge, facet);
1162     if (neighbor->seen) {
1163     ridge->tested= True;
1164     ridge->nonconvex= False;
1165     }else if (neighbor->visitid != qh visit_id) {
1166     ridge->tested= True;
1167     ridge->nonconvex= False;
1168     neighbor->seen= True; /* only one ridge is marked nonconvex */
1169     if (qh_test_appendmerge (facet, neighbor))
1170     ridge->nonconvex= True;
1171     }
1172     }
1173     }
1174     nummerges= qh_setsize (qh facet_mergeset);
1175     if (qh ANGLEmerge)
1176     qsort(SETaddr_(qh facet_mergeset, mergeT), nummerges,sizeof(mergeT *),qh_compareangle);
1177     else
1178     qsort(SETaddr_(qh facet_mergeset, mergeT), nummerges,sizeof(mergeT *),qh_comparemerge);
1179     if (qh POSTmerging) {
1180     zadd_(Zmergesettot2, nummerges);
1181     }else {
1182     zadd_(Zmergesettot, nummerges);
1183     zmax_(Zmergesetmax, nummerges);
1184     }
1185     trace2((qh ferr, "qh_getmergeset: %d merges found\n", nummerges));
1186     } /* getmergeset */
1187    
1188    
1189     /*-<a href="qh-merge.htm#TOC"
1190     >-------------------------------</a><a name="getmergeset_initial">-</a>
1191    
1192     qh_getmergeset_initial( facetlist )
1193     determine initial qh.facet_mergeset for facets
1194     tests all facet/neighbor pairs on facetlist
1195    
1196     returns:
1197     sorted qh.facet_mergeset with nonconvex ridges
1198     sets facet->tested, ridge->tested, and ridge->nonconvex
1199    
1200     notes:
1201     uses visit_id, assumes ridge->nonconvex is False
1202    
1203     see:
1204     qh_getmergeset()
1205    
1206     design:
1207     for each facet on facetlist
1208     for each untested neighbor of facet
1209     test facet and neighbor for convexity
1210     if non-convex
1211     append merge to qh.facet_mergeset
1212     mark one of the ridges as nonconvex
1213     sort qh.facet_mergeset by angle
1214     */
1215     void qh_getmergeset_initial (facetT *facetlist) {
1216     facetT *facet, *neighbor, **neighborp;
1217     ridgeT *ridge, **ridgep;
1218     int nummerges;
1219    
1220     qh visit_id++;
1221     FORALLfacet_(facetlist) {
1222     facet->visitid= qh visit_id;
1223     facet->tested= True;
1224     FOREACHneighbor_(facet) {
1225     if (neighbor->visitid != qh visit_id) {
1226     if (qh_test_appendmerge (facet, neighbor)) {
1227     FOREACHridge_(neighbor->ridges) {
1228     if (facet == otherfacet_(ridge, neighbor)) {
1229     ridge->nonconvex= True;
1230     break; /* only one ridge is marked nonconvex */
1231     }
1232     }
1233     }
1234     }
1235     }
1236     FOREACHridge_(facet->ridges)
1237     ridge->tested= True;
1238     }
1239     nummerges= qh_setsize (qh facet_mergeset);
1240     if (qh ANGLEmerge)
1241     qsort(SETaddr_(qh facet_mergeset, mergeT), nummerges,sizeof(mergeT *),qh_compareangle);
1242     else
1243     qsort(SETaddr_(qh facet_mergeset, mergeT), nummerges,sizeof(mergeT *),qh_comparemerge);
1244     if (qh POSTmerging) {
1245     zadd_(Zmergeinittot2, nummerges);
1246     }else {
1247     zadd_(Zmergeinittot, nummerges);
1248     zmax_(Zmergeinitmax, nummerges);
1249     }
1250     trace2((qh ferr, "qh_getmergeset_initial: %d merges found\n", nummerges));
1251     } /* getmergeset_initial */
1252    
1253    
1254     /*-<a href="qh-merge.htm#TOC"
1255     >-------------------------------</a><a name="hashridge">-</a>
1256    
1257     qh_hashridge( hashtable, hashsize, ridge, oldvertex )
1258     add ridge to hashtable without oldvertex
1259    
1260     notes:
1261     assumes hashtable is large enough
1262    
1263     design:
1264     determine hash value for ridge without oldvertex
1265     find next empty slot for ridge
1266     */
1267     void qh_hashridge (setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
1268     int hash;
1269     ridgeT *ridgeA;
1270    
1271     hash= (int)qh_gethash (hashsize, ridge->vertices, qh hull_dim-1, 0, oldvertex);
1272     while (True) {
1273     if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
1274     SETelem_(hashtable, hash)= ridge;
1275     break;
1276     }else if (ridgeA == ridge)
1277     break;
1278     if (++hash == hashsize)
1279     hash= 0;
1280     }
1281     } /* hashridge */
1282    
1283    
1284     /*-<a href="qh-merge.htm#TOC"
1285     >-------------------------------</a><a name="hashridge_find">-</a>
1286    
1287     qh_hashridge_find( hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
1288     returns matching ridge without oldvertex in hashtable
1289     for ridge without vertex
1290     if oldvertex is NULL
1291     matches with any one skip
1292    
1293     returns:
1294     matching ridge or NULL
1295     if no match,
1296     if ridge already in table
1297     hashslot= -1
1298     else
1299     hashslot= next NULL index
1300    
1301     notes:
1302     assumes hashtable is large enough
1303     can't match ridge to itself
1304    
1305     design:
1306     get hash value for ridge without vertex
1307     for each hashslot
1308     return match if ridge matches ridgeA without oldvertex
1309     */
1310     ridgeT *qh_hashridge_find (setT *hashtable, int hashsize, ridgeT *ridge,
1311     vertexT *vertex, vertexT *oldvertex, int *hashslot) {
1312     int hash;
1313     ridgeT *ridgeA;
1314    
1315     *hashslot= 0;
1316     zinc_(Zhashridge);
1317     hash= (int)qh_gethash (hashsize, ridge->vertices, qh hull_dim-1, 0, vertex);
1318     while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
1319     if (ridgeA == ridge)
1320     *hashslot= -1;
1321     else {
1322     zinc_(Zhashridgetest);
1323     if (qh_setequal_except (ridge->vertices, vertex, ridgeA->vertices, oldvertex))
1324     return ridgeA;
1325     }
1326     if (++hash == hashsize)
1327     hash= 0;
1328     }
1329     if (!*hashslot)
1330     *hashslot= hash;
1331     return NULL;
1332     } /* hashridge_find */
1333    
1334    
1335     /*-<a href="qh-merge.htm#TOC"
1336     >-------------------------------</a><a name="makeridges">-</a>
1337    
1338     qh_makeridges( facet )
1339     creates explicit ridges between simplicial facets
1340    
1341     returns:
1342     facet with ridges and without qh_MERGEridge
1343     ->simplicial is False
1344    
1345     notes:
1346     allows qh_MERGEridge flag
1347     uses existing ridges
1348     duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)
1349    
1350     see:
1351     qh_mergecycle_ridges()
1352    
1353     design:
1354     look for qh_MERGEridge neighbors
1355     mark neighbors that already have ridges
1356     for each unprocessed neighbor of facet
1357     create a ridge for neighbor and facet
1358     if any qh_MERGEridge neighbors
1359     delete qh_MERGEridge flags (already handled by qh_mark_dupridges)
1360     */
1361     void qh_makeridges(facetT *facet) {
1362     facetT *neighbor, **neighborp;
1363     ridgeT *ridge, **ridgep;
1364     int neighbor_i, neighbor_n;
1365     boolT toporient, mergeridge= False;
1366    
1367     if (!facet->simplicial)
1368     return;
1369     trace4((qh ferr, "qh_makeridges: make ridges for f%d\n", facet->id));
1370     facet->simplicial= False;
1371     FOREACHneighbor_(facet) {
1372     if (neighbor == qh_MERGEridge)
1373     mergeridge= True;
1374     else
1375     neighbor->seen= False;
1376     }
1377     FOREACHridge_(facet->ridges)
1378     otherfacet_(ridge, facet)->seen= True;
1379     FOREACHneighbor_i_(facet) {
1380     if (neighbor == qh_MERGEridge)
1381     continue; /* fixed by qh_mark_dupridges */
1382     else if (!neighbor->seen) { /* no current ridges */
1383     ridge= qh_newridge();
1384     ridge->vertices= qh_setnew_delnthsorted (facet->vertices, qh hull_dim,
1385     neighbor_i, 0);
1386     toporient= facet->toporient ^ (neighbor_i & 0x1);
1387     if (toporient) {
1388     ridge->top= facet;
1389     ridge->bottom= neighbor;
1390     }else {
1391     ridge->top= neighbor;
1392     ridge->bottom= facet;
1393     }
1394     #if 0
1395     /* this also works */
1396     flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);
1397     if (facet->toporient ^ (skip1 & 0x1) ^ flip) {
1398     ridge->top= neighbor;
1399     ridge->bottom= facet;
1400     }else {
1401     ridge->top= facet;
1402     ridge->bottom= neighbor;
1403     }
1404     #endif
1405     qh_setappend(&(facet->ridges), ridge);
1406     qh_setappend(&(neighbor->ridges), ridge);
1407     }
1408     }
1409     if (mergeridge) {
1410     while (qh_setdel (facet->neighbors, qh_MERGEridge))
1411     ; /* delete each one */
1412     }
1413     } /* makeridges */
1414    
1415    
1416     /*-<a href="qh-merge.htm#TOC"
1417     >-------------------------------</a><a name="mark_dupridges">-</a>
1418    
1419     qh_mark_dupridges( facetlist )
1420     add duplicated ridges to qh.facet_mergeset
1421     facet->dupridge is true
1422    
1423     returns:
1424     duplicate ridges on qh.facet_mergeset
1425     ->mergeridge/->mergeridge2 set
1426     duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge
1427     no MERGEridges in neighbor sets
1428    
1429     notes:
1430     duplicate ridges occur when the horizon is pinched,
1431     i.e. a subridge occurs in more than two horizon ridges.
1432     could rename vertices that pinch the horizon
1433     uses qh.visit_id
1434    
1435     design:
1436     for all facets on facetlist
1437     if facet contains a duplicate ridge
1438     for each neighbor of facet
1439     if neighbor marked qh_MERGEridge (one side of the merge)
1440     set facet->mergeridge
1441     else
1442     if neighbor contains a duplicate ridge
1443     and the back link is qh_MERGEridge
1444     append duplicate ridge to qh.facet_mergeset
1445     for each duplicate ridge
1446     make ridge sets in preparation for merging
1447     remove qh_MERGEridge from neighbor set
1448     for each duplicate ridge
1449     restore the missing neighbor from the neighbor set that was qh_MERGEridge
1450     add the missing ridge for this neighbor
1451     */
1452     void qh_mark_dupridges(facetT *facetlist) {
1453     facetT *facet, *neighbor, **neighborp;
1454     int nummerge=0;
1455     mergeT *merge, **mergep;
1456    
1457    
1458     trace4((qh ferr, "qh_mark_dupridges: identify duplicate ridges\n"));
1459     FORALLfacet_(facetlist) {
1460     if (facet->dupridge) {
1461     FOREACHneighbor_(facet) {
1462     if (neighbor == qh_MERGEridge) {
1463     facet->mergeridge= True;
1464     continue;
1465     }
1466     if (neighbor->dupridge
1467     && !qh_setin (neighbor->neighbors, facet)) { /* qh_MERGEridge */
1468     qh_appendmergeset (facet, neighbor, MRGridge, NULL);
1469     facet->mergeridge2= True;
1470     facet->mergeridge= True;
1471     nummerge++;
1472     }
1473     }
1474     }
1475     }
1476     if (!nummerge)
1477     return;
1478     FORALLfacet_(facetlist) { /* gets rid of qh_MERGEridge */
1479     if (facet->mergeridge && !facet->mergeridge2)
1480     qh_makeridges (facet);
1481     }
1482     FOREACHmerge_(qh facet_mergeset) { /* restore the missing neighbors */
1483     if (merge->type == MRGridge) {
1484     qh_setappend (&merge->facet2->neighbors, merge->facet1);
1485     qh_makeridges (merge->facet1); /* and the missing ridges */
1486     }
1487     }
1488     trace1((qh ferr, "qh_mark_dupridges: found %d duplicated ridges\n",
1489     nummerge));
1490     } /* mark_dupridges */
1491    
1492     /*-<a href="qh-merge.htm#TOC"
1493     >-------------------------------</a><a name="maydropneighbor">-</a>
1494    
1495     qh_maydropneighbor( facet )
1496     drop neighbor relationship if no ridge between facet and neighbor
1497    
1498     returns:
1499     neighbor sets updated
1500     appends degenerate facets to qh.facet_mergeset
1501    
1502     notes:
1503     won't cause redundant facets since vertex inclusion is the same
1504     may drop vertex and neighbor if no ridge
1505     uses qh.visit_id
1506    
1507     design:
1508     visit all neighbors with ridges
1509     for each unvisited neighbor of facet
1510     delete neighbor and facet from the neighbor sets
1511     if neighbor becomes degenerate
1512     append neighbor to qh.degen_mergeset
1513     if facet is degenerate
1514     append facet to qh.degen_mergeset
1515     */
1516     void qh_maydropneighbor (facetT *facet) {
1517     ridgeT *ridge, **ridgep;
1518     realT angledegen= qh_ANGLEdegen;
1519     facetT *neighbor, **neighborp;
1520    
1521     qh visit_id++;
1522     trace4((qh ferr, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
1523     facet->id));
1524     FOREACHridge_(facet->ridges) {
1525     ridge->top->visitid= qh visit_id;
1526     ridge->bottom->visitid= qh visit_id;
1527     }
1528     FOREACHneighbor_(facet) {
1529     if (neighbor->visitid != qh visit_id) {
1530     trace0((qh ferr, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n",
1531     facet->id, neighbor->id, qh furthest_id));
1532     zinc_(Zdropneighbor);
1533     qh_setdel (facet->neighbors, neighbor);
1534     neighborp--; /* repeat, deleted a neighbor */
1535     qh_setdel (neighbor->neighbors, facet);
1536     if (qh_setsize (neighbor->neighbors) < qh hull_dim) {
1537     zinc_(Zdropdegen);
1538     qh_appendmergeset (neighbor, neighbor, MRGdegen, &angledegen);
1539     trace2((qh ferr, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
1540     }
1541     }
1542     }
1543     if (qh_setsize (facet->neighbors) < qh hull_dim) {
1544     zinc_(Zdropdegen);
1545     qh_appendmergeset (facet, facet, MRGdegen, &angledegen);
1546     trace2((qh ferr, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
1547     }
1548     } /* maydropneighbor */
1549    
1550    
1551     /*-<a href="qh-merge.htm#TOC"
1552     >-------------------------------</a><a name="merge_degenredundant">-</a>
1553    
1554     qh_merge_degenredundant()
1555     merge all degenerate and redundant facets
1556     qh.degen_mergeset contains merges from qh_degen_redundant_neighbors()
1557    
1558     returns:
1559     number of merges performed
1560     resets facet->degenerate/redundant
1561     if deleted (visible) facet has no neighbors
1562     sets ->f.replace to NULL
1563    
1564     notes:
1565     redundant merges happen before degenerate ones
1566     merging and renaming vertices can result in degen/redundant facets
1567    
1568     design:
1569     for each merge on qh.degen_mergeset
1570     if redundant merge
1571     if non-redundant facet merged into redundant facet
1572     recheck facet for redundancy
1573     else
1574     merge redundant facet into other facet
1575     */
1576     int qh_merge_degenredundant (void) {
1577     int size;
1578     mergeT *merge;
1579     facetT *bestneighbor, *facet1, *facet2;
1580     realT dist, mindist, maxdist;
1581     vertexT *vertex, **vertexp;
1582     int nummerges= 0;
1583     mergeType mergetype;
1584    
1585     while ((merge= (mergeT*)qh_setdellast (qh degen_mergeset))) {
1586     facet1= merge->facet1;
1587     facet2= merge->facet2;
1588     mergetype= merge->type;
1589     qh_memfree (merge, sizeof(mergeT));
1590     if (facet1->visible)
1591     continue;
1592     facet1->degenerate= False;
1593     facet1->redundant= False;
1594     if (qh TRACEmerge-1 == zzval_(Ztotmerge))
1595     qhmem.IStracing= qh IStracing= qh TRACElevel;
1596     if (mergetype == MRGredundant) {
1597     zinc_(Zneighbor);
1598     while (facet2->visible) {
1599     if (!facet2->f.replace) {
1600     fprintf (qh ferr, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n",
1601     facet1->id, facet2->id);
1602     qh_errexit2 (qh_ERRqhull, facet1, facet2);
1603     }
1604     facet2= facet2->f.replace;
1605     }
1606     if (facet1 == facet2) {
1607     qh_degen_redundant_facet (facet1); /* in case of others */
1608     continue;
1609     }
1610     trace2((qh ferr, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",
1611     facet1->id, facet2->id));
1612     qh_mergefacet(facet1, facet2, NULL, NULL, !qh_MERGEapex);
1613     /* merge distance is already accounted for */
1614     nummerges++;
1615     }else { /* mergetype == MRGdegen, other merges may have fixed */
1616     if (!(size= qh_setsize (facet1->neighbors))) {
1617     zinc_(Zdelfacetdup);
1618     trace2((qh ferr, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id));
1619     qh_willdelete (facet1, NULL);
1620     FOREACHvertex_(facet1->vertices) {
1621     qh_setdel (vertex->neighbors, facet1);
1622     if (!SETfirst_(vertex->neighbors)) {
1623     zinc_(Zdegenvertex);
1624     trace2((qh ferr, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
1625     vertex->id, facet1->id));
1626     vertex->deleted= True;
1627     qh_setappend (&qh del_vertices, vertex);
1628     }
1629     }
1630     nummerges++;
1631     }else if (size < qh hull_dim) {
1632     bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
1633     trace2((qh ferr, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
1634     facet1->id, size, bestneighbor->id, dist));
1635     qh_mergefacet(facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
1636     nummerges++;
1637     if (qh PRINTstatistics) {
1638     zinc_(Zdegen);
1639     wadd_(Wdegentot, dist);
1640     wmax_(Wdegenmax, dist);
1641     }
1642     } /* else, another merge fixed the degeneracy and redundancy tested */
1643     }
1644     }
1645     return nummerges;
1646     } /* merge_degenredundant */
1647    
1648     /*-<a href="qh-merge.htm#TOC"
1649     >-------------------------------</a><a name="merge_nonconvex">-</a>
1650    
1651     qh_merge_nonconvex( facet1, facet2, mergetype )
1652     remove non-convex ridge between facet1 into facet2
1653     mergetype gives why the facet's are non-convex
1654    
1655     returns:
1656     merges one of the facets into the best neighbor
1657    
1658     design:
1659     if one of the facets is a new facet
1660     prefer merging new facet into old facet
1661     find best neighbors for both facets
1662     merge the nearest facet into its best neighbor
1663     update the statistics
1664     */
1665     void qh_merge_nonconvex (facetT *facet1, facetT *facet2, mergeType mergetype) {
1666     facetT *bestfacet, *bestneighbor, *neighbor;
1667     realT dist, dist2, mindist, mindist2, maxdist, maxdist2;
1668    
1669     if (qh TRACEmerge-1 == zzval_(Ztotmerge))
1670     qhmem.IStracing= qh IStracing= qh TRACElevel;
1671     trace3((qh ferr, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
1672     zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
1673     /* concave or coplanar */
1674     if (!facet1->newfacet) {
1675     bestfacet= facet2; /* avoid merging old facet if new is ok */
1676     facet2= facet1;
1677     facet1= bestfacet;
1678     }else
1679     bestfacet= facet1;
1680     bestneighbor= qh_findbestneighbor(bestfacet, &dist, &mindist, &maxdist);
1681     neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2);
1682     if (dist < dist2) {
1683     qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
1684     }else if (qh AVOIDold && !facet2->newfacet
1685     && ((mindist >= -qh MAXcoplanar && maxdist <= qh max_outside)
1686     || dist * 1.5 < dist2)) {
1687     zinc_(Zavoidold);
1688     wadd_(Wavoidoldtot, dist);
1689     wmax_(Wavoidoldmax, dist);
1690     trace2((qh ferr, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n",
1691     facet2->id, dist2, facet1->id, dist2));
1692     qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
1693     }else {
1694     qh_mergefacet(facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex);
1695     dist= dist2;
1696     }
1697     if (qh PRINTstatistics) {
1698     if (mergetype == MRGanglecoplanar) {
1699     zinc_(Zacoplanar);
1700     wadd_(Wacoplanartot, dist);
1701     wmax_(Wacoplanarmax, dist);
1702     }else if (mergetype == MRGconcave) {
1703     zinc_(Zconcave);
1704     wadd_(Wconcavetot, dist);
1705     wmax_(Wconcavemax, dist);
1706     }else { /* MRGcoplanar */
1707     zinc_(Zcoplanar);
1708     wadd_(Wcoplanartot, dist);
1709     wmax_(Wcoplanarmax, dist);
1710     }
1711     }
1712     } /* merge_nonconvex */
1713    
1714     /*-<a href="qh-merge.htm#TOC"
1715     >-------------------------------</a><a name="mergecycle">-</a>
1716    
1717     qh_mergecycle( samecycle, newfacet )
1718     merge a cycle of facets starting at samecycle into a newfacet
1719     newfacet is a horizon facet with ->normal
1720     samecycle facets are simplicial from an apex
1721    
1722     returns:
1723     initializes vertex neighbors on first merge
1724     samecycle deleted (placed on qh.visible_list)
1725     newfacet at end of qh.facet_list
1726     deleted vertices on qh.del_vertices
1727    
1728     see:
1729     qh_mergefacet()
1730     called by qh_mergecycle_all() for multiple, same cycle facets
1731    
1732     design:
1733     make vertex neighbors if necessary
1734     make ridges for newfacet
1735     merge neighbor sets of samecycle into newfacet
1736     merge ridges of samecycle into newfacet
1737     merge vertex neighbors of samecycle into newfacet
1738     make apex of samecycle the apex of newfacet
1739     if newfacet wasn't a new facet
1740     add its vertices to qh.newvertex_list
1741     delete samecycle facets a make newfacet a newfacet
1742     */
1743     void qh_mergecycle (facetT *samecycle, facetT *newfacet) {
1744     int traceonce= False, tracerestore= 0;
1745     vertexT *apex;
1746     #ifndef qh_NOtrace
1747     facetT *same;
1748     #endif
1749    
1750     if (newfacet->tricoplanar) {
1751     if (!qh TRInormals) {
1752     fprintf (qh ferr, "qh_mergecycle: does not work for tricoplanar facets. Use option 'Q11'\n");
1753     qh_errexit (qh_ERRqhull, newfacet, NULL);
1754     }
1755     newfacet->tricoplanar= False;
1756     newfacet->keepcentrum= False;
1757     }
1758     if (!qh VERTEXneighbors)
1759     qh_vertexneighbors();
1760     zzinc_(Ztotmerge);
1761     if (qh REPORTfreq2 && qh POSTmerging) {
1762     if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
1763     qh_tracemerging();
1764     }
1765     #ifndef qh_NOtrace
1766     if (qh TRACEmerge == zzval_(Ztotmerge))
1767     qhmem.IStracing= qh IStracing= qh TRACElevel;
1768     trace2((qh ferr, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
1769     zzval_(Ztotmerge), samecycle->id, newfacet->id));
1770     if (newfacet == qh tracefacet) {
1771     tracerestore= qh IStracing;
1772     qh IStracing= 4;
1773     fprintf (qh ferr, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
1774     zzval_(Ztotmerge), samecycle->id, newfacet->id, qh furthest_id);
1775     traceonce= True;
1776     }
1777     if (qh IStracing >=4) {
1778     fprintf (qh ferr, " same cycle:");
1779     FORALLsame_cycle_(samecycle)
1780     fprintf(qh ferr, " f%d", same->id);
1781     fprintf (qh ferr, "\n");
1782     }
1783     if (qh IStracing >=4)
1784     qh_errprint ("MERGING CYCLE", samecycle, newfacet, NULL, NULL);
1785     #endif /* !qh_NOtrace */
1786     apex= SETfirstt_(samecycle->vertices, vertexT);
1787     qh_makeridges (newfacet);
1788     qh_mergecycle_neighbors (samecycle, newfacet);
1789     qh_mergecycle_ridges (samecycle, newfacet);
1790     qh_mergecycle_vneighbors (samecycle, newfacet);
1791     if (SETfirstt_(newfacet->vertices, vertexT) != apex)
1792     qh_setaddnth (&newfacet->vertices, 0, apex); /* apex has last id */
1793     if (!newfacet->newfacet)
1794     qh_newvertices (newfacet->vertices);
1795     qh_mergecycle_facets (samecycle, newfacet);
1796     qh_tracemerge (samecycle, newfacet);
1797     /* check for degen_redundant_neighbors after qh_forcedmerges() */
1798     if (traceonce) {
1799     fprintf (qh ferr, "qh_mergecycle: end of trace facet\n");
1800     qh IStracing= tracerestore;
1801     }
1802     } /* mergecycle */
1803    
1804     /*-<a href="qh-merge.htm#TOC"
1805     >-------------------------------</a><a name="mergecycle_all">-</a>
1806    
1807     qh_mergecycle_all( facetlist, wasmerge )
1808     merge all samecycles of coplanar facets into horizon
1809     don't merge facets with ->mergeridge (these already have ->normal)
1810     all facets are simplicial from apex
1811     all facet->cycledone == False
1812    
1813     returns:
1814     all newfacets merged into coplanar horizon facets
1815     deleted vertices on qh.del_vertices
1816     sets wasmerge if any merge
1817    
1818     see:
1819     calls qh_mergecycle for multiple, same cycle facets
1820    
1821     design:
1822     for each facet on facetlist
1823     skip facets with duplicate ridges and normals
1824     check that facet is in a samecycle (->mergehorizon)
1825     if facet only member of samecycle
1826     sets vertex->delridge for all vertices except apex
1827     merge facet into horizon
1828     else
1829     mark all facets in samecycle
1830     remove facets with duplicate ridges from samecycle
1831     merge samecycle into horizon (deletes facets from facetlist)
1832     */
1833     void qh_mergecycle_all (facetT *facetlist, boolT *wasmerge) {
1834     facetT *facet, *same, *prev, *horizon;
1835     facetT *samecycle= NULL, *nextfacet, *nextsame;
1836     vertexT *apex, *vertex, **vertexp;
1837     int cycles=0, total=0, facets, nummerge;
1838    
1839     trace2((qh ferr, "qh_mergecycle_all: begin\n"));
1840     for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {
1841     if (facet->normal)
1842     continue;
1843     if (!facet->mergehorizon) {
1844     fprintf (qh ferr, "qh_mergecycle_all: f%d without normal\n", facet->id);
1845     qh_errexit (qh_ERRqhull, facet, NULL);
1846     }
1847     horizon= SETfirstt_(facet->neighbors, facetT);
1848     if (facet->f.samecycle == facet) {
1849     zinc_(Zonehorizon);
1850     /* merge distance done in qh_findhorizon */
1851     apex= SETfirstt_(facet->vertices, vertexT);
1852     FOREACHvertex_(facet->vertices) {
1853     if (vertex != apex)
1854     vertex->delridge= True;
1855     }
1856     horizon->f.newcycle= NULL;
1857     qh_mergefacet (facet, horizon, NULL, NULL, qh_MERGEapex);
1858     }else {
1859     samecycle= facet;
1860     facets= 0;
1861     prev= facet;
1862     for (same= facet->f.samecycle; same; /* FORALLsame_cycle_(facet) */
1863     same= (same == facet ? NULL :nextsame)) { /* ends at facet */
1864     nextsame= same->f.samecycle;
1865     if (same->cycledone || same->visible)
1866     qh_infiniteloop (same);
1867     same->cycledone= True;
1868     if (same->normal) {
1869     prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
1870     same->f.samecycle= NULL;
1871     }else {
1872     prev= same;
1873     facets++;
1874     }
1875     }
1876     while (nextfacet && nextfacet->cycledone) /* will delete samecycle */
1877     nextfacet= nextfacet->next;
1878     horizon->f.newcycle= NULL;
1879     qh_mergecycle (samecycle, horizon);
1880     nummerge= horizon->nummerge + facets;
1881     if (nummerge > qh_MAXnummerge)
1882     horizon->nummerge= qh_MAXnummerge;
1883     else
1884     horizon->nummerge= nummerge;
1885     zzinc_(Zcyclehorizon);
1886     total += facets;
1887     zzadd_(Zcyclefacettot, facets);
1888     zmax_(Zcyclefacetmax, facets);
1889     }
1890     cycles++;
1891     }
1892     if (cycles)
1893     *wasmerge= True;
1894     trace1((qh ferr, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));
1895     } /* mergecycle_all */
1896    
1897     /*-<a href="qh-merge.htm#TOC"
1898     >-------------------------------</a><a name="mergecycle_facets">-</a>
1899    
1900     qh_mergecycle_facets( samecycle, newfacet )
1901     finish merge of samecycle into newfacet
1902    
1903     returns:
1904     samecycle prepended to visible_list for later deletion and partitioning
1905     each facet->f.replace == newfacet
1906    
1907     newfacet moved to end of qh.facet_list
1908     makes newfacet a newfacet (get's facet1->id if it was old)
1909     sets newfacet->newmerge
1910     clears newfacet->center (unless merging into a large facet)
1911     clears newfacet->tested and ridge->tested for facet1
1912    
1913     adds neighboring facets to facet_mergeset if redundant or degenerate
1914    
1915     design:
1916     make newfacet a new facet and set its flags
1917     move samecycle facets to qh.visible_list for later deletion
1918     unless newfacet is large
1919     remove its centrum
1920     */
1921     void qh_mergecycle_facets (facetT *samecycle, facetT *newfacet) {
1922     facetT *same, *next;
1923    
1924     trace4((qh ferr, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
1925     qh_removefacet(newfacet); /* append as a newfacet to end of qh facet_list */
1926     qh_appendfacet(newfacet);
1927     newfacet->newfacet= True;
1928     newfacet->simplicial= False;
1929     newfacet->newmerge= True;
1930    
1931     for (same= samecycle->f.samecycle; same; same= (same == samecycle ? NULL : next)) {
1932     next= same->f.samecycle; /* reused by willdelete */
1933     qh_willdelete (same, newfacet);
1934     }
1935     if (newfacet->center
1936     && qh_setsize (newfacet->vertices) <= qh hull_dim + qh_MAXnewcentrum) {
1937     qh_memfree (newfacet->center, qh normal_size);
1938     newfacet->center= NULL;
1939     }
1940     trace3((qh ferr, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
1941     samecycle->id, newfacet->id));
1942     } /* mergecycle_facets */
1943    
1944     /*-<a href="qh-merge.htm#TOC"
1945     >-------------------------------</a><a name="mergecycle_neighbors">-</a>
1946    
1947     qh_mergecycle_neighbors( samecycle, newfacet )
1948     add neighbors for samecycle facets to newfacet
1949    
1950     returns:
1951     newfacet with updated neighbors and vice-versa
1952     newfacet has ridges
1953     all neighbors of newfacet marked with qh.visit_id
1954     samecycle facets marked with qh.visit_id-1
1955     ridges updated for simplicial neighbors of samecycle with a ridge
1956    
1957     notes:
1958     assumes newfacet not in samecycle
1959     usually, samecycle facets are new, simplicial facets without internal ridges
1960     not so if horizon facet is coplanar to two different samecycles
1961    
1962     see:
1963     qh_mergeneighbors()
1964    
1965     design:
1966     check samecycle
1967     delete neighbors from newfacet that are also in samecycle
1968     for each neighbor of a facet in samecycle
1969     if neighbor is simplicial
1970     if first visit
1971     move the neighbor relation to newfacet
1972     update facet links for its ridges
1973     else
1974     make ridges for neighbor
1975     remove samecycle reference
1976     else
1977     update neighbor sets
1978     */
1979     void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet) {
1980     facetT *same, *neighbor, **neighborp;
1981     int delneighbors= 0, newneighbors= 0;
1982     unsigned int samevisitid;
1983     ridgeT *ridge, **ridgep;
1984    
1985     samevisitid= ++qh visit_id;
1986     FORALLsame_cycle_(samecycle) {
1987     if (same->visitid == samevisitid || same->visible)
1988     qh_infiniteloop (samecycle);
1989     same->visitid= samevisitid;
1990     }
1991     newfacet->visitid= ++qh visit_id;
1992     trace4((qh ferr, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
1993     FOREACHneighbor_(newfacet) {
1994     if (neighbor->visitid == samevisitid) {
1995     SETref_(neighbor)= NULL; /* samecycle neighbors deleted */
1996     delneighbors++;
1997     }else
1998     neighbor->visitid= qh visit_id;
1999     }
2000     qh_setcompact (newfacet->neighbors);
2001    
2002     trace4((qh ferr, "qh_mergecycle_neighbors: update neighbors\n"));
2003     FORALLsame_cycle_(samecycle) {
2004     FOREACHneighbor_(same) {
2005     if (neighbor->visitid == samevisitid)
2006     continue;
2007     if (neighbor->simplicial) {
2008     if (neighbor->visitid != qh visit_id) {
2009     qh_setappend (&newfacet->neighbors, neighbor);
2010     qh_setreplace (neighbor->neighbors, same, newfacet);
2011     newneighbors++;
2012     neighbor->visitid= qh visit_id;
2013     FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
2014     if (ridge->top == same) {
2015     ridge->top= newfacet;
2016     break;
2017     }else if (ridge->bottom == same) {
2018     ridge->bottom= newfacet;
2019     break;
2020     }
2021     }
2022     }else {
2023     qh_makeridges (neighbor);
2024     qh_setdel (neighbor->neighbors, same);
2025     /* same can't be horizon facet for neighbor */
2026     }
2027     }else { /* non-simplicial neighbor */
2028     qh_setdel (neighbor->neighbors, same);
2029     if (neighbor->visitid != qh visit_id) {
2030     qh_setappend (&neighbor->neighbors, newfacet);
2031     qh_setappend (&newfacet->neighbors, neighbor);
2032     neighbor->visitid= qh visit_id;
2033     newneighbors++;
2034     }
2035     }
2036     }
2037     }
2038     trace2((qh ferr, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
2039     delneighbors, newneighbors));
2040     } /* mergecycle_neighbors */
2041    
2042     /*-<a href="qh-merge.htm#TOC"
2043     >-------------------------------</a><a name="mergecycle_ridges">-</a>
2044    
2045     qh_mergecycle_ridges( samecycle, newfacet )
2046     add ridges/neighbors for facets in samecycle to newfacet
2047     all new/old neighbors of newfacet marked with qh.visit_id
2048     facets in samecycle marked with qh.visit_id-1
2049     newfacet marked with qh.visit_id
2050    
2051     returns:
2052     newfacet has merged ridges
2053    
2054     notes:
2055     ridge already updated for simplicial neighbors of samecycle with a ridge
2056    
2057     see:
2058     qh_mergeridges()
2059     qh_makeridges()
2060    
2061     design:
2062     remove ridges between newfacet and samecycle
2063     for each facet in samecycle
2064     for each ridge in facet
2065     update facet pointers in ridge
2066     skip ridges processed in qh_mergecycle_neighors
2067     free ridges between newfacet and samecycle
2068     free ridges between facets of samecycle (on 2nd visit)
2069     append remaining ridges to newfacet
2070     if simpilicial facet
2071     for each neighbor of facet
2072     if simplicial facet
2073     and not samecycle facet or newfacet
2074     make ridge between neighbor and newfacet
2075     */
2076     void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet) {
2077     facetT *same, *neighbor= NULL;
2078     int numold=0, numnew=0;
2079     int neighbor_i, neighbor_n;
2080     unsigned int samevisitid;
2081     ridgeT *ridge, **ridgep;
2082     boolT toporient;
2083     void **freelistp; /* used !qh_NOmem */
2084    
2085     trace4((qh ferr, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
2086     samevisitid= qh visit_id -1;
2087     FOREACHridge_(newfacet->ridges) {
2088     neighbor= otherfacet_(ridge, newfacet);
2089     if (neighbor->visitid == samevisitid)
2090     SETref_(ridge)= NULL; /* ridge free'd below */
2091     }
2092     qh_setcompact (newfacet->ridges);
2093    
2094     trace4((qh ferr, "qh_mergecycle_ridges: add ridges to newfacet\n"));
2095     FORALLsame_cycle_(samecycle) {
2096     FOREACHridge_(same->ridges) {
2097     if (ridge->top == same) {
2098     ridge->top= newfacet;
2099     neighbor= ridge->bottom;
2100     }else if (ridge->bottom == same) {
2101     ridge->bottom= newfacet;
2102     neighbor= ridge->top;
2103     }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
2104     qh_setappend (&newfacet->ridges, ridge);
2105     numold++; /* already set by qh_mergecycle_neighbors */
2106     continue;
2107     }else {
2108     fprintf (qh ferr, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
2109     qh_errexit (qh_ERRqhull, NULL, ridge);
2110     }
2111     if (neighbor == newfacet) {
2112     qh_setfree(&(ridge->vertices));
2113     qh_memfree_(ridge, sizeof(ridgeT), freelistp);
2114     numold++;
2115     }else if (neighbor->visitid == samevisitid) {
2116     qh_setdel (neighbor->ridges, ridge);
2117     qh_setfree(&(ridge->vertices));
2118     qh_memfree_(ridge, sizeof(ridgeT), freelistp);
2119     numold++;
2120     }else {
2121     qh_setappend (&newfacet->ridges, ridge);
2122     numold++;
2123     }
2124     }
2125     if (same->ridges)
2126     qh_settruncate (same->ridges, 0);
2127     if (!same->simplicial)
2128     continue;
2129     FOREACHneighbor_i_(same) { /* note: !newfact->simplicial */
2130     if (neighbor->visitid != samevisitid && neighbor->simplicial) {
2131     ridge= qh_newridge();
2132     ridge->vertices= qh_setnew_delnthsorted (same->vertices, qh hull_dim,
2133     neighbor_i, 0);
2134     toporient= same->toporient ^ (neighbor_i & 0x1);
2135     if (toporient) {
2136     ridge->top= newfacet;
2137     ridge->bottom= neighbor;
2138     }else {
2139     ridge->top= neighbor;
2140     ridge->bottom= newfacet;
2141     }
2142     qh_setappend(&(newfacet->ridges), ridge);
2143     qh_setappend(&(neighbor->ridges), ridge);
2144     numnew++;
2145     }
2146     }
2147     }
2148    
2149     trace2((qh ferr, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
2150     numold, numnew));
2151     } /* mergecycle_ridges */
2152    
2153     /*-<a href="qh-merge.htm#TOC"
2154     >-------------------------------</a><a name="mergecycle_vneighbors">-</a>
2155    
2156     qh_mergecycle_vneighbors( samecycle, newfacet )
2157     create vertex neighbors for newfacet from vertices of facets in samecycle
2158     samecycle marked with visitid == qh.visit_id - 1
2159    
2160     returns:
2161     newfacet vertices with updated neighbors
2162     marks newfacet with qh.visit_id-1
2163     deletes vertices that are merged away
2164     sets delridge on all vertices (faster here than in mergecycle_ridges)
2165    
2166     see:
2167     qh_mergevertex_neighbors()
2168    
2169     design:
2170     for each vertex of samecycle facet
2171     set vertex->delridge
2172     delete samecycle facets from vertex neighbors
2173     append newfacet to vertex neighbors
2174     if vertex only in newfacet
2175     delete it from newfacet
2176     add it to qh.del_vertices for later deletion
2177     */
2178     void qh_mergecycle_vneighbors (facetT *samecycle, facetT *newfacet) {
2179     facetT *neighbor, **neighborp;
2180     unsigned int mergeid;
2181     vertexT *vertex, **vertexp, *apex;
2182     setT *vertices;
2183    
2184     trace4((qh ferr, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
2185     mergeid= qh visit_id - 1;
2186     newfacet->visitid= mergeid;
2187     vertices= qh_basevertices (samecycle); /* temp */
2188     apex= SETfirstt_(samecycle->vertices, vertexT);
2189     qh_setappend (&vertices, apex);
2190     FOREACHvertex_(vertices) {
2191     vertex->delridge= True;
2192     FOREACHneighbor_(vertex) {
2193     if (neighbor->visitid == mergeid)
2194     SETref_(neighbor)= NULL;
2195     }
2196     qh_setcompact (vertex->neighbors);
2197     qh_setappend (&vertex->neighbors, newfacet);
2198     if (!SETsecond_(vertex->neighbors)) {
2199     zinc_(Zcyclevertex);
2200     trace2((qh ferr, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
2201     vertex->id, samecycle->id, newfacet->id));
2202     qh_setdelsorted (newfacet->vertices, vertex);
2203     vertex->deleted= True;
2204     qh_setappend (&qh del_vertices, vertex);
2205     }
2206     }
2207     qh_settempfree (&vertices);
2208     trace3((qh ferr, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
2209     samecycle->id, newfacet->id));
2210     } /* mergecycle_vneighbors */
2211    
2212     /*-<a href="qh-merge.htm#TOC"
2213     >-------------------------------</a><a name="mergefacet">-</a>
2214    
2215     qh_mergefacet( facet1, facet2, mindist, maxdist, mergeapex )
2216     merges facet1 into facet2
2217     mergeapex==qh_MERGEapex if merging new facet into coplanar horizon
2218    
2219     returns:
2220     qh.max_outside and qh.min_vertex updated
2221     initializes vertex neighbors on first merge
2222    
2223     returns:
2224     facet2 contains facet1's vertices, neighbors, and ridges
2225     facet2 moved to end of qh.facet_list
2226     makes facet2 a newfacet
2227     sets facet2->newmerge set
2228     clears facet2->center (unless merging into a large facet)
2229     clears facet2->tested and ridge->tested for facet1
2230    
2231     facet1 prepended to visible_list for later deletion and partitioning
2232     facet1->f.replace == facet2
2233    
2234     adds neighboring facets to facet_mergeset if redundant or degenerate
2235    
2236     notes:
2237     mindist/maxdist may be NULL
2238     traces merge if fmax_(maxdist,-mindist) > TRACEdist
2239    
2240     see:
2241     qh_mergecycle()
2242    
2243     design:
2244     trace merge and check for degenerate simplex
2245     make ridges for both facets
2246     update qh.max_outside, qh.max_vertex, qh.min_vertex
2247     update facet2->maxoutside and keepcentrum
2248     update facet2->nummerge
2249     update tested flags for facet2
2250     if facet1 is simplicial
2251     merge facet1 into facet2
2252     else
2253     merge facet1's neighbors into facet2
2254     merge facet1's ridges into facet2
2255     merge facet1's vertices into facet2
2256     merge facet1's vertex neighbors into facet2
2257     add facet2's vertices to qh.new_vertexlist
2258     unless qh_MERGEapex
2259     test facet2 for degenerate or redundant neighbors
2260     move facet1 to qh.visible_list for later deletion
2261     move facet2 to end of qh.newfacet_list
2262     */
2263     void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) {
2264     boolT traceonce= False;
2265     vertexT *vertex, **vertexp;
2266     int tracerestore=0, nummerge;
2267    
2268     if (facet1->tricoplanar || facet2->tricoplanar) {
2269     if (!qh TRInormals) {
2270     fprintf (qh ferr, "qh_mergefacet: does not work for tricoplanar facets. Use option 'Q11'\n");
2271     qh_errexit2 (qh_ERRqhull, facet1, facet2);
2272     }
2273     if (facet2->tricoplanar) {
2274     facet2->tricoplanar= False;
2275     facet2->keepcentrum= False;
2276     }
2277     }
2278     zzinc_(Ztotmerge);
2279     if (qh REPORTfreq2 && qh POSTmerging) {
2280     if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
2281     qh_tracemerging();
2282     }
2283     #ifndef qh_NOtrace
2284     if (qh build_cnt >= qh RERUN) {
2285     if (mindist && (-*mindist > qh TRACEdist || *maxdist > qh TRACEdist)) {
2286     tracerestore= 0;
2287     qh IStracing= qh TRACElevel;
2288     traceonce= True;
2289     fprintf (qh ferr, "qh_mergefacet: ========= trace wide merge #%d (%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge),
2290     fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh furthest_id);
2291     }else if (facet1 == qh tracefacet || facet2 == qh tracefacet) {
2292     tracerestore= qh IStracing;
2293     qh IStracing= 4;
2294     traceonce= True;
2295     fprintf (qh ferr, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n",
2296     zzval_(Ztotmerge), qh tracefacet_id, qh furthest_id);
2297     }
2298     }
2299     if (qh IStracing >= 2) {
2300     realT mergemin= -2;
2301     realT mergemax= -2;
2302    
2303     if (mindist) {
2304     mergemin= *mindist;
2305     mergemax= *maxdist;
2306     }
2307     fprintf (qh ferr, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n",
2308     zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax);
2309     }
2310     #endif /* !qh_NOtrace */
2311     if (facet1 == facet2 || facet1->visible || facet2->visible) {
2312     fprintf (qh ferr, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n",
2313     facet1->id, facet2->id);
2314     qh_errexit2 (qh_ERRqhull, facet1, facet2);
2315     }
2316     if (qh num_facets - qh num_visible <= qh hull_dim + 1) {
2317     fprintf(qh ferr, "\n\
2318     qhull precision error: Only %d facets remain. Can not merge another\n\
2319     pair. The input is too degenerate or the convexity constraints are\n\
2320     too strong.\n", qh hull_dim+1);
2321     if (qh hull_dim >= 5 && !qh MERGEexact)
2322     fprintf(qh ferr, "Option 'Qx' may avoid this problem.\n");
2323     qh_errexit(qh_ERRinput, NULL, NULL);
2324     }
2325     if (!qh VERTEXneighbors)
2326     qh_vertexneighbors();
2327     qh_makeridges(facet1);
2328     qh_makeridges(facet2);
2329     if (qh IStracing >=4)
2330     qh_errprint ("MERGING", facet1, facet2, NULL, NULL);
2331     if (mindist) {
2332     maximize_(qh max_outside, *maxdist);
2333     maximize_(qh max_vertex, *maxdist);
2334     #if qh_MAXoutside
2335     maximize_(facet2->maxoutside, *maxdist);
2336     #endif
2337     minimize_(qh min_vertex, *mindist);
2338     if (!facet2->keepcentrum
2339     && (*maxdist > qh WIDEfacet || *mindist < -qh WIDEfacet)) {
2340     facet2->keepcentrum= True;
2341     zinc_(Zwidefacet);
2342     }
2343     }
2344     nummerge= facet1->nummerge + facet2->nummerge + 1;
2345     if (nummerge >= qh_MAXnummerge)
2346     facet2->nummerge= qh_MAXnummerge;
2347     else
2348     facet2->nummerge= nummerge;
2349     facet2->newmerge= True;
2350     facet2->dupridge= False;
2351     qh_updatetested (facet1, facet2);
2352     if (qh hull_dim > 2 && qh_setsize (facet1->vertices) == qh hull_dim)
2353     qh_mergesimplex (facet1, facet2, mergeapex);
2354     else {
2355     qh vertex_visit++;
2356     FOREACHvertex_(facet2->vertices)
2357     vertex->visitid= qh vertex_visit;
2358     if (qh hull_dim == 2)
2359     qh_mergefacet2d(facet1, facet2);
2360     else {
2361     qh_mergeneighbors(facet1, facet2);
2362     qh_mergevertices(facet1->vertices, &facet2->vertices);
2363     }
2364     qh_mergeridges(facet1, facet2);
2365     qh_mergevertex_neighbors(facet1, facet2);
2366     if (!facet2->newfacet)
2367     qh_newvertices (facet2->vertices);
2368     }
2369     if (!mergeapex)
2370     qh_degen_redundant_neighbors (facet2, facet1);
2371     if (facet2->coplanar || !facet2->newfacet) {
2372     zinc_(Zmergeintohorizon);
2373     }else if (!facet1->newfacet && facet2->newfacet) {
2374     zinc_(Zmergehorizon);
2375     }else {
2376     zinc_(Zmergenew);
2377     }
2378     qh_willdelete (facet1, facet2);
2379     qh_removefacet(facet2); /* append as a newfacet to end of qh facet_list */
2380     qh_appendfacet(facet2);
2381     facet2->newfacet= True;
2382     facet2->tested= False;
2383     qh_tracemerge (facet1, facet2);
2384     if (traceonce) {
2385     fprintf (qh ferr, "qh_mergefacet: end of wide tracing\n");
2386     qh IStracing= tracerestore;
2387     }
2388     } /* mergefacet */
2389    
2390    
2391     /*-<a href="qh-merge.htm#TOC"
2392     >-------------------------------</a><a name="mergefacet2d">-</a>
2393    
2394     qh_mergefacet2d( facet1, facet2 )
2395     in 2d, merges neighbors and vertices of facet1 into facet2
2396    
2397     returns:
2398     build ridges for neighbors if necessary
2399     facet2 looks like a simplicial facet except for centrum, ridges
2400     neighbors are opposite the corresponding vertex
2401     maintains orientation of facet2
2402    
2403     notes:
2404     qh_mergefacet() retains non-simplicial structures
2405     they are not needed in 2d, but later routines may use them
2406     preserves qh.vertex_visit for qh_mergevertex_neighbors()
2407    
2408     design:
2409     get vertices and neighbors
2410     determine new vertices and neighbors
2411     set new vertices and neighbors and adjust orientation
2412     make ridges for new neighbor if needed
2413     */
2414     void qh_mergefacet2d (facetT *facet1, facetT *facet2) {
2415     vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
2416     facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;
2417    
2418     vertex1A= SETfirstt_(facet1->vertices, vertexT);
2419     vertex1B= SETsecondt_(facet1->vertices, vertexT);
2420     vertex2A= SETfirstt_(facet2->vertices, vertexT);
2421     vertex2B= SETsecondt_(facet2->vertices, vertexT);
2422     neighbor1A= SETfirstt_(facet1->neighbors, facetT);
2423     neighbor1B= SETsecondt_(facet1->neighbors, facetT);
2424     neighbor2A= SETfirstt_(facet2->neighbors, facetT);
2425     neighbor2B= SETsecondt_(facet2->neighbors, facetT);
2426     if (vertex1A == vertex2A) {
2427     vertexA= vertex1B;
2428     vertexB= vertex2B;
2429     neighborA= neighbor2A;
2430     neighborB= neighbor1A;
2431     }else if (vertex1A == vertex2B) {
2432     vertexA= vertex1B;
2433     vertexB= vertex2A;
2434     neighborA= neighbor2B;
2435     neighborB= neighbor1A;
2436     }else if (vertex1B == vertex2A) {
2437     vertexA= vertex1A;
2438     vertexB= vertex2B;
2439     neighborA= neighbor2A;
2440     neighborB= neighbor1B;
2441     }else { /* 1B == 2B */
2442     vertexA= vertex1A;
2443     vertexB= vertex2A;
2444     neighborA= neighbor2B;
2445     neighborB= neighbor1B;
2446     }
2447     /* vertexB always from facet2, neighborB always from facet1 */
2448     if (vertexA->id > vertexB->id) {
2449     SETfirst_(facet2->vertices)= vertexA;
2450     SETsecond_(facet2->vertices)= vertexB;
2451     if (vertexB == vertex2A)
2452     facet2->toporient= !facet2->toporient;
2453     SETfirst_(facet2->neighbors)= neighborA;
2454     SETsecond_(facet2->neighbors)= neighborB;
2455     }else {
2456     SETfirst_(facet2->vertices)= vertexB;
2457     SETsecond_(facet2->vertices)= vertexA;
2458     if (vertexB == vertex2B)
2459     facet2->toporient= !facet2->toporient;
2460     SETfirst_(facet2->neighbors)= neighborB;
2461     SETsecond_(facet2->neighbors)= neighborA;
2462     }
2463     qh_makeridges (neighborB);
2464     qh_setreplace(neighborB->neighbors, facet1, facet2);
2465     trace4((qh ferr, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
2466     vertexA->id, neighborB->id, facet1->id, facet2->id));
2467     } /* mergefacet2d */
2468    
2469    
2470     /*-<a href="qh-merge.htm#TOC"
2471     >-------------------------------</a><a name="mergeneighbors">-</a>
2472    
2473     qh_mergeneighbors( facet1, facet2 )
2474     merges the neighbors of facet1 into facet2
2475    
2476     see:
2477     qh_mergecycle_neighbors()
2478    
2479     design:
2480     for each neighbor of facet1
2481     if neighbor is also a neighbor of facet2
2482     if neighbor is simpilicial
2483     make ridges for later deletion as a degenerate facet
2484     update its neighbor set
2485     else
2486     move the neighbor relation to facet2
2487     remove the neighbor relation for facet1 and facet2
2488     */
2489     void qh_mergeneighbors(facetT *facet1, facetT *facet2) {
2490     facetT *neighbor, **neighborp;
2491    
2492     trace4((qh ferr, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
2493     facet1->id, facet2->id));
2494     qh visit_id++;
2495     FOREACHneighbor_(facet2) {
2496     neighbor->visitid= qh visit_id;
2497     }
2498     FOREACHneighbor_(facet1) {
2499     if (neighbor->visitid == qh visit_id) {
2500     if (neighbor->simplicial) /* is degen, needs ridges */
2501     qh_makeridges (neighbor);
2502     if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
2503     qh_setdel (neighbor->neighbors, facet1);
2504     else {
2505     qh_setdel(neighbor->neighbors, facet2);
2506     qh_setreplace(neighbor->neighbors, facet1, facet2);
2507     }
2508     }else if (neighbor != facet2) {
2509     qh_setappend(&(facet2->neighbors), neighbor);
2510     qh_setreplace(neighbor->neighbors, facet1, facet2);
2511     }
2512     }
2513     qh_setdel(facet1->neighbors, facet2); /* here for makeridges */
2514     qh_setdel(facet2->neighbors, facet1);
2515     } /* mergeneighbors */
2516    
2517    
2518     /*-<a href="qh-merge.htm#TOC"
2519     >-------------------------------</a><a name="mergeridges">-</a>
2520    
2521     qh_mergeridges( facet1, facet2 )
2522     merges the ridge set of facet1 into facet2
2523    
2524     returns:
2525     may delete all ridges for a vertex
2526     sets vertex->delridge on deleted ridges
2527    
2528     see:
2529     qh_mergecycle_ridges()
2530    
2531     design:
2532     delete ridges between facet1 and facet2
2533     mark (delridge) vertices on these ridges for later testing
2534     for each remaining ridge
2535     rename facet1 to facet2
2536     */
2537     void qh_mergeridges(facetT *facet1, facetT *facet2) {
2538     ridgeT *ridge, **ridgep;
2539     vertexT *vertex, **vertexp;
2540    
2541     trace4((qh ferr, "qh_mergeridges: merge ridges of f%d and f%d\n",
2542     facet1->id, facet2->id));
2543     FOREACHridge_(facet2->ridges) {
2544     if ((ridge->top == facet1) || (ridge->bottom == facet1)) {
2545     FOREACHvertex_(ridge->vertices)
2546     vertex->delridge= True;
2547     qh_delridge(ridge); /* expensive in high-d, could rebuild */
2548     ridgep--; /*repeat*/
2549     }
2550     }
2551     FOREACHridge_(facet1->ridges) {
2552     if (ridge->top == facet1)
2553     ridge->top= facet2;
2554     else
2555     ridge->bottom= facet2;
2556     qh_setappend(&(facet2->ridges), ridge);
2557     }
2558     } /* mergeridges */
2559    
2560    
2561     /*-<a href="qh-merge.htm#TOC"
2562     >-------------------------------</a><a name="mergesimplex">-</a>
2563    
2564     qh_mergesimplex( facet1, facet2, mergeapex )
2565     merge simplicial facet1 into facet2
2566     mergeapex==qh_MERGEapex if merging samecycle into horizon facet
2567     vertex id is latest (most recently created)
2568     facet1 may be contained in facet2
2569     ridges exist for both facets
2570    
2571     returns:
2572     facet2 with updated vertices, ridges, neighbors
2573     updated neighbors for facet1's vertices
2574     facet1 not deleted
2575     sets vertex->delridge on deleted ridges
2576    
2577     notes:
2578     special case code since this is the most common merge
2579     called from qh_mergefacet()
2580    
2581     design:
2582     if qh_MERGEapex
2583     add vertices of facet2 to qh.new_vertexlist if necessary
2584     add apex to facet2
2585     else
2586     for each ridge between facet1 and facet2
2587     set vertex->delridge
2588     determine the apex for facet1 (i.e., vertex to be merged)
2589     unless apex already in facet2
2590     insert apex into vertices for facet2
2591     add vertices of facet2 to qh.new_vertexlist if necessary
2592     add apex to qh.new_vertexlist if necessary
2593     for each vertex of facet1
2594     if apex
2595     rename facet1 to facet2 in its vertex neighbors
2596     else
2597     delete facet1 from vertex neighors
2598     if only in facet2
2599     add vertex to qh.del_vertices for later deletion
2600     for each ridge of facet1
2601     delete ridges between facet1 and facet2
2602     append other ridges to facet2 after renaming facet to facet2
2603     */
2604     void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) {
2605     vertexT *vertex, **vertexp, *apex;
2606     ridgeT *ridge, **ridgep;
2607     boolT issubset= False;
2608     int vertex_i= -1, vertex_n;
2609     facetT *neighbor, **neighborp, *otherfacet;
2610    
2611     if (mergeapex) {
2612     if (!facet2->newfacet)
2613     qh_newvertices (facet2->vertices); /* apex is new */
2614     apex= SETfirstt_(facet1->vertices, vertexT);
2615     if (SETfirstt_(facet2->vertices, vertexT) != apex)
2616     qh_setaddnth (&facet2->vertices, 0, apex); /* apex has last id */
2617     else
2618     issubset= True;
2619     }else {
2620     zinc_(Zmergesimplex);
2621     FOREACHvertex_(facet1->vertices)
2622     vertex->seen= False;
2623     FOREACHridge_(facet1->ridges) {
2624     if (otherfacet_(ridge, facet1) == facet2) {
2625     FOREACHvertex_(ridge->vertices) {
2626     vertex->seen= True;
2627     vertex->delridge= True;
2628     }
2629     break;
2630     }
2631     }
2632     FOREACHvertex_(facet1->vertices) {
2633     if (!vertex->seen)
2634     break; /* must occur */
2635     }
2636     apex= vertex;
2637     trace4((qh ferr, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
2638     apex->id, facet1->id, facet2->id));
2639     FOREACHvertex_i_(facet2->vertices) {
2640     if (vertex->id < apex->id) {
2641     break;
2642     }else if (vertex->id == apex->id) {
2643     issubset= True;
2644     break;
2645     }
2646     }
2647     if (!issubset)
2648     qh_setaddnth (&facet2->vertices, vertex_i, apex);
2649     if (!facet2->newfacet)
2650     qh_newvertices (facet2->vertices);
2651     else if (!apex->newlist) {
2652     qh_removevertex (apex);
2653     qh_appendvertex (apex);
2654     }
2655     }
2656     trace4((qh ferr, "qh_mergesimplex: update vertex neighbors of f%d\n",
2657     facet1->id));
2658     FOREACHvertex_(facet1->vertices) {
2659     if (vertex == apex && !issubset)
2660     qh_setreplace (vertex->neighbors, facet1, facet2);
2661     else {
2662     qh_setdel (vertex->neighbors, facet1);
2663     if (!SETsecond_(vertex->neighbors))
2664     qh_mergevertex_del (vertex, facet1, facet2);
2665     }
2666     }
2667     trace4((qh ferr, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
2668     facet1->id, facet2->id));
2669     qh visit_id++;
2670     FOREACHneighbor_(facet2)
2671     neighbor->visitid= qh visit_id;
2672     FOREACHridge_(facet1->ridges) {
2673     otherfacet= otherfacet_(ridge, facet1);
2674     if (otherfacet == facet2) {
2675     qh_setdel (facet2->ridges, ridge);
2676     qh_setfree(&(ridge->vertices));
2677     qh_memfree (ridge, sizeof(ridgeT));
2678     qh_setdel (facet2->neighbors, facet1);
2679     }else {
2680     qh_setappend (&facet2->ridges, ridge);
2681     if (otherfacet->visitid != qh visit_id) {
2682     qh_setappend (&facet2->neighbors, otherfacet);
2683     qh_setreplace (otherfacet->neighbors, facet1, facet2);
2684     otherfacet->visitid= qh visit_id;
2685     }else {
2686     if (otherfacet->simplicial) /* is degen, needs ridges */
2687     qh_makeridges (otherfacet);
2688     if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)
2689     qh_setdel (otherfacet->neighbors, facet1);
2690     else { /*keep newfacet->neighbors->horizon*/
2691     qh_setdel(otherfacet->neighbors, facet2);
2692     qh_setreplace(otherfacet->neighbors, facet1, facet2);
2693     }
2694     }
2695     if (ridge->top == facet1) /* wait until after qh_makeridges */
2696     ridge->top= facet2;
2697     else
2698     ridge->bottom= facet2;
2699     }
2700     }
2701     SETfirst_(facet1->ridges)= NULL; /* it will be deleted */
2702     trace3((qh ferr, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",
2703     facet1->id, getid_(apex), facet2->id));
2704     } /* mergesimplex */
2705    
2706     /*-<a href="qh-merge.htm#TOC"
2707     >-------------------------------</a><a name="mergevertex_del">-</a>
2708    
2709     qh_mergevertex_del( vertex, facet1, facet2 )
2710     delete a vertex because of merging facet1 into facet2
2711    
2712     returns:
2713     deletes vertex from facet2
2714     adds vertex to qh.del_vertices for later deletion
2715     */
2716     void qh_mergevertex_del (vertexT *vertex, facetT *facet1, facetT *facet2) {
2717    
2718     zinc_(Zmergevertex);
2719     trace2((qh ferr, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
2720     vertex->id, facet1->id, facet2->id));
2721     qh_setdelsorted (facet2->vertices, vertex);
2722     vertex->deleted= True;
2723     qh_setappend (&qh del_vertices, vertex);
2724     } /* mergevertex_del */
2725    
2726     /*-<a href="qh-merge.htm#TOC"
2727     >-------------------------------</a><a name="mergevertex_neighbors">-</a>
2728    
2729     qh_mergevertex_neighbors( facet1, facet2 )
2730     merge the vertex neighbors of facet1 to facet2
2731    
2732     returns:
2733     if vertex is current qh.vertex_visit
2734     deletes facet1 from vertex->neighbors
2735     else
2736     renames facet1 to facet2 in vertex->neighbors
2737     deletes vertices if only one neighbor
2738    
2739     notes:
2740     assumes vertex neighbor sets are good
2741     */
2742     void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2) {
2743     vertexT *vertex, **vertexp;
2744    
2745     trace4((qh ferr, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n",
2746     facet1->id, facet2->id));
2747     if (qh tracevertex) {
2748     fprintf (qh ferr, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n",
2749     facet1->id, facet2->id, qh furthest_id, qh tracevertex->neighbors->e[0].p);
2750     qh_errprint ("TRACE", NULL, NULL, NULL, qh tracevertex);
2751     }
2752     FOREACHvertex_(facet1->vertices) {
2753     if (vertex->visitid != qh vertex_visit)
2754     qh_setreplace(vertex->neighbors, facet1, facet2);
2755     else {
2756     qh_setdel(vertex->neighbors, facet1);
2757     if (!SETsecond_(vertex->neighbors))
2758     qh_mergevertex_del (vertex, facet1, facet2);
2759     }
2760     }
2761     if (qh tracevertex)
2762     qh_errprint ("TRACE", NULL, NULL, NULL, qh tracevertex);
2763     } /* mergevertex_neighbors */
2764    
2765    
2766     /*-<a href="qh-merge.htm#TOC"
2767     >-------------------------------</a><a name="mergevertices">-</a>
2768    
2769     qh_mergevertices( vertices1, vertices2 )
2770     merges the vertex set of facet1 into facet2
2771    
2772     returns:
2773     replaces vertices2 with merged set
2774     preserves vertex_visit for qh_mergevertex_neighbors
2775     updates qh.newvertex_list
2776    
2777     design:
2778     create a merged set of both vertices (in inverse id order)
2779     */
2780     void qh_mergevertices(setT *vertices1, setT **vertices2) {
2781     int newsize= qh_setsize(vertices1)+qh_setsize(*vertices2) - qh hull_dim + 1;
2782     setT *mergedvertices;
2783     vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);
2784    
2785     mergedvertices= qh_settemp (newsize);
2786     FOREACHvertex_(vertices1) {
2787     if (!*vertex2 || vertex->id > (*vertex2)->id)
2788     qh_setappend (&mergedvertices, vertex);
2789     else {
2790     while (*vertex2 && (*vertex2)->id > vertex->id)
2791     qh_setappend (&mergedvertices, *vertex2++);
2792     if (!*vertex2 || (*vertex2)->id < vertex->id)
2793     qh_setappend (&mergedvertices, vertex);
2794     else
2795     qh_setappend (&mergedvertices, *vertex2++);
2796     }
2797     }
2798     while (*vertex2)
2799     qh_setappend (&mergedvertices, *vertex2++);
2800     if (newsize < qh_setsize (mergedvertices)) {
2801     fprintf (qh ferr, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
2802     qh_errexit (qh_ERRqhull, NULL, NULL);
2803     }
2804     qh_setfree(vertices2);
2805     *vertices2= mergedvertices;
2806     qh_settemppop ();
2807     } /* mergevertices */
2808    
2809    
2810     /*-<a href="qh-merge.htm#TOC"
2811     >-------------------------------</a><a name="neighbor_intersections">-</a>
2812    
2813     qh_neighbor_intersections( vertex )
2814     return intersection of all vertices in vertex->neighbors except for vertex
2815    
2816     returns:
2817     returns temporary set of vertices
2818     does not include vertex
2819     NULL if a neighbor is simplicial
2820     NULL if empty set
2821    
2822     notes:
2823     used for renaming vertices
2824    
2825     design:
2826     initialize the intersection set with vertices of the first two neighbors
2827     delete vertex from the intersection
2828     for each remaining neighbor
2829     intersect its vertex set with the intersection set
2830     return NULL if empty
2831     return the intersection set
2832     */
2833     setT *qh_neighbor_intersections (vertexT *vertex) {
2834     facetT *neighbor, **neighborp, *neighborA, *neighborB;
2835     setT *intersect;
2836     int neighbor_i, neighbor_n;
2837    
2838     FOREACHneighbor_(vertex) {
2839     if (neighbor->simplicial)
2840     return NULL;
2841     }
2842     neighborA= SETfirstt_(vertex->neighbors, facetT);
2843     neighborB= SETsecondt_(vertex->neighbors, facetT);
2844     zinc_(Zintersectnum);
2845     if (!neighborA)
2846     return NULL;
2847     if (!neighborB)
2848     intersect= qh_setcopy (neighborA->vertices, 0);
2849     else
2850     intersect= qh_vertexintersect_new (neighborA->vertices, neighborB->vertices);
2851     qh_settemppush (intersect);
2852     qh_setdelsorted (intersect, vertex);
2853     FOREACHneighbor_i_(vertex) {
2854     if (neighbor_i >= 2) {
2855     zinc_(Zintersectnum);
2856     qh_vertexintersect (&intersect, neighbor->vertices);
2857     if (!SETfirst_(intersect)) {
2858     zinc_(Zintersectfail);
2859     qh_settempfree (&intersect);
2860     return NULL;
2861     }
2862     }
2863     }
2864     trace3((qh ferr, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
2865     qh_setsize (intersect), vertex->id));
2866     return intersect;
2867     } /* neighbor_intersections */
2868    
2869     /*-<a href="qh-merge.htm#TOC"
2870     >-------------------------------</a><a name="newvertices">-</a>
2871    
2872     qh_newvertices( vertices )
2873     add vertices to end of qh.vertex_list (marks as new vertices)
2874    
2875     returns:
2876     vertices on qh.newvertex_list
2877     vertex->newlist set
2878     */
2879     void qh_newvertices (setT *vertices) {
2880     vertexT *vertex, **vertexp;
2881    
2882     FOREACHvertex_(vertices) {
2883     if (!vertex->newlist) {
2884     qh_removevertex (vertex);
2885     qh_appendvertex (vertex);
2886     }
2887     }
2888     } /* newvertices */
2889    
2890     /*-<a href="qh-merge.htm#TOC"
2891     >-------------------------------</a><a name="reducevertices">-</a>
2892    
2893     qh_reducevertices()
2894     reduce extra vertices, shared vertices, and redundant vertices
2895     facet->newmerge is set if merged since last call
2896     if !qh.MERGEvertices, only removes extra vertices
2897    
2898     returns:
2899     True if also merged degen_redundant facets
2900     vertices are renamed if possible
2901     clears facet->newmerge and vertex->delridge
2902    
2903     notes:
2904     ignored if 2-d
2905    
2906     design:
2907     merge any degenerate or redundant facets
2908     for each newly merged facet
2909     remove extra vertices
2910     if qh.MERGEvertices
2911     for each newly merged facet
2912     for each vertex
2913     if vertex was on a deleted ridge
2914     rename vertex if it is shared
2915     remove delridge flag from new vertices
2916     */
2917     boolT qh_reducevertices (void) {
2918     int numshare=0, numrename= 0;
2919     boolT degenredun= False;
2920     facetT *newfacet;
2921     vertexT *vertex, **vertexp;
2922    
2923     if (qh hull_dim == 2)
2924     return False;
2925     if (qh_merge_degenredundant())
2926     degenredun= True;
2927     LABELrestart:
2928     FORALLnew_facets {
2929     if (newfacet->newmerge) {
2930     if (!qh MERGEvertices)
2931     newfacet->newmerge= False;
2932     qh_remove_extravertices (newfacet);
2933     }
2934     }
2935     if (!qh MERGEvertices)
2936     return False;
2937     FORALLnew_facets {
2938     if (newfacet->newmerge) {
2939     newfacet->newmerge= False;
2940     FOREACHvertex_(newfacet->vertices) {
2941     if (vertex->delridge) {
2942     if (qh_rename_sharedvertex (vertex, newfacet)) {
2943     numshare++;
2944     vertexp--; /* repeat since deleted vertex */
2945     }
2946     }
2947     }
2948     }
2949     }
2950     FORALLvertex_(qh newvertex_list) {
2951     if (vertex->delridge && !vertex->deleted) {
2952     vertex->delridge= False;
2953     if (qh hull_dim >= 4 && qh_redundant_vertex (vertex)) {
2954     numrename++;
2955     if (qh_merge_degenredundant()) {
2956     degenredun= True;
2957     goto LABELrestart;
2958     }
2959     }
2960     }
2961     }
2962     trace1((qh ferr, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
2963     numshare, numrename, degenredun));
2964     return degenredun;
2965     } /* reducevertices */
2966    
2967     /*-<a href="qh-merge.htm#TOC"
2968     >-------------------------------</a><a name="redundant_vertex">-</a>
2969    
2970     qh_redundant_vertex( vertex )
2971     detect and rename a redundant vertex
2972     vertices have full vertex->neighbors
2973    
2974     returns:
2975     returns true if find a redundant vertex
2976     deletes vertex (vertex->deleted)
2977    
2978     notes:
2979     only needed if vertex->delridge and hull_dim >= 4
2980     may add degenerate facets to qh.facet_mergeset
2981     doesn't change vertex->neighbors or create redundant facets
2982    
2983     design:
2984     intersect vertices of all facet neighbors of vertex
2985     determine ridges for these vertices
2986     if find a new vertex for vertex amoung these ridges and vertices
2987     rename vertex to the new vertex
2988     */
2989     vertexT *qh_redundant_vertex (vertexT *vertex) {
2990     vertexT *newvertex= NULL;
2991     setT *vertices, *ridges;
2992    
2993     trace3((qh ferr, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));
2994     if ((vertices= qh_neighbor_intersections (vertex))) {
2995     ridges= qh_vertexridges (vertex);
2996     if ((newvertex= qh_find_newvertex (vertex, vertices, ridges)))
2997     qh_renamevertex (vertex, newvertex, ridges, NULL, NULL);
2998     qh_settempfree (&ridges);
2999     qh_settempfree (&vertices);
3000     }
3001     return newvertex;
3002     } /* redundant_vertex */
3003    
3004     /*-<a href="qh-merge.htm#TOC"
3005     >-------------------------------</a><a name="remove_extravertices">-</a>
3006    
3007     qh_remove_extravertices( facet )
3008     remove extra vertices from non-simplicial facets
3009    
3010     returns:
3011     returns True if it finds them
3012    
3013     design:
3014     for each vertex in facet
3015     if vertex not in a ridge (i.e., no longer used)
3016     delete vertex from facet
3017     delete facet from vertice's neighbors
3018     unless vertex in another facet
3019     add vertex to qh.del_vertices for later deletion
3020     */
3021     boolT qh_remove_extravertices (facetT *facet) {
3022     ridgeT *ridge, **ridgep;
3023     vertexT *vertex, **vertexp;
3024     boolT foundrem= False;
3025    
3026     trace4((qh ferr, "qh_remove_extravertices: test f%d for extra vertices\n",
3027     facet->id));
3028     FOREACHvertex_(facet->vertices)
3029     vertex->seen= False;
3030     FOREACHridge_(facet->ridges) {
3031     FOREACHvertex_(ridge->vertices)
3032     vertex->seen= True;
3033     }
3034     FOREACHvertex_(facet->vertices) {
3035     if (!vertex->seen) {
3036     foundrem= True;
3037     zinc_(Zremvertex);
3038     qh_setdelsorted (facet->vertices, vertex);
3039     qh_setdel (vertex->neighbors, facet);
3040     if (!qh_setsize (vertex->neighbors)) {
3041     vertex->deleted= True;
3042     qh_setappend (&qh del_vertices, vertex);
3043     zinc_(Zremvertexdel);
3044     trace2((qh ferr, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
3045     }else
3046     trace3((qh ferr, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
3047     vertexp--; /*repeat*/
3048     }
3049     }
3050     return foundrem;
3051     } /* remove_extravertices */
3052    
3053     /*-<a href="qh-merge.htm#TOC"
3054     >-------------------------------</a><a name="rename_sharedvertex">-</a>
3055    
3056     qh_rename_sharedvertex( vertex, facet )
3057     detect and rename if shared vertex in facet
3058     vertices have full ->neighbors
3059    
3060     returns:
3061     newvertex or NULL
3062     the vertex may still exist in other facets (i.e., a neighbor was pinched)
3063     does not change facet->neighbors
3064     updates vertex->neighbors
3065    
3066     notes:
3067     a shared vertex for a facet is only in ridges to one neighbor
3068     this may undo a pinched facet
3069    
3070     it does not catch pinches involving multiple facets. These appear
3071     to be difficult to detect, since an exhaustive search is too expensive.
3072    
3073     design:
3074     if vertex only has two neighbors
3075     determine the ridges that contain the vertex
3076     determine the vertices shared by both neighbors
3077     if can find a new vertex in this set
3078     rename the vertex to the new vertex
3079     */
3080     vertexT *qh_rename_sharedvertex (vertexT *vertex, facetT *facet) {
3081     facetT *neighbor, **neighborp, *neighborA= NULL;
3082     setT *vertices, *ridges;
3083     vertexT *newvertex;
3084    
3085     if (qh_setsize (vertex->neighbors) == 2) {
3086     neighborA= SETfirstt_(vertex->neighbors, facetT);
3087     if (neighborA == facet)
3088     neighborA= SETsecondt_(vertex->neighbors, facetT);
3089     }else if (qh hull_dim == 3)
3090     return NULL;
3091     else {
3092     qh visit_id++;
3093     FOREACHneighbor_(facet)
3094     neighbor->visitid= qh visit_id;
3095     FOREACHneighbor_(vertex) {
3096     if (neighbor->visitid == qh visit_id) {
3097     if (neighborA)
3098     return NULL;
3099     neighborA= neighbor;
3100     }
3101     }
3102     if (!neighborA) {
3103     fprintf (qh ferr, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
3104     vertex->id, facet->id);
3105     qh_errprint ("ERRONEOUS", facet, NULL, NULL, vertex);
3106     qh_errexit (qh_ERRqhull, NULL, NULL);
3107     }
3108     }
3109     /* the vertex is shared by facet and neighborA */
3110     ridges= qh_settemp (qh TEMPsize);
3111     neighborA->visitid= ++qh visit_id;
3112     qh_vertexridges_facet (vertex, facet, &ridges);
3113     trace2((qh ferr, "qh_rename_sharedvertex: p%d (v%d) is shared by f%d (%d ridges) and f%d\n",
3114     qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize (ridges), neighborA->id));
3115     zinc_(Zintersectnum);
3116     vertices= qh_vertexintersect_new (facet->vertices, neighborA->vertices);
3117     qh_setdel (vertices, vertex);
3118     qh_settemppush (vertices);
3119     if ((newvertex= qh_find_newvertex (vertex, vertices, ridges)))
3120     qh_renamevertex (vertex, newvertex, ridges, facet, neighborA);
3121     qh_settempfree (&vertices);
3122     qh_settempfree (&ridges);
3123     return newvertex;
3124     } /* rename_sharedvertex */
3125    
3126     /*-<a href="qh-merge.htm#TOC"
3127     >-------------------------------</a><a name="renameridgevertex">-</a>
3128    
3129     qh_renameridgevertex( ridge, oldvertex, newvertex )
3130     renames oldvertex as newvertex in ridge
3131    
3132     returns:
3133    
3134     design:
3135     delete oldvertex from ridge
3136     if newvertex already in ridge
3137     copy ridge->noconvex to another ridge if possible
3138     delete the ridge
3139     else
3140     insert newvertex into the ridge
3141     adjust the ridge's orientation
3142     */
3143     void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
3144     int nth= 0, oldnth;
3145     facetT *temp;
3146     vertexT *vertex, **vertexp;
3147    
3148     oldnth= qh_setindex (ridge->vertices, oldvertex);
3149     qh_setdelnthsorted (ridge->vertices, oldnth);
3150     FOREACHvertex_(ridge->vertices) {
3151     if (vertex == newvertex) {
3152     zinc_(Zdelridge);
3153     if (ridge->nonconvex) /* only one ridge has nonconvex set */
3154     qh_copynonconvex (ridge);
3155     qh_delridge (ridge);
3156     trace2((qh ferr, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n",
3157     ridge->id, oldvertex->id, newvertex->id));
3158     return;
3159     }
3160     if (vertex->id < newvertex->id)
3161     break;
3162     nth++;
3163     }
3164     qh_setaddnth(&ridge->vertices, nth, newvertex);
3165     if (abs(oldnth - nth)%2) {
3166     trace3((qh ferr, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
3167     ridge->id));
3168     temp= ridge->top;
3169     ridge->top= ridge->bottom;
3170     ridge->bottom= temp;
3171     }
3172     } /* renameridgevertex */
3173    
3174    
3175     /*-<a href="qh-merge.htm#TOC"
3176     >-------------------------------</a><a name="renamevertex">-</a>
3177    
3178     qh_renamevertex( oldvertex, newvertex, ridges, oldfacet, neighborA )
3179     renames oldvertex as newvertex in ridges
3180     gives oldfacet/neighborA if oldvertex is shared between two facets
3181    
3182     returns:
3183     oldvertex may still exist afterwards
3184    
3185    
3186     notes:
3187     can not change neighbors of newvertex (since it's a subset)
3188    
3189     design:
3190     for each ridge in ridges
3191     rename oldvertex to newvertex and delete degenerate ridges
3192     if oldfacet not defined
3193     for each neighbor of oldvertex
3194     delete oldvertex from neighbor's vertices
3195     remove extra vertices from neighbor
3196     add oldvertex to qh.del_vertices
3197     else if oldvertex only between oldfacet and neighborA
3198     delete oldvertex from oldfacet and neighborA
3199     add oldvertex to qh.del_vertices
3200     else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)
3201     delete oldvertex from oldfacet
3202     delete oldfacet from oldvertice's neighbors
3203     remove extra vertices (e.g., oldvertex) from neighborA
3204     */
3205     void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {
3206     facetT *neighbor, **neighborp;
3207     ridgeT *ridge, **ridgep;
3208     boolT istrace= False;
3209    
3210     if (qh IStracing >= 2 || oldvertex->id == qh tracevertex_id ||
3211     newvertex->id == qh tracevertex_id)
3212     istrace= True;
3213     FOREACHridge_(ridges)
3214     qh_renameridgevertex (ridge, oldvertex, newvertex);
3215     if (!oldfacet) {
3216     zinc_(Zrenameall);
3217     if (istrace)
3218     fprintf (qh ferr, "qh_renamevertex: renamed v%d to v%d in several facets\n",
3219     oldvertex->id, newvertex->id);
3220     FOREACHneighbor_(oldvertex) {
3221     qh_maydropneighbor (neighbor);
3222     qh_setdelsorted (neighbor->vertices, oldvertex);
3223     if (qh_remove_extravertices (neighbor))
3224     neighborp--; /* neighbor may be deleted */
3225     }
3226     if (!oldvertex->deleted) {
3227     oldvertex->deleted= True;
3228     qh_setappend (&qh del_vertices, oldvertex);
3229     }
3230     }else if (qh_setsize (oldvertex->neighbors) == 2) {
3231     zinc_(Zrenameshare);
3232     if (istrace)
3233     fprintf (qh ferr, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n",
3234     oldvertex->id, newvertex->id, oldfacet->id);
3235     FOREACHneighbor_(oldvertex)
3236     qh_setdelsorted (neighbor->vertices, oldvertex);
3237     oldvertex->deleted= True;
3238     qh_setappend (&qh del_vertices, oldvertex);
3239     }else {
3240     zinc_(Zrenamepinch);
3241     if (istrace || qh IStracing)
3242     fprintf (qh ferr, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n",
3243     oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);
3244     qh_setdelsorted (oldfacet->vertices, oldvertex);
3245     qh_setdel (oldvertex->neighbors, oldfacet);
3246     qh_remove_extravertices (neighborA);
3247     }
3248     } /* renamevertex */
3249    
3250    
3251     /*-<a href="qh-merge.htm#TOC"
3252     >-------------------------------</a><a name="test_appendmerge">-</a>
3253    
3254     qh_test_appendmerge( facet, neighbor )
3255     tests facet/neighbor for convexity
3256     appends to mergeset if non-convex
3257     if pre-merging,
3258     nop if qh.SKIPconvex, or qh.MERGEexact and coplanar
3259    
3260     returns:
3261     true if appends facet/neighbor to mergeset
3262     sets facet->center as needed
3263     does not change facet->seen
3264    
3265     design:
3266     if qh.cos_max is defined
3267     if the angle between facet normals is too shallow
3268     append an angle-coplanar merge to qh.mergeset
3269     return True
3270     make facet's centrum if needed
3271     if facet's centrum is above the neighbor
3272     set isconcave
3273     else
3274     if facet's centrum is not below the neighbor
3275     set iscoplanar
3276     make neighbor's centrum if needed
3277     if neighbor's centrum is above the facet
3278     set isconcave
3279     else if neighbor's centrum is not below the facet
3280     set iscoplanar
3281     if isconcave or iscoplanar
3282     get angle if needed
3283     append concave or coplanar merge to qh.mergeset
3284     */
3285     boolT qh_test_appendmerge (facetT *facet, facetT *neighbor) {
3286     realT dist, dist2= -REALmax, angle= -REALmax;
3287     boolT isconcave= False, iscoplanar= False, okangle= False;
3288    
3289     if (qh SKIPconvex && !qh POSTmerging)
3290     return False;
3291     if ((!qh MERGEexact || qh POSTmerging) && qh cos_max < REALmax/2) {
3292     angle= qh_getangle(facet->normal, neighbor->normal);
3293     zinc_(Zangletests);
3294     if (angle > qh cos_max) {
3295     zinc_(Zcoplanarangle);
3296     qh_appendmergeset(facet, neighbor, MRGanglecoplanar, &angle);
3297     trace2((qh ferr, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
3298     angle, facet->id, neighbor->id));
3299     return True;
3300     }else
3301     okangle= True;
3302     }
3303     if (!facet->center)
3304     facet->center= qh_getcentrum (facet);
3305     zzinc_(Zcentrumtests);
3306     qh_distplane(facet->center, neighbor, &dist);
3307     if (dist > qh centrum_radius)
3308     isconcave= True;
3309     else {
3310     if (dist > -qh centrum_radius)
3311     iscoplanar= True;
3312     if (!neighbor->center)
3313     neighbor->center= qh_getcentrum (neighbor);
3314     zzinc_(Zcentrumtests);
3315     qh_distplane(neighbor->center, facet, &dist2);
3316     if (dist2 > qh centrum_radius)
3317     isconcave= True;
3318     else if (!iscoplanar && dist2 > -qh centrum_radius)
3319     iscoplanar= True;
3320     }
3321     if (!isconcave && (!iscoplanar || (qh MERGEexact && !qh POSTmerging)))
3322     return False;
3323     if (!okangle && qh ANGLEmerge) {
3324     angle= qh_getangle(facet->normal, neighbor->normal);
3325     zinc_(Zangletests);
3326     }
3327     if (isconcave) {
3328     zinc_(Zconcaveridge);
3329     if (qh ANGLEmerge)
3330     angle += qh_ANGLEconcave + 0.5;
3331     qh_appendmergeset(facet, neighbor, MRGconcave, &angle);
3332     trace0((qh ferr, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n",
3333     facet->id, neighbor->id, dist, dist2, angle, qh furthest_id));
3334     }else /* iscoplanar */ {
3335     zinc_(Zcoplanarcentrum);
3336     qh_appendmergeset(facet, neighbor, MRGcoplanar, &angle);
3337     trace2((qh ferr, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
3338     facet->id, neighbor->id, dist, dist2, angle));
3339     }
3340     return True;
3341     } /* test_appendmerge */
3342    
3343     /*-<a href="qh-merge.htm#TOC"
3344     >-------------------------------</a><a name="test_vneighbors">-</a>
3345    
3346     qh_test_vneighbors()
3347     test vertex neighbors for convexity
3348     tests all facets on qh.newfacet_list
3349    
3350     returns:
3351     true if non-convex vneighbors appended to qh.facet_mergeset
3352     initializes vertex neighbors if needed
3353    
3354     notes:
3355     assumes all facet neighbors have been tested
3356     this can be expensive
3357     this does not guarantee that a centrum is below all facets
3358     but it is unlikely
3359     uses qh.visit_id
3360    
3361     design:
3362     build vertex neighbors if necessary
3363     for all new facets
3364     for all vertices
3365     for each unvisited facet neighbor of the vertex
3366     test new facet and neighbor for convexity
3367     */
3368     boolT qh_test_vneighbors (void /* qh newfacet_list */) {
3369     facetT *newfacet, *neighbor, **neighborp;
3370     vertexT *vertex, **vertexp;
3371     int nummerges= 0;
3372    
3373     trace1((qh ferr, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
3374     if (!qh VERTEXneighbors)
3375     qh_vertexneighbors();
3376     FORALLnew_facets
3377     newfacet->seen= False;
3378     FORALLnew_facets {
3379     newfacet->seen= True;
3380     newfacet->visitid= qh visit_id++;
3381     FOREACHneighbor_(newfacet)
3382     newfacet->visitid= qh visit_id;
3383     FOREACHvertex_(newfacet->vertices) {
3384     FOREACHneighbor_(vertex) {
3385     if (neighbor->seen || neighbor->visitid == qh visit_id)
3386     continue;
3387     if (qh_test_appendmerge (newfacet, neighbor))
3388     nummerges++;
3389     }
3390     }
3391     }
3392     zadd_(Ztestvneighbor, nummerges);
3393     trace1((qh ferr, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
3394     nummerges));
3395     return (nummerges > 0);
3396     } /* test_vneighbors */
3397    
3398     /*-<a href="qh-merge.htm#TOC"
3399     >-------------------------------</a><a name="tracemerge">-</a>
3400    
3401     qh_tracemerge( facet1, facet2 )
3402     print trace message after merge
3403     */
3404     void qh_tracemerge (facetT *facet1, facetT *facet2) {
3405     boolT waserror= False;
3406    
3407     #ifndef qh_NOtrace
3408     if (qh IStracing >= 4)
3409     qh_errprint ("MERGED", facet2, NULL, NULL, NULL);
3410     if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newlist)) {
3411     fprintf (qh ferr, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh furthest_id);
3412     if (facet2 != qh tracefacet)
3413     qh_errprint ("TRACE", qh tracefacet,
3414     (qh tracevertex && qh tracevertex->neighbors) ?
3415     SETfirstt_(qh tracevertex->neighbors, facetT) : NULL,
3416     NULL, qh tracevertex);
3417     }
3418     if (qh tracevertex) {
3419     if (qh tracevertex->deleted)
3420     fprintf (qh ferr, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
3421     qh furthest_id);
3422     else
3423     qh_checkvertex (qh tracevertex);
3424     }
3425     if (qh tracefacet) {
3426     qh_checkfacet (qh tracefacet, True, &waserror);
3427     if (waserror)
3428     qh_errexit (qh_ERRqhull, qh tracefacet, NULL);
3429     }
3430     #endif /* !qh_NOtrace */
3431     if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */
3432     qh_checkfacet (facet2, True, &waserror);
3433     if (waserror)
3434     qh_errexit(qh_ERRqhull, NULL, NULL);
3435     }
3436     } /* tracemerge */
3437    
3438     /*-<a href="qh-merge.htm#TOC"
3439     >-------------------------------</a><a name="tracemerging">-</a>
3440    
3441     qh_tracemerging()
3442     print trace message during POSTmerging
3443    
3444     returns:
3445     updates qh.mergereport
3446    
3447     notes:
3448     called from qh_mergecycle() and qh_mergefacet()
3449    
3450     see:
3451     qh_buildtracing()
3452     */
3453     void qh_tracemerging (void) {
3454     realT cpu;
3455     int total;
3456     time_t timedata;
3457     struct tm *tp;
3458    
3459     qh mergereport= zzval_(Ztotmerge);
3460     time (&timedata);
3461     tp= localtime (&timedata);
3462     cpu= qh_CPUclock;
3463     cpu /= qh_SECticks;
3464     total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
3465     fprintf (qh ferr, "\n\
3466     At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\
3467     contains %d facets and %d vertices.\n",
3468     tp->tm_hour, tp->tm_min, tp->tm_sec, cpu,
3469     total, qh num_facets - qh num_visible,
3470     qh num_vertices-qh_setsize (qh del_vertices));
3471     } /* tracemerging */
3472    
3473     /*-<a href="qh-merge.htm#TOC"
3474     >-------------------------------</a><a name="updatetested">-</a>
3475    
3476     qh_updatetested( facet1, facet2 )
3477     clear facet2->tested and facet1->ridge->tested for merge
3478    
3479     returns:
3480     deletes facet2->center unless it's already large
3481     if so, clears facet2->ridge->tested
3482    
3483     design:
3484     clear facet2->tested
3485     clear ridge->tested for facet1's ridges
3486     if facet2 has a centrum
3487     if facet2 is large
3488     set facet2->keepcentrum
3489     else if facet2 has 3 vertices due to many merges, or not large and post merging
3490     clear facet2->keepcentrum
3491     unless facet2->keepcentrum
3492     clear facet2->center to recompute centrum later
3493     clear ridge->tested for facet2's ridges
3494     */
3495     void qh_updatetested (facetT *facet1, facetT *facet2) {
3496     ridgeT *ridge, **ridgep;
3497     int size;
3498    
3499     facet2->tested= False;
3500     FOREACHridge_(facet1->ridges)
3501     ridge->tested= False;
3502     if (!facet2->center)
3503     return;
3504     size= qh_setsize (facet2->vertices);
3505     if (!facet2->keepcentrum) {
3506     if (size > qh hull_dim + qh_MAXnewcentrum) {
3507     facet2->keepcentrum= True;
3508     zinc_(Zwidevertices);
3509     }
3510     }else if (size <= qh hull_dim + qh_MAXnewcentrum) {
3511     /* center and keepcentrum was set */
3512     if (size == qh hull_dim || qh POSTmerging)
3513     facet2->keepcentrum= False; /* if many merges need to recompute centrum */
3514     }
3515     if (!facet2->keepcentrum) {
3516     qh_memfree (facet2->center, qh normal_size);
3517     facet2->center= NULL;
3518     FOREACHridge_(facet2->ridges)
3519     ridge->tested= False;
3520     }
3521     } /* updatetested */
3522    
3523     /*-<a href="qh-merge.htm#TOC"
3524     >-------------------------------</a><a name="vertexridges">-</a>
3525    
3526     qh_vertexridges( vertex )
3527     return temporary set of ridges adjacent to a vertex
3528     vertex->neighbors defined
3529    
3530     ntoes:
3531     uses qh.visit_id
3532     does not include implicit ridges for simplicial facets
3533    
3534     design:
3535     for each neighbor of vertex
3536     add ridges that include the vertex to ridges
3537     */
3538     setT *qh_vertexridges (vertexT *vertex) {
3539     facetT *neighbor, **neighborp;
3540     setT *ridges= qh_settemp (qh TEMPsize);
3541     int size;
3542    
3543     qh visit_id++;
3544     FOREACHneighbor_(vertex)
3545     neighbor->visitid= qh visit_id;
3546     FOREACHneighbor_(vertex) {
3547     if (*neighborp) /* no new ridges in last neighbor */
3548     qh_vertexridges_facet (vertex, neighbor, &ridges);
3549     }
3550     if (qh PRINTstatistics || qh IStracing) {
3551     size= qh_setsize (ridges);
3552     zinc_(Zvertexridge);
3553     zadd_(Zvertexridgetot, size);
3554     zmax_(Zvertexridgemax, size);
3555     trace3((qh ferr, "qh_vertexridges: found %d ridges for v%d\n",
3556     size, vertex->id));
3557     }
3558     return ridges;
3559     } /* vertexridges */
3560    
3561     /*-<a href="qh-merge.htm#TOC"
3562     >-------------------------------</a><a name="vertexridges_facet">-</a>
3563    
3564     qh_vertexridges_facet( vertex, facet, ridges )
3565     add adjacent ridges for vertex in facet
3566     neighbor->visitid==qh.visit_id if it hasn't been visited
3567    
3568     returns:
3569     ridges updated
3570     sets facet->visitid to qh.visit_id-1
3571    
3572     design:
3573     for each ridge of facet
3574     if ridge of visited neighbor (i.e., unprocessed)
3575     if vertex in ridge
3576     append ridge to vertex
3577     mark facet processed
3578     */
3579     void qh_vertexridges_facet (vertexT *vertex, facetT *facet, setT **ridges) {
3580     ridgeT *ridge, **ridgep;
3581     facetT *neighbor;
3582    
3583     FOREACHridge_(facet->ridges) {
3584     neighbor= otherfacet_(ridge, facet);
3585     if (neighbor->visitid == qh visit_id
3586     && qh_setin (ridge->vertices, vertex))
3587     qh_setappend (ridges, ridge);
3588     }
3589     facet->visitid= qh visit_id-1;
3590     } /* vertexridges_facet */
3591    
3592     /*-<a href="qh-merge.htm#TOC"
3593     >-------------------------------</a><a name="willdelete">-</a>
3594    
3595     qh_willdelete( facet, replace )
3596     moves facet to visible list
3597     sets facet->f.replace to replace (may be NULL)
3598    
3599     returns:
3600     bumps qh.num_visible
3601     */
3602     void qh_willdelete (facetT *facet, facetT *replace) {
3603    
3604     qh_removefacet(facet);
3605     qh_prependfacet (facet, &qh visible_list);
3606     qh num_visible++;
3607     facet->visible= True;
3608     facet->f.replace= replace;
3609     } /* willdelete */
3610    
3611     #else /* qh_NOmerge */
3612     void qh_premerge (vertexT *apex, realT maxcentrum, realT maxangle) {
3613     }
3614     void qh_postmerge (char *reason, realT maxcentrum, realT maxangle,
3615     boolT vneighbors) {
3616     }
3617     boolT qh_checkzero (boolT testall) {
3618     }
3619     #endif /* qh_NOmerge */
3620