Line data Source code
1 : /*<html><pre> -<a href="qh-merge_r.htm#TOC"
2 : >-------------------------------</a><a name="TOP">-</a>
3 :
4 : merge_r.c
5 : merges non-convex facets
6 :
7 : see qh-merge_r.htm and merge_r.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 libqhull_r.c):
14 : qh_partitionvisible(qh, !qh_ALL, &numoutside); // visible_list, newfacet_list
15 : qh_deletevisible(); // qh.visible_list
16 : qh_resetlists(qh, 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-2020 C.B. Barber.
24 : $Id: //main/2019/qhull/src/libqhull_r/merge_r.c#14 $$Change: 2953 $
25 : $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
26 : */
27 :
28 : #include "qhull_ra.h"
29 :
30 : #ifndef qh_NOmerge
31 :
32 : /* MRGnone, etc. */
33 : const char *mergetypes[]= {
34 : "none",
35 : "coplanar",
36 : "anglecoplanar",
37 : "concave",
38 : "concavecoplanar",
39 : "twisted",
40 : "flip",
41 : "dupridge",
42 : "subridge",
43 : "vertices",
44 : "degen",
45 : "redundant",
46 : "mirror",
47 : "coplanarhorizon",
48 : };
49 :
50 : /*===== functions(alphabetical after premerge and postmerge) ======*/
51 :
52 : /*-<a href="qh-merge_r.htm#TOC"
53 : >-------------------------------</a><a name="premerge">-</a>
54 :
55 : qh_premerge(qh, apexpointid, maxcentrum )
56 : pre-merge nonconvex facets in qh.newfacet_list for apexpointid
57 : maxcentrum defines coplanar and concave (qh_test_appendmerge)
58 :
59 : returns:
60 : deleted facets added to qh.visible_list with facet->visible set
61 :
62 : notes:
63 : only called by qh_addpoint
64 : uses globals, qh.MERGEexact, qh.PREmerge
65 :
66 : design:
67 : mark dupridges in qh.newfacet_list
68 : merge facet cycles in qh.newfacet_list
69 : merge dupridges and concave facets in qh.newfacet_list
70 : check merged facet cycles for degenerate and redundant facets
71 : merge degenerate and redundant facets
72 : collect coplanar and concave facets
73 : merge concave, coplanar, degenerate, and redundant facets
74 : */
75 14660 : void qh_premerge(qhT *qh, int apexpointid, realT maxcentrum, realT maxangle /* qh.newfacet_list */) {
76 14660 : boolT othermerge= False;
77 :
78 14660 : if (qh->ZEROcentrum && qh_checkzero(qh, !qh_ALL))
79 2463 : return;
80 12197 : trace2((qh, qh->ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %4.4g for apex p%d newfacet_list f%d\n",
81 : maxcentrum, maxangle, apexpointid, getid_(qh->newfacet_list)));
82 12197 : if (qh->IStracing >= 4 && qh->num_facets < 100)
83 0 : qh_printlists(qh);
84 12197 : qh->centrum_radius= maxcentrum;
85 12197 : qh->cos_max= maxangle;
86 12197 : if (qh->hull_dim >=3) {
87 12197 : qh_mark_dupridges(qh, qh->newfacet_list, qh_ALL); /* facet_mergeset */
88 12197 : qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);
89 12197 : qh_forcedmerges(qh, &othermerge /* qh.facet_mergeset */);
90 : }else /* qh.hull_dim == 2 */
91 0 : qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);
92 12197 : qh_flippedmerges(qh, qh->newfacet_list, &othermerge);
93 12197 : if (!qh->MERGEexact || zzval_(Ztotmerge)) {
94 12197 : zinc_(Zpremergetot);
95 12197 : qh->POSTmerging= False;
96 12197 : qh_getmergeset_initial(qh, qh->newfacet_list);
97 12197 : qh_all_merges(qh, othermerge, False);
98 : }
99 : } /* premerge */
100 :
101 : /*-<a href="qh-merge_r.htm#TOC"
102 : >-------------------------------</a><a name="postmerge">-</a>
103 :
104 : qh_postmerge(qh, reason, maxcentrum, maxangle, vneighbors )
105 : post-merge nonconvex facets as defined by maxcentrum and maxangle
106 : 'reason' is for reporting progress
107 : if vneighbors ('Qv'),
108 : calls qh_test_vneighbors at end of qh_all_merge from qh_postmerge
109 :
110 : returns:
111 : if first call (qh.visible_list != qh.facet_list),
112 : builds qh.facet_newlist, qh.newvertex_list
113 : deleted facets added to qh.visible_list with facet->visible
114 : qh.visible_list == qh.facet_list
115 :
116 : notes:
117 : called by qh_qhull after qh_buildhull
118 : called if a merge may be needed due to
119 : qh.MERGEexact ('Qx'), qh_DIMreduceBuild, POSTmerge (e.g., 'Cn'), or TESTvneighbors ('Qv')
120 : if firstmerge,
121 : calls qh_reducevertices before qh_getmergeset
122 :
123 : design:
124 : if first call
125 : set qh.visible_list and qh.newfacet_list to qh.facet_list
126 : add all facets to qh.newfacet_list
127 : mark non-simplicial facets, facet->newmerge
128 : set qh.newvertext_list to qh.vertex_list
129 : add all vertices to qh.newvertex_list
130 : if a pre-merge occurred
131 : set vertex->delridge {will retest the ridge}
132 : if qh.MERGEexact
133 : call qh_reducevertices()
134 : if no pre-merging
135 : merge flipped facets
136 : determine non-convex facets
137 : merge all non-convex facets
138 : */
139 0 : void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
140 : boolT vneighbors) {
141 : facetT *newfacet;
142 0 : boolT othermerges= False;
143 : vertexT *vertex;
144 :
145 0 : if (qh->REPORTfreq || qh->IStracing) {
146 0 : qh_buildtracing(qh, NULL, NULL);
147 0 : qh_printsummary(qh, qh->ferr);
148 0 : if (qh->PRINTstatistics)
149 0 : qh_printallstatistics(qh, qh->ferr, "reason");
150 0 : qh_fprintf(qh, qh->ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
151 : reason, maxcentrum, maxangle);
152 : }
153 0 : trace2((qh, qh->ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n",
154 : vneighbors));
155 0 : qh->centrum_radius= maxcentrum;
156 0 : qh->cos_max= maxangle;
157 0 : qh->POSTmerging= True;
158 0 : if (qh->visible_list != qh->facet_list) { /* first call due to qh_buildhull, multiple calls if qh.POSTmerge */
159 0 : qh->NEWfacets= True;
160 0 : qh->visible_list= qh->newfacet_list= qh->facet_list;
161 0 : FORALLnew_facets { /* all facets are new facets for qh_postmerge */
162 0 : newfacet->newfacet= True;
163 0 : if (!newfacet->simplicial)
164 0 : newfacet->newmerge= True; /* test f.vertices for 'delridge'. 'newmerge' was cleared at end of qh_all_merges */
165 0 : zinc_(Zpostfacets);
166 : }
167 0 : qh->newvertex_list= qh->vertex_list;
168 0 : FORALLvertices
169 0 : vertex->newfacet= True;
170 0 : if (qh->VERTEXneighbors) { /* a merge has occurred */
171 0 : if (qh->MERGEexact && qh->hull_dim <= qh_DIMreduceBuild)
172 0 : qh_reducevertices(qh); /* qh_all_merges did not call qh_reducevertices for v.delridge */
173 : }
174 0 : if (!qh->PREmerge && !qh->MERGEexact)
175 0 : qh_flippedmerges(qh, qh->newfacet_list, &othermerges);
176 : }
177 0 : qh_getmergeset_initial(qh, qh->newfacet_list);
178 0 : qh_all_merges(qh, False, vneighbors); /* calls qh_reducevertices before exiting */
179 0 : FORALLnew_facets
180 0 : newfacet->newmerge= False; /* Was True if no vertex in f.vertices was 'delridge' */
181 0 : } /* post_merge */
182 :
183 : /*-<a href="qh-merge_r.htm#TOC"
184 : >-------------------------------</a><a name="all_merges">-</a>
185 :
186 : qh_all_merges(qh, othermerge, vneighbors )
187 : merge all non-convex facets
188 :
189 : set othermerge if already merged facets (calls qh_reducevertices)
190 : if vneighbors ('Qv' at qh.POSTmerge)
191 : tests vertex neighbors for convexity at end (qh_test_vneighbors)
192 : qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list
193 : qh.degen_mergeset is defined
194 : if qh.MERGEexact && !qh.POSTmerging,
195 : does not merge coplanar facets
196 :
197 : returns:
198 : deleted facets added to qh.visible_list with facet->visible
199 : deleted vertices added qh.delvertex_list with vertex->delvertex
200 :
201 : notes:
202 : unless !qh.MERGEindependent,
203 : merges facets in independent sets
204 : uses qh.newfacet_list as implicit argument since merges call qh_removefacet()
205 : [apr'19] restored qh_setdellast in place of qh_next_facetmerge. Much faster for post-merge
206 :
207 : design:
208 : while merges occur
209 : for each merge in qh.facet_mergeset
210 : unless one of the facets was already merged in this pass
211 : merge the facets
212 : test merged facets for additional merges
213 : add merges to qh.facet_mergeset
214 : if qh.POSTmerging
215 : periodically call qh_reducevertices to reduce extra vertices and redundant vertices
216 : after each pass, if qh.VERTEXneighbors
217 : if qh.POSTmerging or was a merge with qh.hull_dim<=5
218 : call qh_reducevertices
219 : update qh.facet_mergeset if degenredundant merges
220 : if 'Qv' and qh.POSTmerging
221 : test vertex neighbors for convexity
222 : */
223 12197 : void qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors) {
224 : facetT *facet1, *facet2, *newfacet;
225 : mergeT *merge;
226 12197 : boolT wasmerge= False, isreduce;
227 : void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
228 : vertexT *vertex;
229 : realT angle, distance;
230 : mergeType mergetype;
231 12197 : int numcoplanar=0, numconcave=0, numconcavecoplanar= 0, numdegenredun= 0, numnewmerges= 0, numtwisted= 0;
232 :
233 12197 : trace2((qh, qh->ferr, 2010, "qh_all_merges: starting to merge %d facet and %d degenerate merges for new facets f%d, othermerge? %d\n",
234 : qh_setsize(qh, qh->facet_mergeset), qh_setsize(qh, qh->degen_mergeset), getid_(qh->newfacet_list), othermerge));
235 :
236 : while (True) {
237 12197 : wasmerge= False;
238 12198 : while (qh_setsize(qh, qh->facet_mergeset) > 0 || qh_setsize(qh, qh->degen_mergeset) > 0) {
239 1 : if (qh_setsize(qh, qh->degen_mergeset) > 0) {
240 0 : numdegenredun += qh_merge_degenredundant(qh);
241 0 : wasmerge= True;
242 : }
243 2 : while ((merge= (mergeT *)qh_setdellast(qh->facet_mergeset))) {
244 1 : facet1= merge->facet1;
245 1 : facet2= merge->facet2;
246 1 : vertex= merge->vertex1; /* not used for qh.facet_mergeset*/
247 1 : mergetype= merge->mergetype;
248 1 : angle= merge->angle;
249 1 : distance= merge->distance;
250 1 : qh_memfree_(qh, merge, (int)sizeof(mergeT), freelistp); /* 'merge' is invalid */
251 1 : if (facet1->visible || facet2->visible) {
252 0 : trace3((qh, qh->ferr, 3045, "qh_all_merges: drop merge of f%d (del? %d) into f%d (del? %d) mergetype %d, dist %4.4g, angle %4.4g. One or both facets is deleted\n",
253 : facet1->id, facet1->visible, facet2->id, facet2->visible, mergetype, distance, angle));
254 0 : continue;
255 1 : }else if (mergetype == MRGcoplanar || mergetype == MRGanglecoplanar) {
256 1 : if (qh->MERGEindependent) {
257 1 : if ((!facet1->tested && facet1->newfacet)
258 1 : || (!facet2->tested && facet2->newfacet)) {
259 0 : trace3((qh, qh->ferr, 3064, "qh_all_merges: drop merge of f%d (tested? %d) into f%d (tested? %d) mergetype %d, dist %2.2g, angle %4.4g. Merge independent sets of coplanar merges\n",
260 : facet1->id, facet1->visible, facet2->id, facet2->visible, mergetype, distance, angle));
261 0 : continue;
262 : }
263 : }
264 : }
265 1 : trace3((qh, qh->ferr, 3047, "qh_all_merges: merge f%d and f%d type %d dist %2.2g angle %4.4g\n",
266 : facet1->id, facet2->id, mergetype, distance, angle));
267 1 : if (mergetype == MRGtwisted)
268 0 : qh_merge_twisted(qh, facet1, facet2);
269 : else
270 1 : qh_merge_nonconvex(qh, facet1, facet2, mergetype);
271 1 : numnewmerges++;
272 1 : numdegenredun += qh_merge_degenredundant(qh);
273 1 : wasmerge= True;
274 1 : if (mergetype == MRGconcave)
275 0 : numconcave++;
276 1 : else if (mergetype == MRGconcavecoplanar)
277 0 : numconcavecoplanar++;
278 1 : else if (mergetype == MRGtwisted)
279 0 : numtwisted++;
280 1 : else if (mergetype == MRGcoplanar || mergetype == MRGanglecoplanar)
281 1 : numcoplanar++;
282 : else {
283 0 : qh_fprintf(qh, qh->ferr, 6394, "qhull internal error (qh_all_merges): expecting concave, coplanar, or twisted merge. Got merge f%d f%d v%d mergetype %d\n",
284 0 : getid_(facet1), getid_(facet2), getid_(vertex), mergetype);
285 0 : qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
286 : }
287 : } /* while qh_setdellast */
288 1 : if (qh->POSTmerging && qh->hull_dim <= qh_DIMreduceBuild
289 0 : && numnewmerges > qh_MAXnewmerges) {
290 0 : numnewmerges= 0;
291 0 : wasmerge= othermerge= False;
292 0 : qh_reducevertices(qh); /* otherwise large post merges too slow */
293 : }
294 1 : qh_getmergeset(qh, qh->newfacet_list); /* qh.facet_mergeset */
295 : } /* while facet_mergeset or degen_mergeset */
296 12197 : if (qh->VERTEXneighbors) { /* at least one merge */
297 12197 : isreduce= False;
298 12197 : if (qh->POSTmerging && qh->hull_dim >= 4) {
299 0 : isreduce= True;
300 12197 : }else if (qh->POSTmerging || !qh->MERGEexact) {
301 12197 : if ((wasmerge || othermerge) && qh->hull_dim > 2 && qh->hull_dim <= qh_DIMreduceBuild)
302 9686 : isreduce= True;
303 : }
304 12197 : if (isreduce) {
305 9686 : wasmerge= othermerge= False;
306 9686 : if (qh_reducevertices(qh)) {
307 0 : qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */
308 0 : continue;
309 : }
310 : }
311 : }
312 12197 : if (vneighbors && qh_test_vneighbors(qh /* qh.newfacet_list */))
313 0 : continue;
314 12197 : break;
315 : } /* while (True) */
316 12197 : if (wasmerge || othermerge) {
317 0 : trace3((qh, qh->ferr, 3033, "qh_all_merges: skip qh_reducevertices due to post-merging, no qh.VERTEXneighbors (%d), or hull_dim %d ==2 or >%d\n", qh->VERTEXneighbors, qh->hull_dim, qh_DIMreduceBuild))
318 0 : FORALLnew_facets {
319 0 : newfacet->newmerge= False;
320 : }
321 : }
322 12197 : if (qh->CHECKfrequently && !qh->MERGEexact) {
323 0 : qh->old_randomdist= qh->RANDOMdist;
324 0 : qh->RANDOMdist= False;
325 0 : qh_checkconvex(qh, qh->newfacet_list, qh_ALGORITHMfault);
326 : /* qh_checkconnect(qh); [this is slow and it changes the facet order] */
327 0 : qh->RANDOMdist= qh->old_randomdist;
328 : }
329 12197 : trace1((qh, qh->ferr, 1009, "qh_all_merges: merged %d coplanar %d concave %d concavecoplanar %d twisted facets and %d degen or redundant facets.\n",
330 : numcoplanar, numconcave, numconcavecoplanar, numtwisted, numdegenredun));
331 12197 : if (qh->IStracing >= 4 && qh->num_facets < 500)
332 0 : qh_printlists(qh);
333 12197 : } /* all_merges */
334 :
335 : /*-<a href="qh-merge_r.htm#TOC"
336 : >-------------------------------</a><a name="all_vertexmerges">-</a>
337 :
338 : qh_all_vertexmerges(qh, apexpointid, facet, &retryfacet )
339 : merge vertices in qh.vertex_mergeset and subsequent merges
340 :
341 : returns:
342 : returns retryfacet for facet (if defined)
343 : updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices
344 : mergesets are empty
345 : if merges, resets facet lists
346 :
347 : notes:
348 : called from qh_qhull, qh_addpoint, and qh_buildcone_mergepinched
349 : vertex merges occur after facet merges and qh_resetlists
350 :
351 : design:
352 : while merges in vertex_mergeset (MRGvertices)
353 : merge a pair of pinched vertices
354 : update vertex neighbors
355 : merge non-convex and degenerate facets and check for ridges with duplicate vertices
356 : partition outside points of deleted, "visible" facets
357 : */
358 14663 : void qh_all_vertexmerges(qhT *qh, int apexpointid, facetT *facet, facetT **retryfacet) {
359 : int numpoints; /* ignore count of partitioned points. Used by qh_addpoint for Zpbalance */
360 :
361 14663 : if (retryfacet)
362 0 : *retryfacet= facet;
363 14663 : while (qh_setsize(qh, qh->vertex_mergeset) > 0) {
364 0 : trace1((qh, qh->ferr, 1057, "qh_all_vertexmerges: starting to merge %d vertex merges for apex p%d facet f%d\n",
365 : qh_setsize(qh, qh->vertex_mergeset), apexpointid, getid_(facet)));
366 0 : if (qh->IStracing >= 4 && qh->num_facets < 1000)
367 0 : qh_printlists(qh);
368 0 : qh_merge_pinchedvertices(qh, apexpointid /* qh.vertex_mergeset, visible_list, newvertex_list, newfacet_list */);
369 0 : qh_update_vertexneighbors(qh); /* update neighbors of qh.newvertex_list from qh_newvertices for deleted facets on qh.visible_list */
370 : /* test ridges and merge non-convex facets */
371 0 : qh_getmergeset(qh, qh->newfacet_list);
372 0 : qh_all_merges(qh, True, False); /* calls qh_reducevertices */
373 0 : if (qh->CHECKfrequently)
374 0 : qh_checkpolygon(qh, qh->facet_list);
375 0 : qh_partitionvisible(qh, !qh_ALL, &numpoints /* qh.visible_list qh.del_vertices*/);
376 0 : if (retryfacet)
377 0 : *retryfacet= qh_getreplacement(qh, *retryfacet);
378 0 : qh_deletevisible(qh /* qh.visible_list qh.del_vertices*/);
379 0 : qh_resetlists(qh, False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */);
380 0 : if (qh->IStracing >= 4 && qh->num_facets < 1000) {
381 0 : qh_printlists(qh);
382 0 : qh_checkpolygon(qh, qh->facet_list);
383 : }
384 : }
385 14663 : } /* all_vertexmerges */
386 :
387 : /*-<a href="qh-merge_r.htm#TOC"
388 : >-------------------------------</a><a name="appendmergeset">-</a>
389 :
390 : qh_appendmergeset(qh, facet, vertex, neighbor, mergetype, dist, angle )
391 : appends an entry to qh.facet_mergeset or qh.degen_mergeset
392 : if 'dist' is unknown, set it to 0.0
393 : if 'angle' is unknown, set it to 1.0 (coplanar)
394 :
395 : returns:
396 : merge appended to facet_mergeset or degen_mergeset
397 : sets ->degenerate or ->redundant if degen_mergeset
398 :
399 : notes:
400 : caller collects statistics and/or caller of qh_mergefacet
401 : see: qh_test_appendmerge()
402 :
403 : design:
404 : allocate merge entry
405 : if regular merge
406 : append to qh.facet_mergeset
407 : else if degenerate merge and qh.facet_mergeset is all degenerate
408 : append to qh.degen_mergeset
409 : else if degenerate merge
410 : prepend to qh.degen_mergeset (merged last)
411 : else if redundant merge
412 : append to qh.degen_mergeset
413 : */
414 1 : void qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, coordT dist, realT angle) {
415 : mergeT *merge, *lastmerge;
416 : void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
417 : const char *mergename;
418 :
419 1 : if ((facet->redundant && mergetype != MRGmirror) || neighbor->redundant) {
420 0 : trace3((qh, qh->ferr, 3051, "qh_appendmergeset: f%d is already redundant (%d) or f%d is already redundant (%d). Ignore merge f%d and f%d type %d\n",
421 : facet->id, facet->redundant, neighbor->id, neighbor->redundant, facet->id, neighbor->id, mergetype));
422 0 : return;
423 : }
424 1 : if (facet->degenerate && mergetype == MRGdegen) {
425 0 : trace3((qh, qh->ferr, 3077, "qh_appendmergeset: f%d is already degenerate. Ignore merge f%d type %d (MRGdegen)\n",
426 : facet->id, facet->id, mergetype));
427 0 : return;
428 : }
429 1 : if (!qh->facet_mergeset || !qh->degen_mergeset) {
430 0 : qh_fprintf(qh, qh->ferr, 6403, "qhull internal error (qh_appendmergeset): expecting temp set defined for qh.facet_mergeset (0x%x) and qh.degen_mergeset (0x%x). Got NULL\n",
431 : qh->facet_mergeset, qh->degen_mergeset);
432 : /* otherwise qh_setappend creates a new set that is not freed by qh_freebuild() */
433 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
434 : }
435 1 : if (neighbor->flipped && !facet->flipped) {
436 0 : if (mergetype != MRGdupridge) {
437 0 : qh_fprintf(qh, qh->ferr, 6355, "qhull internal error (qh_appendmergeset): except for MRGdupridge, cannot merge a non-flipped facet f%d into flipped f%d, mergetype %d, dist %4.4g\n",
438 : facet->id, neighbor->id, mergetype, dist);
439 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
440 : }else {
441 0 : trace2((qh, qh->ferr, 2106, "qh_appendmergeset: dupridge will merge a non-flipped facet f%d into flipped f%d, dist %4.4g\n",
442 : facet->id, neighbor->id, dist));
443 : }
444 : }
445 1 : qh_memalloc_(qh, (int)sizeof(mergeT), freelistp, merge, mergeT);
446 1 : merge->angle= angle;
447 1 : merge->distance= dist;
448 1 : merge->facet1= facet;
449 1 : merge->facet2= neighbor;
450 1 : merge->vertex1= NULL;
451 1 : merge->vertex2= NULL;
452 1 : merge->ridge1= NULL;
453 1 : merge->ridge2= NULL;
454 1 : merge->mergetype= mergetype;
455 1 : if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *))
456 1 : mergename= mergetypes[mergetype];
457 : else
458 0 : mergename= mergetypes[MRGnone];
459 1 : if (mergetype < MRGdegen)
460 1 : qh_setappend(qh, &(qh->facet_mergeset), merge);
461 0 : else if (mergetype == MRGdegen) {
462 0 : facet->degenerate= True;
463 0 : if (!(lastmerge= (mergeT *)qh_setlast(qh->degen_mergeset))
464 0 : || lastmerge->mergetype == MRGdegen)
465 0 : qh_setappend(qh, &(qh->degen_mergeset), merge);
466 : else
467 0 : qh_setaddnth(qh, &(qh->degen_mergeset), 0, merge); /* merged last */
468 0 : }else if (mergetype == MRGredundant) {
469 0 : facet->redundant= True;
470 0 : qh_setappend(qh, &(qh->degen_mergeset), merge);
471 : }else /* mergetype == MRGmirror */ {
472 0 : if (facet->redundant || neighbor->redundant) {
473 0 : qh_fprintf(qh, qh->ferr, 6092, "qhull internal error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet (i.e., 'redundant')\n",
474 : facet->id, neighbor->id);
475 0 : qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
476 : }
477 0 : if (!qh_setequal(facet->vertices, neighbor->vertices)) {
478 0 : qh_fprintf(qh, qh->ferr, 6093, "qhull internal error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
479 : facet->id, neighbor->id);
480 0 : qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
481 : }
482 0 : facet->redundant= True;
483 0 : neighbor->redundant= True;
484 0 : qh_setappend(qh, &(qh->degen_mergeset), merge);
485 : }
486 1 : if (merge->mergetype >= MRGdegen) {
487 0 : trace3((qh, qh->ferr, 3044, "qh_appendmergeset: append merge f%d and f%d type %d (%s) to qh.degen_mergeset (size %d)\n",
488 : merge->facet1->id, merge->facet2->id, merge->mergetype, mergename, qh_setsize(qh, qh->degen_mergeset)));
489 : }else {
490 1 : trace3((qh, qh->ferr, 3027, "qh_appendmergeset: append merge f%d and f%d type %d (%s) dist %2.2g angle %4.4g to qh.facet_mergeset (size %d)\n",
491 : merge->facet1->id, merge->facet2->id, merge->mergetype, mergename, merge->distance, merge->angle, qh_setsize(qh, qh->facet_mergeset)));
492 : }
493 : } /* appendmergeset */
494 :
495 :
496 : /*-<a href="qh-merge_r.htm#TOC"
497 : >-------------------------------</a><a name="appendvertexmerge">-</a>
498 :
499 : qh_appendvertexmerge(qh, vertex, vertex2, mergetype, distance, ridge1, ridge2 )
500 : appends a vertex merge to qh.vertex_mergeset
501 : MRGsubridge includes two ridges (from MRGdupridge)
502 : MRGvertices includes two ridges
503 :
504 : notes:
505 : called by qh_getpinchedmerges for MRGsubridge
506 : called by qh_maybe_duplicateridge and qh_maybe_duplicateridges for MRGvertices
507 : only way to add a vertex merge to qh.vertex_mergeset
508 : checked by qh_next_vertexmerge
509 : */
510 0 : void qh_appendvertexmerge(qhT *qh, vertexT *vertex, vertexT *destination, mergeType mergetype, realT distance, ridgeT *ridge1, ridgeT *ridge2) {
511 : mergeT *merge;
512 : void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
513 : const char *mergename;
514 :
515 0 : if (!qh->vertex_mergeset) {
516 0 : qh_fprintf(qh, qh->ferr, 6387, "qhull internal error (qh_appendvertexmerge): expecting temp set defined for qh.vertex_mergeset (0x%x). Got NULL\n",
517 : qh->vertex_mergeset);
518 : /* otherwise qh_setappend creates a new set that is not freed by qh_freebuild() */
519 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
520 : }
521 0 : qh_memalloc_(qh, (int)sizeof(mergeT), freelistp, merge, mergeT);
522 0 : merge->angle= qh_ANGLEnone;
523 0 : merge->distance= distance;
524 0 : merge->facet1= NULL;
525 0 : merge->facet2= NULL;
526 0 : merge->vertex1= vertex;
527 0 : merge->vertex2= destination;
528 0 : merge->ridge1= ridge1;
529 0 : merge->ridge2= ridge2;
530 0 : merge->mergetype= mergetype;
531 0 : if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *))
532 0 : mergename= mergetypes[mergetype];
533 : else
534 0 : mergename= mergetypes[MRGnone];
535 0 : if (mergetype == MRGvertices) {
536 0 : if (!ridge1 || !ridge2 || ridge1 == ridge2) {
537 0 : qh_fprintf(qh, qh->ferr, 6106, "qhull internal error (qh_appendvertexmerge): expecting two distinct ridges for MRGvertices. Got r%d r%d\n",
538 0 : getid_(ridge1), getid_(ridge2));
539 0 : qh_errexit(qh, qh_ERRqhull, NULL, ridge1);
540 : }
541 : }
542 0 : qh_setappend(qh, &(qh->vertex_mergeset), merge);
543 0 : trace3((qh, qh->ferr, 3034, "qh_appendvertexmerge: append merge v%d into v%d r%d r%d dist %2.2g type %d (%s)\n",
544 : vertex->id, destination->id, getid_(ridge1), getid_(ridge2), distance, merge->mergetype, mergename));
545 0 : } /* appendvertexmerge */
546 :
547 :
548 : /*-<a href="qh-merge_r.htm#TOC"
549 : >-------------------------------</a><a name="basevertices">-</a>
550 :
551 : qh_basevertices(qh, samecycle )
552 : return temporary set of base vertices for samecycle
553 : samecycle is first facet in the cycle
554 : assumes apex is SETfirst_( samecycle->vertices )
555 :
556 : returns:
557 : vertices(settemp)
558 : all ->seen are cleared
559 :
560 : notes:
561 : uses qh_vertex_visit;
562 :
563 : design:
564 : for each facet in samecycle
565 : for each unseen vertex in facet->vertices
566 : append to result
567 : */
568 0 : setT *qh_basevertices(qhT *qh, facetT *samecycle) {
569 : facetT *same;
570 : vertexT *apex, *vertex, **vertexp;
571 0 : setT *vertices= qh_settemp(qh, qh->TEMPsize);
572 :
573 0 : apex= SETfirstt_(samecycle->vertices, vertexT);
574 0 : apex->visitid= ++qh->vertex_visit;
575 0 : FORALLsame_cycle_(samecycle) {
576 0 : if (same->mergeridge)
577 0 : continue;
578 0 : FOREACHvertex_(same->vertices) {
579 0 : if (vertex->visitid != qh->vertex_visit) {
580 0 : qh_setappend(qh, &vertices, vertex);
581 0 : vertex->visitid= qh->vertex_visit;
582 0 : vertex->seen= False;
583 : }
584 : }
585 : }
586 0 : trace4((qh, qh->ferr, 4019, "qh_basevertices: found %d vertices\n",
587 : qh_setsize(qh, vertices)));
588 0 : return vertices;
589 : } /* basevertices */
590 :
591 : /*-<a href="qh-merge_r.htm#TOC"
592 : >-------------------------------</a><a name="check_dupridge">-</a>
593 :
594 : qh_check_dupridge(qh, facet1, dist1, facet2, dist2 )
595 : Check dupridge between facet1 and facet2 for wide merge
596 : dist1 is the maximum distance of facet1's vertices to facet2
597 : dist2 is the maximum distance of facet2's vertices to facet1
598 :
599 : returns
600 : Level 1 log of the dupridge with the minimum distance between vertices
601 : Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x)
602 :
603 : notes:
604 : only called from qh_forcedmerges
605 : */
606 0 : void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2) {
607 : vertexT *vertex, **vertexp, *vertexA, **vertexAp;
608 : realT dist, innerplane, mergedist, outerplane, prevdist, ratio, vertexratio;
609 0 : realT minvertex= REALmax;
610 :
611 0 : mergedist= fmin_(dist1, dist2);
612 0 : qh_outerinner(qh, NULL, &outerplane, &innerplane); /* ratio from qh_printsummary */
613 0 : FOREACHvertex_(facet1->vertices) { /* The dupridge is between facet1 and facet2, so either facet can be tested */
614 0 : FOREACHvertexA_(facet1->vertices) {
615 0 : if (vertex > vertexA){ /* Test each pair once */
616 0 : dist= qh_pointdist(vertex->point, vertexA->point, qh->hull_dim);
617 0 : minimize_(minvertex, dist);
618 : /* Not quite correct. A facet may have a dupridge and another pair of nearly adjacent vertices. */
619 : }
620 : }
621 : }
622 0 : prevdist= fmax_(outerplane, innerplane);
623 0 : maximize_(prevdist, qh->ONEmerge + qh->DISTround);
624 0 : maximize_(prevdist, qh->MINoutside + qh->DISTround);
625 0 : ratio= mergedist/prevdist;
626 0 : vertexratio= minvertex/prevdist;
627 0 : trace0((qh, qh->ferr, 16, "qh_check_dupridge: dupridge between f%d and f%d (vertex dist %2.2g), dist %2.2g, reverse dist %2.2g, ratio %2.2g while processing p%d\n",
628 : facet1->id, facet2->id, minvertex, dist1, dist2, ratio, qh->furthest_id));
629 0 : if (ratio > qh_WIDEduplicate) {
630 0 : qh_fprintf(qh, qh->ferr, 6271, "qhull topology error (qh_check_dupridge): wide merge (%.1fx wider) due to dupridge between f%d and f%d (vertex dist %2.2g), merge dist %2.2g, while processing p%d\n- Allow error with option 'Q12'\n",
631 : ratio, facet1->id, facet2->id, minvertex, mergedist, qh->furthest_id);
632 0 : if (vertexratio < qh_WIDEpinched)
633 0 : qh_fprintf(qh, qh->ferr, 8145, "- Experimental option merge-pinched-vertices ('Q14') may avoid this error. It merges nearly adjacent vertices.\n");
634 0 : if (qh->DELAUNAY)
635 0 : qh_fprintf(qh, qh->ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n");
636 0 : if (!qh->ALLOWwide)
637 0 : qh_errexit2(qh, qh_ERRwide, facet1, facet2);
638 : }
639 0 : } /* check_dupridge */
640 :
641 : /*-<a href="qh-merge_r.htm#TOC"
642 : >-------------------------------</a><a name="checkconnect">-</a>
643 :
644 : qh_checkconnect(qh)
645 : check that new facets are connected
646 : new facets are on qh.newfacet_list
647 :
648 : notes:
649 : this is slow and it changes the order of the facets
650 : uses qh.visit_id
651 :
652 : design:
653 : move first new facet to end of qh.facet_list
654 : for all newly appended facets
655 : append unvisited neighbors to end of qh.facet_list
656 : for all new facets
657 : report error if unvisited
658 : */
659 0 : void qh_checkconnect(qhT *qh /* qh.newfacet_list */) {
660 0 : facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;
661 :
662 0 : facet= qh->newfacet_list;
663 0 : qh_removefacet(qh, facet);
664 0 : qh_appendfacet(qh, facet);
665 0 : facet->visitid= ++qh->visit_id;
666 0 : FORALLfacet_(facet) {
667 0 : FOREACHneighbor_(facet) {
668 0 : if (neighbor->visitid != qh->visit_id) {
669 0 : qh_removefacet(qh, neighbor);
670 0 : qh_appendfacet(qh, neighbor);
671 0 : neighbor->visitid= qh->visit_id;
672 : }
673 : }
674 : }
675 0 : FORALLnew_facets {
676 0 : if (newfacet->visitid == qh->visit_id)
677 0 : break;
678 0 : qh_fprintf(qh, qh->ferr, 6094, "qhull internal error (qh_checkconnect): f%d is not attached to the new facets\n",
679 : newfacet->id);
680 0 : errfacet= newfacet;
681 : }
682 0 : if (errfacet)
683 0 : qh_errexit(qh, qh_ERRqhull, errfacet, NULL);
684 0 : } /* checkconnect */
685 :
686 : /*-<a href="qh-merge_r.htm#TOC"
687 : >-------------------------------</a><a name="checkdelfacet">-</a>
688 :
689 : qh_checkdelfacet(qh, facet, mergeset )
690 : check that mergeset does not reference facet
691 :
692 : */
693 0 : void qh_checkdelfacet(qhT *qh, facetT *facet, setT *mergeset) {
694 : mergeT *merge, **mergep;
695 :
696 0 : FOREACHmerge_(mergeset) {
697 0 : if (merge->facet1 == facet || merge->facet2 == facet) {
698 0 : qh_fprintf(qh, qh->ferr, 6390, "qhull internal error (qh_checkdelfacet): cannot delete f%d. It is referenced by merge f%d f%d mergetype %d\n",
699 0 : facet->id, merge->facet1->id, getid_(merge->facet2), merge->mergetype);
700 0 : qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2);
701 : }
702 : }
703 0 : } /* checkdelfacet */
704 :
705 : /*-<a href="qh-merge_r.htm#TOC"
706 : >-------------------------------</a><a name="checkdelridge">-</a>
707 :
708 : qh_checkdelridge(qh)
709 : check that qh_delridge_merge is not needed for deleted ridges
710 :
711 : notes:
712 : called from qh_mergecycle, qh_makenewfacets, qh_attachnewfacets
713 : errors if qh.vertex_mergeset is non-empty
714 : errors if any visible or new facet has a ridge with r.nonconvex set
715 : assumes that vertex.delfacet is not needed
716 : */
717 0 : void qh_checkdelridge(qhT *qh /* qh.visible_facets, vertex_mergeset */) {
718 : facetT *newfacet, *visible;
719 : ridgeT *ridge, **ridgep;
720 :
721 0 : if (!SETempty_(qh->vertex_mergeset)) {
722 0 : qh_fprintf(qh, qh->ferr, 6382, "qhull internal error (qh_checkdelridge): expecting empty qh.vertex_mergeset in order to avoid calling qh_delridge_merge. Got %d merges\n", qh_setsize(qh, qh->vertex_mergeset));
723 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
724 : }
725 :
726 0 : FORALLnew_facets {
727 0 : FOREACHridge_(newfacet->ridges) {
728 0 : if (ridge->nonconvex) {
729 0 : qh_fprintf(qh, qh->ferr, 6313, "qhull internal error (qh_checkdelridge): unexpected 'nonconvex' flag for ridge r%d in newfacet f%d. Otherwise need to call qh_delridge_merge\n",
730 : ridge->id, newfacet->id);
731 0 : qh_errexit(qh, qh_ERRqhull, newfacet, ridge);
732 : }
733 : }
734 : }
735 :
736 0 : FORALLvisible_facets {
737 0 : FOREACHridge_(visible->ridges) {
738 0 : if (ridge->nonconvex) {
739 0 : qh_fprintf(qh, qh->ferr, 6385, "qhull internal error (qh_checkdelridge): unexpected 'nonconvex' flag for ridge r%d in visible facet f%d. Otherwise need to call qh_delridge_merge\n",
740 : ridge->id, visible->id);
741 0 : qh_errexit(qh, qh_ERRqhull, visible, ridge);
742 : }
743 : }
744 : }
745 0 : } /* checkdelridge */
746 :
747 :
748 : /*-<a href="qh-merge_r.htm#TOC"
749 : >-------------------------------</a><a name="checkzero">-</a>
750 :
751 : qh_checkzero(qh, testall )
752 : check that facets are clearly convex for qh.DISTround with qh.MERGEexact
753 :
754 : if testall,
755 : test all facets for qh.MERGEexact post-merging
756 : else
757 : test qh.newfacet_list
758 :
759 : if qh.MERGEexact,
760 : allows coplanar ridges
761 : skips convexity test while qh.ZEROall_ok
762 :
763 : returns:
764 : True if all facets !flipped, !dupridge, normal
765 : if all horizon facets are simplicial
766 : if all vertices are clearly below neighbor
767 : if all opposite vertices of horizon are below
768 : clears qh.ZEROall_ok if any problems or coplanar facets
769 :
770 : notes:
771 : called by qh_premerge (qh.CHECKzero, 'C-0') and qh_qhull ('Qx')
772 : uses qh.vertex_visit
773 : horizon facets may define multiple new facets
774 :
775 : design:
776 : for all facets in qh.newfacet_list or qh.facet_list
777 : check for flagged faults (flipped, etc.)
778 : for all facets in qh.newfacet_list or qh.facet_list
779 : for each neighbor of facet
780 : skip horizon facets for qh.newfacet_list
781 : test the opposite vertex
782 : if qh.newfacet_list
783 : test the other vertices in the facet's horizon facet
784 : */
785 14660 : boolT qh_checkzero(qhT *qh, boolT testall) {
786 : facetT *facet, *neighbor;
787 : facetT *horizon, *facetlist;
788 : int neighbor_i, neighbor_n;
789 : vertexT *vertex, **vertexp;
790 : realT dist;
791 :
792 14660 : if (testall)
793 0 : facetlist= qh->facet_list;
794 : else {
795 14660 : facetlist= qh->newfacet_list;
796 35119 : FORALLfacet_(facetlist) {
797 32655 : horizon= SETfirstt_(facet->neighbors, facetT);
798 32655 : if (!horizon->simplicial)
799 5733 : goto LABELproblem;
800 26922 : if (facet->flipped || facet->dupridge || !facet->normal)
801 6463 : goto LABELproblem;
802 : }
803 2464 : if (qh->MERGEexact && qh->ZEROall_ok) {
804 0 : trace2((qh, qh->ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n"));
805 0 : return True;
806 : }
807 : }
808 15583 : FORALLfacet_(facetlist) {
809 13120 : qh->vertex_visit++;
810 13120 : horizon= NULL;
811 52480 : FOREACHneighbor_i_(qh, facet) {
812 39360 : if (!neighbor_i && !testall) {
813 13120 : horizon= neighbor;
814 13120 : continue; /* horizon facet tested in qh_findhorizon */
815 : }
816 26240 : vertex= SETelemt_(facet->vertices, neighbor_i, vertexT);
817 26240 : vertex->visitid= qh->vertex_visit;
818 26240 : zzinc_(Zdistzero);
819 26240 : qh_distplane(qh, vertex->point, neighbor, &dist);
820 26240 : if (dist >= -2 * qh->DISTround) { /* need 2x for qh_distround and 'Rn' for qh_checkconvex, same as qh.premerge_centrum */
821 0 : qh->ZEROall_ok= False;
822 0 : if (!qh->MERGEexact || testall || dist > qh->DISTround)
823 0 : goto LABELnonconvex;
824 : }
825 : }
826 13120 : if (!testall && horizon) {
827 23635 : FOREACHvertex_(horizon->vertices) {
828 23635 : if (vertex->visitid != qh->vertex_visit) {
829 13120 : zzinc_(Zdistzero);
830 13120 : qh_distplane(qh, vertex->point, facet, &dist);
831 13120 : if (dist >= -2 * qh->DISTround) {
832 1 : qh->ZEROall_ok= False;
833 1 : if (!qh->MERGEexact || dist > qh->DISTround)
834 1 : goto LABELnonconvexhorizon;
835 : }
836 13119 : break;
837 : }
838 : }
839 : }
840 : }
841 2463 : trace2((qh, qh->ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall,
842 : (qh->MERGEexact && !testall) ?
843 : "not concave, flipped, or dupridge" : "clearly convex"));
844 2463 : return True;
845 :
846 12196 : LABELproblem:
847 12196 : qh->ZEROall_ok= False;
848 12196 : trace2((qh, qh->ferr, 2013, "qh_checkzero: qh_premerge is needed. New facet f%d or its horizon f%d is non-simplicial, flipped, dupridge, or mergehorizon\n",
849 : facet->id, horizon->id));
850 12196 : return False;
851 :
852 0 : LABELnonconvex:
853 0 : trace2((qh, qh->ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n",
854 : facet->id, neighbor->id, vertex->id, dist));
855 0 : return False;
856 :
857 1 : LABELnonconvexhorizon:
858 1 : trace2((qh, qh->ferr, 2060, "qh_checkzero: facet f%d and horizon f%d are not clearly convex. v%d dist %.2g\n",
859 : facet->id, horizon->id, vertex->id, dist));
860 1 : return False;
861 : } /* checkzero */
862 :
863 : /*-<a href="qh-merge_r.htm#TOC"
864 : >-------------------------------</a><a name="compare_anglemerge">-</a>
865 :
866 : qh_compare_anglemerge( mergeA, mergeB )
867 : used by qsort() to order qh.facet_mergeset by mergetype and angle (qh.ANGLEmerge, 'Q1')
868 : lower numbered mergetypes done first (MRGcoplanar before MRGconcave)
869 :
870 : notes:
871 : qh_all_merges processes qh.facet_mergeset by qh_setdellast
872 : [mar'19] evaluated various options with eg/q_benchmark and merging of pinched vertices (Q14)
873 : */
874 0 : int qh_compare_anglemerge(const void *p1, const void *p2) {
875 0 : const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
876 :
877 0 : if (a->mergetype != b->mergetype)
878 0 : return (a->mergetype < b->mergetype ? 1 : -1); /* select MRGcoplanar (1) before MRGconcave (3) */
879 : else
880 0 : return (a->angle > b->angle ? 1 : -1); /* select coplanar merge (1.0) before sharp merge (-0.5) */
881 : } /* compare_anglemerge */
882 :
883 : /*-<a href="qh-merge_r.htm#TOC"
884 : >-------------------------------</a><a name="compare_facetmerge">-</a>
885 :
886 : qh_compare_facetmerge( mergeA, mergeB )
887 : used by qsort() to order merges by mergetype, first merge, first
888 : lower numbered mergetypes done first (MRGcoplanar before MRGconcave)
889 : if same merge type, flat merges are first
890 :
891 : notes:
892 : qh_all_merges processes qh.facet_mergeset by qh_setdellast
893 : [mar'19] evaluated various options with eg/q_benchmark and merging of pinched vertices (Q14)
894 : */
895 0 : int qh_compare_facetmerge(const void *p1, const void *p2) {
896 0 : const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
897 :
898 0 : if (a->mergetype != b->mergetype)
899 0 : return (a->mergetype < b->mergetype ? 1 : -1); /* select MRGcoplanar (1) before MRGconcave (3) */
900 0 : else if (a->mergetype == MRGanglecoplanar)
901 0 : return (a->angle > b->angle ? 1 : -1); /* if MRGanglecoplanar, select coplanar merge (1.0) before sharp merge (-0.5) */
902 : else
903 0 : return (a->distance < b->distance ? 1 : -1); /* select flat (0.0) merge before wide (1e-10) merge */
904 : } /* compare_facetmerge */
905 :
906 : /*-<a href="qh-merge_r.htm#TOC"
907 : >-------------------------------</a><a name="comparevisit">-</a>
908 :
909 : qh_comparevisit( vertexA, vertexB )
910 : used by qsort() to order vertices by their visitid
911 :
912 : notes:
913 : only called by qh_find_newvertex
914 : */
915 0 : int qh_comparevisit(const void *p1, const void *p2) {
916 0 : const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
917 :
918 0 : if (a->visitid > b->visitid)
919 0 : return 1;
920 0 : return -1;
921 : } /* comparevisit */
922 :
923 : /*-<a href="qh-merge_r.htm#TOC"
924 : >-------------------------------</a><a name="copynonconvex">-</a>
925 :
926 : qh_copynonconvex(qh, atridge )
927 : set non-convex flag on other ridges (if any) between same neighbors
928 :
929 : notes:
930 : may be faster if use smaller ridge set
931 :
932 : design:
933 : for each ridge of atridge's top facet
934 : if ridge shares the same neighbor
935 : set nonconvex flag
936 : */
937 0 : void qh_copynonconvex(qhT *qh, ridgeT *atridge) {
938 : facetT *facet, *otherfacet;
939 : ridgeT *ridge, **ridgep;
940 :
941 0 : facet= atridge->top;
942 0 : otherfacet= atridge->bottom;
943 0 : atridge->nonconvex= False;
944 0 : FOREACHridge_(facet->ridges) {
945 0 : if (otherfacet == ridge->top || otherfacet == ridge->bottom) {
946 0 : if (ridge != atridge) {
947 0 : ridge->nonconvex= True;
948 0 : trace4((qh, qh->ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d between f%d and f%d\n",
949 : atridge->id, ridge->id, facet->id, otherfacet->id));
950 0 : break;
951 : }
952 : }
953 : }
954 0 : } /* copynonconvex */
955 :
956 : /*-<a href="qh-merge_r.htm#TOC"
957 : >-------------------------------</a><a name="degen_redundant_facet">-</a>
958 :
959 : qh_degen_redundant_facet(qh, facet )
960 : check for a degenerate (too few neighbors) or redundant (subset of vertices) facet
961 :
962 : notes:
963 : called at end of qh_mergefacet, qh_renamevertex, and qh_reducevertices
964 : bumps vertex_visit
965 : called if a facet was redundant but no longer is (qh_merge_degenredundant)
966 : qh_appendmergeset() only appends first reference to facet (i.e., redundant)
967 : see: qh_test_redundant_neighbors, qh_maydropneighbor
968 :
969 : design:
970 : test for redundant neighbor
971 : test for degenerate facet
972 : */
973 1 : void qh_degen_redundant_facet(qhT *qh, facetT *facet) {
974 : vertexT *vertex, **vertexp;
975 : facetT *neighbor, **neighborp;
976 :
977 1 : trace3((qh, qh->ferr, 3028, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",
978 : facet->id));
979 1 : if (facet->flipped) {
980 0 : trace2((qh, qh->ferr, 3074, "qh_degen_redundant_facet: f%d is flipped, will merge later\n", facet->id));
981 0 : return;
982 : }
983 5 : FOREACHneighbor_(facet) {
984 4 : if (neighbor->flipped) /* disallow merge of non-flipped into flipped, neighbor will be merged later */
985 0 : continue;
986 4 : if (neighbor->visible) {
987 0 : qh_fprintf(qh, qh->ferr, 6357, "qhull internal error (qh_degen_redundant_facet): facet f%d has deleted neighbor f%d (qh.visible_list)\n",
988 : facet->id, neighbor->id);
989 0 : qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
990 : }
991 4 : qh->vertex_visit++;
992 16 : FOREACHvertex_(neighbor->vertices)
993 12 : vertex->visitid= qh->vertex_visit;
994 7 : FOREACHvertex_(facet->vertices) {
995 7 : if (vertex->visitid != qh->vertex_visit)
996 4 : break;
997 : }
998 4 : if (!vertex) {
999 0 : trace2((qh, qh->ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id));
1000 0 : qh_appendmergeset(qh, facet, neighbor, MRGredundant, 0.0, 1.0);
1001 0 : return;
1002 : }
1003 : }
1004 1 : if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {
1005 0 : qh_appendmergeset(qh, facet, facet, MRGdegen, 0.0, 1.0);
1006 0 : trace2((qh, qh->ferr, 2016, "qh_degen_redundant_facet: f%d is degenerate.\n", facet->id));
1007 : }
1008 : } /* degen_redundant_facet */
1009 :
1010 :
1011 : /*-<a href="qh-merge_r.htm#TOC"
1012 : >-------------------------------</a><a name="delridge_merge">-</a>
1013 :
1014 : qh_delridge_merge(qh, ridge )
1015 : delete ridge due to a merge
1016 :
1017 : notes:
1018 : only called by merge_r.c (qh_mergeridges, qh_renameridgevertex)
1019 : ridges also freed in qh_freeqhull and qh_mergecycle_ridges
1020 :
1021 : design:
1022 : if needed, moves ridge.nonconvex to another ridge
1023 : sets vertex.delridge for qh_reducevertices
1024 : deletes ridge from qh.vertex_mergeset
1025 : deletes ridge from its neighboring facets
1026 : frees up its memory
1027 : */
1028 20048 : void qh_delridge_merge(qhT *qh, ridgeT *ridge) {
1029 : vertexT *vertex, **vertexp;
1030 : mergeT *merge;
1031 : int merge_i, merge_n;
1032 :
1033 20048 : trace3((qh, qh->ferr, 3036, "qh_delridge_merge: delete ridge r%d between f%d and f%d\n",
1034 : ridge->id, ridge->top->id, ridge->bottom->id));
1035 20048 : if (ridge->nonconvex)
1036 0 : qh_copynonconvex(qh, ridge);
1037 60144 : FOREACHvertex_(ridge->vertices)
1038 40096 : vertex->delridge= True;
1039 20048 : FOREACHmerge_i_(qh, qh->vertex_mergeset) {
1040 0 : if (merge->ridge1 == ridge || merge->ridge2 == ridge) {
1041 0 : trace3((qh, qh->ferr, 3029, "qh_delridge_merge: drop merge of v%d into v%d (dist %2.2g r%d r%d) due to deleted, duplicated ridge r%d\n",
1042 : merge->vertex1->id, merge->vertex2->id, merge->distance, merge->ridge1->id, merge->ridge2->id, ridge->id));
1043 0 : if (merge->ridge1 == ridge)
1044 0 : merge->ridge2->mergevertex= False;
1045 : else
1046 0 : merge->ridge1->mergevertex= False;
1047 0 : qh_setdelnth(qh, qh->vertex_mergeset, merge_i);
1048 0 : merge_i--; merge_n--; /* next merge after deleted */
1049 : }
1050 : }
1051 20048 : qh_setdel(ridge->top->ridges, ridge);
1052 20048 : qh_setdel(ridge->bottom->ridges, ridge);
1053 20048 : qh_delridge(qh, ridge);
1054 20048 : } /* delridge_merge */
1055 :
1056 :
1057 : /*-<a href="qh-merge_r.htm#TOC"
1058 : >-------------------------------</a><a name="drop_mergevertex">-</a>
1059 :
1060 : qh_drop_mergevertex(qh, merge )
1061 :
1062 : clear mergevertex flags for ridges of a vertex merge
1063 : */
1064 0 : void qh_drop_mergevertex(qhT *qh, mergeT *merge)
1065 : {
1066 0 : if (merge->mergetype == MRGvertices) {
1067 0 : merge->ridge1->mergevertex= False;
1068 0 : merge->ridge1->mergevertex2= True;
1069 0 : merge->ridge2->mergevertex= False;
1070 0 : merge->ridge2->mergevertex2= True;
1071 0 : trace3((qh, qh->ferr, 3032, "qh_drop_mergevertex: unset mergevertex for r%d and r%d due to dropped vertex merge v%d to v%d. Sets mergevertex2\n",
1072 : merge->ridge1->id, merge->ridge2->id, merge->vertex1->id, merge->vertex2->id));
1073 : }
1074 0 : } /* drop_mergevertex */
1075 :
1076 : /*-<a href="qh-merge_r.htm#TOC"
1077 : >-------------------------------</a><a name="find_newvertex">-</a>
1078 :
1079 : qh_find_newvertex(qh, oldvertex, vertices, ridges )
1080 : locate new vertex for renaming old vertex
1081 : vertices is a set of possible new vertices
1082 : vertices sorted by number of deleted ridges
1083 :
1084 : returns:
1085 : newvertex or NULL
1086 : each ridge includes both newvertex and oldvertex
1087 : vertices without oldvertex sorted by number of deleted ridges
1088 : qh.vertex_visit updated
1089 : sets v.seen
1090 :
1091 : notes:
1092 : called by qh_redundant_vertex due to vertex->delridge and qh_rename_sharedvertex
1093 : sets vertex->visitid to 0..setsize() for vertices
1094 : new vertex is in one of the ridges
1095 : renaming will not cause a duplicate ridge
1096 : renaming will minimize the number of deleted ridges
1097 : newvertex may not be adjacent in the dual (though unlikely)
1098 :
1099 : design:
1100 : for each vertex in vertices
1101 : set vertex->visitid to number of ridges
1102 : remove unvisited vertices
1103 : set qh.vertex_visit above all possible values
1104 : sort vertices by number of ridges (minimize ridges that need renaming
1105 : add each ridge to qh.hash_table
1106 : for each vertex in vertices
1107 : find the first vertex that would not cause a duplicate ridge after a rename
1108 : */
1109 0 : vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges) {
1110 : vertexT *vertex, **vertexp;
1111 : setT *newridges;
1112 : ridgeT *ridge, **ridgep;
1113 : int size, hashsize;
1114 : int hash;
1115 : unsigned int maxvisit;
1116 :
1117 : #ifndef qh_NOtrace
1118 0 : if (qh->IStracing >= 4) {
1119 0 : qh_fprintf(qh, qh->ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
1120 : oldvertex->id);
1121 0 : FOREACHvertex_(vertices)
1122 0 : qh_fprintf(qh, qh->ferr, 8064, "v%d ", vertex->id);
1123 0 : FOREACHridge_(ridges)
1124 0 : qh_fprintf(qh, qh->ferr, 8065, "r%d ", ridge->id);
1125 0 : qh_fprintf(qh, qh->ferr, 8066, "\n");
1126 : }
1127 : #endif
1128 0 : FOREACHridge_(ridges) {
1129 0 : FOREACHvertex_(ridge->vertices)
1130 0 : vertex->seen= False;
1131 : }
1132 0 : FOREACHvertex_(vertices) {
1133 0 : vertex->visitid= 0; /* v.visitid will be number of ridges */
1134 0 : vertex->seen= True;
1135 : }
1136 0 : FOREACHridge_(ridges) {
1137 0 : FOREACHvertex_(ridge->vertices) {
1138 0 : if (vertex->seen)
1139 0 : vertex->visitid++;
1140 : }
1141 : }
1142 0 : FOREACHvertex_(vertices) {
1143 0 : if (!vertex->visitid) {
1144 0 : qh_setdelnth(qh, vertices, SETindex_(vertices,vertex));
1145 0 : vertexp--; /* repeat since deleted this vertex */
1146 : }
1147 : }
1148 0 : maxvisit= (unsigned int)qh_setsize(qh, ridges);
1149 0 : maximize_(qh->vertex_visit, maxvisit);
1150 0 : if (!qh_setsize(qh, vertices)) {
1151 0 : trace4((qh, qh->ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",
1152 : oldvertex->id));
1153 0 : return NULL;
1154 : }
1155 0 : qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(qh, vertices),
1156 : sizeof(vertexT *), qh_comparevisit);
1157 : /* can now use qh->vertex_visit */
1158 0 : if (qh->PRINTstatistics) {
1159 0 : size= qh_setsize(qh, vertices);
1160 0 : zinc_(Zintersect);
1161 0 : zadd_(Zintersecttot, size);
1162 0 : zmax_(Zintersectmax, size);
1163 : }
1164 0 : hashsize= qh_newhashtable(qh, qh_setsize(qh, ridges));
1165 0 : FOREACHridge_(ridges)
1166 0 : qh_hashridge(qh, qh->hash_table, hashsize, ridge, oldvertex);
1167 0 : FOREACHvertex_(vertices) {
1168 0 : newridges= qh_vertexridges(qh, vertex, !qh_ALL);
1169 0 : FOREACHridge_(newridges) {
1170 0 : if (qh_hashridge_find(qh, qh->hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
1171 0 : zinc_(Zvertexridge);
1172 0 : break;
1173 : }
1174 : }
1175 0 : qh_settempfree(qh, &newridges);
1176 0 : if (!ridge)
1177 0 : break; /* found a rename */
1178 : }
1179 0 : if (vertex) {
1180 : /* counted in qh_renamevertex */
1181 0 : trace2((qh, qh->ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
1182 : vertex->id, oldvertex->id, qh_setsize(qh, vertices), qh_setsize(qh, ridges)));
1183 : }else {
1184 0 : zinc_(Zfindfail);
1185 0 : trace0((qh, qh->ferr, 14, "qh_find_newvertex: no vertex for renaming v%d (all duplicated ridges) during p%d\n",
1186 : oldvertex->id, qh->furthest_id));
1187 : }
1188 0 : qh_setfree(qh, &qh->hash_table);
1189 0 : return vertex;
1190 : } /* find_newvertex */
1191 :
1192 : /*-<a href="qh-geom2_r.htm#TOC"
1193 : >-------------------------------</a><a name="findbest_pinchedvertex">-</a>
1194 :
1195 : qh_findbest_pinchedvertex(qh, merge, apex, nearestp, distp )
1196 : Determine the best pinched vertex to rename as its nearest neighboring vertex
1197 : Renaming will remove a duplicate MRGdupridge in newfacet_list
1198 :
1199 : returns:
1200 : pinched vertex (either apex or subridge), nearest vertex (subridge or neighbor vertex), and the distance between them
1201 :
1202 : notes:
1203 : only called by qh_getpinchedmerges
1204 : assumes qh.VERTEXneighbors
1205 : see qh_findbest_ridgevertex
1206 :
1207 : design:
1208 : if the facets have the same vertices
1209 : return the nearest vertex pair
1210 : else
1211 : the subridge is the intersection of the two new facets minus the apex
1212 : the subridge consists of qh.hull_dim-2 horizon vertices
1213 : the subridge is also a matched ridge for the new facets (its duplicate)
1214 : determine the nearest vertex to the apex
1215 : determine the nearest pair of subridge vertices
1216 : for each vertex in the subridge
1217 : determine the nearest neighbor vertex (not in the subridge)
1218 : */
1219 0 : vertexT *qh_findbest_pinchedvertex(qhT *qh, mergeT *merge, vertexT *apex, vertexT **nearestp, coordT *distp /* qh.newfacet_list */) {
1220 : vertexT *vertex, **vertexp, *vertexA, **vertexAp;
1221 0 : vertexT *bestvertex= NULL, *bestpinched= NULL;
1222 : setT *subridge, *maybepinched;
1223 0 : coordT dist, bestdist= REALmax;
1224 0 : coordT pincheddist= (qh->ONEmerge+qh->DISTround)*qh_RATIOpinchedsubridge;
1225 :
1226 0 : if (!merge->facet1->simplicial || !merge->facet2->simplicial) {
1227 0 : qh_fprintf(qh, qh->ferr, 6351, "qhull internal error (qh_findbest_pinchedvertex): expecting merge of adjacent, simplicial new facets. f%d or f%d is not simplicial\n",
1228 0 : merge->facet1->id, merge->facet2->id);
1229 0 : qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2);
1230 : }
1231 0 : subridge= qh_vertexintersect_new(qh, merge->facet1->vertices, merge->facet2->vertices); /* new setT. No error_exit() */
1232 0 : if (qh_setsize(qh, subridge) == qh->hull_dim) { /* duplicate vertices */
1233 0 : bestdist= qh_vertex_bestdist2(qh, subridge, &bestvertex, &bestpinched);
1234 0 : if(bestvertex == apex) {
1235 0 : bestvertex= bestpinched;
1236 0 : bestpinched= apex;
1237 : }
1238 : }else {
1239 0 : qh_setdel(subridge, apex);
1240 0 : if (qh_setsize(qh, subridge) != qh->hull_dim - 2) {
1241 0 : qh_fprintf(qh, qh->ferr, 6409, "qhull internal error (qh_findbest_pinchedvertex): expecting subridge of qh.hull_dim-2 vertices for the intersection of new facets f%d and f%d minus their apex. Got %d vertices\n",
1242 0 : merge->facet1->id, merge->facet2->id, qh_setsize(qh, subridge));
1243 0 : qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2);
1244 : }
1245 0 : FOREACHvertex_(subridge) {
1246 0 : dist= qh_pointdist(vertex->point, apex->point, qh->hull_dim);
1247 0 : if (dist < bestdist) {
1248 0 : bestpinched= apex;
1249 0 : bestvertex= vertex;
1250 0 : bestdist= dist;
1251 : }
1252 : }
1253 0 : if (bestdist > pincheddist) {
1254 0 : FOREACHvertex_(subridge) {
1255 0 : FOREACHvertexA_(subridge) {
1256 0 : if (vertexA->id > vertex->id) { /* once per vertex pair, do not compare addresses */
1257 0 : dist= qh_pointdist(vertexA->point, vertex->point, qh->hull_dim);
1258 0 : if (dist < bestdist) {
1259 0 : bestpinched= vertexA;
1260 0 : bestvertex= vertex;
1261 0 : bestdist= dist;
1262 : }
1263 : }
1264 : }
1265 : }
1266 : }
1267 0 : if (bestdist > pincheddist) {
1268 0 : FOREACHvertexA_(subridge) {
1269 0 : maybepinched= qh_neighbor_vertices(qh, vertexA, subridge); /* subridge and apex tested above */
1270 0 : FOREACHvertex_(maybepinched) {
1271 0 : dist= qh_pointdist(vertex->point, vertexA->point, qh->hull_dim);
1272 0 : if (dist < bestdist) {
1273 0 : bestvertex= vertex;
1274 0 : bestpinched= vertexA;
1275 0 : bestdist= dist;
1276 : }
1277 : }
1278 0 : qh_settempfree(qh, &maybepinched);
1279 : }
1280 : }
1281 : }
1282 0 : *distp= bestdist;
1283 0 : qh_setfree(qh, &subridge); /* qh_err_exit not called since allocated */
1284 0 : if (!bestvertex) { /* should never happen if qh.hull_dim > 2 */
1285 0 : qh_fprintf(qh, qh->ferr, 6274, "qhull internal error (qh_findbest_pinchedvertex): did not find best vertex for subridge of dupridge between f%d and f%d, while processing p%d\n", merge->facet1->id, merge->facet2->id, qh->furthest_id);
1286 0 : qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2);
1287 : }
1288 0 : *nearestp= bestvertex;
1289 0 : trace2((qh, qh->ferr, 2061, "qh_findbest_pinchedvertex: best pinched p%d(v%d) and vertex p%d(v%d) are closest (%2.2g) for duplicate subridge between f%d and f%d\n",
1290 : qh_pointid(qh, bestpinched->point), bestpinched->id, qh_pointid(qh, bestvertex->point), bestvertex->id, bestdist, merge->facet1->id, merge->facet2->id));
1291 0 : return bestpinched;
1292 : } /* findbest_pinchedvertex */
1293 :
1294 : /*-<a href="qh-geom2_r.htm#TOC"
1295 : >-------------------------------</a><a name="findbest_ridgevertex">-</a>
1296 :
1297 : qh_findbest_ridgevertex(qh, ridge, pinchedp, distp )
1298 : Determine the best vertex/pinched-vertex to merge for ridges with the same vertices
1299 :
1300 : returns:
1301 : vertex, pinched vertex, and the distance between them
1302 :
1303 : notes:
1304 : assumes qh.hull_dim>=3
1305 : see qh_findbest_pinchedvertex
1306 :
1307 : */
1308 0 : vertexT *qh_findbest_ridgevertex(qhT *qh, ridgeT *ridge, vertexT **pinchedp, coordT *distp) {
1309 : vertexT *bestvertex;
1310 :
1311 0 : *distp= qh_vertex_bestdist2(qh, ridge->vertices, &bestvertex, pinchedp);
1312 0 : trace4((qh, qh->ferr, 4069, "qh_findbest_ridgevertex: best pinched p%d(v%d) and vertex p%d(v%d) are closest (%2.2g) for duplicated ridge r%d (same vertices) between f%d and f%d\n",
1313 : qh_pointid(qh, (*pinchedp)->point), (*pinchedp)->id, qh_pointid(qh, bestvertex->point), bestvertex->id, *distp, ridge->id, ridge->top->id, ridge->bottom->id));
1314 0 : return bestvertex;
1315 : } /* findbest_ridgevertex */
1316 :
1317 : /*-<a href="qh-merge_r.htm#TOC"
1318 : >-------------------------------</a><a name="findbest_test">-</a>
1319 :
1320 : qh_findbest_test(qh, testcentrum, facet, neighbor, &bestfacet, &dist, &mindist, &maxdist )
1321 : test neighbor of facet for qh_findbestneighbor()
1322 : if testcentrum,
1323 : tests centrum (assumes it is defined)
1324 : else
1325 : tests vertices
1326 : initially *bestfacet==NULL and *dist==REALmax
1327 :
1328 : returns:
1329 : if a better facet (i.e., vertices/centrum of facet closer to neighbor)
1330 : updates bestfacet, dist, mindist, and maxdist
1331 :
1332 : notes:
1333 : called by qh_findbestneighbor
1334 : ignores pairs of flipped facets, unless that's all there is
1335 : */
1336 6 : void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor,
1337 : facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
1338 : realT dist, mindist, maxdist;
1339 :
1340 6 : if (facet->flipped && neighbor->flipped && *bestfacet && !(*bestfacet)->flipped)
1341 0 : return; /* do not merge flipped into flipped facets */
1342 6 : if (testcentrum) {
1343 0 : zzinc_(Zbestdist);
1344 0 : qh_distplane(qh, facet->center, neighbor, &dist);
1345 0 : dist *= qh->hull_dim; /* estimate furthest vertex */
1346 0 : if (dist < 0) {
1347 0 : maxdist= 0;
1348 0 : mindist= dist;
1349 0 : dist= -dist;
1350 : }else {
1351 0 : mindist= 0;
1352 0 : maxdist= dist;
1353 : }
1354 : }else
1355 6 : dist= qh_getdistance(qh, facet, neighbor, &mindist, &maxdist);
1356 6 : if (dist < *distp) {
1357 4 : *bestfacet= neighbor;
1358 4 : *mindistp= mindist;
1359 4 : *maxdistp= maxdist;
1360 4 : *distp= dist;
1361 : }
1362 : } /* findbest_test */
1363 :
1364 : /*-<a href="qh-merge_r.htm#TOC"
1365 : >-------------------------------</a><a name="findbestneighbor">-</a>
1366 :
1367 : qh_findbestneighbor(qh, facet, dist, mindist, maxdist )
1368 : finds best neighbor (least dist) of a facet for merging
1369 :
1370 : returns:
1371 : returns min and max distances and their max absolute value
1372 :
1373 : notes:
1374 : error if qh_ASvoronoi
1375 : avoids merging old into new
1376 : assumes ridge->nonconvex only set on one ridge between a pair of facets
1377 : could use an early out predicate but not worth it
1378 :
1379 : design:
1380 : if a large facet
1381 : will test centrum
1382 : else
1383 : will test vertices
1384 : if a large facet
1385 : test nonconvex neighbors for best merge
1386 : else
1387 : test all neighbors for the best merge
1388 : if testing centrum
1389 : get distance information
1390 : */
1391 2 : facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
1392 2 : facetT *neighbor, **neighborp, *bestfacet= NULL;
1393 : ridgeT *ridge, **ridgep;
1394 2 : boolT nonconvex= True, testcentrum= False;
1395 2 : int size= qh_setsize(qh, facet->vertices);
1396 :
1397 2 : if(qh->CENTERtype==qh_ASvoronoi){
1398 0 : qh_fprintf(qh, qh->ferr, 6272, "qhull internal error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id);
1399 0 : qh_errexit(qh, qh_ERRqhull, facet, NULL);
1400 : }
1401 2 : *distp= REALmax;
1402 2 : if (size > qh_BESTcentrum2 * qh->hull_dim + qh_BESTcentrum) {
1403 0 : testcentrum= True;
1404 0 : zinc_(Zbestcentrum);
1405 0 : if (!facet->center)
1406 0 : facet->center= qh_getcentrum(qh, facet);
1407 : }
1408 2 : if (size > qh->hull_dim + qh_BESTnonconvex) {
1409 0 : FOREACHridge_(facet->ridges) {
1410 0 : if (ridge->nonconvex) {
1411 0 : neighbor= otherfacet_(ridge, facet);
1412 0 : qh_findbest_test(qh, testcentrum, facet, neighbor,
1413 : &bestfacet, distp, mindistp, maxdistp);
1414 : }
1415 : }
1416 : }
1417 2 : if (!bestfacet) {
1418 2 : nonconvex= False;
1419 8 : FOREACHneighbor_(facet)
1420 6 : qh_findbest_test(qh, testcentrum, facet, neighbor,
1421 : &bestfacet, distp, mindistp, maxdistp);
1422 : }
1423 2 : if (!bestfacet) {
1424 0 : qh_fprintf(qh, qh->ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
1425 0 : qh_errexit(qh, qh_ERRqhull, facet, NULL);
1426 : }
1427 2 : if (testcentrum)
1428 0 : qh_getdistance(qh, facet, bestfacet, mindistp, maxdistp);
1429 2 : trace3((qh, qh->ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
1430 : bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
1431 2 : return(bestfacet);
1432 : } /* findbestneighbor */
1433 :
1434 :
1435 : /*-<a href="qh-merge_r.htm#TOC"
1436 : >-------------------------------</a><a name="flippedmerges">-</a>
1437 :
1438 : qh_flippedmerges(qh, facetlist, wasmerge )
1439 : merge flipped facets into best neighbor
1440 : assumes qh.facet_mergeset at top of temporary stack
1441 :
1442 : returns:
1443 : no flipped facets on facetlist
1444 : sets wasmerge if merge occurred
1445 : degen/redundant merges passed through
1446 :
1447 : notes:
1448 : othermerges not needed since qh.facet_mergeset is empty before & after
1449 : keep it in case of change
1450 :
1451 : design:
1452 : append flipped facets to qh.facetmergeset
1453 : for each flipped merge
1454 : find best neighbor
1455 : merge facet into neighbor
1456 : merge degenerate and redundant facets
1457 : remove flipped merges from qh.facet_mergeset
1458 : */
1459 12197 : void qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge) {
1460 : facetT *facet, *neighbor, *facet1;
1461 : realT dist, mindist, maxdist;
1462 : mergeT *merge, **mergep;
1463 : setT *othermerges;
1464 12197 : int nummerge= 0, numdegen= 0;
1465 :
1466 12197 : trace4((qh, qh->ferr, 4024, "qh_flippedmerges: begin\n"));
1467 72484 : FORALLfacet_(facetlist) {
1468 60287 : if (facet->flipped && !facet->visible)
1469 0 : qh_appendmergeset(qh, facet, facet, MRGflip, 0.0, 1.0);
1470 : }
1471 12197 : othermerges= qh_settemppop(qh);
1472 12197 : if(othermerges != qh->facet_mergeset) {
1473 0 : qh_fprintf(qh, qh->ferr, 6392, "qhull internal error (qh_flippedmerges): facet_mergeset (%d merges) not at top of tempstack (%d merges)\n",
1474 : qh_setsize(qh, qh->facet_mergeset), qh_setsize(qh, othermerges));
1475 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
1476 : }
1477 12197 : qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
1478 12197 : qh_settemppush(qh, othermerges);
1479 12197 : FOREACHmerge_(othermerges) {
1480 0 : facet1= merge->facet1;
1481 0 : if (merge->mergetype != MRGflip || facet1->visible)
1482 0 : continue;
1483 0 : if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
1484 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
1485 0 : neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
1486 0 : trace0((qh, qh->ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
1487 : facet1->id, neighbor->id, dist, qh->furthest_id));
1488 0 : qh_mergefacet(qh, facet1, neighbor, merge->mergetype, &mindist, &maxdist, !qh_MERGEapex);
1489 0 : nummerge++;
1490 0 : if (qh->PRINTstatistics) {
1491 0 : zinc_(Zflipped);
1492 0 : wadd_(Wflippedtot, dist);
1493 0 : wmax_(Wflippedmax, dist);
1494 : }
1495 : }
1496 12197 : FOREACHmerge_(othermerges) {
1497 0 : if (merge->facet1->visible || merge->facet2->visible)
1498 0 : qh_memfree(qh, merge, (int)sizeof(mergeT)); /* invalidates merge and othermerges */
1499 : else
1500 0 : qh_setappend(qh, &qh->facet_mergeset, merge);
1501 : }
1502 12197 : qh_settempfree(qh, &othermerges);
1503 12197 : numdegen += qh_merge_degenredundant(qh); /* somewhat better here than after each flipped merge -- qtest.sh 10 '500 C1,2e-13 D4' 'd Qbb' */
1504 12197 : if (nummerge)
1505 0 : *wasmerge= True;
1506 12197 : trace1((qh, qh->ferr, 1010, "qh_flippedmerges: merged %d flipped and %d degenredundant facets into a good neighbor\n",
1507 : nummerge, numdegen));
1508 12197 : } /* flippedmerges */
1509 :
1510 :
1511 : /*-<a href="qh-merge_r.htm#TOC"
1512 : >-------------------------------</a><a name="forcedmerges">-</a>
1513 :
1514 : qh_forcedmerges(qh, wasmerge )
1515 : merge dupridges
1516 : calls qh_check_dupridge to report an error on wide merges
1517 : assumes qh_settemppop is qh.facet_mergeset
1518 :
1519 : returns:
1520 : removes all dupridges on facet_mergeset
1521 : wasmerge set if merge
1522 : qh.facet_mergeset may include non-forced merges(none for now)
1523 : qh.degen_mergeset includes degen/redun merges
1524 :
1525 : notes:
1526 : called by qh_premerge
1527 : dupridges occur when the horizon is pinched,
1528 : i.e. a subridge occurs in more than two horizon ridges.
1529 : could rename vertices that pinch the horizon
1530 : assumes qh_merge_degenredundant() has not be called
1531 : othermerges isn't needed since facet_mergeset is empty afterwards
1532 : keep it in case of change
1533 :
1534 : design:
1535 : for each dupridge
1536 : find current facets by chasing f.replace links
1537 : check for wide merge due to dupridge
1538 : determine best direction for facet
1539 : merge one facet into the other
1540 : remove dupridges from qh.facet_mergeset
1541 : */
1542 12197 : void qh_forcedmerges(qhT *qh, boolT *wasmerge) {
1543 : facetT *facet1, *facet2, *merging, *merged, *newfacet;
1544 : mergeT *merge, **mergep;
1545 : realT dist, mindist, maxdist, dist2, mindist2, maxdist2;
1546 : setT *othermerges;
1547 12197 : int nummerge=0, numflip=0, numdegen= 0;
1548 12197 : boolT wasdupridge= False;
1549 :
1550 12197 : if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
1551 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
1552 12197 : trace3((qh, qh->ferr, 3054, "qh_forcedmerges: merge dupridges\n"));
1553 12197 : othermerges= qh_settemppop(qh); /* was facet_mergeset */
1554 12197 : if (qh->facet_mergeset != othermerges ) {
1555 0 : qh_fprintf(qh, qh->ferr, 6279, "qhull internal error (qh_forcedmerges): qh_settemppop (size %d) is not qh->facet_mergeset (size %d)\n",
1556 : qh_setsize(qh, othermerges), qh_setsize(qh, qh->facet_mergeset));
1557 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
1558 : }
1559 12197 : qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
1560 12197 : qh_settemppush(qh, othermerges);
1561 12197 : FOREACHmerge_(othermerges) {
1562 0 : if (merge->mergetype != MRGdupridge)
1563 0 : continue;
1564 0 : wasdupridge= True;
1565 0 : if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
1566 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
1567 0 : facet1= qh_getreplacement(qh, merge->facet1); /* must exist, no qh_merge_degenredunant */
1568 0 : facet2= qh_getreplacement(qh, merge->facet2); /* previously merged facet, if any */
1569 0 : if (facet1 == facet2)
1570 0 : continue;
1571 0 : if (!qh_setin(facet2->neighbors, facet1)) {
1572 0 : qh_fprintf(qh, qh->ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a dupridge but as f%d and f%d they are no longer neighbors\n",
1573 0 : merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);
1574 0 : qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
1575 : }
1576 0 : dist= qh_getdistance(qh, facet1, facet2, &mindist, &maxdist);
1577 0 : dist2= qh_getdistance(qh, facet2, facet1, &mindist2, &maxdist2);
1578 0 : qh_check_dupridge(qh, facet1, dist, facet2, dist2);
1579 0 : if (dist < dist2) {
1580 0 : if (facet2->flipped && !facet1->flipped && dist2 < qh_WIDEdupridge*(qh->ONEmerge+qh->DISTround)) { /* prefer merge of flipped facet */
1581 0 : merging= facet2;
1582 0 : merged= facet1;
1583 0 : dist= dist2;
1584 0 : mindist= mindist2;
1585 0 : maxdist= maxdist2;
1586 : }else {
1587 0 : merging= facet1;
1588 0 : merged= facet2;
1589 : }
1590 : }else {
1591 0 : if (facet1->flipped && !facet2->flipped && dist < qh_WIDEdupridge*(qh->ONEmerge+qh->DISTround)) { /* prefer merge of flipped facet */
1592 0 : merging= facet1;
1593 0 : merged= facet2;
1594 : }else {
1595 0 : merging= facet2;
1596 0 : merged= facet1;
1597 0 : dist= dist2;
1598 0 : mindist= mindist2;
1599 0 : maxdist= maxdist2;
1600 : }
1601 : }
1602 0 : qh_mergefacet(qh, merging, merged, merge->mergetype, &mindist, &maxdist, !qh_MERGEapex);
1603 0 : numdegen += qh_merge_degenredundant(qh); /* better here than at end -- qtest.sh 10 '500 C1,2e-13 D4' 'd Qbb' */
1604 0 : if (facet1->flipped) {
1605 0 : zinc_(Zmergeflipdup);
1606 0 : numflip++;
1607 : }else
1608 0 : nummerge++;
1609 0 : if (qh->PRINTstatistics) {
1610 0 : zinc_(Zduplicate);
1611 0 : wadd_(Wduplicatetot, dist);
1612 0 : wmax_(Wduplicatemax, dist);
1613 : }
1614 : }
1615 12197 : FOREACHmerge_(othermerges) {
1616 0 : if (merge->mergetype == MRGdupridge)
1617 0 : qh_memfree(qh, merge, (int)sizeof(mergeT)); /* invalidates merge and othermerges */
1618 : else
1619 0 : qh_setappend(qh, &qh->facet_mergeset, merge);
1620 : }
1621 12197 : qh_settempfree(qh, &othermerges);
1622 12197 : if (wasdupridge) {
1623 0 : FORALLnew_facets {
1624 0 : if (newfacet->dupridge) {
1625 0 : newfacet->dupridge= False;
1626 0 : newfacet->mergeridge= False;
1627 0 : newfacet->mergeridge2= False;
1628 0 : if (qh_setsize(qh, newfacet->neighbors) < qh->hull_dim) { /* not tested for MRGdupridge */
1629 0 : qh_appendmergeset(qh, newfacet, newfacet, MRGdegen, 0.0, 1.0);
1630 0 : trace2((qh, qh->ferr, 2107, "qh_forcedmerges: dupridge f%d is degenerate with fewer than %d neighbors\n",
1631 : newfacet->id, qh->hull_dim));
1632 : }
1633 : }
1634 : }
1635 0 : numdegen += qh_merge_degenredundant(qh);
1636 : }
1637 12197 : if (nummerge || numflip) {
1638 0 : *wasmerge= True;
1639 0 : trace1((qh, qh->ferr, 1011, "qh_forcedmerges: merged %d facets, %d flipped facets, and %d degenredundant facets across dupridges\n",
1640 : nummerge, numflip, numdegen));
1641 : }
1642 12197 : } /* forcedmerges */
1643 :
1644 :
1645 : /*-<a href="qh-merge_r.htm#TOC"
1646 : >-------------------------------</a><a name="freemergesets">-</a>
1647 :
1648 : qh_freemergesets(qh )
1649 : free the merge sets
1650 :
1651 : notes:
1652 : matches qh_initmergesets
1653 : */
1654 14666 : void qh_freemergesets(qhT *qh) {
1655 :
1656 14666 : if (!qh->facet_mergeset || !qh->degen_mergeset || !qh->vertex_mergeset) {
1657 0 : qh_fprintf(qh, qh->ferr, 6388, "qhull internal error (qh_freemergesets): expecting mergesets. Got a NULL mergeset, qh.facet_mergeset (0x%x), qh.degen_mergeset (0x%x), qh.vertex_mergeset (0x%x)\n",
1658 : qh->facet_mergeset, qh->degen_mergeset, qh->vertex_mergeset);
1659 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
1660 : }
1661 14666 : if (!SETempty_(qh->facet_mergeset) || !SETempty_(qh->degen_mergeset) || !SETempty_(qh->vertex_mergeset)) {
1662 0 : qh_fprintf(qh, qh->ferr, 6389, "qhull internal error (qh_freemergesets): expecting empty mergesets. Got qh.facet_mergeset (%d merges), qh.degen_mergeset (%d merges), qh.vertex_mergeset (%d merges)\n",
1663 : qh_setsize(qh, qh->facet_mergeset), qh_setsize(qh, qh->degen_mergeset), qh_setsize(qh, qh->vertex_mergeset));
1664 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
1665 : }
1666 14666 : qh_settempfree(qh, &qh->facet_mergeset);
1667 14666 : qh_settempfree(qh, &qh->vertex_mergeset);
1668 14666 : qh_settempfree(qh, &qh->degen_mergeset);
1669 14666 : } /* freemergesets */
1670 :
1671 : /*-<a href="qh-merge_r.htm#TOC"
1672 : >-------------------------------</a><a name="getmergeset">-</a>
1673 :
1674 : qh_getmergeset(qh, facetlist )
1675 : determines nonconvex facets on facetlist
1676 : tests !tested ridges and nonconvex ridges of !tested facets
1677 :
1678 : returns:
1679 : returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
1680 : all ridges tested
1681 :
1682 : notes:
1683 : facetlist is qh.facet_newlist, use qh_getmergeset_initial for all facets
1684 : assumes no nonconvex ridges with both facets tested
1685 : uses facet->tested/ridge->tested to prevent duplicate tests
1686 : can not limit tests to modified ridges since the centrum changed
1687 : uses qh.visit_id
1688 :
1689 : design:
1690 : for each facet on facetlist
1691 : for each ridge of facet
1692 : if untested ridge
1693 : test ridge for convexity
1694 : if non-convex
1695 : append ridge to qh.facet_mergeset
1696 : sort qh.facet_mergeset by mergetype and angle or distance
1697 : */
1698 1 : void qh_getmergeset(qhT *qh, facetT *facetlist) {
1699 : facetT *facet, *neighbor, **neighborp;
1700 : ridgeT *ridge, **ridgep;
1701 : int nummerges;
1702 : boolT simplicial;
1703 :
1704 1 : nummerges= qh_setsize(qh, qh->facet_mergeset);
1705 1 : trace4((qh, qh->ferr, 4026, "qh_getmergeset: started.\n"));
1706 1 : qh->visit_id++;
1707 7 : FORALLfacet_(facetlist) {
1708 6 : if (facet->tested)
1709 5 : continue;
1710 1 : facet->visitid= qh->visit_id;
1711 5 : FOREACHneighbor_(facet)
1712 4 : neighbor->seen= False;
1713 : /* facet must be non-simplicial due to merge to qh.facet_newlist */
1714 5 : FOREACHridge_(facet->ridges) {
1715 4 : if (ridge->tested && !ridge->nonconvex)
1716 0 : continue;
1717 : /* if r.tested & r.nonconvex, need to retest and append merge */
1718 4 : neighbor= otherfacet_(ridge, facet);
1719 4 : if (neighbor->seen) { /* another ridge for this facet-neighbor pair was already tested in this loop */
1720 0 : ridge->tested= True;
1721 0 : ridge->nonconvex= False; /* only one ridge is marked nonconvex per facet-neighbor pair */
1722 4 : }else if (neighbor->visitid != qh->visit_id) {
1723 4 : neighbor->seen= True;
1724 4 : ridge->nonconvex= False;
1725 4 : simplicial= False;
1726 4 : if (ridge->simplicialbot && ridge->simplicialtop)
1727 2 : simplicial= True;
1728 4 : if (qh_test_appendmerge(qh, facet, neighbor, simplicial))
1729 0 : ridge->nonconvex= True;
1730 4 : ridge->tested= True;
1731 : }
1732 : }
1733 1 : facet->tested= True;
1734 : }
1735 1 : nummerges= qh_setsize(qh, qh->facet_mergeset);
1736 1 : if (qh->ANGLEmerge)
1737 0 : qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_anglemerge);
1738 : else
1739 1 : qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_facetmerge);
1740 1 : nummerges += qh_setsize(qh, qh->degen_mergeset);
1741 1 : if (qh->POSTmerging) {
1742 0 : zadd_(Zmergesettot2, nummerges);
1743 : }else {
1744 1 : zadd_(Zmergesettot, nummerges);
1745 1 : zmax_(Zmergesetmax, nummerges);
1746 : }
1747 1 : trace2((qh, qh->ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
1748 1 : } /* getmergeset */
1749 :
1750 :
1751 : /*-<a href="qh-merge_r.htm#TOC"
1752 : >-------------------------------</a><a name="getmergeset_initial">-</a>
1753 :
1754 : qh_getmergeset_initial(qh, facetlist )
1755 : determine initial qh.facet_mergeset for facets
1756 : tests all facet/neighbor pairs on facetlist
1757 :
1758 : returns:
1759 : sorted qh.facet_mergeset with nonconvex ridges
1760 : sets facet->tested, ridge->tested, and ridge->nonconvex
1761 :
1762 : notes:
1763 : uses visit_id, assumes ridge->nonconvex is False
1764 : see qh_getmergeset
1765 :
1766 : design:
1767 : for each facet on facetlist
1768 : for each untested neighbor of facet
1769 : test facet and neighbor for convexity
1770 : if non-convex
1771 : append merge to qh.facet_mergeset
1772 : mark one of the ridges as nonconvex
1773 : sort qh.facet_mergeset by mergetype and angle or distance
1774 : */
1775 12197 : void qh_getmergeset_initial(qhT *qh, facetT *facetlist) {
1776 : facetT *facet, *neighbor, **neighborp;
1777 : ridgeT *ridge, **ridgep;
1778 : int nummerges;
1779 : boolT simplicial;
1780 :
1781 12197 : qh->visit_id++;
1782 72484 : FORALLfacet_(facetlist) {
1783 60287 : facet->visitid= qh->visit_id;
1784 289389 : FOREACHneighbor_(facet) {
1785 229102 : if (neighbor->visitid != qh->visit_id) {
1786 168815 : simplicial= False; /* ignores r.simplicialtop/simplicialbot. Need to test horizon facets */
1787 168815 : if (facet->simplicial && neighbor->simplicial)
1788 59296 : simplicial= True;
1789 168815 : if (qh_test_appendmerge(qh, facet, neighbor, simplicial)) {
1790 1 : FOREACHridge_(neighbor->ridges) {
1791 0 : if (facet == otherfacet_(ridge, neighbor)) {
1792 0 : ridge->nonconvex= True;
1793 0 : break; /* only one ridge is marked nonconvex */
1794 : }
1795 : }
1796 : }
1797 : }
1798 : }
1799 60287 : facet->tested= True;
1800 199407 : FOREACHridge_(facet->ridges)
1801 139120 : ridge->tested= True;
1802 : }
1803 12197 : nummerges= qh_setsize(qh, qh->facet_mergeset);
1804 12197 : if (qh->ANGLEmerge)
1805 0 : qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_anglemerge);
1806 : else
1807 12197 : qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_facetmerge);
1808 12197 : nummerges += qh_setsize(qh, qh->degen_mergeset);
1809 12197 : if (qh->POSTmerging) {
1810 0 : zadd_(Zmergeinittot2, nummerges);
1811 : }else {
1812 12197 : zadd_(Zmergeinittot, nummerges);
1813 12197 : zmax_(Zmergeinitmax, nummerges);
1814 : }
1815 12197 : trace2((qh, qh->ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
1816 12197 : } /* getmergeset_initial */
1817 :
1818 : /*-<a href="qh-merge_r.htm#TOC"
1819 : >-------------------------------</a><a name="getpinchedmerges">-</a>
1820 :
1821 : qh_getpinchedmerges(qh, apex, maxdist, iscoplanar )
1822 : get pinched merges for dupridges in qh.facet_mergeset
1823 : qh.NEWtentative==True
1824 : qh.newfacet_list with apex
1825 : qh.horizon_list is attached to qh.visible_list instead of qh.newfacet_list
1826 : maxdist for vertex-facet of a dupridge
1827 : qh.facet_mergeset is empty
1828 : qh.vertex_mergeset is a temporary set
1829 :
1830 : returns:
1831 : False if nearest vertex would increase facet width by more than maxdist or qh_WIDEpinched
1832 : True and iscoplanar, if the pinched vertex is the apex (i.e., make the apex a coplanar point)
1833 : True and !iscoplanar, if should merge a pinched vertex of a dupridge
1834 : qh.vertex_mergeset contains one or more MRGsubridge with a pinched vertex and a nearby, neighboring vertex
1835 : qh.facet_mergeset is empty
1836 :
1837 : notes:
1838 : called by qh_buildcone_mergepinched
1839 : hull_dim >= 3
1840 : a pinched vertex is in a dupridge and the horizon
1841 : selects the pinched vertex that is closest to its neighbor
1842 :
1843 : design:
1844 : for each dupridge
1845 : determine the best pinched vertex to be merged into a neighboring vertex
1846 : if merging the pinched vertex would produce a wide merge (qh_WIDEpinched)
1847 : ignore pinched vertex with a warning, and use qh_merge_degenredundant instead
1848 : else
1849 : append the pinched vertex to vertex_mergeset for merging
1850 : */
1851 0 : boolT qh_getpinchedmerges(qhT *qh, vertexT *apex, coordT maxdupdist, boolT *iscoplanar /* qh.newfacet_list, qh.vertex_mergeset */) {
1852 0 : mergeT *merge, **mergep, *bestmerge= NULL;
1853 0 : vertexT *nearest, *pinched, *bestvertex= NULL, *bestpinched= NULL;
1854 : boolT result;
1855 0 : coordT dist, prevdist, bestdist= REALmax/(qh_RATIOcoplanarapex+1.0); /* allow *3.0 */
1856 : realT ratio;
1857 :
1858 0 : trace2((qh, qh->ferr, 2062, "qh_getpinchedmerges: try to merge pinched vertices for dupridges in new facets with apex p%d(v%d) max dupdist %2.2g\n",
1859 : qh_pointid(qh, apex->point), apex->id, maxdupdist));
1860 0 : *iscoplanar= False;
1861 0 : prevdist= fmax_(qh->ONEmerge + qh->DISTround, qh->MINoutside + qh->DISTround);
1862 0 : maximize_(prevdist, qh->max_outside);
1863 0 : maximize_(prevdist, -qh->min_vertex);
1864 0 : qh_mark_dupridges(qh, qh->newfacet_list, !qh_ALL); /* qh.facet_mergeset, creates ridges */
1865 : /* qh_mark_dupridges is called a second time in qh_premerge */
1866 0 : FOREACHmerge_(qh->facet_mergeset) { /* read-only */
1867 0 : if (merge->mergetype != MRGdupridge) {
1868 0 : qh_fprintf(qh, qh->ferr, 6393, "qhull internal error (qh_getpinchedmerges): expecting MRGdupridge from qh_mark_dupridges. Got merge f%d f%d type %d\n",
1869 0 : getid_(merge->facet1), getid_(merge->facet2), merge->mergetype);
1870 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
1871 : }
1872 : /* dist is distance between vertices */
1873 0 : pinched= qh_findbest_pinchedvertex(qh, merge, apex, &nearest, &dist /* qh.newfacet_list */);
1874 0 : if (pinched == apex && dist < qh_RATIOcoplanarapex*bestdist) { /* prefer coplanar apex since it always works */
1875 0 : bestdist= dist/qh_RATIOcoplanarapex;
1876 0 : bestmerge= merge;
1877 0 : bestpinched= pinched;
1878 0 : bestvertex= nearest;
1879 0 : }else if (dist < bestdist) {
1880 0 : bestdist= dist;
1881 0 : bestmerge= merge;
1882 0 : bestpinched= pinched;
1883 0 : bestvertex= nearest;
1884 : }
1885 : }
1886 0 : result= False;
1887 0 : if (bestmerge && bestdist < maxdupdist) {
1888 0 : ratio= bestdist / prevdist;
1889 0 : if (ratio > qh_WIDEpinched) {
1890 0 : if (bestmerge->facet1->mergehorizon || bestmerge->facet2->mergehorizon) { /* e.g., rbox 175 C3,2e-13 t1539182828 | qhull d */
1891 0 : trace1((qh, qh->ferr, 1051, "qh_getpinchedmerges: dupridge (MRGdupridge) of coplanar horizon would produce a wide merge (%.0fx) due to pinched vertices v%d and v%d (dist %2.2g) for f%d and f%d. qh_mergecycle_all will merge one or both facets\n",
1892 : ratio, bestpinched->id, bestvertex->id, bestdist, bestmerge->facet1->id, bestmerge->facet2->id));
1893 : }else {
1894 0 : qh_fprintf(qh, qh->ferr, 7081, "qhull precision warning (qh_getpinchedmerges): pinched vertices v%d and v%d (dist %2.2g, %.0fx) would produce a wide merge for f%d and f%d. Will merge dupridge instead\n",
1895 0 : bestpinched->id, bestvertex->id, bestdist, ratio, bestmerge->facet1->id, bestmerge->facet2->id);
1896 : }
1897 : }else {
1898 0 : if (bestpinched == apex) {
1899 0 : trace2((qh, qh->ferr, 2063, "qh_getpinchedmerges: will make the apex a coplanar point. apex p%d(v%d) is the nearest vertex to v%d on dupridge. Dist %2.2g\n",
1900 : qh_pointid(qh, apex->point), apex->id, bestvertex->id, bestdist*qh_RATIOcoplanarapex));
1901 0 : qh->coplanar_apex= apex->point;
1902 0 : *iscoplanar= True;
1903 0 : result= True;
1904 0 : }else if (qh_setin(bestmerge->facet1->vertices, bestpinched) != qh_setin(bestmerge->facet2->vertices, bestpinched)) { /* pinched in one facet but not the other facet */
1905 0 : trace2((qh, qh->ferr, 2064, "qh_getpinchedmerges: will merge new facets to resolve dupridge between f%d and f%d with pinched v%d and v%d\n",
1906 : bestmerge->facet1->id, bestmerge->facet2->id, bestpinched->id, bestvertex->id));
1907 0 : qh_appendvertexmerge(qh, bestpinched, bestvertex, MRGsubridge, bestdist, NULL, NULL);
1908 0 : result= True;
1909 : }else {
1910 0 : trace2((qh, qh->ferr, 2065, "qh_getpinchedmerges: will merge pinched v%d into v%d to resolve dupridge between f%d and f%d\n",
1911 : bestpinched->id, bestvertex->id, bestmerge->facet1->id, bestmerge->facet2->id));
1912 0 : qh_appendvertexmerge(qh, bestpinched, bestvertex, MRGsubridge, bestdist, NULL, NULL);
1913 0 : result= True;
1914 : }
1915 : }
1916 : }
1917 : /* delete MRGdupridge, qh_mark_dupridges is called a second time in qh_premerge */
1918 0 : while ((merge= (mergeT *)qh_setdellast(qh->facet_mergeset)))
1919 0 : qh_memfree(qh, merge, (int)sizeof(mergeT));
1920 0 : return result;
1921 : }/* getpinchedmerges */
1922 :
1923 : /*-<a href="qh-merge_r.htm#TOC"
1924 : >-------------------------------</a><a name="hasmerge">-</a>
1925 :
1926 : qh_hasmerge( mergeset, mergetype, facetA, facetB )
1927 : True if mergeset has mergetype for facetA and facetB
1928 : */
1929 0 : boolT qh_hasmerge(setT *mergeset, mergeType type, facetT *facetA, facetT *facetB) {
1930 : mergeT *merge, **mergep;
1931 :
1932 0 : FOREACHmerge_(mergeset) {
1933 0 : if (merge->mergetype == type) {
1934 0 : if (merge->facet1 == facetA && merge->facet2 == facetB)
1935 0 : return True;
1936 0 : if (merge->facet1 == facetB && merge->facet2 == facetA)
1937 0 : return True;
1938 : }
1939 : }
1940 0 : return False;
1941 : }/* hasmerge */
1942 :
1943 : /*-<a href="qh-merge_r.htm#TOC"
1944 : >-------------------------------</a><a name="hashridge">-</a>
1945 :
1946 : qh_hashridge(qh, hashtable, hashsize, ridge, oldvertex )
1947 : add ridge to hashtable without oldvertex
1948 :
1949 : notes:
1950 : assumes hashtable is large enough
1951 :
1952 : design:
1953 : determine hash value for ridge without oldvertex
1954 : find next empty slot for ridge
1955 : */
1956 0 : void qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
1957 : int hash;
1958 : ridgeT *ridgeA;
1959 :
1960 0 : hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, oldvertex);
1961 : while (True) {
1962 0 : if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
1963 0 : SETelem_(hashtable, hash)= ridge;
1964 0 : break;
1965 0 : }else if (ridgeA == ridge)
1966 0 : break;
1967 0 : if (++hash == hashsize)
1968 0 : hash= 0;
1969 : }
1970 0 : } /* hashridge */
1971 :
1972 :
1973 : /*-<a href="qh-merge_r.htm#TOC"
1974 : >-------------------------------</a><a name="hashridge_find">-</a>
1975 :
1976 : qh_hashridge_find(qh, hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
1977 : returns matching ridge without oldvertex in hashtable
1978 : for ridge without vertex
1979 : if oldvertex is NULL
1980 : matches with any one skip
1981 :
1982 : returns:
1983 : matching ridge or NULL
1984 : if no match,
1985 : if ridge already in table
1986 : hashslot= -1
1987 : else
1988 : hashslot= next NULL index
1989 :
1990 : notes:
1991 : assumes hashtable is large enough
1992 : can't match ridge to itself
1993 :
1994 : design:
1995 : get hash value for ridge without vertex
1996 : for each hashslot
1997 : return match if ridge matches ridgeA without oldvertex
1998 : */
1999 0 : ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge,
2000 : vertexT *vertex, vertexT *oldvertex, int *hashslot) {
2001 : int hash;
2002 : ridgeT *ridgeA;
2003 :
2004 0 : *hashslot= 0;
2005 0 : zinc_(Zhashridge);
2006 0 : hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, vertex);
2007 0 : while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
2008 0 : if (ridgeA == ridge)
2009 0 : *hashslot= -1;
2010 : else {
2011 0 : zinc_(Zhashridgetest);
2012 0 : if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))
2013 0 : return ridgeA;
2014 : }
2015 0 : if (++hash == hashsize)
2016 0 : hash= 0;
2017 : }
2018 0 : if (!*hashslot)
2019 0 : *hashslot= hash;
2020 0 : return NULL;
2021 : } /* hashridge_find */
2022 :
2023 :
2024 : /*-<a href="qh-merge_r.htm#TOC"
2025 : >-------------------------------</a><a name="initmergesets">-</a>
2026 :
2027 : qh_initmergesets(qh )
2028 : initialize the merge sets
2029 : if 'all', include qh.degen_mergeset
2030 :
2031 : notes:
2032 : matches qh_freemergesets
2033 : */
2034 14666 : void qh_initmergesets(qhT *qh /* qh.facet_mergeset,degen_mergeset,vertex_mergeset */) {
2035 :
2036 14666 : if (qh->facet_mergeset || qh->degen_mergeset || qh->vertex_mergeset) {
2037 0 : qh_fprintf(qh, qh->ferr, 6386, "qhull internal error (qh_initmergesets): expecting NULL mergesets. Got qh.facet_mergeset (0x%x), qh.degen_mergeset (0x%x), qh.vertex_mergeset (0x%x)\n",
2038 : qh->facet_mergeset, qh->degen_mergeset, qh->vertex_mergeset);
2039 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
2040 : }
2041 14666 : qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
2042 14666 : qh->vertex_mergeset= qh_settemp(qh, qh->TEMPsize);
2043 14666 : qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize); /* last temporary set for qh_forcedmerges */
2044 14666 : } /* initmergesets */
2045 :
2046 : /*-<a href="qh-merge_r.htm#TOC"
2047 : >-------------------------------</a><a name="makeridges">-</a>
2048 :
2049 : qh_makeridges(qh, facet )
2050 : creates explicit ridges between simplicial facets
2051 :
2052 : returns:
2053 : facet with ridges and without qh_MERGEridge
2054 : ->simplicial is False
2055 : if facet was tested, new ridges are tested
2056 :
2057 : notes:
2058 : allows qh_MERGEridge flag
2059 : uses existing ridges
2060 : duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)
2061 :
2062 : see:
2063 : qh_mergecycle_ridges()
2064 : qh_rename_adjacentvertex for qh_merge_pinchedvertices
2065 :
2066 : design:
2067 : look for qh_MERGEridge neighbors
2068 : mark neighbors that already have ridges
2069 : for each unprocessed neighbor of facet
2070 : create a ridge for neighbor and facet
2071 : if any qh_MERGEridge neighbors
2072 : delete qh_MERGEridge flags (previously processed by qh_mark_dupridges)
2073 : */
2074 40096 : void qh_makeridges(qhT *qh, facetT *facet) {
2075 : facetT *neighbor, **neighborp;
2076 : ridgeT *ridge, **ridgep;
2077 : int neighbor_i, neighbor_n;
2078 40096 : boolT toporient, mergeridge= False;
2079 :
2080 40096 : if (!facet->simplicial)
2081 804 : return;
2082 39292 : trace4((qh, qh->ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));
2083 39292 : facet->simplicial= False;
2084 157168 : FOREACHneighbor_(facet) {
2085 117876 : if (neighbor == qh_MERGEridge)
2086 0 : mergeridge= True;
2087 : else
2088 117876 : neighbor->seen= False;
2089 : }
2090 92638 : FOREACHridge_(facet->ridges)
2091 53346 : otherfacet_(ridge, facet)->seen= True;
2092 157168 : FOREACHneighbor_i_(qh, facet) {
2093 117876 : if (neighbor == qh_MERGEridge)
2094 0 : continue; /* fixed by qh_mark_dupridges */
2095 117876 : else if (!neighbor->seen) { /* no current ridges */
2096 64530 : ridge= qh_newridge(qh);
2097 64530 : ridge->vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
2098 : neighbor_i, 0);
2099 64530 : toporient= (boolT)(facet->toporient ^ (neighbor_i & 0x1));
2100 64530 : if (toporient) {
2101 32125 : ridge->top= facet;
2102 32125 : ridge->bottom= neighbor;
2103 32125 : ridge->simplicialtop= True;
2104 32125 : ridge->simplicialbot= neighbor->simplicial;
2105 : }else {
2106 32405 : ridge->top= neighbor;
2107 32405 : ridge->bottom= facet;
2108 32405 : ridge->simplicialtop= neighbor->simplicial;
2109 32405 : ridge->simplicialbot= True;
2110 : }
2111 64530 : if (facet->tested && !mergeridge)
2112 12631 : ridge->tested= True;
2113 : #if 0 /* this also works */
2114 : flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);
2115 : if (facet->toporient ^ (skip1 & 0x1) ^ flip) {
2116 : ridge->top= neighbor;
2117 : ridge->bottom= facet;
2118 : ridge->simplicialtop= True;
2119 : ridge->simplicialbot= neighbor->simplicial;
2120 : }else {
2121 : ridge->top= facet;
2122 : ridge->bottom= neighbor;
2123 : ridge->simplicialtop= neighbor->simplicial;
2124 : ridge->simplicialbot= True;
2125 : }
2126 : #endif
2127 64530 : qh_setappend(qh, &(facet->ridges), ridge);
2128 64530 : trace5((qh, qh->ferr, 5005, "makeridges: appended r%d to ridges for f%d. Next is ridges for neighbor f%d\n",
2129 : ridge->id, facet->id, neighbor->id));
2130 64530 : qh_setappend(qh, &(neighbor->ridges), ridge);
2131 64530 : if (qh->ridge_id == qh->traceridge_id)
2132 0 : qh->traceridge= ridge;
2133 : }
2134 : }
2135 39292 : if (mergeridge) {
2136 0 : while (qh_setdel(facet->neighbors, qh_MERGEridge))
2137 : ; /* delete each one */
2138 : }
2139 : } /* makeridges */
2140 :
2141 :
2142 : /*-<a href="qh-merge_r.htm#TOC"
2143 : >-------------------------------</a><a name="mark_dupridges">-</a>
2144 :
2145 : qh_mark_dupridges(qh, facetlist, allmerges )
2146 : add duplicated ridges to qh.facet_mergeset
2147 : facet-dupridge is true if it contains a subridge shared by more than one new facet
2148 : for each such facet, one has a neighbor marked qh_MERGEridge
2149 : allmerges is true if merging dupridges
2150 : allmerges is false if merging pinched vertices followed by retry addpoint
2151 : qh_mark_dupridges will be called again if pinched vertices not found
2152 :
2153 : returns:
2154 : dupridges on qh.facet_mergeset (MRGdupridge)
2155 : f.mergeridge and f.mergeridge2 set for facet
2156 : f.mergeridge set for neighbor
2157 : if allmerges is true
2158 : make ridges for facets with dupridges as marked by qh_MERGEridge and both sides facet->dupridge
2159 : removes qh_MERGEridge from neighbor sets
2160 :
2161 : notes:
2162 : called by qh_premerge and qh_getpinchedmerges
2163 : dupridges are due to duplicate subridges
2164 : i.e. a subridge occurs in more than two horizon ridges.
2165 : i.e., a ridge has more than two neighboring facets
2166 : dupridges occur in at least two cases
2167 : 1) a pinched horizon with nearly adjacent vertices -> merge the vertices (qh_getpinchedmerges)
2168 : 2) more than one newfacet for a horizon face -> merge coplanar facets (qh_premerge)
2169 : qh_matchdupridge previously identified the furthest apart pair of facets to retain
2170 : they must have a matching subridge and the same orientation
2171 : only way to set facet->mergeridge and mergeridge2
2172 : uses qh.visit_id
2173 :
2174 : design:
2175 : for all facets on facetlist
2176 : if facet contains a dupridge
2177 : for each neighbor of facet
2178 : if neighbor marked qh_MERGEridge (one side of the merge)
2179 : set facet->mergeridge
2180 : else
2181 : if neighbor contains a dupridge
2182 : and the back link is qh_MERGEridge
2183 : append dupridge to qh.facet_mergeset
2184 : exit if !allmerges for repeating qh_mark_dupridges later
2185 : for each dupridge
2186 : make ridge sets in preparation for merging
2187 : remove qh_MERGEridge from neighbor set
2188 : for each dupridge
2189 : restore the missing neighbor from the neighbor set that was qh_MERGEridge
2190 : add the missing ridge for this neighbor
2191 : */
2192 12197 : void qh_mark_dupridges(qhT *qh, facetT *facetlist, boolT allmerges) {
2193 : facetT *facet, *neighbor, **neighborp;
2194 12197 : int nummerge=0;
2195 : mergeT *merge, **mergep;
2196 :
2197 12197 : trace4((qh, qh->ferr, 4028, "qh_mark_dupridges: identify dupridges in facetlist f%d, allmerges? %d\n",
2198 : facetlist->id, allmerges));
2199 72484 : FORALLfacet_(facetlist) { /* not necessary for first call */
2200 60287 : facet->mergeridge2= False;
2201 60287 : facet->mergeridge= False;
2202 : }
2203 72484 : FORALLfacet_(facetlist) {
2204 60287 : if (facet->dupridge) {
2205 0 : FOREACHneighbor_(facet) {
2206 0 : if (neighbor == qh_MERGEridge) {
2207 0 : facet->mergeridge= True;
2208 0 : continue;
2209 : }
2210 0 : if (neighbor->dupridge) {
2211 0 : if (!qh_setin(neighbor->neighbors, facet)) { /* i.e., it is qh_MERGEridge, neighbors are distinct */
2212 0 : qh_appendmergeset(qh, facet, neighbor, MRGdupridge, 0.0, 1.0);
2213 0 : facet->mergeridge2= True;
2214 0 : facet->mergeridge= True;
2215 0 : nummerge++;
2216 0 : }else if (qh_setequal(facet->vertices, neighbor->vertices)) { /* neighbors are the same except for horizon and qh_MERGEridge, see QH7085 */
2217 0 : trace3((qh, qh->ferr, 3043, "qh_mark_dupridges): dupridge due to duplicate vertices for subridges f%d and f%d\n",
2218 : facet->id, neighbor->id));
2219 0 : qh_appendmergeset(qh, facet, neighbor, MRGdupridge, 0.0, 1.0);
2220 0 : facet->mergeridge2= True;
2221 0 : facet->mergeridge= True;
2222 0 : nummerge++;
2223 0 : break; /* same for all neighbors */
2224 : }
2225 : }
2226 : }
2227 : }
2228 : }
2229 12197 : if (!nummerge)
2230 12197 : return;
2231 0 : if (!allmerges) {
2232 0 : trace1((qh, qh->ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges (MRGdupridge) for qh_getpinchedmerges\n", nummerge));
2233 0 : return;
2234 : }
2235 0 : trace1((qh, qh->ferr, 1048, "qh_mark_dupridges: found %d duplicated ridges (MRGdupridge) for qh_premerge. Prepare facets for merging\n", nummerge));
2236 : /* make ridges in preparation for merging */
2237 0 : FORALLfacet_(facetlist) {
2238 0 : if (facet->mergeridge && !facet->mergeridge2)
2239 0 : qh_makeridges(qh, facet);
2240 : }
2241 0 : trace3((qh, qh->ferr, 3075, "qh_mark_dupridges: restore missing neighbors and ridges due to qh_MERGEridge\n"));
2242 0 : FOREACHmerge_(qh->facet_mergeset) { /* restore the missing neighbors */
2243 0 : if (merge->mergetype == MRGdupridge) { /* only between simplicial facets */
2244 0 : if (merge->facet2->mergeridge2 && qh_setin(merge->facet2->neighbors, merge->facet1)) {
2245 : /* Due to duplicate or multiple subridges, e.g., ../eg/qtest.sh t712682 '200 s W1e-13 C1,1e-13 D5' 'd'
2246 : merge->facet1: - neighboring facets: f27779 f59186 f59186 f59186 MERGEridge f59186
2247 : merge->facet2: - neighboring facets: f27779 f59100 f59100 f59100 f59100 f59100
2248 : or, ../eg/qtest.sh 100 '500 s W1e-13 C1,1e-13 D4' 'd'
2249 : both facets will be degenerate after merge, consider for special case handling
2250 : */
2251 0 : qh_fprintf(qh, qh->ferr, 6361, "qhull topological error (qh_mark_dupridges): multiple dupridges for f%d and f%d, including reverse\n",
2252 0 : merge->facet1->id, merge->facet2->id);
2253 0 : qh_errexit2(qh, qh_ERRtopology, merge->facet1, merge->facet2);
2254 : }else
2255 0 : qh_setappend(qh, &merge->facet2->neighbors, merge->facet1);
2256 0 : qh_makeridges(qh, merge->facet1); /* and the missing ridges */
2257 : }
2258 : }
2259 : } /* mark_dupridges */
2260 :
2261 : /*-<a href="qh-merge_r.htm#TOC"
2262 : >-------------------------------</a><a name="maybe_duplicateridge">-</a>
2263 :
2264 : qh_maybe_duplicateridge(qh, ridge )
2265 : add MRGvertices if neighboring facet has another ridge with the same vertices
2266 :
2267 : returns:
2268 : adds rename requests to qh.vertex_mergeset
2269 :
2270 : notes:
2271 : called by qh_renamevertex
2272 : nop if 2-D
2273 : expensive test
2274 : Duplicate ridges may lead to new facets with same vertex set (QH7084), will try merging vertices
2275 : same as qh_maybe_duplicateridges
2276 :
2277 : design:
2278 : for the two neighbors
2279 : if non-simplicial
2280 : for each ridge with the same first and last vertices (max id and min id)
2281 : if the remaining vertices are the same
2282 : get the closest pair of vertices
2283 : add to vertex_mergeset for merging
2284 : */
2285 0 : void qh_maybe_duplicateridge(qhT *qh, ridgeT *ridgeA) {
2286 : ridgeT *ridge, **ridgep;
2287 : vertexT *vertex, *pinched;
2288 : facetT *neighbor;
2289 : coordT dist;
2290 0 : int i, k, last= qh->hull_dim-2;
2291 :
2292 0 : if (qh->hull_dim < 3 )
2293 0 : return;
2294 :
2295 0 : for (neighbor= ridgeA->top, i=0; i<2; neighbor= ridgeA->bottom, i++) {
2296 0 : if (!neighbor->simplicial && neighbor->nummerge > 0) { /* skip degenerate neighbors with both new and old vertices that will be merged */
2297 0 : FOREACHridge_(neighbor->ridges) {
2298 0 : if (ridge != ridgeA && SETfirst_(ridge->vertices) == SETfirst_(ridgeA->vertices)) {
2299 0 : if (SETelem_(ridge->vertices, last) == SETelem_(ridgeA->vertices, last)) {
2300 0 : for (k=1; k<last; k++) {
2301 0 : if (SETelem_(ridge->vertices, k) != SETelem_(ridgeA->vertices, k))
2302 0 : break;
2303 : }
2304 0 : if (k == last) {
2305 0 : vertex= qh_findbest_ridgevertex(qh, ridge, &pinched, &dist);
2306 0 : trace2((qh, qh->ferr, 2069, "qh_maybe_duplicateridge: will merge v%d into v%d (dist %2.2g) due to duplicate ridges r%d/r%d with the same vertices. mergevertex set\n",
2307 : pinched->id, vertex->id, dist, ridgeA->id, ridge->id, ridgeA->top->id, ridgeA->bottom->id, ridge->top->id, ridge->bottom->id));
2308 0 : qh_appendvertexmerge(qh, pinched, vertex, MRGvertices, dist, ridgeA, ridge);
2309 0 : ridge->mergevertex= True; /* disables check for duplicate vertices in qh_checkfacet */
2310 0 : ridgeA->mergevertex= True;
2311 : }
2312 : }
2313 : }
2314 : }
2315 : }
2316 : }
2317 : } /* maybe_duplicateridge */
2318 :
2319 : /*-<a href="qh-merge_r.htm#TOC"
2320 : >-------------------------------</a><a name="maybe_duplicateridges">-</a>
2321 :
2322 : qh_maybe_duplicateridges(qh, facet )
2323 : if Q15, add MRGvertices if facet has ridges with the same vertices
2324 :
2325 : returns:
2326 : adds rename requests to qh.vertex_mergeset
2327 :
2328 : notes:
2329 : called at end of qh_mergefacet and qh_mergecycle_all
2330 : only enabled if qh.CHECKduplicates ('Q15') and 3-D or more
2331 : expensive test, not worth it
2332 : same as qh_maybe_duplicateridge
2333 :
2334 : design:
2335 : for all ridge pairs in facet
2336 : if the same first and last vertices (max id and min id)
2337 : if the remaining vertices are the same
2338 : get the closest pair of vertices
2339 : add to vertex_mergeset for merging
2340 : */
2341 20048 : void qh_maybe_duplicateridges(qhT *qh, facetT *facet) {
2342 : facetT *otherfacet;
2343 : ridgeT *ridge, *ridge2;
2344 : vertexT *vertex, *pinched;
2345 : coordT dist;
2346 20048 : int ridge_i, ridge_n, i, k, last_v= qh->hull_dim-2;
2347 :
2348 20048 : if (qh->hull_dim < 3 || !qh->CHECKduplicates)
2349 20048 : return;
2350 :
2351 0 : FOREACHridge_i_(qh, facet->ridges) {
2352 0 : otherfacet= otherfacet_(ridge, facet);
2353 0 : if (otherfacet->degenerate || otherfacet->redundant || otherfacet->dupridge || otherfacet->flipped) /* will merge */
2354 0 : continue;
2355 0 : for (i=ridge_i+1; i < ridge_n; i++) {
2356 0 : ridge2= SETelemt_(facet->ridges, i, ridgeT);
2357 0 : otherfacet= otherfacet_(ridge2, facet);
2358 0 : if (otherfacet->degenerate || otherfacet->redundant || otherfacet->dupridge || otherfacet->flipped) /* will merge */
2359 0 : continue;
2360 : /* optimize qh_setequal(ridge->vertices, ridge2->vertices) */
2361 0 : if (SETelem_(ridge->vertices, last_v) == SETelem_(ridge2->vertices, last_v)) { /* SETfirst is likely to be the same */
2362 0 : if (SETfirst_(ridge->vertices) == SETfirst_(ridge2->vertices)) {
2363 0 : for (k=1; k<last_v; k++) {
2364 0 : if (SETelem_(ridge->vertices, k) != SETelem_(ridge2->vertices, k))
2365 0 : break;
2366 : }
2367 0 : if (k == last_v) {
2368 0 : vertex= qh_findbest_ridgevertex(qh, ridge, &pinched, &dist);
2369 0 : if (ridge->top == ridge2->bottom && ridge->bottom == ridge2->top) {
2370 : /* proof that ridges may have opposite orientation */
2371 0 : trace2((qh, qh->ferr, 2088, "qh_maybe_duplicateridges: will merge v%d into v%d (dist %2.2g) due to opposite oriented ridges r%d/r%d for f%d and f%d\n",
2372 : pinched->id, vertex->id, dist, ridge->id, ridge2->id, ridge->top->id, ridge->bottom->id));
2373 : }else {
2374 0 : trace2((qh, qh->ferr, 2083, "qh_maybe_duplicateridges: will merge v%d into v%d (dist %2.2g) due to duplicate ridges with the same vertices r%d/r%d in merged facet f%d\n",
2375 : pinched->id, vertex->id, dist, ridge->id, ridge2->id, facet->id));
2376 : }
2377 0 : qh_appendvertexmerge(qh, pinched, vertex, MRGvertices, dist, ridge, ridge2);
2378 0 : ridge->mergevertex= True; /* disables check for duplicate vertices in qh_checkfacet */
2379 0 : ridge2->mergevertex= True;
2380 : }
2381 : }
2382 : }
2383 : }
2384 : }
2385 : } /* maybe_duplicateridges */
2386 :
2387 : /*-<a href="qh-merge_r.htm#TOC"
2388 : >-------------------------------</a><a name="maydropneighbor">-</a>
2389 :
2390 : qh_maydropneighbor(qh, facet )
2391 : drop neighbor relationship if ridge was deleted between a non-simplicial facet and its neighbors
2392 :
2393 : returns:
2394 : for deleted ridges
2395 : ridges made for simplicial neighbors
2396 : neighbor sets updated
2397 : appends degenerate facets to qh.facet_mergeset
2398 :
2399 : notes:
2400 : called by qh_renamevertex
2401 : assumes neighbors do not include qh_MERGEridge (qh_makeridges)
2402 : won't cause redundant facets since vertex inclusion is the same
2403 : may drop vertex and neighbor if no ridge
2404 : uses qh.visit_id
2405 :
2406 : design:
2407 : visit all neighbors with ridges
2408 : for each unvisited neighbor of facet
2409 : delete neighbor and facet from the non-simplicial neighbor sets
2410 : if neighbor becomes degenerate
2411 : append neighbor to qh.degen_mergeset
2412 : if facet is degenerate
2413 : append facet to qh.degen_mergeset
2414 : */
2415 0 : void qh_maydropneighbor(qhT *qh, facetT *facet) {
2416 : ridgeT *ridge, **ridgep;
2417 : facetT *neighbor, **neighborp;
2418 :
2419 0 : qh->visit_id++;
2420 0 : trace4((qh, qh->ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
2421 : facet->id));
2422 0 : if (facet->simplicial) {
2423 0 : qh_fprintf(qh, qh->ferr, 6278, "qhull internal error (qh_maydropneighbor): not valid for simplicial f%d while adding furthest p%d\n",
2424 : facet->id, qh->furthest_id);
2425 0 : qh_errexit(qh, qh_ERRqhull, facet, NULL);
2426 : }
2427 0 : FOREACHridge_(facet->ridges) {
2428 0 : ridge->top->visitid= qh->visit_id;
2429 0 : ridge->bottom->visitid= qh->visit_id;
2430 : }
2431 0 : FOREACHneighbor_(facet) {
2432 0 : if (neighbor->visible) {
2433 0 : qh_fprintf(qh, qh->ferr, 6358, "qhull internal error (qh_maydropneighbor): facet f%d has deleted neighbor f%d (qh.visible_list)\n",
2434 : facet->id, neighbor->id);
2435 0 : qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
2436 : }
2437 0 : if (neighbor->visitid != qh->visit_id) {
2438 0 : trace2((qh, qh->ferr, 2104, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors while adding furthest p%d\n",
2439 : facet->id, neighbor->id, qh->furthest_id));
2440 0 : if (neighbor->simplicial) {
2441 0 : qh_fprintf(qh, qh->ferr, 6280, "qhull internal error (qh_maydropneighbor): not valid for simplicial neighbor f%d of f%d while adding furthest p%d\n",
2442 : neighbor->id, facet->id, qh->furthest_id);
2443 0 : qh_errexit2(qh, qh_ERRqhull, neighbor, facet);
2444 : }
2445 0 : zinc_(Zdropneighbor);
2446 0 : qh_setdel(neighbor->neighbors, facet);
2447 0 : if (qh_setsize(qh, neighbor->neighbors) < qh->hull_dim) {
2448 0 : zinc_(Zdropdegen);
2449 0 : qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, 0.0, qh_ANGLEnone);
2450 0 : trace2((qh, qh->ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
2451 : }
2452 0 : qh_setdel(facet->neighbors, neighbor);
2453 0 : neighborp--; /* repeat, deleted a neighbor */
2454 : }
2455 : }
2456 0 : if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {
2457 0 : zinc_(Zdropdegen);
2458 0 : qh_appendmergeset(qh, facet, facet, MRGdegen, 0.0, qh_ANGLEnone);
2459 0 : trace2((qh, qh->ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
2460 : }
2461 0 : } /* maydropneighbor */
2462 :
2463 :
2464 : /*-<a href="qh-merge_r.htm#TOC"
2465 : >-------------------------------</a><a name="merge_degenredundant">-</a>
2466 :
2467 : qh_merge_degenredundant(qh)
2468 : merge all degenerate and redundant facets
2469 : qh.degen_mergeset contains merges from qh_test_degen_neighbors, qh_test_redundant_neighbors, and qh_degen_redundant_facet
2470 :
2471 : returns:
2472 : number of merges performed
2473 : resets facet->degenerate/redundant
2474 : if deleted (visible) facet has no neighbors
2475 : sets ->f.replace to NULL
2476 :
2477 : notes:
2478 : redundant merges happen before degenerate ones
2479 : merging and renaming vertices can result in degen/redundant facets
2480 : check for coplanar and convex neighbors afterwards
2481 :
2482 : design:
2483 : for each merge on qh.degen_mergeset
2484 : if redundant merge
2485 : if non-redundant facet merged into redundant facet
2486 : recheck facet for redundancy
2487 : else
2488 : merge redundant facet into other facet
2489 : */
2490 31569 : int qh_merge_degenredundant(qhT *qh) {
2491 : int size;
2492 : mergeT *merge;
2493 : facetT *bestneighbor, *facet1, *facet2, *facet3;
2494 : realT dist, mindist, maxdist;
2495 : vertexT *vertex, **vertexp;
2496 31569 : int nummerges= 0;
2497 : mergeType mergetype;
2498 : setT *mergedfacets;
2499 :
2500 31569 : trace2((qh, qh->ferr, 2095, "qh_merge_degenredundant: merge %d degenerate, redundant, and mirror facets\n",
2501 : qh_setsize(qh, qh->degen_mergeset)));
2502 31569 : mergedfacets= qh_settemp(qh, qh->TEMPsize);
2503 31569 : while ((merge= (mergeT *)qh_setdellast(qh->degen_mergeset))) {
2504 0 : facet1= merge->facet1;
2505 0 : facet2= merge->facet2;
2506 0 : mergetype= merge->mergetype;
2507 0 : qh_memfree(qh, merge, (int)sizeof(mergeT)); /* 'merge' is invalidated */
2508 0 : if (facet1->visible)
2509 0 : continue;
2510 0 : facet1->degenerate= False;
2511 0 : facet1->redundant= False;
2512 0 : if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
2513 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
2514 0 : if (mergetype == MRGredundant) {
2515 0 : zinc_(Zredundant);
2516 0 : facet3= qh_getreplacement(qh, facet2); /* the same facet if !facet2.visible */
2517 0 : if (!facet3) {
2518 0 : qh_fprintf(qh, qh->ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d is redundant but visible f%d has no replacement\n",
2519 0 : facet1->id, getid_(facet2));
2520 0 : qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
2521 : }
2522 0 : qh_setunique(qh, &mergedfacets, facet3);
2523 0 : if (facet1 == facet3) {
2524 0 : continue;
2525 : }
2526 0 : trace2((qh, qh->ferr, 2025, "qh_merge_degenredundant: merge redundant f%d into f%d (arg f%d)\n",
2527 : facet1->id, facet3->id, facet2->id));
2528 0 : qh_mergefacet(qh, facet1, facet3, mergetype, NULL, NULL, !qh_MERGEapex);
2529 : /* merge distance is already accounted for */
2530 0 : nummerges++;
2531 : }else { /* mergetype == MRGdegen or MRGmirror, other merges may have fixed */
2532 0 : if (!(size= qh_setsize(qh, facet1->neighbors))) {
2533 0 : zinc_(Zdelfacetdup);
2534 0 : trace2((qh, qh->ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id));
2535 0 : qh_willdelete(qh, facet1, NULL);
2536 0 : FOREACHvertex_(facet1->vertices) {
2537 0 : qh_setdel(vertex->neighbors, facet1);
2538 0 : if (!SETfirst_(vertex->neighbors)) {
2539 0 : zinc_(Zdegenvertex);
2540 0 : trace2((qh, qh->ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
2541 : vertex->id, facet1->id));
2542 0 : vertex->deleted= True;
2543 0 : qh_setappend(qh, &qh->del_vertices, vertex);
2544 : }
2545 : }
2546 0 : nummerges++;
2547 0 : }else if (size < qh->hull_dim) {
2548 0 : bestneighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
2549 0 : trace2((qh, qh->ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
2550 : facet1->id, size, bestneighbor->id, dist));
2551 0 : qh_mergefacet(qh, facet1, bestneighbor, mergetype, &mindist, &maxdist, !qh_MERGEapex);
2552 0 : nummerges++;
2553 0 : if (qh->PRINTstatistics) {
2554 0 : zinc_(Zdegen);
2555 0 : wadd_(Wdegentot, dist);
2556 0 : wmax_(Wdegenmax, dist);
2557 : }
2558 : } /* else, another merge fixed the degeneracy and redundancy tested */
2559 : }
2560 : }
2561 31569 : qh_settempfree(qh, &mergedfacets);
2562 31569 : return nummerges;
2563 : } /* merge_degenredundant */
2564 :
2565 : /*-<a href="qh-merge_r.htm#TOC"
2566 : >-------------------------------</a><a name="merge_nonconvex">-</a>
2567 :
2568 : qh_merge_nonconvex(qh, facet1, facet2, mergetype )
2569 : remove non-convex ridge between facet1 into facet2
2570 : mergetype gives why the facet's are non-convex
2571 :
2572 : returns:
2573 : merges one of the facets into the best neighbor
2574 :
2575 : notes:
2576 : mergetype is MRGcoplanar..MRGconvex
2577 :
2578 : design:
2579 : if one of the facets is a new facet
2580 : prefer merging new facet into old facet
2581 : find best neighbors for both facets
2582 : merge the nearest facet into its best neighbor
2583 : update the statistics
2584 : */
2585 1 : void qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) {
2586 : facetT *bestfacet, *bestneighbor, *neighbor, *merging, *merged;
2587 : realT dist, dist2, mindist, mindist2, maxdist, maxdist2;
2588 :
2589 1 : if (mergetype < MRGcoplanar || mergetype > MRGconcavecoplanar) {
2590 0 : qh_fprintf(qh, qh->ferr, 6398, "qhull internal error (qh_merge_nonconvex): expecting mergetype MRGcoplanar..MRGconcavecoplanar. Got merge f%d and f%d type %d\n",
2591 : facet1->id, facet2->id, mergetype);
2592 0 : qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
2593 : }
2594 1 : if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
2595 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
2596 1 : trace3((qh, qh->ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
2597 : zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
2598 : /* concave or coplanar */
2599 1 : if (!facet1->newfacet) {
2600 0 : bestfacet= facet2; /* avoid merging old facet if new is ok */
2601 0 : facet2= facet1;
2602 0 : facet1= bestfacet;
2603 : }else
2604 1 : bestfacet= facet1;
2605 1 : bestneighbor= qh_findbestneighbor(qh, bestfacet, &dist, &mindist, &maxdist);
2606 1 : neighbor= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2);
2607 1 : if (dist < dist2) {
2608 0 : merging= bestfacet;
2609 0 : merged= bestneighbor;
2610 1 : }else if (qh->AVOIDold && !facet2->newfacet
2611 0 : && ((mindist >= -qh->MAXcoplanar && maxdist <= qh->max_outside)
2612 0 : || dist * 1.5 < dist2)) {
2613 0 : zinc_(Zavoidold);
2614 0 : wadd_(Wavoidoldtot, dist);
2615 0 : wmax_(Wavoidoldmax, dist);
2616 0 : trace2((qh, qh->ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n",
2617 : facet2->id, dist2, facet1->id, dist2));
2618 0 : merging= bestfacet;
2619 0 : merged= bestneighbor;
2620 : }else {
2621 1 : merging= facet2;
2622 1 : merged= neighbor;
2623 1 : dist= dist2;
2624 1 : mindist= mindist2;
2625 1 : maxdist= maxdist2;
2626 : }
2627 1 : qh_mergefacet(qh, merging, merged, mergetype, &mindist, &maxdist, !qh_MERGEapex);
2628 : /* caller merges qh_degenredundant */
2629 1 : if (qh->PRINTstatistics) {
2630 0 : if (mergetype == MRGanglecoplanar) {
2631 0 : zinc_(Zacoplanar);
2632 0 : wadd_(Wacoplanartot, dist);
2633 0 : wmax_(Wacoplanarmax, dist);
2634 0 : }else if (mergetype == MRGconcave) {
2635 0 : zinc_(Zconcave);
2636 0 : wadd_(Wconcavetot, dist);
2637 0 : wmax_(Wconcavemax, dist);
2638 0 : }else if (mergetype == MRGconcavecoplanar) {
2639 0 : zinc_(Zconcavecoplanar);
2640 0 : wadd_(Wconcavecoplanartot, dist);
2641 0 : wmax_(Wconcavecoplanarmax, dist);
2642 : }else { /* MRGcoplanar */
2643 0 : zinc_(Zcoplanar);
2644 0 : wadd_(Wcoplanartot, dist);
2645 0 : wmax_(Wcoplanarmax, dist);
2646 : }
2647 : }
2648 1 : } /* merge_nonconvex */
2649 :
2650 : /*-<a href="qh-merge_r.htm#TOC"
2651 : >-------------------------------</a><a name="merge_pinchedvertices">-</a>
2652 :
2653 : qh_merge_pinchedvertices(qh, apex )
2654 : merge pinched vertices in qh.vertex_mergeset to avoid qh_forcedmerges of dupridges
2655 :
2656 : notes:
2657 : only called by qh_all_vertexmerges
2658 : hull_dim >= 3
2659 :
2660 : design:
2661 : make vertex neighbors if necessary
2662 : for each pinched vertex
2663 : determine the ridges for the pinched vertex (make ridges as needed)
2664 : merge the pinched vertex into the horizon vertex
2665 : merge the degenerate and redundant facets that result
2666 : check and resolve new dupridges
2667 : */
2668 0 : void qh_merge_pinchedvertices(qhT *qh, int apexpointid /* qh.newfacet_list */) {
2669 : mergeT *merge, *mergeA, **mergeAp;
2670 : vertexT *vertex, *vertex2;
2671 : realT dist;
2672 0 : boolT firstmerge= True;
2673 :
2674 0 : qh_vertexneighbors(qh);
2675 0 : if (qh->visible_list || qh->newfacet_list || qh->newvertex_list) {
2676 0 : qh_fprintf(qh, qh->ferr, 6402, "qhull internal error (qh_merge_pinchedvertices): qh.visible_list (f%d), newfacet_list (f%d), or newvertex_list (v%d) not empty\n",
2677 0 : getid_(qh->visible_list), getid_(qh->newfacet_list), getid_(qh->newvertex_list));
2678 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
2679 : }
2680 0 : qh->visible_list= qh->newfacet_list= qh->facet_tail;
2681 0 : qh->newvertex_list= qh->vertex_tail;
2682 0 : qh->isRenameVertex= True; /* disable duplicate ridge vertices check in qh_checkfacet */
2683 0 : while ((merge= qh_next_vertexmerge(qh /* qh.vertex_mergeset */))) { /* only one at a time from qh_getpinchedmerges */
2684 0 : if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
2685 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
2686 0 : if (merge->mergetype == MRGsubridge) {
2687 0 : zzinc_(Zpinchedvertex);
2688 0 : trace1((qh, qh->ferr, 1050, "qh_merge_pinchedvertices: merge one of %d pinched vertices before adding apex p%d. Try to resolve duplicate ridges in newfacets\n",
2689 : qh_setsize(qh, qh->vertex_mergeset)+1, apexpointid));
2690 0 : qh_remove_mergetype(qh, qh->vertex_mergeset, MRGsubridge);
2691 : }else {
2692 0 : zzinc_(Zpinchduplicate);
2693 0 : if (firstmerge)
2694 0 : trace1((qh, qh->ferr, 1056, "qh_merge_pinchedvertices: merge %d pinched vertices from dupridges in merged facets, apex p%d\n",
2695 : qh_setsize(qh, qh->vertex_mergeset)+1, apexpointid));
2696 0 : firstmerge= False;
2697 : }
2698 0 : vertex= merge->vertex1;
2699 0 : vertex2= merge->vertex2;
2700 0 : dist= merge->distance;
2701 0 : qh_memfree(qh, merge, (int)sizeof(mergeT)); /* merge is invalidated */
2702 0 : qh_rename_adjacentvertex(qh, vertex, vertex2, dist);
2703 : #ifndef qh_NOtrace
2704 0 : if (qh->IStracing >= 2) {
2705 0 : FOREACHmergeA_(qh->degen_mergeset) {
2706 0 : if (mergeA->mergetype== MRGdegen) {
2707 0 : qh_fprintf(qh, qh->ferr, 2072, "qh_merge_pinchedvertices: merge degenerate f%d into an adjacent facet\n", mergeA->facet1->id);
2708 : }else {
2709 0 : qh_fprintf(qh, qh->ferr, 2084, "qh_merge_pinchedvertices: merge f%d into f%d mergeType %d\n", mergeA->facet1->id, mergeA->facet2->id, mergeA->mergetype);
2710 : }
2711 : }
2712 : }
2713 : #endif
2714 0 : qh_merge_degenredundant(qh); /* simplicial facets with both old and new vertices */
2715 : }
2716 0 : qh->isRenameVertex= False;
2717 0 : }/* merge_pinchedvertices */
2718 :
2719 : /*-<a href="qh-merge_r.htm#TOC"
2720 : >-------------------------------</a><a name="merge_twisted">-</a>
2721 :
2722 : qh_merge_twisted(qh, facet1, facet2 )
2723 : remove twisted ridge between facet1 into facet2 or report error
2724 :
2725 : returns:
2726 : merges one of the facets into the best neighbor
2727 :
2728 : notes:
2729 : a twisted ridge has opposite vertices that are convex and concave
2730 :
2731 : design:
2732 : find best neighbors for both facets
2733 : error if wide merge
2734 : merge the nearest facet into its best neighbor
2735 : update statistics
2736 : */
2737 0 : void qh_merge_twisted(qhT *qh, facetT *facet1, facetT *facet2) {
2738 : facetT *neighbor2, *neighbor, *merging, *merged;
2739 : vertexT *bestvertex, *bestpinched;
2740 : realT dist, dist2, mindist, mindist2, maxdist, maxdist2, mintwisted, bestdist;
2741 :
2742 0 : if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
2743 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
2744 0 : trace3((qh, qh->ferr, 3050, "qh_merge_twisted: merge #%d for twisted f%d and f%d\n",
2745 : zzval_(Ztotmerge) + 1, facet1->id, facet2->id));
2746 : /* twisted */
2747 0 : neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
2748 0 : neighbor2= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2);
2749 0 : mintwisted= qh_RATIOtwisted * qh->ONEmerge;
2750 0 : maximize_(mintwisted, facet1->maxoutside);
2751 0 : maximize_(mintwisted, facet2->maxoutside);
2752 0 : if (dist > mintwisted && dist2 > mintwisted) {
2753 0 : bestdist= qh_vertex_bestdist2(qh, facet1->vertices, &bestvertex, &bestpinched);
2754 0 : if (bestdist > mintwisted) {
2755 0 : qh_fprintf(qh, qh->ferr, 6417, "qhull precision error (qh_merge_twisted): twisted facet f%d does not contain pinched vertices. Too wide to merge into neighbor. mindist %2.2g maxdist %2.2g vertexdist %2.2g maxpinched %2.2g neighbor f%d mindist %2.2g maxdist %2.2g\n",
2756 : facet1->id, mindist, maxdist, bestdist, mintwisted, facet2->id, mindist2, maxdist2);
2757 : }else {
2758 0 : qh_fprintf(qh, qh->ferr, 6418, "qhull precision error (qh_merge_twisted): twisted facet f%d with pinched vertices. Could merge vertices, but too wide to merge into neighbor. mindist %2.2g maxdist %2.2g vertexdist %2.2g neighbor f%d mindist %2.2g maxdist %2.2g\n",
2759 : facet1->id, mindist, maxdist, bestdist, facet2->id, mindist2, maxdist2);
2760 : }
2761 0 : qh_errexit2(qh, qh_ERRwide, facet1, facet2);
2762 : }
2763 0 : if (dist < dist2) {
2764 0 : merging= facet1;
2765 0 : merged= neighbor;
2766 : }else {
2767 : /* ignores qh.AVOIDold ('Q4') */
2768 0 : merging= facet2;
2769 0 : merged= neighbor2;
2770 0 : dist= dist2;
2771 0 : mindist= mindist2;
2772 0 : maxdist= maxdist2;
2773 : }
2774 0 : qh_mergefacet(qh, merging, merged, MRGtwisted, &mindist, &maxdist, !qh_MERGEapex);
2775 : /* caller merges qh_degenredundant */
2776 0 : zinc_(Ztwisted);
2777 0 : wadd_(Wtwistedtot, dist);
2778 0 : wmax_(Wtwistedmax, dist);
2779 0 : } /* merge_twisted */
2780 :
2781 : /*-<a href="qh-merge_r.htm#TOC"
2782 : >-------------------------------</a><a name="mergecycle">-</a>
2783 :
2784 : qh_mergecycle(qh, samecycle, newfacet )
2785 : merge a cycle of facets starting at samecycle into a newfacet
2786 : newfacet is a horizon facet with ->normal
2787 : samecycle facets are simplicial from an apex
2788 :
2789 : returns:
2790 : initializes vertex neighbors on first merge
2791 : samecycle deleted (placed on qh.visible_list)
2792 : newfacet at end of qh.facet_list
2793 : deleted vertices on qh.del_vertices
2794 :
2795 : notes:
2796 : only called by qh_mergecycle_all for multiple, same cycle facets
2797 : see qh_mergefacet
2798 :
2799 : design:
2800 : make vertex neighbors if necessary
2801 : make ridges for newfacet
2802 : merge neighbor sets of samecycle into newfacet
2803 : merge ridges of samecycle into newfacet
2804 : merge vertex neighbors of samecycle into newfacet
2805 : make apex of samecycle the apex of newfacet
2806 : if newfacet wasn't a new facet
2807 : add its vertices to qh.newvertex_list
2808 : delete samecycle facets a make newfacet a newfacet
2809 : */
2810 0 : void qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet) {
2811 0 : int traceonce= False, tracerestore= 0;
2812 : vertexT *apex;
2813 : #ifndef qh_NOtrace
2814 : facetT *same;
2815 : #endif
2816 :
2817 0 : zzinc_(Ztotmerge);
2818 0 : if (qh->REPORTfreq2 && qh->POSTmerging) {
2819 0 : if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)
2820 0 : qh_tracemerging(qh);
2821 : }
2822 : #ifndef qh_NOtrace
2823 0 : if (qh->TRACEmerge == zzval_(Ztotmerge))
2824 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
2825 0 : trace2((qh, qh->ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
2826 : zzval_(Ztotmerge), samecycle->id, newfacet->id));
2827 0 : if (newfacet == qh->tracefacet) {
2828 0 : tracerestore= qh->IStracing;
2829 0 : qh->IStracing= 4;
2830 0 : qh_fprintf(qh, qh->ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
2831 : zzval_(Ztotmerge), samecycle->id, newfacet->id, qh->furthest_id);
2832 0 : traceonce= True;
2833 : }
2834 0 : if (qh->IStracing >=4) {
2835 0 : qh_fprintf(qh, qh->ferr, 8069, " same cycle:");
2836 0 : FORALLsame_cycle_(samecycle)
2837 0 : qh_fprintf(qh, qh->ferr, 8070, " f%d", same->id);
2838 0 : qh_fprintf(qh, qh->ferr, 8071, "\n");
2839 : }
2840 0 : if (qh->IStracing >=4)
2841 0 : qh_errprint(qh, "MERGING CYCLE", samecycle, newfacet, NULL, NULL);
2842 : #endif /* !qh_NOtrace */
2843 0 : if (newfacet->tricoplanar) {
2844 0 : if (!qh->TRInormals) {
2845 0 : qh_fprintf(qh, qh->ferr, 6224, "qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n");
2846 0 : qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
2847 : }
2848 0 : newfacet->tricoplanar= False;
2849 0 : newfacet->keepcentrum= False;
2850 : }
2851 0 : if (qh->CHECKfrequently)
2852 0 : qh_checkdelridge(qh);
2853 0 : if (!qh->VERTEXneighbors)
2854 0 : qh_vertexneighbors(qh);
2855 0 : apex= SETfirstt_(samecycle->vertices, vertexT);
2856 0 : qh_makeridges(qh, newfacet);
2857 0 : qh_mergecycle_neighbors(qh, samecycle, newfacet);
2858 0 : qh_mergecycle_ridges(qh, samecycle, newfacet);
2859 0 : qh_mergecycle_vneighbors(qh, samecycle, newfacet);
2860 0 : if (SETfirstt_(newfacet->vertices, vertexT) != apex)
2861 0 : qh_setaddnth(qh, &newfacet->vertices, 0, apex); /* apex has last id */
2862 0 : if (!newfacet->newfacet)
2863 0 : qh_newvertices(qh, newfacet->vertices);
2864 0 : qh_mergecycle_facets(qh, samecycle, newfacet);
2865 0 : qh_tracemerge(qh, samecycle, newfacet, MRGcoplanarhorizon);
2866 : /* check for degen_redundant_neighbors after qh_forcedmerges() */
2867 0 : if (traceonce) {
2868 0 : qh_fprintf(qh, qh->ferr, 8072, "qh_mergecycle: end of trace facet\n");
2869 0 : qh->IStracing= tracerestore;
2870 : }
2871 0 : } /* mergecycle */
2872 :
2873 : /*-<a href="qh-merge_r.htm#TOC"
2874 : >-------------------------------</a><a name="mergecycle_all">-</a>
2875 :
2876 : qh_mergecycle_all(qh, facetlist, wasmerge )
2877 : merge all samecycles of coplanar facets into horizon
2878 : don't merge facets with ->mergeridge (these already have ->normal)
2879 : all facets are simplicial from apex
2880 : all facet->cycledone == False
2881 :
2882 : returns:
2883 : all newfacets merged into coplanar horizon facets
2884 : deleted vertices on qh.del_vertices
2885 : sets wasmerge if any merge
2886 :
2887 : notes:
2888 : called by qh_premerge
2889 : calls qh_mergecycle for multiple, same cycle facets
2890 :
2891 : design:
2892 : for each facet on facetlist
2893 : skip facets with dupridges and normals
2894 : check that facet is in a samecycle (->mergehorizon)
2895 : if facet only member of samecycle
2896 : sets vertex->delridge for all vertices except apex
2897 : merge facet into horizon
2898 : else
2899 : mark all facets in samecycle
2900 : remove facets with dupridges from samecycle
2901 : merge samecycle into horizon (deletes facets from facetlist)
2902 : */
2903 12197 : void qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge) {
2904 : facetT *facet, *same, *prev, *horizon, *newfacet;
2905 12197 : facetT *samecycle= NULL, *nextfacet, *nextsame;
2906 : vertexT *apex, *vertex, **vertexp;
2907 12197 : int cycles=0, total=0, facets, nummerge, numdegen= 0;
2908 :
2909 12197 : trace2((qh, qh->ferr, 2031, "qh_mergecycle_all: merge new facets into coplanar horizon facets. Bulk merge a cycle of facets with the same horizon facet\n"));
2910 91892 : for (facet=facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {
2911 79695 : if (facet->normal)
2912 59648 : continue;
2913 20047 : if (!facet->mergehorizon) {
2914 0 : qh_fprintf(qh, qh->ferr, 6225, "qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
2915 0 : qh_errexit(qh, qh_ERRqhull, facet, NULL);
2916 : }
2917 20047 : horizon= SETfirstt_(facet->neighbors, facetT);
2918 20047 : if (facet->f.samecycle == facet) {
2919 20047 : if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
2920 0 : qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
2921 20047 : zinc_(Zonehorizon);
2922 : /* merge distance done in qh_findhorizon */
2923 20047 : apex= SETfirstt_(facet->vertices, vertexT);
2924 80188 : FOREACHvertex_(facet->vertices) {
2925 60141 : if (vertex != apex)
2926 40094 : vertex->delridge= True;
2927 : }
2928 20047 : horizon->f.newcycle= NULL;
2929 20047 : qh_mergefacet(qh, facet, horizon, MRGcoplanarhorizon, NULL, NULL, qh_MERGEapex);
2930 : }else {
2931 0 : samecycle= facet;
2932 0 : facets= 0;
2933 0 : prev= facet;
2934 0 : for (same= facet->f.samecycle; same; /* FORALLsame_cycle_(facet) */
2935 0 : same= (same == facet ? NULL :nextsame)) { /* ends at facet */
2936 0 : nextsame= same->f.samecycle;
2937 0 : if (same->cycledone || same->visible)
2938 0 : qh_infiniteloop(qh, same);
2939 0 : same->cycledone= True;
2940 0 : if (same->normal) {
2941 0 : prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
2942 0 : same->f.samecycle= NULL;
2943 : }else {
2944 0 : prev= same;
2945 0 : facets++;
2946 : }
2947 : }
2948 0 : while (nextfacet && nextfacet->cycledone) /* will delete samecycle */
2949 0 : nextfacet= nextfacet->next;
2950 0 : horizon->f.newcycle= NULL;
2951 0 : qh_mergecycle(qh, samecycle, horizon);
2952 0 : nummerge= horizon->nummerge + facets;
2953 0 : if (nummerge > qh_MAXnummerge)
2954 0 : horizon->nummerge= qh_MAXnummerge;
2955 : else
2956 0 : horizon->nummerge= (short unsigned int)nummerge; /* limited to 9 bits by qh_MAXnummerge, -Wconversion */
2957 0 : zzinc_(Zcyclehorizon);
2958 0 : total += facets;
2959 0 : zzadd_(Zcyclefacettot, facets);
2960 0 : zmax_(Zcyclefacetmax, facets);
2961 : }
2962 20047 : cycles++;
2963 : }
2964 12197 : if (cycles) {
2965 55564 : FORALLnew_facets {
2966 : /* qh_maybe_duplicateridges postponed since qh_mergecycle_ridges deletes ridges without calling qh_delridge_merge */
2967 45879 : if (newfacet->coplanarhorizon) {
2968 20047 : qh_test_redundant_neighbors(qh, newfacet);
2969 20047 : qh_maybe_duplicateridges(qh, newfacet);
2970 20047 : newfacet->coplanarhorizon= False;
2971 : }
2972 : }
2973 9685 : numdegen += qh_merge_degenredundant(qh);
2974 9685 : *wasmerge= True;
2975 9685 : trace1((qh, qh->ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons and %d degenredundant facets\n",
2976 : cycles, numdegen));
2977 : }
2978 12197 : } /* mergecycle_all */
2979 :
2980 : /*-<a href="qh-merge_r.htm#TOC"
2981 : >-------------------------------</a><a name="mergecycle_facets">-</a>
2982 :
2983 : qh_mergecycle_facets(qh, samecycle, newfacet )
2984 : finish merge of samecycle into newfacet
2985 :
2986 : returns:
2987 : samecycle prepended to visible_list for later deletion and partitioning
2988 : each facet->f.replace == newfacet
2989 :
2990 : newfacet moved to end of qh.facet_list
2991 : makes newfacet a newfacet (get's facet1->id if it was old)
2992 : sets newfacet->newmerge
2993 : clears newfacet->center (unless merging into a large facet)
2994 : clears newfacet->tested and ridge->tested for facet1
2995 :
2996 : adds neighboring facets to facet_mergeset if redundant or degenerate
2997 :
2998 : design:
2999 : make newfacet a new facet and set its flags
3000 : move samecycle facets to qh.visible_list for later deletion
3001 : unless newfacet is large
3002 : remove its centrum
3003 : */
3004 0 : void qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet) {
3005 : facetT *same, *next;
3006 :
3007 0 : trace4((qh, qh->ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
3008 0 : qh_removefacet(qh, newfacet); /* append as a newfacet to end of qh->facet_list */
3009 0 : qh_appendfacet(qh, newfacet);
3010 0 : newfacet->newfacet= True;
3011 0 : newfacet->simplicial= False;
3012 0 : newfacet->newmerge= True;
3013 :
3014 0 : for (same= samecycle->f.samecycle; same; same= (same == samecycle ? NULL : next)) {
3015 0 : next= same->f.samecycle; /* reused by willdelete */
3016 0 : qh_willdelete(qh, same, newfacet);
3017 : }
3018 0 : if (newfacet->center
3019 0 : && qh_setsize(qh, newfacet->vertices) <= qh->hull_dim + qh_MAXnewcentrum) {
3020 0 : qh_memfree(qh, newfacet->center, qh->normal_size);
3021 0 : newfacet->center= NULL;
3022 : }
3023 0 : trace3((qh, qh->ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
3024 : samecycle->id, newfacet->id));
3025 0 : } /* mergecycle_facets */
3026 :
3027 : /*-<a href="qh-merge_r.htm#TOC"
3028 : >-------------------------------</a><a name="mergecycle_neighbors">-</a>
3029 :
3030 : qh_mergecycle_neighbors(qh, samecycle, newfacet )
3031 : add neighbors for samecycle facets to newfacet
3032 :
3033 : returns:
3034 : newfacet with updated neighbors and vice-versa
3035 : newfacet has ridges
3036 : all neighbors of newfacet marked with qh.visit_id
3037 : samecycle facets marked with qh.visit_id-1
3038 : ridges updated for simplicial neighbors of samecycle with a ridge
3039 :
3040 : notes:
3041 : assumes newfacet not in samecycle
3042 : usually, samecycle facets are new, simplicial facets without internal ridges
3043 : not so if horizon facet is coplanar to two different samecycles
3044 :
3045 : see:
3046 : qh_mergeneighbors()
3047 :
3048 : design:
3049 : check samecycle
3050 : delete neighbors from newfacet that are also in samecycle
3051 : for each neighbor of a facet in samecycle
3052 : if neighbor is simplicial
3053 : if first visit
3054 : move the neighbor relation to newfacet
3055 : update facet links for its ridges
3056 : else
3057 : make ridges for neighbor
3058 : remove samecycle reference
3059 : else
3060 : update neighbor sets
3061 : */
3062 0 : void qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {
3063 : facetT *same, *neighbor, **neighborp;
3064 0 : int delneighbors= 0, newneighbors= 0;
3065 : unsigned int samevisitid;
3066 : ridgeT *ridge, **ridgep;
3067 :
3068 0 : samevisitid= ++qh->visit_id;
3069 0 : FORALLsame_cycle_(samecycle) {
3070 0 : if (same->visitid == samevisitid || same->visible)
3071 0 : qh_infiniteloop(qh, samecycle);
3072 0 : same->visitid= samevisitid;
3073 : }
3074 0 : newfacet->visitid= ++qh->visit_id;
3075 0 : trace4((qh, qh->ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
3076 0 : FOREACHneighbor_(newfacet) {
3077 0 : if (neighbor->visitid == samevisitid) {
3078 0 : SETref_(neighbor)= NULL; /* samecycle neighbors deleted */
3079 0 : delneighbors++;
3080 : }else
3081 0 : neighbor->visitid= qh->visit_id;
3082 : }
3083 0 : qh_setcompact(qh, newfacet->neighbors);
3084 :
3085 0 : trace4((qh, qh->ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));
3086 0 : FORALLsame_cycle_(samecycle) {
3087 0 : FOREACHneighbor_(same) {
3088 0 : if (neighbor->visitid == samevisitid)
3089 0 : continue;
3090 0 : if (neighbor->simplicial) {
3091 0 : if (neighbor->visitid != qh->visit_id) {
3092 0 : qh_setappend(qh, &newfacet->neighbors, neighbor);
3093 0 : qh_setreplace(qh, neighbor->neighbors, same, newfacet);
3094 0 : newneighbors++;
3095 0 : neighbor->visitid= qh->visit_id;
3096 0 : FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
3097 0 : if (ridge->top == same) {
3098 0 : ridge->top= newfacet;
3099 0 : break;
3100 0 : }else if (ridge->bottom == same) {
3101 0 : ridge->bottom= newfacet;
3102 0 : break;
3103 : }
3104 : }
3105 : }else {
3106 0 : qh_makeridges(qh, neighbor);
3107 0 : qh_setdel(neighbor->neighbors, same);
3108 : /* same can't be horizon facet for neighbor */
3109 : }
3110 : }else { /* non-simplicial neighbor */
3111 0 : qh_setdel(neighbor->neighbors, same);
3112 0 : if (neighbor->visitid != qh->visit_id) {
3113 0 : qh_setappend(qh, &neighbor->neighbors, newfacet);
3114 0 : qh_setappend(qh, &newfacet->neighbors, neighbor);
3115 0 : neighbor->visitid= qh->visit_id;
3116 0 : newneighbors++;
3117 : }
3118 : }
3119 : }
3120 : }
3121 0 : trace2((qh, qh->ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
3122 : delneighbors, newneighbors));
3123 0 : } /* mergecycle_neighbors */
3124 :
3125 : /*-<a href="qh-merge_r.htm#TOC"
3126 : >-------------------------------</a><a name="mergecycle_ridges">-</a>
3127 :
3128 : qh_mergecycle_ridges(qh, samecycle, newfacet )
3129 : add ridges/neighbors for facets in samecycle to newfacet
3130 : all new/old neighbors of newfacet marked with qh.visit_id
3131 : facets in samecycle marked with qh.visit_id-1
3132 : newfacet marked with qh.visit_id
3133 :
3134 : returns:
3135 : newfacet has merged ridges
3136 :
3137 : notes:
3138 : ridge already updated for simplicial neighbors of samecycle with a ridge
3139 : qh_checkdelridge called by qh_mergecycle
3140 :
3141 : see:
3142 : qh_mergeridges()
3143 : qh_makeridges()
3144 :
3145 : design:
3146 : remove ridges between newfacet and samecycle
3147 : for each facet in samecycle
3148 : for each ridge in facet
3149 : update facet pointers in ridge
3150 : skip ridges processed in qh_mergecycle_neighors
3151 : free ridges between newfacet and samecycle
3152 : free ridges between facets of samecycle (on 2nd visit)
3153 : append remaining ridges to newfacet
3154 : if simplicial facet
3155 : for each neighbor of facet
3156 : if simplicial facet
3157 : and not samecycle facet or newfacet
3158 : make ridge between neighbor and newfacet
3159 : */
3160 0 : void qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet) {
3161 0 : facetT *same, *neighbor= NULL;
3162 0 : int numold=0, numnew=0;
3163 : int neighbor_i, neighbor_n;
3164 : unsigned int samevisitid;
3165 : ridgeT *ridge, **ridgep;
3166 : boolT toporient;
3167 : void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
3168 :
3169 0 : trace4((qh, qh->ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
3170 0 : samevisitid= qh->visit_id -1;
3171 0 : FOREACHridge_(newfacet->ridges) {
3172 0 : neighbor= otherfacet_(ridge, newfacet);
3173 0 : if (neighbor->visitid == samevisitid)
3174 0 : SETref_(ridge)= NULL; /* ridge free'd below */
3175 : }
3176 0 : qh_setcompact(qh, newfacet->ridges);
3177 :
3178 0 : trace4((qh, qh->ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));
3179 0 : FORALLsame_cycle_(samecycle) {
3180 0 : FOREACHridge_(same->ridges) {
3181 0 : if (ridge->top == same) {
3182 0 : ridge->top= newfacet;
3183 0 : neighbor= ridge->bottom;
3184 0 : }else if (ridge->bottom == same) {
3185 0 : ridge->bottom= newfacet;
3186 0 : neighbor= ridge->top;
3187 0 : }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
3188 0 : qh_setappend(qh, &newfacet->ridges, ridge);
3189 0 : numold++; /* already set by qh_mergecycle_neighbors */
3190 0 : continue;
3191 : }else {
3192 0 : qh_fprintf(qh, qh->ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
3193 0 : qh_errexit(qh, qh_ERRqhull, NULL, ridge);
3194 : }
3195 0 : if (neighbor == newfacet) {
3196 0 : if (qh->traceridge == ridge)
3197 0 : qh->traceridge= NULL;
3198 0 : qh_setfree(qh, &(ridge->vertices));
3199 0 : qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
3200 0 : numold++;
3201 0 : }else if (neighbor->visitid == samevisitid) {
3202 0 : qh_setdel(neighbor->ridges, ridge);
3203 0 : if (qh->traceridge == ridge)
3204 0 : qh->traceridge= NULL;
3205 0 : qh_setfree(qh, &(ridge->vertices));
3206 0 : qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
3207 0 : numold++;
3208 : }else {
3209 0 : qh_setappend(qh, &newfacet->ridges, ridge);
3210 0 : numold++;
3211 : }
3212 : }
3213 0 : if (same->ridges)
3214 0 : qh_settruncate(qh, same->ridges, 0);
3215 0 : if (!same->simplicial)
3216 0 : continue;
3217 0 : FOREACHneighbor_i_(qh, same) { /* note: !newfact->simplicial */
3218 0 : if (neighbor->visitid != samevisitid && neighbor->simplicial) {
3219 0 : ridge= qh_newridge(qh);
3220 0 : ridge->vertices= qh_setnew_delnthsorted(qh, same->vertices, qh->hull_dim,
3221 : neighbor_i, 0);
3222 0 : toporient= (boolT)(same->toporient ^ (neighbor_i & 0x1));
3223 0 : if (toporient) {
3224 0 : ridge->top= newfacet;
3225 0 : ridge->bottom= neighbor;
3226 0 : ridge->simplicialbot= True;
3227 : }else {
3228 0 : ridge->top= neighbor;
3229 0 : ridge->bottom= newfacet;
3230 0 : ridge->simplicialtop= True;
3231 : }
3232 0 : qh_setappend(qh, &(newfacet->ridges), ridge);
3233 0 : qh_setappend(qh, &(neighbor->ridges), ridge);
3234 0 : if (qh->ridge_id == qh->traceridge_id)
3235 0 : qh->traceridge= ridge;
3236 0 : numnew++;
3237 : }
3238 : }
3239 : }
3240 :
3241 0 : trace2((qh, qh->ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
3242 : numold, numnew));
3243 0 : } /* mergecycle_ridges */
3244 :
3245 : /*-<a href="qh-merge_r.htm#TOC"
3246 : >-------------------------------</a><a name="mergecycle_vneighbors">-</a>
3247 :
3248 : qh_mergecycle_vneighbors(qh, samecycle, newfacet )
3249 : create vertex neighbors for newfacet from vertices of facets in samecycle
3250 : samecycle marked with visitid == qh.visit_id - 1
3251 :
3252 : returns:
3253 : newfacet vertices with updated neighbors
3254 : marks newfacet with qh.visit_id-1
3255 : deletes vertices that are merged away
3256 : sets delridge on all vertices (faster here than in mergecycle_ridges)
3257 :
3258 : see:
3259 : qh_mergevertex_neighbors()
3260 :
3261 : design:
3262 : for each vertex of samecycle facet
3263 : set vertex->delridge
3264 : delete samecycle facets from vertex neighbors
3265 : append newfacet to vertex neighbors
3266 : if vertex only in newfacet
3267 : delete it from newfacet
3268 : add it to qh.del_vertices for later deletion
3269 : */
3270 0 : void qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {
3271 : facetT *neighbor, **neighborp;
3272 : unsigned int mergeid;
3273 : vertexT *vertex, **vertexp, *apex;
3274 : setT *vertices;
3275 :
3276 0 : trace4((qh, qh->ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
3277 0 : mergeid= qh->visit_id - 1;
3278 0 : newfacet->visitid= mergeid;
3279 0 : vertices= qh_basevertices(qh, samecycle); /* temp */
3280 0 : apex= SETfirstt_(samecycle->vertices, vertexT);
3281 0 : qh_setappend(qh, &vertices, apex);
3282 0 : FOREACHvertex_(vertices) {
3283 0 : vertex->delridge= True;
3284 0 : FOREACHneighbor_(vertex) {
3285 0 : if (neighbor->visitid == mergeid)
3286 0 : SETref_(neighbor)= NULL;
3287 : }
3288 0 : qh_setcompact(qh, vertex->neighbors);
3289 0 : qh_setappend(qh, &vertex->neighbors, newfacet);
3290 0 : if (!SETsecond_(vertex->neighbors)) {
3291 0 : zinc_(Zcyclevertex);
3292 0 : trace2((qh, qh->ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
3293 : vertex->id, samecycle->id, newfacet->id));
3294 0 : qh_setdelsorted(newfacet->vertices, vertex);
3295 0 : vertex->deleted= True;
3296 0 : qh_setappend(qh, &qh->del_vertices, vertex);
3297 : }
3298 : }
3299 0 : qh_settempfree(qh, &vertices);
3300 0 : trace3((qh, qh->ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
3301 : samecycle->id, newfacet->id));
3302 0 : } /* mergecycle_vneighbors */
3303 :
3304 : /*-<a href="qh-merge_r.htm#TOC"
3305 : >-------------------------------</a><a name="mergefacet">-</a>
3306 :
3307 : qh_mergefacet(qh, facet1, facet2, mergetype, mindist, maxdist, mergeapex )
3308 : merges facet1 into facet2
3309 : mergeapex==qh_MERGEapex if merging new facet into coplanar horizon (optimizes qh_mergesimplex)
3310 :
3311 : returns:
3312 : qh.max_outside and qh.min_vertex updated
3313 : initializes vertex neighbors on first merge
3314 :
3315 : note:
3316 : mergetype only used for logging and error reporting
3317 :
3318 : returns:
3319 : facet2 contains facet1's vertices, neighbors, and ridges
3320 : facet2 moved to end of qh.facet_list
3321 : makes facet2 a newfacet
3322 : sets facet2->newmerge set
3323 : clears facet2->center (unless merging into a large facet)
3324 : clears facet2->tested and ridge->tested for facet1
3325 :
3326 : facet1 prepended to visible_list for later deletion and partitioning
3327 : facet1->f.replace == facet2
3328 :
3329 : adds neighboring facets to facet_mergeset if redundant or degenerate
3330 :
3331 : notes:
3332 : when done, tests facet1 and facet2 for degenerate or redundant neighbors and dupridges
3333 : mindist/maxdist may be NULL (only if both NULL)
3334 : traces merge if fmax_(maxdist,-mindist) > TRACEdist
3335 :
3336 : see:
3337 : qh_mergecycle()
3338 :
3339 : design:
3340 : trace merge and check for degenerate simplex
3341 : make ridges for both facets
3342 : update qh.max_outside, qh.max_vertex, qh.min_vertex
3343 : update facet2->maxoutside and keepcentrum
3344 : update facet2->nummerge
3345 : update tested flags for facet2
3346 : if facet1 is simplicial
3347 : merge facet1 into facet2
3348 : else
3349 : merge facet1's neighbors into facet2
3350 : merge facet1's ridges into facet2
3351 : merge facet1's vertices into facet2
3352 : merge facet1's vertex neighbors into facet2
3353 : add facet2's vertices to qh.new_vertexlist
3354 : move facet2 to end of qh.newfacet_list
3355 : unless MRGcoplanarhorizon
3356 : test facet2 for redundant neighbors
3357 : test facet1 for degenerate neighbors
3358 : test for redundant facet2
3359 : maybe test for duplicate ridges ('Q15')
3360 : move facet1 to qh.visible_list for later deletion
3361 : */
3362 20048 : void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype, realT *mindist, realT *maxdist, boolT mergeapex) {
3363 20048 : boolT traceonce= False;
3364 : vertexT *vertex, **vertexp;
3365 : realT mintwisted, vertexdist;
3366 : realT onemerge;
3367 20048 : int tracerestore=0, nummerge;
3368 : const char *mergename;
3369 :
3370 20048 : if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *))
3371 20048 : mergename= mergetypes[mergetype];
3372 : else
3373 0 : mergename= mergetypes[MRGnone];
3374 20048 : if (facet1->tricoplanar || facet2->tricoplanar) {
3375 0 : if (!qh->TRInormals) {
3376 0 : qh_fprintf(qh, qh->ferr, 6226, "qhull internal error (qh_mergefacet): merge f%d into f%d for mergetype %d (%s) does not work for tricoplanar facets. Use option 'Q11'\n",
3377 : facet1->id, facet2->id, mergetype, mergename);
3378 0 : qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
3379 : }
3380 0 : if (facet2->tricoplanar) {
3381 0 : facet2->tricoplanar= False;
3382 0 : facet2->keepcentrum= False;
3383 : }
3384 : }
3385 20048 : zzinc_(Ztotmerge);
3386 20048 : if (qh->REPORTfreq2 && qh->POSTmerging) {
3387 0 : if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)
3388 0 : qh_tracemerging(qh);
3389 : }
3390 : #ifndef qh_NOtrace
3391 20048 : if (qh->build_cnt >= qh->RERUN) {
3392 20048 : if (mindist && (-*mindist > qh->TRACEdist || *maxdist > qh->TRACEdist)) {
3393 0 : tracerestore= 0;
3394 0 : qh->IStracing= qh->TRACElevel;
3395 0 : traceonce= True;
3396 0 : qh_fprintf(qh, qh->ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d for mergetype %d (%s), last point was p%d\n",
3397 0 : zzval_(Ztotmerge), fmax_(-*mindist, *maxdist), facet1->id, facet2->id, mergetype, mergename, qh->furthest_id);
3398 20048 : }else if (facet1 == qh->tracefacet || facet2 == qh->tracefacet) {
3399 0 : tracerestore= qh->IStracing;
3400 0 : qh->IStracing= 4;
3401 0 : traceonce= True;
3402 0 : qh_fprintf(qh, qh->ferr, 8076, "qh_mergefacet: ========= trace merge #%d for f%d into f%d for mergetype %d (%s), furthest is p%d\n",
3403 : zzval_(Ztotmerge), facet1->id, facet2->id, mergetype, mergename, qh->furthest_id);
3404 : }
3405 : }
3406 20048 : if (qh->IStracing >= 2) {
3407 0 : realT mergemin= -2;
3408 0 : realT mergemax= -2;
3409 :
3410 0 : if (mindist) {
3411 0 : mergemin= *mindist;
3412 0 : mergemax= *maxdist;
3413 : }
3414 0 : qh_fprintf(qh, qh->ferr, 2081, "qh_mergefacet: #%d merge f%d into f%d for merge for mergetype %d (%s), mindist= %2.2g, maxdist= %2.2g, max_outside %2.2g\n",
3415 : zzval_(Ztotmerge), facet1->id, facet2->id, mergetype, mergename, mergemin, mergemax, qh->max_outside);
3416 : }
3417 : #endif /* !qh_NOtrace */
3418 20048 : if(!qh->ALLOWwide && mindist) {
3419 1 : mintwisted= qh_WIDEmaxoutside * qh->ONEmerge; /* same as qh_merge_twisted and qh_check_maxout (poly2) */
3420 1 : maximize_(mintwisted, facet1->maxoutside);
3421 1 : maximize_(mintwisted, facet2->maxoutside);
3422 1 : if (*maxdist > mintwisted || -*mindist > mintwisted) {
3423 0 : vertexdist= qh_vertex_bestdist(qh, facet1->vertices);
3424 0 : onemerge= qh->ONEmerge + qh->DISTround;
3425 0 : if (vertexdist > mintwisted) {
3426 0 : qh_fprintf(qh, qh->ferr, 6347, "qhull precision error (qh_mergefacet): wide merge for facet f%d into f%d for mergetype %d (%s). maxdist %2.2g (%.1fx) mindist %2.2g (%.1fx) vertexdist %2.2g Allow with 'Q12' (allow-wide)\n",
3427 0 : facet1->id, facet2->id, mergetype, mergename, *maxdist, *maxdist/onemerge, *mindist, -*mindist/onemerge, vertexdist);
3428 : }else {
3429 0 : qh_fprintf(qh, qh->ferr, 6348, "qhull precision error (qh_mergefacet): wide merge for pinched facet f%d into f%d for mergetype %d (%s). maxdist %2.2g (%.fx) mindist %2.2g (%.1fx) vertexdist %2.2g Allow with 'Q12' (allow-wide)\n",
3430 0 : facet1->id, facet2->id, mergetype, mergename, *maxdist, *maxdist/onemerge, *mindist, -*mindist/onemerge, vertexdist);
3431 : }
3432 0 : qh_errexit2(qh, qh_ERRwide, facet1, facet2);
3433 : }
3434 : }
3435 20048 : if (facet1 == facet2 || facet1->visible || facet2->visible) {
3436 0 : qh_fprintf(qh, qh->ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet, mergetype %d (%s)\n",
3437 : facet1->id, facet2->id, mergetype, mergename);
3438 0 : qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
3439 : }
3440 20048 : if (qh->num_facets - qh->num_visible <= qh->hull_dim + 1) {
3441 0 : qh_fprintf(qh, qh->ferr, 6227, "qhull topology error: Only %d facets remain. The input is too degenerate or the convexity constraints are too strong.\n",
3442 0 : qh->hull_dim+1);
3443 0 : if (qh->hull_dim >= 5 && !qh->MERGEexact)
3444 0 : qh_fprintf(qh, qh->ferr, 8079, " Option 'Qx' may avoid this problem.\n");
3445 0 : qh_errexit(qh, qh_ERRtopology, NULL, NULL);
3446 : }
3447 20048 : if (!qh->VERTEXneighbors)
3448 3 : qh_vertexneighbors(qh);
3449 20048 : qh_makeridges(qh, facet1);
3450 20048 : qh_makeridges(qh, facet2);
3451 20048 : if (qh->IStracing >=4)
3452 0 : qh_errprint(qh, "MERGING", facet1, facet2, NULL, NULL);
3453 20048 : if (mindist) {
3454 1 : maximize_(qh->max_outside, *maxdist);
3455 1 : maximize_(qh->max_vertex, *maxdist);
3456 : #if qh_MAXoutside
3457 1 : maximize_(facet2->maxoutside, *maxdist);
3458 : #endif
3459 1 : minimize_(qh->min_vertex, *mindist);
3460 1 : if (!facet2->keepcentrum
3461 1 : && (*maxdist > qh->WIDEfacet || *mindist < -qh->WIDEfacet)) {
3462 0 : facet2->keepcentrum= True;
3463 0 : zinc_(Zwidefacet);
3464 : }
3465 : }
3466 20048 : nummerge= facet1->nummerge + facet2->nummerge + 1;
3467 20048 : if (nummerge >= qh_MAXnummerge)
3468 0 : facet2->nummerge= qh_MAXnummerge;
3469 : else
3470 20048 : facet2->nummerge= (short unsigned int)nummerge; /* limited to 9 bits by qh_MAXnummerge, -Wconversion */
3471 20048 : facet2->newmerge= True;
3472 20048 : facet2->dupridge= False;
3473 20048 : qh_updatetested(qh, facet1, facet2);
3474 20048 : if (qh->hull_dim > 2 && qh_setsize(qh, facet1->vertices) == qh->hull_dim)
3475 20048 : qh_mergesimplex(qh, facet1, facet2, mergeapex);
3476 : else {
3477 0 : qh->vertex_visit++;
3478 0 : FOREACHvertex_(facet2->vertices)
3479 0 : vertex->visitid= qh->vertex_visit;
3480 0 : if (qh->hull_dim == 2)
3481 0 : qh_mergefacet2d(qh, facet1, facet2);
3482 : else {
3483 0 : qh_mergeneighbors(qh, facet1, facet2);
3484 0 : qh_mergevertices(qh, facet1->vertices, &facet2->vertices);
3485 : }
3486 0 : qh_mergeridges(qh, facet1, facet2);
3487 0 : qh_mergevertex_neighbors(qh, facet1, facet2);
3488 0 : if (!facet2->newfacet)
3489 0 : qh_newvertices(qh, facet2->vertices);
3490 : }
3491 20048 : if (facet2->coplanarhorizon) {
3492 20047 : zinc_(Zmergeintocoplanar);
3493 1 : }else if (!facet2->newfacet) {
3494 0 : zinc_(Zmergeintohorizon);
3495 1 : }else if (!facet1->newfacet && facet2->newfacet) {
3496 1 : zinc_(Zmergehorizon);
3497 : }else {
3498 0 : zinc_(Zmergenew);
3499 : }
3500 20048 : qh_removefacet(qh, facet2); /* append as a newfacet to end of qh->facet_list */
3501 20048 : qh_appendfacet(qh, facet2);
3502 20048 : facet2->newfacet= True;
3503 20048 : facet2->tested= False;
3504 20048 : qh_tracemerge(qh, facet1, facet2, mergetype);
3505 20048 : if (traceonce) {
3506 0 : qh_fprintf(qh, qh->ferr, 8080, "qh_mergefacet: end of wide tracing\n");
3507 0 : qh->IStracing= tracerestore;
3508 : }
3509 20048 : if (mergetype != MRGcoplanarhorizon) {
3510 1 : trace3((qh, qh->ferr, 3076, "qh_mergefacet: check f%d and f%d for redundant and degenerate neighbors\n",
3511 : facet1->id, facet2->id));
3512 1 : qh_test_redundant_neighbors(qh, facet2);
3513 1 : qh_test_degen_neighbors(qh, facet1); /* after qh_test_redundant_neighbors since MRGdegen more difficult than MRGredundant
3514 : and before qh_willdelete which clears facet1.neighbors */
3515 1 : qh_degen_redundant_facet(qh, facet2); /* may occur in qh_merge_pinchedvertices, e.g., rbox 175 C3,2e-13 D4 t1545228104 | qhull d */
3516 1 : qh_maybe_duplicateridges(qh, facet2);
3517 : }
3518 20048 : qh_willdelete(qh, facet1, facet2);
3519 20048 : } /* mergefacet */
3520 :
3521 :
3522 : /*-<a href="qh-merge_r.htm#TOC"
3523 : >-------------------------------</a><a name="mergefacet2d">-</a>
3524 :
3525 : qh_mergefacet2d(qh, facet1, facet2 )
3526 : in 2d, merges neighbors and vertices of facet1 into facet2
3527 :
3528 : returns:
3529 : build ridges for neighbors if necessary
3530 : facet2 looks like a simplicial facet except for centrum, ridges
3531 : neighbors are opposite the corresponding vertex
3532 : maintains orientation of facet2
3533 :
3534 : notes:
3535 : qh_mergefacet() retains non-simplicial structures
3536 : they are not needed in 2d, but later routines may use them
3537 : preserves qh.vertex_visit for qh_mergevertex_neighbors()
3538 :
3539 : design:
3540 : get vertices and neighbors
3541 : determine new vertices and neighbors
3542 : set new vertices and neighbors and adjust orientation
3543 : make ridges for new neighbor if needed
3544 : */
3545 0 : void qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2) {
3546 : vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
3547 : facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;
3548 :
3549 0 : vertex1A= SETfirstt_(facet1->vertices, vertexT);
3550 0 : vertex1B= SETsecondt_(facet1->vertices, vertexT);
3551 0 : vertex2A= SETfirstt_(facet2->vertices, vertexT);
3552 0 : vertex2B= SETsecondt_(facet2->vertices, vertexT);
3553 0 : neighbor1A= SETfirstt_(facet1->neighbors, facetT);
3554 0 : neighbor1B= SETsecondt_(facet1->neighbors, facetT);
3555 0 : neighbor2A= SETfirstt_(facet2->neighbors, facetT);
3556 0 : neighbor2B= SETsecondt_(facet2->neighbors, facetT);
3557 0 : if (vertex1A == vertex2A) {
3558 0 : vertexA= vertex1B;
3559 0 : vertexB= vertex2B;
3560 0 : neighborA= neighbor2A;
3561 0 : neighborB= neighbor1A;
3562 0 : }else if (vertex1A == vertex2B) {
3563 0 : vertexA= vertex1B;
3564 0 : vertexB= vertex2A;
3565 0 : neighborA= neighbor2B;
3566 0 : neighborB= neighbor1A;
3567 0 : }else if (vertex1B == vertex2A) {
3568 0 : vertexA= vertex1A;
3569 0 : vertexB= vertex2B;
3570 0 : neighborA= neighbor2A;
3571 0 : neighborB= neighbor1B;
3572 : }else { /* 1B == 2B */
3573 0 : vertexA= vertex1A;
3574 0 : vertexB= vertex2A;
3575 0 : neighborA= neighbor2B;
3576 0 : neighborB= neighbor1B;
3577 : }
3578 : /* vertexB always from facet2, neighborB always from facet1 */
3579 0 : if (vertexA->id > vertexB->id) {
3580 0 : SETfirst_(facet2->vertices)= vertexA;
3581 0 : SETsecond_(facet2->vertices)= vertexB;
3582 0 : if (vertexB == vertex2A)
3583 0 : facet2->toporient= !facet2->toporient;
3584 0 : SETfirst_(facet2->neighbors)= neighborA;
3585 0 : SETsecond_(facet2->neighbors)= neighborB;
3586 : }else {
3587 0 : SETfirst_(facet2->vertices)= vertexB;
3588 0 : SETsecond_(facet2->vertices)= vertexA;
3589 0 : if (vertexB == vertex2B)
3590 0 : facet2->toporient= !facet2->toporient;
3591 0 : SETfirst_(facet2->neighbors)= neighborB;
3592 0 : SETsecond_(facet2->neighbors)= neighborA;
3593 : }
3594 : /* qh_makeridges not needed since neighborB is not degenerate */
3595 0 : qh_setreplace(qh, neighborB->neighbors, facet1, facet2);
3596 0 : trace4((qh, qh->ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
3597 : vertexA->id, neighborB->id, facet1->id, facet2->id));
3598 0 : } /* mergefacet2d */
3599 :
3600 :
3601 : /*-<a href="qh-merge_r.htm#TOC"
3602 : >-------------------------------</a><a name="mergeneighbors">-</a>
3603 :
3604 : qh_mergeneighbors(qh, facet1, facet2 )
3605 : merges the neighbors of facet1 into facet2
3606 :
3607 : notes:
3608 : only called by qh_mergefacet
3609 : qh.hull_dim >= 3
3610 : see qh_mergecycle_neighbors
3611 :
3612 : design:
3613 : for each neighbor of facet1
3614 : if neighbor is also a neighbor of facet2
3615 : if neighbor is simplicial
3616 : make ridges for later deletion as a degenerate facet
3617 : update its neighbor set
3618 : else
3619 : move the neighbor relation to facet2
3620 : remove the neighbor relation for facet1 and facet2
3621 : */
3622 0 : void qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2) {
3623 : facetT *neighbor, **neighborp;
3624 :
3625 0 : trace4((qh, qh->ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
3626 : facet1->id, facet2->id));
3627 0 : qh->visit_id++;
3628 0 : FOREACHneighbor_(facet2) {
3629 0 : neighbor->visitid= qh->visit_id;
3630 : }
3631 0 : FOREACHneighbor_(facet1) {
3632 0 : if (neighbor->visitid == qh->visit_id) {
3633 0 : if (neighbor->simplicial) /* is degen, needs ridges */
3634 0 : qh_makeridges(qh, neighbor);
3635 0 : if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
3636 0 : qh_setdel(neighbor->neighbors, facet1);
3637 : else {
3638 0 : qh_setdel(neighbor->neighbors, facet2);
3639 0 : qh_setreplace(qh, neighbor->neighbors, facet1, facet2);
3640 : }
3641 0 : }else if (neighbor != facet2) {
3642 0 : qh_setappend(qh, &(facet2->neighbors), neighbor);
3643 0 : qh_setreplace(qh, neighbor->neighbors, facet1, facet2);
3644 : }
3645 : }
3646 0 : qh_setdel(facet1->neighbors, facet2); /* here for makeridges */
3647 0 : qh_setdel(facet2->neighbors, facet1);
3648 0 : } /* mergeneighbors */
3649 :
3650 :
3651 : /*-<a href="qh-merge_r.htm#TOC"
3652 : >-------------------------------</a><a name="mergeridges">-</a>
3653 :
3654 : qh_mergeridges(qh, facet1, facet2 )
3655 : merges the ridge set of facet1 into facet2
3656 :
3657 : returns:
3658 : may delete all ridges for a vertex
3659 : sets vertex->delridge on deleted ridges
3660 :
3661 : see:
3662 : qh_mergecycle_ridges()
3663 :
3664 : design:
3665 : delete ridges between facet1 and facet2
3666 : mark (delridge) vertices on these ridges for later testing
3667 : for each remaining ridge
3668 : rename facet1 to facet2
3669 : */
3670 0 : void qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2) {
3671 : ridgeT *ridge, **ridgep;
3672 :
3673 0 : trace4((qh, qh->ferr, 4038, "qh_mergeridges: merge ridges of f%d into f%d\n",
3674 : facet1->id, facet2->id));
3675 0 : FOREACHridge_(facet2->ridges) {
3676 0 : if ((ridge->top == facet1) || (ridge->bottom == facet1)) {
3677 : /* ridge.nonconvex is irrelevant due to merge */
3678 0 : qh_delridge_merge(qh, ridge); /* expensive in high-d, could rebuild */
3679 0 : ridgep--; /* deleted this ridge, repeat with next ridge*/
3680 : }
3681 : }
3682 0 : FOREACHridge_(facet1->ridges) {
3683 0 : if (ridge->top == facet1) {
3684 0 : ridge->top= facet2;
3685 0 : ridge->simplicialtop= False;
3686 : }else { /* ridge.bottom is facet1 */
3687 0 : ridge->bottom= facet2;
3688 0 : ridge->simplicialbot= False;
3689 : }
3690 0 : qh_setappend(qh, &(facet2->ridges), ridge);
3691 : }
3692 0 : } /* mergeridges */
3693 :
3694 :
3695 : /*-<a href="qh-merge_r.htm#TOC"
3696 : >-------------------------------</a><a name="mergesimplex">-</a>
3697 :
3698 : qh_mergesimplex(qh, facet1, facet2, mergeapex )
3699 : merge simplicial facet1 into facet2
3700 : mergeapex==qh_MERGEapex if merging samecycle into horizon facet
3701 : vertex id is latest (most recently created)
3702 : facet1 may be contained in facet2
3703 : ridges exist for both facets
3704 :
3705 : returns:
3706 : facet2 with updated vertices, ridges, neighbors
3707 : updated neighbors for facet1's vertices
3708 : facet1 not deleted
3709 : sets vertex->delridge on deleted ridges
3710 :
3711 : notes:
3712 : special case code since this is the most common merge
3713 : called from qh_mergefacet()
3714 :
3715 : design:
3716 : if qh_MERGEapex
3717 : add vertices of facet2 to qh.new_vertexlist if necessary
3718 : add apex to facet2
3719 : else
3720 : for each ridge between facet1 and facet2
3721 : set vertex->delridge
3722 : determine the apex for facet1 (i.e., vertex to be merged)
3723 : unless apex already in facet2
3724 : insert apex into vertices for facet2
3725 : add vertices of facet2 to qh.new_vertexlist if necessary
3726 : add apex to qh.new_vertexlist if necessary
3727 : for each vertex of facet1
3728 : if apex
3729 : rename facet1 to facet2 in its vertex neighbors
3730 : else
3731 : delete facet1 from vertex neighbors
3732 : if only in facet2
3733 : add vertex to qh.del_vertices for later deletion
3734 : for each ridge of facet1
3735 : delete ridges between facet1 and facet2
3736 : append other ridges to facet2 after renaming facet to facet2
3737 : */
3738 20048 : void qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex) {
3739 : vertexT *vertex, **vertexp, *opposite;
3740 : ridgeT *ridge, **ridgep;
3741 20048 : boolT isnew= False;
3742 : facetT *neighbor, **neighborp, *otherfacet;
3743 :
3744 20048 : if (mergeapex) {
3745 20047 : opposite= SETfirstt_(facet1->vertices, vertexT); /* apex is opposite facet2. It has the last vertex id */
3746 20047 : trace4((qh, qh->ferr, 4086, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
3747 : opposite->id, facet1->id, facet2->id));
3748 20047 : if (!facet2->newfacet)
3749 20047 : qh_newvertices(qh, facet2->vertices); /* apex, the first vertex, is already new */
3750 20047 : if (SETfirstt_(facet2->vertices, vertexT) != opposite) {
3751 20047 : qh_setaddnth(qh, &facet2->vertices, 0, opposite);
3752 20047 : isnew= True;
3753 : }
3754 : }else {
3755 1 : zinc_(Zmergesimplex);
3756 4 : FOREACHvertex_(facet1->vertices)
3757 3 : vertex->seen= False;
3758 3 : FOREACHridge_(facet1->ridges) {
3759 3 : if (otherfacet_(ridge, facet1) == facet2) {
3760 3 : FOREACHvertex_(ridge->vertices) {
3761 2 : vertex->seen= True;
3762 2 : vertex->delridge= True;
3763 : }
3764 1 : break;
3765 : }
3766 : }
3767 3 : FOREACHvertex_(facet1->vertices) {
3768 3 : if (!vertex->seen)
3769 1 : break; /* must occur */
3770 : }
3771 1 : opposite= vertex;
3772 1 : trace4((qh, qh->ferr, 4039, "qh_mergesimplex: merge opposite v%d of f%d into facet f%d\n",
3773 : opposite->id, facet1->id, facet2->id));
3774 1 : isnew= qh_addfacetvertex(qh, facet2, opposite);
3775 1 : if (!facet2->newfacet)
3776 0 : qh_newvertices(qh, facet2->vertices);
3777 1 : else if (!opposite->newfacet) {
3778 1 : qh_removevertex(qh, opposite);
3779 1 : qh_appendvertex(qh, opposite);
3780 : }
3781 : }
3782 20048 : trace4((qh, qh->ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
3783 : facet1->id));
3784 80192 : FOREACHvertex_(facet1->vertices) {
3785 60144 : if (vertex == opposite && isnew)
3786 20048 : qh_setreplace(qh, vertex->neighbors, facet1, facet2);
3787 : else {
3788 40096 : qh_setdel(vertex->neighbors, facet1);
3789 40096 : if (!SETsecond_(vertex->neighbors))
3790 0 : qh_mergevertex_del(qh, vertex, facet1, facet2);
3791 : }
3792 : }
3793 20048 : trace4((qh, qh->ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
3794 : facet1->id, facet2->id));
3795 20048 : qh->visit_id++;
3796 108386 : FOREACHneighbor_(facet2)
3797 88338 : neighbor->visitid= qh->visit_id;
3798 80192 : FOREACHridge_(facet1->ridges) {
3799 60144 : otherfacet= otherfacet_(ridge, facet1);
3800 60144 : if (otherfacet == facet2) {
3801 : /* ridge.nonconvex is irrelevant due to merge */
3802 20048 : qh_delridge_merge(qh, ridge); /* expensive in high-d, could rebuild */
3803 20048 : ridgep--; /* deleted this ridge, repeat with next ridge*/
3804 20048 : qh_setdel(facet2->neighbors, facet1); /* a simplicial facet may have duplicate neighbors, need to delete each one */
3805 40096 : }else if (otherfacet->dupridge && !qh_setin(otherfacet->neighbors, facet1)) {
3806 0 : qh_fprintf(qh, qh->ferr, 6356, "qhull topology error (qh_mergesimplex): f%d is a dupridge of f%d, cannot merge f%d into f%d\n",
3807 : facet1->id, otherfacet->id, facet1->id, facet2->id);
3808 0 : qh_errexit2(qh, qh_ERRqhull, facet1, otherfacet);
3809 : }else {
3810 40096 : trace4((qh, qh->ferr, 4059, "qh_mergesimplex: move r%d with f%d to f%d, new neighbor? %d, maybe horizon? %d\n",
3811 : ridge->id, otherfacet->id, facet2->id, (otherfacet->visitid != qh->visit_id), (SETfirstt_(otherfacet->neighbors, facetT) == facet1)));
3812 40096 : qh_setappend(qh, &facet2->ridges, ridge);
3813 40096 : if (otherfacet->visitid != qh->visit_id) {
3814 40096 : qh_setappend(qh, &facet2->neighbors, otherfacet);
3815 40096 : qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);
3816 40096 : otherfacet->visitid= qh->visit_id;
3817 : }else {
3818 0 : if (otherfacet->simplicial) /* is degen, needs ridges */
3819 0 : qh_makeridges(qh, otherfacet);
3820 0 : if (SETfirstt_(otherfacet->neighbors, facetT) == facet1) {
3821 : /* keep new, otherfacet->neighbors->horizon */
3822 0 : qh_setdel(otherfacet->neighbors, facet2);
3823 0 : qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);
3824 : }else {
3825 : /* facet2 is already a neighbor of otherfacet, by f.visitid */
3826 0 : qh_setdel(otherfacet->neighbors, facet1);
3827 : }
3828 : }
3829 40096 : if (ridge->top == facet1) { /* wait until after qh_makeridges */
3830 20048 : ridge->top= facet2;
3831 20048 : ridge->simplicialtop= False;
3832 : }else {
3833 20048 : ridge->bottom= facet2;
3834 20048 : ridge->simplicialbot= False;
3835 : }
3836 : }
3837 : }
3838 20048 : trace3((qh, qh->ferr, 3006, "qh_mergesimplex: merged simplex f%d v%d into facet f%d\n",
3839 : facet1->id, opposite->id, facet2->id));
3840 20048 : } /* mergesimplex */
3841 :
3842 : /*-<a href="qh-merge_r.htm#TOC"
3843 : >-------------------------------</a><a name="mergevertex_del">-</a>
3844 :
3845 : qh_mergevertex_del(qh, vertex, facet1, facet2 )
3846 : delete a vertex because of merging facet1 into facet2
3847 :
3848 : returns:
3849 : deletes vertex from facet2
3850 : adds vertex to qh.del_vertices for later deletion
3851 : */
3852 0 : void qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2) {
3853 :
3854 0 : zinc_(Zmergevertex);
3855 0 : trace2((qh, qh->ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
3856 : vertex->id, facet1->id, facet2->id));
3857 0 : qh_setdelsorted(facet2->vertices, vertex);
3858 0 : vertex->deleted= True;
3859 0 : qh_setappend(qh, &qh->del_vertices, vertex);
3860 0 : } /* mergevertex_del */
3861 :
3862 : /*-<a href="qh-merge_r.htm#TOC"
3863 : >-------------------------------</a><a name="mergevertex_neighbors">-</a>
3864 :
3865 : qh_mergevertex_neighbors(qh, facet1, facet2 )
3866 : merge the vertex neighbors of facet1 to facet2
3867 :
3868 : returns:
3869 : if vertex is current qh.vertex_visit
3870 : deletes facet1 from vertex->neighbors
3871 : else
3872 : renames facet1 to facet2 in vertex->neighbors
3873 : deletes vertices if only one neighbor
3874 :
3875 : notes:
3876 : assumes vertex neighbor sets are good
3877 : */
3878 0 : void qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2) {
3879 : vertexT *vertex, **vertexp;
3880 :
3881 0 : trace4((qh, qh->ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighborset for f%d into f%d\n",
3882 : facet1->id, facet2->id));
3883 0 : if (qh->tracevertex) {
3884 0 : qh_fprintf(qh, qh->ferr, 8081, "qh_mergevertex_neighbors: of f%d into f%d at furthest p%d f0= %p\n",
3885 0 : facet1->id, facet2->id, qh->furthest_id, qh->tracevertex->neighbors->e[0].p);
3886 0 : qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);
3887 : }
3888 0 : FOREACHvertex_(facet1->vertices) {
3889 0 : if (vertex->visitid != qh->vertex_visit)
3890 0 : qh_setreplace(qh, vertex->neighbors, facet1, facet2);
3891 : else {
3892 0 : qh_setdel(vertex->neighbors, facet1);
3893 0 : if (!SETsecond_(vertex->neighbors))
3894 0 : qh_mergevertex_del(qh, vertex, facet1, facet2);
3895 : }
3896 : }
3897 0 : if (qh->tracevertex)
3898 0 : qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);
3899 0 : } /* mergevertex_neighbors */
3900 :
3901 :
3902 : /*-<a href="qh-merge_r.htm#TOC"
3903 : >-------------------------------</a><a name="mergevertices">-</a>
3904 :
3905 : qh_mergevertices(qh, vertices1, vertices2 )
3906 : merges the vertex set of facet1 into facet2
3907 :
3908 : returns:
3909 : replaces vertices2 with merged set
3910 : preserves vertex_visit for qh_mergevertex_neighbors
3911 : updates qh.newvertex_list
3912 :
3913 : design:
3914 : create a merged set of both vertices (in inverse id order)
3915 : */
3916 0 : void qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices2) {
3917 0 : int newsize= qh_setsize(qh, vertices1)+qh_setsize(qh, *vertices2) - qh->hull_dim + 1;
3918 : setT *mergedvertices;
3919 0 : vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);
3920 :
3921 0 : mergedvertices= qh_settemp(qh, newsize);
3922 0 : FOREACHvertex_(vertices1) {
3923 0 : if (!*vertex2 || vertex->id > (*vertex2)->id)
3924 0 : qh_setappend(qh, &mergedvertices, vertex);
3925 : else {
3926 0 : while (*vertex2 && (*vertex2)->id > vertex->id)
3927 0 : qh_setappend(qh, &mergedvertices, *vertex2++);
3928 0 : if (!*vertex2 || (*vertex2)->id < vertex->id)
3929 0 : qh_setappend(qh, &mergedvertices, vertex);
3930 : else
3931 0 : qh_setappend(qh, &mergedvertices, *vertex2++);
3932 : }
3933 : }
3934 0 : while (*vertex2)
3935 0 : qh_setappend(qh, &mergedvertices, *vertex2++);
3936 0 : if (newsize < qh_setsize(qh, mergedvertices)) {
3937 0 : qh_fprintf(qh, qh->ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
3938 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
3939 : }
3940 0 : qh_setfree(qh, vertices2);
3941 0 : *vertices2= mergedvertices;
3942 0 : qh_settemppop(qh);
3943 0 : } /* mergevertices */
3944 :
3945 :
3946 : /*-<a href="qh-merge_r.htm#TOC"
3947 : >-------------------------------</a><a name="neighbor_intersections">-</a>
3948 :
3949 : qh_neighbor_intersections(qh, vertex )
3950 : return intersection of all vertices in vertex->neighbors except for vertex
3951 :
3952 : returns:
3953 : returns temporary set of vertices
3954 : does not include vertex
3955 : NULL if a neighbor is simplicial
3956 : NULL if empty set
3957 :
3958 : notes:
3959 : only called by qh_redundant_vertex for qh_reducevertices
3960 : so f.vertices does not contain extraneous vertices that are not in f.ridges
3961 : used for renaming vertices
3962 :
3963 : design:
3964 : initialize the intersection set with vertices of the first two neighbors
3965 : delete vertex from the intersection
3966 : for each remaining neighbor
3967 : intersect its vertex set with the intersection set
3968 : return NULL if empty
3969 : return the intersection set
3970 : */
3971 0 : setT *qh_neighbor_intersections(qhT *qh, vertexT *vertex) {
3972 : facetT *neighbor, **neighborp, *neighborA, *neighborB;
3973 : setT *intersect;
3974 : int neighbor_i, neighbor_n;
3975 :
3976 0 : FOREACHneighbor_(vertex) {
3977 0 : if (neighbor->simplicial)
3978 0 : return NULL;
3979 : }
3980 0 : neighborA= SETfirstt_(vertex->neighbors, facetT);
3981 0 : neighborB= SETsecondt_(vertex->neighbors, facetT);
3982 0 : zinc_(Zintersectnum);
3983 0 : if (!neighborA)
3984 0 : return NULL;
3985 0 : if (!neighborB)
3986 0 : intersect= qh_setcopy(qh, neighborA->vertices, 0);
3987 : else
3988 0 : intersect= qh_vertexintersect_new(qh, neighborA->vertices, neighborB->vertices);
3989 0 : qh_settemppush(qh, intersect);
3990 0 : qh_setdelsorted(intersect, vertex);
3991 0 : FOREACHneighbor_i_(qh, vertex) {
3992 0 : if (neighbor_i >= 2) {
3993 0 : zinc_(Zintersectnum);
3994 0 : qh_vertexintersect(qh, &intersect, neighbor->vertices);
3995 0 : if (!SETfirst_(intersect)) {
3996 0 : zinc_(Zintersectfail);
3997 0 : qh_settempfree(qh, &intersect);
3998 0 : return NULL;
3999 : }
4000 : }
4001 : }
4002 0 : trace3((qh, qh->ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
4003 : qh_setsize(qh, intersect), vertex->id));
4004 0 : return intersect;
4005 : } /* neighbor_intersections */
4006 :
4007 : /*-<a href="qh-merge_r.htm#TOC"
4008 : >-------------------------------</a><a name="neighbor_vertices">-</a>
4009 :
4010 : qh_neighbor_vertices(qh, vertex )
4011 : return neighboring vertices for a vertex (not in subridge)
4012 : assumes vertices have full vertex->neighbors
4013 :
4014 : returns:
4015 : temporary set of vertices
4016 :
4017 : notes:
4018 : updates qh.visit_id and qh.vertex_visit
4019 : similar to qh_vertexridges
4020 :
4021 : */
4022 0 : setT *qh_neighbor_vertices(qhT *qh, vertexT *vertexA, setT *subridge) {
4023 : facetT *neighbor, **neighborp;
4024 : vertexT *vertex, **vertexp;
4025 0 : setT *vertices= qh_settemp(qh, qh->TEMPsize);
4026 :
4027 0 : qh->visit_id++;
4028 0 : FOREACHneighbor_(vertexA)
4029 0 : neighbor->visitid= qh->visit_id;
4030 0 : qh->vertex_visit++;
4031 0 : vertexA->visitid= qh->vertex_visit;
4032 0 : FOREACHvertex_(subridge) {
4033 0 : vertex->visitid= qh->vertex_visit;
4034 : }
4035 0 : FOREACHneighbor_(vertexA) {
4036 0 : if (*neighborp) /* no new ridges in last neighbor */
4037 0 : qh_neighbor_vertices_facet(qh, vertexA, neighbor, &vertices);
4038 : }
4039 0 : trace3((qh, qh->ferr, 3035, "qh_neighbor_vertices: %d non-subridge, vertex neighbors for v%d\n",
4040 : qh_setsize(qh, vertices), vertexA->id));
4041 0 : return vertices;
4042 : } /* neighbor_vertices */
4043 :
4044 : /*-<a href="qh-merge_r.htm#TOC"
4045 : >-------------------------------</a><a name="neighbor_vertices_facet">-</a>
4046 :
4047 : qh_neighbor_vertices_facet(qh, vertex, facet, vertices )
4048 : add neighboring vertices on ridges for vertex in facet
4049 : neighbor->visitid==qh.visit_id if it hasn't been visited
4050 : v.visitid==qh.vertex_visit if it is already in vertices
4051 :
4052 : returns:
4053 : vertices updated
4054 : sets facet->visitid to qh.visit_id-1
4055 :
4056 : notes:
4057 : only called by qh_neighbor_vertices
4058 : similar to qh_vertexridges_facet
4059 :
4060 : design:
4061 : for each ridge of facet
4062 : if ridge of visited neighbor (i.e., unprocessed)
4063 : if vertex in ridge
4064 : append unprocessed vertices of ridge
4065 : mark facet processed
4066 : */
4067 0 : void qh_neighbor_vertices_facet(qhT *qh, vertexT *vertexA, facetT *facet, setT **vertices) {
4068 : ridgeT *ridge, **ridgep;
4069 : facetT *neighbor;
4070 : vertexT *second, *last, *vertex, **vertexp;
4071 0 : int last_i= qh->hull_dim-2, count= 0;
4072 : boolT isridge;
4073 :
4074 0 : if (facet->simplicial) {
4075 0 : FOREACHvertex_(facet->vertices) {
4076 0 : if (vertex->visitid != qh->vertex_visit) {
4077 0 : vertex->visitid= qh->vertex_visit;
4078 0 : qh_setappend(qh, vertices, vertex);
4079 0 : count++;
4080 : }
4081 : }
4082 : }else {
4083 0 : FOREACHridge_(facet->ridges) {
4084 0 : neighbor= otherfacet_(ridge, facet);
4085 0 : if (neighbor->visitid == qh->visit_id) {
4086 0 : isridge= False;
4087 0 : if (SETfirst_(ridge->vertices) == vertexA) {
4088 0 : isridge= True;
4089 0 : }else if (last_i > 2) {
4090 0 : second= SETsecondt_(ridge->vertices, vertexT);
4091 0 : last= SETelemt_(ridge->vertices, last_i, vertexT);
4092 0 : if (second->id >= vertexA->id && last->id <= vertexA->id) { /* vertices inverse sorted by id */
4093 0 : if (second == vertexA || last == vertexA)
4094 0 : isridge= True;
4095 0 : else if (qh_setin(ridge->vertices, vertexA))
4096 0 : isridge= True;
4097 : }
4098 0 : }else if (SETelem_(ridge->vertices, last_i) == vertexA) {
4099 0 : isridge= True;
4100 0 : }else if (last_i > 1 && SETsecond_(ridge->vertices) == vertexA) {
4101 0 : isridge= True;
4102 : }
4103 0 : if (isridge) {
4104 0 : FOREACHvertex_(ridge->vertices) {
4105 0 : if (vertex->visitid != qh->vertex_visit) {
4106 0 : vertex->visitid= qh->vertex_visit;
4107 0 : qh_setappend(qh, vertices, vertex);
4108 0 : count++;
4109 : }
4110 : }
4111 : }
4112 : }
4113 : }
4114 : }
4115 0 : facet->visitid= qh->visit_id-1;
4116 0 : if (count) {
4117 0 : trace4((qh, qh->ferr, 4079, "qh_neighbor_vertices_facet: found %d vertex neighbors for v%d in f%d (simplicial? %d)\n",
4118 : count, vertexA->id, facet->id, facet->simplicial));
4119 : }
4120 0 : } /* neighbor_vertices_facet */
4121 :
4122 :
4123 : /*-<a href="qh-merge_r.htm#TOC"
4124 : >-------------------------------</a><a name="newvertices">-</a>
4125 :
4126 : qh_newvertices(qh, vertices )
4127 : add vertices to end of qh.vertex_list (marks as new vertices)
4128 :
4129 : returns:
4130 : vertices on qh.newvertex_list
4131 : vertex->newfacet set
4132 : */
4133 20047 : void qh_newvertices(qhT *qh, setT *vertices) {
4134 : vertexT *vertex, **vertexp;
4135 :
4136 108382 : FOREACHvertex_(vertices) {
4137 88335 : if (!vertex->newfacet) {
4138 48241 : qh_removevertex(qh, vertex);
4139 48241 : qh_appendvertex(qh, vertex);
4140 : }
4141 : }
4142 20047 : } /* newvertices */
4143 :
4144 : /*-<a href="qh-merge_r.htm#TOC"
4145 : >-------------------------------</a><a name="next_vertexmerge">-</a>
4146 :
4147 : qh_next_vertexmerge(qh )
4148 : return next vertex merge from qh.vertex_mergeset
4149 :
4150 : returns:
4151 : vertex merge either MRGvertices or MRGsubridge
4152 : drops merges of deleted vertices
4153 :
4154 : notes:
4155 : called from qh_merge_pinchedvertices
4156 : */
4157 0 : mergeT *qh_next_vertexmerge(qhT *qh /* qh.vertex_mergeset */) {
4158 : mergeT *merge;
4159 0 : int merge_i, merge_n, best_i= -1;
4160 0 : realT bestdist= REALmax;
4161 :
4162 0 : FOREACHmerge_i_(qh, qh->vertex_mergeset) {
4163 0 : if (!merge->vertex1 || !merge->vertex2) {
4164 0 : qh_fprintf(qh, qh->ferr, 6299, "qhull internal error (qh_next_vertexmerge): expecting two vertices for vertex merge. Got v%d v%d and optional f%d\n",
4165 0 : getid_(merge->vertex1), getid_(merge->vertex2), getid_(merge->facet1));
4166 0 : qh_errexit(qh, qh_ERRqhull, merge->facet1, NULL);
4167 : }
4168 0 : if (merge->vertex1->deleted || merge->vertex2->deleted) {
4169 0 : trace3((qh, qh->ferr, 3030, "qh_next_vertexmerge: drop merge of v%d (del? %d) into v%d (del? %d) due to deleted vertex of r%d and r%d\n",
4170 : merge->vertex1->id, merge->vertex1->deleted, merge->vertex2->id, merge->vertex2->deleted, getid_(merge->ridge1), getid_(merge->ridge2)));
4171 0 : qh_drop_mergevertex(qh, merge);
4172 0 : qh_setdelnth(qh, qh->vertex_mergeset, merge_i);
4173 0 : merge_i--; merge_n--;
4174 0 : qh_memfree(qh, merge, (int)sizeof(mergeT));
4175 0 : }else if (merge->distance < bestdist) {
4176 0 : bestdist= merge->distance;
4177 0 : best_i= merge_i;
4178 : }
4179 : }
4180 0 : merge= NULL;
4181 0 : if (best_i >= 0) {
4182 0 : merge= SETelemt_(qh->vertex_mergeset, best_i, mergeT);
4183 0 : if (bestdist/qh->ONEmerge > qh_WIDEpinched) {
4184 0 : if (merge->mergetype==MRGvertices) {
4185 0 : if (merge->ridge1->top == merge->ridge2->bottom && merge->ridge1->bottom == merge->ridge2->top)
4186 0 : qh_fprintf(qh, qh->ferr, 6391, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve opposite oriented ridges r%d and r%d in f%d and f%d. Nearest v%d and v%d dist %2.2g (%.1fx)\n",
4187 0 : merge->ridge1->id, merge->ridge2->id, merge->ridge1->top->id, merge->ridge1->bottom->id, merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh->ONEmerge);
4188 : else
4189 0 : qh_fprintf(qh, qh->ferr, 6381, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve duplicate ridges r%d and r%d. Nearest v%d and v%d dist %2.2g (%.1fx)\n",
4190 0 : merge->ridge1->id, merge->ridge2->id, merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh->ONEmerge);
4191 : }else {
4192 0 : qh_fprintf(qh, qh->ferr, 6208, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve dupridge. Nearest v%d and v%d dist %2.2g (%.1fx)\n",
4193 0 : merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh->ONEmerge);
4194 : }
4195 : /* it may be possible to find a different vertex, after other vertex merges have occurred */
4196 0 : qh_errexit(qh, qh_ERRtopology, NULL, merge->ridge1);
4197 : }
4198 0 : qh_setdelnth(qh, qh->vertex_mergeset, best_i);
4199 : }
4200 0 : return merge;
4201 : } /* next_vertexmerge */
4202 :
4203 : /*-<a href="qh-merge_r.htm#TOC"
4204 : >-------------------------------</a><a name="opposite_horizonfacet">-</a>
4205 :
4206 : qh_opposite_horizonfacet(qh, merge, opposite )
4207 : return horizon facet for one of the merge facets, and its opposite vertex across the ridge
4208 : assumes either facet1 or facet2 of merge is 'mergehorizon'
4209 : assumes both facets are simplicial facets on qh.new_facetlist
4210 :
4211 : returns:
4212 : horizon facet and opposite vertex
4213 :
4214 : notes:
4215 : called by qh_getpinchedmerges
4216 : */
4217 0 : facetT *qh_opposite_horizonfacet(qhT *qh, mergeT *merge, vertexT **opposite) {
4218 : facetT *facet, *horizon, *otherfacet;
4219 : int neighbor_i;
4220 :
4221 0 : if (!merge->facet1->simplicial || !merge->facet2->simplicial || (!merge->facet1->mergehorizon && !merge->facet2->mergehorizon)) {
4222 0 : qh_fprintf(qh, qh->ferr, 6273, "qhull internal error (qh_opposite_horizonfacet): expecting merge of simplicial facets, at least one of which is mergehorizon. Either simplicial or mergehorizon is wrong\n");
4223 0 : qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2);
4224 : }
4225 0 : if (merge->facet1->mergehorizon) {
4226 0 : facet= merge->facet1;
4227 0 : otherfacet= merge->facet2;
4228 : }else {
4229 0 : facet= merge->facet2;
4230 0 : otherfacet= merge->facet1;
4231 : }
4232 0 : horizon= SETfirstt_(facet->neighbors, facetT);
4233 0 : neighbor_i= qh_setindex(otherfacet->neighbors, facet);
4234 0 : if (neighbor_i==-1)
4235 0 : neighbor_i= qh_setindex(otherfacet->neighbors, qh_MERGEridge);
4236 0 : if (neighbor_i==-1) {
4237 0 : qh_fprintf(qh, qh->ferr, 6238, "qhull internal error (qh_opposite_horizonfacet): merge facet f%d not connected to mergehorizon f%d\n",
4238 : otherfacet->id, facet->id);
4239 0 : qh_errexit2(qh, qh_ERRqhull, otherfacet, facet);
4240 : }
4241 0 : *opposite= SETelemt_(otherfacet->vertices, neighbor_i, vertexT);
4242 0 : return horizon;
4243 : } /* opposite_horizonfacet */
4244 :
4245 :
4246 : /*-<a href="qh-merge_r.htm#TOC"
4247 : >-------------------------------</a><a name="reducevertices">-</a>
4248 :
4249 : qh_reducevertices(qh)
4250 : reduce extra vertices, shared vertices, and redundant vertices
4251 : facet->newmerge is set if merged since last call
4252 : vertex->delridge is set if vertex was on a deleted ridge
4253 : if !qh.MERGEvertices, only removes extra vertices
4254 :
4255 : returns:
4256 : True if also merged degen_redundant facets
4257 : vertices are renamed if possible
4258 : clears facet->newmerge and vertex->delridge
4259 :
4260 : notes:
4261 : called by qh_all_merges and qh_postmerge
4262 : ignored if 2-d
4263 :
4264 : design:
4265 : merge any degenerate or redundant facets
4266 : repeat until no more degenerate or redundant facets
4267 : for each newly merged facet
4268 : remove extra vertices
4269 : if qh.MERGEvertices
4270 : for each newly merged facet
4271 : for each vertex
4272 : if vertex was on a deleted ridge
4273 : rename vertex if it is shared
4274 : for each new, undeleted vertex
4275 : remove delridge flag
4276 : if vertex is redundant
4277 : merge degenerate or redundant facets
4278 : */
4279 9686 : boolT qh_reducevertices(qhT *qh) {
4280 9686 : int numshare=0, numrename= 0;
4281 9686 : boolT degenredun= False;
4282 : facetT *newfacet;
4283 : vertexT *vertex, **vertexp;
4284 :
4285 9686 : if (qh->hull_dim == 2)
4286 0 : return False;
4287 9686 : trace2((qh, qh->ferr, 2101, "qh_reducevertices: reduce extra vertices, shared vertices, and redundant vertices\n"));
4288 9686 : if (qh_merge_degenredundant(qh))
4289 0 : degenredun= True;
4290 9686 : LABELrestart:
4291 55571 : FORALLnew_facets {
4292 45885 : if (newfacet->newmerge) {
4293 20048 : if (!qh->MERGEvertices)
4294 0 : newfacet->newmerge= False;
4295 20048 : if (qh_remove_extravertices(qh, newfacet)) {
4296 0 : qh_degen_redundant_facet(qh, newfacet);
4297 0 : if (qh_merge_degenredundant(qh)) {
4298 0 : degenredun= True;
4299 0 : goto LABELrestart;
4300 : }
4301 : }
4302 : }
4303 : }
4304 9686 : if (!qh->MERGEvertices)
4305 0 : return False;
4306 55571 : FORALLnew_facets {
4307 45885 : if (newfacet->newmerge) {
4308 20048 : newfacet->newmerge= False;
4309 128434 : FOREACHvertex_(newfacet->vertices) {
4310 108386 : if (vertex->delridge) {
4311 40096 : if (qh_rename_sharedvertex(qh, vertex, newfacet)) {
4312 0 : numshare++;
4313 0 : if (qh_merge_degenredundant(qh)) {
4314 0 : degenredun= True;
4315 0 : goto LABELrestart;
4316 : }
4317 0 : vertexp--; /* repeat since deleted vertex */
4318 : }
4319 : }
4320 : }
4321 : }
4322 : }
4323 113499 : FORALLvertex_(qh->newvertex_list) {
4324 103813 : if (vertex->delridge && !vertex->deleted) {
4325 29603 : vertex->delridge= False;
4326 29603 : if (qh->hull_dim >= 4 && qh_redundant_vertex(qh, vertex)) {
4327 0 : numrename++;
4328 0 : if (qh_merge_degenredundant(qh)) {
4329 0 : degenredun= True;
4330 0 : goto LABELrestart;
4331 : }
4332 : }
4333 : }
4334 : }
4335 9686 : trace1((qh, qh->ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
4336 : numshare, numrename, degenredun));
4337 9686 : return degenredun;
4338 : } /* reducevertices */
4339 :
4340 : /*-<a href="qh-merge_r.htm#TOC"
4341 : >-------------------------------</a><a name="redundant_vertex">-</a>
4342 :
4343 : qh_redundant_vertex(qh, vertex )
4344 : rename a redundant vertex if qh_find_newvertex succeeds
4345 : assumes vertices have full vertex->neighbors
4346 :
4347 : returns:
4348 : if find a replacement vertex
4349 : returns new vertex
4350 : qh_renamevertex sets vertex->deleted for redundant vertex
4351 :
4352 : notes:
4353 : only called by qh_reducevertices for vertex->delridge and hull_dim >= 4
4354 : may add degenerate facets to qh.facet_mergeset
4355 : doesn't change vertex->neighbors or create redundant facets
4356 :
4357 : design:
4358 : intersect vertices of all facet neighbors of vertex
4359 : determine ridges for these vertices
4360 : if find a new vertex for vertex among these ridges and vertices
4361 : rename vertex to the new vertex
4362 : */
4363 0 : vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex) {
4364 0 : vertexT *newvertex= NULL;
4365 : setT *vertices, *ridges;
4366 :
4367 0 : trace3((qh, qh->ferr, 3008, "qh_redundant_vertex: check if v%d from a deleted ridge can be renamed\n", vertex->id));
4368 0 : if ((vertices= qh_neighbor_intersections(qh, vertex))) {
4369 0 : ridges= qh_vertexridges(qh, vertex, !qh_ALL);
4370 0 : if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges))) {
4371 0 : zinc_(Zrenameall);
4372 0 : qh_renamevertex(qh, vertex, newvertex, ridges, NULL, NULL); /* ridges invalidated */
4373 : }
4374 0 : qh_settempfree(qh, &ridges);
4375 0 : qh_settempfree(qh, &vertices);
4376 : }
4377 0 : return newvertex;
4378 : } /* redundant_vertex */
4379 :
4380 : /*-<a href="qh-merge_r.htm#TOC"
4381 : >-------------------------------</a><a name="remove_extravertices">-</a>
4382 :
4383 : qh_remove_extravertices(qh, facet )
4384 : remove extra vertices from non-simplicial facets
4385 :
4386 : returns:
4387 : returns True if it finds them
4388 : deletes facet from vertex neighbors
4389 : facet may be redundant (test with qh_degen_redundant)
4390 :
4391 : notes:
4392 : called by qh_renamevertex and qh_reducevertices
4393 : a merge (qh_reducevertices) or qh_renamevertex may drop all ridges for a vertex in a facet
4394 :
4395 : design:
4396 : for each vertex in facet
4397 : if vertex not in a ridge (i.e., no longer used)
4398 : delete vertex from facet
4399 : delete facet from vertex's neighbors
4400 : unless vertex in another facet
4401 : add vertex to qh.del_vertices for later deletion
4402 : */
4403 20048 : boolT qh_remove_extravertices(qhT *qh, facetT *facet) {
4404 : ridgeT *ridge, **ridgep;
4405 : vertexT *vertex, **vertexp;
4406 20048 : boolT foundrem= False;
4407 :
4408 20048 : if (facet->simplicial) {
4409 0 : return False;
4410 : }
4411 20048 : trace4((qh, qh->ferr, 4043, "qh_remove_extravertices: test non-simplicial f%d for extra vertices\n",
4412 : facet->id));
4413 128434 : FOREACHvertex_(facet->vertices)
4414 108386 : vertex->seen= False;
4415 128434 : FOREACHridge_(facet->ridges) {
4416 325158 : FOREACHvertex_(ridge->vertices)
4417 216772 : vertex->seen= True;
4418 : }
4419 128434 : FOREACHvertex_(facet->vertices) {
4420 108386 : if (!vertex->seen) {
4421 0 : foundrem= True;
4422 0 : zinc_(Zremvertex);
4423 0 : qh_setdelsorted(facet->vertices, vertex);
4424 0 : qh_setdel(vertex->neighbors, facet);
4425 0 : if (!qh_setsize(qh, vertex->neighbors)) {
4426 0 : vertex->deleted= True;
4427 0 : qh_setappend(qh, &qh->del_vertices, vertex);
4428 0 : zinc_(Zremvertexdel);
4429 0 : trace2((qh, qh->ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
4430 : }else
4431 0 : trace3((qh, qh->ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
4432 0 : vertexp--; /*repeat*/
4433 : }
4434 : }
4435 20048 : return foundrem;
4436 : } /* remove_extravertices */
4437 :
4438 : /*-<a href="qh-merge_r.htm#TOC"
4439 : >-------------------------------</a><a name="remove_mergetype">-</a>
4440 :
4441 : qh_remove_mergetype(qh, mergeset, mergetype )
4442 : Remove mergetype merges from mergeset
4443 :
4444 : notes:
4445 : Does not preserve order
4446 : */
4447 0 : void qh_remove_mergetype(qhT *qh, setT *mergeset, mergeType type) {
4448 : mergeT *merge;
4449 : int merge_i, merge_n;
4450 :
4451 0 : FOREACHmerge_i_(qh, mergeset) {
4452 0 : if (merge->mergetype == type) {
4453 0 : trace3((qh, qh->ferr, 3037, "qh_remove_mergetype: remove merge f%d f%d v%d v%d r%d r%d dist %2.2g type %d",
4454 : getid_(merge->facet1), getid_(merge->facet2), getid_(merge->vertex1), getid_(merge->vertex2), getid_(merge->ridge1), getid_(merge->ridge2), merge->distance, type));
4455 0 : qh_setdelnth(qh, mergeset, merge_i);
4456 0 : merge_i--; merge_n--; /* repeat with next merge */
4457 : }
4458 : }
4459 0 : } /* remove_mergetype */
4460 :
4461 : /*-<a href="qh-merge_r.htm#TOC"
4462 : >-------------------------------</a><a name="rename_adjacentvertex">-</a>
4463 :
4464 : qh_rename_adjacentvertex(qh, oldvertex, newvertex )
4465 : renames oldvertex as newvertex. Must be adjacent (i.e., in the same subridge)
4466 : no-op if either vertex is deleted
4467 :
4468 : notes:
4469 : called from qh_merge_pinchedvertices
4470 :
4471 : design:
4472 : for all neighbors of oldvertex
4473 : if simplicial, rename oldvertex to newvertex and drop if degenerate
4474 : if needed, add oldvertex neighbor to newvertex
4475 : determine ridges for oldvertex
4476 : rename oldvertex as newvertex in ridges (qh_renamevertex)
4477 : */
4478 0 : void qh_rename_adjacentvertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, realT dist) {
4479 : setT *ridges;
4480 0 : facetT *neighbor, **neighborp, *maxfacet= NULL;
4481 : ridgeT *ridge, **ridgep;
4482 0 : boolT istrace= False;
4483 0 : int oldsize= qh_setsize(qh, oldvertex->neighbors);
4484 0 : int newsize= qh_setsize(qh, newvertex->neighbors);
4485 0 : coordT maxdist2= -REALmax, dist2;
4486 :
4487 0 : if (qh->IStracing >= 4 || oldvertex->id == qh->tracevertex_id || newvertex->id == qh->tracevertex_id) {
4488 0 : istrace= True;
4489 : }
4490 0 : zzinc_(Ztotmerge);
4491 0 : if (istrace) {
4492 0 : qh_fprintf(qh, qh->ferr, 2071, "qh_rename_adjacentvertex: merge #%d rename v%d (%d neighbors) to v%d (%d neighbors) dist %2.2g\n",
4493 : zzval_(Ztotmerge), oldvertex->id, oldsize, newvertex->id, newsize, dist);
4494 : }
4495 0 : if (oldvertex->deleted || newvertex->deleted) {
4496 0 : if (istrace || qh->IStracing >= 2) {
4497 0 : qh_fprintf(qh, qh->ferr, 2072, "qh_rename_adjacentvertex: ignore rename. Either v%d (%d) or v%d (%d) is deleted\n",
4498 0 : oldvertex->id, oldvertex->deleted, newvertex->id, newvertex->deleted);
4499 : }
4500 0 : return;
4501 : }
4502 0 : if (oldsize == 0 || newsize == 0) {
4503 0 : qh_fprintf(qh, qh->ferr, 2072, "qhull internal error (qh_rename_adjacentvertex): expecting neighbor facets for v%d and v%d. Got %d and %d neighbors resp.\n",
4504 : oldvertex->id, newvertex->id, oldsize, newsize);
4505 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
4506 : }
4507 0 : FOREACHneighbor_(oldvertex) {
4508 0 : if (neighbor->simplicial) {
4509 0 : if (qh_setin(neighbor->vertices, newvertex)) {
4510 0 : if (istrace || qh->IStracing >= 2) {
4511 0 : qh_fprintf(qh, qh->ferr, 2070, "qh_rename_adjacentvertex: simplicial f%d contains old v%d and new v%d. Will be marked degenerate by qh_renamevertex\n",
4512 : neighbor->id, oldvertex->id, newvertex->id);
4513 : }
4514 0 : qh_makeridges(qh, neighbor); /* no longer simplicial, nummerge==0, skipped by qh_maybe_duplicateridge */
4515 : }else {
4516 0 : qh_replacefacetvertex(qh, neighbor, oldvertex, newvertex);
4517 0 : qh_setunique(qh, &newvertex->neighbors, neighbor);
4518 0 : qh_newvertices(qh, neighbor->vertices); /* for qh_update_vertexneighbors of vertex neighbors */
4519 : }
4520 : }
4521 : }
4522 0 : ridges= qh_vertexridges(qh, oldvertex, qh_ALL);
4523 0 : if (istrace) {
4524 0 : FOREACHridge_(ridges) {
4525 0 : qh_printridge(qh, qh->ferr, ridge);
4526 : }
4527 : }
4528 0 : FOREACHneighbor_(oldvertex) {
4529 0 : if (!neighbor->simplicial){
4530 0 : qh_addfacetvertex(qh, neighbor, newvertex);
4531 0 : qh_setunique(qh, &newvertex->neighbors, neighbor);
4532 0 : qh_newvertices(qh, neighbor->vertices); /* for qh_update_vertexneighbors of vertex neighbors */
4533 0 : if (qh->newfacet_list == qh->facet_tail) {
4534 0 : qh_removefacet(qh, neighbor); /* add a neighbor to newfacet_list so that qh_partitionvisible has a newfacet */
4535 0 : qh_appendfacet(qh, neighbor);
4536 0 : neighbor->newfacet= True;
4537 : }
4538 : }
4539 : }
4540 0 : qh_renamevertex(qh, oldvertex, newvertex, ridges, NULL, NULL); /* ridges invalidated */
4541 0 : if (oldvertex->deleted && !oldvertex->partitioned) {
4542 0 : FOREACHneighbor_(newvertex) {
4543 0 : if (!neighbor->visible) {
4544 0 : qh_distplane(qh, oldvertex->point, neighbor, &dist2);
4545 0 : if (dist2>maxdist2) {
4546 0 : maxdist2= dist2;
4547 0 : maxfacet= neighbor;
4548 : }
4549 : }
4550 : }
4551 0 : trace2((qh, qh->ferr, 2096, "qh_rename_adjacentvertex: partition old p%d(v%d) as a coplanar point for furthest f%d dist %2.2g. Maybe repartition later (QH0031)\n",
4552 : qh_pointid(qh, oldvertex->point), oldvertex->id, maxfacet->id, maxdist2))
4553 0 : qh_partitioncoplanar(qh, oldvertex->point, maxfacet, NULL, !qh_ALL); /* faster with maxdist2, otherwise duplicates distance tests from maxdist2/dist2 */
4554 0 : oldvertex->partitioned= True;
4555 : }
4556 0 : qh_settempfree(qh, &ridges);
4557 : } /* rename_adjacentvertex */
4558 :
4559 : /*-<a href="qh-merge_r.htm#TOC"
4560 : >-------------------------------</a><a name="rename_sharedvertex">-</a>
4561 :
4562 : qh_rename_sharedvertex(qh, vertex, facet )
4563 : detect and rename if shared vertex in facet
4564 : vertices have full ->neighbors
4565 :
4566 : returns:
4567 : newvertex or NULL
4568 : the vertex may still exist in other facets (i.e., a neighbor was pinched)
4569 : does not change facet->neighbors
4570 : updates vertex->neighbors
4571 :
4572 : notes:
4573 : only called by qh_reducevertices after qh_remove_extravertices
4574 : so f.vertices does not contain extraneous vertices
4575 : a shared vertex for a facet is only in ridges to one neighbor
4576 : this may undo a pinched facet
4577 :
4578 : it does not catch pinches involving multiple facets. These appear
4579 : to be difficult to detect, since an exhaustive search is too expensive.
4580 :
4581 : design:
4582 : if vertex only has two neighbors
4583 : determine the ridges that contain the vertex
4584 : determine the vertices shared by both neighbors
4585 : if can find a new vertex in this set
4586 : rename the vertex to the new vertex
4587 : */
4588 40096 : vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet) {
4589 40096 : facetT *neighbor, **neighborp, *neighborA= NULL;
4590 : setT *vertices, *ridges;
4591 40096 : vertexT *newvertex= NULL;
4592 :
4593 40096 : if (qh_setsize(qh, vertex->neighbors) == 2) {
4594 0 : neighborA= SETfirstt_(vertex->neighbors, facetT);
4595 0 : if (neighborA == facet)
4596 0 : neighborA= SETsecondt_(vertex->neighbors, facetT);
4597 40096 : }else if (qh->hull_dim == 3)
4598 40096 : return NULL;
4599 : else {
4600 0 : qh->visit_id++;
4601 0 : FOREACHneighbor_(facet)
4602 0 : neighbor->visitid= qh->visit_id;
4603 0 : FOREACHneighbor_(vertex) {
4604 0 : if (neighbor->visitid == qh->visit_id) {
4605 0 : if (neighborA)
4606 0 : return NULL;
4607 0 : neighborA= neighbor;
4608 : }
4609 : }
4610 : }
4611 0 : if (!neighborA) {
4612 0 : qh_fprintf(qh, qh->ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
4613 : vertex->id, facet->id);
4614 0 : qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, vertex);
4615 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL);
4616 : }
4617 0 : if (neighborA) { /* avoid warning */
4618 : /* the vertex is shared by facet and neighborA */
4619 0 : ridges= qh_settemp(qh, qh->TEMPsize);
4620 0 : neighborA->visitid= ++qh->visit_id;
4621 0 : qh_vertexridges_facet(qh, vertex, facet, &ridges);
4622 0 : trace2((qh, qh->ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
4623 : qh_pointid(qh, vertex->point), vertex->id, facet->id, qh_setsize(qh, ridges), neighborA->id));
4624 0 : zinc_(Zintersectnum);
4625 0 : vertices= qh_vertexintersect_new(qh, facet->vertices, neighborA->vertices);
4626 0 : qh_setdel(vertices, vertex);
4627 0 : qh_settemppush(qh, vertices);
4628 0 : if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))
4629 0 : qh_renamevertex(qh, vertex, newvertex, ridges, facet, neighborA); /* ridges invalidated */
4630 0 : qh_settempfree(qh, &vertices);
4631 0 : qh_settempfree(qh, &ridges);
4632 : }
4633 0 : return newvertex;
4634 : } /* rename_sharedvertex */
4635 :
4636 : /*-<a href="qh-merge_r.htm#TOC"
4637 : >-------------------------------</a><a name="renameridgevertex">-</a>
4638 :
4639 : qh_renameridgevertex(qh, ridge, oldvertex, newvertex )
4640 : renames oldvertex as newvertex in ridge
4641 :
4642 : returns:
4643 : True if renames oldvertex
4644 : False if deleted the ridge
4645 :
4646 : notes:
4647 : called by qh_renamevertex
4648 : caller sets newvertex->delridge for deleted ridges (qh_reducevertices)
4649 :
4650 : design:
4651 : delete oldvertex from ridge
4652 : if newvertex already in ridge
4653 : copy ridge->noconvex to another ridge if possible
4654 : delete the ridge
4655 : else
4656 : insert newvertex into the ridge
4657 : adjust the ridge's orientation
4658 : */
4659 0 : boolT qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
4660 0 : int nth= 0, oldnth;
4661 : facetT *temp;
4662 : vertexT *vertex, **vertexp;
4663 :
4664 0 : oldnth= qh_setindex(ridge->vertices, oldvertex);
4665 0 : if (oldnth < 0) {
4666 0 : qh_fprintf(qh, qh->ferr, 6424, "qhull internal error (qh_renameridgevertex): oldvertex v%d not found in r%d. Cannot rename to v%d\n",
4667 : oldvertex->id, ridge->id, newvertex->id);
4668 0 : qh_errexit(qh, qh_ERRqhull, NULL, ridge);
4669 : }
4670 0 : qh_setdelnthsorted(qh, ridge->vertices, oldnth);
4671 0 : FOREACHvertex_(ridge->vertices) {
4672 0 : if (vertex == newvertex) {
4673 0 : zinc_(Zdelridge);
4674 0 : if (ridge->nonconvex) /* only one ridge has nonconvex set */
4675 0 : qh_copynonconvex(qh, ridge);
4676 0 : trace2((qh, qh->ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n",
4677 : ridge->id, oldvertex->id, newvertex->id));
4678 0 : qh_delridge_merge(qh, ridge); /* ridge.vertices deleted */
4679 0 : return False;
4680 : }
4681 0 : if (vertex->id < newvertex->id)
4682 0 : break;
4683 0 : nth++;
4684 : }
4685 0 : qh_setaddnth(qh, &ridge->vertices, nth, newvertex);
4686 0 : ridge->simplicialtop= False;
4687 0 : ridge->simplicialbot= False;
4688 0 : if (abs(oldnth - nth)%2) {
4689 0 : trace3((qh, qh->ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
4690 : ridge->id));
4691 0 : temp= ridge->top;
4692 0 : ridge->top= ridge->bottom;
4693 0 : ridge->bottom= temp;
4694 : }
4695 0 : return True;
4696 : } /* renameridgevertex */
4697 :
4698 :
4699 : /*-<a href="qh-merge_r.htm#TOC"
4700 : >-------------------------------</a><a name="renamevertex">-</a>
4701 :
4702 : qh_renamevertex(qh, oldvertex, newvertex, ridges, oldfacet, neighborA )
4703 : renames oldvertex as newvertex in ridges of non-simplicial neighbors
4704 : set oldfacet/neighborA if oldvertex is shared between two facets (qh_rename_sharedvertex)
4705 : otherwise qh_redundant_vertex or qh_rename_adjacentvertex
4706 :
4707 : returns:
4708 : if oldfacet and multiple neighbors, oldvertex may still exist afterwards
4709 : otherwise sets oldvertex->deleted for later deletion
4710 : one or more ridges maybe deleted
4711 : ridges is invalidated
4712 : merges may be added to degen_mergeset via qh_maydropneighbor or qh_degen_redundant_facet
4713 :
4714 : notes:
4715 : qh_rename_sharedvertex can not change neighbors of newvertex (since it's a subset)
4716 : qh_redundant_vertex due to vertex->delridge for qh_reducevertices
4717 : qh_rename_adjacentvertex for complete renames
4718 :
4719 : design:
4720 : for each ridge in ridges
4721 : rename oldvertex to newvertex and delete degenerate ridges
4722 : if oldfacet not defined
4723 : for each non-simplicial neighbor of oldvertex
4724 : delete oldvertex from neighbor's vertices
4725 : remove extra vertices from neighbor
4726 : add oldvertex to qh.del_vertices
4727 : else if oldvertex only between oldfacet and neighborA
4728 : delete oldvertex from oldfacet and neighborA
4729 : add oldvertex to qh.del_vertices
4730 : else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)
4731 : delete oldvertex from oldfacet
4732 : delete oldfacet from old vertex's neighbors
4733 : remove extra vertices (e.g., oldvertex) from neighborA
4734 : */
4735 0 : void qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {
4736 : facetT *neighbor, **neighborp;
4737 : ridgeT *ridge, **ridgep;
4738 : int topsize, bottomsize;
4739 0 : boolT istrace= False;
4740 :
4741 : #ifndef qh_NOtrace
4742 0 : if (qh->IStracing >= 2 || oldvertex->id == qh->tracevertex_id ||
4743 0 : newvertex->id == qh->tracevertex_id) {
4744 0 : istrace= True;
4745 0 : qh_fprintf(qh, qh->ferr, 2086, "qh_renamevertex: rename v%d to v%d in %d ridges with old f%d and neighbor f%d\n",
4746 0 : oldvertex->id, newvertex->id, qh_setsize(qh, ridges), getid_(oldfacet), getid_(neighborA));
4747 : }
4748 : #endif
4749 0 : FOREACHridge_(ridges) {
4750 0 : if (qh_renameridgevertex(qh, ridge, oldvertex, newvertex)) { /* ridge is deleted if False, invalidating ridges */
4751 0 : topsize= qh_setsize(qh, ridge->top->vertices);
4752 0 : bottomsize= qh_setsize(qh, ridge->bottom->vertices);
4753 0 : if (topsize < qh->hull_dim || (topsize == qh->hull_dim && !ridge->top->simplicial && qh_setin(ridge->top->vertices, newvertex))) {
4754 0 : trace4((qh, qh->ferr, 4070, "qh_renamevertex: ignore duplicate check for r%d. top f%d (size %d) will be degenerate after rename v%d to v%d\n",
4755 : ridge->id, ridge->top->id, topsize, oldvertex->id, newvertex->id));
4756 0 : }else if (bottomsize < qh->hull_dim || (bottomsize == qh->hull_dim && !ridge->bottom->simplicial && qh_setin(ridge->bottom->vertices, newvertex))) {
4757 0 : trace4((qh, qh->ferr, 4071, "qh_renamevertex: ignore duplicate check for r%d. bottom f%d (size %d) will be degenerate after rename v%d to v%d\n",
4758 : ridge->id, ridge->bottom->id, bottomsize, oldvertex->id, newvertex->id));
4759 : }else
4760 0 : qh_maybe_duplicateridge(qh, ridge);
4761 : }
4762 : }
4763 0 : if (!oldfacet) {
4764 : /* stat Zrenameall or Zpinchduplicate */
4765 0 : if (istrace)
4766 0 : qh_fprintf(qh, qh->ferr, 2087, "qh_renamevertex: renaming v%d to v%d in several facets for qh_redundant_vertex or MRGsubridge\n",
4767 : oldvertex->id, newvertex->id);
4768 0 : FOREACHneighbor_(oldvertex) {
4769 0 : if (neighbor->simplicial) {
4770 0 : qh_degen_redundant_facet(qh, neighbor); /* e.g., rbox 175 C3,2e-13 D4 t1545235541 | qhull d */
4771 : }else {
4772 0 : if (istrace)
4773 0 : qh_fprintf(qh, qh->ferr, 4080, "qh_renamevertex: rename vertices in non-simplicial neighbor f%d of v%d\n", neighbor->id, oldvertex->id);
4774 0 : qh_maydropneighbor(qh, neighbor);
4775 0 : qh_setdelsorted(neighbor->vertices, oldvertex); /* if degenerate, qh_degen_redundant_facet will add to mergeset */
4776 0 : if (qh_remove_extravertices(qh, neighbor))
4777 0 : neighborp--; /* neighbor deleted from oldvertex neighborset */
4778 0 : qh_degen_redundant_facet(qh, neighbor); /* either direction may be redundant, faster if combine? */
4779 0 : qh_test_redundant_neighbors(qh, neighbor);
4780 0 : qh_test_degen_neighbors(qh, neighbor);
4781 : }
4782 : }
4783 0 : if (!oldvertex->deleted) {
4784 0 : oldvertex->deleted= True;
4785 0 : qh_setappend(qh, &qh->del_vertices, oldvertex);
4786 : }
4787 0 : }else if (qh_setsize(qh, oldvertex->neighbors) == 2) {
4788 0 : zinc_(Zrenameshare);
4789 0 : if (istrace)
4790 0 : qh_fprintf(qh, qh->ferr, 3039, "qh_renamevertex: renaming v%d to v%d in oldfacet f%d for qh_rename_sharedvertex\n",
4791 : oldvertex->id, newvertex->id, oldfacet->id);
4792 0 : FOREACHneighbor_(oldvertex) {
4793 0 : qh_setdelsorted(neighbor->vertices, oldvertex);
4794 0 : qh_degen_redundant_facet(qh, neighbor);
4795 : }
4796 0 : oldvertex->deleted= True;
4797 0 : qh_setappend(qh, &qh->del_vertices, oldvertex);
4798 : }else {
4799 0 : zinc_(Zrenamepinch);
4800 0 : if (istrace || qh->IStracing >= 1)
4801 0 : qh_fprintf(qh, qh->ferr, 3040, "qh_renamevertex: renaming pinched v%d to v%d between f%d and f%d\n",
4802 : oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);
4803 0 : qh_setdelsorted(oldfacet->vertices, oldvertex);
4804 0 : qh_setdel(oldvertex->neighbors, oldfacet);
4805 0 : if (qh_remove_extravertices(qh, neighborA))
4806 0 : qh_degen_redundant_facet(qh, neighborA);
4807 : }
4808 0 : if (oldfacet)
4809 0 : qh_degen_redundant_facet(qh, oldfacet);
4810 0 : } /* renamevertex */
4811 :
4812 : /*-<a href="qh-merge_r.htm#TOC"
4813 : >-------------------------------</a><a name="test_appendmerge">-</a>
4814 :
4815 : qh_test_appendmerge(qh, facet, neighbor, simplicial )
4816 : test convexity and append to qh.facet_mergeset if non-convex
4817 : if pre-merging,
4818 : no-op if qh.SKIPconvex, or qh.MERGEexact and coplanar
4819 : if simplicial, assumes centrum test is valid (e.g., adjacent, simplicial new facets)
4820 :
4821 : returns:
4822 : true if appends facet/neighbor to qh.facet_mergeset
4823 : sets facet->center as needed
4824 : does not change facet->seen
4825 :
4826 : notes:
4827 : called from qh_getmergeset_initial, qh_getmergeset, and qh_test_vneighbors
4828 : must be at least as strong as qh_checkconvex (poly2_r.c)
4829 : assumes !f.flipped
4830 :
4831 : design:
4832 : exit if qh.SKIPconvex ('Q0') and !qh.POSTmerging
4833 : if qh.cos_max ('An') is defined and merging coplanars
4834 : if the angle between facet normals is too shallow
4835 : append an angle-coplanar merge to qh.mergeset
4836 : return True
4837 : test convexity of facet and neighbor
4838 : */
4839 168819 : boolT qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor, boolT simplicial) {
4840 168819 : realT angle= -REALmax;
4841 168819 : boolT okangle= False;
4842 :
4843 168819 : if (qh->SKIPconvex && !qh->POSTmerging)
4844 0 : return False;
4845 168819 : if (qh->cos_max < REALmax/2 && (!qh->MERGEexact || qh->POSTmerging)) {
4846 0 : angle= qh_getangle(qh, facet->normal, neighbor->normal);
4847 0 : okangle= True;
4848 0 : zinc_(Zangletests);
4849 0 : if (angle > qh->cos_max) {
4850 0 : zinc_(Zcoplanarangle);
4851 0 : qh_appendmergeset(qh, facet, neighbor, MRGanglecoplanar, 0.0, angle);
4852 0 : trace2((qh, qh->ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
4853 : angle, facet->id, neighbor->id));
4854 0 : return True;
4855 : }
4856 : }
4857 168819 : if (simplicial || qh->hull_dim <= 3)
4858 168819 : return qh_test_centrum_merge(qh, facet, neighbor, angle, okangle);
4859 : else
4860 0 : return qh_test_nonsimplicial_merge(qh, facet, neighbor, angle, okangle);
4861 : } /* test_appendmerge */
4862 :
4863 : /*-<a href="qh-merge_r.htm#TOC"
4864 : >-------------------------------</a><a name="test_centrum_merge">-</a>
4865 :
4866 : qh_test_centrum_merge(qh, facet, neighbor, angle, okangle )
4867 : test centrum convexity and append non-convex facets to qh.facet_mergeset
4868 : 'angle' is angle between facets if okangle is true, otherwise use 0.0
4869 :
4870 : returns:
4871 : true if append facet/neighbor to qh.facet_mergeset
4872 : sets facet->center as needed
4873 : does not change facet->seen
4874 :
4875 : notes:
4876 : called from test_appendmerge if adjacent simplicial facets or 2-d/3-d
4877 : at least as strict as qh_checkconvex, including qh.DISTround ('En' and 'Rn')
4878 :
4879 : design:
4880 : make facet's centrum if needed
4881 : if facet's centrum is above the neighbor (qh.centrum_radius)
4882 : set isconcave
4883 :
4884 : if facet's centrum is not below the neighbor (-qh.centrum_radius)
4885 : set iscoplanar
4886 : make neighbor's centrum if needed
4887 : if neighbor's centrum is above the facet
4888 : set isconcave
4889 : else if neighbor's centrum is not below the facet
4890 : set iscoplanar
4891 : if isconcave or iscoplanar and merging coplanars
4892 : get angle if needed (qh.ANGLEmerge 'An')
4893 : append concave-coplanar, concave ,or coplanar merge to qh.mergeset
4894 : */
4895 168819 : boolT qh_test_centrum_merge(qhT *qh, facetT *facet, facetT *neighbor, realT angle, boolT okangle) {
4896 : coordT dist, dist2, mergedist;
4897 168819 : boolT isconcave= False, iscoplanar= False;
4898 :
4899 168819 : if (!facet->center)
4900 17491 : facet->center= qh_getcentrum(qh, facet);
4901 168819 : zzinc_(Zcentrumtests);
4902 168819 : qh_distplane(qh, facet->center, neighbor, &dist);
4903 168819 : if (dist > qh->centrum_radius)
4904 0 : isconcave= True;
4905 168819 : else if (dist >= -qh->centrum_radius)
4906 1 : iscoplanar= True;
4907 168819 : if (!neighbor->center)
4908 48425 : neighbor->center= qh_getcentrum(qh, neighbor);
4909 168819 : zzinc_(Zcentrumtests);
4910 168819 : qh_distplane(qh, neighbor->center, facet, &dist2);
4911 168819 : if (dist2 > qh->centrum_radius)
4912 0 : isconcave= True;
4913 168819 : else if (!iscoplanar && dist2 >= -qh->centrum_radius)
4914 0 : iscoplanar= True;
4915 168819 : if (!isconcave && (!iscoplanar || (qh->MERGEexact && !qh->POSTmerging)))
4916 168818 : return False;
4917 1 : if (!okangle && qh->ANGLEmerge) {
4918 0 : angle= qh_getangle(qh, facet->normal, neighbor->normal);
4919 0 : zinc_(Zangletests);
4920 : }
4921 1 : if (isconcave && iscoplanar) {
4922 0 : zinc_(Zconcavecoplanarridge);
4923 0 : if (dist > dist2)
4924 0 : qh_appendmergeset(qh, facet, neighbor, MRGconcavecoplanar, dist, angle);
4925 : else
4926 0 : qh_appendmergeset(qh, neighbor, facet, MRGconcavecoplanar, dist2, angle);
4927 0 : trace0((qh, qh->ferr, 36, "qh_test_centrum_merge: concave f%d to coplanar f%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
4928 : facet->id, neighbor->id, dist, dist2, angle, qh->furthest_id));
4929 1 : }else if (isconcave) {
4930 0 : mergedist= fmax_(dist, dist2);
4931 0 : zinc_(Zconcaveridge);
4932 0 : qh_appendmergeset(qh, facet, neighbor, MRGconcave, mergedist, angle);
4933 0 : trace0((qh, qh->ferr, 37, "qh_test_centrum_merge: concave f%d to f%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
4934 : facet->id, neighbor->id, dist, dist2, angle, qh->furthest_id));
4935 : }else /* iscoplanar */ {
4936 1 : mergedist= fmin_(fabs_(dist), fabs_(dist2));
4937 1 : zinc_(Zcoplanarcentrum);
4938 1 : qh_appendmergeset(qh, facet, neighbor, MRGcoplanar, mergedist, angle);
4939 1 : trace2((qh, qh->ferr, 2097, "qh_test_centrum_merge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
4940 : facet->id, neighbor->id, dist, dist2, angle));
4941 : }
4942 1 : return True;
4943 : } /* test_centrum_merge */
4944 :
4945 : /*-<a href="qh-merge_r.htm#TOC"
4946 : >-------------------------------</a><a name="test_degen_neighbors">-</a>
4947 :
4948 : qh_test_degen_neighbors(qh, facet )
4949 : append degenerate neighbors to qh.degen_mergeset
4950 :
4951 : notes:
4952 : called at end of qh_mergefacet() and qh_renamevertex()
4953 : call after test_redundant_facet() since MRGredundant is less expensive then MRGdegen
4954 : a degenerate facet has fewer than hull_dim neighbors
4955 : see: qh_merge_degenredundant()
4956 :
4957 : */
4958 1 : void qh_test_degen_neighbors(qhT *qh, facetT *facet) {
4959 : facetT *neighbor, **neighborp;
4960 : int size;
4961 :
4962 1 : trace4((qh, qh->ferr, 4073, "qh_test_degen_neighbors: test for degenerate neighbors of f%d\n", facet->id));
4963 4 : FOREACHneighbor_(facet) {
4964 3 : if (neighbor->visible) {
4965 0 : qh_fprintf(qh, qh->ferr, 6359, "qhull internal error (qh_test_degen_neighbors): facet f%d has deleted neighbor f%d (qh.visible_list)\n",
4966 : facet->id, neighbor->id);
4967 0 : qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
4968 : }
4969 3 : if (neighbor->degenerate || neighbor->redundant || neighbor->dupridge) /* will merge or delete */
4970 0 : continue;
4971 : /* merge flipped-degenerate facet before flipped facets */
4972 3 : if ((size= qh_setsize(qh, neighbor->neighbors)) < qh->hull_dim) {
4973 0 : qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, 0.0, 1.0);
4974 0 : trace2((qh, qh->ferr, 2019, "qh_test_degen_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id));
4975 : }
4976 : }
4977 1 : } /* test_degen_neighbors */
4978 :
4979 :
4980 : /*-<a href="qh-merge_r.htm#TOC"
4981 : >-------------------------------</a><a name="test_nonsimplicial_merge">-</a>
4982 :
4983 : qh_test_nonsimplicial_merge(qh, facet, neighbor, angle, okangle )
4984 : test centrum and vertex convexity and append non-convex or redundant facets to qh.facet_mergeset
4985 : 'angle' is angle between facets if okangle is true, otherwise use 0.0
4986 : skips coplanar merges if pre-merging with qh.MERGEexact ('Qx')
4987 :
4988 : returns:
4989 : true if appends facet/neighbor to qh.facet_mergeset
4990 : sets facet->center as needed
4991 : does not change facet->seen
4992 :
4993 : notes:
4994 : only called from test_appendmerge if a non-simplicial facet and at least 4-d
4995 : at least as strict as qh_checkconvex, including qh.DISTround ('En' and 'Rn')
4996 : centrums must be < -qh.centrum_radius
4997 : tests vertices as well as centrums since a facet may be twisted relative to its neighbor
4998 :
4999 : design:
5000 : set precision constants for maxoutside, clearlyconcave, minvertex, and coplanarcentrum
5001 : use maxoutside for coplanarcentrum if premerging with 'Qx' and qh_MAXcoplanarcentrum merges
5002 : otherwise use qh.centrum_radious for coplanarcentrum
5003 : make facet and neighbor centrums if needed
5004 : isconcave if a centrum is above neighbor (coplanarcentrum)
5005 : iscoplanar if a centrum is not below neighbor (-qh.centrum_radius)
5006 : maybeconvex if a centrum is clearly below neighbor (-clearyconvex)
5007 : return False if both centrums clearly below neighbor (-clearyconvex)
5008 : return MRGconcave if isconcave
5009 :
5010 : facets are neither clearly convex nor clearly concave
5011 : test vertices as well as centrums
5012 : if maybeconvex
5013 : determine mindist and maxdist for vertices of the other facet
5014 : maybe MRGredundant
5015 : otherwise
5016 : determine mindist and maxdist for vertices of either facet
5017 : maybe MRGredundant
5018 : maybeconvex if a vertex is clearly below neighbor (-clearconvex)
5019 :
5020 : vertices are concave if dist > clearlyconcave
5021 : vertices are twisted if dist > maxoutside (isconcave and maybeconvex)
5022 : return False if not concave and pre-merge of 'Qx' (qh.MERGEexact)
5023 : vertices are coplanar if dist in -minvertex..maxoutside
5024 : if !isconcave, vertices are coplanar if dist >= -qh.MAXcoplanar (n*qh.premerge_centrum)
5025 :
5026 : return False if neither concave nor coplanar
5027 : return MRGtwisted if isconcave and maybeconvex
5028 : return MRGconcavecoplanar if isconcave and isconvex
5029 : return MRGconcave if isconcave
5030 : return MRGcoplanar if iscoplanar
5031 : */
5032 0 : boolT qh_test_nonsimplicial_merge(qhT *qh, facetT *facet, facetT *neighbor, realT angle, boolT okangle) {
5033 : coordT dist, mindist, maxdist, mindist2, maxdist2, dist2, maxoutside, clearlyconcave, minvertex, clearlyconvex, mergedist, coplanarcentrum;
5034 0 : boolT isconcave= False, iscoplanar= False, maybeconvex= False, isredundant= False;
5035 0 : vertexT *maxvertex= NULL, *maxvertex2= NULL;
5036 :
5037 0 : maxoutside= fmax_(neighbor->maxoutside, qh->ONEmerge + qh->DISTround);
5038 0 : maxoutside= fmax_(maxoutside, facet->maxoutside);
5039 0 : clearlyconcave= qh_RATIOconcavehorizon * maxoutside;
5040 0 : minvertex= fmax_(-qh->min_vertex, qh->MAXcoplanar); /* non-negative, not available per facet, not used for iscoplanar */
5041 0 : clearlyconvex= qh_RATIOconvexmerge * minvertex; /* must be convex for MRGtwisted */
5042 0 : if (qh->MERGEexact && !qh->POSTmerging && (facet->nummerge > qh_MAXcoplanarcentrum || neighbor->nummerge > qh_MAXcoplanarcentrum))
5043 0 : coplanarcentrum= maxoutside;
5044 : else
5045 0 : coplanarcentrum= qh->centrum_radius;
5046 :
5047 0 : if (!facet->center)
5048 0 : facet->center= qh_getcentrum(qh, facet);
5049 0 : zzinc_(Zcentrumtests);
5050 0 : qh_distplane(qh, facet->center, neighbor, &dist);
5051 0 : if (dist > coplanarcentrum)
5052 0 : isconcave= True;
5053 0 : else if (dist >= -qh->centrum_radius)
5054 0 : iscoplanar= True;
5055 0 : else if (dist < -clearlyconvex)
5056 0 : maybeconvex= True;
5057 0 : if (!neighbor->center)
5058 0 : neighbor->center= qh_getcentrum(qh, neighbor);
5059 0 : zzinc_(Zcentrumtests);
5060 0 : qh_distplane(qh, neighbor->center, facet, &dist2);
5061 0 : if (dist2 > coplanarcentrum)
5062 0 : isconcave= True;
5063 0 : else if (dist2 >= -qh->centrum_radius)
5064 0 : iscoplanar= True;
5065 0 : else if (dist2 < -clearlyconvex) {
5066 0 : if (maybeconvex)
5067 0 : return False; /* both centrums clearly convex */
5068 0 : maybeconvex= True;
5069 : }
5070 0 : if (isconcave) {
5071 0 : if (!okangle && qh->ANGLEmerge) {
5072 0 : angle= qh_getangle(qh, facet->normal, neighbor->normal);
5073 0 : zinc_(Zangletests);
5074 : }
5075 0 : mergedist= fmax_(dist, dist2);
5076 0 : zinc_(Zconcaveridge);
5077 0 : qh_appendmergeset(qh, facet, neighbor, MRGconcave, mergedist, angle);
5078 0 : trace0((qh, qh->ferr, 18, "qh_test_nonsimplicial_merge: concave centrum for f%d or f%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
5079 : facet->id, neighbor->id, dist, dist2, angle, qh->furthest_id));
5080 0 : return True;
5081 : }
5082 : /* neither clearly convex nor clearly concave, test vertices as well as centrums */
5083 0 : if (maybeconvex) {
5084 0 : if (dist < -clearlyconvex) {
5085 0 : maxdist= dist; /* facet centrum clearly convex, no need to test its vertex distance */
5086 0 : mindist= dist;
5087 0 : maxvertex2= qh_furthestvertex(qh, neighbor, facet, &maxdist2, &mindist2);
5088 0 : if (!maxvertex2) {
5089 0 : qh_appendmergeset(qh, neighbor, facet, MRGredundant, maxdist2, qh_ANGLEnone);
5090 0 : isredundant= True;
5091 : }
5092 : }else { /* dist2 < -clearlyconvex */
5093 0 : maxdist2= dist2; /* neighbor centrum clearly convex, no need to test its vertex distance */
5094 0 : mindist2= dist2;
5095 0 : maxvertex= qh_furthestvertex(qh, facet, neighbor, &maxdist, &mindist);
5096 0 : if (!maxvertex) {
5097 0 : qh_appendmergeset(qh, facet, neighbor, MRGredundant, maxdist, qh_ANGLEnone);
5098 0 : isredundant= True;
5099 : }
5100 : }
5101 : }else {
5102 0 : maxvertex= qh_furthestvertex(qh, facet, neighbor, &maxdist, &mindist);
5103 0 : if (maxvertex) {
5104 0 : maxvertex2= qh_furthestvertex(qh, neighbor, facet, &maxdist2, &mindist2);
5105 0 : if (!maxvertex2) {
5106 0 : qh_appendmergeset(qh, neighbor, facet, MRGredundant, maxdist2, qh_ANGLEnone);
5107 0 : isredundant= True;
5108 0 : }else if (mindist < -clearlyconvex || mindist2 < -clearlyconvex)
5109 0 : maybeconvex= True;
5110 : }else { /* !maxvertex */
5111 0 : qh_appendmergeset(qh, facet, neighbor, MRGredundant, maxdist, qh_ANGLEnone);
5112 0 : isredundant= True;
5113 : }
5114 : }
5115 0 : if (isredundant) {
5116 0 : zinc_(Zredundantmerge);
5117 0 : return True;
5118 : }
5119 :
5120 0 : if (maxdist > clearlyconcave || maxdist2 > clearlyconcave)
5121 0 : isconcave= True;
5122 0 : else if (maybeconvex) {
5123 0 : if (maxdist > maxoutside || maxdist2 > maxoutside)
5124 0 : isconcave= True; /* MRGtwisted */
5125 : }
5126 0 : if (!isconcave && qh->MERGEexact && !qh->POSTmerging)
5127 0 : return False;
5128 0 : if (isconcave && !iscoplanar) {
5129 0 : if (maxdist < maxoutside && (-qh->MAXcoplanar || (maxdist2 < maxoutside && mindist2 >= -qh->MAXcoplanar)))
5130 0 : iscoplanar= True; /* MRGconcavecoplanar */
5131 0 : }else if (!iscoplanar) {
5132 0 : if (mindist >= -qh->MAXcoplanar || mindist2 >= -qh->MAXcoplanar)
5133 0 : iscoplanar= True; /* MRGcoplanar */
5134 : }
5135 0 : if (!isconcave && !iscoplanar)
5136 0 : return False;
5137 0 : if (!okangle && qh->ANGLEmerge) {
5138 0 : angle= qh_getangle(qh, facet->normal, neighbor->normal);
5139 0 : zinc_(Zangletests);
5140 : }
5141 0 : if (isconcave && maybeconvex) {
5142 0 : zinc_(Ztwistedridge);
5143 0 : if (maxdist > maxdist2)
5144 0 : qh_appendmergeset(qh, facet, neighbor, MRGtwisted, maxdist, angle);
5145 : else
5146 0 : qh_appendmergeset(qh, neighbor, facet, MRGtwisted, maxdist2, angle);
5147 0 : trace0((qh, qh->ferr, 27, "qh_test_nonsimplicial_merge: twisted concave f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
5148 : facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh->furthest_id));
5149 0 : }else if (isconcave && iscoplanar) {
5150 0 : zinc_(Zconcavecoplanarridge);
5151 0 : if (maxdist > maxdist2)
5152 0 : qh_appendmergeset(qh, facet, neighbor, MRGconcavecoplanar, maxdist, angle);
5153 : else
5154 0 : qh_appendmergeset(qh, neighbor, facet, MRGconcavecoplanar, maxdist2, angle);
5155 0 : trace0((qh, qh->ferr, 28, "qh_test_nonsimplicial_merge: concave coplanar f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
5156 : facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh->furthest_id));
5157 0 : }else if (isconcave) {
5158 0 : mergedist= fmax_(maxdist, maxdist2);
5159 0 : zinc_(Zconcaveridge);
5160 0 : qh_appendmergeset(qh, facet, neighbor, MRGconcave, mergedist, angle);
5161 0 : trace0((qh, qh->ferr, 29, "qh_test_nonsimplicial_merge: concave f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
5162 : facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh->furthest_id));
5163 : }else /* iscoplanar */ {
5164 0 : mergedist= fmax_(fmax_(maxdist, maxdist2), fmax_(-mindist, -mindist2));
5165 0 : zinc_(Zcoplanarcentrum);
5166 0 : qh_appendmergeset(qh, facet, neighbor, MRGcoplanar, mergedist, angle);
5167 0 : trace2((qh, qh->ferr, 2099, "qh_test_nonsimplicial_merge: coplanar f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
5168 : facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh->furthest_id));
5169 : }
5170 0 : return True;
5171 : } /* test_nonsimplicial_merge */
5172 :
5173 : /*-<a href="qh-merge_r.htm#TOC"
5174 : >-------------------------------</a><a name="test_redundant_neighbors">-</a>
5175 :
5176 : qh_test_redundant_neighbors(qh, facet )
5177 : append degenerate facet or its redundant neighbors to qh.degen_mergeset
5178 :
5179 : returns:
5180 : bumps vertex_visit
5181 :
5182 : notes:
5183 : called at end of qh_mergefacet(), qh_mergecycle_all(), and qh_renamevertex
5184 : call before qh_test_degen_neighbors (MRGdegen are more likely to cause problems)
5185 : a redundant neighbor's vertices is a subset of the facet's vertices
5186 : with pinched and flipped facets, a redundant neighbor may have a wildly different normal
5187 :
5188 : see qh_merge_degenredundant() and qh_-_facet()
5189 :
5190 : design:
5191 : if facet is degenerate
5192 : appends facet to degen_mergeset
5193 : else
5194 : appends redundant neighbors of facet to degen_mergeset
5195 : */
5196 20048 : void qh_test_redundant_neighbors(qhT *qh, facetT *facet) {
5197 : vertexT *vertex, **vertexp;
5198 : facetT *neighbor, **neighborp;
5199 : int size;
5200 :
5201 20048 : trace4((qh, qh->ferr, 4022, "qh_test_redundant_neighbors: test neighbors of f%d vertex_visit %d\n",
5202 : facet->id, qh->vertex_visit+1));
5203 20048 : if ((size= qh_setsize(qh, facet->neighbors)) < qh->hull_dim) {
5204 0 : qh_appendmergeset(qh, facet, facet, MRGdegen, 0.0, 1.0);
5205 0 : trace2((qh, qh->ferr, 2017, "qh_test_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));
5206 : }else {
5207 20048 : qh->vertex_visit++;
5208 128434 : FOREACHvertex_(facet->vertices)
5209 108386 : vertex->visitid= qh->vertex_visit;
5210 128434 : FOREACHneighbor_(facet) {
5211 108386 : if (neighbor->visible) {
5212 0 : qh_fprintf(qh, qh->ferr, 6360, "qhull internal error (qh_test_redundant_neighbors): facet f%d has deleted neighbor f%d (qh.visible_list)\n",
5213 : facet->id, neighbor->id);
5214 0 : qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
5215 : }
5216 108386 : if (neighbor->degenerate || neighbor->redundant || neighbor->dupridge) /* will merge or delete */
5217 0 : continue;
5218 108386 : if (facet->flipped && !neighbor->flipped) /* do not merge non-flipped into flipped */
5219 0 : continue;
5220 : /* merge redundant-flipped facet first */
5221 : /* uses early out instead of checking vertex count */
5222 212947 : FOREACHvertex_(neighbor->vertices) {
5223 212947 : if (vertex->visitid != qh->vertex_visit)
5224 108386 : break;
5225 : }
5226 108386 : if (!vertex) {
5227 0 : qh_appendmergeset(qh, neighbor, facet, MRGredundant, 0.0, 1.0);
5228 0 : trace2((qh, qh->ferr, 2018, "qh_test_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id));
5229 : }
5230 : }
5231 : }
5232 20048 : } /* test_redundant_neighbors */
5233 :
5234 : /*-<a href="qh-merge_r.htm#TOC"
5235 : >-------------------------------</a><a name="test_vneighbors">-</a>
5236 :
5237 : qh_test_vneighbors(qh)
5238 : test vertex neighbors for convexity
5239 : tests all facets on qh.newfacet_list
5240 :
5241 : returns:
5242 : true if non-convex vneighbors appended to qh.facet_mergeset
5243 : initializes vertex neighbors if needed
5244 :
5245 : notes:
5246 : called by qh_all_merges from qh_postmerge if qh.TESTvneighbors ('Qv')
5247 : assumes all facet neighbors have been tested
5248 : this can be expensive
5249 : this does not guarantee that a centrum is below all facets
5250 : but it is unlikely
5251 : uses qh.visit_id
5252 :
5253 : design:
5254 : build vertex neighbors if necessary
5255 : for all new facets
5256 : for all vertices
5257 : for each unvisited facet neighbor of the vertex
5258 : test new facet and neighbor for convexity
5259 : */
5260 0 : boolT qh_test_vneighbors(qhT *qh /* qh.newfacet_list */) {
5261 : facetT *newfacet, *neighbor, **neighborp;
5262 : vertexT *vertex, **vertexp;
5263 0 : int nummerges= 0;
5264 :
5265 0 : trace1((qh, qh->ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
5266 0 : if (!qh->VERTEXneighbors)
5267 0 : qh_vertexneighbors(qh);
5268 0 : FORALLnew_facets
5269 0 : newfacet->seen= False;
5270 0 : FORALLnew_facets {
5271 0 : newfacet->seen= True;
5272 0 : newfacet->visitid= qh->visit_id++;
5273 0 : FOREACHneighbor_(newfacet)
5274 0 : newfacet->visitid= qh->visit_id;
5275 0 : FOREACHvertex_(newfacet->vertices) {
5276 0 : FOREACHneighbor_(vertex) {
5277 0 : if (neighbor->seen || neighbor->visitid == qh->visit_id)
5278 0 : continue;
5279 0 : if (qh_test_appendmerge(qh, newfacet, neighbor, False)) /* ignores optimization for simplicial ridges */
5280 0 : nummerges++;
5281 : }
5282 : }
5283 : }
5284 0 : zadd_(Ztestvneighbor, nummerges);
5285 0 : trace1((qh, qh->ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
5286 : nummerges));
5287 0 : return (nummerges > 0);
5288 : } /* test_vneighbors */
5289 :
5290 : /*-<a href="qh-merge_r.htm#TOC"
5291 : >-------------------------------</a><a name="tracemerge">-</a>
5292 :
5293 : qh_tracemerge(qh, facet1, facet2 )
5294 : print trace message after merge
5295 : */
5296 20048 : void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) {
5297 20048 : boolT waserror= False;
5298 : const char *mergename;
5299 :
5300 : #ifndef qh_NOtrace
5301 20048 : if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *))
5302 20048 : mergename= mergetypes[mergetype];
5303 : else
5304 0 : mergename= mergetypes[MRGnone];
5305 20048 : if (qh->IStracing >= 4)
5306 0 : qh_errprint(qh, "MERGED", facet2, NULL, NULL, NULL);
5307 20048 : if (facet2 == qh->tracefacet || (qh->tracevertex && qh->tracevertex->newfacet)) {
5308 0 : qh_fprintf(qh, qh->ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d into f%d type %d (%s), furthest p%d\n",
5309 : facet1->id, facet2->id, mergetype, mergename, qh->furthest_id);
5310 0 : if (facet2 != qh->tracefacet)
5311 0 : qh_errprint(qh, "TRACE", qh->tracefacet,
5312 0 : (qh->tracevertex && qh->tracevertex->neighbors) ?
5313 0 : SETfirstt_(qh->tracevertex->neighbors, facetT) : NULL,
5314 : NULL, qh->tracevertex);
5315 : }
5316 20048 : if (qh->tracevertex) {
5317 0 : if (qh->tracevertex->deleted)
5318 0 : qh_fprintf(qh, qh->ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
5319 : qh->furthest_id);
5320 : else
5321 0 : qh_checkvertex(qh, qh->tracevertex, qh_ALL, &waserror);
5322 : }
5323 20048 : if (qh->tracefacet && qh->tracefacet->normal && !qh->tracefacet->visible)
5324 0 : qh_checkfacet(qh, qh->tracefacet, True /* newmerge */, &waserror);
5325 : #endif /* !qh_NOtrace */
5326 20048 : if (qh->CHECKfrequently || qh->IStracing >= 4) { /* can't check polygon here */
5327 0 : if (qh->IStracing >= 4 && qh->num_facets < 500) {
5328 0 : qh_printlists(qh);
5329 : }
5330 0 : qh_checkfacet(qh, facet2, True /* newmerge */, &waserror);
5331 : }
5332 20048 : if (waserror)
5333 0 : qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* erroneous facet logged by qh_checkfacet */
5334 20048 : } /* tracemerge */
5335 :
5336 : /*-<a href="qh-merge_r.htm#TOC"
5337 : >-------------------------------</a><a name="tracemerging">-</a>
5338 :
5339 : qh_tracemerging(qh)
5340 : print trace message during POSTmerging
5341 :
5342 : returns:
5343 : updates qh.mergereport
5344 :
5345 : notes:
5346 : called from qh_mergecycle() and qh_mergefacet()
5347 :
5348 : see:
5349 : qh_buildtracing()
5350 : */
5351 0 : void qh_tracemerging(qhT *qh) {
5352 : realT cpu;
5353 : int total;
5354 : time_t timedata;
5355 : struct tm *tp;
5356 :
5357 0 : qh->mergereport= zzval_(Ztotmerge);
5358 0 : time(&timedata);
5359 0 : tp= localtime(&timedata);
5360 0 : cpu= qh_CPUclock;
5361 0 : cpu /= qh_SECticks;
5362 0 : total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
5363 0 : qh_fprintf(qh, qh->ferr, 8087, "\n\
5364 : At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets with max_outside %2.2g, min_vertex %2.2g.\n\
5365 : The hull contains %d facets and %d vertices.\n",
5366 : tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, total, qh->max_outside, qh->min_vertex,
5367 0 : qh->num_facets - qh->num_visible,
5368 0 : qh->num_vertices-qh_setsize(qh, qh->del_vertices));
5369 0 : } /* tracemerging */
5370 :
5371 : /*-<a href="qh-merge_r.htm#TOC"
5372 : >-------------------------------</a><a name="updatetested">-</a>
5373 :
5374 : qh_updatetested(qh, facet1, facet2 )
5375 : clear facet2->tested and facet1->ridge->tested for merge
5376 :
5377 : returns:
5378 : deletes facet2->center unless it's already large
5379 : if so, clears facet2->ridge->tested
5380 :
5381 : notes:
5382 : only called by qh_mergefacet
5383 :
5384 : design:
5385 : clear facet2->tested
5386 : clear ridge->tested for facet1's ridges
5387 : if facet2 has a centrum
5388 : if facet2 is large
5389 : set facet2->keepcentrum
5390 : else if facet2 has 3 vertices due to many merges, or not large and post merging
5391 : clear facet2->keepcentrum
5392 : unless facet2->keepcentrum
5393 : clear facet2->center to recompute centrum later
5394 : clear ridge->tested for facet2's ridges
5395 : */
5396 20048 : void qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2) {
5397 : ridgeT *ridge, **ridgep;
5398 : int size;
5399 :
5400 20048 : facet2->tested= False;
5401 80192 : FOREACHridge_(facet1->ridges)
5402 60144 : ridge->tested= False;
5403 20048 : if (!facet2->center)
5404 836 : return;
5405 19212 : size= qh_setsize(qh, facet2->vertices);
5406 19212 : if (!facet2->keepcentrum) {
5407 18768 : if (size > qh->hull_dim + qh_MAXnewcentrum) {
5408 4 : facet2->keepcentrum= True;
5409 4 : zinc_(Zwidevertices);
5410 : }
5411 444 : }else if (size <= qh->hull_dim + qh_MAXnewcentrum) {
5412 : /* center and keepcentrum was set */
5413 0 : if (size == qh->hull_dim || qh->POSTmerging)
5414 0 : facet2->keepcentrum= False; /* if many merges need to recompute centrum */
5415 : }
5416 19212 : if (!facet2->keepcentrum) {
5417 18764 : qh_memfree(qh, facet2->center, qh->normal_size);
5418 18764 : facet2->center= NULL;
5419 75698 : FOREACHridge_(facet2->ridges)
5420 56934 : ridge->tested= False;
5421 : }
5422 : } /* updatetested */
5423 :
5424 : /*-<a href="qh-merge_r.htm#TOC"
5425 : >-------------------------------</a><a name="vertexridges">-</a>
5426 :
5427 : qh_vertexridges(qh, vertex, allneighbors )
5428 : return temporary set of ridges adjacent to a vertex
5429 : vertex->neighbors defined (qh_vertexneighbors)
5430 :
5431 : notes:
5432 : uses qh.visit_id
5433 : does not include implicit ridges for simplicial facets
5434 : skips last neighbor, unless allneighbors. For new facets, the last neighbor shares ridges with adjacent neighbors
5435 : if the last neighbor is not simplicial, it will have ridges for its simplicial neighbors
5436 : Use allneighbors when a new cone is attached to an existing convex hull
5437 : similar to qh_neighbor_vertices
5438 :
5439 : design:
5440 : for each neighbor of vertex
5441 : add ridges that include the vertex to ridges
5442 : */
5443 0 : setT *qh_vertexridges(qhT *qh, vertexT *vertex, boolT allneighbors) {
5444 : facetT *neighbor, **neighborp;
5445 0 : setT *ridges= qh_settemp(qh, qh->TEMPsize);
5446 : int size;
5447 :
5448 0 : qh->visit_id += 2; /* visit_id for vertex neighbors, visit_id-1 for facets of visited ridges */
5449 0 : FOREACHneighbor_(vertex)
5450 0 : neighbor->visitid= qh->visit_id;
5451 0 : FOREACHneighbor_(vertex) {
5452 0 : if (*neighborp || allneighbors) /* no new ridges in last neighbor */
5453 0 : qh_vertexridges_facet(qh, vertex, neighbor, &ridges);
5454 : }
5455 0 : if (qh->PRINTstatistics || qh->IStracing) {
5456 0 : size= qh_setsize(qh, ridges);
5457 0 : zinc_(Zvertexridge);
5458 0 : zadd_(Zvertexridgetot, size);
5459 0 : zmax_(Zvertexridgemax, size);
5460 0 : trace3((qh, qh->ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
5461 : size, vertex->id));
5462 : }
5463 0 : return ridges;
5464 : } /* vertexridges */
5465 :
5466 : /*-<a href="qh-merge_r.htm#TOC"
5467 : >-------------------------------</a><a name="vertexridges_facet">-</a>
5468 :
5469 : qh_vertexridges_facet(qh, vertex, facet, ridges )
5470 : add adjacent ridges for vertex in facet
5471 : neighbor->visitid==qh.visit_id if it hasn't been visited
5472 :
5473 : returns:
5474 : ridges updated
5475 : sets facet->visitid to qh.visit_id-1
5476 :
5477 : design:
5478 : for each ridge of facet
5479 : if ridge of visited neighbor (i.e., unprocessed)
5480 : if vertex in ridge
5481 : append ridge
5482 : mark facet processed
5483 : */
5484 0 : void qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges) {
5485 : ridgeT *ridge, **ridgep;
5486 : facetT *neighbor;
5487 0 : int last_i= qh->hull_dim-2;
5488 : vertexT *second, *last;
5489 :
5490 0 : FOREACHridge_(facet->ridges) {
5491 0 : neighbor= otherfacet_(ridge, facet);
5492 0 : if (neighbor->visitid == qh->visit_id) {
5493 0 : if (SETfirst_(ridge->vertices) == vertex) {
5494 0 : qh_setappend(qh, ridges, ridge);
5495 0 : }else if (last_i > 2) {
5496 0 : second= SETsecondt_(ridge->vertices, vertexT);
5497 0 : last= SETelemt_(ridge->vertices, last_i, vertexT);
5498 0 : if (second->id >= vertex->id && last->id <= vertex->id) { /* vertices inverse sorted by id */
5499 0 : if (second == vertex || last == vertex)
5500 0 : qh_setappend(qh, ridges, ridge);
5501 0 : else if (qh_setin(ridge->vertices, vertex))
5502 0 : qh_setappend(qh, ridges, ridge);
5503 : }
5504 0 : }else if (SETelem_(ridge->vertices, last_i) == vertex
5505 0 : || (last_i > 1 && SETsecond_(ridge->vertices) == vertex)) {
5506 0 : qh_setappend(qh, ridges, ridge);
5507 : }
5508 : }
5509 : }
5510 0 : facet->visitid= qh->visit_id-1;
5511 0 : } /* vertexridges_facet */
5512 :
5513 : /*-<a href="qh-merge_r.htm#TOC"
5514 : >-------------------------------</a><a name="willdelete">-</a>
5515 :
5516 : qh_willdelete(qh, facet, replace )
5517 : moves facet to visible list for qh_deletevisible
5518 : sets facet->f.replace to replace (may be NULL)
5519 : clears f.ridges and f.neighbors -- no longer valid
5520 :
5521 : returns:
5522 : bumps qh.num_visible
5523 : */
5524 63263 : void qh_willdelete(qhT *qh, facetT *facet, facetT *replace) {
5525 :
5526 63263 : trace4((qh, qh->ferr, 4081, "qh_willdelete: move f%d to visible list, set its replacement as f%d, and clear f.neighbors and f.ridges\n", facet->id, getid_(replace)));
5527 63263 : if (!qh->visible_list && qh->newfacet_list) {
5528 0 : qh_fprintf(qh, qh->ferr, 6378, "qhull internal error (qh_willdelete): expecting qh.visible_list at before qh.newfacet_list f%d. Got NULL\n",
5529 0 : qh->newfacet_list->id);
5530 0 : qh_errexit2(qh, qh_ERRqhull, NULL, NULL);
5531 : }
5532 63263 : qh_removefacet(qh, facet);
5533 63263 : qh_prependfacet(qh, facet, &qh->visible_list);
5534 63263 : qh->num_visible++;
5535 63263 : facet->visible= True;
5536 63263 : facet->f.replace= replace;
5537 63263 : if (facet->ridges)
5538 34453 : SETfirst_(facet->ridges)= NULL;
5539 63263 : if (facet->neighbors)
5540 63263 : SETfirst_(facet->neighbors)= NULL;
5541 63263 : } /* willdelete */
5542 :
5543 : #else /* qh_NOmerge */
5544 :
5545 : void qh_all_vertexmerges(qhT *qh, int apexpointid, facetT *facet, facetT **retryfacet) {
5546 : QHULL_UNUSED(qh)
5547 : QHULL_UNUSED(apexpointid)
5548 : QHULL_UNUSED(facet)
5549 : QHULL_UNUSED(retryfacet)
5550 : }
5551 : void qh_premerge(qhT *qh, int apexpointid, realT maxcentrum, realT maxangle) {
5552 : QHULL_UNUSED(qh)
5553 : QHULL_UNUSED(apexpointid)
5554 : QHULL_UNUSED(maxcentrum)
5555 : QHULL_UNUSED(maxangle)
5556 : }
5557 : void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
5558 : boolT vneighbors) {
5559 : QHULL_UNUSED(qh)
5560 : QHULL_UNUSED(reason)
5561 : QHULL_UNUSED(maxcentrum)
5562 : QHULL_UNUSED(maxangle)
5563 : QHULL_UNUSED(vneighbors)
5564 : }
5565 : void qh_checkdelfacet(qhT *qh, facetT *facet, setT *mergeset) {
5566 : QHULL_UNUSED(qh)
5567 : QHULL_UNUSED(facet)
5568 : QHULL_UNUSED(mergeset)
5569 : }
5570 : void qh_checkdelridge(qhT *qh /* qh.visible_facets, vertex_mergeset */) {
5571 : QHULL_UNUSED(qh)
5572 : }
5573 : boolT qh_checkzero(qhT *qh, boolT testall) {
5574 : QHULL_UNUSED(qh)
5575 : QHULL_UNUSED(testall)
5576 :
5577 : return True;
5578 : }
5579 : void qh_freemergesets(qhT *qh) {
5580 : QHULL_UNUSED(qh)
5581 : }
5582 : void qh_initmergesets(qhT *qh) {
5583 : QHULL_UNUSED(qh)
5584 : }
5585 : void qh_merge_pinchedvertices(qhT *qh, int apexpointid /* qh.newfacet_list */) {
5586 : QHULL_UNUSED(qh)
5587 : QHULL_UNUSED(apexpointid)
5588 : }
5589 : #endif /* qh_NOmerge */
5590 :
|