mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-23 20:22:04 +00:00
Move many third-party components' source codes from the src folder to a new folder called deps_src. The goal is to make the code structure clearer and easier to navigate.
1206 lines
40 KiB
C
1206 lines
40 KiB
C
/*<html><pre> -<a href="qh-poly.htm"
|
|
>-------------------------------</a><a name="TOP">-</a>
|
|
|
|
poly.c
|
|
implements polygons and simplices
|
|
|
|
see qh-poly.htm, poly.h and libqhull.h
|
|
|
|
infrequent code is in poly2.c
|
|
(all but top 50 and their callers 12/3/95)
|
|
|
|
Copyright (c) 1993-2015 The Geometry Center.
|
|
$Id: //main/2015/qhull/src/libqhull/poly.c#3 $$Change: 2064 $
|
|
$DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
|
|
*/
|
|
|
|
#include "qhull_a.h"
|
|
|
|
/*======== functions in alphabetical order ==========*/
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="appendfacet">-</a>
|
|
|
|
qh_appendfacet( facet )
|
|
appends facet to end of qh.facet_list,
|
|
|
|
returns:
|
|
updates qh.newfacet_list, facet_next, facet_list
|
|
increments qh.numfacets
|
|
|
|
notes:
|
|
assumes qh.facet_list/facet_tail is defined (createsimplex)
|
|
|
|
see:
|
|
qh_removefacet()
|
|
|
|
*/
|
|
void qh_appendfacet(facetT *facet) {
|
|
facetT *tail= qh facet_tail;
|
|
|
|
if (tail == qh newfacet_list)
|
|
qh newfacet_list= facet;
|
|
if (tail == qh facet_next)
|
|
qh facet_next= facet;
|
|
facet->previous= tail->previous;
|
|
facet->next= tail;
|
|
if (tail->previous)
|
|
tail->previous->next= facet;
|
|
else
|
|
qh facet_list= facet;
|
|
tail->previous= facet;
|
|
qh num_facets++;
|
|
trace4((qh ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));
|
|
} /* appendfacet */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="appendvertex">-</a>
|
|
|
|
qh_appendvertex( vertex )
|
|
appends vertex to end of qh.vertex_list,
|
|
|
|
returns:
|
|
sets vertex->newlist
|
|
updates qh.vertex_list, newvertex_list
|
|
increments qh.num_vertices
|
|
|
|
notes:
|
|
assumes qh.vertex_list/vertex_tail is defined (createsimplex)
|
|
|
|
*/
|
|
void qh_appendvertex(vertexT *vertex) {
|
|
vertexT *tail= qh vertex_tail;
|
|
|
|
if (tail == qh newvertex_list)
|
|
qh newvertex_list= vertex;
|
|
vertex->newlist= True;
|
|
vertex->previous= tail->previous;
|
|
vertex->next= tail;
|
|
if (tail->previous)
|
|
tail->previous->next= vertex;
|
|
else
|
|
qh vertex_list= vertex;
|
|
tail->previous= vertex;
|
|
qh num_vertices++;
|
|
trace4((qh ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));
|
|
} /* appendvertex */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="attachnewfacets">-</a>
|
|
|
|
qh_attachnewfacets( )
|
|
attach horizon facets to new facets in qh.newfacet_list
|
|
newfacets have neighbor and ridge links to horizon but not vice versa
|
|
only needed for qh.ONLYgood
|
|
|
|
returns:
|
|
set qh.NEWfacets
|
|
horizon facets linked to new facets
|
|
ridges changed from visible facets to new facets
|
|
simplicial ridges deleted
|
|
qh.visible_list, no ridges valid
|
|
facet->f.replace is a newfacet (if any)
|
|
|
|
design:
|
|
delete interior ridges and neighbor sets by
|
|
for each visible, non-simplicial facet
|
|
for each ridge
|
|
if last visit or if neighbor is simplicial
|
|
if horizon neighbor
|
|
delete ridge for horizon's ridge set
|
|
delete ridge
|
|
erase neighbor set
|
|
attach horizon facets and new facets by
|
|
for all new facets
|
|
if corresponding horizon facet is simplicial
|
|
locate corresponding visible facet {may be more than one}
|
|
link visible facet to new facet
|
|
replace visible facet with new facet in horizon
|
|
else it's non-simplicial
|
|
for all visible neighbors of the horizon facet
|
|
link visible neighbor to new facet
|
|
delete visible neighbor from horizon facet
|
|
append new facet to horizon's neighbors
|
|
the first ridge of the new facet is the horizon ridge
|
|
link the new facet into the horizon ridge
|
|
*/
|
|
void qh_attachnewfacets(void /* qh.visible_list, newfacet_list */) {
|
|
facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;
|
|
ridgeT *ridge, **ridgep;
|
|
|
|
qh NEWfacets= True;
|
|
trace3((qh ferr, 3012, "qh_attachnewfacets: delete interior ridges\n"));
|
|
qh visit_id++;
|
|
FORALLvisible_facets {
|
|
visible->visitid= qh visit_id;
|
|
if (visible->ridges) {
|
|
FOREACHridge_(visible->ridges) {
|
|
neighbor= otherfacet_(ridge, visible);
|
|
if (neighbor->visitid == qh visit_id
|
|
|| (!neighbor->visible && neighbor->simplicial)) {
|
|
if (!neighbor->visible) /* delete ridge for simplicial horizon */
|
|
qh_setdel(neighbor->ridges, ridge);
|
|
qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */
|
|
qh_memfree(ridge, (int)sizeof(ridgeT));
|
|
}
|
|
}
|
|
SETfirst_(visible->ridges)= NULL;
|
|
}
|
|
SETfirst_(visible->neighbors)= NULL;
|
|
}
|
|
trace1((qh ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n"));
|
|
FORALLnew_facets {
|
|
horizon= SETfirstt_(newfacet->neighbors, facetT);
|
|
if (horizon->simplicial) {
|
|
visible= NULL;
|
|
FOREACHneighbor_(horizon) { /* may have more than one horizon ridge */
|
|
if (neighbor->visible) {
|
|
if (visible) {
|
|
if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices,
|
|
SETindex_(horizon->neighbors, neighbor))) {
|
|
visible= neighbor;
|
|
break;
|
|
}
|
|
}else
|
|
visible= neighbor;
|
|
}
|
|
}
|
|
if (visible) {
|
|
visible->f.replace= newfacet;
|
|
qh_setreplace(horizon->neighbors, visible, newfacet);
|
|
}else {
|
|
qh_fprintf(qh ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n",
|
|
horizon->id, newfacet->id);
|
|
qh_errexit2(qh_ERRqhull, horizon, newfacet);
|
|
}
|
|
}else { /* non-simplicial, with a ridge for newfacet */
|
|
FOREACHneighbor_(horizon) { /* may hold for many new facets */
|
|
if (neighbor->visible) {
|
|
neighbor->f.replace= newfacet;
|
|
qh_setdelnth(horizon->neighbors,
|
|
SETindex_(horizon->neighbors, neighbor));
|
|
neighborp--; /* repeat */
|
|
}
|
|
}
|
|
qh_setappend(&horizon->neighbors, newfacet);
|
|
ridge= SETfirstt_(newfacet->ridges, ridgeT);
|
|
if (ridge->top == horizon)
|
|
ridge->bottom= newfacet;
|
|
else
|
|
ridge->top= newfacet;
|
|
}
|
|
} /* newfacets */
|
|
if (qh PRINTstatistics) {
|
|
FORALLvisible_facets {
|
|
if (!visible->f.replace)
|
|
zinc_(Zinsidevisible);
|
|
}
|
|
}
|
|
} /* attachnewfacets */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="checkflipped">-</a>
|
|
|
|
qh_checkflipped( facet, dist, allerror )
|
|
checks facet orientation to interior point
|
|
|
|
if allerror set,
|
|
tests against qh.DISTround
|
|
else
|
|
tests against 0 since tested against DISTround before
|
|
|
|
returns:
|
|
False if it flipped orientation (sets facet->flipped)
|
|
distance if non-NULL
|
|
*/
|
|
boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) {
|
|
realT dist;
|
|
|
|
if (facet->flipped && !distp)
|
|
return False;
|
|
zzinc_(Zdistcheck);
|
|
qh_distplane(qh interior_point, facet, &dist);
|
|
if (distp)
|
|
*distp= dist;
|
|
if ((allerror && dist > -qh DISTround)|| (!allerror && dist >= 0.0)) {
|
|
facet->flipped= True;
|
|
zzinc_(Zflippedfacets);
|
|
trace0((qh ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n",
|
|
facet->id, dist, qh furthest_id));
|
|
qh_precision("flipped facet");
|
|
return False;
|
|
}
|
|
return True;
|
|
} /* checkflipped */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="delfacet">-</a>
|
|
|
|
qh_delfacet( facet )
|
|
removes facet from facet_list and frees up its memory
|
|
|
|
notes:
|
|
assumes vertices and ridges already freed
|
|
*/
|
|
void qh_delfacet(facetT *facet) {
|
|
void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
|
|
|
|
trace4((qh ferr, 4046, "qh_delfacet: delete f%d\n", facet->id));
|
|
if (facet == qh tracefacet)
|
|
qh tracefacet= NULL;
|
|
if (facet == qh GOODclosest)
|
|
qh GOODclosest= NULL;
|
|
qh_removefacet(facet);
|
|
if (!facet->tricoplanar || facet->keepcentrum) {
|
|
qh_memfree_(facet->normal, qh normal_size, freelistp);
|
|
if (qh CENTERtype == qh_ASvoronoi) { /* braces for macro calls */
|
|
qh_memfree_(facet->center, qh center_size, freelistp);
|
|
}else /* AScentrum */ {
|
|
qh_memfree_(facet->center, qh normal_size, freelistp);
|
|
}
|
|
}
|
|
qh_setfree(&(facet->neighbors));
|
|
if (facet->ridges)
|
|
qh_setfree(&(facet->ridges));
|
|
qh_setfree(&(facet->vertices));
|
|
if (facet->outsideset)
|
|
qh_setfree(&(facet->outsideset));
|
|
if (facet->coplanarset)
|
|
qh_setfree(&(facet->coplanarset));
|
|
qh_memfree_(facet, (int)sizeof(facetT), freelistp);
|
|
} /* delfacet */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="deletevisible">-</a>
|
|
|
|
qh_deletevisible()
|
|
delete visible facets and vertices
|
|
|
|
returns:
|
|
deletes each facet and removes from facetlist
|
|
at exit, qh.visible_list empty (== qh.newfacet_list)
|
|
|
|
notes:
|
|
ridges already deleted
|
|
horizon facets do not reference facets on qh.visible_list
|
|
new facets in qh.newfacet_list
|
|
uses qh.visit_id;
|
|
*/
|
|
void qh_deletevisible(void /*qh.visible_list*/) {
|
|
facetT *visible, *nextfacet;
|
|
vertexT *vertex, **vertexp;
|
|
int numvisible= 0, numdel= qh_setsize(qh del_vertices);
|
|
|
|
trace1((qh ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n",
|
|
qh num_visible, numdel));
|
|
for (visible= qh visible_list; visible && visible->visible;
|
|
visible= nextfacet) { /* deleting current */
|
|
nextfacet= visible->next;
|
|
numvisible++;
|
|
qh_delfacet(visible);
|
|
}
|
|
if (numvisible != qh num_visible) {
|
|
qh_fprintf(qh ferr, 6103, "qhull internal error (qh_deletevisible): qh num_visible %d is not number of visible facets %d\n",
|
|
qh num_visible, numvisible);
|
|
qh_errexit(qh_ERRqhull, NULL, NULL);
|
|
}
|
|
qh num_visible= 0;
|
|
zadd_(Zvisfacettot, numvisible);
|
|
zmax_(Zvisfacetmax, numvisible);
|
|
zzadd_(Zdelvertextot, numdel);
|
|
zmax_(Zdelvertexmax, numdel);
|
|
FOREACHvertex_(qh del_vertices)
|
|
qh_delvertex(vertex);
|
|
qh_settruncate(qh del_vertices, 0);
|
|
} /* deletevisible */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="facetintersect">-</a>
|
|
|
|
qh_facetintersect( facetA, facetB, skipa, skipB, prepend )
|
|
return vertices for intersection of two simplicial facets
|
|
may include 1 prepended entry (if more, need to settemppush)
|
|
|
|
returns:
|
|
returns set of qh.hull_dim-1 + prepend vertices
|
|
returns skipped index for each test and checks for exactly one
|
|
|
|
notes:
|
|
does not need settemp since set in quick memory
|
|
|
|
see also:
|
|
qh_vertexintersect and qh_vertexintersect_new
|
|
use qh_setnew_delnthsorted to get nth ridge (no skip information)
|
|
|
|
design:
|
|
locate skipped vertex by scanning facet A's neighbors
|
|
locate skipped vertex by scanning facet B's neighbors
|
|
intersect the vertex sets
|
|
*/
|
|
setT *qh_facetintersect(facetT *facetA, facetT *facetB,
|
|
int *skipA,int *skipB, int prepend) {
|
|
setT *intersect;
|
|
int dim= qh hull_dim, i, j;
|
|
facetT **neighborsA, **neighborsB;
|
|
|
|
neighborsA= SETaddr_(facetA->neighbors, facetT);
|
|
neighborsB= SETaddr_(facetB->neighbors, facetT);
|
|
i= j= 0;
|
|
if (facetB == *neighborsA++)
|
|
*skipA= 0;
|
|
else if (facetB == *neighborsA++)
|
|
*skipA= 1;
|
|
else if (facetB == *neighborsA++)
|
|
*skipA= 2;
|
|
else {
|
|
for (i=3; i < dim; i++) {
|
|
if (facetB == *neighborsA++) {
|
|
*skipA= i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (facetA == *neighborsB++)
|
|
*skipB= 0;
|
|
else if (facetA == *neighborsB++)
|
|
*skipB= 1;
|
|
else if (facetA == *neighborsB++)
|
|
*skipB= 2;
|
|
else {
|
|
for (j=3; j < dim; j++) {
|
|
if (facetA == *neighborsB++) {
|
|
*skipB= j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (i >= dim || j >= dim) {
|
|
qh_fprintf(qh ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n",
|
|
facetA->id, facetB->id);
|
|
qh_errexit2(qh_ERRqhull, facetA, facetB);
|
|
}
|
|
intersect= qh_setnew_delnthsorted(facetA->vertices, qh hull_dim, *skipA, prepend);
|
|
trace4((qh ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n",
|
|
facetA->id, *skipA, facetB->id, *skipB));
|
|
return(intersect);
|
|
} /* facetintersect */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="gethash">-</a>
|
|
|
|
qh_gethash( hashsize, set, size, firstindex, skipelem )
|
|
return hashvalue for a set with firstindex and skipelem
|
|
|
|
notes:
|
|
returned hash is in [0,hashsize)
|
|
assumes at least firstindex+1 elements
|
|
assumes skipelem is NULL, in set, or part of hash
|
|
|
|
hashes memory addresses which may change over different runs of the same data
|
|
using sum for hash does badly in high d
|
|
*/
|
|
int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem) {
|
|
void **elemp= SETelemaddr_(set, firstindex, void);
|
|
ptr_intT hash = 0, elem;
|
|
unsigned result;
|
|
int i;
|
|
#ifdef _MSC_VER /* Microsoft Visual C++ -- warn about 64-bit issues */
|
|
#pragma warning( push) /* WARN64 -- ptr_intT holds a 64-bit pointer */
|
|
#pragma warning( disable : 4311) /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */
|
|
#endif
|
|
|
|
switch (size-firstindex) {
|
|
case 1:
|
|
hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem;
|
|
break;
|
|
case 2:
|
|
hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem;
|
|
break;
|
|
case 3:
|
|
hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
|
|
- (ptr_intT) skipelem;
|
|
break;
|
|
case 4:
|
|
hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
|
|
+ (ptr_intT)elemp[3] - (ptr_intT) skipelem;
|
|
break;
|
|
case 5:
|
|
hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
|
|
+ (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem;
|
|
break;
|
|
case 6:
|
|
hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
|
|
+ (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5]
|
|
- (ptr_intT) skipelem;
|
|
break;
|
|
default:
|
|
hash= 0;
|
|
i= 3;
|
|
do { /* this is about 10% in 10-d */
|
|
if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) {
|
|
hash ^= (elem << i) + (elem >> (32-i));
|
|
i += 3;
|
|
if (i >= 32)
|
|
i -= 32;
|
|
}
|
|
}while (*elemp);
|
|
break;
|
|
}
|
|
if (hashsize<0) {
|
|
qh_fprintf(qh ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);
|
|
qh_errexit2(qh_ERRqhull, NULL, NULL);
|
|
}
|
|
result= (unsigned)hash;
|
|
result %= (unsigned)hashsize;
|
|
/* result= 0; for debugging */
|
|
return result;
|
|
#ifdef _MSC_VER
|
|
#pragma warning( pop)
|
|
#endif
|
|
} /* gethash */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="makenewfacet">-</a>
|
|
|
|
qh_makenewfacet( vertices, toporient, horizon )
|
|
creates a toporient? facet from vertices
|
|
|
|
returns:
|
|
returns newfacet
|
|
adds newfacet to qh.facet_list
|
|
newfacet->vertices= vertices
|
|
if horizon
|
|
newfacet->neighbor= horizon, but not vice versa
|
|
newvertex_list updated with vertices
|
|
*/
|
|
facetT *qh_makenewfacet(setT *vertices, boolT toporient,facetT *horizon) {
|
|
facetT *newfacet;
|
|
vertexT *vertex, **vertexp;
|
|
|
|
FOREACHvertex_(vertices) {
|
|
if (!vertex->newlist) {
|
|
qh_removevertex(vertex);
|
|
qh_appendvertex(vertex);
|
|
}
|
|
}
|
|
newfacet= qh_newfacet();
|
|
newfacet->vertices= vertices;
|
|
newfacet->toporient= (unsigned char)toporient;
|
|
if (horizon)
|
|
qh_setappend(&(newfacet->neighbors), horizon);
|
|
qh_appendfacet(newfacet);
|
|
return(newfacet);
|
|
} /* makenewfacet */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="makenewplanes">-</a>
|
|
|
|
qh_makenewplanes()
|
|
make new hyperplanes for facets on qh.newfacet_list
|
|
|
|
returns:
|
|
all facets have hyperplanes or are marked for merging
|
|
doesn't create hyperplane if horizon is coplanar (will merge)
|
|
updates qh.min_vertex if qh.JOGGLEmax
|
|
|
|
notes:
|
|
facet->f.samecycle is defined for facet->mergehorizon facets
|
|
*/
|
|
void qh_makenewplanes(void /* newfacet_list */) {
|
|
facetT *newfacet;
|
|
|
|
FORALLnew_facets {
|
|
if (!newfacet->mergehorizon)
|
|
qh_setfacetplane(newfacet);
|
|
}
|
|
if (qh JOGGLEmax < REALmax/2)
|
|
minimize_(qh min_vertex, -wwval_(Wnewvertexmax));
|
|
} /* makenewplanes */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="makenew_nonsimplicial">-</a>
|
|
|
|
qh_makenew_nonsimplicial( visible, apex, numnew )
|
|
make new facets for ridges of a visible facet
|
|
|
|
returns:
|
|
first newfacet, bumps numnew as needed
|
|
attaches new facets if !qh.ONLYgood
|
|
marks ridge neighbors for simplicial visible
|
|
if (qh.ONLYgood)
|
|
ridges on newfacet, horizon, and visible
|
|
else
|
|
ridge and neighbors between newfacet and horizon
|
|
visible facet's ridges are deleted
|
|
|
|
notes:
|
|
qh.visit_id if visible has already been processed
|
|
sets neighbor->seen for building f.samecycle
|
|
assumes all 'seen' flags initially false
|
|
|
|
design:
|
|
for each ridge of visible facet
|
|
get neighbor of visible facet
|
|
if neighbor was already processed
|
|
delete the ridge (will delete all visible facets later)
|
|
if neighbor is a horizon facet
|
|
create a new facet
|
|
if neighbor coplanar
|
|
adds newfacet to f.samecycle for later merging
|
|
else
|
|
updates neighbor's neighbor set
|
|
(checks for non-simplicial facet with multiple ridges to visible facet)
|
|
updates neighbor's ridge set
|
|
(checks for simplicial neighbor to non-simplicial visible facet)
|
|
(deletes ridge if neighbor is simplicial)
|
|
|
|
*/
|
|
#ifndef qh_NOmerge
|
|
facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) {
|
|
void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
|
|
ridgeT *ridge, **ridgep;
|
|
facetT *neighbor, *newfacet= NULL, *samecycle;
|
|
setT *vertices;
|
|
boolT toporient;
|
|
int ridgeid;
|
|
|
|
FOREACHridge_(visible->ridges) {
|
|
ridgeid= ridge->id;
|
|
neighbor= otherfacet_(ridge, visible);
|
|
if (neighbor->visible) {
|
|
if (!qh ONLYgood) {
|
|
if (neighbor->visitid == qh visit_id) {
|
|
qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */
|
|
qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
|
|
}
|
|
}
|
|
}else { /* neighbor is an horizon facet */
|
|
toporient= (ridge->top == visible);
|
|
vertices= qh_setnew(qh hull_dim); /* makes sure this is quick */
|
|
qh_setappend(&vertices, apex);
|
|
qh_setappend_set(&vertices, ridge->vertices);
|
|
newfacet= qh_makenewfacet(vertices, toporient, neighbor);
|
|
(*numnew)++;
|
|
if (neighbor->coplanar) {
|
|
newfacet->mergehorizon= True;
|
|
if (!neighbor->seen) {
|
|
newfacet->f.samecycle= newfacet;
|
|
neighbor->f.newcycle= newfacet;
|
|
}else {
|
|
samecycle= neighbor->f.newcycle;
|
|
newfacet->f.samecycle= samecycle->f.samecycle;
|
|
samecycle->f.samecycle= newfacet;
|
|
}
|
|
}
|
|
if (qh ONLYgood) {
|
|
if (!neighbor->simplicial)
|
|
qh_setappend(&(newfacet->ridges), ridge);
|
|
}else { /* qh_attachnewfacets */
|
|
if (neighbor->seen) {
|
|
if (neighbor->simplicial) {
|
|
qh_fprintf(qh ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n",
|
|
neighbor->id, visible->id);
|
|
qh_errexit2(qh_ERRqhull, neighbor, visible);
|
|
}
|
|
qh_setappend(&(neighbor->neighbors), newfacet);
|
|
}else
|
|
qh_setreplace(neighbor->neighbors, visible, newfacet);
|
|
if (neighbor->simplicial) {
|
|
qh_setdel(neighbor->ridges, ridge);
|
|
qh_setfree(&(ridge->vertices));
|
|
qh_memfree(ridge, (int)sizeof(ridgeT));
|
|
}else {
|
|
qh_setappend(&(newfacet->ridges), ridge);
|
|
if (toporient)
|
|
ridge->top= newfacet;
|
|
else
|
|
ridge->bottom= newfacet;
|
|
}
|
|
trace4((qh ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n",
|
|
newfacet->id, apex->id, ridgeid, neighbor->id));
|
|
}
|
|
}
|
|
neighbor->seen= True;
|
|
} /* for each ridge */
|
|
if (!qh ONLYgood)
|
|
SETfirst_(visible->ridges)= NULL;
|
|
return newfacet;
|
|
} /* makenew_nonsimplicial */
|
|
#else /* qh_NOmerge */
|
|
facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) {
|
|
return NULL;
|
|
}
|
|
#endif /* qh_NOmerge */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="makenew_simplicial">-</a>
|
|
|
|
qh_makenew_simplicial( visible, apex, numnew )
|
|
make new facets for simplicial visible facet and apex
|
|
|
|
returns:
|
|
attaches new facets if (!qh.ONLYgood)
|
|
neighbors between newfacet and horizon
|
|
|
|
notes:
|
|
nop if neighbor->seen or neighbor->visible(see qh_makenew_nonsimplicial)
|
|
|
|
design:
|
|
locate neighboring horizon facet for visible facet
|
|
determine vertices and orientation
|
|
create new facet
|
|
if coplanar,
|
|
add new facet to f.samecycle
|
|
update horizon facet's neighbor list
|
|
*/
|
|
facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) {
|
|
facetT *neighbor, **neighborp, *newfacet= NULL;
|
|
setT *vertices;
|
|
boolT flip, toporient;
|
|
int horizonskip= 0, visibleskip= 0;
|
|
|
|
FOREACHneighbor_(visible) {
|
|
if (!neighbor->seen && !neighbor->visible) {
|
|
vertices= qh_facetintersect(neighbor,visible, &horizonskip, &visibleskip, 1);
|
|
SETfirst_(vertices)= apex;
|
|
flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1));
|
|
if (neighbor->toporient)
|
|
toporient= horizonskip & 0x1;
|
|
else
|
|
toporient= (horizonskip & 0x1) ^ 0x1;
|
|
newfacet= qh_makenewfacet(vertices, toporient, neighbor);
|
|
(*numnew)++;
|
|
if (neighbor->coplanar && (qh PREmerge || qh MERGEexact)) {
|
|
#ifndef qh_NOmerge
|
|
newfacet->f.samecycle= newfacet;
|
|
newfacet->mergehorizon= True;
|
|
#endif
|
|
}
|
|
if (!qh ONLYgood)
|
|
SETelem_(neighbor->neighbors, horizonskip)= newfacet;
|
|
trace4((qh ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n",
|
|
newfacet->id, toporient, apex->id, neighbor->id, horizonskip,
|
|
neighbor->toporient, visible->id, visibleskip, flip));
|
|
}
|
|
}
|
|
return newfacet;
|
|
} /* makenew_simplicial */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="matchneighbor">-</a>
|
|
|
|
qh_matchneighbor( newfacet, newskip, hashsize, hashcount )
|
|
either match subridge of newfacet with neighbor or add to hash_table
|
|
|
|
returns:
|
|
duplicate ridges are unmatched and marked by qh_DUPLICATEridge
|
|
|
|
notes:
|
|
ridge is newfacet->vertices w/o newskip vertex
|
|
do not allocate memory (need to free hash_table cleanly)
|
|
uses linear hash chains
|
|
|
|
see also:
|
|
qh_matchduplicates
|
|
|
|
design:
|
|
for each possible matching facet in qh.hash_table
|
|
if vertices match
|
|
set ismatch, if facets have opposite orientation
|
|
if ismatch and matching facet doesn't have a match
|
|
match the facets by updating their neighbor sets
|
|
else
|
|
indicate a duplicate ridge
|
|
set facet hyperplane for later testing
|
|
add facet to hashtable
|
|
unless the other facet was already a duplicate ridge
|
|
mark both facets with a duplicate ridge
|
|
add other facet (if defined) to hash table
|
|
*/
|
|
void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcount) {
|
|
boolT newfound= False; /* True, if new facet is already in hash chain */
|
|
boolT same, ismatch;
|
|
int hash, scan;
|
|
facetT *facet, *matchfacet;
|
|
int skip, matchskip;
|
|
|
|
hash= qh_gethash(hashsize, newfacet->vertices, qh hull_dim, 1,
|
|
SETelem_(newfacet->vertices, newskip));
|
|
trace4((qh ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n",
|
|
newfacet->id, newskip, hash, *hashcount));
|
|
zinc_(Zhashlookup);
|
|
for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT));
|
|
scan= (++scan >= hashsize ? 0 : scan)) {
|
|
if (facet == newfacet) {
|
|
newfound= True;
|
|
continue;
|
|
}
|
|
zinc_(Zhashtests);
|
|
if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
|
|
if (SETelem_(newfacet->vertices, newskip) ==
|
|
SETelem_(facet->vertices, skip)) {
|
|
qh_precision("two facets with the same vertices");
|
|
qh_fprintf(qh ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d. Can not force output.\n",
|
|
facet->id, newfacet->id);
|
|
qh_errexit2(qh_ERRprec, facet, newfacet);
|
|
}
|
|
ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient)));
|
|
matchfacet= SETelemt_(facet->neighbors, skip, facetT);
|
|
if (ismatch && !matchfacet) {
|
|
SETelem_(facet->neighbors, skip)= newfacet;
|
|
SETelem_(newfacet->neighbors, newskip)= facet;
|
|
(*hashcount)--;
|
|
trace4((qh ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n",
|
|
facet->id, skip, newfacet->id, newskip));
|
|
return;
|
|
}
|
|
if (!qh PREmerge && !qh MERGEexact) {
|
|
qh_precision("a ridge with more than two neighbors");
|
|
qh_fprintf(qh ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue.\n",
|
|
facet->id, newfacet->id, getid_(matchfacet));
|
|
qh_errexit2(qh_ERRprec, facet, newfacet);
|
|
}
|
|
SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;
|
|
newfacet->dupridge= True;
|
|
if (!newfacet->normal)
|
|
qh_setfacetplane(newfacet);
|
|
qh_addhash(newfacet, qh hash_table, hashsize, hash);
|
|
(*hashcount)++;
|
|
if (!facet->normal)
|
|
qh_setfacetplane(facet);
|
|
if (matchfacet != qh_DUPLICATEridge) {
|
|
SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;
|
|
facet->dupridge= True;
|
|
if (!facet->normal)
|
|
qh_setfacetplane(facet);
|
|
if (matchfacet) {
|
|
matchskip= qh_setindex(matchfacet->neighbors, facet);
|
|
if (matchskip<0) {
|
|
qh_fprintf(qh ferr, 6260, "qhull internal error (qh_matchneighbor): matchfacet f%d is in f%d neighbors but not vice versa. Can not continue.\n",
|
|
matchfacet->id, facet->id);
|
|
qh_errexit2(qh_ERRqhull, matchfacet, facet);
|
|
}
|
|
SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */
|
|
matchfacet->dupridge= True;
|
|
if (!matchfacet->normal)
|
|
qh_setfacetplane(matchfacet);
|
|
qh_addhash(matchfacet, qh hash_table, hashsize, hash);
|
|
*hashcount += 2;
|
|
}
|
|
}
|
|
trace4((qh ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n",
|
|
newfacet->id, newskip, facet->id, skip,
|
|
(matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)),
|
|
ismatch, hash));
|
|
return; /* end of duplicate ridge */
|
|
}
|
|
}
|
|
if (!newfound)
|
|
SETelem_(qh hash_table, scan)= newfacet; /* same as qh_addhash */
|
|
(*hashcount)++;
|
|
trace4((qh ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n",
|
|
newfacet->id, newskip, hash));
|
|
} /* matchneighbor */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="matchnewfacets">-</a>
|
|
|
|
qh_matchnewfacets()
|
|
match newfacets in qh.newfacet_list to their newfacet neighbors
|
|
|
|
returns:
|
|
qh.newfacet_list with full neighbor sets
|
|
get vertices with nth neighbor by deleting nth vertex
|
|
if qh.PREmerge/MERGEexact or qh.FORCEoutput
|
|
sets facet->flippped if flipped normal (also prevents point partitioning)
|
|
if duplicate ridges and qh.PREmerge/MERGEexact
|
|
sets facet->dupridge
|
|
missing neighbor links identifies extra ridges to be merging (qh_MERGEridge)
|
|
|
|
notes:
|
|
newfacets already have neighbor[0] (horizon facet)
|
|
assumes qh.hash_table is NULL
|
|
vertex->neighbors has not been updated yet
|
|
do not allocate memory after qh.hash_table (need to free it cleanly)
|
|
|
|
design:
|
|
delete neighbor sets for all new facets
|
|
initialize a hash table
|
|
for all new facets
|
|
match facet with neighbors
|
|
if unmatched facets (due to duplicate ridges)
|
|
for each new facet with a duplicate ridge
|
|
match it with a facet
|
|
check for flipped facets
|
|
*/
|
|
void qh_matchnewfacets(void /* qh.newfacet_list */) {
|
|
int numnew=0, hashcount=0, newskip;
|
|
facetT *newfacet, *neighbor;
|
|
int dim= qh hull_dim, hashsize, neighbor_i, neighbor_n;
|
|
setT *neighbors;
|
|
#ifndef qh_NOtrace
|
|
int facet_i, facet_n, numfree= 0;
|
|
facetT *facet;
|
|
#endif
|
|
|
|
trace1((qh ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));
|
|
FORALLnew_facets {
|
|
numnew++;
|
|
{ /* inline qh_setzero(newfacet->neighbors, 1, qh hull_dim); */
|
|
neighbors= newfacet->neighbors;
|
|
neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/
|
|
memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize);
|
|
}
|
|
}
|
|
|
|
qh_newhashtable(numnew*(qh hull_dim-1)); /* twice what is normally needed,
|
|
but every ridge could be DUPLICATEridge */
|
|
hashsize= qh_setsize(qh hash_table);
|
|
FORALLnew_facets {
|
|
for (newskip=1; newskip<qh hull_dim; newskip++) /* furthest/horizon already matched */
|
|
/* hashsize>0 because hull_dim>1 and numnew>0 */
|
|
qh_matchneighbor(newfacet, newskip, hashsize, &hashcount);
|
|
#if 0 /* use the following to trap hashcount errors */
|
|
{
|
|
int count= 0, k;
|
|
facetT *facet, *neighbor;
|
|
|
|
count= 0;
|
|
FORALLfacet_(qh newfacet_list) { /* newfacet already in use */
|
|
for (k=1; k < qh hull_dim; k++) {
|
|
neighbor= SETelemt_(facet->neighbors, k, facetT);
|
|
if (!neighbor || neighbor == qh_DUPLICATEridge)
|
|
count++;
|
|
}
|
|
if (facet == newfacet)
|
|
break;
|
|
}
|
|
if (count != hashcount) {
|
|
qh_fprintf(qh ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n",
|
|
newfacet->id, hashcount, count);
|
|
qh_errexit(qh_ERRqhull, newfacet, NULL);
|
|
}
|
|
}
|
|
#endif /* end of trap code */
|
|
}
|
|
if (hashcount) {
|
|
FORALLnew_facets {
|
|
if (newfacet->dupridge) {
|
|
FOREACHneighbor_i_(newfacet) {
|
|
if (neighbor == qh_DUPLICATEridge) {
|
|
qh_matchduplicates(newfacet, neighbor_i, hashsize, &hashcount);
|
|
/* this may report MERGEfacet */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (hashcount) {
|
|
qh_fprintf(qh ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n",
|
|
hashcount);
|
|
qh_printhashtable(qh ferr);
|
|
qh_errexit(qh_ERRqhull, NULL, NULL);
|
|
}
|
|
#ifndef qh_NOtrace
|
|
if (qh IStracing >= 2) {
|
|
FOREACHfacet_i_(qh hash_table) {
|
|
if (!facet)
|
|
numfree++;
|
|
}
|
|
qh_fprintf(qh ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries . hashsize %d\n",
|
|
numnew, numfree, qh_setsize(qh hash_table));
|
|
}
|
|
#endif /* !qh_NOtrace */
|
|
qh_setfree(&qh hash_table);
|
|
if (qh PREmerge || qh MERGEexact) {
|
|
if (qh IStracing >= 4)
|
|
qh_printfacetlist(qh newfacet_list, NULL, qh_ALL);
|
|
FORALLnew_facets {
|
|
if (newfacet->normal)
|
|
qh_checkflipped(newfacet, NULL, qh_ALL);
|
|
}
|
|
}else if (qh FORCEoutput)
|
|
qh_checkflipped_all(qh newfacet_list); /* prints warnings for flipped */
|
|
} /* matchnewfacets */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="matchvertices">-</a>
|
|
|
|
qh_matchvertices( firstindex, verticesA, skipA, verticesB, skipB, same )
|
|
tests whether vertices match with a single skip
|
|
starts match at firstindex since all new facets have a common vertex
|
|
|
|
returns:
|
|
true if matched vertices
|
|
skip index for each set
|
|
sets same iff vertices have the same orientation
|
|
|
|
notes:
|
|
assumes skipA is in A and both sets are the same size
|
|
|
|
design:
|
|
set up pointers
|
|
scan both sets checking for a match
|
|
test orientation
|
|
*/
|
|
boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA,
|
|
setT *verticesB, int *skipB, boolT *same) {
|
|
vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp;
|
|
|
|
elemAp= SETelemaddr_(verticesA, firstindex, vertexT);
|
|
elemBp= SETelemaddr_(verticesB, firstindex, vertexT);
|
|
skipAp= SETelemaddr_(verticesA, skipA, vertexT);
|
|
do if (elemAp != skipAp) {
|
|
while (*elemAp != *elemBp++) {
|
|
if (skipBp)
|
|
return False;
|
|
skipBp= elemBp; /* one extra like FOREACH */
|
|
}
|
|
}while (*(++elemAp));
|
|
if (!skipBp)
|
|
skipBp= ++elemBp;
|
|
*skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */
|
|
*same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */
|
|
trace4((qh ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n",
|
|
skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same));
|
|
return(True);
|
|
} /* matchvertices */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="newfacet">-</a>
|
|
|
|
qh_newfacet()
|
|
return a new facet
|
|
|
|
returns:
|
|
all fields initialized or cleared (NULL)
|
|
preallocates neighbors set
|
|
*/
|
|
facetT *qh_newfacet(void) {
|
|
facetT *facet;
|
|
void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
|
|
|
|
qh_memalloc_((int)sizeof(facetT), freelistp, facet, facetT);
|
|
memset((char *)facet, (size_t)0, sizeof(facetT));
|
|
if (qh facet_id == qh tracefacet_id)
|
|
qh tracefacet= facet;
|
|
facet->id= qh facet_id++;
|
|
facet->neighbors= qh_setnew(qh hull_dim);
|
|
#if !qh_COMPUTEfurthest
|
|
facet->furthestdist= 0.0;
|
|
#endif
|
|
#if qh_MAXoutside
|
|
if (qh FORCEoutput && qh APPROXhull)
|
|
facet->maxoutside= qh MINoutside;
|
|
else
|
|
facet->maxoutside= qh DISTround;
|
|
#endif
|
|
facet->simplicial= True;
|
|
facet->good= True;
|
|
facet->newfacet= True;
|
|
trace4((qh ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id));
|
|
return(facet);
|
|
} /* newfacet */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="newridge">-</a>
|
|
|
|
qh_newridge()
|
|
return a new ridge
|
|
*/
|
|
ridgeT *qh_newridge(void) {
|
|
ridgeT *ridge;
|
|
void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
|
|
|
|
qh_memalloc_((int)sizeof(ridgeT), freelistp, ridge, ridgeT);
|
|
memset((char *)ridge, (size_t)0, sizeof(ridgeT));
|
|
zinc_(Ztotridges);
|
|
if (qh ridge_id == UINT_MAX) {
|
|
qh_fprintf(qh ferr, 7074, "\
|
|
qhull warning: more than 2^32 ridges. Qhull results are OK. Since the ridge ID wraps around to 0, two ridges may have the same identifier.\n");
|
|
}
|
|
ridge->id= qh ridge_id++;
|
|
trace4((qh ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id));
|
|
return(ridge);
|
|
} /* newridge */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="pointid">-</a>
|
|
|
|
qh_pointid( point )
|
|
return id for a point,
|
|
returns qh_IDnone(-3) if null, qh_IDinterior(-2) if interior, or qh_IDunknown(-1) if not known
|
|
|
|
alternative code if point is in qh.first_point...
|
|
unsigned long id;
|
|
id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size;
|
|
|
|
notes:
|
|
Valid points are non-negative
|
|
WARN64 -- id truncated to 32-bits, at most 2G points
|
|
NOerrors returned (QhullPoint::id)
|
|
if point not in point array
|
|
the code does a comparison of unrelated pointers.
|
|
*/
|
|
int qh_pointid(pointT *point) {
|
|
ptr_intT offset, id;
|
|
|
|
if (!point)
|
|
return qh_IDnone;
|
|
else if (point == qh interior_point)
|
|
return qh_IDinterior;
|
|
else if (point >= qh first_point
|
|
&& point < qh first_point + qh num_points * qh hull_dim) {
|
|
offset= (ptr_intT)(point - qh first_point);
|
|
id= offset / qh hull_dim;
|
|
}else if ((id= qh_setindex(qh other_points, point)) != -1)
|
|
id += qh num_points;
|
|
else
|
|
return qh_IDunknown;
|
|
return (int)id;
|
|
} /* pointid */
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="removefacet">-</a>
|
|
|
|
qh_removefacet( facet )
|
|
unlinks facet from qh.facet_list,
|
|
|
|
returns:
|
|
updates qh.facet_list .newfacet_list .facet_next visible_list
|
|
decrements qh.num_facets
|
|
|
|
see:
|
|
qh_appendfacet
|
|
*/
|
|
void qh_removefacet(facetT *facet) {
|
|
facetT *next= facet->next, *previous= facet->previous;
|
|
|
|
if (facet == qh newfacet_list)
|
|
qh newfacet_list= next;
|
|
if (facet == qh facet_next)
|
|
qh facet_next= next;
|
|
if (facet == qh visible_list)
|
|
qh visible_list= next;
|
|
if (previous) {
|
|
previous->next= next;
|
|
next->previous= previous;
|
|
}else { /* 1st facet in qh facet_list */
|
|
qh facet_list= next;
|
|
qh facet_list->previous= NULL;
|
|
}
|
|
qh num_facets--;
|
|
trace4((qh ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id));
|
|
} /* removefacet */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="removevertex">-</a>
|
|
|
|
qh_removevertex( vertex )
|
|
unlinks vertex from qh.vertex_list,
|
|
|
|
returns:
|
|
updates qh.vertex_list .newvertex_list
|
|
decrements qh.num_vertices
|
|
*/
|
|
void qh_removevertex(vertexT *vertex) {
|
|
vertexT *next= vertex->next, *previous= vertex->previous;
|
|
|
|
if (vertex == qh newvertex_list)
|
|
qh newvertex_list= next;
|
|
if (previous) {
|
|
previous->next= next;
|
|
next->previous= previous;
|
|
}else { /* 1st vertex in qh vertex_list */
|
|
qh vertex_list= vertex->next;
|
|
qh vertex_list->previous= NULL;
|
|
}
|
|
qh num_vertices--;
|
|
trace4((qh ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id));
|
|
} /* removevertex */
|
|
|
|
|
|
/*-<a href="qh-poly.htm#TOC"
|
|
>-------------------------------</a><a name="updatevertices">-</a>
|
|
|
|
qh_updatevertices()
|
|
update vertex neighbors and delete interior vertices
|
|
|
|
returns:
|
|
if qh.VERTEXneighbors, updates neighbors for each vertex
|
|
if qh.newvertex_list,
|
|
removes visible neighbors from vertex neighbors
|
|
if qh.newfacet_list
|
|
adds new facets to vertex neighbors
|
|
if qh.visible_list
|
|
interior vertices added to qh.del_vertices for later partitioning
|
|
|
|
design:
|
|
if qh.VERTEXneighbors
|
|
deletes references to visible facets from vertex neighbors
|
|
appends new facets to the neighbor list for each vertex
|
|
checks all vertices of visible facets
|
|
removes visible facets from neighbor lists
|
|
marks unused vertices for deletion
|
|
*/
|
|
void qh_updatevertices(void /*qh.newvertex_list, newfacet_list, visible_list*/) {
|
|
facetT *newfacet= NULL, *neighbor, **neighborp, *visible;
|
|
vertexT *vertex, **vertexp;
|
|
|
|
trace3((qh ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n"));
|
|
if (qh VERTEXneighbors) {
|
|
FORALLvertex_(qh newvertex_list) {
|
|
FOREACHneighbor_(vertex) {
|
|
if (neighbor->visible)
|
|
SETref_(neighbor)= NULL;
|
|
}
|
|
qh_setcompact(vertex->neighbors);
|
|
}
|
|
FORALLnew_facets {
|
|
FOREACHvertex_(newfacet->vertices)
|
|
qh_setappend(&vertex->neighbors, newfacet);
|
|
}
|
|
FORALLvisible_facets {
|
|
FOREACHvertex_(visible->vertices) {
|
|
if (!vertex->newlist && !vertex->deleted) {
|
|
FOREACHneighbor_(vertex) { /* this can happen under merging */
|
|
if (!neighbor->visible)
|
|
break;
|
|
}
|
|
if (neighbor)
|
|
qh_setdel(vertex->neighbors, visible);
|
|
else {
|
|
vertex->deleted= True;
|
|
qh_setappend(&qh del_vertices, vertex);
|
|
trace2((qh ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
|
|
qh_pointid(vertex->point), vertex->id, visible->id));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}else { /* !VERTEXneighbors */
|
|
FORALLvisible_facets {
|
|
FOREACHvertex_(visible->vertices) {
|
|
if (!vertex->newlist && !vertex->deleted) {
|
|
vertex->deleted= True;
|
|
qh_setappend(&qh del_vertices, vertex);
|
|
trace2((qh ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
|
|
qh_pointid(vertex->point), vertex->id, visible->id));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} /* updatevertices */
|
|
|
|
|
|
|