ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-4/src/QuickHull/merge.c
Revision: 3138
Committed: Tue May 29 22:51:00 2007 UTC (17 years, 2 months ago) by chuckv
Content type: text/plain
File size: 119797 byte(s)
Log Message:
Addded QuickHull to cvs.

File Contents

# Content
1 /*<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