qhull (empty) → 0.1.0.1
raw patch · 61 files changed
+35045/−0 lines, 61 filesdep +Uniquedep +basedep +combinatsetup-changed
Dependencies added: Unique, base, combinat, containers, data-default-class, extra, hashable, ilist, insert-ordered-containers, optparse-applicative, pretty-show, qhull, random, regex-base, regex-compat, regex-posix, split, toysolver, vector-algorithms, vector-space
Files
- C/convexhull.c +714/−0
- C/delaunay.c +577/−0
- C/geom2_r.c +2096/−0
- C/geom_r.c +1234/−0
- C/global_r.c +2100/−0
- C/halfspaces.c +68/−0
- C/io_r.c +4062/−0
- C/libqhull_r.c +1403/−0
- C/mem_r.c +562/−0
- C/merge_r.c +3627/−0
- C/poly2_r.c +3222/−0
- C/poly_r.c +1205/−0
- C/qset_r.c +1340/−0
- C/random_r.c +247/−0
- C/stat_r.c +682/−0
- C/user_r.c +527/−0
- C/usermem_r.c +94/−0
- C/userprintf_r.c +65/−0
- C/utils.c +91/−0
- GPL_3 +674/−0
- README.md +476/−0
- Setup.hs +2/−0
- qhull.cabal +190/−0
- src-exe/Adjacency/AdjacencyMatrix.hs +53/−0
- src-exe/ConvexHull/Main.hs +847/−0
- src-exe/Delaunay/Main.hs +65/−0
- src-exe/HalfSpaces/Main.hs +9/−0
- src-exe/Voronoi/Main.hs +249/−0
- src/ConvexHull.hs +7/−0
- src/ConvexHull/BiTruncatedTesseract.hs +23/−0
- src/ConvexHull/CConvexHull.hs +446/−0
- src/ConvexHull/CantiTrunc600Cell/Data.hs +67/−0
- src/ConvexHull/ConvexHull.hs +207/−0
- src/ConvexHull/Examples.hs +4996/−0
- src/ConvexHull/OmniTruncated120Cell.hs +108/−0
- src/ConvexHull/R.hs +55/−0
- src/ConvexHull/SnubDodecahedron/SnubDodecahedron.hs +31/−0
- src/ConvexHull/Truncated120Cell3.hs +43/−0
- src/ConvexHull/Types.hs +69/−0
- src/Delaunay.hs +7/−0
- src/Delaunay/Adjacency.hs +25/−0
- src/Delaunay/CDelaunay.hs +390/−0
- src/Delaunay/Delaunay.hs +94/−0
- src/Delaunay/Examples.hs +910/−0
- src/Delaunay/R.hs +98/−0
- src/Delaunay/Types.hs +85/−0
- src/HalfSpaces.hs +7/−0
- src/HalfSpaces/CHalfSpaces.hs +15/−0
- src/HalfSpaces/Constraint.hs +28/−0
- src/HalfSpaces/Examples.hs +53/−0
- src/HalfSpaces/HalfSpaces.hs +64/−0
- src/HalfSpaces/Internal.hs +46/−0
- src/HalfSpaces/LinearCombination.hs +63/−0
- src/HalfSpaces/ToySolver.hs +44/−0
- src/Qhull/Shared.hs +54/−0
- src/Qhull/Types.hs +42/−0
- src/Voronoi/R.hs +98/−0
- src/Voronoi/Shared.hs +8/−0
- src/Voronoi/Voronoi.hs +63/−0
- src/Voronoi2D.hs +122/−0
- src/Voronoi3D.hs +196/−0
+ C/convexhull.c view
@@ -0,0 +1,714 @@+#define qh_QHimport+#include "qhull_ra.h"+#include "convexhull.h"+#include "utils.h"++// to use the qsort function - sort vertices according to their ids+int cmpvertices (const void * a, const void * b) {+ return ( (*((VertexT*)a)).id - (*((VertexT*)b)).id );+}+// - sort full vertices+int cmpfullvertices (const void * a, const void * b) {+ return ( (*((FullVertexT*)a)).id - (*((FullVertexT*)b)).id );+}+// - sort edges+int cmpedges (const void * a, const void * b) {+ if((*(unsigned**)a)[0] > (*(unsigned**)b)[0]){+ return 1;+ }else if((*(unsigned**)a)[0] == (*(unsigned**)b)[0]){+ return (*(unsigned**)a)[1] - (*(unsigned**)b)[1];+ }else{+ return -1;+ }+}++/* test equality of two _sorted_ arrays */+unsigned equalarraysu(unsigned* array1, unsigned* array2, unsigned length){+ unsigned i;+ for(i=0; i < length; i++){+ if(array1[i] != array2[i]){+ break;+ }+ }+ return i == length;+}++/* return ids of a vector of VertexT */+unsigned* map_vertexid(VertexT* vertices, unsigned nvertices){+ unsigned* ids = malloc(nvertices * sizeof(unsigned));+ for(unsigned v=0; v < nvertices; v++){+ ids[v] = vertices[v].id;+ }+ return ids;+}++/* return ids of a vector of RidgeT */+unsigned* map_ridgeid(RidgeT* ridges, unsigned nridges){+ unsigned* ids = malloc(nridges * sizeof(unsigned));+ for(unsigned r=0; r < nridges; r++){+ ids[r] = ridges[r].id;+ }+ return ids;+}++// void deepCopyRidge(RidgeT* src, RidgeT* dest) { et dim !+// dest = malloc(sizeof(RidgeT));+// *dest = *src;+// dest->vertices = malloc(src->nvertices * sizeof(VertexT));+// for(unsigned v=0; v<src->nvertices; v++){+// dest->vertices[v].id = src.vertices[v].id;+// memcpy(dest->vertices[v].point, dest->vertices[v].point, dim * sizeof(double));+// }+// }++/* deep copy of a ridge */+RidgeT copyRidge(RidgeT ridge, unsigned dim){+ RidgeT out;+ out.ridgeOf1 = ridge.ridgeOf1;+ out.ridgeOf2 = ridge.ridgeOf2;+ out.nvertices = ridge.nvertices;+ out.nedges = ridge.nedges;+ out.vertices = malloc(out.nvertices * sizeof(VertexT));+ for(unsigned v=0; v < out.nvertices; v++){+ out.vertices[v].id = ridge.vertices[v].id;+ out.vertices[v].point = malloc(dim * sizeof(double));+ for(unsigned i=0; i < dim; i++){+ out.vertices[v].point[i] = ridge.vertices[v].point[i];+ }+ }+ // out.edges = malloc(out.nedges * sizeof(unsigned*));+ // for(unsigned e=0; e < out.nedges; e++){+ // out.edges[e] = malloc(2 * sizeof(unsigned));+ // out.edges[e][0] = ridge.edges[e][0];+ // out.edges[e][1] = ridge.edges[e][1];+ // }+ return out;+}++/* append to a vector of VertexT */+void appendv(VertexT x, VertexT** array, unsigned length, unsigned* flag){+ *flag = 1;+ for(unsigned i=0; i < length; i++){+ if(x.id == (*(*array + i)).id){+ *flag = 0;+ break;+ }+ }+ if(*flag == 1){+ *array = realloc(*array, (length+1) * sizeof(VertexT));+ if(*array == NULL){+ printf("realloc failure - exiting\n");+ exit(1);+ }+ *(*array + length) = x;+ }+}++/* union of two vectors of VertexT */+void unionv(VertexT** vs1, VertexT* vs2, unsigned l1, unsigned l2, unsigned* l){+ *l = l1;+ for(unsigned v=0; v < l2; v++){+ unsigned pushed;+ appendv(vs2[v], vs1, *l, &pushed);+ if(pushed){+ (*l)++;+ }+ }+ /* sort vertices according to their ids */+ qsort(*vs1, *l, sizeof(VertexT), cmpvertices);+}++/* merge ridges with same ridgeOf's */+RidgeT* mergeRidges(RidgeT* ridges, unsigned nridges, unsigned* newlength){+ // http://www.c4learn.com/c-programs/to-delete-duplicate-elements-in-array.html+ *newlength = nridges;+ unsigned i,j,k;+ for(i = 0; i < nridges; i++){+ for(j = i+1; j < nridges; ){+ if(ridges[i].ridgeOf1 == ridges[j].ridgeOf1 &&+ ridges[i].ridgeOf2 == ridges[j].ridgeOf2)+ {+ unsigned l;+ unionv(&(ridges[i].vertices), ridges[j].vertices,+ ridges[i].nvertices, ridges[j].nvertices, &l);+ ridges[i].nvertices = l;+ (*newlength)--;+ for(k = j; k+1 < nridges; k++){+ ridges[k] = ridges[k+1];+ }+ nridges--;+ }else{+ j++;+ }+ }+ }+ RidgeT* out = malloc(*newlength * sizeof(RidgeT));+ for(unsigned r=0; r < *newlength; r++){+ out[r] = ridges[r];+ }+ return out;+}++/* all ridges from the ridges stored in the faces */+RidgeT* allRidges(FaceT *faces, unsigned nfaces, unsigned dim, unsigned* length){+ RidgeT* out = malloc(faces[0].nridges * sizeof(RidgeT));+ for(unsigned i=0; i < faces[0].nridges; i++){+ out[i] = copyRidge(faces[0].ridges[i], dim);+ out[i].id = i;+ out[i].nedges = 0;+ // RidgeT out[i];+ // deepCopyRidge(&(faces[0].ridges[i]), &(out[i]));+ }+ *length = faces[0].nridges;+ unsigned n = faces[0].nridges;+ for(unsigned f=1; f < nfaces; f++){+ for(unsigned j=0; j < faces[f].nridges; j++){+ unsigned count = 0;+ for(unsigned i=0; i < n; i++){+ unsigned flag = 0;+ for(unsigned v=0; v < faces[f].ridges[j].nvertices; v++){+ if(faces[f].ridges[j].vertices[v].id != out[i].vertices[v].id){+ flag = 1;+ break;+ }+ }+ if(flag){+ count++;+ }else{+ break;+ }+ }+ if(count == n){+ out = realloc(out, (*length+1) * sizeof(RidgeT));+ if(out == NULL){+ printf("realloc failure - exiting\n");+ exit(1);+ }+ out[*length] = copyRidge(faces[f].ridges[j], dim);+ out[*length].id = *length;+ out[*length].nedges = 0;+ // RidgeT out[*length];+ // deepCopyRidge(&(faces[f].ridges[j]), &(out[*length]));+ (*length)++;+ }+ }+ n = *length;+ }+ return out;+}++/* assign ids to the ridges stored in the faces */+void assignRidgesIds(FaceT** faces, unsigned nfaces, RidgeT* allridges,+ unsigned nallridges)+{+ for(unsigned f=0; f < nfaces; f++){+ for(unsigned fr=0; fr < (*(*faces + f)).nridges; fr++){+ for(unsigned r=0; r < nallridges; r++){+ if((allridges[r].nvertices == (*(*faces + f)).ridges[fr].nvertices) &&+ equalarraysu(map_vertexid(allridges[r].vertices,+ allridges[r].nvertices),+ map_vertexid((*(*faces + f)).ridges[fr].vertices,+ allridges[r].nvertices),+ allridges[r].nvertices))+ {+ (*(*faces + f)).ridges[fr].id = allridges[r].id;+ break;+ }+ }+ }+ }+}++// double* ridgeCentroid(RidgeT ridge, unsigned dim){+// double* out = malloc(dim * sizeof(double));+// for(unsigned i=0; i<dim; i++){+// out[i] = 0;+// for(unsigned v=0; v<dim-1; v++){+// out[i] += ridge.vertices[v].point[i];+// }+// out[i] /= dim - 1;+// }+// return out;+// }++/* the threshold distance to detect neighbor vertices */+double ridgeMaxDistance(RidgeT ridge, unsigned v, unsigned dim){+ double dists[ridge.nvertices-1];+ unsigned count = 0;+ for(unsigned w=0; w < ridge.nvertices; w++){+ if(w != v){+ dists[count] = squaredDistance(ridge.vertices[v].point,+ ridge.vertices[w].point, dim);+ count++;+ }+ }+ qsort(dists, ridge.nvertices-1, sizeof(double), cmpfuncdbl);+ return dists[1];+}++/* neighbor vertices of a vertex from all ridges, for dim>2 */+unsigned* neighVertices(unsigned id, RidgeT* allridges, unsigned nridges,+ unsigned dim, unsigned triangulate, unsigned* lengthout)+{+ unsigned* neighs = malloc(0);+ *lengthout = 0;+ for(unsigned e=0; e < nridges; e++){+ for(unsigned v=0; v < allridges[e].nvertices; v++){+ if(id == allridges[e].vertices[v].id){+ for(unsigned w=0; w < allridges[e].nvertices; w++){+ if(w != v && (triangulate || dim == 3 || // dim3 pas besoin de tester la distance: il n'y a que deux vertices connectés+ squaredDistance(allridges[e].vertices[w].point,+ allridges[e].vertices[v].point, dim) <=+ ridgeMaxDistance(allridges[e], v, dim)))+ {+ unsigned pushed;+ appendu(allridges[e].vertices[w].id, &neighs, *lengthout, &pushed);+ if(pushed){+ (*lengthout)++;+ }+ }+ }+ break;+ }+ }+ }+ return neighs;+}++/* neighbor ridges of a vertex */+unsigned* neighRidges(unsigned id, RidgeT* allridges, unsigned nridges,+ unsigned* length)+{+ unsigned* neighs = malloc(0);+ *length = 0;+ for(unsigned e=0; e < nridges; e++){+ unsigned flag = 0;+ for(unsigned v=0; v < allridges[e].nvertices; v++){+ if(id == allridges[e].vertices[v].id){+ flag = 1;+ break;+ }+ }+ if(flag){+ neighs = realloc(neighs, (*length+1)*sizeof(unsigned));+ if(neighs == NULL){+ printf("realloc failure - exiting\n");+ exit(1);+ }+ neighs[*length] = e;+ (*length)++;+ }+ }+ return neighs;+}++/* whether distinct x1 and x2 belong to array of distinct values */+unsigned areElementsOf(unsigned x1, unsigned x2, unsigned* array,+ unsigned length)+{+ unsigned count = 0;+ for(unsigned i=0; (i < length) && (count < 2); i++){+ if(x1 == array[i] || x2 == array[i]){+ count++;+ }+ }+ return count==2;+}++/* make face/ridge edges from all edges */+unsigned** makeEdges(SetOfVerticesT face, unsigned** alledges,+ unsigned nalledges, unsigned* lengthout)+{+ *lengthout = 0;+ unsigned* faceverticesids = map_vertexid(face.vertices, face.nvertices);+ unsigned flags[nalledges];+ for(unsigned e=0; e < nalledges; e++){+ if(areElementsOf(alledges[e][0], alledges[e][1], faceverticesids,+ face.nvertices))+ {+ flags[e] = 1;+ (*lengthout)++;+ }else{+ flags[e] = 0;+ }+ }+ unsigned** out = malloc(*lengthout * sizeof(unsigned*));+ unsigned count = 0;+ for(unsigned e=0; e < nalledges; e++){+ if(flags[e] == 1){+ out[count] = alledges[e];+ count++;+ }+ }+ return out;+}++/* all edges from all vertices */+unsigned** allEdges(FullVertexT* vertices, unsigned nvertices,+ unsigned outlength)+{+ unsigned** out = malloc(outlength * sizeof(unsigned*));+ for(unsigned i=0; i < vertices[0].nneighsvertices; i++){+ out[i] = malloc(2 * sizeof(unsigned));+ out[i][0] = vertices[0].id;+ out[i][1] = vertices[0].neighvertices[i];+ qsortu(out[i], 2);+ }+ unsigned n = vertices[0].nneighsvertices;+ for(unsigned v=1; v < nvertices; v++){+ unsigned ids[2];+ for(unsigned i=0; i < vertices[v].nneighsvertices; i++){+ ids[0] = vertices[v].id;+ ids[1] = vertices[v].neighvertices[i];+ qsortu(ids, 2);+ unsigned j;+ for(j=0; j < n; j++){+ if(ids[0] == out[j][0] && ids[1] == out[j][1]){+ break;+ }+ }+ if(j == n){+ out[n] = malloc(2 * sizeof(unsigned));+ out[n][0] = ids[0]; out[n][1] = ids[1];+ n++;+ }+ if(n == outlength){+ break;+ }+ }+ if(n == outlength){+ break;+ }+ }+ return out;+}++// ATTENTION avec Qt le center dans facet->center est le centre de l'union des triangles,+// (ainsi que normal et offset mais ça ok)++// un ridge est simplicial ; pour l'hypercube il y a 2 ridges entre 2 faces,+// ils forment le carré à l'intersection++/* main function */+ConvexHullT* convexHull(+ double* points,+ unsigned dim,+ unsigned n,+ unsigned triangulate,+ unsigned print,+ char* summaryFile,+ unsigned* exitcode+)+{+ char opts[250]; /* option flags for qhull, see qh_opt.htm */+ sprintf(opts, "qhull s FF %s", triangulate ? "Qt" : "");+ qhT qh_qh; /* Qhull's data structure */+ qhT *qh= &qh_qh;+ QHULL_LIB_CHECK+ qh_meminit(qh, stderr);+ boolT ismalloc = False; /* True if qhull should free points in qh_freeqhull() or reallocation */+ FILE *errfile = NULL;+ FILE* outfile;+ if(print){+ outfile = stdout;+ }else{+ outfile = NULL;+ }+ qh_zero(qh, errfile);+ exitcode[0] = qh_new_qhull(qh, dim, n, points, ismalloc, opts, outfile,+ errfile);+ //fclose(tmpstdout);+ printf("exitcode: %u\n", exitcode[0]);++ ConvexHullT* out = malloc(sizeof(ConvexHullT));++ if (!exitcode[0]) { /* 0 if no error from qhull */++ /* print summary to file */+ if(*summaryFile != 0){+ FILE* sfile = fopen(summaryFile, "w");+ qh_printsummary(qh, sfile);+ fclose(sfile);+ }++ //qh_getarea(qh, qh->facet_list); // no triowner if I do that; do qh_facetarea, not facet->f.area++ unsigned nfaces = qh->num_facets;+ FaceT* faces = malloc(nfaces * sizeof(FaceT));+ {+ facetT *facet; unsigned i_facet = 0;+ FORALLfacets{+ facet->id = i_facet; /* for neighbors and ridgeOf */+ faces[i_facet].area = qh_facetarea(qh, facet);+ double* center = qh_getcenter(qh, facet->vertices);+ faces[i_facet].center = malloc(dim * sizeof(double));+ for(unsigned i=0; i < dim; i++){+ faces[i_facet].center[i] = center[i];+ }+ double* normal = facet->normal;+ faces[i_facet].normal = malloc(dim * sizeof(double));+ for(unsigned i=0; i < dim; i++){+ faces[i_facet].normal[i] = normal[i];+ }+ faces[i_facet].offset = facet->offset;+ faces[i_facet].nvertices = (unsigned) qh_setsize(qh, facet->vertices);+ { /* face vertices */+ faces[i_facet].vertices =+ (VertexT*) malloc(faces[i_facet].nvertices * sizeof(VertexT));+ vertexT *vertex, **vertexp;+ unsigned i_vertex = 0;+ FOREACHvertex_(facet->vertices){+ faces[i_facet].vertices[i_vertex].id =+ (unsigned) qh_pointid(qh, vertex->point);+ faces[i_facet].vertices[i_vertex].point =+ malloc(dim * sizeof(double));+ faces[i_facet].vertices[i_vertex].point =+ getpoint(points, dim, faces[i_facet].vertices[i_vertex].id);+ // plante dans Haskell: (faces[i_facet].vertices)[i_vertex].point = vertex->point;+ i_vertex++;+ }+ qsort(faces[i_facet].vertices, faces[i_facet].nvertices,+ sizeof(VertexT), cmpvertices);+ }+ if(dim == 3){ /* orientation of the normals */+ pointT* onepoint = ((vertexT*)facet->vertices->e[0].p)->point;+ double thepoint[dim]; /* onepoint+normal */+ for(unsigned i=0; i < dim; i++){+ thepoint[i] = onepoint[i] + faces[i_facet].normal[i];+ }+ /* we check that these two points are on the same side of the ridge */+ double h1 = dotproduct(qh->interior_point,+ faces[i_facet].normal, dim) ++ faces[i_facet].offset;+ double h2 = dotproduct(thepoint, faces[i_facet].normal, dim) ++ faces[i_facet].offset;+ if(h1*h2 > 0){+ for(unsigned i=0; i < dim; i++){+ faces[i_facet].normal[i] *= -1;+ }+ printf("change sign\n"); // seems to never occur+ }else{+ printf("not change sign\n");+ }+ }+ /**/+ i_facet++;+ }+ }++ { /* neighbor faces, faces families, and ridges */+ facetT *facet;+ unsigned i_facet = 0;+ FORALLfacets{+ {+ faces[i_facet].neighborsize = qh_setsize(qh, facet->neighbors);+ faces[i_facet].neighbors =+ malloc(faces[i_facet].neighborsize * sizeof(unsigned));+ unsigned i_neighbor = 0;+ facetT *neighbor, **neighborp;+ FOREACHneighbor_(facet){+ faces[i_facet].neighbors[i_neighbor] = (unsigned) neighbor->id;+ i_neighbor++;+ }+ qsortu(faces[i_facet].neighbors, faces[i_facet].neighborsize);+ }+ { /* face family, when option Qt */+ if(facet->tricoplanar){+ faces[i_facet].family = facet->f.triowner->id;+ }else{+ faces[i_facet].family = -1;+ }+ }+ { /* face ridges */+ qh_makeridges(qh, facet);+ unsigned nridges = qh_setsize(qh, facet->ridges);+ RidgeT* ridges = malloc(nridges * sizeof(RidgeT));+ ridgeT *ridge, **ridgep;+ unsigned i_ridge = 0;+ FOREACHridge_(facet->ridges){+ ridges[i_ridge].nedges = 0;+ unsigned ridgeSize = qh_setsize(qh, ridge->vertices); // dim-1+// printf("ridge size: %u\n", ridgeSize);+ ridges[i_ridge].nvertices = ridgeSize;+ unsigned ids[ridgeSize];+ for(unsigned v=0; v < ridgeSize; v++){+ ids[v] =+ qh_pointid(qh, ((vertexT*)ridge->vertices->e[v].p)->point);+ }+ qsortu(ids, ridgeSize);+ ridges[i_ridge].vertices = malloc(ridgeSize * sizeof(VertexT));+ for(unsigned v=0; v < ridgeSize; v++){+ ridges[i_ridge].vertices[v].id = ids[v];+ ridges[i_ridge].vertices[v].point = getpoint(points, dim, ids[v]);+ }+ unsigned ridgeofs[2];+ ridgeofs[0] = ridge->bottom->id;+ ridgeofs[1] = ridge->top->id;+ qsortu(ridgeofs, 2);+ ridges[i_ridge].ridgeOf1 = ridgeofs[0];+ ridges[i_ridge].ridgeOf2 = ridgeofs[1];+ /**/+ i_ridge++;+ }+ /* merge triangulated ridges */+ if(dim > 3 && !triangulate){+ unsigned l;+ faces[i_facet].ridges = mergeRidges(ridges, nridges, &l);+ faces[i_facet].nridges = l;+ }else{ /* dim 2 or 3, or triangulate option */+ faces[i_facet].ridges = ridges;+ faces[i_facet].nridges = nridges;+ }+ }+ /**/+ i_facet++;+ }+ }++ /* make unique ridges */+ unsigned n_allridges;+ RidgeT* allridges = allRidges(faces, nfaces, dim, &n_allridges);+// printf("nallridges: %u\n", n_allridges);++ /* assign ridges ids to the ridges stored in the faces */+ assignRidgesIds(&faces, nfaces, allridges, n_allridges);++ /* all vertices */+ unsigned nvertices = qh->num_vertices;+ FullVertexT* vertices = malloc(nvertices * sizeof(FullVertexT));+ {+ qh_vertexneighbors(qh); /* make the neighbor facets of the vertices */+ vertexT *vertex;+ unsigned i_vertex=0;+ FORALLvertices{+ /* vertex id and coordinates */+ vertices[i_vertex].id = (unsigned) qh_pointid(qh, vertex->point);+ vertices[i_vertex].point = getpoint(points, dim, vertices[i_vertex].id);++ /* neighbor facets of the vertex */+ vertices[i_vertex].nneighfacets = qh_setsize(qh, vertex->neighbors);+ vertices[i_vertex].neighfacets =+ malloc(vertices[i_vertex].nneighfacets * sizeof(unsigned));+ facetT *neighbor, **neighborp;+ unsigned i_neighbor = 0;+ FOREACHneighbor_(vertex){+ vertices[i_vertex].neighfacets[i_neighbor] = neighbor->id;+ i_neighbor++;+ }+ qsortu(vertices[i_vertex].neighfacets, vertices[i_vertex].nneighfacets);++ /* neighbor vertices of the vertex */+ if(dim > 2){+ unsigned nneighsvertices;+ vertices[i_vertex].neighvertices =+ neighVertices(vertices[i_vertex].id, allridges, n_allridges,+ dim, triangulate, &nneighsvertices);+ vertices[i_vertex].nneighsvertices = nneighsvertices;+ }else{ /* dim=2 */+ vertices[i_vertex].nneighsvertices = 2;+ vertices[i_vertex].neighvertices = malloc(2 * sizeof(unsigned));+ unsigned count = 0;+ for(unsigned f=0; f < nfaces; f++){+ for(unsigned i=0; i < 2; i++){+ if(faces[f].vertices[i].id == vertices[i_vertex].id){+ vertices[i_vertex].neighvertices[count] =+ faces[f].vertices[1-i].id;+ count++;+ break;+ }+ }+ if(count == 2){+ break;+ }+ }+ }+ qsortu(vertices[i_vertex].neighvertices,+ vertices[i_vertex].nneighsvertices);++ /* neighbor ridges of the vertex */+ if(dim > 2){+ unsigned nneighridges;+ vertices[i_vertex].neighridges =+ neighRidges(vertices[i_vertex].id, allridges, n_allridges,+ &nneighridges);+ qsortu(vertices[i_vertex].neighridges, nneighridges);+ vertices[i_vertex].nneighridges = nneighridges;+ }else{ /* dim=2 */+ vertices[i_vertex].nneighridges = 0; /* ridge = vertex singleton */+ }+ /**/+ i_vertex++;+ }+ /* sort vertices according to their ids */+ qsort(vertices, nvertices, sizeof(FullVertexT), cmpfullvertices);+ }++ /* all edges */+ unsigned nalledges = 0;+ for(unsigned v=0; v < nvertices; v++){+ nalledges += vertices[v].nneighsvertices;+ }+ nalledges /= 2;+ unsigned** alledges = allEdges(vertices, nvertices, nalledges);+ qsort(alledges, nalledges, sizeof(unsigned*), cmpedges);++ { /* faces edges and ridges ids */+ facetT *facet; unsigned i_facet=0;+ FORALLfacets{+ /* facet ridges ids */+ faces[i_facet].ridgesids =+ map_ridgeid(faces[i_facet].ridges, faces[i_facet].nridges);+ qsortu(faces[i_facet].ridgesids, faces[i_facet].nridges);+ /* facet edges */+ SetOfVerticesT facet_vset = {.vertices = faces[i_facet].vertices,+ .nvertices = faces[i_facet].nvertices};+ unsigned nfaceedges;+ faces[i_facet].edges =+ makeEdges(facet_vset, alledges, nalledges, &nfaceedges);+ //qsort(faces[i_facet].edges, nfaceedges, sizeof(unsigned*), cmpedges); useless, I think+ faces[i_facet].nedges = nfaceedges;+ /**/+ i_facet++;+ }+ }++ /* ridges edges */+ if(dim > 3){+ for(unsigned r=0; r < n_allridges; r++){+ unsigned facetid = allridges[r].ridgeOf1;+ SetOfVerticesT vset = {.vertices = allridges[r].vertices,+ .nvertices = allridges[r].nvertices};+ unsigned nedges;+ allridges[r].edges =+ makeEdges(vset, faces[facetid].edges, faces[facetid].nedges, &nedges);+ allridges[r].nedges = nedges;+ }+ }++ /* output */+ out->dim = dim;+ out->vertices = vertices;+ out->nvertices = nvertices;+ out->faces = faces;+ out->nfaces = nfaces;+ out->ridges = allridges;+ out->nridges = n_allridges;+ out->edges = alledges;+ out->nedges = nalledges;++ } // end if exitcode++ /* Do cleanup regardless of whether there is an error */+ int curlong, totlong;+ qh_freeqhull(qh, !qh_ALL); /* free long memory */+ qh_memfreeshort(qh, &curlong, &totlong); /* free short memory and memory allocator */++ printf("RETURN\n");+ if(*exitcode){+ free(out);+ return 0;+ }else{+ return out;+ }++}
+ C/delaunay.c view
@@ -0,0 +1,577 @@+#define qh_QHimport+#include "qhull_ra.h"+#include "delaunay.h"+#include "utils.h"+#include <math.h> /* to use NAN */++// void printfacet(qhT* qh, facetT* facet){+// vertexT *vertex, **vertexp;+// FOREACHvertex_(facet->vertices){+// printf("facetid: %d, pointid: %d ", facet->id, qh_pointid(qh, vertex->point));+// }+// }++unsigned facetOK_(facetT* facet, unsigned degenerate){+ return !facet->upperdelaunay && (degenerate || !facet->degenerate);+} // && simplicial, && !facet->redundant - pas de simplicial avec Qt++++TesselationT* tesselation(+ double* sites,+ unsigned dim,+ unsigned n,+ unsigned atinfinity,+ unsigned degenerate,+ double vthreshold,+ unsigned* exitcode+)+{+ char opts[50]; /* option flags for qhull, see qh_opt.htm */+ sprintf(opts, "qhull d Qt Qbb%s%s",+ atinfinity ? " Qz" : "", dim>3 ? " Qx" : "");+ qhT qh_qh; /* Qhull's data structure */+ qhT *qh= &qh_qh;+ QHULL_LIB_CHECK+ qh_meminit(qh, stderr);+ boolT ismalloc = False; /* True if qhull should free points in qh_freeqhull() or reallocation */+ FILE *errfile = NULL;+ FILE* outfile = NULL;+ qh_zero(qh, errfile);+ *exitcode = qh_new_qhull(qh, dim, n, sites, ismalloc, opts, outfile, errfile);+ //fclose(tmpstdout);+ printf("exitcode: %u\n", *exitcode);++ TesselationT* out = malloc(sizeof(TesselationT)); /* output */++ if (!(*exitcode)) { /* 0 if no error from qhull */++ /* Count the number of facets we keep */+ unsigned nfacets = 0; /* to store the number of facets */+ {+ facetT *facet; /* set by FORALLfacets */+ FORALLfacets {+ if(facetOK_(facet, degenerate)){+ facet->id = nfacets;+ nfacets++;+ }else{+ qh_removefacet(qh, facet);+ }+ }+ }++ /* Initialize the tiles */+ TileT* allfacets = malloc(nfacets * sizeof(TileT));++ { /* tiles families and volumes, and centers of tiles with >0 volume */+ facetT* facet;+ unsigned i_facet = 0;+ FORALLfacets{+ if(facet->tricoplanar){+ allfacets[i_facet].family = facet->f.triowner->id;+ // if(!facet->degenerate){+ // if(i_facet == 392){+ // printf("area: %f", qh_facetarea(qh, facet));+ // }+ // allfacets[i_facet].simplex.center =+ // qh_facetcenter(qh, facet->vertices);+ // }else{+ // facetT *neighbor, **neighborp;+ // FOREACHneighbor_(facet){+ // if(facetOK_(neighbor,0) && neighbor->f.triowner->id == facet->f.triowner->id){+ // allfacets[i_facet].simplex.center =+ // qh_facetcenter(qh, neighbor->vertices);+ // break;+ // }+ // }+ // }+ }else{+ allfacets[i_facet].family = -1;+ }+ if(facet->degenerate){ // ?+ allfacets[i_facet].simplex.volume = 0;+ }else{+ allfacets[i_facet].simplex.volume = fmax(0, qh_facetarea(qh, facet));+ }+ if(allfacets[i_facet].simplex.volume > vthreshold){+ allfacets[i_facet].simplex.center = malloc(dim * sizeof(double));+ double* center = qh_facetcenter(qh, facet->vertices);+ for(unsigned i=0; i < dim; i++){+ allfacets[i_facet].simplex.center[i] = center[i];+ }+ }+ i_facet++;+ }+ }++ { /* facets ids, orientations, centers, sites ids, neighbors */+ facetT* facet;+ unsigned i_facet = 0; /* facet counter */+ FORALLfacets {+ allfacets[i_facet].id = facet->id;+ allfacets[i_facet].orientation = facet->toporient ? 1 : -1;+ /* center and circumradius */+ if(allfacets[i_facet].simplex.volume <= vthreshold){+ if(facet->tricoplanar){+ unsigned ok = 0;+ vertexT* apex = (vertexT*)facet->vertices->e[0].p;+ facetT *neighbor, **neighborp;+ FOREACHneighbor_(apex){+ if(facetOK_(neighbor,degenerate) &&+ allfacets[neighbor->id].family == allfacets[i_facet].family &&+ allfacets[neighbor->id].simplex.volume > vthreshold)+ {+ allfacets[i_facet].simplex.center =+ allfacets[neighbor->id].simplex.center;+ ok = 1;+ break;+ }+ }+ if(!ok){ /* should not happen */+ allfacets[i_facet].simplex.center = nanvector(dim);+ }+ }else{ /* should not happen */+ allfacets[i_facet].simplex.center = nanvector(dim);+ }+ }+// printf("center facet %u: %f %f %f\n", i_facet, allfacets[i_facet].simplex.center[0], allfacets[i_facet].simplex.center[1], allfacets[i_facet].simplex.center[2]);+ allfacets[i_facet].simplex.radius =+ sqrt(squaredDistance(((vertexT*)facet->vertices->e[0].p)->point,+ allfacets[i_facet].simplex.center, dim));+ // allfacets[i_facet].simplex.center =+ // facet->degenerate ? nanvector(dim)+ // : qh_facetcenter(qh, facet->vertices);+ // if(!facet->degenerate){+ // allfacets[i_facet].simplex.center = //facet->center;+ // qh_facetcenter(qh, facet->vertices);+ // // faire une première passe : calculer les centres des triowner+ // // pour ne pas les calculer pour les facets de la même famille+ // printf("center1: %f %f %f\n", facet->center[0], facet->center[1], facet->center[2]);+ // printf("center2: %f %f %f\n", allfacets[i_facet].simplex.center[0], allfacets[i_facet].simplex.center[1], allfacets[i_facet].simplex.center[2]);+ // pointT* point = ((vertexT*)facet->vertices->e[0].p)->point;+ // allfacets[i_facet].simplex.radius =+ // sqrt(squaredDistance(point, allfacets[i_facet].simplex.center,+ // dim));+ // }// }else{+ // // allfacets[i_facet].simplex.radius = NAN;+ // // }++ { /* vertices ids of the facet */+ allfacets[i_facet].simplex.sitesids =+ malloc((dim+1) * sizeof(unsigned));+ vertexT *vertex, **vertexp;+ unsigned i_vertex = 0;+ FOREACHvertex_(facet->vertices) {+ allfacets[i_facet].simplex.sitesids[i_vertex] =+ qh_pointid(qh, vertex->point);+ i_vertex++;+ }+ qsortu(allfacets[i_facet].simplex.sitesids, dim+1);+ }++ { /* neighbors facets of the facet */+ facetT *neighbor, **neighborp;+ unsigned flag[dim+1];+ allfacets[i_facet].nneighbors = 0;+ unsigned i_neighbor = 0;+ FOREACHneighbor_(facet) {+ if(flag[i_neighbor] = facetOK_(neighbor, degenerate)){+ allfacets[i_facet].nneighbors++;+ }+ i_neighbor++;+ }+ allfacets[i_facet].neighbors =+ malloc(allfacets[i_facet].nneighbors * sizeof(unsigned));+ unsigned countok = 0;+ i_neighbor = 0;+ FOREACHneighbor_(facet) {+ if(flag[i_neighbor]){+ allfacets[i_facet].neighbors[countok] = neighbor->id;+ countok++;+ }+ i_neighbor++;+ }+ }++ // /* facet family */+ // if(facet->tricoplanar){+ // allfacets[i_facet].family = facet->f.triowner->id;+ // }else{+ // allfacets[i_facet].family = -1;+ // }++ /**/+ i_facet++;+ }+ }++ // /* for degenerate facets, take the center of the owner */+ // if(degenerate){+ // facetT *facet;+ // unsigned i_facet = 0;+ // FORALLfacets{+ // if(facet->degenerate){+ // allfacets[i_facet].simplex.center =+ // allfacets[allfacets[i_facet].family].simplex.center;+ // pointT* point = ((vertexT*)facet->vertices->e[0].p)->point;+ // allfacets[i_facet].simplex.radius =+ // sqrt(squaredDistance(point, allfacets[i_facet].simplex.center,+ // dim));+ // }+ // i_facet++;+ // }+ // }++ /* neighbor facets and neighbor vertices per vertex */+ /* --- we will use the following combinations, also used later */+ /* --- combinations[m] contains all k between 0 and dim but m */+ unsigned combinations[dim+1][dim];+ for(unsigned m=0; m < dim+1; m++){+ unsigned kk=0;+ for(unsigned k=0; k < dim+1; k++){+ if(k != m){+ combinations[m][kk] = k;+ kk++;+ }+ }+ }+ /* --- initialize the sites */+ SiteT* allsites = malloc(n * sizeof(SiteT));+ /* --- array to flag neighbors - 0/1 if not neighbour/neighbour */+ unsigned** verticesFacetsNeighbours = malloc(n * sizeof(unsigned*));+ /* unsigned verticesFacetsNeighbours[n][nfacets] => stackoverflow */+ for(unsigned v=0; v < n; v++){+ allsites[v].id = v;+ allsites[v].nneighsites = 0;+ allsites[v].neighsites = malloc(0); /* will be filled by appending */+ allsites[v].nneighridges = 0;+ allsites[v].nneightiles = 0;+ verticesFacetsNeighbours[v] = uzeros(nfacets);+ }+ /* --- fill verticesFacetsNeighbours, derive number of neighbor facets */+ /* --- and derive neighbor sites */+ for(unsigned i_facet=0; i_facet < nfacets; i_facet++){+ for(unsigned j=0; j < dim+1; j++){+ unsigned vertexid = allfacets[i_facet].simplex.sitesids[j];+ if(verticesFacetsNeighbours[vertexid][i_facet] == 0){+ verticesFacetsNeighbours[vertexid][i_facet] = 1;+ allsites[vertexid].nneightiles++;+ }+ for(unsigned k=0; k < dim; k++){+ unsigned vertexid2 =+ allfacets[i_facet].simplex.sitesids[combinations[j][k]];+ unsigned pushed;+ appendu(vertexid2, &allsites[vertexid].neighsites,+ allsites[vertexid].nneighsites, &pushed);+ if(pushed){+ allsites[vertexid].nneighsites++;+ }+ }+ }+ }++ /************************************************************/+ /* second pass on facets: ridges and facet volumes */+ unsigned n_ridges_dup = nfacets * (dim+1); /* number of ridges with duplicates */+ SubTileT* allridges_dup = malloc(n_ridges_dup * sizeof(SubTileT));+ for(unsigned r=0; r < n_ridges_dup; r++){+ allridges_dup[r].simplex.sitesids = malloc(dim * sizeof(unsigned));+ allridges_dup[r].flag = 0;+ }+// qh_getarea(qh, qh->facet_list); /* make facets volumes, available in facet->f.area */+ unsigned n_ridges = 0; /* count distinct ridges */++ { /* loop on facets */+ facetT *facet;+ unsigned i_ridge_dup = 0; /* ridge counter */+ unsigned i_facet = 0; /* facet counter */+ FORALLfacets {++// allfacets[i_facet].simplex.volume = facet->f.area;+ allfacets[i_facet].nridges = dim+1;+ allfacets[i_facet].ridgesids = malloc((dim+1) * sizeof(unsigned));++ /* loop on the combinations - it increments i_ridge_dup */+ for(unsigned m=0; m < dim+1; m++){+ allridges_dup[i_ridge_dup].ridgeOf1 = facet->id;+ allridges_dup[i_ridge_dup].ridgeOf2 = -1; /* this means "nothing" */+ unsigned ids[dim];+ for(unsigned i=0; i < dim; i++){+ ids[i] = allfacets[i_facet].simplex.sitesids[combinations[m][i]];+ }+ unsigned done = 0; /* flag ridge is already done */+ for(unsigned r=0; r < i_ridge_dup; r++){+ if(allridges_dup[r].ridgeOf2 == (int) facet->id &&+ allridges_dup[r].flag==1)+ {+ unsigned ids2[dim];+ unsigned i;+ for(i=0; i < dim; i++){+ ids2[i] = allridges_dup[r].simplex.sitesids[i];+ if(ids2[i] != ids[i]){+ break;+ }+ }+ if(i == dim){+ allfacets[i_facet].ridgesids[m] = allridges_dup[r].id;+ done = 1;+ break;+ }+ }+ }+ if(done == 0){ /* => then do the ridge */+ allridges_dup[i_ridge_dup].flag = 1;+ allridges_dup[i_ridge_dup].id = n_ridges;+ allfacets[i_facet].ridgesids[m] = n_ridges;+ n_ridges++;+ for(unsigned i=0; i < dim; i++){+ allridges_dup[i_ridge_dup].simplex.sitesids[i] = ids[i];+ allsites[ids[i]].nneighridges++;+ }++ { /* loop on facet neighbors to find ridgeOf2 */+ facetT *neighbor, **neighborp;+ FOREACHneighbor_(facet){+ if(facetOK_(neighbor, degenerate)){+ unsigned fnid = neighbor->id;+ unsigned ok;+ for(unsigned mm=0; mm < dim+1; mm++){+ ok = 0;+ for(unsigned i=0; i < dim; i++){+ if(allfacets[fnid].simplex.sitesids[combinations[mm][i]]+ != ids[i])+ {+ break;+ }else{+ ok++;+ }+ }+ if(ok==dim){+ break;+ }+ }+ if(ok==dim){+ allridges_dup[i_ridge_dup].ridgeOf2 = (int) fnid;+ break;+ }+ }+ } /* end FOREACHneighbor_(facet) */+ }++ pointT* points[dim]; /* the points corresponding to the combination */+ for(unsigned i=0; i < dim; i++){+ points[i] = getpoint(sites, dim, ids[i]);+ }+ double normal[dim]; /* to store the ridge normal */+ if(dim == 2){+ double u1 = points[1][0] - points[0][0];+ double v1 = points[1][1] - points[0][1];+ allridges_dup[i_ridge_dup].simplex.volume =+ sqrt(square(u1)+square(v1));+ allridges_dup[i_ridge_dup].simplex.center =+ middle(points[0], points[1], dim);+ allridges_dup[i_ridge_dup].simplex.radius =+ sqrt(squaredDistance(allridges_dup[i_ridge_dup].simplex.center,+ points[0], dim));+ normal[0] = v1; normal[1] = -u1;+ }else{+ int parity=1;+ double squaredNorm = 0;+ for(unsigned i=0; i < dim; i++){+ double** rows = malloc((dim-1) * sizeof(double*));+ for(unsigned j=0; j < dim-1; j++){+ rows[j] = (double*) malloc((dim-1) * sizeof(double));+ for(unsigned k=0; k < dim-1; k++){+ unsigned kk = k<i ? k : k+1;+ rows[j][k] = points[j+1][kk] - points[0][kk];+ }+ }+ boolT nearzero;+ normal[i] = parity * qh_determinant(qh, rows, dim-1, &nearzero);+ squaredNorm += square(normal[i]);+ for(unsigned j=0; j < dim-1; j++){+ free(rows[j]);+ }+ free(rows);+ parity = -parity;+ }+ double surface = sqrt(squaredNorm);+ for(unsigned k=2; k < dim-1; k++){+ surface /= k;+ }+ allridges_dup[i_ridge_dup].simplex.volume = surface;+ }+ qh_normalize2(qh, normal, dim, 1, NULL, NULL);+ allridges_dup[i_ridge_dup].normal =+ malloc(dim * sizeof(double));+ for(unsigned i=0; i < dim; i++){+ allridges_dup[i_ridge_dup].normal[i] = normal[i];+ }+ allridges_dup[i_ridge_dup].offset =+ - dotproduct(points[0], normal, dim);+ if(dim > 2){ /* ridge center is already done if dim 2 */+ // if(facet->degenerate){+ // allridges_dup[i_ridge_dup].simplex.center = nanvector(dim);+ // allridges_dup[i_ridge_dup].simplex.radius = NAN;+ // }else{+ allridges_dup[i_ridge_dup].simplex.center =+ malloc(dim * sizeof(double));+ double scal = 0;+ for(unsigned i=0; i < dim; i++){+ scal += (points[0][i]-allfacets[i_facet].simplex.center[i]) *+ normal[i];+ }+ for(unsigned i=0; i < dim; i++){+ allridges_dup[i_ridge_dup].simplex.center[i] =+ allfacets[i_facet].simplex.center[i] + scal*normal[i];+ }+ allridges_dup[i_ridge_dup].simplex.radius =+ sqrt(squaredDistance(+ allridges_dup[i_ridge_dup].simplex.center,+ points[0], dim));+// }+ }+ /* orient the normal (used for plotting unbounded Voronoi cells) */+ if(allridges_dup[i_ridge_dup].ridgeOf2 == -1)+ //&& (!facet->degenerate || dim==2))+ {+ pointT* otherpoint = /* the remaining vertex of the facet (the one not in the ridge) */+ qh->interior_point; // getpoint(sites, dim, allfacets[facet->id].simplex.sitesids[m]);+ double thepoint[dim]; /* the point center+normal */+ for(unsigned i=0; i < dim; i++){+ thepoint[i] = allridges_dup[i_ridge_dup].simplex.center[i] ++ allridges_dup[i_ridge_dup].normal[i];+ }+ /* we check that these two points are on the same side of the ridge */+ double h1 = dotproduct(otherpoint,+ allridges_dup[i_ridge_dup].normal, dim) ++ allridges_dup[i_ridge_dup].offset;+ double h2 = dotproduct(thepoint,+ allridges_dup[i_ridge_dup].normal, dim) ++ allridges_dup[i_ridge_dup].offset;+ // printf("deg: %u, h1: %f, h2: %f\n", facet->degenerate, h1, h2);+ // printf("offset: %f\n", allridges_dup[i_ridge_dup].offset);+ // printf("normal: %f %f %f\n", allridges_dup[i_ridge_dup].normal[0], allridges_dup[i_ridge_dup].normal[1], allridges_dup[i_ridge_dup].normal[2]);+ if(h1*h2 >= 0){+ for(unsigned i=0; i < dim; i++){+ allridges_dup[i_ridge_dup].normal[i] *= -1;+ }+ }+ }+ for(unsigned i=0; i < dim; i++){+ free(points[i]);+ }+ }+ i_ridge_dup++;+ } // end loop combinations (m)+ qsortu(allfacets[i_facet].ridgesids, dim+1);+ /**/+ i_facet++;+ } // end FORALLfacets+ }++ /* extract unique ridges */+ SubTileT* allridges = malloc(n_ridges * sizeof(SubTileT));+ unsigned inc_ridge = 0;+ for(unsigned l=0; l < n_ridges_dup; l++){+ if(allridges_dup[l].flag){+ allridges[inc_ridge] = allridges_dup[l];+ inc_ridge++;+ }+ }++ /* make neighbor ridges per vertex */+ unsigned* i_ridges_per_vertex = uzeros(n);+ for(unsigned v=0; v < n; v++){+ allsites[v].neighridgesids =+ malloc(allsites[v].nneighridges * sizeof(unsigned));+ }+ for(unsigned l=0; l < n_ridges_dup; l++){+ if(allridges_dup[l].flag){+ for(unsigned i=0; i < dim; i++){+ unsigned v = allridges_dup[l].simplex.sitesids[i];+ allsites[v].neighridgesids[i_ridges_per_vertex[v]] =+ allridges_dup[l].id;+ i_ridges_per_vertex[v]++;+ }+ }+ }++ /* order vertices neighbor sites and make neighbor tiles per vertex */+ for(unsigned v=0; v < n; v++){+ qsortu(allsites[v].neighsites, allsites[v].nneighsites);+ allsites[v].neightiles =+ malloc(allsites[v].nneightiles * sizeof(unsigned));+ unsigned inc_facet = 0; unsigned inc_vfn = 0;+ while(inc_vfn < allsites[v].nneightiles){+ if(verticesFacetsNeighbours[v][inc_facet] == 1){+ allsites[v].neightiles[inc_vfn] = inc_facet;+ inc_vfn++;+ }+ inc_facet++;+ }+ }++ /* make the output */+ out->sites = allsites;+ out->tiles = allfacets;+ out->ntiles = nfacets;+ out->subtiles = allridges;+ out->nsubtiles = n_ridges;++ free(allridges_dup);+ free(i_ridges_per_vertex);+ free(verticesFacetsNeighbours);++ }++ /* Do cleanup regardless of whether there is an error */+ int curlong, totlong;+ qh_freeqhull(qh, !qh_ALL); /* free long memory */+ qh_memfreeshort(qh, &curlong, &totlong); /* free short memory and memory allocator */++ printf("RETURN\n");+ if(*exitcode){+ free(out);+ return 0;+ }else{+ return out;+ }++}+++void testdel2(){+ double sites[27] = {0,0,0, 0,0,1, 0,1,0, 0,1,1, 1,0,0, 1,0,1, 1,1,0, 1,1,1, 0.5,0.5,0.5};+ unsigned exitcode;+ unsigned dim = 3;+ TesselationT* x = tesselation(sites, dim, 9, 0, 0, 0, &exitcode);+ printf("TESTDEL2 - nfacets:%u\n", x->ntiles);+ for(unsigned f=0; f < x->ntiles; f++){+ printf("facet %u - sites:\n", f);+ for(unsigned i=0; i < dim+1; i++){+ printf("%u - ", x->tiles[f].simplex.sitesids[i]);+ }+ printf("\n");+ printf("facet %u - ridges:\n", f);+ for(unsigned i=0; i < dim+1; i++){+ printf("%u - ", x->tiles[f].ridgesids[i]);+ }+ printf("\n");+ printf("facet %u - neighbors:\n", f);+ for(unsigned i=0; i < x->tiles[f].nneighbors; i++){+ printf("%u - ", x->tiles[f].neighbors[i]);+ }+ printf("\n");+ }+ printf("nallridges:%u\n", x->nsubtiles);+ for(unsigned r=0; r < x->nsubtiles; r++){+ printf("ridge %u - id %u:\n", r, x->subtiles[r].id);+ for(unsigned i=0; i < dim; i++){+ printf("%u - ", x->subtiles[r].simplex.sitesids[i]);+ }+ printf("ridgeOf: %u %d", x->subtiles[r].ridgeOf1, x->subtiles[r].ridgeOf2);+ printf("\n");+ }+ free(x);+}
+ C/geom2_r.c view
@@ -0,0 +1,2096 @@+/*<html><pre> -<a href="qh-geom_r.htm"+ >-------------------------------</a><a name="TOP">-</a>+++ geom2_r.c+ infrequently used geometric routines of qhull++ see qh-geom_r.htm and geom_r.h++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/geom2_r.c#6 $$Change: 2065 $+ $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $++ frequently used code goes into geom_r.c+*/++#include "qhull_ra.h"++/*================== functions in alphabetic order ============*/++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="copypoints">-</a>++ qh_copypoints(qh, points, numpoints, dimension)+ return qh_malloc'd copy of points+ + notes:+ qh_free the returned points to avoid a memory leak+*/+coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension) {+ int size;+ coordT *newpoints;++ size= numpoints * dimension * (int)sizeof(coordT);+ if (!(newpoints= (coordT*)qh_malloc((size_t)size))) {+ qh_fprintf(qh, qh->ferr, 6004, "qhull error: insufficient memory to copy %d points\n",+ numpoints);+ qh_errexit(qh, qh_ERRmem, NULL, NULL);+ }+ memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */+ return newpoints;+} /* copypoints */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="crossproduct">-</a>++ qh_crossproduct( dim, vecA, vecB, vecC )+ crossproduct of 2 dim vectors+ C= A x B++ notes:+ from Glasner, Graphics Gems I, p. 639+ only defined for dim==3+*/+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){++ if (dim == 3) {+ vecC[0]= det2_(vecA[1], vecA[2],+ vecB[1], vecB[2]);+ vecC[1]= - det2_(vecA[0], vecA[2],+ vecB[0], vecB[2]);+ vecC[2]= det2_(vecA[0], vecA[1],+ vecB[0], vecB[1]);+ }+} /* vcross */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="determinant">-</a>++ qh_determinant(qh, rows, dim, nearzero )+ compute signed determinant of a square matrix+ uses qh.NEARzero to test for degenerate matrices++ returns:+ determinant+ overwrites rows and the matrix+ if dim == 2 or 3+ nearzero iff determinant < qh->NEARzero[dim-1]+ (!quite correct, not critical)+ if dim >= 4+ nearzero iff diagonal[k] < qh->NEARzero[k]+*/+realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero) {+ realT det=0;+ int i;+ boolT sign= False;++ *nearzero= False;+ if (dim < 2) {+ qh_fprintf(qh, qh->ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }else if (dim == 2) {+ det= det2_(rows[0][0], rows[0][1],+ rows[1][0], rows[1][1]);+ if (fabs_(det) < 10*qh->NEARzero[1]) /* not really correct, what should this be? */+ *nearzero= True;+ }else if (dim == 3) {+ det= det3_(rows[0][0], rows[0][1], rows[0][2],+ rows[1][0], rows[1][1], rows[1][2],+ rows[2][0], rows[2][1], rows[2][2]);+ if (fabs_(det) < 10*qh->NEARzero[2]) /* what should this be? det 5.5e-12 was flat for qh_maxsimplex of qdelaunay 0,0 27,27 -36,36 -9,63 */+ *nearzero= True;+ }else {+ qh_gausselim(qh, rows, dim, dim, &sign, nearzero); /* if nearzero, diagonal still ok*/+ det= 1.0;+ for (i=dim; i--; )+ det *= (rows[i])[i];+ if (sign)+ det= -det;+ }+ return det;+} /* determinant */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="detjoggle">-</a>++ qh_detjoggle(qh, points, numpoints, dimension )+ determine default max joggle for point array+ as qh_distround * qh_JOGGLEdefault++ returns:+ initial value for JOGGLEmax from points and REALepsilon++ notes:+ computes DISTround since qh_maxmin not called yet+ if qh->SCALElast, last dimension will be scaled later to MAXwidth++ loop duplicated from qh_maxmin+*/+realT qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension) {+ realT abscoord, distround, joggle, maxcoord, mincoord;+ pointT *point, *pointtemp;+ realT maxabs= -REALmax;+ realT sumabs= 0;+ realT maxwidth= 0;+ int k;++ for (k=0; k < dimension; k++) {+ if (qh->SCALElast && k == dimension-1)+ abscoord= maxwidth;+ else if (qh->DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */+ abscoord= 2 * maxabs * maxabs; /* may be low by qh->hull_dim/2 */+ else {+ maxcoord= -REALmax;+ mincoord= REALmax;+ FORALLpoint_(qh, points, numpoints) {+ maximize_(maxcoord, point[k]);+ minimize_(mincoord, point[k]);+ }+ maximize_(maxwidth, maxcoord-mincoord);+ abscoord= fmax_(maxcoord, -mincoord);+ }+ sumabs += abscoord;+ maximize_(maxabs, abscoord);+ } /* for k */+ distround= qh_distround(qh, qh->hull_dim, maxabs, sumabs);+ joggle= distround * qh_JOGGLEdefault;+ maximize_(joggle, REALepsilon * qh_JOGGLEdefault);+ trace2((qh, qh->ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));+ return joggle;+} /* detjoggle */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="detroundoff">-</a>++ qh_detroundoff(qh)+ determine maximum roundoff errors from+ REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,+ qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1++ accounts for qh.SETroundoff, qh.RANDOMdist, qh->MERGEexact+ qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,+ qh.postmerge_centrum, qh.MINoutside,+ qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar++ returns:+ sets qh.DISTround, etc. (see below)+ appends precision constants to qh.qhull_options++ see:+ qh_maxmin() for qh.NEARzero++ design:+ determine qh.DISTround for distance computations+ determine minimum denominators for qh_divzero+ determine qh.ANGLEround for angle computations+ adjust qh.premerge_cos,... for roundoff error+ determine qh.ONEmerge for maximum error due to a single merge+ determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,+ qh.MINoutside, qh.WIDEfacet+ initialize qh.max_vertex and qh.minvertex+*/+void qh_detroundoff(qhT *qh) {++ qh_option(qh, "_max-width", NULL, &qh->MAXwidth);+ if (!qh->SETroundoff) {+ qh->DISTround= qh_distround(qh, qh->hull_dim, qh->MAXabs_coord, qh->MAXsumcoord);+ if (qh->RANDOMdist)+ qh->DISTround += qh->RANDOMfactor * qh->MAXabs_coord;+ qh_option(qh, "Error-roundoff", NULL, &qh->DISTround);+ }+ qh->MINdenom= qh->MINdenom_1 * qh->MAXabs_coord;+ qh->MINdenom_1_2= sqrt(qh->MINdenom_1 * qh->hull_dim) ; /* if will be normalized */+ qh->MINdenom_2= qh->MINdenom_1_2 * qh->MAXabs_coord;+ /* for inner product */+ qh->ANGLEround= 1.01 * qh->hull_dim * REALepsilon;+ if (qh->RANDOMdist)+ qh->ANGLEround += qh->RANDOMfactor;+ if (qh->premerge_cos < REALmax/2) {+ qh->premerge_cos -= qh->ANGLEround;+ if (qh->RANDOMdist)+ qh_option(qh, "Angle-premerge-with-random", NULL, &qh->premerge_cos);+ }+ if (qh->postmerge_cos < REALmax/2) {+ qh->postmerge_cos -= qh->ANGLEround;+ if (qh->RANDOMdist)+ qh_option(qh, "Angle-postmerge-with-random", NULL, &qh->postmerge_cos);+ }+ qh->premerge_centrum += 2 * qh->DISTround; /*2 for centrum and distplane()*/+ qh->postmerge_centrum += 2 * qh->DISTround;+ if (qh->RANDOMdist && (qh->MERGEexact || qh->PREmerge))+ qh_option(qh, "Centrum-premerge-with-random", NULL, &qh->premerge_centrum);+ if (qh->RANDOMdist && qh->POSTmerge)+ qh_option(qh, "Centrum-postmerge-with-random", NULL, &qh->postmerge_centrum);+ { /* compute ONEmerge, max vertex offset for merging simplicial facets */+ realT maxangle= 1.0, maxrho;++ minimize_(maxangle, qh->premerge_cos);+ minimize_(maxangle, qh->postmerge_cos);+ /* max diameter * sin theta + DISTround for vertex to its hyperplane */+ qh->ONEmerge= sqrt((realT)qh->hull_dim) * qh->MAXwidth *+ sqrt(1.0 - maxangle * maxangle) + qh->DISTround;+ maxrho= qh->hull_dim * qh->premerge_centrum + qh->DISTround;+ maximize_(qh->ONEmerge, maxrho);+ maxrho= qh->hull_dim * qh->postmerge_centrum + qh->DISTround;+ maximize_(qh->ONEmerge, maxrho);+ if (qh->MERGING)+ qh_option(qh, "_one-merge", NULL, &qh->ONEmerge);+ }+ qh->NEARinside= qh->ONEmerge * qh_RATIOnearinside; /* only used if qh->KEEPnearinside */+ if (qh->JOGGLEmax < REALmax/2 && (qh->KEEPcoplanar || qh->KEEPinside)) {+ realT maxdist; /* adjust qh.NEARinside for joggle */+ qh->KEEPnearinside= True;+ maxdist= sqrt((realT)qh->hull_dim) * qh->JOGGLEmax + qh->DISTround;+ maxdist= 2*maxdist; /* vertex and coplanar point can joggle in opposite directions */+ maximize_(qh->NEARinside, maxdist); /* must agree with qh_nearcoplanar() */+ }+ if (qh->KEEPnearinside)+ qh_option(qh, "_near-inside", NULL, &qh->NEARinside);+ if (qh->JOGGLEmax < qh->DISTround) {+ qh_fprintf(qh, qh->ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n",+ qh->JOGGLEmax, qh->DISTround);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (qh->MINvisible > REALmax/2) {+ if (!qh->MERGING)+ qh->MINvisible= qh->DISTround;+ else if (qh->hull_dim <= 3)+ qh->MINvisible= qh->premerge_centrum;+ else+ qh->MINvisible= qh_COPLANARratio * qh->premerge_centrum;+ if (qh->APPROXhull && qh->MINvisible > qh->MINoutside)+ qh->MINvisible= qh->MINoutside;+ qh_option(qh, "Visible-distance", NULL, &qh->MINvisible);+ }+ if (qh->MAXcoplanar > REALmax/2) {+ qh->MAXcoplanar= qh->MINvisible;+ qh_option(qh, "U-coplanar-distance", NULL, &qh->MAXcoplanar);+ }+ if (!qh->APPROXhull) { /* user may specify qh->MINoutside */+ qh->MINoutside= 2 * qh->MINvisible;+ if (qh->premerge_cos < REALmax/2)+ maximize_(qh->MINoutside, (1- qh->premerge_cos) * qh->MAXabs_coord);+ qh_option(qh, "Width-outside", NULL, &qh->MINoutside);+ }+ qh->WIDEfacet= qh->MINoutside;+ maximize_(qh->WIDEfacet, qh_WIDEcoplanar * qh->MAXcoplanar);+ maximize_(qh->WIDEfacet, qh_WIDEcoplanar * qh->MINvisible);+ qh_option(qh, "_wide-facet", NULL, &qh->WIDEfacet);+ if (qh->MINvisible > qh->MINoutside + 3 * REALepsilon+ && !qh->BESToutside && !qh->FORCEoutput)+ qh_fprintf(qh, qh->ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g. Flipped facets are likely.\n",+ qh->MINvisible, qh->MINoutside);+ qh->max_vertex= qh->DISTround;+ qh->min_vertex= -qh->DISTround;+ /* numeric constants reported in printsummary */+} /* detroundoff */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="detsimplex">-</a>++ qh_detsimplex(qh, apex, points, dim, nearzero )+ compute determinant of a simplex with point apex and base points++ returns:+ signed determinant and nearzero from qh_determinant++ notes:+ uses qh.gm_matrix/qh.gm_row (assumes they're big enough)++ design:+ construct qm_matrix by subtracting apex from points+ compute determinate+*/+realT qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero) {+ pointT *coorda, *coordp, *gmcoord, *point, **pointp;+ coordT **rows;+ int k, i=0;+ realT det;++ zinc_(Zdetsimplex);+ gmcoord= qh->gm_matrix;+ rows= qh->gm_row;+ FOREACHpoint_(points) {+ if (i == dim)+ break;+ rows[i++]= gmcoord;+ coordp= point;+ coorda= apex;+ for (k=dim; k--; )+ *(gmcoord++)= *coordp++ - *coorda++;+ }+ if (i < dim) {+ qh_fprintf(qh, qh->ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",+ i, dim);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ det= qh_determinant(qh, rows, dim, nearzero);+ trace2((qh, qh->ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",+ det, qh_pointid(qh, apex), dim, *nearzero));+ return det;+} /* detsimplex */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="distnorm">-</a>++ qh_distnorm( dim, point, normal, offset )+ return distance from point to hyperplane at normal/offset++ returns:+ dist++ notes:+ dist > 0 if point is outside of hyperplane++ see:+ qh_distplane in geom_r.c+*/+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {+ coordT *normalp= normal, *coordp= point;+ realT dist;+ int k;++ dist= *offsetp;+ for (k=dim; k--; )+ dist += *(coordp++) * *(normalp++);+ return dist;+} /* distnorm */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="distround">-</a>++ qh_distround(qh, dimension, maxabs, maxsumabs )+ compute maximum round-off error for a distance computation+ to a normalized hyperplane+ maxabs is the maximum absolute value of a coordinate+ maxsumabs is the maximum possible sum of absolute coordinate values++ returns:+ max dist round for REALepsilon++ notes:+ calculate roundoff error according to Golub & van Loan, 1983, Lemma 3.2-1, "Rounding Errors"+ use sqrt(dim) since one vector is normalized+ or use maxsumabs since one vector is < 1+*/+realT qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs) {+ realT maxdistsum, maxround;++ maxdistsum= sqrt((realT)dimension) * maxabs;+ minimize_( maxdistsum, maxsumabs);+ maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs);+ /* adds maxabs for offset */+ trace4((qh, qh->ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",+ maxround, maxabs, maxsumabs, maxdistsum));+ return maxround;+} /* distround */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="divzero">-</a>++ qh_divzero( numer, denom, mindenom1, zerodiv )+ divide by a number that's nearly zero+ mindenom1= minimum denominator for dividing into 1.0++ returns:+ quotient+ sets zerodiv and returns 0.0 if it would overflow++ design:+ if numer is nearly zero and abs(numer) < abs(denom)+ return numer/denom+ else if numer is nearly zero+ return 0 and zerodiv+ else if denom/numer non-zero+ return numer/denom+ else+ return 0 and zerodiv+*/+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {+ realT temp, numerx, denomx;+++ if (numer < mindenom1 && numer > -mindenom1) {+ numerx= fabs_(numer);+ denomx= fabs_(denom);+ if (numerx < denomx) {+ *zerodiv= False;+ return numer/denom;+ }else {+ *zerodiv= True;+ return 0.0;+ }+ }+ temp= denom/numer;+ if (temp > mindenom1 || temp < -mindenom1) {+ *zerodiv= False;+ return numer/denom;+ }else {+ *zerodiv= True;+ return 0.0;+ }+} /* divzero */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="facetarea">-</a>++ qh_facetarea(qh, facet )+ return area for a facet++ notes:+ if non-simplicial,+ uses centrum to triangulate facet and sums the projected areas.+ if (qh->DELAUNAY),+ computes projected area instead for last coordinate+ assumes facet->normal exists+ projecting tricoplanar facets to the hyperplane does not appear to make a difference++ design:+ if simplicial+ compute area+ else+ for each ridge+ compute area from centrum to ridge+ negate area if upper Delaunay facet+*/+realT qh_facetarea(qhT *qh, facetT *facet) {+ vertexT *apex;+ pointT *centrum;+ realT area= 0.0;+ ridgeT *ridge, **ridgep;++ if (facet->simplicial) {+ apex= SETfirstt_(facet->vertices, vertexT);+ area= qh_facetarea_simplex(qh, qh->hull_dim, apex->point, facet->vertices,+ apex, facet->toporient, facet->normal, &facet->offset);+ }else {+ if (qh->CENTERtype == qh_AScentrum)+ centrum= facet->center;+ else+ centrum= qh_getcentrum(qh, facet);+ FOREACHridge_(facet->ridges)+ area += qh_facetarea_simplex(qh, qh->hull_dim, centrum, ridge->vertices,+ NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset);+ if (qh->CENTERtype != qh_AScentrum)+ qh_memfree(qh, centrum, qh->normal_size);+ }+ if (facet->upperdelaunay && qh->DELAUNAY)+ area= -area; /* the normal should be [0,...,1] */+ trace4((qh, qh->ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));+ return area;+} /* facetarea */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="facetarea_simplex">-</a>++ qh_facetarea_simplex(qh, dim, apex, vertices, notvertex, toporient, normal, offset )+ return area for a simplex defined by+ an apex, a base of vertices, an orientation, and a unit normal+ if simplicial or tricoplanar facet,+ notvertex is defined and it is skipped in vertices++ returns:+ computes area of simplex projected to plane [normal,offset]+ returns 0 if vertex too far below plane (qh->WIDEfacet)+ vertex can't be apex of tricoplanar facet++ notes:+ if (qh->DELAUNAY),+ computes projected area instead for last coordinate+ uses qh->gm_matrix/gm_row and qh->hull_dim+ helper function for qh_facetarea++ design:+ if Notvertex+ translate simplex to apex+ else+ project simplex to normal/offset+ translate simplex to apex+ if Delaunay+ set last row/column to 0 with -1 on diagonal+ else+ set last row to Normal+ compute determinate+ scale and flip sign for area+*/+realT qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices,+ vertexT *notvertex, boolT toporient, coordT *normal, realT *offset) {+ pointT *coorda, *coordp, *gmcoord;+ coordT **rows, *normalp;+ int k, i=0;+ realT area, dist;+ vertexT *vertex, **vertexp;+ boolT nearzero;++ gmcoord= qh->gm_matrix;+ rows= qh->gm_row;+ FOREACHvertex_(vertices) {+ if (vertex == notvertex)+ continue;+ rows[i++]= gmcoord;+ coorda= apex;+ coordp= vertex->point;+ normalp= normal;+ if (notvertex) {+ for (k=dim; k--; )+ *(gmcoord++)= *coordp++ - *coorda++;+ }else {+ dist= *offset;+ for (k=dim; k--; )+ dist += *coordp++ * *normalp++;+ if (dist < -qh->WIDEfacet) {+ zinc_(Znoarea);+ return 0.0;+ }+ coordp= vertex->point;+ normalp= normal;+ for (k=dim; k--; )+ *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;+ }+ }+ if (i != dim-1) {+ qh_fprintf(qh, qh->ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",+ i, dim);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ rows[i]= gmcoord;+ if (qh->DELAUNAY) {+ for (i=0; i < dim-1; i++)+ rows[i][dim-1]= 0.0;+ for (k=dim; k--; )+ *(gmcoord++)= 0.0;+ rows[dim-1][dim-1]= -1.0;+ }else {+ normalp= normal;+ for (k=dim; k--; )+ *(gmcoord++)= *normalp++;+ }+ zinc_(Zdetsimplex);+ area= qh_determinant(qh, rows, dim, &nearzero);+ if (toporient)+ area= -area;+ area *= qh->AREAfactor;+ trace4((qh, qh->ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",+ area, qh_pointid(qh, apex), toporient, nearzero));+ return area;+} /* facetarea_simplex */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="facetcenter">-</a>++ qh_facetcenter(qh, vertices )+ return Voronoi center (Voronoi vertex) for a facet's vertices++ returns:+ return temporary point equal to the center++ see:+ qh_voronoi_center()+*/+pointT *qh_facetcenter(qhT *qh, setT *vertices) {+ setT *points= qh_settemp(qh, qh_setsize(qh, vertices));+ vertexT *vertex, **vertexp;+ pointT *center;++ FOREACHvertex_(vertices)+ qh_setappend(qh, &points, vertex->point);+ center= qh_voronoi_center(qh, qh->hull_dim-1, points);+ qh_settempfree(qh, &points);+ return center;+} /* facetcenter */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="findgooddist">-</a>++ qh_findgooddist(qh, point, facetA, dist, facetlist )+ find best good facet visible for point from facetA+ assumes facetA is visible from point++ returns:+ best facet, i.e., good facet that is furthest from point+ distance to best facet+ NULL if none++ moves good, visible facets (and some other visible facets)+ to end of qh->facet_list++ notes:+ uses qh->visit_id++ design:+ initialize bestfacet if facetA is good+ move facetA to end of facetlist+ for each facet on facetlist+ for each unvisited neighbor of facet+ move visible neighbors to end of facetlist+ update best good neighbor+ if no good neighbors, update best facet+*/+facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp,+ facetT **facetlist) {+ realT bestdist= -REALmax, dist;+ facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;+ boolT goodseen= False;++ if (facetA->good) {+ zzinc_(Zcheckpart); /* calls from check_bestdist occur after print stats */+ qh_distplane(qh, point, facetA, &bestdist);+ bestfacet= facetA;+ goodseen= True;+ }+ qh_removefacet(qh, facetA);+ qh_appendfacet(qh, facetA);+ *facetlist= facetA;+ facetA->visitid= ++qh->visit_id;+ FORALLfacet_(*facetlist) {+ FOREACHneighbor_(facet) {+ if (neighbor->visitid == qh->visit_id)+ continue;+ neighbor->visitid= qh->visit_id;+ if (goodseen && !neighbor->good)+ continue;+ zzinc_(Zcheckpart);+ qh_distplane(qh, point, neighbor, &dist);+ if (dist > 0) {+ qh_removefacet(qh, neighbor);+ qh_appendfacet(qh, neighbor);+ if (neighbor->good) {+ goodseen= True;+ if (dist > bestdist) {+ bestdist= dist;+ bestfacet= neighbor;+ }+ }+ }+ }+ }+ if (bestfacet) {+ *distp= bestdist;+ trace2((qh, qh->ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",+ qh_pointid(qh, point), bestdist, bestfacet->id));+ return bestfacet;+ }+ trace4((qh, qh->ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",+ qh_pointid(qh, point), facetA->id));+ return NULL;+} /* findgooddist */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="getarea">-</a>++ qh_getarea(qh, facetlist )+ set area of all facets in facetlist+ collect statistics+ nop if hasAreaVolume++ returns:+ sets qh->totarea/totvol to total area and volume of convex hull+ for Delaunay triangulation, computes projected area of the lower or upper hull+ ignores upper hull if qh->ATinfinity++ notes:+ could compute outer volume by expanding facet area by rays from interior+ the following attempt at perpendicular projection underestimated badly:+ qh.totoutvol += (-dist + facet->maxoutside + qh->DISTround)+ * area/ qh->hull_dim;+ design:+ for each facet on facetlist+ compute facet->area+ update qh.totarea and qh.totvol+*/+void qh_getarea(qhT *qh, facetT *facetlist) {+ realT area;+ realT dist;+ facetT *facet;++ if (qh->hasAreaVolume)+ return;+ if (qh->REPORTfreq)+ qh_fprintf(qh, qh->ferr, 8020, "computing area of each facet and volume of the convex hull\n");+ else+ trace1((qh, qh->ferr, 1001, "qh_getarea: computing volume and area for each facet\n"));+ qh->totarea= qh->totvol= 0.0;+ FORALLfacet_(facetlist) {+ if (!facet->normal)+ continue;+ if (facet->upperdelaunay && qh->ATinfinity)+ continue;+ if (!facet->isarea) {+ facet->f.area= qh_facetarea(qh, facet);+ facet->isarea= True;+ }+ area= facet->f.area;+ if (qh->DELAUNAY) {+ if (facet->upperdelaunay == qh->UPPERdelaunay)+ qh->totarea += area;+ }else {+ qh->totarea += area;+ qh_distplane(qh, qh->interior_point, facet, &dist);+ qh->totvol += -dist * area/ qh->hull_dim;+ }+ if (qh->PRINTstatistics) {+ wadd_(Wareatot, area);+ wmax_(Wareamax, area);+ wmin_(Wareamin, area);+ }+ }+ qh->hasAreaVolume= True;+} /* getarea */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="gram_schmidt">-</a>++ qh_gram_schmidt(qh, dim, row )+ implements Gram-Schmidt orthogonalization by rows++ returns:+ false if zero norm+ overwrites rows[dim][dim]++ notes:+ see Golub & van Loan, 1983, Algorithm 6.2-2, "Modified Gram-Schmidt"+ overflow due to small divisors not handled++ design:+ for each row+ compute norm for row+ if non-zero, normalize row+ for each remaining rowA+ compute inner product of row and rowA+ reduce rowA by row * inner product+*/+boolT qh_gram_schmidt(qhT *qh, int dim, realT **row) {+ realT *rowi, *rowj, norm;+ int i, j, k;++ for (i=0; i < dim; i++) {+ rowi= row[i];+ for (norm= 0.0, k= dim; k--; rowi++)+ norm += *rowi * *rowi;+ norm= sqrt(norm);+ wmin_(Wmindenom, norm);+ if (norm == 0.0) /* either 0 or overflow due to sqrt */+ return False;+ for (k=dim; k--; )+ *(--rowi) /= norm;+ for (j=i+1; j < dim; j++) {+ rowj= row[j];+ for (norm= 0.0, k=dim; k--; )+ norm += *rowi++ * *rowj++;+ for (k=dim; k--; )+ *(--rowj) -= *(--rowi) * norm;+ }+ }+ return True;+} /* gram_schmidt */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="inthresholds">-</a>++ qh_inthresholds(qh, normal, angle )+ return True if normal within qh.lower_/upper_threshold++ returns:+ estimate of angle by summing of threshold diffs+ angle may be NULL+ smaller "angle" is better++ notes:+ invalid if qh.SPLITthresholds++ see:+ qh.lower_threshold in qh_initbuild()+ qh_initthresholds()++ design:+ for each dimension+ test threshold+*/+boolT qh_inthresholds(qhT *qh, coordT *normal, realT *angle) {+ boolT within= True;+ int k;+ realT threshold;++ if (angle)+ *angle= 0.0;+ for (k=0; k < qh->hull_dim; k++) {+ threshold= qh->lower_threshold[k];+ if (threshold > -REALmax/2) {+ if (normal[k] < threshold)+ within= False;+ if (angle) {+ threshold -= normal[k];+ *angle += fabs_(threshold);+ }+ }+ if (qh->upper_threshold[k] < REALmax/2) {+ threshold= qh->upper_threshold[k];+ if (normal[k] > threshold)+ within= False;+ if (angle) {+ threshold -= normal[k];+ *angle += fabs_(threshold);+ }+ }+ }+ return within;+} /* inthresholds */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="joggleinput">-</a>++ qh_joggleinput(qh)+ randomly joggle input to Qhull by qh.JOGGLEmax+ initial input is qh.first_point/qh.num_points of qh.hull_dim+ repeated calls use qh.input_points/qh.num_points++ returns:+ joggles points at qh.first_point/qh.num_points+ copies data to qh.input_points/qh.input_malloc if first time+ determines qh.JOGGLEmax if it was zero+ if qh.DELAUNAY+ computes the Delaunay projection of the joggled points++ notes:+ if qh.DELAUNAY, unnecessarily joggles the last coordinate+ the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease++ design:+ if qh.DELAUNAY+ set qh.SCALElast for reduced precision errors+ if first call+ initialize qh.input_points to the original input points+ if qh.JOGGLEmax == 0+ determine default qh.JOGGLEmax+ else+ increase qh.JOGGLEmax according to qh.build_cnt+ joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]+ if qh.DELAUNAY+ sets the Delaunay projection+*/+void qh_joggleinput(qhT *qh) {+ int i, seed, size;+ coordT *coordp, *inputp;+ realT randr, randa, randb;++ if (!qh->input_points) { /* first call */+ qh->input_points= qh->first_point;+ qh->input_malloc= qh->POINTSmalloc;+ size= qh->num_points * qh->hull_dim * sizeof(coordT);+ if (!(qh->first_point=(coordT*)qh_malloc((size_t)size))) {+ qh_fprintf(qh, qh->ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",+ qh->num_points);+ qh_errexit(qh, qh_ERRmem, NULL, NULL);+ }+ qh->POINTSmalloc= True;+ if (qh->JOGGLEmax == 0.0) {+ qh->JOGGLEmax= qh_detjoggle(qh, qh->input_points, qh->num_points, qh->hull_dim);+ qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);+ }+ }else { /* repeated call */+ if (!qh->RERUN && qh->build_cnt > qh_JOGGLEretry) {+ if (((qh->build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) {+ realT maxjoggle= qh->MAXwidth * qh_JOGGLEmaxincrease;+ if (qh->JOGGLEmax < maxjoggle) {+ qh->JOGGLEmax *= qh_JOGGLEincrease;+ minimize_(qh->JOGGLEmax, maxjoggle);+ }+ }+ }+ qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);+ }+ if (qh->build_cnt > 1 && qh->JOGGLEmax > fmax_(qh->MAXwidth/4, 0.1)) {+ qh_fprintf(qh, qh->ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n",+ qh->JOGGLEmax);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ /* for some reason, using qh->ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */+ seed= qh_RANDOMint;+ qh_option(qh, "_joggle-seed", &seed, NULL);+ trace0((qh, qh->ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n",+ qh->JOGGLEmax, seed));+ inputp= qh->input_points;+ coordp= qh->first_point;+ randa= 2.0 * qh->JOGGLEmax/qh_RANDOMmax;+ randb= -qh->JOGGLEmax;+ size= qh->num_points * qh->hull_dim;+ for (i=size; i--; ) {+ randr= qh_RANDOMint;+ *(coordp++)= *(inputp++) + (randr * randa + randb);+ }+ if (qh->DELAUNAY) {+ qh->last_low= qh->last_high= qh->last_newhigh= REALmax;+ qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);+ }+} /* joggleinput */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="maxabsval">-</a>++ qh_maxabsval( normal, dim )+ return pointer to maximum absolute value of a dim vector+ returns NULL if dim=0+*/+realT *qh_maxabsval(realT *normal, int dim) {+ realT maxval= -REALmax;+ realT *maxp= NULL, *colp, absval;+ int k;++ for (k=dim, colp= normal; k--; colp++) {+ absval= fabs_(*colp);+ if (absval > maxval) {+ maxval= absval;+ maxp= colp;+ }+ }+ return maxp;+} /* maxabsval */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="maxmin">-</a>++ qh_maxmin(qh, points, numpoints, dimension )+ return max/min points for each dimension+ determine max and min coordinates++ returns:+ returns a temporary set of max and min points+ may include duplicate points. Does not include qh.GOODpoint+ sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth+ qh.MAXlastcoord, qh.MINlastcoord+ initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok++ notes:+ loop duplicated in qh_detjoggle()++ design:+ initialize global precision variables+ checks definition of REAL...+ for each dimension+ for each point+ collect maximum and minimum point+ collect maximum of maximums and minimum of minimums+ determine qh.NEARzero for Gaussian Elimination+*/+setT *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension) {+ int k;+ realT maxcoord, temp;+ pointT *minimum, *maximum, *point, *pointtemp;+ setT *set;++ qh->max_outside= 0.0;+ qh->MAXabs_coord= 0.0;+ qh->MAXwidth= -REALmax;+ qh->MAXsumcoord= 0.0;+ qh->min_vertex= 0.0;+ qh->WAScoplanar= False;+ if (qh->ZEROcentrum)+ qh->ZEROall_ok= True;+ if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax+ && REALmax > 0.0 && -REALmax < 0.0)+ ; /* all ok */+ else {+ qh_fprintf(qh, qh->ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\+REALepsilon %g REALmin %g REALmax %g -REALmax %g\n",+ REALepsilon, REALmin, REALmax, -REALmax);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ set= qh_settemp(qh, 2*dimension);+ for (k=0; k < dimension; k++) {+ if (points == qh->GOODpointp)+ minimum= maximum= points + dimension;+ else+ minimum= maximum= points;+ FORALLpoint_(qh, points, numpoints) {+ if (point == qh->GOODpointp)+ continue;+ if (maximum[k] < point[k])+ maximum= point;+ else if (minimum[k] > point[k])+ minimum= point;+ }+ if (k == dimension-1) {+ qh->MINlastcoord= minimum[k];+ qh->MAXlastcoord= maximum[k];+ }+ if (qh->SCALElast && k == dimension-1)+ maxcoord= qh->MAXwidth;+ else {+ maxcoord= fmax_(maximum[k], -minimum[k]);+ if (qh->GOODpointp) {+ temp= fmax_(qh->GOODpointp[k], -qh->GOODpointp[k]);+ maximize_(maxcoord, temp);+ }+ temp= maximum[k] - minimum[k];+ maximize_(qh->MAXwidth, temp);+ }+ maximize_(qh->MAXabs_coord, maxcoord);+ qh->MAXsumcoord += maxcoord;+ qh_setappend(qh, &set, maximum);+ qh_setappend(qh, &set, minimum);+ /* calculation of qh NEARzero is based on Golub & van Loan, 1983,+ Eq. 4.4-13 for "Gaussian elimination with complete pivoting".+ Golub & van Loan say that n^3 can be ignored and 10 be used in+ place of rho */+ qh->NEARzero[k]= 80 * qh->MAXsumcoord * REALepsilon;+ }+ if (qh->IStracing >=1)+ qh_printpoints(qh, qh->ferr, "qh_maxmin: found the max and min points(by dim):", set);+ return(set);+} /* maxmin */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="maxouter">-</a>++ qh_maxouter(qh)+ return maximum distance from facet to outer plane+ normally this is qh.max_outside+qh.DISTround+ does not include qh.JOGGLEmax++ see:+ qh_outerinner()++ notes:+ need to add another qh.DISTround if testing actual point with computation++ for joggle:+ qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex)+ need to use Wnewvertexmax since could have a coplanar point for a high+ facet that is replaced by a low facet+ need to add qh.JOGGLEmax if testing input points+*/+realT qh_maxouter(qhT *qh) {+ realT dist;++ dist= fmax_(qh->max_outside, qh->DISTround);+ dist += qh->DISTround;+ trace4((qh, qh->ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh->max_outside));+ return dist;+} /* maxouter */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="maxsimplex">-</a>++ qh_maxsimplex(qh, dim, maxpoints, points, numpoints, simplex )+ determines maximum simplex for a set of points+ starts from points already in simplex+ skips qh.GOODpointp (assumes that it isn't in maxpoints)++ returns:+ simplex with dim+1 points++ notes:+ assumes at least pointsneeded points in points+ maximizes determinate for x,y,z,w, etc.+ uses maxpoints as long as determinate is clearly non-zero++ design:+ initialize simplex with at least two points+ (find points with max or min x coordinate)+ for each remaining dimension+ add point that maximizes the determinate+ (use points from maxpoints first)+*/+void qh_maxsimplex(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) {+ pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL;+ boolT nearzero, maxnearzero= False;+ int k, sizinit;+ realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax;++ sizinit= qh_setsize(qh, *simplex);+ if (sizinit < 2) {+ if (qh_setsize(qh, maxpoints) >= 2) {+ FOREACHpoint_(maxpoints) {+ if (maxcoord < point[0]) {+ maxcoord= point[0];+ maxx= point;+ }+ if (mincoord > point[0]) {+ mincoord= point[0];+ minx= point;+ }+ }+ }else {+ FORALLpoint_(qh, points, numpoints) {+ if (point == qh->GOODpointp)+ continue;+ if (maxcoord < point[0]) {+ maxcoord= point[0];+ maxx= point;+ }+ if (mincoord > point[0]) {+ mincoord= point[0];+ minx= point;+ }+ }+ }+ qh_setunique(qh, simplex, minx);+ if (qh_setsize(qh, *simplex) < 2)+ qh_setunique(qh, simplex, maxx);+ sizinit= qh_setsize(qh, *simplex);+ if (sizinit < 2) {+ qh_precision(qh, "input has same x coordinate");+ if (zzval_(Zsetplane) > qh->hull_dim+1) {+ qh_fprintf(qh, qh->ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",+ qh_setsize(qh, maxpoints)+numpoints);+ qh_errexit(qh, qh_ERRprec, NULL, NULL);+ }else {+ qh_fprintf(qh, qh->ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh->hull_dim);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ }+ }+ for (k=sizinit; k < dim+1; k++) {+ maxpoint= NULL;+ maxdet= -REALmax;+ FOREACHpoint_(maxpoints) {+ if (!qh_setin(*simplex, point)) {+ det= qh_detsimplex(qh, point, *simplex, k, &nearzero);+ if ((det= fabs_(det)) > maxdet) {+ maxdet= det;+ maxpoint= point;+ maxnearzero= nearzero;+ }+ }+ }+ if (!maxpoint || maxnearzero) {+ zinc_(Zsearchpoints);+ if (!maxpoint) {+ trace0((qh, qh->ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));+ }else {+ trace0((qh, qh->ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n",+ k+1, qh_pointid(qh, maxpoint), maxdet));+ }+ FORALLpoint_(qh, points, numpoints) {+ if (point == qh->GOODpointp)+ continue;+ if (!qh_setin(*simplex, point)) {+ det= qh_detsimplex(qh, point, *simplex, k, &nearzero);+ if ((det= fabs_(det)) > maxdet) {+ maxdet= det;+ maxpoint= point;+ maxnearzero= nearzero;+ }+ }+ }+ } /* !maxpoint */+ if (!maxpoint) {+ qh_fprintf(qh, qh->ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ qh_setappend(qh, simplex, maxpoint);+ trace1((qh, qh->ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",+ qh_pointid(qh, maxpoint), k+1, maxdet));+ } /* k */+} /* maxsimplex */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="minabsval">-</a>++ qh_minabsval( normal, dim )+ return minimum absolute value of a dim vector+*/+realT qh_minabsval(realT *normal, int dim) {+ realT minval= 0;+ realT maxval= 0;+ realT *colp;+ int k;++ for (k=dim, colp=normal; k--; colp++) {+ maximize_(maxval, *colp);+ minimize_(minval, *colp);+ }+ return fmax_(maxval, -minval);+} /* minabsval */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="mindiff">-</a>++ qh_mindif(qh, vecA, vecB, dim )+ return index of min abs. difference of two vectors+*/+int qh_mindiff(realT *vecA, realT *vecB, int dim) {+ realT mindiff= REALmax, diff;+ realT *vecAp= vecA, *vecBp= vecB;+ int k, mink= 0;++ for (k=0; k < dim; k++) {+ diff= *vecAp++ - *vecBp++;+ diff= fabs_(diff);+ if (diff < mindiff) {+ mindiff= diff;+ mink= k;+ }+ }+ return mink;+} /* mindiff */++++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="orientoutside">-</a>++ qh_orientoutside(qh, facet )+ make facet outside oriented via qh.interior_point++ returns:+ True if facet reversed orientation.+*/+boolT qh_orientoutside(qhT *qh, facetT *facet) {+ int k;+ realT dist;++ qh_distplane(qh, qh->interior_point, facet, &dist);+ if (dist > 0) {+ for (k=qh->hull_dim; k--; )+ facet->normal[k]= -facet->normal[k];+ facet->offset= -facet->offset;+ return True;+ }+ return False;+} /* orientoutside */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="outerinner">-</a>++ qh_outerinner(qh, facet, outerplane, innerplane )+ if facet and qh.maxoutdone (i.e., qh_check_maxout)+ returns outer and inner plane for facet+ else+ returns maximum outer and inner plane+ accounts for qh.JOGGLEmax++ see:+ qh_maxouter(qh), qh_check_bestdist(), qh_check_points()++ notes:+ outerplaner or innerplane may be NULL+ facet is const+ Does not error (QhullFacet)++ includes qh.DISTround for actual points+ adds another qh.DISTround if testing with floating point arithmetic+*/+void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {+ realT dist, mindist;+ vertexT *vertex, **vertexp;++ if (outerplane) {+ if (!qh_MAXoutside || !facet || !qh->maxoutdone) {+ *outerplane= qh_maxouter(qh); /* includes qh.DISTround */+ }else { /* qh_MAXoutside ... */+#if qh_MAXoutside+ *outerplane= facet->maxoutside + qh->DISTround;+#endif++ }+ if (qh->JOGGLEmax < REALmax/2)+ *outerplane += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);+ }+ if (innerplane) {+ if (facet) {+ mindist= REALmax;+ FOREACHvertex_(facet->vertices) {+ zinc_(Zdistio);+ qh_distplane(qh, vertex->point, facet, &dist);+ minimize_(mindist, dist);+ }+ *innerplane= mindist - qh->DISTround;+ }else+ *innerplane= qh->min_vertex - qh->DISTround;+ if (qh->JOGGLEmax < REALmax/2)+ *innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);+ }+} /* outerinner */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="pointdist">-</a>++ qh_pointdist( point1, point2, dim )+ return distance between two points++ notes:+ returns distance squared if 'dim' is negative+*/+coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {+ coordT dist, diff;+ int k;++ dist= 0.0;+ for (k= (dim > 0 ? dim : -dim); k--; ) {+ diff= *point1++ - *point2++;+ dist += diff * diff;+ }+ if (dim > 0)+ return(sqrt(dist));+ return dist;+} /* pointdist */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="printmatrix">-</a>++ qh_printmatrix(qh, fp, string, rows, numrow, numcol )+ print matrix to fp given by row vectors+ print string as header+ qh may be NULL if fp is defined++ notes:+ print a vector by qh_printmatrix(qh, fp, "", &vect, 1, len)+*/+void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol) {+ realT *rowp;+ realT r; /*bug fix*/+ int i,k;++ qh_fprintf(qh, fp, 9001, "%s\n", string);+ for (i=0; i < numrow; i++) {+ rowp= rows[i];+ for (k=0; k < numcol; k++) {+ r= *rowp++;+ qh_fprintf(qh, fp, 9002, "%6.3g ", r);+ }+ qh_fprintf(qh, fp, 9003, "\n");+ }+} /* printmatrix */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="printpoints">-</a>++ qh_printpoints(qh, fp, string, points )+ print pointids to fp for a set of points+ if string, prints string and 'p' point ids+*/+void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points) {+ pointT *point, **pointp;++ if (string) {+ qh_fprintf(qh, fp, 9004, "%s", string);+ FOREACHpoint_(points)+ qh_fprintf(qh, fp, 9005, " p%d", qh_pointid(qh, point));+ qh_fprintf(qh, fp, 9006, "\n");+ }else {+ FOREACHpoint_(points)+ qh_fprintf(qh, fp, 9007, " %d", qh_pointid(qh, point));+ qh_fprintf(qh, fp, 9008, "\n");+ }+} /* printpoints */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="projectinput">-</a>++ qh_projectinput(qh)+ project input points using qh.lower_bound/upper_bound and qh->DELAUNAY+ if qh.lower_bound[k]=qh.upper_bound[k]= 0,+ removes dimension k+ if halfspace intersection+ removes dimension k from qh.feasible_point+ input points in qh->first_point, num_points, input_dim++ returns:+ new point array in qh->first_point of qh->hull_dim coordinates+ sets qh->POINTSmalloc+ if qh->DELAUNAY+ projects points to paraboloid+ lowbound/highbound is also projected+ if qh->ATinfinity+ adds point "at-infinity"+ if qh->POINTSmalloc+ frees old point array++ notes:+ checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY+++ design:+ sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)+ determines newdim and newnum for qh->hull_dim and qh->num_points+ projects points to newpoints+ projects qh.lower_bound to itself+ projects qh.upper_bound to itself+ if qh->DELAUNAY+ if qh->ATINFINITY+ projects points to paraboloid+ computes "infinity" point as vertex average and 10% above all points+ else+ uses qh_setdelaunay to project points to paraboloid+*/+void qh_projectinput(qhT *qh) {+ int k,i;+ int newdim= qh->input_dim, newnum= qh->num_points;+ signed char *project;+ int projectsize= (qh->input_dim+1)*sizeof(*project);+ pointT *newpoints, *coord, *infinity;+ realT paraboloid, maxboloid= 0;++ project= (signed char*)qh_memalloc(qh, projectsize);+ memset((char*)project, 0, (size_t)projectsize);+ for (k=0; k < qh->input_dim; k++) { /* skip Delaunay bound */+ if (qh->lower_bound[k] == 0 && qh->upper_bound[k] == 0) {+ project[k]= -1;+ newdim--;+ }+ }+ if (qh->DELAUNAY) {+ project[k]= 1;+ newdim++;+ if (qh->ATinfinity)+ newnum++;+ }+ if (newdim != qh->hull_dim) {+ qh_memfree(qh, project, projectsize);+ qh_fprintf(qh, qh->ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh->hull_dim);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ if (!(newpoints= qh->temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){+ qh_memfree(qh, project, projectsize);+ qh_fprintf(qh, qh->ferr, 6016, "qhull error: insufficient memory to project %d points\n",+ qh->num_points);+ qh_errexit(qh, qh_ERRmem, NULL, NULL);+ }+ /* qh_projectpoints throws error if mismatched dimensions */+ qh_projectpoints(qh, project, qh->input_dim+1, qh->first_point,+ qh->num_points, qh->input_dim, newpoints, newdim);+ trace1((qh, qh->ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));+ qh_projectpoints(qh, project, qh->input_dim+1, qh->lower_bound,+ 1, qh->input_dim+1, qh->lower_bound, newdim+1);+ qh_projectpoints(qh, project, qh->input_dim+1, qh->upper_bound,+ 1, qh->input_dim+1, qh->upper_bound, newdim+1);+ if (qh->HALFspace) {+ if (!qh->feasible_point) {+ qh_memfree(qh, project, projectsize);+ qh_fprintf(qh, qh->ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ qh_projectpoints(qh, project, qh->input_dim, qh->feasible_point,+ 1, qh->input_dim, qh->feasible_point, newdim);+ }+ qh_memfree(qh, project, projectsize);+ if (qh->POINTSmalloc)+ qh_free(qh->first_point);+ qh->first_point= newpoints;+ qh->POINTSmalloc= True;+ qh->temp_malloc= NULL;+ if (qh->DELAUNAY && qh->ATinfinity) {+ coord= qh->first_point;+ infinity= qh->first_point + qh->hull_dim * qh->num_points;+ for (k=qh->hull_dim-1; k--; )+ infinity[k]= 0.0;+ for (i=qh->num_points; i--; ) {+ paraboloid= 0.0;+ for (k=0; k < qh->hull_dim-1; k++) {+ paraboloid += *coord * *coord;+ infinity[k] += *coord;+ coord++;+ }+ *(coord++)= paraboloid;+ maximize_(maxboloid, paraboloid);+ }+ /* coord == infinity */+ for (k=qh->hull_dim-1; k--; )+ *(coord++) /= qh->num_points;+ *(coord++)= maxboloid * 1.1;+ qh->num_points++;+ trace0((qh, qh->ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));+ }else if (qh->DELAUNAY) /* !qh->ATinfinity */+ qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);+} /* projectinput */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="projectpoints">-</a>++ qh_projectpoints(qh, project, n, points, numpoints, dim, newpoints, newdim )+ project points/numpoints/dim to newpoints/newdim+ if project[k] == -1+ delete dimension k+ if project[k] == 1+ add dimension k by duplicating previous column+ n is size of project++ notes:+ newpoints may be points if only adding dimension at end++ design:+ check that 'project' and 'newdim' agree+ for each dimension+ if project == -1+ skip dimension+ else+ determine start of column in newpoints+ determine start of column in points+ if project == +1, duplicate previous column+ copy dimension (column) from points to newpoints+*/+void qh_projectpoints(qhT *qh, signed char *project, int n, realT *points,+ int numpoints, int dim, realT *newpoints, int newdim) {+ int testdim= dim, oldk=0, newk=0, i,j=0,k;+ realT *newp, *oldp;++ for (k=0; k < n; k++)+ testdim += project[k];+ if (testdim != newdim) {+ qh_fprintf(qh, qh->ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",+ newdim, testdim);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ for (j=0; j<n; j++) {+ if (project[j] == -1)+ oldk++;+ else {+ newp= newpoints+newk++;+ if (project[j] == +1) {+ if (oldk >= dim)+ continue;+ oldp= points+oldk;+ }else+ oldp= points+oldk++;+ for (i=numpoints; i--; ) {+ *newp= *oldp;+ newp += newdim;+ oldp += dim;+ }+ }+ if (oldk >= dim)+ break;+ }+ trace1((qh, qh->ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",+ numpoints, dim, newdim));+} /* projectpoints */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="rotateinput">-</a>++ qh_rotateinput(qh, rows )+ rotate input using row matrix+ input points given by qh->first_point, num_points, hull_dim+ assumes rows[dim] is a scratch buffer+ if qh->POINTSmalloc, overwrites input points, else mallocs a new array++ returns:+ rotated input+ sets qh->POINTSmalloc++ design:+ see qh_rotatepoints+*/+void qh_rotateinput(qhT *qh, realT **rows) {++ if (!qh->POINTSmalloc) {+ qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);+ qh->POINTSmalloc= True;+ }+ qh_rotatepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, rows);+} /* rotateinput */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="rotatepoints">-</a>++ qh_rotatepoints(qh, points, numpoints, dim, row )+ rotate numpoints points by a d-dim row matrix+ assumes rows[dim] is a scratch buffer++ returns:+ rotated points in place++ design:+ for each point+ for each coordinate+ use row[dim] to compute partial inner product+ for each coordinate+ rotate by partial inner product+*/+void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **row) {+ realT *point, *rowi, *coord= NULL, sum, *newval;+ int i,j,k;++ if (qh->IStracing >= 1)+ qh_printmatrix(qh, qh->ferr, "qh_rotatepoints: rotate points by", row, dim, dim);+ for (point= points, j= numpoints; j--; point += dim) {+ newval= row[dim];+ for (i=0; i < dim; i++) {+ rowi= row[i];+ coord= point;+ for (sum= 0.0, k= dim; k--; )+ sum += *rowi++ * *coord++;+ *(newval++)= sum;+ }+ for (k=dim; k--; )+ *(--coord)= *(--newval);+ }+} /* rotatepoints */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="scaleinput">-</a>++ qh_scaleinput(qh)+ scale input points using qh->low_bound/high_bound+ input points given by qh->first_point, num_points, hull_dim+ if qh->POINTSmalloc, overwrites input points, else mallocs a new array++ returns:+ scales coordinates of points to low_bound[k], high_bound[k]+ sets qh->POINTSmalloc++ design:+ see qh_scalepoints+*/+void qh_scaleinput(qhT *qh) {++ if (!qh->POINTSmalloc) {+ qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);+ qh->POINTSmalloc= True;+ }+ qh_scalepoints(qh, qh->first_point, qh->num_points, qh->hull_dim,+ qh->lower_bound, qh->upper_bound);+} /* scaleinput */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="scalelast">-</a>++ qh_scalelast(qh, points, numpoints, dim, low, high, newhigh )+ scale last coordinate to [0,m] for Delaunay triangulations+ input points given by points, numpoints, dim++ returns:+ changes scale of last coordinate from [low, high] to [0, newhigh]+ overwrites last coordinate of each point+ saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay()++ notes:+ when called by qh_setdelaunay, low/high may not match actual data++ design:+ compute scale and shift factors+ apply to last coordinate of each point+*/+void qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low,+ coordT high, coordT newhigh) {+ realT scale, shift;+ coordT *coord;+ int i;+ boolT nearzero= False;++ trace4((qh, qh->ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n",+ low, high, newhigh));+ qh->last_low= low;+ qh->last_high= high;+ qh->last_newhigh= newhigh;+ scale= qh_divzero(newhigh, high - low,+ qh->MINdenom_1, &nearzero);+ if (nearzero) {+ if (qh->DELAUNAY)+ qh_fprintf(qh, qh->ferr, 6019, "qhull input error: can not scale last coordinate. Input is cocircular\n or cospherical. Use option 'Qz' to add a point at infinity.\n");+ else+ qh_fprintf(qh, qh->ferr, 6020, "qhull input error: can not scale last coordinate. New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n",+ newhigh, low, high, high-low);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ shift= - low * newhigh / (high-low);+ coord= points + dim - 1;+ for (i=numpoints; i--; coord += dim)+ *coord= *coord * scale + shift;+} /* scalelast */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="scalepoints">-</a>++ qh_scalepoints(qh, points, numpoints, dim, newlows, newhighs )+ scale points to new lowbound and highbound+ retains old bound when newlow= -REALmax or newhigh= +REALmax++ returns:+ scaled points+ overwrites old points++ design:+ for each coordinate+ compute current low and high bound+ compute scale and shift factors+ scale all points+ enforce new low and high bound for all points+*/+void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim,+ realT *newlows, realT *newhighs) {+ int i,k;+ realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord;+ boolT nearzero= False;++ for (k=0; k < dim; k++) {+ newhigh= newhighs[k];+ newlow= newlows[k];+ if (newhigh > REALmax/2 && newlow < -REALmax/2)+ continue;+ low= REALmax;+ high= -REALmax;+ for (i=numpoints, coord=points+k; i--; coord += dim) {+ minimize_(low, *coord);+ maximize_(high, *coord);+ }+ if (newhigh > REALmax/2)+ newhigh= high;+ if (newlow < -REALmax/2)+ newlow= low;+ if (qh->DELAUNAY && k == dim-1 && newhigh < newlow) {+ qh_fprintf(qh, qh->ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n",+ k, k, newhigh, newlow);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ scale= qh_divzero(newhigh - newlow, high - low,+ qh->MINdenom_1, &nearzero);+ if (nearzero) {+ qh_fprintf(qh, qh->ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n",+ k, newlow, newhigh, low, high);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ shift= (newlow * high - low * newhigh)/(high-low);+ coord= points+k;+ for (i=numpoints; i--; coord += dim)+ *coord= *coord * scale + shift;+ coord= points+k;+ if (newlow < newhigh) {+ mincoord= newlow;+ maxcoord= newhigh;+ }else {+ mincoord= newhigh;+ maxcoord= newlow;+ }+ for (i=numpoints; i--; coord += dim) {+ minimize_(*coord, maxcoord); /* because of roundoff error */+ maximize_(*coord, mincoord);+ }+ trace0((qh, qh->ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n",+ k, low, high, newlow, newhigh, numpoints, scale, shift));+ }+} /* scalepoints */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="setdelaunay">-</a>++ qh_setdelaunay(qh, dim, count, points )+ project count points to dim-d paraboloid for Delaunay triangulation++ dim is one more than the dimension of the input set+ assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation)++ points is a dim*count realT array. The first dim-1 coordinates+ are the coordinates of the first input point. array[dim] is+ the first coordinate of the second input point. array[2*dim] is+ the first coordinate of the third input point.++ if qh.last_low defined (i.e., 'Qbb' called qh_scalelast)+ calls qh_scalelast to scale the last coordinate the same as the other points++ returns:+ for each point+ sets point[dim-1] to sum of squares of coordinates+ scale points to 'Qbb' if needed++ notes:+ to project one point, use+ qh_setdelaunay(qh, qh->hull_dim, 1, point)++ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale+ the coordinates after the original projection.++*/+void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points) {+ int i, k;+ coordT *coordp, coord;+ realT paraboloid;++ trace0((qh, qh->ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count));+ coordp= points;+ for (i=0; i < count; i++) {+ coord= *coordp++;+ paraboloid= coord*coord;+ for (k=dim-2; k--; ) {+ coord= *coordp++;+ paraboloid += coord*coord;+ }+ *coordp++ = paraboloid;+ }+ if (qh->last_low < REALmax/2)+ qh_scalelast(qh, points, count, dim, qh->last_low, qh->last_high, qh->last_newhigh);+} /* setdelaunay */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="sethalfspace">-</a>++ qh_sethalfspace(qh, dim, coords, nextp, normal, offset, feasible )+ set point to dual of halfspace relative to feasible point+ halfspace is normal coefficients and offset.++ returns:+ false and prints error if feasible point is outside of hull+ overwrites coordinates for point at dim coords+ nextp= next point (coords)+ does not call qh_errexit++ design:+ compute distance from feasible point to halfspace+ divide each normal coefficient by -dist+*/+boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp,+ coordT *normal, coordT *offset, coordT *feasible) {+ coordT *normp= normal, *feasiblep= feasible, *coordp= coords;+ realT dist;+ realT r; /*bug fix*/+ int k;+ boolT zerodiv;++ dist= *offset;+ for (k=dim; k--; )+ dist += *(normp++) * *(feasiblep++);+ if (dist > 0)+ goto LABELerroroutside;+ normp= normal;+ if (dist < -qh->MINdenom) {+ for (k=dim; k--; )+ *(coordp++)= *(normp++) / -dist;+ }else {+ for (k=dim; k--; ) {+ *(coordp++)= qh_divzero(*(normp++), -dist, qh->MINdenom_1, &zerodiv);+ if (zerodiv)+ goto LABELerroroutside;+ }+ }+ *nextp= coordp;+ if (qh->IStracing >= 4) {+ qh_fprintf(qh, qh->ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset);+ for (k=dim, coordp=coords; k--; ) {+ r= *coordp++;+ qh_fprintf(qh, qh->ferr, 8022, " %6.2g", r);+ }+ qh_fprintf(qh, qh->ferr, 8023, "\n");+ }+ return True;+LABELerroroutside:+ feasiblep= feasible;+ normp= normal;+ qh_fprintf(qh, qh->ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");+ for (k=dim; k--; )+ qh_fprintf(qh, qh->ferr, 8024, qh_REAL_1, r=*(feasiblep++));+ qh_fprintf(qh, qh->ferr, 8025, "\n halfspace: ");+ for (k=dim; k--; )+ qh_fprintf(qh, qh->ferr, 8026, qh_REAL_1, r=*(normp++));+ qh_fprintf(qh, qh->ferr, 8027, "\n at offset: ");+ qh_fprintf(qh, qh->ferr, 8028, qh_REAL_1, *offset);+ qh_fprintf(qh, qh->ferr, 8029, " and distance: ");+ qh_fprintf(qh, qh->ferr, 8030, qh_REAL_1, dist);+ qh_fprintf(qh, qh->ferr, 8031, "\n");+ return False;+} /* sethalfspace */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="sethalfspace_all">-</a>++ qh_sethalfspace_all(qh, dim, count, halfspaces, feasible )+ generate dual for halfspace intersection with feasible point+ array of count halfspaces+ each halfspace is normal coefficients followed by offset+ the origin is inside the halfspace if the offset is negative+ feasible is a point inside all halfspaces (http://www.qhull.org/html/qhalf.htm#notes)++ returns:+ malloc'd array of count X dim-1 points++ notes:+ call before qh_init_B or qh_initqhull_globals+ free memory when done+ unused/untested code: please email bradb@shore.net if this works ok for you+ if using option 'Fp', qh->feasible_point must be set (e.g., to 'feasible')+ qh->feasible_point is a malloc'd array that is freed by qh_freebuffers.++ design:+ see qh_sethalfspace+*/+coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible) {+ int i, newdim;+ pointT *newpoints;+ coordT *coordp, *normalp, *offsetp;++ trace0((qh, qh->ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n"));+ newdim= dim - 1;+ if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){+ qh_fprintf(qh, qh->ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",+ count);+ qh_errexit(qh, qh_ERRmem, NULL, NULL);+ }+ coordp= newpoints;+ normalp= halfspaces;+ for (i=0; i < count; i++) {+ offsetp= normalp + newdim;+ if (!qh_sethalfspace(qh, newdim, coordp, &coordp, normalp, offsetp, feasible)) {+ qh_free(newpoints); /* feasible is not inside halfspace as reported by qh_sethalfspace */+ qh_fprintf(qh, qh->ferr, 8032, "The halfspace was at index %d\n", i);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ normalp= offsetp + 1;+ }+ return newpoints;+} /* sethalfspace_all */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="sharpnewfacets">-</a>++ qh_sharpnewfacets(qh)++ returns:+ true if could be an acute angle (facets in different quadrants)++ notes:+ for qh_findbest++ design:+ for all facets on qh.newfacet_list+ if two facets are in different quadrants+ set issharp+*/+boolT qh_sharpnewfacets(qhT *qh) {+ facetT *facet;+ boolT issharp = False;+ int *quadrant, k;++ quadrant= (int*)qh_memalloc(qh, qh->hull_dim * sizeof(int));+ FORALLfacet_(qh->newfacet_list) {+ if (facet == qh->newfacet_list) {+ for (k=qh->hull_dim; k--; )+ quadrant[ k]= (facet->normal[ k] > 0);+ }else {+ for (k=qh->hull_dim; k--; ) {+ if (quadrant[ k] != (facet->normal[ k] > 0)) {+ issharp= True;+ break;+ }+ }+ }+ if (issharp)+ break;+ }+ qh_memfree(qh, quadrant, qh->hull_dim * sizeof(int));+ trace3((qh, qh->ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));+ return issharp;+} /* sharpnewfacets */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="voronoi_center">-</a>++ qh_voronoi_center(qh, dim, points )+ return Voronoi center for a set of points+ dim is the orginal dimension of the points+ gh.gm_matrix/qh.gm_row are scratch buffers++ returns:+ center as a temporary point (qh_memalloc)+ if non-simplicial,+ returns center for max simplex of points++ notes:+ only called by qh_facetcenter+ from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65++ design:+ if non-simplicial+ determine max simplex for points+ translate point0 of simplex to origin+ compute sum of squares of diagonal+ compute determinate+ compute Voronoi center (see Bowyer & Woodwark)+*/+pointT *qh_voronoi_center(qhT *qh, int dim, setT *points) {+ pointT *point, **pointp, *point0;+ pointT *center= (pointT*)qh_memalloc(qh, qh->center_size);+ setT *simplex;+ int i, j, k, size= qh_setsize(qh, points);+ coordT *gmcoord;+ realT *diffp, sum2, *sum2row, *sum2p, det, factor;+ boolT nearzero, infinite;++ if (size == dim+1)+ simplex= points;+ else if (size < dim+1) {+ qh_memfree(qh, center, qh->center_size);+ qh_fprintf(qh, qh->ferr, 6025, "qhull internal error (qh_voronoi_center):\n need at least %d points to construct a Voronoi center\n",+ dim+1);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ simplex= points; /* never executed -- avoids warning */+ }else {+ simplex= qh_settemp(qh, dim+1);+ qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);+ }+ point0= SETfirstt_(simplex, pointT);+ gmcoord= qh->gm_matrix;+ for (k=0; k < dim; k++) {+ qh->gm_row[k]= gmcoord;+ FOREACHpoint_(simplex) {+ if (point != point0)+ *(gmcoord++)= point[k] - point0[k];+ }+ }+ sum2row= gmcoord;+ for (i=0; i < dim; i++) {+ sum2= 0.0;+ for (k=0; k < dim; k++) {+ diffp= qh->gm_row[k] + i;+ sum2 += *diffp * *diffp;+ }+ *(gmcoord++)= sum2;+ }+ det= qh_determinant(qh, qh->gm_row, dim, &nearzero);+ factor= qh_divzero(0.5, det, qh->MINdenom, &infinite);+ if (infinite) {+ for (k=dim; k--; )+ center[k]= qh_INFINITE;+ if (qh->IStracing)+ qh_printpoints(qh, qh->ferr, "qh_voronoi_center: at infinity for ", simplex);+ }else {+ for (i=0; i < dim; i++) {+ gmcoord= qh->gm_matrix;+ sum2p= sum2row;+ for (k=0; k < dim; k++) {+ qh->gm_row[k]= gmcoord;+ if (k == i) {+ for (j=dim; j--; )+ *(gmcoord++)= *sum2p++;+ }else {+ FOREACHpoint_(simplex) {+ if (point != point0)+ *(gmcoord++)= point[k] - point0[k];+ }+ }+ }+ center[i]= qh_determinant(qh, qh->gm_row, dim, &nearzero)*factor + point0[i];+ }+#ifndef qh_NOtrace+ if (qh->IStracing >= 3) {+ qh_fprintf(qh, qh->ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);+ qh_printmatrix(qh, qh->ferr, "center:", ¢er, 1, dim);+ if (qh->IStracing >= 5) {+ qh_printpoints(qh, qh->ferr, "points", simplex);+ FOREACHpoint_(simplex)+ qh_fprintf(qh, qh->ferr, 8034, "p%d dist %.2g, ", qh_pointid(qh, point),+ qh_pointdist(point, center, dim));+ qh_fprintf(qh, qh->ferr, 8035, "\n");+ }+ }+#endif+ }+ if (simplex != points)+ qh_settempfree(qh, &simplex);+ return center;+} /* voronoi_center */+
+ C/geom_r.c view
@@ -0,0 +1,1234 @@+/*<html><pre> -<a href="qh-geom_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ geom_r.c+ geometric routines of qhull++ see qh-geom_r.htm and geom_r.h++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/geom_r.c#2 $$Change: 1995 $+ $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $++ infrequent code goes into geom2_r.c+*/++#include "qhull_ra.h"++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="distplane">-</a>++ qh_distplane(qh, point, facet, dist )+ return distance from point to facet++ returns:+ dist+ if qh.RANDOMdist, joggles result++ notes:+ dist > 0 if point is above facet (i.e., outside)+ does not error (for qh_sortfacets, qh_outerinner)++ see:+ qh_distnorm in geom2_r.c+ qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies+*/+void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist) {+ coordT *normal= facet->normal, *coordp, randr;+ int k;++ switch (qh->hull_dim){+ case 2:+ *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];+ break;+ case 3:+ *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];+ break;+ case 4:+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];+ break;+ case 5:+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];+ break;+ case 6:+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];+ break;+ case 7:+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];+ break;+ case 8:+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];+ break;+ default:+ *dist= facet->offset;+ coordp= point;+ for (k=qh->hull_dim; k--; )+ *dist += *coordp++ * *normal++;+ break;+ }+ zinc_(Zdistplane);+ if (!qh->RANDOMdist && qh->IStracing < 4)+ return;+ if (qh->RANDOMdist) {+ randr= qh_RANDOMint;+ *dist += (2.0 * randr / qh_RANDOMmax - 1.0) *+ qh->RANDOMfactor * qh->MAXabs_coord;+ }+ if (qh->IStracing >= 4) {+ qh_fprintf(qh, qh->ferr, 8001, "qh_distplane: ");+ qh_fprintf(qh, qh->ferr, 8002, qh_REAL_1, *dist);+ qh_fprintf(qh, qh->ferr, 8003, "from p%d to f%d\n", qh_pointid(qh, point), facet->id);+ }+ return;+} /* distplane */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="findbest">-</a>++ qh_findbest(qh, point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )+ find facet that is furthest below a point+ for upperDelaunay facets+ returns facet only if !qh_NOupper and clearly above++ input:+ starts search at 'startfacet' (can not be flipped)+ if !bestoutside(qh_ALL), stops at qh.MINoutside++ returns:+ best facet (reports error if NULL)+ early out if isoutside defined and bestdist > qh.MINoutside+ dist is distance to facet+ isoutside is true if point is outside of facet+ numpart counts the number of distance tests++ see also:+ qh_findbestnew()++ notes:+ If merging (testhorizon), searches horizon facets of coplanar best facets because+ after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d+ avoid calls to distplane, function calls, and real number operations.+ caller traces result+ Optimized for outside points. Tried recording a search set for qh_findhorizon.+ Made code more complicated.++ when called by qh_partitionvisible():+ indicated by qh_ISnewfacets+ qh.newfacet_list is list of simplicial, new facets+ qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)+ qh.bestfacet_notsharp set if qh_sharpnewfacets returns False++ when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),+ qh_check_bestdist(), qh_addpoint()+ indicated by !qh_ISnewfacets+ returns best facet in neighborhood of given facet+ this is best facet overall if dist > - qh.MAXcoplanar+ or hull has at least a "spherical" curvature++ design:+ initialize and test for early exit+ repeat while there are better facets+ for each neighbor of facet+ exit if outside facet found+ test for better facet+ if point is inside and partitioning+ test for new facets with a "sharp" intersection+ if so, future calls go to qh_findbestnew()+ test horizon facets+*/+facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,+ boolT bestoutside, boolT isnewfacets, boolT noupper,+ realT *dist, boolT *isoutside, int *numpart) {+ realT bestdist= -REALmax/2 /* avoid underflow */;+ facetT *facet, *neighbor, **neighborp;+ facetT *bestfacet= NULL, *lastfacet= NULL;+ int oldtrace= qh->IStracing;+ unsigned int visitid= ++qh->visit_id;+ int numpartnew=0;+ boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */++ zinc_(Zfindbest);+ if (qh->IStracing >= 3 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {+ if (qh->TRACElevel > qh->IStracing)+ qh->IStracing= qh->TRACElevel;+ qh_fprintf(qh, qh->ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",+ qh_pointid(qh, point), startfacet->id, isnewfacets, bestoutside, qh->MINoutside);+ qh_fprintf(qh, qh->ferr, 8005, " testhorizon? %d noupper? %d", testhorizon, noupper);+ qh_fprintf(qh, qh->ferr, 8006, " Last point added was p%d.", qh->furthest_id);+ qh_fprintf(qh, qh->ferr, 8007, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh->max_outside);+ }+ if (isoutside)+ *isoutside= True;+ if (!startfacet->flipped) { /* test startfacet */+ *numpart= 1;+ qh_distplane(qh, point, startfacet, dist); /* this code is duplicated below */+ if (!bestoutside && *dist >= qh->MINoutside+ && (!startfacet->upperdelaunay || !noupper)) {+ bestfacet= startfacet;+ goto LABELreturn_best;+ }+ bestdist= *dist;+ if (!startfacet->upperdelaunay) {+ bestfacet= startfacet;+ }+ }else+ *numpart= 0;+ startfacet->visitid= visitid;+ facet= startfacet;+ while (facet) {+ trace4((qh, qh->ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n",+ facet->id, bestdist, getid_(bestfacet)));+ lastfacet= facet;+ FOREACHneighbor_(facet) {+ if (!neighbor->newfacet && isnewfacets)+ continue;+ if (neighbor->visitid == visitid)+ continue;+ neighbor->visitid= visitid;+ if (!neighbor->flipped) { /* code duplicated above */+ (*numpart)++;+ qh_distplane(qh, point, neighbor, dist);+ if (*dist > bestdist) {+ if (!bestoutside && *dist >= qh->MINoutside+ && (!neighbor->upperdelaunay || !noupper)) {+ bestfacet= neighbor;+ goto LABELreturn_best;+ }+ if (!neighbor->upperdelaunay) {+ bestfacet= neighbor;+ bestdist= *dist;+ break; /* switch to neighbor */+ }else if (!bestfacet) {+ bestdist= *dist;+ break; /* switch to neighbor */+ }+ } /* end of *dist>bestdist */+ } /* end of !flipped */+ } /* end of FOREACHneighbor */+ facet= neighbor; /* non-NULL only if *dist>bestdist */+ } /* end of while facet (directed search) */+ if (isnewfacets) {+ if (!bestfacet) {+ bestdist= -REALmax/2;+ bestfacet= qh_findbestnew(qh, point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew);+ testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */+ }else if (!qh->findbest_notsharp && bestdist < - qh->DISTround) {+ if (qh_sharpnewfacets(qh)) {+ /* seldom used, qh_findbestnew will retest all facets */+ zinc_(Zfindnewsharp);+ bestfacet= qh_findbestnew(qh, point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);+ testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */+ qh->findbestnew= True;+ }else+ qh->findbest_notsharp= True;+ }+ }+ if (!bestfacet)+ bestfacet= qh_findbestlower(qh, lastfacet, point, &bestdist, numpart);+ if (testhorizon)+ bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);+ *dist= bestdist;+ if (isoutside && bestdist < qh->MINoutside)+ *isoutside= False;+LABELreturn_best:+ zadd_(Zfindbesttot, *numpart);+ zmax_(Zfindbestmax, *numpart);+ (*numpart) += numpartnew;+ qh->IStracing= oldtrace;+ return bestfacet;+} /* findbest */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="findbesthorizon">-</a>++ qh_findbesthorizon(qh, qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )+ search coplanar and better horizon facets from startfacet/bestdist+ ischeckmax turns off statistics and minsearch update+ all arguments must be initialized+ returns(ischeckmax):+ best facet+ returns(!ischeckmax):+ best facet that is not upperdelaunay+ allows upperdelaunay that is clearly outside+ returns:+ bestdist is distance to bestfacet+ numpart -- updates number of distance tests++ notes:+ no early out -- use qh_findbest() or qh_findbestnew()+ Searches coplanar or better horizon facets++ when called by qh_check_maxout() (qh_IScheckmax)+ startfacet must be closest to the point+ Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum+ even though other facets are below the point.+ updates facet->maxoutside for good, visited facets+ may return NULL++ searchdist is qh.max_outside + 2 * DISTround+ + max( MINvisible('Vn'), MAXcoplanar('Un'));+ This setting is a guess. It must be at least max_outside + 2*DISTround+ because a facet may have a geometric neighbor across a vertex++ design:+ for each horizon facet of coplanar best facets+ continue if clearly inside+ unless upperdelaunay or clearly outside+ update best facet+*/+facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {+ facetT *bestfacet= startfacet;+ realT dist;+ facetT *neighbor, **neighborp, *facet;+ facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */+ int numpartinit= *numpart, coplanarfacetset_size;+ unsigned int visitid= ++qh->visit_id;+ boolT newbest= False; /* for tracing */+ realT minsearch, searchdist; /* skip facets that are too far from point */++ if (!ischeckmax) {+ zinc_(Zfindhorizon);+ }else {+#if qh_MAXoutside+ if ((!qh->ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)+ startfacet->maxoutside= *bestdist;+#endif+ }+ searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */+ minsearch= *bestdist - searchdist;+ if (ischeckmax) {+ /* Always check coplanar facets. Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */+ minimize_(minsearch, -searchdist);+ }+ coplanarfacetset_size= 0;+ facet= startfacet;+ while (True) {+ trace4((qh, qh->ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n",+ facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper,+ minsearch, searchdist));+ FOREACHneighbor_(facet) {+ if (neighbor->visitid == visitid)+ continue;+ neighbor->visitid= visitid;+ if (!neighbor->flipped) {+ qh_distplane(qh, point, neighbor, &dist);+ (*numpart)++;+ if (dist > *bestdist) {+ if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh->MINoutside)) {+ bestfacet= neighbor;+ *bestdist= dist;+ newbest= True;+ if (!ischeckmax) {+ minsearch= dist - searchdist;+ if (dist > *bestdist + searchdist) {+ zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */+ coplanarfacetset_size= 0;+ }+ }+ }+ }else if (dist < minsearch)+ continue; /* if ischeckmax, dist can't be positive */+#if qh_MAXoutside+ if (ischeckmax && dist > neighbor->maxoutside)+ neighbor->maxoutside= dist;+#endif+ } /* end of !flipped */+ if (nextfacet) {+ if (!coplanarfacetset_size++) {+ SETfirst_(qh->coplanarfacetset)= nextfacet;+ SETtruncate_(qh->coplanarfacetset, 1);+ }else+ qh_setappend(qh, &qh->coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv+ and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */+ }+ nextfacet= neighbor;+ } /* end of EACHneighbor */+ facet= nextfacet;+ if (facet)+ nextfacet= NULL;+ else if (!coplanarfacetset_size)+ break;+ else if (!--coplanarfacetset_size) {+ facet= SETfirstt_(qh->coplanarfacetset, facetT);+ SETtruncate_(qh->coplanarfacetset, 0);+ }else+ facet= (facetT*)qh_setdellast(qh->coplanarfacetset);+ } /* while True, for each facet in qh.coplanarfacetset */+ if (!ischeckmax) {+ zadd_(Zfindhorizontot, *numpart - numpartinit);+ zmax_(Zfindhorizonmax, *numpart - numpartinit);+ if (newbest)+ zinc_(Zparthorizon);+ }+ trace4((qh, qh->ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));+ return bestfacet;+} /* findbesthorizon */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="findbestnew">-</a>++ qh_findbestnew(qh, point, startfacet, dist, isoutside, numpart )+ find best newfacet for point+ searches all of qh.newfacet_list starting at startfacet+ searches horizon facets of coplanar best newfacets+ searches all facets if startfacet == qh.facet_list+ returns:+ best new or horizon facet that is not upperdelaunay+ early out if isoutside and not 'Qf'+ dist is distance to facet+ isoutside is true if point is outside of facet+ numpart is number of distance tests++ notes:+ Always used for merged new facets (see qh_USEfindbestnew)+ Avoids upperdelaunay facet unless (isoutside and outside)++ Uses qh.visit_id, qh.coplanarfacetset.+ If share visit_id with qh_findbest, coplanarfacetset is incorrect.++ If merging (testhorizon), searches horizon facets of coplanar best facets because+ a point maybe coplanar to the bestfacet, below its horizon facet,+ and above a horizon facet of a coplanar newfacet. For example,+ rbox 1000 s Z1 G1e-13 | qhull+ rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc++ qh_findbestnew() used if+ qh_sharpnewfacets -- newfacets contains a sharp angle+ if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)++ see also:+ qh_partitionall() and qh_findbest()++ design:+ for each new facet starting from startfacet+ test distance from point to facet+ return facet if clearly outside+ unless upperdelaunay and a lowerdelaunay exists+ update best facet+ test horizon facets+*/+facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,+ realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {+ realT bestdist= -REALmax/2;+ facetT *bestfacet= NULL, *facet;+ int oldtrace= qh->IStracing, i;+ unsigned int visitid= ++qh->visit_id;+ realT distoutside= 0.0;+ boolT isdistoutside; /* True if distoutside is defined */+ boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */++ if (!startfacet) {+ if (qh->MERGING)+ qh_fprintf(qh, qh->ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n");+ else+ qh_fprintf(qh, qh->ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",+ qh->furthest_id);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ zinc_(Zfindnew);+ if (qh->BESToutside || bestoutside)+ isdistoutside= False;+ else {+ isdistoutside= True;+ distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */+ }+ if (isoutside)+ *isoutside= True;+ *numpart= 0;+ if (qh->IStracing >= 3 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {+ if (qh->TRACElevel > qh->IStracing)+ qh->IStracing= qh->TRACElevel;+ qh_fprintf(qh, qh->ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",+ qh_pointid(qh, point), startfacet->id, isdistoutside, distoutside);+ qh_fprintf(qh, qh->ferr, 8009, " Last point added p%d visitid %d.", qh->furthest_id, visitid);+ qh_fprintf(qh, qh->ferr, 8010, " Last merge was #%d.\n", zzval_(Ztotmerge));+ }+ /* visit all new facets starting with startfacet, maybe qh->facet_list */+ for (i=0, facet=startfacet; i < 2; i++, facet= qh->newfacet_list) {+ FORALLfacet_(facet) {+ if (facet == startfacet && i)+ break;+ facet->visitid= visitid;+ if (!facet->flipped) {+ qh_distplane(qh, point, facet, dist);+ (*numpart)++;+ if (*dist > bestdist) {+ if (!facet->upperdelaunay || *dist >= qh->MINoutside) {+ bestfacet= facet;+ if (isdistoutside && *dist >= distoutside)+ goto LABELreturn_bestnew;+ bestdist= *dist;+ }+ }+ } /* end of !flipped */+ } /* FORALLfacet from startfacet or qh->newfacet_list */+ }+ if (testhorizon || !bestfacet) /* testhorizon is always True. Keep the same code as qh_findbest */+ bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,+ !qh_NOupper, &bestdist, numpart);+ *dist= bestdist;+ if (isoutside && *dist < qh->MINoutside)+ *isoutside= False;+LABELreturn_bestnew:+ zadd_(Zfindnewtot, *numpart);+ zmax_(Zfindnewmax, *numpart);+ trace4((qh, qh->ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist));+ qh->IStracing= oldtrace;+ return bestfacet;+} /* findbestnew */++/* ============ hyperplane functions -- keep code together [?] ============ */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="backnormal">-</a>++ qh_backnormal(qh, rows, numrow, numcol, sign, normal, nearzero )+ given an upper-triangular rows array and a sign,+ solve for normal equation x using back substitution over rows U++ returns:+ normal= x++ if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),+ if fails on last row+ this means that the hyperplane intersects [0,..,1]+ sets last coordinate of normal to sign+ otherwise+ sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]+ sets nearzero++ notes:+ assumes numrow == numcol-1++ see Golub & van Loan, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting"++ solves Ux=b where Ax=b and PA=LU+ b= [0,...,0,sign or 0] (sign is either -1 or +1)+ last row of A= [0,...,0,1]++ 1) Ly=Pb == y=b since P only permutes the 0's of b++ design:+ for each row from end+ perform back substitution+ if near zero+ use qh_divzero for division+ if zero divide and not last row+ set tail of normal to 0+*/+void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign,+ coordT *normal, boolT *nearzero) {+ int i, j;+ coordT *normalp, *normal_tail, *ai, *ak;+ realT diagonal;+ boolT waszero;+ int zerocol= -1;++ normalp= normal + numcol - 1;+ *normalp--= (sign ? -1.0 : 1.0);+ for (i=numrow; i--; ) {+ *normalp= 0.0;+ ai= rows[i] + i + 1;+ ak= normalp+1;+ for (j=i+1; j < numcol; j++)+ *normalp -= *ai++ * *ak++;+ diagonal= (rows[i])[i];+ if (fabs_(diagonal) > qh->MINdenom_2)+ *(normalp--) /= diagonal;+ else {+ waszero= False;+ *normalp= qh_divzero(*normalp, diagonal, qh->MINdenom_1_2, &waszero);+ if (waszero) {+ zerocol= i;+ *(normalp--)= (sign ? -1.0 : 1.0);+ for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)+ *normal_tail= 0.0;+ }else+ normalp--;+ }+ }+ if (zerocol != -1) {+ zzinc_(Zback0);+ *nearzero= True;+ trace4((qh, qh->ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));+ qh_precision(qh, "zero diagonal on back substitution");+ }+} /* backnormal */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="gausselim">-</a>++ qh_gausselim(qh, rows, numrow, numcol, sign )+ Gaussian elimination with partial pivoting++ returns:+ rows is upper triangular (includes row exchanges)+ flips sign for each row exchange+ sets nearzero if pivot[k] < qh.NEARzero[k], else clears it++ notes:+ if nearzero, the determinant's sign may be incorrect.+ assumes numrow <= numcol++ design:+ for each row+ determine pivot and exchange rows if necessary+ test for near zero+ perform gaussian elimination step+*/+void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {+ realT *ai, *ak, *rowp, *pivotrow;+ realT n, pivot, pivot_abs= 0.0, temp;+ int i, j, k, pivoti, flip=0;++ *nearzero= False;+ for (k=0; k < numrow; k++) {+ pivot_abs= fabs_((rows[k])[k]);+ pivoti= k;+ for (i=k+1; i < numrow; i++) {+ if ((temp= fabs_((rows[i])[k])) > pivot_abs) {+ pivot_abs= temp;+ pivoti= i;+ }+ }+ if (pivoti != k) {+ rowp= rows[pivoti];+ rows[pivoti]= rows[k];+ rows[k]= rowp;+ *sign ^= 1;+ flip ^= 1;+ }+ if (pivot_abs <= qh->NEARzero[k]) {+ *nearzero= True;+ if (pivot_abs == 0.0) { /* remainder of column == 0 */+ if (qh->IStracing >= 4) {+ qh_fprintf(qh, qh->ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh->DISTround);+ qh_printmatrix(qh, qh->ferr, "Matrix:", rows, numrow, numcol);+ }+ zzinc_(Zgauss0);+ qh_precision(qh, "zero pivot for Gaussian elimination");+ goto LABELnextcol;+ }+ }+ pivotrow= rows[k] + k;+ pivot= *pivotrow++; /* signed value of pivot, and remainder of row */+ for (i=k+1; i < numrow; i++) {+ ai= rows[i] + k;+ ak= pivotrow;+ n= (*ai++)/pivot; /* divzero() not needed since |pivot| >= |*ai| */+ for (j= numcol - (k+1); j--; )+ *ai++ -= n * *ak++;+ }+ LABELnextcol:+ ;+ }+ wmin_(Wmindenom, pivot_abs); /* last pivot element */+ if (qh->IStracing >= 5)+ qh_printmatrix(qh, qh->ferr, "qh_gausselem: result", rows, numrow, numcol);+} /* gausselim */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="getangle">-</a>++ qh_getangle(qh, vect1, vect2 )+ returns the dot product of two vectors+ if qh.RANDOMdist, joggles result++ notes:+ the angle may be > 1.0 or < -1.0 because of roundoff errors++*/+realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2) {+ realT angle= 0, randr;+ int k;++ for (k=qh->hull_dim; k--; )+ angle += *vect1++ * *vect2++;+ if (qh->RANDOMdist) {+ randr= qh_RANDOMint;+ angle += (2.0 * randr / qh_RANDOMmax - 1.0) *+ qh->RANDOMfactor;+ }+ trace4((qh, qh->ferr, 4006, "qh_getangle: %2.2g\n", angle));+ return(angle);+} /* getangle */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="getcenter">-</a>++ qh_getcenter(qh, vertices )+ returns arithmetic center of a set of vertices as a new point++ notes:+ allocates point array for center+*/+pointT *qh_getcenter(qhT *qh, setT *vertices) {+ int k;+ pointT *center, *coord;+ vertexT *vertex, **vertexp;+ int count= qh_setsize(qh, vertices);++ if (count < 2) {+ qh_fprintf(qh, qh->ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ center= (pointT *)qh_memalloc(qh, qh->normal_size);+ for (k=0; k < qh->hull_dim; k++) {+ coord= center+k;+ *coord= 0.0;+ FOREACHvertex_(vertices)+ *coord += vertex->point[k];+ *coord /= count; /* count>=2 by QH6003 */+ }+ return(center);+} /* getcenter */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="getcentrum">-</a>++ qh_getcentrum(qh, facet )+ returns the centrum for a facet as a new point++ notes:+ allocates the centrum+*/+pointT *qh_getcentrum(qhT *qh, facetT *facet) {+ realT dist;+ pointT *centrum, *point;++ point= qh_getcenter(qh, facet->vertices);+ zzinc_(Zcentrumtests);+ qh_distplane(qh, point, facet, &dist);+ centrum= qh_projectpoint(qh, point, facet, dist);+ qh_memfree(qh, point, qh->normal_size);+ trace4((qh, qh->ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",+ facet->id, qh_setsize(qh, facet->vertices), dist));+ return centrum;+} /* getcentrum */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="getdistance">-</a>++ qh_getdistance(qh, facet, neighbor, mindist, maxdist )+ returns the maxdist and mindist distance of any vertex from neighbor++ returns:+ the max absolute value++ design:+ for each vertex of facet that is not in neighbor+ test the distance from vertex to neighbor+*/+realT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) {+ vertexT *vertex, **vertexp;+ realT dist, maxd, mind;++ FOREACHvertex_(facet->vertices)+ vertex->seen= False;+ FOREACHvertex_(neighbor->vertices)+ vertex->seen= True;+ mind= 0.0;+ maxd= 0.0;+ FOREACHvertex_(facet->vertices) {+ if (!vertex->seen) {+ zzinc_(Zbestdist);+ qh_distplane(qh, vertex->point, neighbor, &dist);+ if (dist < mind)+ mind= dist;+ else if (dist > maxd)+ maxd= dist;+ }+ }+ *mindist= mind;+ *maxdist= maxd;+ mind= -mind;+ if (maxd > mind)+ return maxd;+ else+ return mind;+} /* getdistance */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="normalize">-</a>++ qh_normalize(qh, normal, dim, toporient )+ normalize a vector and report if too small+ does not use min norm++ see:+ qh_normalize2+*/+void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient) {+ qh_normalize2(qh, normal, dim, toporient, NULL, NULL);+} /* normalize */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="normalize2">-</a>++ qh_normalize2(qh, normal, dim, toporient, minnorm, ismin )+ normalize a vector and report if too small+ qh.MINdenom/MINdenom1 are the upper limits for divide overflow++ returns:+ normalized vector+ flips sign if !toporient+ if minnorm non-NULL,+ sets ismin if normal < minnorm++ notes:+ if zero norm+ sets all elements to sqrt(1.0/dim)+ if divide by zero (divzero())+ sets largest element to +/-1+ bumps Znearlysingular++ design:+ computes norm+ test for minnorm+ if not near zero+ normalizes normal+ else if zero norm+ sets normal to standard value+ else+ uses qh_divzero to normalize+ if nearzero+ sets norm to direction of maximum value+*/+void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,+ realT *minnorm, boolT *ismin) {+ int k;+ realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;+ boolT zerodiv;++ norm1= normal+1;+ norm2= normal+2;+ norm3= normal+3;+ if (dim == 2)+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));+ else if (dim == 3)+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));+ else if (dim == 4) {+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)+ + (*norm3)*(*norm3));+ }else if (dim > 4) {+ norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)+ + (*norm3)*(*norm3);+ for (k=dim-4, colp=normal+4; k--; colp++)+ norm += (*colp) * (*colp);+ norm= sqrt(norm);+ }+ if (minnorm) {+ if (norm < *minnorm)+ *ismin= True;+ else+ *ismin= False;+ }+ wmin_(Wmindenom, norm);+ if (norm > qh->MINdenom) {+ if (!toporient)+ norm= -norm;+ *normal /= norm;+ *norm1 /= norm;+ if (dim == 2)+ ; /* all done */+ else if (dim == 3)+ *norm2 /= norm;+ else if (dim == 4) {+ *norm2 /= norm;+ *norm3 /= norm;+ }else if (dim >4) {+ *norm2 /= norm;+ *norm3 /= norm;+ for (k=dim-4, colp=normal+4; k--; )+ *colp++ /= norm;+ }+ }else if (norm == 0.0) {+ temp= sqrt(1.0/dim);+ for (k=dim, colp=normal; k--; )+ *colp++ = temp;+ }else {+ if (!toporient)+ norm= -norm;+ for (k=dim, colp=normal; k--; colp++) { /* k used below */+ temp= qh_divzero(*colp, norm, qh->MINdenom_1, &zerodiv);+ if (!zerodiv)+ *colp= temp;+ else {+ maxp= qh_maxabsval(normal, dim);+ temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);+ for (k=dim, colp=normal; k--; colp++)+ *colp= 0.0;+ *maxp= temp;+ zzinc_(Znearlysingular);+ trace0((qh, qh->ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",+ norm, qh->furthest_id));+ return;+ }+ }+ }+} /* normalize */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="projectpoint">-</a>++ qh_projectpoint(qh, point, facet, dist )+ project point onto a facet by dist++ returns:+ returns a new point++ notes:+ if dist= distplane(point,facet)+ this projects point to hyperplane+ assumes qh_memfree_() is valid for normal_size+*/+pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist) {+ pointT *newpoint, *np, *normal;+ int normsize= qh->normal_size;+ int k;+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */++ qh_memalloc_(qh, normsize, freelistp, newpoint, pointT);+ np= newpoint;+ normal= facet->normal;+ for (k=qh->hull_dim; k--; )+ *(np++)= *point++ - dist * *normal++;+ return(newpoint);+} /* projectpoint */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="setfacetplane">-</a>++ qh_setfacetplane(qh, facet )+ sets the hyperplane for a facet+ if qh.RANDOMdist, joggles hyperplane++ notes:+ uses global buffers qh.gm_matrix and qh.gm_row+ overwrites facet->normal if already defined+ updates Wnewvertex if PRINTstatistics+ sets facet->upperdelaunay if upper envelope of Delaunay triangulation++ design:+ copy vertex coordinates to qh.gm_matrix/gm_row+ compute determinate+ if nearzero+ recompute determinate with gaussian elimination+ if nearzero+ force outside orientation by testing interior point+*/+void qh_setfacetplane(qhT *qh, facetT *facet) {+ pointT *point;+ vertexT *vertex, **vertexp;+ int normsize= qh->normal_size;+ int k,i, oldtrace= 0;+ realT dist;+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */+ coordT *coord, *gmcoord;+ pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;+ boolT nearzero= False;++ zzinc_(Zsetplane);+ if (!facet->normal)+ qh_memalloc_(qh, normsize, freelistp, facet->normal, coordT);+ if (facet == qh->tracefacet) {+ oldtrace= qh->IStracing;+ qh->IStracing= 5;+ qh_fprintf(qh, qh->ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);+ qh_fprintf(qh, qh->ferr, 8013, " Last point added to hull was p%d.", qh->furthest_id);+ if (zzval_(Ztotmerge))+ qh_fprintf(qh, qh->ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge));+ qh_fprintf(qh, qh->ferr, 8015, "\n\nCurrent summary is:\n");+ qh_printsummary(qh, qh->ferr);+ }+ if (qh->hull_dim <= 4) {+ i= 0;+ if (qh->RANDOMdist) {+ gmcoord= qh->gm_matrix;+ FOREACHvertex_(facet->vertices) {+ qh->gm_row[i++]= gmcoord;+ coord= vertex->point;+ for (k=qh->hull_dim; k--; )+ *(gmcoord++)= *coord++ * qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);+ }+ }else {+ FOREACHvertex_(facet->vertices)+ qh->gm_row[i++]= vertex->point;+ }+ qh_sethyperplane_det(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,+ facet->normal, &facet->offset, &nearzero);+ }+ if (qh->hull_dim > 4 || nearzero) {+ i= 0;+ gmcoord= qh->gm_matrix;+ FOREACHvertex_(facet->vertices) {+ if (vertex->point != point0) {+ qh->gm_row[i++]= gmcoord;+ coord= vertex->point;+ point= point0;+ for (k=qh->hull_dim; k--; )+ *(gmcoord++)= *coord++ - *point++;+ }+ }+ qh->gm_row[i]= gmcoord; /* for areasimplex */+ if (qh->RANDOMdist) {+ gmcoord= qh->gm_matrix;+ for (i=qh->hull_dim-1; i--; ) {+ for (k=qh->hull_dim; k--; )+ *(gmcoord++) *= qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);+ }+ }+ qh_sethyperplane_gauss(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,+ facet->normal, &facet->offset, &nearzero);+ if (nearzero) {+ if (qh_orientoutside(qh, facet)) {+ trace0((qh, qh->ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh->furthest_id));+ /* this is part of using Gaussian Elimination. For example in 5-d+ 1 1 1 1 0+ 1 1 1 1 1+ 0 0 0 1 0+ 0 1 0 0 0+ 1 0 0 0 0+ norm= 0.38 0.38 -0.76 0.38 0+ has a determinate of 1, but g.e. after subtracting pt. 0 has+ 0's in the diagonal, even with full pivoting. It does work+ if you subtract pt. 4 instead. */+ }+ }+ }+ facet->upperdelaunay= False;+ if (qh->DELAUNAY) {+ if (qh->UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */+ if (facet->normal[qh->hull_dim -1] >= qh->ANGLEround * qh_ZEROdelaunay)+ facet->upperdelaunay= True;+ }else {+ if (facet->normal[qh->hull_dim -1] > -qh->ANGLEround * qh_ZEROdelaunay)+ facet->upperdelaunay= True;+ }+ }+ if (qh->PRINTstatistics || qh->IStracing || qh->TRACElevel || qh->JOGGLEmax < REALmax) {+ qh->old_randomdist= qh->RANDOMdist;+ qh->RANDOMdist= False;+ FOREACHvertex_(facet->vertices) {+ if (vertex->point != point0) {+ boolT istrace= False;+ zinc_(Zdiststat);+ qh_distplane(qh, vertex->point, facet, &dist);+ dist= fabs_(dist);+ zinc_(Znewvertex);+ wadd_(Wnewvertex, dist);+ if (dist > wwval_(Wnewvertexmax)) {+ wwval_(Wnewvertexmax)= dist;+ if (dist > qh->max_outside) {+ qh->max_outside= dist; /* used by qh_maxouter(qh) */+ if (dist > qh->TRACEdist)+ istrace= True;+ }+ }else if (-dist > qh->TRACEdist)+ istrace= True;+ if (istrace) {+ qh_fprintf(qh, qh->ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n",+ qh_pointid(qh, vertex->point), vertex->id, dist, facet->id, qh->furthest_id);+ qh_errprint(qh, "DISTANT", facet, NULL, NULL, NULL);+ }+ }+ }+ qh->RANDOMdist= qh->old_randomdist;+ }+ if (qh->IStracing >= 3) {+ qh_fprintf(qh, qh->ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ",+ facet->id, facet->offset);+ for (k=0; k < qh->hull_dim; k++)+ qh_fprintf(qh, qh->ferr, 8018, "%2.2g ", facet->normal[k]);+ qh_fprintf(qh, qh->ferr, 8019, "\n");+ }+ if (facet == qh->tracefacet)+ qh->IStracing= oldtrace;+} /* setfacetplane */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="sethyperplane_det">-</a>++ qh_sethyperplane_det(qh, dim, rows, point0, toporient, normal, offset, nearzero )+ given dim X dim array indexed by rows[], one row per point,+ toporient(flips all signs),+ and point0 (any row)+ set normalized hyperplane equation from oriented simplex++ returns:+ normal (normalized)+ offset (places point0 on the hyperplane)+ sets nearzero if hyperplane not through points++ notes:+ only defined for dim == 2..4+ rows[] is not modified+ solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane+ see Bower & Woodworth, A programmer's geometry, Butterworths 1983.++ derivation of 3-d minnorm+ Goal: all vertices V_i within qh.one_merge of hyperplane+ Plan: exactly translate the facet so that V_0 is the origin+ exactly rotate the facet so that V_1 is on the x-axis and y_2=0.+ exactly rotate the effective perturbation to only effect n_0+ this introduces a factor of sqrt(3)+ n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm+ Let M_d be the max coordinate difference+ Let M_a be the greater of M_d and the max abs. coordinate+ Let u be machine roundoff and distround be max error for distance computation+ The max error for n_0 is sqrt(3) u M_a M_d / norm. n_1 is approx. 1 and n_2 is approx. 0+ The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm. Offset=0 at origin+ Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge+ Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d++ derivation of 4-d minnorm+ same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0+ [if two vertices fixed on x-axis, can rotate the other two in yzw.]+ n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3+ [all other terms contain at least two factors nearly zero.]+ The max error for n_0 is sqrt(4) u M_a M_d M_d / norm+ Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge+ Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d+*/+void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,+ boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {+ realT maxround, dist;+ int i;+ pointT *point;+++ if (dim == 2) {+ normal[0]= dY(1,0);+ normal[1]= dX(0,1);+ qh_normalize2(qh, normal, dim, toporient, NULL, NULL);+ *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);+ *nearzero= False; /* since nearzero norm => incident points */+ }else if (dim == 3) {+ normal[0]= det2_(dY(2,0), dZ(2,0),+ dY(1,0), dZ(1,0));+ normal[1]= det2_(dX(1,0), dZ(1,0),+ dX(2,0), dZ(2,0));+ normal[2]= det2_(dX(2,0), dY(2,0),+ dX(1,0), dY(1,0));+ qh_normalize2(qh, normal, dim, toporient, NULL, NULL);+ *offset= -(point0[0]*normal[0] + point0[1]*normal[1]+ + point0[2]*normal[2]);+ maxround= qh->DISTround;+ for (i=dim; i--; ) {+ point= rows[i];+ if (point != point0) {+ dist= *offset + (point[0]*normal[0] + point[1]*normal[1]+ + point[2]*normal[2]);+ if (dist > maxround || dist < -maxround) {+ *nearzero= True;+ break;+ }+ }+ }+ }else if (dim == 4) {+ normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),+ dY(1,0), dZ(1,0), dW(1,0),+ dY(3,0), dZ(3,0), dW(3,0));+ normal[1]= det3_(dX(2,0), dZ(2,0), dW(2,0),+ dX(1,0), dZ(1,0), dW(1,0),+ dX(3,0), dZ(3,0), dW(3,0));+ normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),+ dX(1,0), dY(1,0), dW(1,0),+ dX(3,0), dY(3,0), dW(3,0));+ normal[3]= det3_(dX(2,0), dY(2,0), dZ(2,0),+ dX(1,0), dY(1,0), dZ(1,0),+ dX(3,0), dY(3,0), dZ(3,0));+ qh_normalize2(qh, normal, dim, toporient, NULL, NULL);+ *offset= -(point0[0]*normal[0] + point0[1]*normal[1]+ + point0[2]*normal[2] + point0[3]*normal[3]);+ maxround= qh->DISTround;+ for (i=dim; i--; ) {+ point= rows[i];+ if (point != point0) {+ dist= *offset + (point[0]*normal[0] + point[1]*normal[1]+ + point[2]*normal[2] + point[3]*normal[3]);+ if (dist > maxround || dist < -maxround) {+ *nearzero= True;+ break;+ }+ }+ }+ }+ if (*nearzero) {+ zzinc_(Zminnorm);+ trace0((qh, qh->ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh->furthest_id));+ zzinc_(Znearlysingular);+ }+} /* sethyperplane_det */+++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="sethyperplane_gauss">-</a>++ qh_sethyperplane_gauss(qh, dim, rows, point0, toporient, normal, offset, nearzero )+ given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)+ set normalized hyperplane equation from oriented simplex++ returns:+ normal (normalized)+ offset (places point0 on the hyperplane)++ notes:+ if nearzero+ orientation may be incorrect because of incorrect sign flips in gausselim+ solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]+ or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]+ i.e., N is normal to the hyperplane, and the unnormalized+ distance to [0 .. 1] is either 1 or 0++ design:+ perform gaussian elimination+ flip sign for negative values+ perform back substitution+ normalize result+ compute offset+*/+void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,+ boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {+ coordT *pointcoord, *normalcoef;+ int k;+ boolT sign= toporient, nearzero2= False;++ qh_gausselim(qh, rows, dim-1, dim, &sign, nearzero);+ for (k=dim-1; k--; ) {+ if ((rows[k])[k] < 0)+ sign ^= 1;+ }+ if (*nearzero) {+ zzinc_(Znearlysingular);+ trace0((qh, qh->ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh->furthest_id));+ qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);+ }else {+ qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);+ if (nearzero2) {+ zzinc_(Znearlysingular);+ trace0((qh, qh->ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh->furthest_id));+ }+ }+ if (nearzero2)+ *nearzero= True;+ qh_normalize2(qh, normal, dim, True, NULL, NULL);+ pointcoord= point0;+ normalcoef= normal;+ *offset= -(*pointcoord++ * *normalcoef++);+ for (k=dim-1; k--; )+ *offset -= *pointcoord++ * *normalcoef++;+} /* sethyperplane_gauss */+++
+ C/global_r.c view
@@ -0,0 +1,2100 @@++/*<html><pre> -<a href="qh-globa_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ global_r.c+ initializes all the globals of the qhull application++ see README++ see libqhull_r.h for qh.globals and function prototypes++ see qhull_ra.h for internal functions++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/global_r.c#16 $$Change: 2066 $+ $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $+ */++#include "qhull_ra.h"++/*========= qh->definition -- globals defined in libqhull_r.h =======================*/++/*-<a href ="qh-globa_r.htm#TOC"+ >--------------------------------</a><a name="qh_version">-</a>++ qh_version+ version string by year and date+ qh_version2 for Unix users and -V++ the revision increases on code changes only++ notes:+ change date: Changes.txt, Announce.txt, index.htm, README.txt,+ qhull-news.html, Eudora signatures, CMakeLists.txt+ change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt, CMakeLists.txt+ check that CmakeLists @version is the same as qh_version2+ change year: Copying.txt+ check download size+ recompile user_eg_r.c, rbox_r.c, libqhull_r.c, qconvex_r.c, qdelaun_r.c qvoronoi_r.c, qhalf_r.c, testqset_r.c+*/++const char qh_version[]= "2015.2.r 2016/01/18";+const char qh_version2[]= "qhull_r 7.2.0 (2015.2.r 2016/01/18)";++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="appendprint">-</a>++ qh_appendprint(qh, printFormat )+ append printFormat to qh.PRINTout unless already defined+*/+void qh_appendprint(qhT *qh, qh_PRINT format) {+ int i;++ for (i=0; i < qh_PRINTEND; i++) {+ if (qh->PRINTout[i] == format && format != qh_PRINTqhull)+ break;+ if (!qh->PRINTout[i]) {+ qh->PRINTout[i]= format;+ break;+ }+ }+} /* appendprint */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="checkflags">-</a>++ qh_checkflags(qh, commandStr, hiddenFlags )+ errors if commandStr contains hiddenFlags+ hiddenFlags starts and ends with a space and is space delimited (checked)++ notes:+ ignores first word (e.g., "qconvex i")+ use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces++ see:+ qh_initflags() initializes Qhull according to commandStr+*/+void qh_checkflags(qhT *qh, char *command, char *hiddenflags) {+ char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */+ char key, opt, prevopt;+ char chkkey[]= " ";+ char chkopt[]= " ";+ char chkopt2[]= " ";+ boolT waserr= False;++ if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {+ qh_fprintf(qh, qh->ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (strpbrk(hiddenflags, ",\n\r\t")) {+ qh_fprintf(qh, qh->ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ while (*s && !isspace(*s)) /* skip program name */+ s++;+ while (*s) {+ while (*s && isspace(*s))+ s++;+ if (*s == '-')+ s++;+ if (!*s)+ break;+ key = *s++;+ chkerr = NULL;+ if (key == 'T' && (*s == 'I' || *s == 'O')) { /* TI or TO 'file name' */+ s= qh_skipfilename(qh, ++s);+ continue;+ }+ chkkey[1]= key;+ if (strstr(hiddenflags, chkkey)) {+ chkerr= chkkey;+ }else if (isupper(key)) {+ opt= ' ';+ prevopt= ' ';+ chkopt[1]= key;+ chkopt2[1]= key;+ while (!chkerr && *s && !isspace(*s)) {+ opt= *s++;+ if (isalpha(opt)) {+ chkopt[2]= opt;+ if (strstr(hiddenflags, chkopt))+ chkerr= chkopt;+ if (prevopt != ' ') {+ chkopt2[2]= prevopt;+ chkopt2[3]= opt;+ if (strstr(hiddenflags, chkopt2))+ chkerr= chkopt2;+ }+ }else if (key == 'Q' && isdigit(opt) && prevopt != 'b'+ && (prevopt == ' ' || islower(prevopt))) {+ chkopt[2]= opt;+ if (strstr(hiddenflags, chkopt))+ chkerr= chkopt;+ }else {+ qh_strtod(s-1, &t);+ if (s < t)+ s= t;+ }+ prevopt= opt;+ }+ }+ if (chkerr) {+ *chkerr= '\'';+ chkerr[strlen(chkerr)-1]= '\'';+ qh_fprintf(qh, qh->ferr, 6029, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr);+ waserr= True;+ }+ }+ if (waserr)+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+} /* checkflags */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="qh_clear_outputflags">-</a>++ qh_clear_outputflags(qh)+ Clear output flags for QhullPoints+*/+void qh_clear_outputflags(qhT *qh) {+ int i,k;++ qh->ANNOTATEoutput= False;+ qh->DOintersections= False;+ qh->DROPdim= -1;+ qh->FORCEoutput= False;+ qh->GETarea= False;+ qh->GOODpoint= 0;+ qh->GOODpointp= NULL;+ qh->GOODthreshold= False;+ qh->GOODvertex= 0;+ qh->GOODvertexp= NULL;+ qh->IStracing= 0;+ qh->KEEParea= False;+ qh->KEEPmerge= False;+ qh->KEEPminArea= REALmax;+ qh->PRINTcentrums= False;+ qh->PRINTcoplanar= False;+ qh->PRINTdots= False;+ qh->PRINTgood= False;+ qh->PRINTinner= False;+ qh->PRINTneighbors= False;+ qh->PRINTnoplanes= False;+ qh->PRINToptions1st= False;+ qh->PRINTouter= False;+ qh->PRINTprecision= True;+ qh->PRINTridges= False;+ qh->PRINTspheres= False;+ qh->PRINTstatistics= False;+ qh->PRINTsummary= False;+ qh->PRINTtransparent= False;+ qh->SPLITthresholds= False;+ qh->TRACElevel= 0;+ qh->TRInormals= False;+ qh->USEstdout= False;+ qh->VERIFYoutput= False;+ for (k=qh->input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */+ qh->lower_threshold[k]= -REALmax;+ qh->upper_threshold[k]= REALmax;+ qh->lower_bound[k]= -REALmax;+ qh->upper_bound[k]= REALmax;+ }++ for (i=0; i < qh_PRINTEND; i++) {+ qh->PRINTout[i]= qh_PRINTnone;+ }++ if (!qh->qhull_commandsiz2)+ qh->qhull_commandsiz2= (int)strlen(qh->qhull_command); /* WARN64 */+ else {+ qh->qhull_command[qh->qhull_commandsiz2]= '\0';+ }+ if (!qh->qhull_optionsiz2)+ qh->qhull_optionsiz2= (int)strlen(qh->qhull_options); /* WARN64 */+ else {+ qh->qhull_options[qh->qhull_optionsiz2]= '\0';+ qh->qhull_optionlen= qh_OPTIONline; /* start a new line */+ }+} /* clear_outputflags */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="clock">-</a>++ qh_clock()+ return user CPU time in 100ths (qh_SECtick)+ only defined for qh_CLOCKtype == 2++ notes:+ use first value to determine time 0+ from Stevens '92 8.15+*/+unsigned long qh_clock(qhT *qh) {++#if (qh_CLOCKtype == 2)+ struct tms time;+ static long clktck; /* initialized first call and never updated */+ double ratio, cpu;+ unsigned long ticks;++ if (!clktck) {+ if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {+ qh_fprintf(qh, qh->ferr, 6030, "qhull internal error (qh_clock): sysconf() failed. Use qh_CLOCKtype 1 in user.h\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ }+ if (times(&time) == -1) {+ qh_fprintf(qh, qh->ferr, 6031, "qhull internal error (qh_clock): times() failed. Use qh_CLOCKtype 1 in user.h\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ ratio= qh_SECticks / (double)clktck;+ ticks= time.tms_utime * ratio;+ return ticks;+#else+ qh_fprintf(qh, qh->ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* never returns */+ return 0;+#endif+} /* clock */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="freebuffers">-</a>++ qh_freebuffers()+ free up global memory buffers++ notes:+ must match qh_initbuffers()+*/+void qh_freebuffers(qhT *qh) {++ trace5((qh, qh->ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));+ /* allocated by qh_initqhull_buffers */+ qh_memfree(qh, qh->NEARzero, qh->hull_dim * sizeof(realT));+ qh_memfree(qh, qh->lower_threshold, (qh->input_dim+1) * sizeof(realT));+ qh_memfree(qh, qh->upper_threshold, (qh->input_dim+1) * sizeof(realT));+ qh_memfree(qh, qh->lower_bound, (qh->input_dim+1) * sizeof(realT));+ qh_memfree(qh, qh->upper_bound, (qh->input_dim+1) * sizeof(realT));+ qh_memfree(qh, qh->gm_matrix, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT));+ qh_memfree(qh, qh->gm_row, (qh->hull_dim+1) * sizeof(coordT *));+ qh->NEARzero= qh->lower_threshold= qh->upper_threshold= NULL;+ qh->lower_bound= qh->upper_bound= NULL;+ qh->gm_matrix= NULL;+ qh->gm_row= NULL;+ qh_setfree(qh, &qh->other_points);+ qh_setfree(qh, &qh->del_vertices);+ qh_setfree(qh, &qh->coplanarfacetset);+ if (qh->line) /* allocated by qh_readinput, freed if no error */+ qh_free(qh->line);+ if (qh->half_space)+ qh_free(qh->half_space);+ if (qh->temp_malloc)+ qh_free(qh->temp_malloc);+ if (qh->feasible_point) /* allocated by qh_readfeasible */+ qh_free(qh->feasible_point);+ if (qh->feasible_string) /* allocated by qh_initflags */+ qh_free(qh->feasible_string);+ qh->line= qh->feasible_string= NULL;+ qh->half_space= qh->feasible_point= qh->temp_malloc= NULL;+ /* usually allocated by qh_readinput */+ if (qh->first_point && qh->POINTSmalloc) {+ qh_free(qh->first_point);+ qh->first_point= NULL;+ }+ if (qh->input_points && qh->input_malloc) { /* set by qh_joggleinput */+ qh_free(qh->input_points);+ qh->input_points= NULL;+ }+ trace5((qh, qh->ferr, 5002, "qh_freebuffers: finished\n"));+} /* freebuffers */+++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="freebuild">-</a>++ qh_freebuild(qh, allmem )+ free global memory used by qh_initbuild and qh_buildhull+ if !allmem,+ does not free short memory (e.g., facetT, freed by qh_memfreeshort)++ design:+ free centrums+ free each vertex+ mark unattached ridges+ for each facet+ free ridges+ free outside set, coplanar set, neighbor set, ridge set, vertex set+ free facet+ free hash table+ free interior point+ free merge set+ free temporary sets+*/+void qh_freebuild(qhT *qh, boolT allmem) {+ facetT *facet;+ vertexT *vertex;+ ridgeT *ridge, **ridgep;+ mergeT *merge, **mergep;++ trace1((qh, qh->ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));+ if (qh->del_vertices)+ qh_settruncate(qh, qh->del_vertices, 0);+ if (allmem) {+ while ((vertex= qh->vertex_list)) {+ if (vertex->next)+ qh_delvertex(qh, vertex);+ else {+ qh_memfree(qh, vertex, (int)sizeof(vertexT));+ qh->newvertex_list= qh->vertex_list= NULL;+ }+ }+ }else if (qh->VERTEXneighbors) {+ FORALLvertices+ qh_setfreelong(qh, &(vertex->neighbors));+ }+ qh->VERTEXneighbors= False;+ qh->GOODclosest= NULL;+ if (allmem) {+ FORALLfacets {+ FOREACHridge_(facet->ridges)+ ridge->seen= False;+ }+ FORALLfacets {+ if (facet->visible) {+ FOREACHridge_(facet->ridges) {+ if (!otherfacet_(ridge, facet)->visible)+ ridge->seen= True; /* an unattached ridge */+ }+ }+ }+ while ((facet= qh->facet_list)) {+ FOREACHridge_(facet->ridges) {+ if (ridge->seen) {+ qh_setfree(qh, &(ridge->vertices));+ qh_memfree(qh, ridge, (int)sizeof(ridgeT));+ }else+ ridge->seen= True;+ }+ qh_setfree(qh, &(facet->outsideset));+ qh_setfree(qh, &(facet->coplanarset));+ qh_setfree(qh, &(facet->neighbors));+ qh_setfree(qh, &(facet->ridges));+ qh_setfree(qh, &(facet->vertices));+ if (facet->next)+ qh_delfacet(qh, facet);+ else {+ qh_memfree(qh, facet, (int)sizeof(facetT));+ qh->visible_list= qh->newfacet_list= qh->facet_list= NULL;+ }+ }+ }else {+ FORALLfacets {+ qh_setfreelong(qh, &(facet->outsideset));+ qh_setfreelong(qh, &(facet->coplanarset));+ if (!facet->simplicial) {+ qh_setfreelong(qh, &(facet->neighbors));+ qh_setfreelong(qh, &(facet->ridges));+ qh_setfreelong(qh, &(facet->vertices));+ }+ }+ }+ qh_setfree(qh, &(qh->hash_table));+ qh_memfree(qh, qh->interior_point, qh->normal_size);+ qh->interior_point= NULL;+ FOREACHmerge_(qh->facet_mergeset) /* usually empty */+ qh_memfree(qh, merge, (int)sizeof(mergeT));+ qh->facet_mergeset= NULL; /* temp set */+ qh->degen_mergeset= NULL; /* temp set */+ qh_settempfree_all(qh);+} /* freebuild */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="freeqhull">-</a>++ qh_freeqhull(qh, allmem )++ free global memory and set qhT to 0+ if !allmem,+ does not free short memory (freed by qh_memfreeshort unless qh_NOmem)++notes:+ sets qh.NOerrexit in case caller forgets to+ Does not throw errors++see:+ see qh_initqhull_start2()+ For libqhull_r, qhstatT is part of qhT++design:+ free global and temporary memory from qh_initbuild and qh_buildhull+ free buffers+*/+void qh_freeqhull(qhT *qh, boolT allmem) {++ qh->NOerrexit= True; /* no more setjmp since called at exit and ~QhullQh */+ trace1((qh, qh->ferr, 1006, "qh_freeqhull: free global memory\n"));+ qh_freebuild(qh, allmem);+ qh_freebuffers(qh);+ /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */+ memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT));+ qh->NOerrexit= True;+} /* freeqhull2 */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="init_A">-</a>++ qh_init_A(qh, infile, outfile, errfile, argc, argv )+ initialize memory and stdio files+ convert input options to option string (qh.qhull_command)++ notes:+ infile may be NULL if qh_readpoints() is not called++ errfile should always be defined. It is used for reporting+ errors. outfile is used for output and format options.++ argc/argv may be 0/NULL++ called before error handling initialized+ qh_errexit() may not be used+*/+void qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {+ qh_meminit(qh, errfile);+ qh_initqhull_start(qh, infile, outfile, errfile);+ qh_init_qhull_command(qh, argc, argv);+} /* init_A */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="init_B">-</a>++ qh_init_B(qh, points, numpoints, dim, ismalloc )+ initialize globals for points array++ points has numpoints dim-dimensional points+ points[0] is the first coordinate of the first point+ points[1] is the second coordinate of the first point+ points[dim] is the first coordinate of the second point++ ismalloc=True+ Qhull will call qh_free(points) on exit or input transformation+ ismalloc=False+ Qhull will allocate a new point array if needed for input transformation++ qh.qhull_command+ is the option string.+ It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags++ returns:+ if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)+ projects the input to a new point array++ if qh.DELAUNAY,+ qh.hull_dim is increased by one+ if qh.ATinfinity,+ qh_projectinput adds point-at-infinity for Delaunay tri.++ if qh.SCALEinput+ changes the upper and lower bounds of the input, see qh_scaleinput(qh)++ if qh.ROTATEinput+ rotates the input by a random rotation, see qh_rotateinput()+ if qh.DELAUNAY+ rotates about the last coordinate++ notes:+ called after points are defined+ qh_errexit() may be used+*/+void qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {+ qh_initqhull_globals(qh, points, numpoints, dim, ismalloc);+ if (qh->qhmem.LASTsize == 0)+ qh_initqhull_mem(qh);+ /* mem_r.c and qset_r.c are initialized */+ qh_initqhull_buffers(qh);+ qh_initthresholds(qh, qh->qhull_command);+ if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay))+ qh_projectinput(qh);+ if (qh->SCALEinput)+ qh_scaleinput(qh);+ if (qh->ROTATErandom >= 0) {+ qh_randommatrix(qh, qh->gm_matrix, qh->hull_dim, qh->gm_row);+ if (qh->DELAUNAY) {+ int k, lastk= qh->hull_dim-1;+ for (k=0; k < lastk; k++) {+ qh->gm_row[k][lastk]= 0.0;+ qh->gm_row[lastk][k]= 0.0;+ }+ qh->gm_row[lastk][lastk]= 1.0;+ }+ qh_gram_schmidt(qh, qh->hull_dim, qh->gm_row);+ qh_rotateinput(qh, qh->gm_row);+ }+} /* init_B */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="init_qhull_command">-</a>++ qh_init_qhull_command(qh, argc, argv )+ build qh.qhull_command from argc/argv+ Calls qh_exit if qhull_command is too short++ returns:+ a space-delimited string of options (just as typed)++ notes:+ makes option string easy to input and output++ argc/argv may be 0/NULL+*/+void qh_init_qhull_command(qhT *qh, int argc, char *argv[]) {++ if (!qh_argv_to_command(argc, argv, qh->qhull_command, (int)sizeof(qh->qhull_command))){+ /* Assumes qh.ferr is defined. */+ qh_fprintf(qh, qh->ferr, 6033, "qhull input error: more than %d characters in command line.\n",+ (int)sizeof(qh->qhull_command));+ qh_exit(qh_ERRinput); /* error reported, can not use qh_errexit */+ }+} /* init_qhull_command */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="initflags">-</a>++ qh_initflags(qh, commandStr )+ set flags and initialized constants from commandStr+ calls qh_exit() if qh->NOerrexit++ returns:+ sets qh.qhull_command to command if needed++ notes:+ ignores first word (e.g., "qhull d")+ use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces++ see:+ qh_initthresholds() continues processing of 'Pdn' and 'PDn'+ 'prompt' in unix_r.c for documentation++ design:+ for each space-delimited option group+ if top-level option+ check syntax+ append appropriate option to option string+ set appropriate global variable or append printFormat to print options+ else+ for each sub-option+ check syntax+ append appropriate option to option string+ set appropriate global variable or append printFormat to print options+*/+void qh_initflags(qhT *qh, char *command) {+ int k, i, lastproject;+ char *s= command, *t, *prev_s, *start, key;+ boolT isgeom= False, wasproject;+ realT r;++ if(qh->NOerrexit){+ qh_fprintf(qh, qh->ferr, 6245, "qhull initflags error: qh.NOerrexit was not cleared before calling qh_initflags(). It should be cleared after setjmp(). Exit qhull.");+ qh_exit(6245);+ }+ if (command <= &qh->qhull_command[0] || command > &qh->qhull_command[0] + sizeof(qh->qhull_command)) {+ if (command != &qh->qhull_command[0]) {+ *qh->qhull_command= '\0';+ strncat(qh->qhull_command, command, sizeof(qh->qhull_command)-strlen(qh->qhull_command)-1);+ }+ while (*s && !isspace(*s)) /* skip program name */+ s++;+ }+ while (*s) {+ while (*s && isspace(*s))+ s++;+ if (*s == '-')+ s++;+ if (!*s)+ break;+ prev_s= s;+ switch (*s++) {+ case 'd':+ qh_option(qh, "delaunay", NULL, NULL);+ qh->DELAUNAY= True;+ break;+ case 'f':+ qh_option(qh, "facets", NULL, NULL);+ qh_appendprint(qh, qh_PRINTfacets);+ break;+ case 'i':+ qh_option(qh, "incidence", NULL, NULL);+ qh_appendprint(qh, qh_PRINTincidences);+ break;+ case 'm':+ qh_option(qh, "mathematica", NULL, NULL);+ qh_appendprint(qh, qh_PRINTmathematica);+ break;+ case 'n':+ qh_option(qh, "normals", NULL, NULL);+ qh_appendprint(qh, qh_PRINTnormals);+ break;+ case 'o':+ qh_option(qh, "offFile", NULL, NULL);+ qh_appendprint(qh, qh_PRINToff);+ break;+ case 'p':+ qh_option(qh, "points", NULL, NULL);+ qh_appendprint(qh, qh_PRINTpoints);+ break;+ case 's':+ qh_option(qh, "summary", NULL, NULL);+ qh->PRINTsummary= True;+ break;+ case 'v':+ qh_option(qh, "voronoi", NULL, NULL);+ qh->VORONOI= True;+ qh->DELAUNAY= True;+ break;+ case 'A':+ if (!isdigit(*s) && *s != '.' && *s != '-')+ qh_fprintf(qh, qh->ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'. Ignored.\n");+ else {+ if (*s == '-') {+ qh->premerge_cos= -qh_strtod(s, &s);+ qh_option(qh, "Angle-premerge-", NULL, &qh->premerge_cos);+ qh->PREmerge= True;+ }else {+ qh->postmerge_cos= qh_strtod(s, &s);+ qh_option(qh, "Angle-postmerge", NULL, &qh->postmerge_cos);+ qh->POSTmerge= True;+ }+ qh->MERGING= True;+ }+ break;+ case 'C':+ if (!isdigit(*s) && *s != '.' && *s != '-')+ qh_fprintf(qh, qh->ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'. Ignored.\n");+ else {+ if (*s == '-') {+ qh->premerge_centrum= -qh_strtod(s, &s);+ qh_option(qh, "Centrum-premerge-", NULL, &qh->premerge_centrum);+ qh->PREmerge= True;+ }else {+ qh->postmerge_centrum= qh_strtod(s, &s);+ qh_option(qh, "Centrum-postmerge", NULL, &qh->postmerge_centrum);+ qh->POSTmerge= True;+ }+ qh->MERGING= True;+ }+ break;+ case 'E':+ if (*s == '-')+ qh_fprintf(qh, qh->ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n");+ else if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'. Ignored.\n");+ else {+ qh->DISTround= qh_strtod(s, &s);+ qh_option(qh, "Distance-roundoff", NULL, &qh->DISTround);+ qh->SETroundoff= True;+ }+ break;+ case 'H':+ start= s;+ qh->HALFspace= True;+ qh_strtod(s, &t);+ while (t > s) {+ if (*t && !isspace(*t)) {+ if (*t == ',')+ t++;+ else+ qh_fprintf(qh, qh->ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n");+ }+ s= t;+ qh_strtod(s, &t);+ }+ if (start < t) {+ if (!(qh->feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) {+ qh_fprintf(qh, qh->ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");+ qh_errexit(qh, qh_ERRmem, NULL, NULL);+ }+ strncpy(qh->feasible_string, start, (size_t)(t-start));+ qh_option(qh, "Halfspace-about", NULL, NULL);+ qh_option(qh, qh->feasible_string, NULL, NULL);+ }else+ qh_option(qh, "Halfspace", NULL, NULL);+ break;+ case 'R':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'. Ignored\n");+ else {+ qh->RANDOMfactor= qh_strtod(s, &s);+ qh_option(qh, "Random_perturb", NULL, &qh->RANDOMfactor);+ qh->RANDOMdist= True;+ }+ break;+ case 'V':+ if (!isdigit(*s) && *s != '-')+ qh_fprintf(qh, qh->ferr, 7008, "qhull warning: missing visible distance for option 'Vn'. Ignored\n");+ else {+ qh->MINvisible= qh_strtod(s, &s);+ qh_option(qh, "Visible", NULL, &qh->MINvisible);+ }+ break;+ case 'U':+ if (!isdigit(*s) && *s != '-')+ qh_fprintf(qh, qh->ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'. Ignored\n");+ else {+ qh->MAXcoplanar= qh_strtod(s, &s);+ qh_option(qh, "U-coplanar", NULL, &qh->MAXcoplanar);+ }+ break;+ case 'W':+ if (*s == '-')+ qh_fprintf(qh, qh->ferr, 7010, "qhull warning: negative outside width for option 'Wn'. Ignored.\n");+ else if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7011, "qhull warning: missing outside width for option 'Wn'. Ignored\n");+ else {+ qh->MINoutside= qh_strtod(s, &s);+ qh_option(qh, "W-outside", NULL, &qh->MINoutside);+ qh->APPROXhull= True;+ }+ break;+ /************ sub menus ***************/+ case 'F':+ while (*s && !isspace(*s)) {+ switch (*s++) {+ case 'a':+ qh_option(qh, "Farea", NULL, NULL);+ qh_appendprint(qh, qh_PRINTarea);+ qh->GETarea= True;+ break;+ case 'A':+ qh_option(qh, "FArea-total", NULL, NULL);+ qh->GETarea= True;+ break;+ case 'c':+ qh_option(qh, "Fcoplanars", NULL, NULL);+ qh_appendprint(qh, qh_PRINTcoplanars);+ break;+ case 'C':+ qh_option(qh, "FCentrums", NULL, NULL);+ qh_appendprint(qh, qh_PRINTcentrums);+ break;+ case 'd':+ qh_option(qh, "Fd-cdd-in", NULL, NULL);+ qh->CDDinput= True;+ break;+ case 'D':+ qh_option(qh, "FD-cdd-out", NULL, NULL);+ qh->CDDoutput= True;+ break;+ case 'F':+ qh_option(qh, "FFacets-xridge", NULL, NULL);+ qh_appendprint(qh, qh_PRINTfacets_xridge);+ break;+ case 'i':+ qh_option(qh, "Finner", NULL, NULL);+ qh_appendprint(qh, qh_PRINTinner);+ break;+ case 'I':+ qh_option(qh, "FIDs", NULL, NULL);+ qh_appendprint(qh, qh_PRINTids);+ break;+ case 'm':+ qh_option(qh, "Fmerges", NULL, NULL);+ qh_appendprint(qh, qh_PRINTmerges);+ break;+ case 'M':+ qh_option(qh, "FMaple", NULL, NULL);+ qh_appendprint(qh, qh_PRINTmaple);+ break;+ case 'n':+ qh_option(qh, "Fneighbors", NULL, NULL);+ qh_appendprint(qh, qh_PRINTneighbors);+ break;+ case 'N':+ qh_option(qh, "FNeighbors-vertex", NULL, NULL);+ qh_appendprint(qh, qh_PRINTvneighbors);+ break;+ case 'o':+ qh_option(qh, "Fouter", NULL, NULL);+ qh_appendprint(qh, qh_PRINTouter);+ break;+ case 'O':+ if (qh->PRINToptions1st) {+ qh_option(qh, "FOptions", NULL, NULL);+ qh_appendprint(qh, qh_PRINToptions);+ }else+ qh->PRINToptions1st= True;+ break;+ case 'p':+ qh_option(qh, "Fpoint-intersect", NULL, NULL);+ qh_appendprint(qh, qh_PRINTpointintersect);+ break;+ case 'P':+ qh_option(qh, "FPoint-nearest", NULL, NULL);+ qh_appendprint(qh, qh_PRINTpointnearest);+ break;+ case 'Q':+ qh_option(qh, "FQhull", NULL, NULL);+ qh_appendprint(qh, qh_PRINTqhull);+ break;+ case 's':+ qh_option(qh, "Fsummary", NULL, NULL);+ qh_appendprint(qh, qh_PRINTsummary);+ break;+ case 'S':+ qh_option(qh, "FSize", NULL, NULL);+ qh_appendprint(qh, qh_PRINTsize);+ qh->GETarea= True;+ break;+ case 't':+ qh_option(qh, "Ftriangles", NULL, NULL);+ qh_appendprint(qh, qh_PRINTtriangles);+ break;+ case 'v':+ /* option set in qh_initqhull_globals */+ qh_appendprint(qh, qh_PRINTvertices);+ break;+ case 'V':+ qh_option(qh, "FVertex-average", NULL, NULL);+ qh_appendprint(qh, qh_PRINTaverage);+ break;+ case 'x':+ qh_option(qh, "Fxtremes", NULL, NULL);+ qh_appendprint(qh, qh_PRINTextremes);+ break;+ default:+ s--;+ qh_fprintf(qh, qh->ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]);+ while (*++s && !isspace(*s));+ break;+ }+ }+ break;+ case 'G':+ isgeom= True;+ qh_appendprint(qh, qh_PRINTgeom);+ while (*s && !isspace(*s)) {+ switch (*s++) {+ case 'a':+ qh_option(qh, "Gall-points", NULL, NULL);+ qh->PRINTdots= True;+ break;+ case 'c':+ qh_option(qh, "Gcentrums", NULL, NULL);+ qh->PRINTcentrums= True;+ break;+ case 'h':+ qh_option(qh, "Gintersections", NULL, NULL);+ qh->DOintersections= True;+ break;+ case 'i':+ qh_option(qh, "Ginner", NULL, NULL);+ qh->PRINTinner= True;+ break;+ case 'n':+ qh_option(qh, "Gno-planes", NULL, NULL);+ qh->PRINTnoplanes= True;+ break;+ case 'o':+ qh_option(qh, "Gouter", NULL, NULL);+ qh->PRINTouter= True;+ break;+ case 'p':+ qh_option(qh, "Gpoints", NULL, NULL);+ qh->PRINTcoplanar= True;+ break;+ case 'r':+ qh_option(qh, "Gridges", NULL, NULL);+ qh->PRINTridges= True;+ break;+ case 't':+ qh_option(qh, "Gtransparent", NULL, NULL);+ qh->PRINTtransparent= True;+ break;+ case 'v':+ qh_option(qh, "Gvertices", NULL, NULL);+ qh->PRINTspheres= True;+ break;+ case 'D':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n");+ else {+ if (qh->DROPdim >= 0)+ qh_fprintf(qh, qh->ferr, 7013, "qhull warning: can only drop one dimension. Previous 'GD%d' ignored\n",+ qh->DROPdim);+ qh->DROPdim= qh_strtol(s, &s);+ qh_option(qh, "GDrop-dim", &qh->DROPdim, NULL);+ }+ break;+ default:+ s--;+ qh_fprintf(qh, qh->ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]);+ while (*++s && !isspace(*s));+ break;+ }+ }+ break;+ case 'P':+ while (*s && !isspace(*s)) {+ switch (*s++) {+ case 'd': case 'D': /* see qh_initthresholds() */+ key= s[-1];+ i= qh_strtol(s, &s);+ r= 0;+ if (*s == ':') {+ s++;+ r= qh_strtod(s, &s);+ }+ if (key == 'd')+ qh_option(qh, "Pdrop-facets-dim-less", &i, &r);+ else+ qh_option(qh, "PDrop-facets-dim-more", &i, &r);+ break;+ case 'g':+ qh_option(qh, "Pgood-facets", NULL, NULL);+ qh->PRINTgood= True;+ break;+ case 'G':+ qh_option(qh, "PGood-facet-neighbors", NULL, NULL);+ qh->PRINTneighbors= True;+ break;+ case 'o':+ qh_option(qh, "Poutput-forced", NULL, NULL);+ qh->FORCEoutput= True;+ break;+ case 'p':+ qh_option(qh, "Pprecision-ignore", NULL, NULL);+ qh->PRINTprecision= False;+ break;+ case 'A':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n");+ else {+ qh->KEEParea= qh_strtol(s, &s);+ qh_option(qh, "PArea-keep", &qh->KEEParea, NULL);+ qh->GETarea= True;+ }+ break;+ case 'F':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n");+ else {+ qh->KEEPminArea= qh_strtod(s, &s);+ qh_option(qh, "PFacet-area-keep", NULL, &qh->KEEPminArea);+ qh->GETarea= True;+ }+ break;+ case 'M':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n");+ else {+ qh->KEEPmerge= qh_strtol(s, &s);+ qh_option(qh, "PMerge-keep", &qh->KEEPmerge, NULL);+ }+ break;+ default:+ s--;+ qh_fprintf(qh, qh->ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]);+ while (*++s && !isspace(*s));+ break;+ }+ }+ break;+ case 'Q':+ lastproject= -1;+ while (*s && !isspace(*s)) {+ switch (*s++) {+ case 'b': case 'B': /* handled by qh_initthresholds */+ key= s[-1];+ if (key == 'b' && *s == 'B') {+ s++;+ r= qh_DEFAULTbox;+ qh->SCALEinput= True;+ qh_option(qh, "QbBound-unit-box", NULL, &r);+ break;+ }+ if (key == 'b' && *s == 'b') {+ s++;+ qh->SCALElast= True;+ qh_option(qh, "Qbbound-last", NULL, NULL);+ break;+ }+ k= qh_strtol(s, &s);+ r= 0.0;+ wasproject= False;+ if (*s == ':') {+ s++;+ if ((r= qh_strtod(s, &s)) == 0.0) {+ t= s; /* need true dimension for memory allocation */+ while (*t && !isspace(*t)) {+ if (toupper(*t++) == 'B'+ && k == qh_strtol(t, &t)+ && *t++ == ':'+ && qh_strtod(t, &t) == 0.0) {+ qh->PROJECTinput++;+ trace2((qh, qh->ferr, 2004, "qh_initflags: project dimension %d\n", k));+ qh_option(qh, "Qb-project-dim", &k, NULL);+ wasproject= True;+ lastproject= k;+ break;+ }+ }+ }+ }+ if (!wasproject) {+ if (lastproject == k && r == 0.0)+ lastproject= -1; /* doesn't catch all possible sequences */+ else if (key == 'b') {+ qh->SCALEinput= True;+ if (r == 0.0)+ r= -qh_DEFAULTbox;+ qh_option(qh, "Qbound-dim-low", &k, &r);+ }else {+ qh->SCALEinput= True;+ if (r == 0.0)+ r= qh_DEFAULTbox;+ qh_option(qh, "QBound-dim-high", &k, &r);+ }+ }+ break;+ case 'c':+ qh_option(qh, "Qcoplanar-keep", NULL, NULL);+ qh->KEEPcoplanar= True;+ break;+ case 'f':+ qh_option(qh, "Qfurthest-outside", NULL, NULL);+ qh->BESToutside= True;+ break;+ case 'g':+ qh_option(qh, "Qgood-facets-only", NULL, NULL);+ qh->ONLYgood= True;+ break;+ case 'i':+ qh_option(qh, "Qinterior-keep", NULL, NULL);+ qh->KEEPinside= True;+ break;+ case 'm':+ qh_option(qh, "Qmax-outside-only", NULL, NULL);+ qh->ONLYmax= True;+ break;+ case 'r':+ qh_option(qh, "Qrandom-outside", NULL, NULL);+ qh->RANDOMoutside= True;+ break;+ case 's':+ qh_option(qh, "Qsearch-initial-simplex", NULL, NULL);+ qh->ALLpoints= True;+ break;+ case 't':+ qh_option(qh, "Qtriangulate", NULL, NULL);+ qh->TRIangulate= True;+ break;+ case 'T':+ qh_option(qh, "QTestPoints", NULL, NULL);+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n");+ else {+ qh->TESTpoints= qh_strtol(s, &s);+ qh_option(qh, "QTestPoints", &qh->TESTpoints, NULL);+ }+ break;+ case 'u':+ qh_option(qh, "QupperDelaunay", NULL, NULL);+ qh->UPPERdelaunay= True;+ break;+ case 'v':+ qh_option(qh, "Qvertex-neighbors-convex", NULL, NULL);+ qh->TESTvneighbors= True;+ break;+ case 'x':+ qh_option(qh, "Qxact-merge", NULL, NULL);+ qh->MERGEexact= True;+ break;+ case 'z':+ qh_option(qh, "Qz-infinity-point", NULL, NULL);+ qh->ATinfinity= True;+ break;+ case '0':+ qh_option(qh, "Q0-no-premerge", NULL, NULL);+ qh->NOpremerge= True;+ break;+ case '1':+ if (!isdigit(*s)) {+ qh_option(qh, "Q1-no-angle-sort", NULL, NULL);+ qh->ANGLEmerge= False;+ break;+ }+ switch (*s++) {+ case '0':+ qh_option(qh, "Q10-no-narrow", NULL, NULL);+ qh->NOnarrow= True;+ break;+ case '1':+ qh_option(qh, "Q11-trinormals Qtriangulate", NULL, NULL);+ qh->TRInormals= True;+ qh->TRIangulate= True;+ break;+ case '2':+ qh_option(qh, "Q12-no-wide-dup", NULL, NULL);+ qh->NOwide= True;+ break;+ default:+ s--;+ qh_fprintf(qh, qh->ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]);+ while (*++s && !isspace(*s));+ break;+ }+ break;+ case '2':+ qh_option(qh, "Q2-no-merge-independent", NULL, NULL);+ qh->MERGEindependent= False;+ goto LABELcheckdigit;+ break; /* no warnings */+ case '3':+ qh_option(qh, "Q3-no-merge-vertices", NULL, NULL);+ qh->MERGEvertices= False;+ LABELcheckdigit:+ if (isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit. '%c' skipped.\n",+ *s++);+ break;+ case '4':+ qh_option(qh, "Q4-avoid-old-into-new", NULL, NULL);+ qh->AVOIDold= True;+ break;+ case '5':+ qh_option(qh, "Q5-no-check-outer", NULL, NULL);+ qh->SKIPcheckmax= True;+ break;+ case '6':+ qh_option(qh, "Q6-no-concave-merge", NULL, NULL);+ qh->SKIPconvex= True;+ break;+ case '7':+ qh_option(qh, "Q7-no-breadth-first", NULL, NULL);+ qh->VIRTUALmemory= True;+ break;+ case '8':+ qh_option(qh, "Q8-no-near-inside", NULL, NULL);+ qh->NOnearinside= True;+ break;+ case '9':+ qh_option(qh, "Q9-pick-furthest", NULL, NULL);+ qh->PICKfurthest= True;+ break;+ case 'G':+ i= qh_strtol(s, &t);+ if (qh->GOODpoint)+ qh_fprintf(qh, qh->ferr, 7018, "qhull warning: good point already defined for option 'QGn'. Ignored\n");+ else if (s == t)+ qh_fprintf(qh, qh->ferr, 7019, "qhull warning: missing good point id for option 'QGn'. Ignored\n");+ else if (i < 0 || *s == '-') {+ qh->GOODpoint= i-1;+ qh_option(qh, "QGood-if-dont-see-point", &i, NULL);+ }else {+ qh->GOODpoint= i+1;+ qh_option(qh, "QGood-if-see-point", &i, NULL);+ }+ s= t;+ break;+ case 'J':+ if (!isdigit(*s) && *s != '-')+ qh->JOGGLEmax= 0.0;+ else {+ qh->JOGGLEmax= (realT) qh_strtod(s, &s);+ qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);+ }+ break;+ case 'R':+ if (!isdigit(*s) && *s != '-')+ qh_fprintf(qh, qh->ferr, 7020, "qhull warning: missing random seed for option 'QRn'. Ignored\n");+ else {+ qh->ROTATErandom= i= qh_strtol(s, &s);+ if (i > 0)+ qh_option(qh, "QRotate-id", &i, NULL );+ else if (i < -1)+ qh_option(qh, "QRandom-seed", &i, NULL );+ }+ break;+ case 'V':+ i= qh_strtol(s, &t);+ if (qh->GOODvertex)+ qh_fprintf(qh, qh->ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n");+ else if (s == t)+ qh_fprintf(qh, qh->ferr, 7022, "qhull warning: no good point id given for option 'QVn'. Ignored\n");+ else if (i < 0) {+ qh->GOODvertex= i - 1;+ qh_option(qh, "QV-good-facets-not-point", &i, NULL);+ }else {+ qh_option(qh, "QV-good-facets-point", &i, NULL);+ qh->GOODvertex= i + 1;+ }+ s= t;+ break;+ default:+ s--;+ qh_fprintf(qh, qh->ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]);+ while (*++s && !isspace(*s));+ break;+ }+ }+ break;+ case 'T':+ while (*s && !isspace(*s)) {+ if (isdigit(*s) || *s == '-')+ qh->IStracing= qh_strtol(s, &s);+ else switch (*s++) {+ case 'a':+ qh_option(qh, "Tannotate-output", NULL, NULL);+ qh->ANNOTATEoutput= True;+ break;+ case 'c':+ qh_option(qh, "Tcheck-frequently", NULL, NULL);+ qh->CHECKfrequently= True;+ break;+ case 's':+ qh_option(qh, "Tstatistics", NULL, NULL);+ qh->PRINTstatistics= True;+ break;+ case 'v':+ qh_option(qh, "Tverify", NULL, NULL);+ qh->VERIFYoutput= True;+ break;+ case 'z':+ if (qh->ferr == qh_FILEstderr) {+ /* The C++ interface captures the output in qh_fprint_qhull() */+ qh_option(qh, "Tz-stdout", NULL, NULL);+ qh->USEstdout= True;+ }else if (!qh->fout)+ qh_fprintf(qh, qh->ferr, 7024, "qhull warning: output file undefined(stdout). Option 'Tz' ignored.\n");+ else {+ qh_option(qh, "Tz-stdout", NULL, NULL);+ qh->USEstdout= True;+ qh->ferr= qh->fout;+ qh->qhmem.ferr= qh->fout;+ }+ break;+ case 'C':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'. Ignored\n");+ else {+ i= qh_strtol(s, &s);+ qh_option(qh, "TCone-stop", &i, NULL);+ qh->STOPcone= i + 1;+ }+ break;+ case 'F':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'. Ignored\n");+ else {+ qh->REPORTfreq= qh_strtol(s, &s);+ qh_option(qh, "TFacet-log", &qh->REPORTfreq, NULL);+ qh->REPORTfreq2= qh->REPORTfreq/2; /* for tracemerging() */+ }+ break;+ case 'I':+ if (!isspace(*s))+ qh_fprintf(qh, qh->ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s);+ while (isspace(*s))+ s++;+ t= qh_skipfilename(qh, s);+ {+ char filename[qh_FILENAMElen];++ qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */+ s= t;+ if (!freopen(filename, "r", stdin)) {+ qh_fprintf(qh, qh->ferr, 6041, "qhull error: could not open file \"%s\".", filename);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }else {+ qh_option(qh, "TInput-file", NULL, NULL);+ qh_option(qh, filename, NULL, NULL);+ }+ }+ break;+ case 'O':+ if (!isspace(*s))+ qh_fprintf(qh, qh->ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s);+ while (isspace(*s))+ s++;+ t= qh_skipfilename(qh, s);+ {+ char filename[qh_FILENAMElen];++ qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */+ s= t;+ if (!qh->fout) {+ qh_fprintf(qh, qh->ferr, 6266, "qhull input warning: qh.fout was not set by caller. Cannot use option 'TO' to redirect output. Ignoring option 'TO'\n");+ }else if (!freopen(filename, "w", qh->fout)) {+ qh_fprintf(qh, qh->ferr, 6044, "qhull error: could not open file \"%s\".", filename);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }else {+ qh_option(qh, "TOutput-file", NULL, NULL);+ qh_option(qh, filename, NULL, NULL);+ }+ }+ break;+ case 'P':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7029, "qhull warning: missing point id for trace option 'TPn'. Ignored\n");+ else {+ qh->TRACEpoint= qh_strtol(s, &s);+ qh_option(qh, "Trace-point", &qh->TRACEpoint, NULL);+ }+ break;+ case 'M':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'. Ignored\n");+ else {+ qh->TRACEmerge= qh_strtol(s, &s);+ qh_option(qh, "Trace-merge", &qh->TRACEmerge, NULL);+ }+ break;+ case 'R':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'. Ignored\n");+ else {+ qh->RERUN= qh_strtol(s, &s);+ qh_option(qh, "TRerun", &qh->RERUN, NULL);+ }+ break;+ case 'V':+ i= qh_strtol(s, &t);+ if (s == t)+ qh_fprintf(qh, qh->ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'. Ignored\n");+ else if (i < 0) {+ qh->STOPpoint= i - 1;+ qh_option(qh, "TV-stop-before-point", &i, NULL);+ }else {+ qh->STOPpoint= i + 1;+ qh_option(qh, "TV-stop-after-point", &i, NULL);+ }+ s= t;+ break;+ case 'W':+ if (!isdigit(*s))+ qh_fprintf(qh, qh->ferr, 7033, "qhull warning: missing max width for trace option 'TWn'. Ignored\n");+ else {+ qh->TRACEdist= (realT) qh_strtod(s, &s);+ qh_option(qh, "TWide-trace", NULL, &qh->TRACEdist);+ }+ break;+ default:+ s--;+ qh_fprintf(qh, qh->ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]);+ while (*++s && !isspace(*s));+ break;+ }+ }+ break;+ default:+ qh_fprintf(qh, qh->ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1],+ (int)s[-1]);+ break;+ }+ if (s-1 == prev_s && *s && !isspace(*s)) {+ qh_fprintf(qh, qh->ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n",+ (int)*prev_s, (int)*prev_s);+ while (*s && !isspace(*s))+ s++;+ }+ }+ if (qh->STOPcone && qh->JOGGLEmax < REALmax/2)+ qh_fprintf(qh, qh->ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");+ if (isgeom && !qh->FORCEoutput && qh->PRINTout[1])+ qh_fprintf(qh, qh->ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n");+ /* set derived values in qh_initqhull_globals */+} /* initflags */+++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="initqhull_buffers">-</a>++ qh_initqhull_buffers(qh)+ initialize global memory buffers++ notes:+ must match qh_freebuffers()+*/+void qh_initqhull_buffers(qhT *qh) {+ int k;++ qh->TEMPsize= (qh->qhmem.LASTsize - sizeof(setT))/SETelemsize;+ if (qh->TEMPsize <= 0 || qh->TEMPsize > qh->qhmem.LASTsize)+ qh->TEMPsize= 8; /* e.g., if qh_NOmem */+ qh->other_points= qh_setnew(qh, qh->TEMPsize);+ qh->del_vertices= qh_setnew(qh, qh->TEMPsize);+ qh->coplanarfacetset= qh_setnew(qh, qh->TEMPsize);+ qh->NEARzero= (realT *)qh_memalloc(qh, qh->hull_dim * sizeof(realT));+ qh->lower_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));+ qh->upper_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));+ qh->lower_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));+ qh->upper_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));+ for (k=qh->input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */+ qh->lower_threshold[k]= -REALmax;+ qh->upper_threshold[k]= REALmax;+ qh->lower_bound[k]= -REALmax;+ qh->upper_bound[k]= REALmax;+ }+ qh->gm_matrix= (coordT *)qh_memalloc(qh, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT));+ qh->gm_row= (coordT **)qh_memalloc(qh, (qh->hull_dim+1) * sizeof(coordT *));+} /* initqhull_buffers */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="initqhull_globals">-</a>++ qh_initqhull_globals(qh, points, numpoints, dim, ismalloc )+ initialize globals+ if ismalloc+ points were malloc'd and qhull should free at end++ returns:+ sets qh.first_point, num_points, input_dim, hull_dim and others+ seeds random number generator (seed=1 if tracing)+ modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)+ adjust user flags as needed+ also checks DIM3 dependencies and constants++ notes:+ do not use qh_point() since an input transformation may move them elsewhere++ see:+ qh_initqhull_start() sets default values for non-zero globals++ design:+ initialize points array from input arguments+ test for qh.ZEROcentrum+ (i.e., use opposite vertex instead of cetrum for convexity testing)+ initialize qh.CENTERtype, qh.normal_size,+ qh.center_size, qh.TRACEpoint/level,+ initialize and test random numbers+ qh_initqhull_outputflags() -- adjust and test output flags+*/+void qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {+ int seed, pointsneeded, extra= 0, i, randi, k;+ realT randr;+ realT factorial;++ time_t timedata;++ trace0((qh, qh->ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh->rbox_command,+ qh->qhull_command));+ qh->POINTSmalloc= ismalloc;+ qh->first_point= points;+ qh->num_points= numpoints;+ qh->hull_dim= qh->input_dim= dim;+ if (!qh->NOpremerge && !qh->MERGEexact && !qh->PREmerge && qh->JOGGLEmax > REALmax/2) {+ qh->MERGING= True;+ if (qh->hull_dim <= 4) {+ qh->PREmerge= True;+ qh_option(qh, "_pre-merge", NULL, NULL);+ }else {+ qh->MERGEexact= True;+ qh_option(qh, "Qxact_merge", NULL, NULL);+ }+ }else if (qh->MERGEexact)+ qh->MERGING= True;+ if (!qh->NOpremerge && qh->JOGGLEmax > REALmax/2) {+#ifdef qh_NOmerge+ qh->JOGGLEmax= 0.0;+#endif+ }+ if (qh->TRIangulate && qh->JOGGLEmax < REALmax/2 && qh->PRINTprecision)+ qh_fprintf(qh, qh->ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output. Triangulated output('Qt') does nothing.\n");+ if (qh->JOGGLEmax < REALmax/2 && qh->DELAUNAY && !qh->SCALEinput && !qh->SCALElast) {+ qh->SCALElast= True;+ qh_option(qh, "Qbbound-last-qj", NULL, NULL);+ }+ if (qh->MERGING && !qh->POSTmerge && qh->premerge_cos > REALmax/2+ && qh->premerge_centrum == 0) {+ qh->ZEROcentrum= True;+ qh->ZEROall_ok= True;+ qh_option(qh, "_zero-centrum", NULL, NULL);+ }+ if (qh->JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh->PRINTprecision)+ qh_fprintf(qh, qh->ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n",+ REALepsilon);+#ifdef qh_NOmerge+ if (qh->MERGING) {+ qh_fprintf(qh, qh->ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+#endif+ if (qh->DELAUNAY && qh->KEEPcoplanar && !qh->KEEPinside) {+ qh->KEEPinside= True;+ qh_option(qh, "Qinterior-keep", NULL, NULL);+ }+ if (qh->DELAUNAY && qh->HALFspace) {+ qh_fprintf(qh, qh->ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (!qh->DELAUNAY && (qh->UPPERdelaunay || qh->ATinfinity)) {+ qh_fprintf(qh, qh->ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (qh->UPPERdelaunay && qh->ATinfinity) {+ qh_fprintf(qh, qh->ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (qh->SCALElast && !qh->DELAUNAY && qh->PRINTprecision)+ qh_fprintf(qh, qh->ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n");+ qh->DOcheckmax= (!qh->SKIPcheckmax && qh->MERGING );+ qh->KEEPnearinside= (qh->DOcheckmax && !(qh->KEEPinside && qh->KEEPcoplanar)+ && !qh->NOnearinside);+ if (qh->MERGING)+ qh->CENTERtype= qh_AScentrum;+ else if (qh->VORONOI)+ qh->CENTERtype= qh_ASvoronoi;+ if (qh->TESTvneighbors && !qh->MERGING) {+ qh_fprintf(qh, qh->ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n");+ qh_errexit(qh, qh_ERRinput, NULL ,NULL);+ }+ if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay)) {+ qh->hull_dim -= qh->PROJECTinput;+ if (qh->DELAUNAY) {+ qh->hull_dim++;+ if (qh->ATinfinity)+ extra= 1;+ }+ }+ if (qh->hull_dim <= 1) {+ qh_fprintf(qh, qh->ferr, 6050, "qhull error: dimension %d must be > 1\n", qh->hull_dim);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ for (k=2, factorial=1.0; k < qh->hull_dim; k++)+ factorial *= k;+ qh->AREAfactor= 1.0 / factorial;+ trace2((qh, qh->ferr, 2005, "qh_initqhull_globals: initialize globals. dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n",+ dim, numpoints, ismalloc, qh->PROJECTinput, qh->hull_dim));+ qh->normal_size= qh->hull_dim * sizeof(coordT);+ qh->center_size= qh->normal_size - sizeof(coordT);+ pointsneeded= qh->hull_dim+1;+ if (qh->hull_dim > qh_DIMmergeVertex) {+ qh->MERGEvertices= False;+ qh_option(qh, "Q3-no-merge-vertices-dim-high", NULL, NULL);+ }+ if (qh->GOODpoint)+ pointsneeded++;+#ifdef qh_NOtrace+ if (qh->IStracing) {+ qh_fprintf(qh, qh->ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+#endif+ if (qh->RERUN > 1) {+ qh->TRACElastrun= qh->IStracing; /* qh_build_withrestart duplicates next conditional */+ if (qh->IStracing != -1)+ qh->IStracing= 0;+ }else if (qh->TRACEpoint != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) {+ qh->TRACElevel= (qh->IStracing? qh->IStracing : 3);+ qh->IStracing= 0;+ }+ if (qh->ROTATErandom == 0 || qh->ROTATErandom == -1) {+ seed= (int)time(&timedata);+ if (qh->ROTATErandom == -1) {+ seed= -seed;+ qh_option(qh, "QRandom-seed", &seed, NULL );+ }else+ qh_option(qh, "QRotate-random", &seed, NULL);+ qh->ROTATErandom= seed;+ }+ seed= qh->ROTATErandom;+ if (seed == INT_MIN) /* default value */+ seed= 1;+ else if (seed < 0)+ seed= -seed;+ qh_RANDOMseed_(qh, seed);+ randr= 0.0;+ for (i=1000; i--; ) {+ randi= qh_RANDOMint;+ randr += randi;+ if (randi > qh_RANDOMmax) {+ qh_fprintf(qh, qh->ferr, 8036, "\+qhull configuration error (qh_RANDOMmax in user.h):\n\+ random integer %d > qh_RANDOMmax(qh, %.8g)\n",+ randi, qh_RANDOMmax);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ }+ qh_RANDOMseed_(qh, seed);+ randr = randr/1000;+ if (randr < qh_RANDOMmax * 0.1+ || randr > qh_RANDOMmax * 0.9)+ qh_fprintf(qh, qh->ferr, 8037, "\+qhull configuration warning (qh_RANDOMmax in user.h):\n\+ average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\+ Is qh_RANDOMmax (%.2g) wrong?\n",+ randr, qh_RANDOMmax * 0.5, qh_RANDOMmax);+ qh->RANDOMa= 2.0 * qh->RANDOMfactor/qh_RANDOMmax;+ qh->RANDOMb= 1.0 - qh->RANDOMfactor;+ if (qh_HASHfactor < 1.1) {+ qh_fprintf(qh, qh->ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1. Qhull uses linear hash probing\n",+ qh_HASHfactor);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ if (numpoints+extra < pointsneeded) {+ qh_fprintf(qh, qh->ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",+ numpoints, pointsneeded);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ qh_initqhull_outputflags(qh);+} /* initqhull_globals */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="initqhull_mem">-</a>++ qh_initqhull_mem(qh, )+ initialize mem_r.c for qhull+ qh.hull_dim and qh.normal_size determine some of the allocation sizes+ if qh.MERGING,+ includes ridgeT+ calls qh_user_memsizes(qh) to add up to 10 additional sizes for quick allocation+ (see numsizes below)++ returns:+ mem_r.c already for qh_memalloc/qh_memfree (errors if called beforehand)++ notes:+ qh_produceoutput() prints memsizes++*/+void qh_initqhull_mem(qhT *qh) {+ int numsizes;+ int i;++ numsizes= 8+10;+ qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, numsizes,+ qh_MEMbufsize, qh_MEMinitbuf);+ qh_memsize(qh, (int)sizeof(vertexT));+ if (qh->MERGING) {+ qh_memsize(qh, (int)sizeof(ridgeT));+ qh_memsize(qh, (int)sizeof(mergeT));+ }+ qh_memsize(qh, (int)sizeof(facetT));+ i= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize; /* ridge.vertices */+ qh_memsize(qh, i);+ qh_memsize(qh, qh->normal_size); /* normal */+ i += SETelemsize; /* facet.vertices, .ridges, .neighbors */+ qh_memsize(qh, i);+ qh_user_memsizes(qh);+ qh_memsetup(qh);+} /* initqhull_mem */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="initqhull_outputflags">-</a>++ qh_initqhull_outputflags+ initialize flags concerned with output++ returns:+ adjust user flags as needed++ see:+ qh_clear_outputflags() resets the flags++ design:+ test for qh.PRINTgood (i.e., only print 'good' facets)+ check for conflicting print output options+*/+void qh_initqhull_outputflags(qhT *qh) {+ boolT printgeom= False, printmath= False, printcoplanar= False;+ int i;++ trace3((qh, qh->ferr, 3024, "qh_initqhull_outputflags: %s\n", qh->qhull_command));+ if (!(qh->PRINTgood || qh->PRINTneighbors)) {+ if (qh->KEEParea || qh->KEEPminArea < REALmax/2 || qh->KEEPmerge || qh->DELAUNAY+ || (!qh->ONLYgood && (qh->GOODvertex || qh->GOODpoint))) {+ qh->PRINTgood= True;+ qh_option(qh, "Pgood", NULL, NULL);+ }+ }+ if (qh->PRINTtransparent) {+ if (qh->hull_dim != 4 || !qh->DELAUNAY || qh->VORONOI || qh->DROPdim >= 0) {+ qh_fprintf(qh, qh->ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ qh->DROPdim = 3;+ qh->PRINTridges = True;+ }+ for (i=qh_PRINTEND; i--; ) {+ if (qh->PRINTout[i] == qh_PRINTgeom)+ printgeom= True;+ else if (qh->PRINTout[i] == qh_PRINTmathematica || qh->PRINTout[i] == qh_PRINTmaple)+ printmath= True;+ else if (qh->PRINTout[i] == qh_PRINTcoplanars)+ printcoplanar= True;+ else if (qh->PRINTout[i] == qh_PRINTpointnearest)+ printcoplanar= True;+ else if (qh->PRINTout[i] == qh_PRINTpointintersect && !qh->HALFspace) {+ qh_fprintf(qh, qh->ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }else if (qh->PRINTout[i] == qh_PRINTtriangles && (qh->HALFspace || qh->VORONOI)) {+ qh_fprintf(qh, qh->ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }else if (qh->PRINTout[i] == qh_PRINTcentrums && qh->VORONOI) {+ qh_fprintf(qh, qh->ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }else if (qh->PRINTout[i] == qh_PRINTvertices) {+ if (qh->VORONOI)+ qh_option(qh, "Fvoronoi", NULL, NULL);+ else+ qh_option(qh, "Fvertices", NULL, NULL);+ }+ }+ if (printcoplanar && qh->DELAUNAY && qh->JOGGLEmax < REALmax/2) {+ if (qh->PRINTprecision)+ qh_fprintf(qh, qh->ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n");+ }+ if (printmath && (qh->hull_dim > 3 || qh->VORONOI)) {+ qh_fprintf(qh, qh->ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (printgeom) {+ if (qh->hull_dim > 4) {+ qh_fprintf(qh, qh->ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (qh->PRINTnoplanes && !(qh->PRINTcoplanar + qh->PRINTcentrums+ + qh->PRINTdots + qh->PRINTspheres + qh->DOintersections + qh->PRINTridges)) {+ qh_fprintf(qh, qh->ferr, 6058, "qhull input error: no output specified for Geomview\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (qh->VORONOI && (qh->hull_dim > 3 || qh->DROPdim >= 0)) {+ qh_fprintf(qh, qh->ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ /* can not warn about furthest-site Geomview output: no lower_threshold */+ if (qh->hull_dim == 4 && qh->DROPdim == -1 &&+ (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {+ qh_fprintf(qh, qh->ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\+available for 4-d output(ignored). Could use 'GDn' instead.\n");+ qh->PRINTcoplanar= qh->PRINTspheres= qh->PRINTcentrums= False;+ }+ }+ if (!qh->KEEPcoplanar && !qh->KEEPinside && !qh->ONLYgood) {+ if ((qh->PRINTcoplanar && qh->PRINTspheres) || printcoplanar) {+ if (qh->QHULLfinished) {+ qh_fprintf(qh, qh->ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n");+ }else {+ qh->KEEPcoplanar = True;+ qh_option(qh, "Qcoplanar", NULL, NULL);+ }+ }+ }+ qh->PRINTdim= qh->hull_dim;+ if (qh->DROPdim >=0) { /* after Geomview checks */+ if (qh->DROPdim < qh->hull_dim) {+ qh->PRINTdim--;+ if (!printgeom || qh->hull_dim < 3)+ qh_fprintf(qh, qh->ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh->DROPdim);+ }else+ qh->DROPdim= -1;+ }else if (qh->VORONOI) {+ qh->DROPdim= qh->hull_dim-1;+ qh->PRINTdim= qh->hull_dim-1;+ }+} /* qh_initqhull_outputflags */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="initqhull_start">-</a>++ qh_initqhull_start(qh, infile, outfile, errfile )+ allocate memory if needed and call qh_initqhull_start2()+*/+void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {++ qh_initstatistics(qh);+ qh_initqhull_start2(qh, infile, outfile, errfile);+} /* initqhull_start */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="initqhull_start2">-</a>++ qh_initqhull_start2(qh, infile, outfile, errfile )+ start initialization of qhull+ initialize statistics, stdio, default values for global variables+ assumes qh is allocated+ notes:+ report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized+ see:+ qh_maxmin() determines the precision constants+ qh_freeqhull()+*/+void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {+ time_t timedata;+ int seed;++ qh_CPUclock; /* start the clock(for qh_clock). One-shot. */+ /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */+ memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT)); /* every field is 0, FALSE, NULL */+ qh->NOerrexit= True;+ qh->ANGLEmerge= True;+ qh->DROPdim= -1;+ qh->ferr= errfile;+ qh->fin= infile;+ qh->fout= outfile;+ qh->furthest_id= qh_IDunknown;+ qh->JOGGLEmax= REALmax;+ qh->KEEPminArea = REALmax;+ qh->last_low= REALmax;+ qh->last_high= REALmax;+ qh->last_newhigh= REALmax;+ qh->last_random= 1;+ qh->max_outside= 0.0;+ qh->max_vertex= 0.0;+ qh->MAXabs_coord= 0.0;+ qh->MAXsumcoord= 0.0;+ qh->MAXwidth= -REALmax;+ qh->MERGEindependent= True;+ qh->MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */+ qh->MINoutside= 0.0;+ qh->MINvisible= REALmax;+ qh->MAXcoplanar= REALmax;+ qh->outside_err= REALmax;+ qh->premerge_centrum= 0.0;+ qh->premerge_cos= REALmax;+ qh->PRINTprecision= True;+ qh->PRINTradius= 0.0;+ qh->postmerge_cos= REALmax;+ qh->postmerge_centrum= 0.0;+ qh->ROTATErandom= INT_MIN;+ qh->MERGEvertices= True;+ qh->totarea= 0.0;+ qh->totvol= 0.0;+ qh->TRACEdist= REALmax;+ qh->TRACEpoint= qh_IDunknown; /* recompile or use 'TPn' */+ qh->tracefacet_id= UINT_MAX; /* recompile to trace a facet */+ qh->tracevertex_id= UINT_MAX; /* recompile to trace a vertex */+ seed= (int)time(&timedata);+ qh_RANDOMseed_(qh, seed);+ qh->run_id= qh_RANDOMint;+ if(!qh->run_id)+ qh->run_id++; /* guarantee non-zero */+ qh_option(qh, "run-id", &qh->run_id, NULL);+ strcat(qh->qhull, "qhull");+} /* initqhull_start2 */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="initthresholds">-</a>++ qh_initthresholds(qh, commandString )+ set thresholds for printing and scaling from commandString++ returns:+ sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used++ see:+ qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'+ qh_inthresholds()++ design:+ for each 'Pdn' or 'PDn' option+ check syntax+ set qh.lower_threshold or qh.upper_threshold+ set qh.GOODthreshold if an unbounded threshold is used+ set qh.SPLITthreshold if a bounded threshold is used+*/+void qh_initthresholds(qhT *qh, char *command) {+ realT value;+ int idx, maxdim, k;+ char *s= command; /* non-const due to strtol */+ char key;++ maxdim= qh->input_dim;+ if (qh->DELAUNAY && (qh->PROJECTdelaunay || qh->PROJECTinput))+ maxdim++;+ while (*s) {+ if (*s == '-')+ s++;+ if (*s == 'P') {+ s++;+ while (*s && !isspace(key= *s++)) {+ if (key == 'd' || key == 'D') {+ if (!isdigit(*s)) {+ qh_fprintf(qh, qh->ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n",+ key, s-1);+ continue;+ }+ idx= qh_strtol(s, &s);+ if (idx >= qh->hull_dim) {+ qh_fprintf(qh, qh->ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n",+ idx, key, qh->hull_dim);+ continue;+ }+ if (*s == ':') {+ s++;+ value= qh_strtod(s, &s);+ if (fabs((double)value) > 1.0) {+ qh_fprintf(qh, qh->ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1. Ignored\n",+ value, key);+ continue;+ }+ }else+ value= 0.0;+ if (key == 'd')+ qh->lower_threshold[idx]= value;+ else+ qh->upper_threshold[idx]= value;+ }+ }+ }else if (*s == 'Q') {+ s++;+ while (*s && !isspace(key= *s++)) {+ if (key == 'b' && *s == 'B') {+ s++;+ for (k=maxdim; k--; ) {+ qh->lower_bound[k]= -qh_DEFAULTbox;+ qh->upper_bound[k]= qh_DEFAULTbox;+ }+ }else if (key == 'b' && *s == 'b')+ s++;+ else if (key == 'b' || key == 'B') {+ if (!isdigit(*s)) {+ qh_fprintf(qh, qh->ferr, 7047, "qhull warning: no dimension given for Qhull option %c. Ignored\n",+ key);+ continue;+ }+ idx= qh_strtol(s, &s);+ if (idx >= maxdim) {+ qh_fprintf(qh, qh->ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n",+ idx, key, maxdim);+ continue;+ }+ if (*s == ':') {+ s++;+ value= qh_strtod(s, &s);+ }else if (key == 'b')+ value= -qh_DEFAULTbox;+ else+ value= qh_DEFAULTbox;+ if (key == 'b')+ qh->lower_bound[idx]= value;+ else+ qh->upper_bound[idx]= value;+ }+ }+ }else {+ while (*s && !isspace(*s))+ s++;+ }+ while (isspace(*s))+ s++;+ }+ for (k=qh->hull_dim; k--; ) {+ if (qh->lower_threshold[k] > -REALmax/2) {+ qh->GOODthreshold= True;+ if (qh->upper_threshold[k] < REALmax/2) {+ qh->SPLITthresholds= True;+ qh->GOODthreshold= False;+ break;+ }+ }else if (qh->upper_threshold[k] < REALmax/2)+ qh->GOODthreshold= True;+ }+} /* initthresholds */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="lib_check">-</a>++ qh_lib_check( qhullLibraryType, qhTsize, vertexTsize, ridgeTsize, facetTsize, setTsize, qhmemTsize )+ Report error if library does not agree with caller++ notes:+ NOerrors -- qh_lib_check can not call qh_errexit()+*/+void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize) {+ boolT iserror= False;++#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) /* user_r.h */+ // _CrtSetBreakAlloc(744); /* Break at memalloc {744}, or 'watch' _crtBreakAlloc */+ _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) );+ _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );+ _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );+ _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );+ _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );+ _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );+ _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );+#endif++ if (qhullLibraryType==QHULL_NON_REENTRANT) { /* 0 */+ qh_fprintf_stderr(6257, "qh_lib_check: Incorrect qhull library called. Caller uses non-reentrant Qhull with a static qhT. Library is reentrant.\n");+ iserror= True;+ }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */+ qh_fprintf_stderr(6258, "qh_lib_check: Incorrect qhull library called. Caller uses non-reentrant Qhull with a dynamic qhT via qh_QHpointer. Library is reentrant.\n");+ iserror= True;+ }else if (qhullLibraryType!=QHULL_REENTRANT) { /* 2 */+ qh_fprintf_stderr(6262, "qh_lib_check: Expecting qhullLibraryType QHULL_NON_REENTRANT(0), QHULL_QH_POINTER(1), or QHULL_REENTRANT(2). Got %d\n", qhullLibraryType);+ iserror= True;+ }+ if (qhTsize != sizeof(qhT)) {+ qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called. Size of qhT for caller is %d, but for library is %d.\n", qhTsize, sizeof(qhT));+ iserror= True;+ }+ if (vertexTsize != sizeof(vertexT)) {+ qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called. Size of vertexT for caller is %d, but for library is %d.\n", vertexTsize, sizeof(vertexT));+ iserror= True;+ }+ if (ridgeTsize != sizeof(ridgeT)) {+ qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called. Size of ridgeT for caller is %d, but for library is %d.\n", ridgeTsize, sizeof(ridgeT));+ iserror= True;+ }+ if (facetTsize != sizeof(facetT)) {+ qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called. Size of facetT for caller is %d, but for library is %d.\n", facetTsize, sizeof(facetT));+ iserror= True;+ }+ if (setTsize && setTsize != sizeof(setT)) {+ qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called. Size of setT for caller is %d, but for library is %d.\n", setTsize, sizeof(setT));+ iserror= True;+ }+ if (qhmemTsize && qhmemTsize != sizeof(qhmemT)) {+ qh_fprintf_stderr(6254, "qh_lib_check: Incorrect qhull library called. Size of qhmemT for caller is %d, but for library is %d.\n", qhmemTsize, sizeof(qhmemT));+ iserror= True;+ }+ if (iserror) {+ qh_fprintf_stderr(6259, "qh_lib_check: Cannot continue. Library '%s' is reentrant (e.g., qhull_r.so)\n", qh_version2);+ qh_exit(qh_ERRqhull); /* can not use qh_errexit() */+ }+} /* lib_check */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="option">-</a>++ qh_option(qh, option, intVal, realVal )+ add an option description to qh.qhull_options++ notes:+ NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]+ will be printed with statistics ('Ts') and errors+ strlen(option) < 40+*/+void qh_option(qhT *qh, const char *option, int *i, realT *r) {+ char buf[200];+ int len, maxlen;++ sprintf(buf, " %s", option);+ if (i)+ sprintf(buf+strlen(buf), " %d", *i);+ if (r)+ sprintf(buf+strlen(buf), " %2.2g", *r);+ len= (int)strlen(buf); /* WARN64 */+ qh->qhull_optionlen += len;+ maxlen= sizeof(qh->qhull_options) - len -1;+ maximize_(maxlen, 0);+ if (qh->qhull_optionlen >= qh_OPTIONline && maxlen > 0) {+ qh->qhull_optionlen= len;+ strncat(qh->qhull_options, "\n", (size_t)(maxlen--));+ }+ strncat(qh->qhull_options, buf, (size_t)maxlen);+} /* option */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="zero">-</a>++ qh_zero( qh, errfile )+ Initialize and zero Qhull's memory for qh_new_qhull()++ notes:+ Not needed in global.c because static variables are initialized to zero+*/+void qh_zero(qhT *qh, FILE *errfile) {+ memset((char *)qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */+ qh->NOerrexit= True;+ qh_meminit(qh, errfile);+} /* zero */+
+ C/halfspaces.c view
@@ -0,0 +1,68 @@+#define qh_QHimport+#include "qhull_ra.h"++double** intersections(+ double* halfspaces,+ double* interiorpoint,+ unsigned dim,+ unsigned n,+ unsigned* nintersections,+ unsigned* exitcode,+ unsigned print+)+{+ char opts[250];+ sprintf(opts, "qhull s Fp FF H H%f", interiorpoint[0]); //, interiorpoint[1] , interiorpoint[2]);+ for(unsigned i=1; i < dim; i++){+ char x[20];+ sprintf(x, ",%f", interiorpoint[i]);+ strcat(opts, x);+ }+ printf(opts); printf("\n");++ qhT qh_qh; /* Qhull's data structure */+ qhT* qh= &qh_qh;+ QHULL_LIB_CHECK+ qh_meminit(qh, stderr);+ boolT ismalloc = False; /* True if qhull should free points in qh_freeqhull() or reallocation */+ FILE *errfile = NULL;+ FILE* outfile = print ? stdout : NULL;+ qh_zero(qh, errfile);+ *exitcode = qh_new_qhull(qh, dim+1, n, halfspaces, ismalloc, opts, outfile,+ errfile);+ printf("exitcode: %u\n", *exitcode);++ double** out;+ if(!(*exitcode)){+ *nintersections = qh->num_facets;+ out = malloc(*nintersections * sizeof(double*));+ facetT *facet;+ unsigned i_facet = 0;+ FORALLfacets{+ if(facet->offset != 0){+ out[i_facet] = malloc(dim * sizeof(double));+ for(unsigned i=0; i < dim; i++){+ out[i_facet][i] = - facet->normal[i] / facet->offset ++ qh->feasible_point[i]; // = interiorpoint ? yes+ }+ i_facet++;+ }else{+ (*nintersections)--;+ }+ }++ }++ /* Do cleanup regardless of whether there is an error */+ int curlong, totlong;+ qh_freeqhull(qh, !qh_ALL); /* free long memory */+ qh_memfreeshort(qh, &curlong, &totlong); /* free short memory and memory allocator */++ if(*exitcode){+ free(out);+ return 0;+ }else{+ return out;+ }++}
+ C/io_r.c view
@@ -0,0 +1,4062 @@+/*<html><pre> -<a href="qh-io_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ io_r.c+ Input/Output routines of qhull application++ see qh-io_r.htm and io_r.h++ see user_r.c for qh_errprint and qh_printfacetlist++ unix_r.c calls qh_readpoints and qh_produce_output++ unix_r.c and user_r.c are the only callers of io_r.c functions+ This allows the user to avoid loading io_r.o from qhull.a++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/io_r.c#4 $$Change: 2064 $+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $+*/++#include "qhull_ra.h"++/*========= -functions in alphabetical order after qh_produce_output(qh) =====*/++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="produce_output">-</a>++ qh_produce_output(qh)+ qh_produce_output2(qh)+ prints out the result of qhull in desired format+ qh_produce_output2(qh) does not call qh_prepare_output(qh)+ if qh.GETarea+ computes and prints area and volume+ qh.PRINTout[] is an array of output formats++ notes:+ prints output in qh.PRINTout order+*/+void qh_produce_output(qhT *qh) {+ int tempsize= qh_setsize(qh, qh->qhmem.tempstack);++ qh_prepare_output(qh);+ qh_produce_output2(qh);+ if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {+ qh_fprintf(qh, qh->ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n",+ qh_setsize(qh, qh->qhmem.tempstack));+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+} /* produce_output */+++void qh_produce_output2(qhT *qh) {+ int i, tempsize= qh_setsize(qh, qh->qhmem.tempstack), d_1;++ if (qh->PRINTsummary)+ qh_printsummary(qh, qh->ferr);+ else if (qh->PRINTout[0] == qh_PRINTnone)+ qh_printsummary(qh, qh->fout);+ for (i=0; i < qh_PRINTEND; i++)+ qh_printfacets(qh, qh->fout, qh->PRINTout[i], qh->facet_list, NULL, !qh_ALL);+ qh_allstatistics(qh);+ if (qh->PRINTprecision && !qh->MERGING && (qh->JOGGLEmax > REALmax/2 || qh->RERUN))+ qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL);+ if (qh->VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0))+ qh_printstats(qh, qh->ferr, qh->qhstat.vridges, NULL);+ if (qh->PRINTstatistics) {+ qh_printstatistics(qh, qh->ferr, "");+ qh_memstatistics(qh, qh->ferr);+ d_1= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize;+ qh_fprintf(qh, qh->ferr, 8040, "\+ size in bytes: merge %d ridge %d vertex %d facet %d\n\+ normal %d ridge vertices %d facet vertices or neighbors %d\n",+ (int)sizeof(mergeT), (int)sizeof(ridgeT),+ (int)sizeof(vertexT), (int)sizeof(facetT),+ qh->normal_size, d_1, d_1 + SETelemsize);+ }+ if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {+ qh_fprintf(qh, qh->ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",+ qh_setsize(qh, qh->qhmem.tempstack));+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+} /* produce_output2 */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="dfacet">-</a>++ qh_dfacet(qh, id )+ print facet by id, for debugging++*/+void qh_dfacet(qhT *qh, unsigned id) {+ facetT *facet;++ FORALLfacets {+ if (facet->id == id) {+ qh_printfacet(qh, qh->fout, facet);+ break;+ }+ }+} /* dfacet */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="dvertex">-</a>++ qh_dvertex(qh, id )+ print vertex by id, for debugging+*/+void qh_dvertex(qhT *qh, unsigned id) {+ vertexT *vertex;++ FORALLvertices {+ if (vertex->id == id) {+ qh_printvertex(qh, qh->fout, vertex);+ break;+ }+ }+} /* dvertex */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="compare_facetarea">-</a>++ qh_compare_facetarea(p1, p2 )+ used by qsort() to order facets by area+*/+int qh_compare_facetarea(const void *p1, const void *p2) {+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);++ if (!a->isarea)+ return -1;+ if (!b->isarea)+ return 1;+ if (a->f.area > b->f.area)+ return 1;+ else if (a->f.area == b->f.area)+ return 0;+ return -1;+} /* compare_facetarea */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="compare_facetmerge">-</a>++ qh_compare_facetmerge(p1, p2 )+ used by qsort() to order facets by number of merges+*/+int qh_compare_facetmerge(const void *p1, const void *p2) {+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);++ return(a->nummerge - b->nummerge);+} /* compare_facetvisit */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="compare_facetvisit">-</a>++ qh_compare_facetvisit(p1, p2 )+ used by qsort() to order facets by visit id or id+*/+int qh_compare_facetvisit(const void *p1, const void *p2) {+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);+ int i,j;++ if (!(i= a->visitid))+ i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */+ if (!(j= b->visitid))+ j= 0 - b->id;+ return(i - j);+} /* compare_facetvisit */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="compare_vertexpoint">-</a>++ qh_compare_vertexpoint( p1, p2 )+ used by qsort() to order vertices by point id++ Not usable in qhulllib_r since qh_pointid depends on qh++ int qh_compare_vertexpoint(const void *p1, const void *p2) {+ const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);++ return((qh_pointid(qh, a->point) > qh_pointid(qh, b->point)?1:-1));+}*/++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="copyfilename">-</a>++ qh_copyfilename(qh, dest, size, source, length )+ copy filename identified by qh_skipfilename()++ notes:+ see qh_skipfilename() for syntax+*/+void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length) {+ char c= *source;++ if (length > size + 1) {+ qh_fprintf(qh, qh->ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ strncpy(filename, source, length);+ filename[length]= '\0';+ if (c == '\'' || c == '"') {+ char *s= filename + 1;+ char *t= filename;+ while (*s) {+ if (*s == c) {+ if (s[-1] == '\\')+ t[-1]= c;+ }else+ *t++= *s;+ s++;+ }+ *t= '\0';+ }+} /* copyfilename */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="countfacets">-</a>++ qh_countfacets(qh, facetlist, facets, printall,+ numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars )+ count good facets for printing and set visitid+ if allfacets, ignores qh_skipfacet()++ notes:+ qh_printsummary and qh_countfacets must match counts++ returns:+ numfacets, numsimplicial, total neighbors, numridges, coplanars+ each facet with ->visitid indicating 1-relative position+ ->visitid==0 indicates not good++ notes+ numfacets >= numsimplicial+ if qh.NEWfacets,+ does not count visible facets (matches qh_printafacet)++ design:+ for all facets on facetlist and in facets set+ unless facet is skipped or visible (i.e., will be deleted)+ mark facet->visitid+ update counts+*/+void qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall,+ int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) {+ facetT *facet, **facetp;+ int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0;++ FORALLfacet_(facetlist) {+ if ((facet->visible && qh->NEWfacets)+ || (!printall && qh_skipfacet(qh, facet)))+ facet->visitid= 0;+ else {+ facet->visitid= ++numfacets;+ totneighbors += qh_setsize(qh, facet->neighbors);+ if (facet->simplicial) {+ numsimplicial++;+ if (facet->keepcentrum && facet->tricoplanar)+ numtricoplanars++;+ }else+ numridges += qh_setsize(qh, facet->ridges);+ if (facet->coplanarset)+ numcoplanars += qh_setsize(qh, facet->coplanarset);+ }+ }++ FOREACHfacet_(facets) {+ if ((facet->visible && qh->NEWfacets)+ || (!printall && qh_skipfacet(qh, facet)))+ facet->visitid= 0;+ else {+ facet->visitid= ++numfacets;+ totneighbors += qh_setsize(qh, facet->neighbors);+ if (facet->simplicial){+ numsimplicial++;+ if (facet->keepcentrum && facet->tricoplanar)+ numtricoplanars++;+ }else+ numridges += qh_setsize(qh, facet->ridges);+ if (facet->coplanarset)+ numcoplanars += qh_setsize(qh, facet->coplanarset);+ }+ }+ qh->visit_id += numfacets+1;+ *numfacetsp= numfacets;+ *numsimplicialp= numsimplicial;+ *totneighborsp= totneighbors;+ *numridgesp= numridges;+ *numcoplanarsp= numcoplanars;+ *numtricoplanarsp= numtricoplanars;+} /* countfacets */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="detvnorm">-</a>++ qh_detvnorm(qh, vertex, vertexA, centers, offset )+ compute separating plane of the Voronoi diagram for a pair of input sites+ centers= set of facets (i.e., Voronoi vertices)+ facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded)++ assumes:+ qh_ASvoronoi and qh_vertexneighbors() already set++ returns:+ norm+ a pointer into qh.gm_matrix to qh.hull_dim-1 reals+ copy the data before reusing qh.gm_matrix+ offset+ if 'QVn'+ sign adjusted so that qh.GOODvertexp is inside+ else+ sign adjusted so that vertex is inside++ qh.gm_matrix= simplex of points from centers relative to first center++ notes:+ in io_r.c so that code for 'v Tv' can be removed by removing io_r.c+ returns pointer into qh.gm_matrix to avoid tracking of temporary memory++ design:+ determine midpoint of input sites+ build points as the set of Voronoi vertices+ select a simplex from points (if necessary)+ include midpoint if the Voronoi region is unbounded+ relocate the first vertex of the simplex to the origin+ compute the normalized hyperplane through the simplex+ orient the hyperplane toward 'QVn' or 'vertex'+ if 'Tv' or 'Ts'+ if bounded+ test that hyperplane is the perpendicular bisector of the input sites+ test that Voronoi vertices not in the simplex are still on the hyperplane+ free up temporary memory+*/+pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) {+ facetT *facet, **facetp;+ int i, k, pointid, pointidA, point_i, point_n;+ setT *simplex= NULL;+ pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint;+ coordT *coord, *gmcoord, *normalp;+ setT *points= qh_settemp(qh, qh->TEMPsize);+ boolT nearzero= False;+ boolT unbounded= False;+ int numcenters= 0;+ int dim= qh->hull_dim - 1;+ realT dist, offset, angle, zero= 0.0;++ midpoint= qh->gm_matrix + qh->hull_dim * qh->hull_dim; /* last row */+ for (k=0; k < dim; k++)+ midpoint[k]= (vertex->point[k] + vertexA->point[k])/2;+ FOREACHfacet_(centers) {+ numcenters++;+ if (!facet->visitid)+ unbounded= True;+ else {+ if (!facet->center)+ facet->center= qh_facetcenter(qh, facet->vertices);+ qh_setappend(qh, &points, facet->center);+ }+ }+ if (numcenters > dim) {+ simplex= qh_settemp(qh, qh->TEMPsize);+ qh_setappend(qh, &simplex, vertex->point);+ if (unbounded)+ qh_setappend(qh, &simplex, midpoint);+ qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);+ qh_setdelnth(qh, simplex, 0);+ }else if (numcenters == dim) {+ if (unbounded)+ qh_setappend(qh, &points, midpoint);+ simplex= points;+ }else {+ qh_fprintf(qh, qh->ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ i= 0;+ gmcoord= qh->gm_matrix;+ point0= SETfirstt_(simplex, pointT);+ FOREACHpoint_(simplex) {+ if (qh->IStracing >= 4)+ qh_printmatrix(qh, qh->ferr, "qh_detvnorm: Voronoi vertex or midpoint",+ &point, 1, dim);+ if (point != point0) {+ qh->gm_row[i++]= gmcoord;+ coord= point0;+ for (k=dim; k--; )+ *(gmcoord++)= *point++ - *coord++;+ }+ }+ qh->gm_row[i]= gmcoord; /* does not overlap midpoint, may be used later for qh_areasimplex */+ normal= gmcoord;+ qh_sethyperplane_gauss(qh, dim, qh->gm_row, point0, True,+ normal, &offset, &nearzero);+ if (qh->GOODvertexp == vertexA->point)+ inpoint= vertexA->point;+ else+ inpoint= vertex->point;+ zinc_(Zdistio);+ dist= qh_distnorm(dim, inpoint, normal, &offset);+ if (dist > 0) {+ offset= -offset;+ normalp= normal;+ for (k=dim; k--; ) {+ *normalp= -(*normalp);+ normalp++;+ }+ }+ if (qh->VERIFYoutput || qh->PRINTstatistics) {+ pointid= qh_pointid(qh, vertex->point);+ pointidA= qh_pointid(qh, vertexA->point);+ if (!unbounded) {+ zinc_(Zdiststat);+ dist= qh_distnorm(dim, midpoint, normal, &offset);+ if (dist < 0)+ dist= -dist;+ zzinc_(Zridgemid);+ wwmax_(Wridgemidmax, dist);+ wwadd_(Wridgemid, dist);+ trace4((qh, qh->ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n",+ pointid, pointidA, dist));+ for (k=0; k < dim; k++)+ midpoint[k]= vertexA->point[k] - vertex->point[k]; /* overwrites midpoint! */+ qh_normalize(qh, midpoint, dim, False);+ angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */+ if (angle < 0.0)+ angle= angle + 1.0;+ else+ angle= angle - 1.0;+ if (angle < 0.0)+ angle -= angle;+ trace4((qh, qh->ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n",+ pointid, pointidA, angle, nearzero));+ if (nearzero) {+ zzinc_(Zridge0);+ wwmax_(Wridge0max, angle);+ wwadd_(Wridge0, angle);+ }else {+ zzinc_(Zridgeok)+ wwmax_(Wridgeokmax, angle);+ wwadd_(Wridgeok, angle);+ }+ }+ if (simplex != points) {+ FOREACHpoint_i_(qh, points) {+ if (!qh_setin(simplex, point)) {+ facet= SETelemt_(centers, point_i, facetT);+ zinc_(Zdiststat);+ dist= qh_distnorm(dim, point, normal, &offset);+ if (dist < 0)+ dist= -dist;+ zzinc_(Zridge);+ wwmax_(Wridgemax, dist);+ wwadd_(Wridge, dist);+ trace4((qh, qh->ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n",+ pointid, pointidA, facet->visitid, dist));+ }+ }+ }+ }+ *offsetp= offset;+ if (simplex != points)+ qh_settempfree(qh, &simplex);+ qh_settempfree(qh, &points);+ return normal;+} /* detvnorm */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="detvridge">-</a>++ qh_detvridge(qh, vertexA )+ determine Voronoi ridge from 'seen' neighbors of vertexA+ include one vertex-at-infinite if an !neighbor->visitid++ returns:+ temporary set of centers (facets, i.e., Voronoi vertices)+ sorted by center id+*/+setT *qh_detvridge(qhT *qh, vertexT *vertex) {+ setT *centers= qh_settemp(qh, qh->TEMPsize);+ setT *tricenters= qh_settemp(qh, qh->TEMPsize);+ facetT *neighbor, **neighborp;+ boolT firstinf= True;++ FOREACHneighbor_(vertex) {+ if (neighbor->seen) {+ if (neighbor->visitid) {+ if (!neighbor->tricoplanar || qh_setunique(qh, &tricenters, neighbor->center))+ qh_setappend(qh, ¢ers, neighbor);+ }else if (firstinf) {+ firstinf= False;+ qh_setappend(qh, ¢ers, neighbor);+ }+ }+ }+ qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(qh, centers),+ sizeof(facetT *), qh_compare_facetvisit);+ qh_settempfree(qh, &tricenters);+ return centers;+} /* detvridge */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="detvridge3">-</a>++ qh_detvridge3(qh, atvertex, vertex )+ determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex+ include one vertex-at-infinite for !neighbor->visitid+ assumes all facet->seen2= True++ returns:+ temporary set of centers (facets, i.e., Voronoi vertices)+ listed in adjacency order (!oriented)+ all facet->seen2= True++ design:+ mark all neighbors of atvertex+ for each adjacent neighbor of both atvertex and vertex+ if neighbor selected+ add neighbor to set of Voronoi vertices+*/+setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex) {+ setT *centers= qh_settemp(qh, qh->TEMPsize);+ setT *tricenters= qh_settemp(qh, qh->TEMPsize);+ facetT *neighbor, **neighborp, *facet= NULL;+ boolT firstinf= True;++ FOREACHneighbor_(atvertex)+ neighbor->seen2= False;+ FOREACHneighbor_(vertex) {+ if (!neighbor->seen2) {+ facet= neighbor;+ break;+ }+ }+ while (facet) {+ facet->seen2= True;+ if (neighbor->seen) {+ if (facet->visitid) {+ if (!facet->tricoplanar || qh_setunique(qh, &tricenters, facet->center))+ qh_setappend(qh, ¢ers, facet);+ }else if (firstinf) {+ firstinf= False;+ qh_setappend(qh, ¢ers, facet);+ }+ }+ FOREACHneighbor_(facet) {+ if (!neighbor->seen2) {+ if (qh_setin(vertex->neighbors, neighbor))+ break;+ else+ neighbor->seen2= True;+ }+ }+ facet= neighbor;+ }+ if (qh->CHECKfrequently) {+ FOREACHneighbor_(vertex) {+ if (!neighbor->seen2) {+ qh_fprintf(qh, qh->ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",+ qh_pointid(qh, vertex->point), neighbor->id);+ qh_errexit(qh, qh_ERRqhull, neighbor, NULL);+ }+ }+ }+ FOREACHneighbor_(atvertex)+ neighbor->seen2= True;+ qh_settempfree(qh, &tricenters);+ return centers;+} /* detvridge3 */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="eachvoronoi">-</a>++ qh_eachvoronoi(qh, fp, printvridge, vertex, visitall, innerouter, inorder )+ if visitall,+ visit all Voronoi ridges for vertex (i.e., an input site)+ else+ visit all unvisited Voronoi ridges for vertex+ all vertex->seen= False if unvisited+ assumes+ all facet->seen= False+ all facet->seen2= True (for qh_detvridge3)+ all facet->visitid == 0 if vertex_at_infinity+ == index of Voronoi vertex+ >= qh.num_facets if ignored+ innerouter:+ qh_RIDGEall-- both inner (bounded) and outer(unbounded) ridges+ qh_RIDGEinner- only inner+ qh_RIDGEouter- only outer++ if inorder+ orders vertices for 3-d Voronoi diagrams++ returns:+ number of visited ridges (does not include previously visited ridges)++ if printvridge,+ calls printvridge( fp, vertex, vertexA, centers)+ fp== any pointer (assumes FILE*)+ vertex,vertexA= pair of input sites that define a Voronoi ridge+ centers= set of facets (i.e., Voronoi vertices)+ ->visitid == index or 0 if vertex_at_infinity+ ordered for 3-d Voronoi diagram+ notes:+ uses qh.vertex_visit++ see:+ qh_eachvoronoi_all()++ design:+ mark selected neighbors of atvertex+ for each selected neighbor (either Voronoi vertex or vertex-at-infinity)+ for each unvisited vertex+ if atvertex and vertex share more than d-1 neighbors+ bump totalcount+ if printvridge defined+ build the set of shared neighbors (i.e., Voronoi vertices)+ call printvridge+*/+int qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) {+ boolT unbounded;+ int count;+ facetT *neighbor, **neighborp, *neighborA, **neighborAp;+ setT *centers;+ setT *tricenters= qh_settemp(qh, qh->TEMPsize);++ vertexT *vertex, **vertexp;+ boolT firstinf;+ unsigned int numfacets= (unsigned int)qh->num_facets;+ int totridges= 0;++ qh->vertex_visit++;+ atvertex->seen= True;+ if (visitall) {+ FORALLvertices+ vertex->seen= False;+ }+ FOREACHneighbor_(atvertex) {+ if (neighbor->visitid < numfacets)+ neighbor->seen= True;+ }+ FOREACHneighbor_(atvertex) {+ if (neighbor->seen) {+ FOREACHvertex_(neighbor->vertices) {+ if (vertex->visitid != qh->vertex_visit && !vertex->seen) {+ vertex->visitid= qh->vertex_visit;+ count= 0;+ firstinf= True;+ qh_settruncate(qh, tricenters, 0);+ FOREACHneighborA_(vertex) {+ if (neighborA->seen) {+ if (neighborA->visitid) {+ if (!neighborA->tricoplanar || qh_setunique(qh, &tricenters, neighborA->center))+ count++;+ }else if (firstinf) {+ count++;+ firstinf= False;+ }+ }+ }+ if (count >= qh->hull_dim - 1) { /* e.g., 3 for 3-d Voronoi */+ if (firstinf) {+ if (innerouter == qh_RIDGEouter)+ continue;+ unbounded= False;+ }else {+ if (innerouter == qh_RIDGEinner)+ continue;+ unbounded= True;+ }+ totridges++;+ trace4((qh, qh->ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n",+ count, qh_pointid(qh, atvertex->point), qh_pointid(qh, vertex->point)));+ if (printvridge && fp) {+ if (inorder && qh->hull_dim == 3+1) /* 3-d Voronoi diagram */+ centers= qh_detvridge3(qh, atvertex, vertex);+ else+ centers= qh_detvridge(qh, vertex);+ (*printvridge)(qh, fp, atvertex, vertex, centers, unbounded);+ qh_settempfree(qh, ¢ers);+ }+ }+ }+ }+ }+ }+ FOREACHneighbor_(atvertex)+ neighbor->seen= False;+ qh_settempfree(qh, &tricenters);+ return totridges;+} /* eachvoronoi */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="eachvoronoi_all">-</a>++ qh_eachvoronoi_all(qh, fp, printvridge, isUpper, innerouter, inorder )+ visit all Voronoi ridges++ innerouter:+ see qh_eachvoronoi()++ if inorder+ orders vertices for 3-d Voronoi diagrams++ returns+ total number of ridges++ if isUpper == facet->upperdelaunay (i.e., a Vornoi vertex)+ facet->visitid= Voronoi vertex index(same as 'o' format)+ else+ facet->visitid= 0++ if printvridge,+ calls printvridge( fp, vertex, vertexA, centers)+ [see qh_eachvoronoi]++ notes:+ Not used for qhull.exe+ same effect as qh_printvdiagram but ridges not sorted by point id+*/+int qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) {+ facetT *facet;+ vertexT *vertex;+ int numcenters= 1; /* vertex 0 is vertex-at-infinity */+ int totridges= 0;++ qh_clearcenters(qh, qh_ASvoronoi);+ qh_vertexneighbors(qh);+ maximize_(qh->visit_id, (unsigned) qh->num_facets);+ FORALLfacets {+ facet->visitid= 0;+ facet->seen= False;+ facet->seen2= True;+ }+ FORALLfacets {+ if (facet->upperdelaunay == isUpper)+ facet->visitid= numcenters++;+ }+ FORALLvertices+ vertex->seen= False;+ FORALLvertices {+ if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)+ continue;+ totridges += qh_eachvoronoi(qh, fp, printvridge, vertex,+ !qh_ALL, innerouter, inorder);+ }+ return totridges;+} /* eachvoronoi_all */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="facet2point">-</a>++ qh_facet2point(qh, facet, point0, point1, mindist )+ return two projected temporary vertices for a 2-d facet+ may be non-simplicial++ returns:+ point0 and point1 oriented and projected to the facet+ returns mindist (maximum distance below plane)+*/+void qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist) {+ vertexT *vertex0, *vertex1;+ realT dist;++ if (facet->toporient ^ qh_ORIENTclock) {+ vertex0= SETfirstt_(facet->vertices, vertexT);+ vertex1= SETsecondt_(facet->vertices, vertexT);+ }else {+ vertex1= SETfirstt_(facet->vertices, vertexT);+ vertex0= SETsecondt_(facet->vertices, vertexT);+ }+ zadd_(Zdistio, 2);+ qh_distplane(qh, vertex0->point, facet, &dist);+ *mindist= dist;+ *point0= qh_projectpoint(qh, vertex0->point, facet, dist);+ qh_distplane(qh, vertex1->point, facet, &dist);+ minimize_(*mindist, dist);+ *point1= qh_projectpoint(qh, vertex1->point, facet, dist);+} /* facet2point */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="facetvertices">-</a>++ qh_facetvertices(qh, facetlist, facets, allfacets )+ returns temporary set of vertices in a set and/or list of facets+ if allfacets, ignores qh_skipfacet()++ returns:+ vertices with qh.vertex_visit++ notes:+ optimized for allfacets of facet_list++ design:+ if allfacets of facet_list+ create vertex set from vertex_list+ else+ for each selected facet in facets or facetlist+ append unvisited vertices to vertex set+*/+setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets) {+ setT *vertices;+ facetT *facet, **facetp;+ vertexT *vertex, **vertexp;++ qh->vertex_visit++;+ if (facetlist == qh->facet_list && allfacets && !facets) {+ vertices= qh_settemp(qh, qh->num_vertices);+ FORALLvertices {+ vertex->visitid= qh->vertex_visit;+ qh_setappend(qh, &vertices, vertex);+ }+ }else {+ vertices= qh_settemp(qh, qh->TEMPsize);+ FORALLfacet_(facetlist) {+ if (!allfacets && qh_skipfacet(qh, facet))+ continue;+ FOREACHvertex_(facet->vertices) {+ if (vertex->visitid != qh->vertex_visit) {+ vertex->visitid= qh->vertex_visit;+ qh_setappend(qh, &vertices, vertex);+ }+ }+ }+ }+ FOREACHfacet_(facets) {+ if (!allfacets && qh_skipfacet(qh, facet))+ continue;+ FOREACHvertex_(facet->vertices) {+ if (vertex->visitid != qh->vertex_visit) {+ vertex->visitid= qh->vertex_visit;+ qh_setappend(qh, &vertices, vertex);+ }+ }+ }+ return vertices;+} /* facetvertices */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="geomplanes">-</a>++ qh_geomplanes(qh, facet, outerplane, innerplane )+ return outer and inner planes for Geomview+ qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax)++ notes:+ assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon+*/+void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {+ realT radius;++ if (qh->MERGING || qh->JOGGLEmax < REALmax/2) {+ qh_outerinner(qh, facet, outerplane, innerplane);+ radius= qh->PRINTradius;+ if (qh->JOGGLEmax < REALmax/2)+ radius -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim); /* already accounted for in qh_outerinner() */+ *outerplane += radius;+ *innerplane -= radius;+ if (qh->PRINTcoplanar || qh->PRINTspheres) {+ *outerplane += qh->MAXabs_coord * qh_GEOMepsilon;+ *innerplane -= qh->MAXabs_coord * qh_GEOMepsilon;+ }+ }else+ *innerplane= *outerplane= 0;+} /* geomplanes */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="markkeep">-</a>++ qh_markkeep(qh, facetlist )+ mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea+ ignores visible facets (!part of convex hull)++ returns:+ may clear facet->good+ recomputes qh.num_good++ design:+ get set of good facets+ if qh.KEEParea+ sort facets by area+ clear facet->good for all but n largest facets+ if qh.KEEPmerge+ sort facets by merge count+ clear facet->good for all but n most merged facets+ if qh.KEEPminarea+ clear facet->good if area too small+ update qh.num_good+*/+void qh_markkeep(qhT *qh, facetT *facetlist) {+ facetT *facet, **facetp;+ setT *facets= qh_settemp(qh, qh->num_facets);+ int size, count;++ trace2((qh, qh->ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n",+ qh->KEEParea, qh->KEEPmerge, qh->KEEPminArea));+ FORALLfacet_(facetlist) {+ if (!facet->visible && facet->good)+ qh_setappend(qh, &facets, facet);+ }+ size= qh_setsize(qh, facets);+ if (qh->KEEParea) {+ qsort(SETaddr_(facets, facetT), (size_t)size,+ sizeof(facetT *), qh_compare_facetarea);+ if ((count= size - qh->KEEParea) > 0) {+ FOREACHfacet_(facets) {+ facet->good= False;+ if (--count == 0)+ break;+ }+ }+ }+ if (qh->KEEPmerge) {+ qsort(SETaddr_(facets, facetT), (size_t)size,+ sizeof(facetT *), qh_compare_facetmerge);+ if ((count= size - qh->KEEPmerge) > 0) {+ FOREACHfacet_(facets) {+ facet->good= False;+ if (--count == 0)+ break;+ }+ }+ }+ if (qh->KEEPminArea < REALmax/2) {+ FOREACHfacet_(facets) {+ if (!facet->isarea || facet->f.area < qh->KEEPminArea)+ facet->good= False;+ }+ }+ qh_settempfree(qh, &facets);+ count= 0;+ FORALLfacet_(facetlist) {+ if (facet->good)+ count++;+ }+ qh->num_good= count;+} /* markkeep */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="markvoronoi">-</a>++ qh_markvoronoi(qh, facetlist, facets, printall, isLower, numcenters )+ mark voronoi vertices for printing by site pairs++ returns:+ temporary set of vertices indexed by pointid+ isLower set if printing lower hull (i.e., at least one facet is lower hull)+ numcenters= total number of Voronoi vertices+ bumps qh.printoutnum for vertex-at-infinity+ clears all facet->seen and sets facet->seen2++ if selected+ facet->visitid= Voronoi vertex id+ else if upper hull (or 'Qu' and lower hull)+ facet->visitid= 0+ else+ facet->visitid >= qh->num_facets++ notes:+ ignores qh.ATinfinity, if defined+*/+setT *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) {+ int numcenters=0;+ facetT *facet, **facetp;+ setT *vertices;+ boolT isLower= False;++ qh->printoutnum++;+ qh_clearcenters(qh, qh_ASvoronoi); /* in case, qh_printvdiagram2 called by user */+ qh_vertexneighbors(qh);+ vertices= qh_pointvertex(qh);+ if (qh->ATinfinity)+ SETelem_(vertices, qh->num_points-1)= NULL;+ qh->visit_id++;+ maximize_(qh->visit_id, (unsigned) qh->num_facets);+ FORALLfacet_(facetlist) {+ if (printall || !qh_skipfacet(qh, facet)) {+ if (!facet->upperdelaunay) {+ isLower= True;+ break;+ }+ }+ }+ FOREACHfacet_(facets) {+ if (printall || !qh_skipfacet(qh, facet)) {+ if (!facet->upperdelaunay) {+ isLower= True;+ break;+ }+ }+ }+ FORALLfacets {+ if (facet->normal && (facet->upperdelaunay == isLower))+ facet->visitid= 0; /* facetlist or facets may overwrite */+ else+ facet->visitid= qh->visit_id;+ facet->seen= False;+ facet->seen2= True;+ }+ numcenters++; /* qh_INFINITE */+ FORALLfacet_(facetlist) {+ if (printall || !qh_skipfacet(qh, facet))+ facet->visitid= numcenters++;+ }+ FOREACHfacet_(facets) {+ if (printall || !qh_skipfacet(qh, facet))+ facet->visitid= numcenters++;+ }+ *isLowerp= isLower;+ *numcentersp= numcenters;+ trace2((qh, qh->ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));+ return vertices;+} /* markvoronoi */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="order_vertexneighbors">-</a>++ qh_order_vertexneighbors(qh, vertex )+ order facet neighbors of a 2-d or 3-d vertex by adjacency++ notes:+ does not orient the neighbors++ design:+ initialize a new neighbor set with the first facet in vertex->neighbors+ while vertex->neighbors non-empty+ select next neighbor in the previous facet's neighbor set+ set vertex->neighbors to the new neighbor set+*/+void qh_order_vertexneighbors(qhT *qh, vertexT *vertex) {+ setT *newset;+ facetT *facet, *neighbor, **neighborp;++ trace4((qh, qh->ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id));+ newset= qh_settemp(qh, qh_setsize(qh, vertex->neighbors));+ facet= (facetT*)qh_setdellast(vertex->neighbors);+ qh_setappend(qh, &newset, facet);+ while (qh_setsize(qh, vertex->neighbors)) {+ FOREACHneighbor_(vertex) {+ if (qh_setin(facet->neighbors, neighbor)) {+ qh_setdel(vertex->neighbors, neighbor);+ qh_setappend(qh, &newset, neighbor);+ facet= neighbor;+ break;+ }+ }+ if (!neighbor) {+ qh_fprintf(qh, qh->ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n",+ vertex->id, facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ }+ qh_setfree(qh, &vertex->neighbors);+ qh_settemppop(qh);+ vertex->neighbors= newset;+} /* order_vertexneighbors */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="prepare_output">-</a>++ qh_prepare_output(qh, )+ prepare for qh_produce_output2(qh) according to+ qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds+ does not reset facet->good++ notes+ except for PRINTstatistics, no-op if previously called with same options+*/+void qh_prepare_output(qhT *qh) {+ if (qh->VORONOI) {+ qh_clearcenters(qh, qh_ASvoronoi); /* must be before qh_triangulate */+ qh_vertexneighbors(qh);+ }+ if (qh->TRIangulate && !qh->hasTriangulation) {+ qh_triangulate(qh);+ if (qh->VERIFYoutput && !qh->CHECKfrequently)+ qh_checkpolygon(qh, qh->facet_list);+ }+ qh_findgood_all(qh, qh->facet_list);+ if (qh->GETarea)+ qh_getarea(qh, qh->facet_list);+ if (qh->KEEParea || qh->KEEPmerge || qh->KEEPminArea < REALmax/2)+ qh_markkeep(qh, qh->facet_list);+ if (qh->PRINTstatistics)+ qh_collectstatistics(qh);+}++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printafacet">-</a>++ qh_printafacet(qh, fp, format, facet, printall )+ print facet to fp in given output format (see qh.PRINTout)++ returns:+ nop if !printall and qh_skipfacet()+ nop if visible facet and NEWfacets and format != PRINTfacets+ must match qh_countfacets++ notes+ preserves qh.visit_id+ facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge++ see+ qh_printbegin() and qh_printend()++ design:+ test for printing facet+ call appropriate routine for format+ or output results directly+*/+void qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall) {+ realT color[4], offset, dist, outerplane, innerplane;+ boolT zerodiv;+ coordT *point, *normp, *coordp, **pointp, *feasiblep;+ int k;+ vertexT *vertex, **vertexp;+ facetT *neighbor, **neighborp;++ if (!printall && qh_skipfacet(qh, facet))+ return;+ if (facet->visible && qh->NEWfacets && format != qh_PRINTfacets)+ return;+ qh->printoutnum++;+ switch (format) {+ case qh_PRINTarea:+ if (facet->isarea) {+ qh_fprintf(qh, fp, 9009, qh_REAL_1, facet->f.area);+ qh_fprintf(qh, fp, 9010, "\n");+ }else+ qh_fprintf(qh, fp, 9011, "0\n");+ break;+ case qh_PRINTcoplanars:+ qh_fprintf(qh, fp, 9012, "%d", qh_setsize(qh, facet->coplanarset));+ FOREACHpoint_(facet->coplanarset)+ qh_fprintf(qh, fp, 9013, " %d", qh_pointid(qh, point));+ qh_fprintf(qh, fp, 9014, "\n");+ break;+ case qh_PRINTcentrums:+ qh_printcenter(qh, fp, format, NULL, facet);+ break;+ case qh_PRINTfacets:+ qh_printfacet(qh, fp, facet);+ break;+ case qh_PRINTfacets_xridge:+ qh_printfacetheader(qh, fp, facet);+ break;+ case qh_PRINTgeom: /* either 2 , 3, or 4-d by qh_printbegin */+ if (!facet->normal)+ break;+ for (k=qh->hull_dim; k--; ) {+ color[k]= (facet->normal[k]+1.0)/2.0;+ maximize_(color[k], -1.0);+ minimize_(color[k], +1.0);+ }+ qh_projectdim3(qh, color, color);+ if (qh->PRINTdim != qh->hull_dim)+ qh_normalize2(qh, color, 3, True, NULL, NULL);+ if (qh->hull_dim <= 2)+ qh_printfacet2geom(qh, fp, facet, color);+ else if (qh->hull_dim == 3) {+ if (facet->simplicial)+ qh_printfacet3geom_simplicial(qh, fp, facet, color);+ else+ qh_printfacet3geom_nonsimplicial(qh, fp, facet, color);+ }else {+ if (facet->simplicial)+ qh_printfacet4geom_simplicial(qh, fp, facet, color);+ else+ qh_printfacet4geom_nonsimplicial(qh, fp, facet, color);+ }+ break;+ case qh_PRINTids:+ qh_fprintf(qh, fp, 9015, "%d\n", facet->id);+ break;+ case qh_PRINTincidences:+ case qh_PRINToff:+ case qh_PRINTtriangles:+ if (qh->hull_dim == 3 && format != qh_PRINTtriangles)+ qh_printfacet3vertex(qh, fp, facet, format);+ else if (facet->simplicial || qh->hull_dim == 2 || format == qh_PRINToff)+ qh_printfacetNvertex_simplicial(qh, fp, facet, format);+ else+ qh_printfacetNvertex_nonsimplicial(qh, fp, facet, qh->printoutvar++, format);+ break;+ case qh_PRINTinner:+ qh_outerinner(qh, facet, NULL, &innerplane);+ offset= facet->offset - innerplane;+ goto LABELprintnorm;+ break; /* prevent warning */+ case qh_PRINTmerges:+ qh_fprintf(qh, fp, 9016, "%d\n", facet->nummerge);+ break;+ case qh_PRINTnormals:+ offset= facet->offset;+ goto LABELprintnorm;+ break; /* prevent warning */+ case qh_PRINTouter:+ qh_outerinner(qh, facet, &outerplane, NULL);+ offset= facet->offset - outerplane;+ LABELprintnorm:+ if (!facet->normal) {+ qh_fprintf(qh, fp, 9017, "no normal for facet f%d\n", facet->id);+ break;+ }+ if (qh->CDDoutput) {+ qh_fprintf(qh, fp, 9018, qh_REAL_1, -offset);+ for (k=0; k < qh->hull_dim; k++)+ qh_fprintf(qh, fp, 9019, qh_REAL_1, -facet->normal[k]);+ }else {+ for (k=0; k < qh->hull_dim; k++)+ qh_fprintf(qh, fp, 9020, qh_REAL_1, facet->normal[k]);+ qh_fprintf(qh, fp, 9021, qh_REAL_1, offset);+ }+ qh_fprintf(qh, fp, 9022, "\n");+ break;+ case qh_PRINTmathematica: /* either 2 or 3-d by qh_printbegin */+ case qh_PRINTmaple:+ if (qh->hull_dim == 2)+ qh_printfacet2math(qh, fp, facet, format, qh->printoutvar++);+ else+ qh_printfacet3math(qh, fp, facet, format, qh->printoutvar++);+ break;+ case qh_PRINTneighbors:+ qh_fprintf(qh, fp, 9023, "%d", qh_setsize(qh, facet->neighbors));+ FOREACHneighbor_(facet)+ qh_fprintf(qh, fp, 9024, " %d",+ neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);+ qh_fprintf(qh, fp, 9025, "\n");+ break;+ case qh_PRINTpointintersect:+ if (!qh->feasible_point) {+ qh_fprintf(qh, qh->ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh->feasible_point\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (facet->offset > 0)+ goto LABELprintinfinite;+ point= coordp= (coordT*)qh_memalloc(qh, qh->normal_size);+ normp= facet->normal;+ feasiblep= qh->feasible_point;+ if (facet->offset < -qh->MINdenom) {+ for (k=qh->hull_dim; k--; )+ *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++);+ }else {+ for (k=qh->hull_dim; k--; ) {+ *(coordp++)= qh_divzero(*(normp++), facet->offset, qh->MINdenom_1,+ &zerodiv) + *(feasiblep++);+ if (zerodiv) {+ qh_memfree(qh, point, qh->normal_size);+ goto LABELprintinfinite;+ }+ }+ }+ qh_printpoint(qh, fp, NULL, point);+ qh_memfree(qh, point, qh->normal_size);+ break;+ LABELprintinfinite:+ for (k=qh->hull_dim; k--; )+ qh_fprintf(qh, fp, 9026, qh_REAL_1, qh_INFINITE);+ qh_fprintf(qh, fp, 9027, "\n");+ break;+ case qh_PRINTpointnearest:+ FOREACHpoint_(facet->coplanarset) {+ int id, id2;+ vertex= qh_nearvertex(qh, facet, point, &dist);+ id= qh_pointid(qh, vertex->point);+ id2= qh_pointid(qh, point);+ qh_fprintf(qh, fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist);+ }+ break;+ case qh_PRINTpoints: /* VORONOI only by qh_printbegin */+ if (qh->CDDoutput)+ qh_fprintf(qh, fp, 9029, "1 ");+ qh_printcenter(qh, fp, format, NULL, facet);+ break;+ case qh_PRINTvertices:+ qh_fprintf(qh, fp, 9030, "%d", qh_setsize(qh, facet->vertices));+ FOREACHvertex_(facet->vertices)+ qh_fprintf(qh, fp, 9031, " %d", qh_pointid(qh, vertex->point));+ qh_fprintf(qh, fp, 9032, "\n");+ break;+ default:+ break;+ }+} /* printafacet */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printbegin">-</a>++ qh_printbegin(qh, )+ prints header for all output formats++ returns:+ checks for valid format++ notes:+ uses qh.visit_id for 3/4off+ changes qh.interior_point if printing centrums+ qh_countfacets clears facet->visitid for non-good facets++ see+ qh_printend() and qh_printafacet()++ design:+ count facets and related statistics+ print header for format+*/+void qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {+ int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;+ int i, num;+ facetT *facet, **facetp;+ vertexT *vertex, **vertexp;+ setT *vertices;+ pointT *point, **pointp, *pointtemp;++ qh->printoutnum= 0;+ qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars);+ switch (format) {+ case qh_PRINTnone:+ break;+ case qh_PRINTarea:+ qh_fprintf(qh, fp, 9033, "%d\n", numfacets);+ break;+ case qh_PRINTcoplanars:+ qh_fprintf(qh, fp, 9034, "%d\n", numfacets);+ break;+ case qh_PRINTcentrums:+ if (qh->CENTERtype == qh_ASnone)+ qh_clearcenters(qh, qh_AScentrum);+ qh_fprintf(qh, fp, 9035, "%d\n%d\n", qh->hull_dim, numfacets);+ break;+ case qh_PRINTfacets:+ case qh_PRINTfacets_xridge:+ if (facetlist)+ qh_printvertexlist(qh, fp, "Vertices and facets:\n", facetlist, facets, printall);+ break;+ case qh_PRINTgeom:+ if (qh->hull_dim > 4) /* qh_initqhull_globals also checks */+ goto LABELnoformat;+ if (qh->VORONOI && qh->hull_dim > 3) /* PRINTdim == DROPdim == hull_dim-1 */+ goto LABELnoformat;+ if (qh->hull_dim == 2 && (qh->PRINTridges || qh->DOintersections))+ qh_fprintf(qh, qh->ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n");+ if (qh->hull_dim == 4 && (qh->PRINTinner || qh->PRINTouter ||+ (qh->PRINTdim == 4 && qh->PRINTcentrums)))+ qh_fprintf(qh, qh->ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n");+ if (qh->PRINTdim == 4 && (qh->PRINTspheres))+ qh_fprintf(qh, qh->ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");+ if (qh->PRINTdim == 4 && qh->DOintersections && qh->PRINTnoplanes)+ qh_fprintf(qh, qh->ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");+ if (qh->PRINTdim == 2) {+ qh_fprintf(qh, fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",+ qh->rbox_command, qh->qhull_command);+ }else if (qh->PRINTdim == 3) {+ qh_fprintf(qh, fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n",+ qh->rbox_command, qh->qhull_command);+ }else if (qh->PRINTdim == 4) {+ qh->visit_id++;+ num= 0;+ FORALLfacet_(facetlist) /* get number of ridges to be printed */+ qh_printend4geom(qh, NULL, facet, &num, printall);+ FOREACHfacet_(facets)+ qh_printend4geom(qh, NULL, facet, &num, printall);+ qh->ridgeoutnum= num;+ qh->printoutvar= 0; /* counts number of ridges in output */+ qh_fprintf(qh, fp, 9038, "LIST # %s | %s\n", qh->rbox_command, qh->qhull_command);+ }++ if (qh->PRINTdots) {+ qh->printoutnum++;+ num= qh->num_points + qh_setsize(qh, qh->other_points);+ if (qh->DELAUNAY && qh->ATinfinity)+ num--;+ if (qh->PRINTdim == 4)+ qh_fprintf(qh, fp, 9039, "4VECT %d %d 1\n", num, num);+ else+ qh_fprintf(qh, fp, 9040, "VECT %d %d 1\n", num, num);++ for (i=num; i--; ) {+ if (i % 20 == 0)+ qh_fprintf(qh, fp, 9041, "\n");+ qh_fprintf(qh, fp, 9042, "1 ");+ }+ qh_fprintf(qh, fp, 9043, "# 1 point per line\n1 ");+ for (i=num-1; i--; ) { /* num at least 3 for D2 */+ if (i % 20 == 0)+ qh_fprintf(qh, fp, 9044, "\n");+ qh_fprintf(qh, fp, 9045, "0 ");+ }+ qh_fprintf(qh, fp, 9046, "# 1 color for all\n");+ FORALLpoints {+ if (!qh->DELAUNAY || !qh->ATinfinity || qh_pointid(qh, point) != qh->num_points-1) {+ if (qh->PRINTdim == 4)+ qh_printpoint(qh, fp, NULL, point);+ else+ qh_printpoint3(qh, fp, point);+ }+ }+ FOREACHpoint_(qh->other_points) {+ if (qh->PRINTdim == 4)+ qh_printpoint(qh, fp, NULL, point);+ else+ qh_printpoint3(qh, fp, point);+ }+ qh_fprintf(qh, fp, 9047, "0 1 1 1 # color of points\n");+ }++ if (qh->PRINTdim == 4 && !qh->PRINTnoplanes)+ /* 4dview loads up multiple 4OFF objects slowly */+ qh_fprintf(qh, fp, 9048, "4OFF %d %d 1\n", 3*qh->ridgeoutnum, qh->ridgeoutnum);+ qh->PRINTcradius= 2 * qh->DISTround; /* include test DISTround */+ if (qh->PREmerge) {+ maximize_(qh->PRINTcradius, qh->premerge_centrum + qh->DISTround);+ }else if (qh->POSTmerge)+ maximize_(qh->PRINTcradius, qh->postmerge_centrum + qh->DISTround);+ qh->PRINTradius= qh->PRINTcradius;+ if (qh->PRINTspheres + qh->PRINTcoplanar)+ maximize_(qh->PRINTradius, qh->MAXabs_coord * qh_MINradius);+ if (qh->premerge_cos < REALmax/2) {+ maximize_(qh->PRINTradius, (1- qh->premerge_cos) * qh->MAXabs_coord);+ }else if (!qh->PREmerge && qh->POSTmerge && qh->postmerge_cos < REALmax/2) {+ maximize_(qh->PRINTradius, (1- qh->postmerge_cos) * qh->MAXabs_coord);+ }+ maximize_(qh->PRINTradius, qh->MINvisible);+ if (qh->JOGGLEmax < REALmax/2)+ qh->PRINTradius += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);+ if (qh->PRINTdim != 4 &&+ (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {+ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ if (qh->PRINTspheres && qh->PRINTdim <= 3)+ qh_printspheres(qh, fp, vertices, qh->PRINTradius);+ if (qh->PRINTcoplanar || qh->PRINTcentrums) {+ qh->firstcentrum= True;+ if (qh->PRINTcoplanar&& !qh->PRINTspheres) {+ FOREACHvertex_(vertices)+ qh_printpointvect2(qh, fp, vertex->point, NULL, qh->interior_point, qh->PRINTradius);+ }+ FORALLfacet_(facetlist) {+ if (!printall && qh_skipfacet(qh, facet))+ continue;+ if (!facet->normal)+ continue;+ if (qh->PRINTcentrums && qh->PRINTdim <= 3)+ qh_printcentrum(qh, fp, facet, qh->PRINTcradius);+ if (!qh->PRINTcoplanar)+ continue;+ FOREACHpoint_(facet->coplanarset)+ qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);+ FOREACHpoint_(facet->outsideset)+ qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);+ }+ FOREACHfacet_(facets) {+ if (!printall && qh_skipfacet(qh, facet))+ continue;+ if (!facet->normal)+ continue;+ if (qh->PRINTcentrums && qh->PRINTdim <= 3)+ qh_printcentrum(qh, fp, facet, qh->PRINTcradius);+ if (!qh->PRINTcoplanar)+ continue;+ FOREACHpoint_(facet->coplanarset)+ qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);+ FOREACHpoint_(facet->outsideset)+ qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);+ }+ }+ qh_settempfree(qh, &vertices);+ }+ qh->visit_id++; /* for printing hyperplane intersections */+ break;+ case qh_PRINTids:+ qh_fprintf(qh, fp, 9049, "%d\n", numfacets);+ break;+ case qh_PRINTincidences:+ if (qh->VORONOI && qh->PRINTprecision)+ qh_fprintf(qh, qh->ferr, 7053, "qhull warning: writing Delaunay. Use 'p' or 'o' for Voronoi centers\n");+ qh->printoutvar= qh->vertex_id; /* centrum id for non-simplicial facets */+ if (qh->hull_dim <= 3)+ qh_fprintf(qh, fp, 9050, "%d\n", numfacets);+ else+ qh_fprintf(qh, fp, 9051, "%d\n", numsimplicial+numridges);+ break;+ case qh_PRINTinner:+ case qh_PRINTnormals:+ case qh_PRINTouter:+ if (qh->CDDoutput)+ qh_fprintf(qh, fp, 9052, "%s | %s\nbegin\n %d %d real\n", qh->rbox_command,+ qh->qhull_command, numfacets, qh->hull_dim+1);+ else+ qh_fprintf(qh, fp, 9053, "%d\n%d\n", qh->hull_dim+1, numfacets);+ break;+ case qh_PRINTmathematica:+ case qh_PRINTmaple:+ if (qh->hull_dim > 3) /* qh_initbuffers also checks */+ goto LABELnoformat;+ if (qh->VORONOI)+ qh_fprintf(qh, qh->ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");+ if (format == qh_PRINTmaple) {+ if (qh->hull_dim == 2)+ qh_fprintf(qh, fp, 9054, "PLOT(CURVES(\n");+ else+ qh_fprintf(qh, fp, 9055, "PLOT3D(POLYGONS(\n");+ }else+ qh_fprintf(qh, fp, 9056, "{\n");+ qh->printoutvar= 0; /* counts number of facets for notfirst */+ break;+ case qh_PRINTmerges:+ qh_fprintf(qh, fp, 9057, "%d\n", numfacets);+ break;+ case qh_PRINTpointintersect:+ qh_fprintf(qh, fp, 9058, "%d\n%d\n", qh->hull_dim, numfacets);+ break;+ case qh_PRINTneighbors:+ qh_fprintf(qh, fp, 9059, "%d\n", numfacets);+ break;+ case qh_PRINToff:+ case qh_PRINTtriangles:+ if (qh->VORONOI)+ goto LABELnoformat;+ num = qh->hull_dim;+ if (format == qh_PRINToff || qh->hull_dim == 2)+ qh_fprintf(qh, fp, 9060, "%d\n%d %d %d\n", num,+ qh->num_points+qh_setsize(qh, qh->other_points), numfacets, totneighbors/2);+ else { /* qh_PRINTtriangles */+ qh->printoutvar= qh->num_points+qh_setsize(qh, qh->other_points); /* first centrum */+ if (qh->DELAUNAY)+ num--; /* drop last dimension */+ qh_fprintf(qh, fp, 9061, "%d\n%d %d %d\n", num, qh->printoutvar+ + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);+ }+ FORALLpoints+ qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);+ FOREACHpoint_(qh->other_points)+ qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);+ if (format == qh_PRINTtriangles && qh->hull_dim > 2) {+ FORALLfacets {+ if (!facet->simplicial && facet->visitid)+ qh_printcenter(qh, qh->fout, format, NULL, facet);+ }+ }+ break;+ case qh_PRINTpointnearest:+ qh_fprintf(qh, fp, 9062, "%d\n", numcoplanars);+ break;+ case qh_PRINTpoints:+ if (!qh->VORONOI)+ goto LABELnoformat;+ if (qh->CDDoutput)+ qh_fprintf(qh, fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,+ qh->qhull_command, numfacets, qh->hull_dim);+ else+ qh_fprintf(qh, fp, 9064, "%d\n%d\n", qh->hull_dim-1, numfacets);+ break;+ case qh_PRINTvertices:+ qh_fprintf(qh, fp, 9065, "%d\n", numfacets);+ break;+ case qh_PRINTsummary:+ default:+ LABELnoformat:+ qh_fprintf(qh, qh->ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",+ qh->hull_dim);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+} /* printbegin */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printcenter">-</a>++ qh_printcenter(qh, fp, string, facet )+ print facet->center as centrum or Voronoi center+ string may be NULL. Don't include '%' codes.+ nop if qh->CENTERtype neither CENTERvoronoi nor CENTERcentrum+ if upper envelope of Delaunay triangulation and point at-infinity+ prints qh_INFINITE instead;++ notes:+ defines facet->center if needed+ if format=PRINTgeom, adds a 0 if would otherwise be 2-d+ Same as QhullFacet::printCenter+*/+void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet) {+ int k, num;++ if (qh->CENTERtype != qh_ASvoronoi && qh->CENTERtype != qh_AScentrum)+ return;+ if (string)+ qh_fprintf(qh, fp, 9066, string);+ if (qh->CENTERtype == qh_ASvoronoi) {+ num= qh->hull_dim-1;+ if (!facet->normal || !facet->upperdelaunay || !qh->ATinfinity) {+ if (!facet->center)+ facet->center= qh_facetcenter(qh, facet->vertices);+ for (k=0; k < num; k++)+ qh_fprintf(qh, fp, 9067, qh_REAL_1, facet->center[k]);+ }else {+ for (k=0; k < num; k++)+ qh_fprintf(qh, fp, 9068, qh_REAL_1, qh_INFINITE);+ }+ }else /* qh->CENTERtype == qh_AScentrum */ {+ num= qh->hull_dim;+ if (format == qh_PRINTtriangles && qh->DELAUNAY)+ num--;+ if (!facet->center)+ facet->center= qh_getcentrum(qh, facet);+ for (k=0; k < num; k++)+ qh_fprintf(qh, fp, 9069, qh_REAL_1, facet->center[k]);+ }+ if (format == qh_PRINTgeom && num == 2)+ qh_fprintf(qh, fp, 9070, " 0\n");+ else+ qh_fprintf(qh, fp, 9071, "\n");+} /* printcenter */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printcentrum">-</a>++ qh_printcentrum(qh, fp, facet, radius )+ print centrum for a facet in OOGL format+ radius defines size of centrum+ 2-d or 3-d only++ returns:+ defines facet->center if needed+*/+void qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius) {+ pointT *centrum, *projpt;+ boolT tempcentrum= False;+ realT xaxis[4], yaxis[4], normal[4], dist;+ realT green[3]={0, 1, 0};+ vertexT *apex;+ int k;++ if (qh->CENTERtype == qh_AScentrum) {+ if (!facet->center)+ facet->center= qh_getcentrum(qh, facet);+ centrum= facet->center;+ }else {+ centrum= qh_getcentrum(qh, facet);+ tempcentrum= True;+ }+ qh_fprintf(qh, fp, 9072, "{appearance {-normal -edge normscale 0} ");+ if (qh->firstcentrum) {+ qh->firstcentrum= False;+ qh_fprintf(qh, fp, 9073, "{INST geom { define centrum CQUAD # f%d\n\+-0.3 -0.3 0.0001 0 0 1 1\n\+ 0.3 -0.3 0.0001 0 0 1 1\n\+ 0.3 0.3 0.0001 0 0 1 1\n\+-0.3 0.3 0.0001 0 0 1 1 } transform { \n", facet->id);+ }else+ qh_fprintf(qh, fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);+ apex= SETfirstt_(facet->vertices, vertexT);+ qh_distplane(qh, apex->point, facet, &dist);+ projpt= qh_projectpoint(qh, apex->point, facet, dist);+ for (k=qh->hull_dim; k--; ) {+ xaxis[k]= projpt[k] - centrum[k];+ normal[k]= facet->normal[k];+ }+ if (qh->hull_dim == 2) {+ xaxis[2]= 0;+ normal[2]= 0;+ }else if (qh->hull_dim == 4) {+ qh_projectdim3(qh, xaxis, xaxis);+ qh_projectdim3(qh, normal, normal);+ qh_normalize2(qh, normal, qh->PRINTdim, True, NULL, NULL);+ }+ qh_crossproduct(3, xaxis, normal, yaxis);+ qh_fprintf(qh, fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);+ qh_fprintf(qh, fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);+ qh_fprintf(qh, fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);+ qh_printpoint3(qh, fp, centrum);+ qh_fprintf(qh, fp, 9078, "1 }}}\n");+ qh_memfree(qh, projpt, qh->normal_size);+ qh_printpointvect(qh, fp, centrum, facet->normal, NULL, radius, green);+ if (tempcentrum)+ qh_memfree(qh, centrum, qh->normal_size);+} /* printcentrum */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printend">-</a>++ qh_printend(qh, fp, format )+ prints trailer for all output formats++ see:+ qh_printbegin() and qh_printafacet()++*/+void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {+ int num;+ facetT *facet, **facetp;++ if (!qh->printoutnum)+ qh_fprintf(qh, qh->ferr, 7055, "qhull warning: no facets printed\n");+ switch (format) {+ case qh_PRINTgeom:+ if (qh->hull_dim == 4 && qh->DROPdim < 0 && !qh->PRINTnoplanes) {+ qh->visit_id++;+ num= 0;+ FORALLfacet_(facetlist)+ qh_printend4geom(qh, fp, facet,&num, printall);+ FOREACHfacet_(facets)+ qh_printend4geom(qh, fp, facet, &num, printall);+ if (num != qh->ridgeoutnum || qh->printoutvar != qh->ridgeoutnum) {+ qh_fprintf(qh, qh->ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh->ridgeoutnum, qh->printoutvar, num);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ }else+ qh_fprintf(qh, fp, 9079, "}\n");+ break;+ case qh_PRINTinner:+ case qh_PRINTnormals:+ case qh_PRINTouter:+ if (qh->CDDoutput)+ qh_fprintf(qh, fp, 9080, "end\n");+ break;+ case qh_PRINTmaple:+ qh_fprintf(qh, fp, 9081, "));\n");+ break;+ case qh_PRINTmathematica:+ qh_fprintf(qh, fp, 9082, "}\n");+ break;+ case qh_PRINTpoints:+ if (qh->CDDoutput)+ qh_fprintf(qh, fp, 9083, "end\n");+ break;+ default:+ break;+ }+} /* printend */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printend4geom">-</a>++ qh_printend4geom(qh, fp, facet, numridges, printall )+ helper function for qh_printbegin/printend++ returns:+ number of printed ridges++ notes:+ just counts printed ridges if fp=NULL+ uses facet->visitid+ must agree with qh_printfacet4geom...++ design:+ computes color for facet from its normal+ prints each ridge of facet+*/+void qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *nump, boolT printall) {+ realT color[3];+ int i, num= *nump;+ facetT *neighbor, **neighborp;+ ridgeT *ridge, **ridgep;++ if (!printall && qh_skipfacet(qh, facet))+ return;+ if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))+ return;+ if (!facet->normal)+ return;+ if (fp) {+ for (i=0; i < 3; i++) {+ color[i]= (facet->normal[i]+1.0)/2.0;+ maximize_(color[i], -1.0);+ minimize_(color[i], +1.0);+ }+ }+ facet->visitid= qh->visit_id;+ if (facet->simplicial) {+ FOREACHneighbor_(facet) {+ if (neighbor->visitid != qh->visit_id) {+ if (fp)+ qh_fprintf(qh, fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n",+ 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],+ facet->id, neighbor->id);+ num++;+ }+ }+ }else {+ FOREACHridge_(facet->ridges) {+ neighbor= otherfacet_(ridge, facet);+ if (neighbor->visitid != qh->visit_id) {+ if (fp)+ qh_fprintf(qh, fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n",+ 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],+ ridge->id, facet->id, neighbor->id);+ num++;+ }+ }+ }+ *nump= num;+} /* printend4geom */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printextremes">-</a>++ qh_printextremes(qh, fp, facetlist, facets, printall )+ print extreme points for convex hulls or halfspace intersections++ notes:+ #points, followed by ids, one per line++ sorted by id+ same order as qh_printpoints_out if no coplanar/interior points+*/+void qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {+ setT *vertices, *points;+ pointT *point;+ vertexT *vertex, **vertexp;+ int id;+ int numpoints=0, point_i, point_n;+ int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);++ points= qh_settemp(qh, allpoints);+ qh_setzero(qh, points, 0, allpoints);+ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ FOREACHvertex_(vertices) {+ id= qh_pointid(qh, vertex->point);+ if (id >= 0) {+ SETelem_(points, id)= vertex->point;+ numpoints++;+ }+ }+ qh_settempfree(qh, &vertices);+ qh_fprintf(qh, fp, 9086, "%d\n", numpoints);+ FOREACHpoint_i_(qh, points) {+ if (point)+ qh_fprintf(qh, fp, 9087, "%d\n", point_i);+ }+ qh_settempfree(qh, &points);+} /* printextremes */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printextremes_2d">-</a>++ qh_printextremes_2d(qh, fp, facetlist, facets, printall )+ prints point ids for facets in qh_ORIENTclock order++ notes:+ #points, followed by ids, one per line+ if facetlist/facets are disjoint than the output includes skips+ errors if facets form a loop+ does not print coplanar points+*/+void qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {+ int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars;+ setT *vertices;+ facetT *facet, *startfacet, *nextfacet;+ vertexT *vertexA, *vertexB;++ qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh->visit_id */+ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ qh_fprintf(qh, fp, 9088, "%d\n", qh_setsize(qh, vertices));+ qh_settempfree(qh, &vertices);+ if (!numfacets)+ return;+ facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT);+ qh->vertex_visit++;+ qh->visit_id++;+ do {+ if (facet->toporient ^ qh_ORIENTclock) {+ vertexA= SETfirstt_(facet->vertices, vertexT);+ vertexB= SETsecondt_(facet->vertices, vertexT);+ nextfacet= SETfirstt_(facet->neighbors, facetT);+ }else {+ vertexA= SETsecondt_(facet->vertices, vertexT);+ vertexB= SETfirstt_(facet->vertices, vertexT);+ nextfacet= SETsecondt_(facet->neighbors, facetT);+ }+ if (facet->visitid == qh->visit_id) {+ qh_fprintf(qh, qh->ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n",+ facet->id, nextfacet->id);+ qh_errexit2(qh, qh_ERRqhull, facet, nextfacet);+ }+ if (facet->visitid) {+ if (vertexA->visitid != qh->vertex_visit) {+ vertexA->visitid= qh->vertex_visit;+ qh_fprintf(qh, fp, 9089, "%d\n", qh_pointid(qh, vertexA->point));+ }+ if (vertexB->visitid != qh->vertex_visit) {+ vertexB->visitid= qh->vertex_visit;+ qh_fprintf(qh, fp, 9090, "%d\n", qh_pointid(qh, vertexB->point));+ }+ }+ facet->visitid= qh->visit_id;+ facet= nextfacet;+ }while (facet && facet != startfacet);+} /* printextremes_2d */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printextremes_d">-</a>++ qh_printextremes_d(qh, fp, facetlist, facets, printall )+ print extreme points of input sites for Delaunay triangulations++ notes:+ #points, followed by ids, one per line++ unordered+*/+void qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {+ setT *vertices;+ vertexT *vertex, **vertexp;+ boolT upperseen, lowerseen;+ facetT *neighbor, **neighborp;+ int numpoints=0;++ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ qh_vertexneighbors(qh);+ FOREACHvertex_(vertices) {+ upperseen= lowerseen= False;+ FOREACHneighbor_(vertex) {+ if (neighbor->upperdelaunay)+ upperseen= True;+ else+ lowerseen= True;+ }+ if (upperseen && lowerseen) {+ vertex->seen= True;+ numpoints++;+ }else+ vertex->seen= False;+ }+ qh_fprintf(qh, fp, 9091, "%d\n", numpoints);+ FOREACHvertex_(vertices) {+ if (vertex->seen)+ qh_fprintf(qh, fp, 9092, "%d\n", qh_pointid(qh, vertex->point));+ }+ qh_settempfree(qh, &vertices);+} /* printextremes_d */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet">-</a>++ qh_printfacet(qh, fp, facet )+ prints all fields of a facet to fp++ notes:+ ridges printed in neighbor order+*/+void qh_printfacet(qhT *qh, FILE *fp, facetT *facet) {++ qh_printfacetheader(qh, fp, facet);+ if (facet->ridges)+ qh_printfacetridges(qh, fp, facet);+} /* printfacet */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet2geom">-</a>++ qh_printfacet2geom(qh, fp, facet, color )+ print facet as part of a 2-d VECT for Geomview++ notes:+ assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon+ mindist is calculated within io_r.c. maxoutside is calculated elsewhere+ so a DISTround error may have occurred.+*/+void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {+ pointT *point0, *point1;+ realT mindist, innerplane, outerplane;+ int k;++ qh_facet2point(qh, facet, &point0, &point1, &mindist);+ qh_geomplanes(qh, facet, &outerplane, &innerplane);+ if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))+ qh_printfacet2geom_points(qh, fp, point0, point1, facet, outerplane, color);+ if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&+ outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {+ for (k=3; k--; )+ color[k]= 1.0 - color[k];+ qh_printfacet2geom_points(qh, fp, point0, point1, facet, innerplane, color);+ }+ qh_memfree(qh, point1, qh->normal_size);+ qh_memfree(qh, point0, qh->normal_size);+} /* printfacet2geom */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet2geom_points">-</a>++ qh_printfacet2geom_points(qh, fp, point1, point2, facet, offset, color )+ prints a 2-d facet as a VECT with 2 points at some offset.+ The points are on the facet's plane.+*/+void qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2,+ facetT *facet, realT offset, realT color[3]) {+ pointT *p1= point1, *p2= point2;++ qh_fprintf(qh, fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);+ if (offset != 0.0) {+ p1= qh_projectpoint(qh, p1, facet, -offset);+ p2= qh_projectpoint(qh, p2, facet, -offset);+ }+ qh_fprintf(qh, fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n",+ p1[0], p1[1], 0.0, p2[0], p2[1], 0.0);+ if (offset != 0.0) {+ qh_memfree(qh, p1, qh->normal_size);+ qh_memfree(qh, p2, qh->normal_size);+ }+ qh_fprintf(qh, fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);+} /* printfacet2geom_points */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet2math">-</a>++ qh_printfacet2math(qh, fp, facet, format, notfirst )+ print 2-d Maple or Mathematica output for a facet+ may be non-simplicial++ notes:+ use %16.8f since Mathematica 2.2 does not handle exponential format+ see qh_printfacet3math+*/+void qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {+ pointT *point0, *point1;+ realT mindist;+ const char *pointfmt;++ qh_facet2point(qh, facet, &point0, &point1, &mindist);+ if (notfirst)+ qh_fprintf(qh, fp, 9096, ",");+ if (format == qh_PRINTmaple)+ pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n";+ else+ pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n";+ qh_fprintf(qh, fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);+ qh_memfree(qh, point1, qh->normal_size);+ qh_memfree(qh, point0, qh->normal_size);+} /* printfacet2math */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a>++ qh_printfacet3geom_nonsimplicial(qh, fp, facet, color )+ print Geomview OFF for a 3-d nonsimplicial facet.+ if DOintersections, prints ridges to unvisited neighbors(qh->visit_id)++ notes+ uses facet->visitid for intersections and ridges+*/+void qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {+ ridgeT *ridge, **ridgep;+ setT *projectedpoints, *vertices;+ vertexT *vertex, **vertexp, *vertexA, *vertexB;+ pointT *projpt, *point, **pointp;+ facetT *neighbor;+ realT dist, outerplane, innerplane;+ int cntvertices, k;+ realT black[3]={0, 0, 0}, green[3]={0, 1, 0};++ qh_geomplanes(qh, facet, &outerplane, &innerplane);+ vertices= qh_facet3vertex(qh, facet); /* oriented */+ cntvertices= qh_setsize(qh, vertices);+ projectedpoints= qh_settemp(qh, cntvertices);+ FOREACHvertex_(vertices) {+ zinc_(Zdistio);+ qh_distplane(qh, vertex->point, facet, &dist);+ projpt= qh_projectpoint(qh, vertex->point, facet, dist);+ qh_setappend(qh, &projectedpoints, projpt);+ }+ if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))+ qh_printfacet3geom_points(qh, fp, projectedpoints, facet, outerplane, color);+ if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&+ outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {+ for (k=3; k--; )+ color[k]= 1.0 - color[k];+ qh_printfacet3geom_points(qh, fp, projectedpoints, facet, innerplane, color);+ }+ FOREACHpoint_(projectedpoints)+ qh_memfree(qh, point, qh->normal_size);+ qh_settempfree(qh, &projectedpoints);+ qh_settempfree(qh, &vertices);+ if ((qh->DOintersections || qh->PRINTridges)+ && (!facet->visible || !qh->NEWfacets)) {+ facet->visitid= qh->visit_id;+ FOREACHridge_(facet->ridges) {+ neighbor= otherfacet_(ridge, facet);+ if (neighbor->visitid != qh->visit_id) {+ if (qh->DOintersections)+ qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, black);+ if (qh->PRINTridges) {+ vertexA= SETfirstt_(ridge->vertices, vertexT);+ vertexB= SETsecondt_(ridge->vertices, vertexT);+ qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);+ }+ }+ }+ }+} /* printfacet3geom_nonsimplicial */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet3geom_points">-</a>++ qh_printfacet3geom_points(qh, fp, points, facet, offset )+ prints a 3-d facet as OFF Geomview object.+ offset is relative to the facet's hyperplane+ Facet is determined as a list of points+*/+void qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {+ int k, n= qh_setsize(qh, points), i;+ pointT *point, **pointp;+ setT *printpoints;++ qh_fprintf(qh, fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);+ if (offset != 0.0) {+ printpoints= qh_settemp(qh, n);+ FOREACHpoint_(points)+ qh_setappend(qh, &printpoints, qh_projectpoint(qh, point, facet, -offset));+ }else+ printpoints= points;+ FOREACHpoint_(printpoints) {+ for (k=0; k < qh->hull_dim; k++) {+ if (k == qh->DROPdim)+ qh_fprintf(qh, fp, 9099, "0 ");+ else+ qh_fprintf(qh, fp, 9100, "%8.4g ", point[k]);+ }+ if (printpoints != points)+ qh_memfree(qh, point, qh->normal_size);+ qh_fprintf(qh, fp, 9101, "\n");+ }+ if (printpoints != points)+ qh_settempfree(qh, &printpoints);+ qh_fprintf(qh, fp, 9102, "%d ", n);+ for (i=0; i < n; i++)+ qh_fprintf(qh, fp, 9103, "%d ", i);+ qh_fprintf(qh, fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);+} /* printfacet3geom_points */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet3geom_simplicial">-</a>++ qh_printfacet3geom_simplicial(qh, )+ print Geomview OFF for a 3-d simplicial facet.++ notes:+ may flip color+ uses facet->visitid for intersections and ridges++ assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon+ innerplane may be off by qh->DISTround. Maxoutside is calculated elsewhere+ so a DISTround error may have occurred.+*/+void qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {+ setT *points, *vertices;+ vertexT *vertex, **vertexp, *vertexA, *vertexB;+ facetT *neighbor, **neighborp;+ realT outerplane, innerplane;+ realT black[3]={0, 0, 0}, green[3]={0, 1, 0};+ int k;++ qh_geomplanes(qh, facet, &outerplane, &innerplane);+ vertices= qh_facet3vertex(qh, facet);+ points= qh_settemp(qh, qh->TEMPsize);+ FOREACHvertex_(vertices)+ qh_setappend(qh, &points, vertex->point);+ if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))+ qh_printfacet3geom_points(qh, fp, points, facet, outerplane, color);+ if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&+ outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {+ for (k=3; k--; )+ color[k]= 1.0 - color[k];+ qh_printfacet3geom_points(qh, fp, points, facet, innerplane, color);+ }+ qh_settempfree(qh, &points);+ qh_settempfree(qh, &vertices);+ if ((qh->DOintersections || qh->PRINTridges)+ && (!facet->visible || !qh->NEWfacets)) {+ facet->visitid= qh->visit_id;+ FOREACHneighbor_(facet) {+ if (neighbor->visitid != qh->visit_id) {+ vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,+ SETindex_(facet->neighbors, neighbor), 0);+ if (qh->DOintersections)+ qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, black);+ if (qh->PRINTridges) {+ vertexA= SETfirstt_(vertices, vertexT);+ vertexB= SETsecondt_(vertices, vertexT);+ qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);+ }+ qh_setfree(qh, &vertices);+ }+ }+ }+} /* printfacet3geom_simplicial */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet3math">-</a>++ qh_printfacet3math(qh, fp, facet, notfirst )+ print 3-d Maple or Mathematica output for a facet++ notes:+ may be non-simplicial+ use %16.8f since Mathematica 2.2 does not handle exponential format+ see qh_printfacet2math+*/+void qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {+ vertexT *vertex, **vertexp;+ setT *points, *vertices;+ pointT *point, **pointp;+ boolT firstpoint= True;+ realT dist;+ const char *pointfmt, *endfmt;++ if (notfirst)+ qh_fprintf(qh, fp, 9105, ",\n");+ vertices= qh_facet3vertex(qh, facet);+ points= qh_settemp(qh, qh_setsize(qh, vertices));+ FOREACHvertex_(vertices) {+ zinc_(Zdistio);+ qh_distplane(qh, vertex->point, facet, &dist);+ point= qh_projectpoint(qh, vertex->point, facet, dist);+ qh_setappend(qh, &points, point);+ }+ if (format == qh_PRINTmaple) {+ qh_fprintf(qh, fp, 9106, "[");+ pointfmt= "[%16.8f, %16.8f, %16.8f]";+ endfmt= "]";+ }else {+ qh_fprintf(qh, fp, 9107, "Polygon[{");+ pointfmt= "{%16.8f, %16.8f, %16.8f}";+ endfmt= "}]";+ }+ FOREACHpoint_(points) {+ if (firstpoint)+ firstpoint= False;+ else+ qh_fprintf(qh, fp, 9108, ",\n");+ qh_fprintf(qh, fp, 9109, pointfmt, point[0], point[1], point[2]);+ }+ FOREACHpoint_(points)+ qh_memfree(qh, point, qh->normal_size);+ qh_settempfree(qh, &points);+ qh_settempfree(qh, &vertices);+ qh_fprintf(qh, fp, 9110, "%s", endfmt);+} /* printfacet3math */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet3vertex">-</a>++ qh_printfacet3vertex(qh, fp, facet, format )+ print vertices in a 3-d facet as point ids++ notes:+ prints number of vertices first if format == qh_PRINToff+ the facet may be non-simplicial+*/+void qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {+ vertexT *vertex, **vertexp;+ setT *vertices;++ vertices= qh_facet3vertex(qh, facet);+ if (format == qh_PRINToff)+ qh_fprintf(qh, fp, 9111, "%d ", qh_setsize(qh, vertices));+ FOREACHvertex_(vertices)+ qh_fprintf(qh, fp, 9112, "%d ", qh_pointid(qh, vertex->point));+ qh_fprintf(qh, fp, 9113, "\n");+ qh_settempfree(qh, &vertices);+} /* printfacet3vertex */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a>++ qh_printfacet4geom_nonsimplicial(qh, )+ print Geomview 4OFF file for a 4d nonsimplicial facet+ prints all ridges to unvisited neighbors (qh.visit_id)+ if qh.DROPdim+ prints in OFF format++ notes:+ must agree with printend4geom()+*/+void qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {+ facetT *neighbor;+ ridgeT *ridge, **ridgep;+ vertexT *vertex, **vertexp;+ pointT *point;+ int k;+ realT dist;++ facet->visitid= qh->visit_id;+ if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))+ return;+ FOREACHridge_(facet->ridges) {+ neighbor= otherfacet_(ridge, facet);+ if (neighbor->visitid == qh->visit_id)+ continue;+ if (qh->PRINTtransparent && !neighbor->good)+ continue;+ if (qh->DOintersections)+ qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, color);+ else {+ if (qh->DROPdim >= 0)+ qh_fprintf(qh, fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);+ else {+ qh->printoutvar++;+ qh_fprintf(qh, fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);+ }+ FOREACHvertex_(ridge->vertices) {+ zinc_(Zdistio);+ qh_distplane(qh, vertex->point,facet, &dist);+ point=qh_projectpoint(qh, vertex->point,facet, dist);+ for (k=0; k < qh->hull_dim; k++) {+ if (k != qh->DROPdim)+ qh_fprintf(qh, fp, 9116, "%8.4g ", point[k]);+ }+ qh_fprintf(qh, fp, 9117, "\n");+ qh_memfree(qh, point, qh->normal_size);+ }+ if (qh->DROPdim >= 0)+ qh_fprintf(qh, fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);+ }+ }+} /* printfacet4geom_nonsimplicial */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacet4geom_simplicial">-</a>++ qh_printfacet4geom_simplicial(qh, fp, facet, color )+ print Geomview 4OFF file for a 4d simplicial facet+ prints triangles for unvisited neighbors (qh.visit_id)++ notes:+ must agree with printend4geom()+*/+void qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {+ setT *vertices;+ facetT *neighbor, **neighborp;+ vertexT *vertex, **vertexp;+ int k;++ facet->visitid= qh->visit_id;+ if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))+ return;+ FOREACHneighbor_(facet) {+ if (neighbor->visitid == qh->visit_id)+ continue;+ if (qh->PRINTtransparent && !neighbor->good)+ continue;+ vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,+ SETindex_(facet->neighbors, neighbor), 0);+ if (qh->DOintersections)+ qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, color);+ else {+ if (qh->DROPdim >= 0)+ qh_fprintf(qh, fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",+ facet->id, neighbor->id);+ else {+ qh->printoutvar++;+ qh_fprintf(qh, fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id);+ }+ FOREACHvertex_(vertices) {+ for (k=0; k < qh->hull_dim; k++) {+ if (k != qh->DROPdim)+ qh_fprintf(qh, fp, 9121, "%8.4g ", vertex->point[k]);+ }+ qh_fprintf(qh, fp, 9122, "\n");+ }+ if (qh->DROPdim >= 0)+ qh_fprintf(qh, fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);+ }+ qh_setfree(qh, &vertices);+ }+} /* printfacet4geom_simplicial */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a>++ qh_printfacetNvertex_nonsimplicial(qh, fp, facet, id, format )+ print vertices for an N-d non-simplicial facet+ triangulates each ridge to the id+*/+void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format) {+ vertexT *vertex, **vertexp;+ ridgeT *ridge, **ridgep;++ if (facet->visible && qh->NEWfacets)+ return;+ FOREACHridge_(facet->ridges) {+ if (format == qh_PRINTtriangles)+ qh_fprintf(qh, fp, 9124, "%d ", qh->hull_dim);+ qh_fprintf(qh, fp, 9125, "%d ", id);+ if ((ridge->top == facet) ^ qh_ORIENTclock) {+ FOREACHvertex_(ridge->vertices)+ qh_fprintf(qh, fp, 9126, "%d ", qh_pointid(qh, vertex->point));+ }else {+ FOREACHvertexreverse12_(ridge->vertices)+ qh_fprintf(qh, fp, 9127, "%d ", qh_pointid(qh, vertex->point));+ }+ qh_fprintf(qh, fp, 9128, "\n");+ }+} /* printfacetNvertex_nonsimplicial */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a>++ qh_printfacetNvertex_simplicial(qh, fp, facet, format )+ print vertices for an N-d simplicial facet+ prints vertices for non-simplicial facets+ 2-d facets (orientation preserved by qh_mergefacet2d)+ PRINToff ('o') for 4-d and higher+*/+void qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {+ vertexT *vertex, **vertexp;++ if (format == qh_PRINToff || format == qh_PRINTtriangles)+ qh_fprintf(qh, fp, 9129, "%d ", qh_setsize(qh, facet->vertices));+ if ((facet->toporient ^ qh_ORIENTclock)+ || (qh->hull_dim > 2 && !facet->simplicial)) {+ FOREACHvertex_(facet->vertices)+ qh_fprintf(qh, fp, 9130, "%d ", qh_pointid(qh, vertex->point));+ }else {+ FOREACHvertexreverse12_(facet->vertices)+ qh_fprintf(qh, fp, 9131, "%d ", qh_pointid(qh, vertex->point));+ }+ qh_fprintf(qh, fp, 9132, "\n");+} /* printfacetNvertex_simplicial */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacetheader">-</a>++ qh_printfacetheader(qh, fp, facet )+ prints header fields of a facet to fp++ notes:+ for 'f' output and debugging+ Same as QhullFacet::printHeader()+*/+void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet) {+ pointT *point, **pointp, *furthest;+ facetT *neighbor, **neighborp;+ realT dist;++ if (facet == qh_MERGEridge) {+ qh_fprintf(qh, fp, 9133, " MERGEridge\n");+ return;+ }else if (facet == qh_DUPLICATEridge) {+ qh_fprintf(qh, fp, 9134, " DUPLICATEridge\n");+ return;+ }else if (!facet) {+ qh_fprintf(qh, fp, 9135, " NULLfacet\n");+ return;+ }+ qh->old_randomdist= qh->RANDOMdist;+ qh->RANDOMdist= False;+ qh_fprintf(qh, fp, 9136, "- f%d\n", facet->id);+ qh_fprintf(qh, fp, 9137, " - flags:");+ if (facet->toporient)+ qh_fprintf(qh, fp, 9138, " top");+ else+ qh_fprintf(qh, fp, 9139, " bottom");+ if (facet->simplicial)+ qh_fprintf(qh, fp, 9140, " simplicial");+ if (facet->tricoplanar)+ qh_fprintf(qh, fp, 9141, " tricoplanar");+ if (facet->upperdelaunay)+ qh_fprintf(qh, fp, 9142, " upperDelaunay");+ if (facet->visible)+ qh_fprintf(qh, fp, 9143, " visible");+ if (facet->newfacet)+ qh_fprintf(qh, fp, 9144, " new");+ if (facet->tested)+ qh_fprintf(qh, fp, 9145, " tested");+ if (!facet->good)+ qh_fprintf(qh, fp, 9146, " notG");+ if (facet->seen)+ qh_fprintf(qh, fp, 9147, " seen");+ if (facet->coplanar)+ qh_fprintf(qh, fp, 9148, " coplanar");+ if (facet->mergehorizon)+ qh_fprintf(qh, fp, 9149, " mergehorizon");+ if (facet->keepcentrum)+ qh_fprintf(qh, fp, 9150, " keepcentrum");+ if (facet->dupridge)+ qh_fprintf(qh, fp, 9151, " dupridge");+ if (facet->mergeridge && !facet->mergeridge2)+ qh_fprintf(qh, fp, 9152, " mergeridge1");+ if (facet->mergeridge2)+ qh_fprintf(qh, fp, 9153, " mergeridge2");+ if (facet->newmerge)+ qh_fprintf(qh, fp, 9154, " newmerge");+ if (facet->flipped)+ qh_fprintf(qh, fp, 9155, " flipped");+ if (facet->notfurthest)+ qh_fprintf(qh, fp, 9156, " notfurthest");+ if (facet->degenerate)+ qh_fprintf(qh, fp, 9157, " degenerate");+ if (facet->redundant)+ qh_fprintf(qh, fp, 9158, " redundant");+ qh_fprintf(qh, fp, 9159, "\n");+ if (facet->isarea)+ qh_fprintf(qh, fp, 9160, " - area: %2.2g\n", facet->f.area);+ else if (qh->NEWfacets && facet->visible && facet->f.replace)+ qh_fprintf(qh, fp, 9161, " - replacement: f%d\n", facet->f.replace->id);+ else if (facet->newfacet) {+ if (facet->f.samecycle && facet->f.samecycle != facet)+ qh_fprintf(qh, fp, 9162, " - shares same visible/horizon as f%d\n", facet->f.samecycle->id);+ }else if (facet->tricoplanar /* !isarea */) {+ if (facet->f.triowner)+ qh_fprintf(qh, fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id);+ }else if (facet->f.newcycle)+ qh_fprintf(qh, fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id);+ if (facet->nummerge)+ qh_fprintf(qh, fp, 9165, " - merges: %d\n", facet->nummerge);+ qh_printpointid(qh, fp, " - normal: ", qh->hull_dim, facet->normal, qh_IDunknown);+ qh_fprintf(qh, fp, 9166, " - offset: %10.7g\n", facet->offset);+ if (qh->CENTERtype == qh_ASvoronoi || facet->center)+ qh_printcenter(qh, fp, qh_PRINTfacets, " - center: ", facet);+#if qh_MAXoutside+ if (facet->maxoutside > qh->DISTround)+ qh_fprintf(qh, fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside);+#endif+ if (!SETempty_(facet->outsideset)) {+ furthest= (pointT*)qh_setlast(facet->outsideset);+ if (qh_setsize(qh, facet->outsideset) < 6) {+ qh_fprintf(qh, fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(qh, furthest));+ FOREACHpoint_(facet->outsideset)+ qh_printpoint(qh, fp, " ", point);+ }else if (qh_setsize(qh, facet->outsideset) < 21) {+ qh_printpoints(qh, fp, " - outside set:", facet->outsideset);+ }else {+ qh_fprintf(qh, fp, 9169, " - outside set: %d points.", qh_setsize(qh, facet->outsideset));+ qh_printpoint(qh, fp, " Furthest", furthest);+ }+#if !qh_COMPUTEfurthest+ qh_fprintf(qh, fp, 9170, " - furthest distance= %2.2g\n", facet->furthestdist);+#endif+ }+ if (!SETempty_(facet->coplanarset)) {+ furthest= (pointT*)qh_setlast(facet->coplanarset);+ if (qh_setsize(qh, facet->coplanarset) < 6) {+ qh_fprintf(qh, fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(qh, furthest));+ FOREACHpoint_(facet->coplanarset)+ qh_printpoint(qh, fp, " ", point);+ }else if (qh_setsize(qh, facet->coplanarset) < 21) {+ qh_printpoints(qh, fp, " - coplanar set:", facet->coplanarset);+ }else {+ qh_fprintf(qh, fp, 9172, " - coplanar set: %d points.", qh_setsize(qh, facet->coplanarset));+ qh_printpoint(qh, fp, " Furthest", furthest);+ }+ zinc_(Zdistio);+ qh_distplane(qh, furthest, facet, &dist);+ qh_fprintf(qh, fp, 9173, " furthest distance= %2.2g\n", dist);+ }+ qh_printvertices(qh, fp, " - vertices:", facet->vertices);+ qh_fprintf(qh, fp, 9174, " - neighboring facets:");+ FOREACHneighbor_(facet) {+ if (neighbor == qh_MERGEridge)+ qh_fprintf(qh, fp, 9175, " MERGE");+ else if (neighbor == qh_DUPLICATEridge)+ qh_fprintf(qh, fp, 9176, " DUP");+ else+ qh_fprintf(qh, fp, 9177, " f%d", neighbor->id);+ }+ qh_fprintf(qh, fp, 9178, "\n");+ qh->RANDOMdist= qh->old_randomdist;+} /* printfacetheader */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacetridges">-</a>++ qh_printfacetridges(qh, fp, facet )+ prints ridges of a facet to fp++ notes:+ ridges printed in neighbor order+ assumes the ridges exist+ for 'f' output+ same as QhullFacet::printRidges+*/+void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet) {+ facetT *neighbor, **neighborp;+ ridgeT *ridge, **ridgep;+ int numridges= 0;+++ if (facet->visible && qh->NEWfacets) {+ qh_fprintf(qh, fp, 9179, " - ridges(ids may be garbage):");+ FOREACHridge_(facet->ridges)+ qh_fprintf(qh, fp, 9180, " r%d", ridge->id);+ qh_fprintf(qh, fp, 9181, "\n");+ }else {+ qh_fprintf(qh, fp, 9182, " - ridges:\n");+ FOREACHridge_(facet->ridges)+ ridge->seen= False;+ if (qh->hull_dim == 3) {+ ridge= SETfirstt_(facet->ridges, ridgeT);+ while (ridge && !ridge->seen) {+ ridge->seen= True;+ qh_printridge(qh, fp, ridge);+ numridges++;+ ridge= qh_nextridge3d(ridge, facet, NULL);+ }+ }else {+ FOREACHneighbor_(facet) {+ FOREACHridge_(facet->ridges) {+ if (otherfacet_(ridge,facet) == neighbor) {+ ridge->seen= True;+ qh_printridge(qh, fp, ridge);+ numridges++;+ }+ }+ }+ }+ if (numridges != qh_setsize(qh, facet->ridges)) {+ qh_fprintf(qh, fp, 9183, " - all ridges:");+ FOREACHridge_(facet->ridges)+ qh_fprintf(qh, fp, 9184, " r%d", ridge->id);+ qh_fprintf(qh, fp, 9185, "\n");+ }+ FOREACHridge_(facet->ridges) {+ if (!ridge->seen)+ qh_printridge(qh, fp, ridge);+ }+ }+} /* printfacetridges */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printfacets">-</a>++ qh_printfacets(qh, fp, format, facetlist, facets, printall )+ prints facetlist and/or facet set in output format++ notes:+ also used for specialized formats ('FO' and summary)+ turns off 'Rn' option since want actual numbers+*/+void qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {+ int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;+ facetT *facet, **facetp;+ setT *vertices;+ coordT *center;+ realT outerplane, innerplane;++ qh->old_randomdist= qh->RANDOMdist;+ qh->RANDOMdist= False;+ if (qh->CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff))+ qh_fprintf(qh, qh->ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n");+ if (format == qh_PRINTnone)+ ; /* print nothing */+ else if (format == qh_PRINTaverage) {+ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ center= qh_getcenter(qh, vertices);+ qh_fprintf(qh, fp, 9186, "%d 1\n", qh->hull_dim);+ qh_printpointid(qh, fp, NULL, qh->hull_dim, center, qh_IDunknown);+ qh_memfree(qh, center, qh->normal_size);+ qh_settempfree(qh, &vertices);+ }else if (format == qh_PRINTextremes) {+ if (qh->DELAUNAY)+ qh_printextremes_d(qh, fp, facetlist, facets, printall);+ else if (qh->hull_dim == 2)+ qh_printextremes_2d(qh, fp, facetlist, facets, printall);+ else+ qh_printextremes(qh, fp, facetlist, facets, printall);+ }else if (format == qh_PRINToptions)+ qh_fprintf(qh, fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);+ else if (format == qh_PRINTpoints && !qh->VORONOI)+ qh_printpoints_out(qh, fp, facetlist, facets, printall);+ else if (format == qh_PRINTqhull)+ qh_fprintf(qh, fp, 9188, "%s | %s\n", qh->rbox_command, qh->qhull_command);+ else if (format == qh_PRINTsize) {+ qh_fprintf(qh, fp, 9189, "0\n2 ");+ qh_fprintf(qh, fp, 9190, qh_REAL_1, qh->totarea);+ qh_fprintf(qh, fp, 9191, qh_REAL_1, qh->totvol);+ qh_fprintf(qh, fp, 9192, "\n");+ }else if (format == qh_PRINTsummary) {+ qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars);+ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ qh_fprintf(qh, fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh->hull_dim,+ qh->num_points + qh_setsize(qh, qh->other_points),+ qh->num_vertices, qh->num_facets - qh->num_visible,+ qh_setsize(qh, vertices), numfacets, numcoplanars,+ numfacets - numsimplicial, zzval_(Zdelvertextot),+ numtricoplanars);+ qh_settempfree(qh, &vertices);+ qh_outerinner(qh, NULL, &outerplane, &innerplane);+ qh_fprintf(qh, fp, 9194, qh_REAL_2n, outerplane, innerplane);+ }else if (format == qh_PRINTvneighbors)+ qh_printvneighbors(qh, fp, facetlist, facets, printall);+ else if (qh->VORONOI && format == qh_PRINToff)+ qh_printvoronoi(qh, fp, format, facetlist, facets, printall);+ else if (qh->VORONOI && format == qh_PRINTgeom) {+ qh_printbegin(qh, fp, format, facetlist, facets, printall);+ qh_printvoronoi(qh, fp, format, facetlist, facets, printall);+ qh_printend(qh, fp, format, facetlist, facets, printall);+ }else if (qh->VORONOI+ && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))+ qh_printvdiagram(qh, fp, format, facetlist, facets, printall);+ else {+ qh_printbegin(qh, fp, format, facetlist, facets, printall);+ FORALLfacet_(facetlist)+ qh_printafacet(qh, fp, format, facet, printall);+ FOREACHfacet_(facets)+ qh_printafacet(qh, fp, format, facet, printall);+ qh_printend(qh, fp, format, facetlist, facets, printall);+ }+ qh->RANDOMdist= qh->old_randomdist;+} /* printfacets */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printhyperplaneintersection">-</a>++ qh_printhyperplaneintersection(qh, fp, facet1, facet2, vertices, color )+ print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d+*/+void qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2,+ setT *vertices, realT color[3]) {+ realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4];+ vertexT *vertex, **vertexp;+ int i, k;+ boolT nearzero1, nearzero2;++ costheta= qh_getangle(qh, facet1->normal, facet2->normal);+ denominator= 1 - costheta * costheta;+ i= qh_setsize(qh, vertices);+ if (qh->hull_dim == 3)+ qh_fprintf(qh, fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);+ else if (qh->hull_dim == 4 && qh->DROPdim >= 0)+ qh_fprintf(qh, fp, 9196, "OFF 3 1 1 ");+ else+ qh->printoutvar++;+ qh_fprintf(qh, fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id);+ mindenom= 1 / (10.0 * qh->MAXabs_coord);+ FOREACHvertex_(vertices) {+ zadd_(Zdistio, 2);+ qh_distplane(qh, vertex->point, facet1, &dist1);+ qh_distplane(qh, vertex->point, facet2, &dist2);+ s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1);+ t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2);+ if (nearzero1 || nearzero2)+ s= t= 0.0;+ for (k=qh->hull_dim; k--; )+ p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t;+ if (qh->PRINTdim <= 3) {+ qh_projectdim3(qh, p, p);+ qh_fprintf(qh, fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);+ }else+ qh_fprintf(qh, fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);+ if (nearzero1+nearzero2)+ qh_fprintf(qh, fp, 9200, "p%d(coplanar facets)\n", qh_pointid(qh, vertex->point));+ else+ qh_fprintf(qh, fp, 9201, "projected p%d\n", qh_pointid(qh, vertex->point));+ }+ if (qh->hull_dim == 3)+ qh_fprintf(qh, fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);+ else if (qh->hull_dim == 4 && qh->DROPdim >= 0)+ qh_fprintf(qh, fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);+} /* printhyperplaneintersection */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printline3geom">-</a>++ qh_printline3geom(qh, fp, pointA, pointB, color )+ prints a line as a VECT+ prints 0's for qh.DROPdim++ notes:+ if pointA == pointB,+ it's a 1 point VECT+*/+void qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {+ int k;+ realT pA[4], pB[4];++ qh_projectdim3(qh, pointA, pA);+ qh_projectdim3(qh, pointB, pB);+ if ((fabs(pA[0] - pB[0]) > 1e-3) ||+ (fabs(pA[1] - pB[1]) > 1e-3) ||+ (fabs(pA[2] - pB[2]) > 1e-3)) {+ qh_fprintf(qh, fp, 9204, "VECT 1 2 1 2 1\n");+ for (k=0; k < 3; k++)+ qh_fprintf(qh, fp, 9205, "%8.4g ", pB[k]);+ qh_fprintf(qh, fp, 9206, " # p%d\n", qh_pointid(qh, pointB));+ }else+ qh_fprintf(qh, fp, 9207, "VECT 1 1 1 1 1\n");+ for (k=0; k < 3; k++)+ qh_fprintf(qh, fp, 9208, "%8.4g ", pA[k]);+ qh_fprintf(qh, fp, 9209, " # p%d\n", qh_pointid(qh, pointA));+ qh_fprintf(qh, fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);+}++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printneighborhood">-</a>++ qh_printneighborhood(qh, fp, format, facetA, facetB, printall )+ print neighborhood of one or two facets++ notes:+ calls qh_findgood_all()+ bumps qh.visit_id+*/+void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) {+ facetT *neighbor, **neighborp, *facet;+ setT *facets;++ if (format == qh_PRINTnone)+ return;+ qh_findgood_all(qh, qh->facet_list);+ if (facetA == facetB)+ facetB= NULL;+ facets= qh_settemp(qh, 2*(qh_setsize(qh, facetA->neighbors)+1));+ qh->visit_id++;+ for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) {+ if (facet->visitid != qh->visit_id) {+ facet->visitid= qh->visit_id;+ qh_setappend(qh, &facets, facet);+ }+ FOREACHneighbor_(facet) {+ if (neighbor->visitid == qh->visit_id)+ continue;+ neighbor->visitid= qh->visit_id;+ if (printall || !qh_skipfacet(qh, neighbor))+ qh_setappend(qh, &facets, neighbor);+ }+ }+ qh_printfacets(qh, fp, format, NULL, facets, printall);+ qh_settempfree(qh, &facets);+} /* printneighborhood */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printpoint">-</a>++ qh_printpoint(qh, fp, string, point )+ qh_printpointid(qh, fp, string, dim, point, id )+ prints the coordinates of a point++ returns:+ if string is defined+ prints 'string p%d'. Skips p%d if id=qh_IDunknown(-1) or qh_IDnone(-3)++ notes:+ nop if point is NULL+ Same as QhullPoint's printPoint+*/+void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point) {+ int id= qh_pointid(qh, point);++ qh_printpointid(qh, fp, string, qh->hull_dim, point, id);+} /* printpoint */++void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id) {+ int k;+ realT r; /*bug fix*/++ if (!point)+ return;+ if (string) {+ qh_fprintf(qh, fp, 9211, "%s", string);+ if (id != qh_IDunknown && id != qh_IDnone)+ qh_fprintf(qh, fp, 9212, " p%d: ", id);+ }+ for (k=dim; k--; ) {+ r= *point++;+ if (string)+ qh_fprintf(qh, fp, 9213, " %8.4g", r);+ else+ qh_fprintf(qh, fp, 9214, qh_REAL_1, r);+ }+ qh_fprintf(qh, fp, 9215, "\n");+} /* printpointid */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printpoint3">-</a>++ qh_printpoint3(qh, fp, point )+ prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates+*/+void qh_printpoint3(qhT *qh, FILE *fp, pointT *point) {+ int k;+ realT p[4];++ qh_projectdim3(qh, point, p);+ for (k=0; k < 3; k++)+ qh_fprintf(qh, fp, 9216, "%8.4g ", p[k]);+ qh_fprintf(qh, fp, 9217, " # p%d\n", qh_pointid(qh, point));+} /* printpoint3 */++/*----------------------------------------+-printpoints- print pointids for a set of points starting at index+ see geom_r.c+*/++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printpoints_out">-</a>++ qh_printpoints_out(qh, fp, facetlist, facets, printall )+ prints vertices, coplanar/inside points, for facets by their point coordinates+ allows qh.CDDoutput++ notes:+ same format as qhull input+ if no coplanar/interior points,+ same order as qh_printextremes+*/+void qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {+ int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);+ int numpoints=0, point_i, point_n;+ setT *vertices, *points;+ facetT *facet, **facetp;+ pointT *point, **pointp;+ vertexT *vertex, **vertexp;+ int id;++ points= qh_settemp(qh, allpoints);+ qh_setzero(qh, points, 0, allpoints);+ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ FOREACHvertex_(vertices) {+ id= qh_pointid(qh, vertex->point);+ if (id >= 0)+ SETelem_(points, id)= vertex->point;+ }+ if (qh->KEEPinside || qh->KEEPcoplanar || qh->KEEPnearinside) {+ FORALLfacet_(facetlist) {+ if (!printall && qh_skipfacet(qh, facet))+ continue;+ FOREACHpoint_(facet->coplanarset) {+ id= qh_pointid(qh, point);+ if (id >= 0)+ SETelem_(points, id)= point;+ }+ }+ FOREACHfacet_(facets) {+ if (!printall && qh_skipfacet(qh, facet))+ continue;+ FOREACHpoint_(facet->coplanarset) {+ id= qh_pointid(qh, point);+ if (id >= 0)+ SETelem_(points, id)= point;+ }+ }+ }+ qh_settempfree(qh, &vertices);+ FOREACHpoint_i_(qh, points) {+ if (point)+ numpoints++;+ }+ if (qh->CDDoutput)+ qh_fprintf(qh, fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,+ qh->qhull_command, numpoints, qh->hull_dim + 1);+ else+ qh_fprintf(qh, fp, 9219, "%d\n%d\n", qh->hull_dim, numpoints);+ FOREACHpoint_i_(qh, points) {+ if (point) {+ if (qh->CDDoutput)+ qh_fprintf(qh, fp, 9220, "1 ");+ qh_printpoint(qh, fp, NULL, point);+ }+ }+ if (qh->CDDoutput)+ qh_fprintf(qh, fp, 9221, "end\n");+ qh_settempfree(qh, &points);+} /* printpoints_out */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printpointvect">-</a>++ qh_printpointvect(qh, fp, point, normal, center, radius, color )+ prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point+*/+void qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) {+ realT diff[4], pointA[4];+ int k;++ for (k=qh->hull_dim; k--; ) {+ if (center)+ diff[k]= point[k]-center[k];+ else if (normal)+ diff[k]= normal[k];+ else+ diff[k]= 0;+ }+ if (center)+ qh_normalize2(qh, diff, qh->hull_dim, True, NULL, NULL);+ for (k=qh->hull_dim; k--; )+ pointA[k]= point[k]+diff[k] * radius;+ qh_printline3geom(qh, fp, point, pointA, color);+} /* printpointvect */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printpointvect2">-</a>++ qh_printpointvect2(qh, fp, point, normal, center, radius )+ prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point+*/+void qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {+ realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};++ qh_printpointvect(qh, fp, point, normal, center, radius, red);+ qh_printpointvect(qh, fp, point, normal, center, -radius, yellow);+} /* printpointvect2 */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printridge">-</a>++ qh_printridge(qh, fp, ridge )+ prints the information in a ridge++ notes:+ for qh_printfacetridges()+ same as operator<< [QhullRidge.cpp]+*/+void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge) {++ qh_fprintf(qh, fp, 9222, " - r%d", ridge->id);+ if (ridge->tested)+ qh_fprintf(qh, fp, 9223, " tested");+ if (ridge->nonconvex)+ qh_fprintf(qh, fp, 9224, " nonconvex");+ qh_fprintf(qh, fp, 9225, "\n");+ qh_printvertices(qh, fp, " vertices:", ridge->vertices);+ if (ridge->top && ridge->bottom)+ qh_fprintf(qh, fp, 9226, " between f%d and f%d\n",+ ridge->top->id, ridge->bottom->id);+} /* printridge */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printspheres">-</a>++ qh_printspheres(qh, fp, vertices, radius )+ prints 3-d vertices as OFF spheres++ notes:+ inflated octahedron from Stuart Levy earth/mksphere2+*/+void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius) {+ vertexT *vertex, **vertexp;++ qh->printoutnum++;+ qh_fprintf(qh, fp, 9227, "{appearance {-edge -normal normscale 0} {\n\+INST geom {define vsphere OFF\n\+18 32 48\n\+\n\+0 0 1\n\+1 0 0\n\+0 1 0\n\+-1 0 0\n\+0 -1 0\n\+0 0 -1\n\+0.707107 0 0.707107\n\+0 -0.707107 0.707107\n\+0.707107 -0.707107 0\n\+-0.707107 0 0.707107\n\+-0.707107 -0.707107 0\n\+0 0.707107 0.707107\n\+-0.707107 0.707107 0\n\+0.707107 0.707107 0\n\+0.707107 0 -0.707107\n\+0 0.707107 -0.707107\n\+-0.707107 0 -0.707107\n\+0 -0.707107 -0.707107\n\+\n\+3 0 6 11\n\+3 0 7 6 \n\+3 0 9 7 \n\+3 0 11 9\n\+3 1 6 8 \n\+3 1 8 14\n\+3 1 13 6\n\+3 1 14 13\n\+3 2 11 13\n\+3 2 12 11\n\+3 2 13 15\n\+3 2 15 12\n\+3 3 9 12\n\+3 3 10 9\n\+3 3 12 16\n\+3 3 16 10\n\+3 4 7 10\n\+3 4 8 7\n\+3 4 10 17\n\+3 4 17 8\n\+3 5 14 17\n\+3 5 15 14\n\+3 5 16 15\n\+3 5 17 16\n\+3 6 13 11\n\+3 7 8 6\n\+3 9 10 7\n\+3 11 12 9\n\+3 14 8 17\n\+3 15 13 14\n\+3 16 12 15\n\+3 17 10 16\n} transforms { TLIST\n");+ FOREACHvertex_(vertices) {+ qh_fprintf(qh, fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n",+ radius, vertex->id, radius, radius);+ qh_printpoint3(qh, fp, vertex->point);+ qh_fprintf(qh, fp, 9229, "1\n");+ }+ qh_fprintf(qh, fp, 9230, "}}}\n");+} /* printspheres */+++/*----------------------------------------------+-printsummary-+ see libqhull_r.c+*/++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvdiagram">-</a>++ qh_printvdiagram(qh, fp, format, facetlist, facets, printall )+ print voronoi diagram+ # of pairs of input sites+ #indices site1 site2 vertex1 ...++ sites indexed by input point id+ point 0 is the first input point+ vertices indexed by 'o' and 'p' order+ vertex 0 is the 'vertex-at-infinity'+ vertex 1 is the first Voronoi vertex++ see:+ qh_printvoronoi()+ qh_eachvoronoi_all()++ notes:+ if all facets are upperdelaunay,+ prints upper hull (furthest-site Voronoi diagram)+*/+void qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {+ setT *vertices;+ int totcount, numcenters;+ boolT isLower;+ qh_RIDGE innerouter= qh_RIDGEall;+ printvridgeT printvridge= NULL;++ if (format == qh_PRINTvertices) {+ innerouter= qh_RIDGEall;+ printvridge= qh_printvridge;+ }else if (format == qh_PRINTinner) {+ innerouter= qh_RIDGEinner;+ printvridge= qh_printvnorm;+ }else if (format == qh_PRINTouter) {+ innerouter= qh_RIDGEouter;+ printvridge= qh_printvnorm;+ }else {+ qh_fprintf(qh, qh->ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);+ totcount= qh_printvdiagram2(qh, NULL, NULL, vertices, innerouter, False);+ qh_fprintf(qh, fp, 9231, "%d\n", totcount);+ totcount= qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, True /* inorder*/);+ qh_settempfree(qh, &vertices);+#if 0 /* for testing qh_eachvoronoi_all */+ qh_fprintf(qh, fp, 9232, "\n");+ totcount= qh_eachvoronoi_all(qh, fp, printvridge, qh->UPPERdelaunay, innerouter, True /* inorder*/);+ qh_fprintf(qh, fp, 9233, "%d\n", totcount);+#endif+} /* printvdiagram */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvdiagram2">-</a>++ qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, inorder )+ visit all pairs of input sites (vertices) for selected Voronoi vertices+ vertices may include NULLs++ innerouter:+ qh_RIDGEall print inner ridges(bounded) and outer ridges(unbounded)+ qh_RIDGEinner print only inner ridges+ qh_RIDGEouter print only outer ridges++ inorder:+ print 3-d Voronoi vertices in order++ assumes:+ qh_markvoronoi marked facet->visitid for Voronoi vertices+ all facet->seen= False+ all facet->seen2= True++ returns:+ total number of Voronoi ridges+ if printvridge,+ calls printvridge( fp, vertex, vertexA, centers) for each ridge+ [see qh_eachvoronoi()]++ see:+ qh_eachvoronoi_all()+*/+int qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) {+ int totcount= 0;+ int vertex_i, vertex_n;+ vertexT *vertex;++ FORALLvertices+ vertex->seen= False;+ FOREACHvertex_i_(qh, vertices) {+ if (vertex) {+ if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)+ continue;+ totcount += qh_eachvoronoi(qh, fp, printvridge, vertex, !qh_ALL, innerouter, inorder);+ }+ }+ return totcount;+} /* printvdiagram2 */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvertex">-</a>++ qh_printvertex(qh, fp, vertex )+ prints the information in a vertex+ Duplicated as operator<< [QhullVertex.cpp]+*/+void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex) {+ pointT *point;+ int k, count= 0;+ facetT *neighbor, **neighborp;+ realT r; /*bug fix*/++ if (!vertex) {+ qh_fprintf(qh, fp, 9234, " NULLvertex\n");+ return;+ }+ qh_fprintf(qh, fp, 9235, "- p%d(v%d):", qh_pointid(qh, vertex->point), vertex->id);+ point= vertex->point;+ if (point) {+ for (k=qh->hull_dim; k--; ) {+ r= *point++;+ qh_fprintf(qh, fp, 9236, " %5.2g", r);+ }+ }+ if (vertex->deleted)+ qh_fprintf(qh, fp, 9237, " deleted");+ if (vertex->delridge)+ qh_fprintf(qh, fp, 9238, " ridgedeleted");+ qh_fprintf(qh, fp, 9239, "\n");+ if (vertex->neighbors) {+ qh_fprintf(qh, fp, 9240, " neighbors:");+ FOREACHneighbor_(vertex) {+ if (++count % 100 == 0)+ qh_fprintf(qh, fp, 9241, "\n ");+ qh_fprintf(qh, fp, 9242, " f%d", neighbor->id);+ }+ qh_fprintf(qh, fp, 9243, "\n");+ }+} /* printvertex */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvertexlist">-</a>++ qh_printvertexlist(qh, fp, string, facetlist, facets, printall )+ prints vertices used by a facetlist or facet set+ tests qh_skipfacet() if !printall+*/+void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist,+ setT *facets, boolT printall) {+ vertexT *vertex, **vertexp;+ setT *vertices;++ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ qh_fprintf(qh, fp, 9244, "%s", string);+ FOREACHvertex_(vertices)+ qh_printvertex(qh, fp, vertex);+ qh_settempfree(qh, &vertices);+} /* printvertexlist */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvertices">-</a>++ qh_printvertices(qh, fp, string, vertices )+ prints vertices in a set+ duplicated as printVertexSet [QhullVertex.cpp]+*/+void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices) {+ vertexT *vertex, **vertexp;++ qh_fprintf(qh, fp, 9245, "%s", string);+ FOREACHvertex_(vertices)+ qh_fprintf(qh, fp, 9246, " p%d(v%d)", qh_pointid(qh, vertex->point), vertex->id);+ qh_fprintf(qh, fp, 9247, "\n");+} /* printvertices */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvneighbors">-</a>++ qh_printvneighbors(qh, fp, facetlist, facets, printall )+ print vertex neighbors of vertices in facetlist and facets ('FN')++ notes:+ qh_countfacets clears facet->visitid for non-printed facets++ design:+ collect facet count and related statistics+ if necessary, build neighbor sets for each vertex+ collect vertices in facetlist and facets+ build a point array for point->vertex and point->coplanar facet+ for each point+ list vertex neighbors or coplanar facet+*/+void qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall) {+ int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars;+ setT *vertices, *vertex_points, *coplanar_points;+ int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);+ vertexT *vertex, **vertexp;+ int vertex_i, vertex_n;+ facetT *facet, **facetp, *neighbor, **neighborp;+ pointT *point, **pointp;++ qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* sets facet->visitid */+ qh_fprintf(qh, fp, 9248, "%d\n", numpoints);+ qh_vertexneighbors(qh);+ vertices= qh_facetvertices(qh, facetlist, facets, printall);+ vertex_points= qh_settemp(qh, numpoints);+ coplanar_points= qh_settemp(qh, numpoints);+ qh_setzero(qh, vertex_points, 0, numpoints);+ qh_setzero(qh, coplanar_points, 0, numpoints);+ FOREACHvertex_(vertices)+ qh_point_add(qh, vertex_points, vertex->point, vertex);+ FORALLfacet_(facetlist) {+ FOREACHpoint_(facet->coplanarset)+ qh_point_add(qh, coplanar_points, point, facet);+ }+ FOREACHfacet_(facets) {+ FOREACHpoint_(facet->coplanarset)+ qh_point_add(qh, coplanar_points, point, facet);+ }+ FOREACHvertex_i_(qh, vertex_points) {+ if (vertex) {+ numneighbors= qh_setsize(qh, vertex->neighbors);+ qh_fprintf(qh, fp, 9249, "%d", numneighbors);+ if (qh->hull_dim == 3)+ qh_order_vertexneighbors(qh, vertex);+ else if (qh->hull_dim >= 4)+ qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,+ sizeof(facetT *), qh_compare_facetvisit);+ FOREACHneighbor_(vertex)+ qh_fprintf(qh, fp, 9250, " %d",+ neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);+ qh_fprintf(qh, fp, 9251, "\n");+ }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))+ qh_fprintf(qh, fp, 9252, "1 %d\n",+ facet->visitid ? facet->visitid - 1 : 0 - facet->id);+ else+ qh_fprintf(qh, fp, 9253, "0\n");+ }+ qh_settempfree(qh, &coplanar_points);+ qh_settempfree(qh, &vertex_points);+ qh_settempfree(qh, &vertices);+} /* printvneighbors */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvoronoi">-</a>++ qh_printvoronoi(qh, fp, format, facetlist, facets, printall )+ print voronoi diagram in 'o' or 'G' format+ for 'o' format+ prints voronoi centers for each facet and for infinity+ for each vertex, lists ids of printed facets or infinity+ assumes facetlist and facets are disjoint+ for 'G' format+ prints an OFF object+ adds a 0 coordinate to center+ prints infinity but does not list in vertices++ see:+ qh_printvdiagram()++ notes:+ if 'o',+ prints a line for each point except "at-infinity"+ if all facets are upperdelaunay,+ reverses lower and upper hull+*/+void qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {+ int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n;+ facetT *facet, **facetp, *neighbor, **neighborp;+ setT *vertices;+ vertexT *vertex;+ boolT isLower;+ unsigned int numfacets= (unsigned int) qh->num_facets;++ vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);+ FOREACHvertex_i_(qh, vertices) {+ if (vertex) {+ numvertices++;+ numneighbors = numinf = 0;+ FOREACHneighbor_(vertex) {+ if (neighbor->visitid == 0)+ numinf= 1;+ else if (neighbor->visitid < numfacets)+ numneighbors++;+ }+ if (numinf && !numneighbors) {+ SETelem_(vertices, vertex_i)= NULL;+ numvertices--;+ }+ }+ }+ if (format == qh_PRINTgeom)+ qh_fprintf(qh, fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",+ numcenters, numvertices);+ else+ qh_fprintf(qh, fp, 9255, "%d\n%d %d 1\n", qh->hull_dim-1, numcenters, qh_setsize(qh, vertices));+ if (format == qh_PRINTgeom) {+ for (k=qh->hull_dim-1; k--; )+ qh_fprintf(qh, fp, 9256, qh_REAL_1, 0.0);+ qh_fprintf(qh, fp, 9257, " 0 # infinity not used\n");+ }else {+ for (k=qh->hull_dim-1; k--; )+ qh_fprintf(qh, fp, 9258, qh_REAL_1, qh_INFINITE);+ qh_fprintf(qh, fp, 9259, "\n");+ }+ FORALLfacet_(facetlist) {+ if (facet->visitid && facet->visitid < numfacets) {+ if (format == qh_PRINTgeom)+ qh_fprintf(qh, fp, 9260, "# %d f%d\n", vid++, facet->id);+ qh_printcenter(qh, fp, format, NULL, facet);+ }+ }+ FOREACHfacet_(facets) {+ if (facet->visitid && facet->visitid < numfacets) {+ if (format == qh_PRINTgeom)+ qh_fprintf(qh, fp, 9261, "# %d f%d\n", vid++, facet->id);+ qh_printcenter(qh, fp, format, NULL, facet);+ }+ }+ FOREACHvertex_i_(qh, vertices) {+ numneighbors= 0;+ numinf=0;+ if (vertex) {+ if (qh->hull_dim == 3)+ qh_order_vertexneighbors(qh, vertex);+ else if (qh->hull_dim >= 4)+ qsort(SETaddr_(vertex->neighbors, facetT),+ (size_t)qh_setsize(qh, vertex->neighbors),+ sizeof(facetT *), qh_compare_facetvisit);+ FOREACHneighbor_(vertex) {+ if (neighbor->visitid == 0)+ numinf= 1;+ else if (neighbor->visitid < numfacets)+ numneighbors++;+ }+ }+ if (format == qh_PRINTgeom) {+ if (vertex) {+ qh_fprintf(qh, fp, 9262, "%d", numneighbors);+ FOREACHneighbor_(vertex) {+ if (neighbor->visitid && neighbor->visitid < numfacets)+ qh_fprintf(qh, fp, 9263, " %d", neighbor->visitid);+ }+ qh_fprintf(qh, fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);+ }else+ qh_fprintf(qh, fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);+ }else {+ if (numinf)+ numneighbors++;+ qh_fprintf(qh, fp, 9266, "%d", numneighbors);+ if (vertex) {+ FOREACHneighbor_(vertex) {+ if (neighbor->visitid == 0) {+ if (numinf) {+ numinf= 0;+ qh_fprintf(qh, fp, 9267, " %d", neighbor->visitid);+ }+ }else if (neighbor->visitid < numfacets)+ qh_fprintf(qh, fp, 9268, " %d", neighbor->visitid);+ }+ }+ qh_fprintf(qh, fp, 9269, "\n");+ }+ }+ if (format == qh_PRINTgeom)+ qh_fprintf(qh, fp, 9270, "}\n");+ qh_settempfree(qh, &vertices);+} /* printvoronoi */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvnorm">-</a>++ qh_printvnorm(qh, fp, vertex, vertexA, centers, unbounded )+ print one separating plane of the Voronoi diagram for a pair of input sites+ unbounded==True if centers includes vertex-at-infinity++ assumes:+ qh_ASvoronoi and qh_vertexneighbors() already set++ note:+ parameter unbounded is UNUSED by this callback++ see:+ qh_printvdiagram()+ qh_eachvoronoi()+*/+void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {+ pointT *normal;+ realT offset;+ int k;+ QHULL_UNUSED(unbounded);++ normal= qh_detvnorm(qh, vertex, vertexA, centers, &offset);+ qh_fprintf(qh, fp, 9271, "%d %d %d ",+ 2+qh->hull_dim, qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));+ for (k=0; k< qh->hull_dim-1; k++)+ qh_fprintf(qh, fp, 9272, qh_REAL_1, normal[k]);+ qh_fprintf(qh, fp, 9273, qh_REAL_1, offset);+ qh_fprintf(qh, fp, 9274, "\n");+} /* printvnorm */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printvridge">-</a>++ qh_printvridge(qh, fp, vertex, vertexA, centers, unbounded )+ print one ridge of the Voronoi diagram for a pair of input sites+ unbounded==True if centers includes vertex-at-infinity++ see:+ qh_printvdiagram()++ notes:+ the user may use a different function+ parameter unbounded is UNUSED+*/+void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {+ facetT *facet, **facetp;+ QHULL_UNUSED(unbounded);++ qh_fprintf(qh, fp, 9275, "%d %d %d", qh_setsize(qh, centers)+2,+ qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));+ FOREACHfacet_(centers)+ qh_fprintf(qh, fp, 9276, " %d", facet->visitid);+ qh_fprintf(qh, fp, 9277, "\n");+} /* printvridge */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="projectdim3">-</a>++ qh_projectdim3(qh, source, destination )+ project 2-d 3-d or 4-d point to a 3-d point+ uses qh.DROPdim and qh.hull_dim+ source and destination may be the same++ notes:+ allocate 4 elements to destination just in case+*/+void qh_projectdim3(qhT *qh, pointT *source, pointT *destination) {+ int i,k;++ for (k=0, i=0; k < qh->hull_dim; k++) {+ if (qh->hull_dim == 4) {+ if (k != qh->DROPdim)+ destination[i++]= source[k];+ }else if (k == qh->DROPdim)+ destination[i++]= 0;+ else+ destination[i++]= source[k];+ }+ while (i < 3)+ destination[i++]= 0.0;+} /* projectdim3 */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="readfeasible">-</a>++ qh_readfeasible(qh, dim, curline )+ read feasible point from current line and qh.fin++ returns:+ number of lines read from qh.fin+ sets qh.feasible_point with malloc'd coordinates++ notes:+ checks for qh.HALFspace+ assumes dim > 1++ see:+ qh_setfeasible+*/+int qh_readfeasible(qhT *qh, int dim, const char *curline) {+ boolT isfirst= True;+ int linecount= 0, tokcount= 0;+ const char *s;+ char *t, firstline[qh_MAXfirst+1];+ coordT *coords, value;++ if (!qh->HALFspace) {+ qh_fprintf(qh, qh->ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (qh->feasible_string)+ qh_fprintf(qh, qh->ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n");+ if (!(qh->feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) {+ qh_fprintf(qh, qh->ferr, 6071, "qhull error: insufficient memory for feasible point\n");+ qh_errexit(qh, qh_ERRmem, NULL, NULL);+ }+ coords= qh->feasible_point;+ while ((s= (isfirst ? curline : fgets(firstline, qh_MAXfirst, qh->fin)))) {+ if (isfirst)+ isfirst= False;+ else+ linecount++;+ while (*s) {+ while (isspace(*s))+ s++;+ value= qh_strtod(s, &t);+ if (s == t)+ break;+ s= t;+ *(coords++)= value;+ if (++tokcount == dim) {+ while (isspace(*s))+ s++;+ qh_strtod(s, &t);+ if (s != t) {+ qh_fprintf(qh, qh->ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",+ s);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ return linecount;+ }+ }+ }+ qh_fprintf(qh, qh->ferr, 6073, "qhull input error: only %d coordinates. Could not read %d-d feasible point.\n",+ tokcount, dim);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ return 0;+} /* readfeasible */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="readpoints">-</a>++ qh_readpoints(qh, numpoints, dimension, ismalloc )+ read points from qh.fin into qh.first_point, qh.num_points+ qh.fin is lines of coordinates, one per vertex, first line number of points+ if 'rbox D4',+ gives message+ if qh.ATinfinity,+ adds point-at-infinity for Delaunay triangulations++ returns:+ number of points, array of point coordinates, dimension, ismalloc True+ if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid+ and clears qh.PROJECTdelaunay+ if qh.HALFspace, reads optional feasible point, reads halfspaces,+ converts to dual.++ for feasible point in "cdd format" in 3-d:+ 3 1+ coordinates+ comments+ begin+ n 4 real/integer+ ...+ end++ notes:+ dimension will change in qh_initqhull_globals if qh.PROJECTinput+ uses malloc() since qh_mem not initialized+ FIXUP QH11012: qh_readpoints needs rewriting, too long+*/+coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc) {+ coordT *points, *coords, *infinity= NULL;+ realT paraboloid, maxboloid= -REALmax, value;+ realT *coordp= NULL, *offsetp= NULL, *normalp= NULL;+ char *s= 0, *t, firstline[qh_MAXfirst+1];+ int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi;+ int firsttext=0, firstshort=0, firstlong=0, firstpoint=0;+ int tokcount= 0, linecount=0, maxcount, coordcount=0;+ boolT islong, isfirst= True, wasbegin= False;+ boolT isdelaunay= qh->DELAUNAY && !qh->PROJECTinput;++ if (qh->CDDinput) {+ while ((s= fgets(firstline, qh_MAXfirst, qh->fin))) {+ linecount++;+ if (qh->HALFspace && linecount == 1 && isdigit(*s)) {+ dimfeasible= qh_strtol(s, &s);+ while (isspace(*s))+ s++;+ if (qh_strtol(s, &s) == 1)+ linecount += qh_readfeasible(qh, dimfeasible, s);+ else+ dimfeasible= 0;+ }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5))+ break;+ else if (!*qh->rbox_command)+ strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);+ }+ if (!s) {+ qh_fprintf(qh, qh->ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ }+ while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh->fin))) {+ linecount++;+ if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5))+ wasbegin= True;+ while (*s) {+ while (isspace(*s))+ s++;+ if (!*s)+ break;+ if (!isdigit(*s)) {+ if (!*qh->rbox_command) {+ strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);+ firsttext= linecount;+ }+ break;+ }+ if (!diminput)+ diminput= qh_strtol(s, &s);+ else {+ numinput= qh_strtol(s, &s);+ if (numinput == 1 && diminput >= 2 && qh->HALFspace && !qh->CDDinput) {+ linecount += qh_readfeasible(qh, diminput, s); /* checks if ok */+ dimfeasible= diminput;+ diminput= numinput= 0;+ }else+ break;+ }+ }+ }+ if (!s) {+ qh_fprintf(qh, qh->ferr, 6075, "qhull input error: short input file. Did not find dimension and number of points\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (diminput > numinput) {+ tempi= diminput; /* exchange dim and n, e.g., for cdd input format */+ diminput= numinput;+ numinput= tempi;+ }+ if (diminput < 2) {+ qh_fprintf(qh, qh->ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n",+ diminput);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (isdelaunay) {+ qh->PROJECTdelaunay= False;+ if (qh->CDDinput)+ *dimension= diminput;+ else+ *dimension= diminput+1;+ *numpoints= numinput;+ if (qh->ATinfinity)+ (*numpoints)++;+ }else if (qh->HALFspace) {+ *dimension= diminput - 1;+ *numpoints= numinput;+ if (diminput < 3) {+ qh_fprintf(qh, qh->ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n",+ diminput);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (dimfeasible) {+ if (dimfeasible != *dimension) {+ qh_fprintf(qh, qh->ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n",+ dimfeasible, diminput);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ }else+ qh_setfeasible(qh, *dimension);+ }else {+ if (qh->CDDinput)+ *dimension= diminput-1;+ else+ *dimension= diminput;+ *numpoints= numinput;+ }+ qh->normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */+ if (qh->HALFspace) {+ qh->half_space= coordp= (coordT*)qh_malloc(qh->normal_size + sizeof(coordT));+ if (qh->CDDinput) {+ offsetp= qh->half_space;+ normalp= offsetp + 1;+ }else {+ normalp= qh->half_space;+ offsetp= normalp + *dimension;+ }+ }+ qh->maxline= diminput * (qh_REALdigits + 5);+ maximize_(qh->maxline, 500);+ qh->line= (char*)qh_malloc((qh->maxline+1) * sizeof(char));+ *ismalloc= True; /* use malloc since memory not setup */+ coords= points= qh->temp_malloc= /* numinput and diminput >=2 by QH6220 */+ (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT));+ if (!coords || !qh->line || (qh->HALFspace && !qh->half_space)) {+ qh_fprintf(qh, qh->ferr, 6076, "qhull error: insufficient memory to read %d points\n",+ numinput);+ qh_errexit(qh, qh_ERRmem, NULL, NULL);+ }+ if (isdelaunay && qh->ATinfinity) {+ infinity= points + numinput * (*dimension);+ for (k= (*dimension) - 1; k--; )+ infinity[k]= 0.0;+ }+ maxcount= numinput * diminput;+ paraboloid= 0.0;+ while ((s= (isfirst ? s : fgets(qh->line, qh->maxline, qh->fin)))) {+ if (!isfirst) {+ linecount++;+ if (*s == 'e' || *s == 'E') {+ if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) {+ if (qh->CDDinput )+ break;+ else if (wasbegin)+ qh_fprintf(qh, qh->ferr, 7058, "qhull input warning: the input appears to be in cdd format. If so, use 'Fd'\n");+ }+ }+ }+ islong= False;+ while (*s) {+ while (isspace(*s))+ s++;+ value= qh_strtod(s, &t);+ if (s == t) {+ if (!*qh->rbox_command)+ strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);+ if (*s && !firsttext)+ firsttext= linecount;+ if (!islong && !firstshort && coordcount)+ firstshort= linecount;+ break;+ }+ if (!firstpoint)+ firstpoint= linecount;+ s= t;+ if (++tokcount > maxcount)+ continue;+ if (qh->HALFspace) {+ if (qh->CDDinput)+ *(coordp++)= -value; /* both coefficients and offset */+ else+ *(coordp++)= value;+ }else {+ *(coords++)= value;+ if (qh->CDDinput && !coordcount) {+ if (value != 1.0) {+ qh_fprintf(qh, qh->ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n",+ linecount);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ coords--;+ }else if (isdelaunay) {+ paraboloid += value * value;+ if (qh->ATinfinity) {+ if (qh->CDDinput)+ infinity[coordcount-1] += value;+ else+ infinity[coordcount] += value;+ }+ }+ }+ if (++coordcount == diminput) {+ coordcount= 0;+ if (isdelaunay) {+ *(coords++)= paraboloid;+ maximize_(maxboloid, paraboloid);+ paraboloid= 0.0;+ }else if (qh->HALFspace) {+ if (!qh_sethalfspace(qh, *dimension, coords, &coords, normalp, offsetp, qh->feasible_point)) {+ qh_fprintf(qh, qh->ferr, 8048, "The halfspace was on line %d\n", linecount);+ if (wasbegin)+ qh_fprintf(qh, qh->ferr, 8049, "The input appears to be in cdd format. If so, you should use option 'Fd'\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ coordp= qh->half_space;+ }+ while (isspace(*s))+ s++;+ if (*s) {+ islong= True;+ if (!firstlong)+ firstlong= linecount;+ }+ }+ }+ if (!islong && !firstshort && coordcount)+ firstshort= linecount;+ if (!isfirst && s - qh->line >= qh->maxline) {+ qh_fprintf(qh, qh->ferr, 6078, "qhull input error: line %d contained more than %d characters\n",+ linecount, (int) (s - qh->line)); /* WARN64 */+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ isfirst= False;+ }+ if (tokcount != maxcount) {+ newnum= fmin_(numinput, tokcount/diminput);+ qh_fprintf(qh, qh->ferr, 7073,"\+qhull warning: instead of %d %d-dimensional points, input contains\n\+%d points and %d extra coordinates. Line %d is the first\npoint",+ numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint);+ if (firsttext)+ qh_fprintf(qh, qh->ferr, 8051, ", line %d is the first comment", firsttext);+ if (firstshort)+ qh_fprintf(qh, qh->ferr, 8052, ", line %d is the first short\nline", firstshort);+ if (firstlong)+ qh_fprintf(qh, qh->ferr, 8053, ", line %d is the first long line", firstlong);+ qh_fprintf(qh, qh->ferr, 8054, ". Continue with %d points.\n", newnum);+ numinput= newnum;+ if (isdelaunay && qh->ATinfinity) {+ for (k= tokcount % diminput; k--; )+ infinity[k] -= *(--coords);+ *numpoints= newnum+1;+ }else {+ coords -= tokcount % diminput;+ *numpoints= newnum;+ }+ }+ if (isdelaunay && qh->ATinfinity) {+ for (k= (*dimension) -1; k--; )+ infinity[k] /= numinput;+ if (coords == infinity)+ coords += (*dimension) -1;+ else {+ for (k=0; k < (*dimension) -1; k++)+ *(coords++)= infinity[k];+ }+ *(coords++)= maxboloid * 1.1;+ }+ if (qh->rbox_command[0]) {+ qh->rbox_command[strlen(qh->rbox_command)-1]= '\0';+ if (!strcmp(qh->rbox_command, "./rbox D4"))+ qh_fprintf(qh, qh->ferr, 8055, "\n\+This is the qhull test case. If any errors or core dumps occur,\n\+recompile qhull with 'make new'. If errors still occur, there is\n\+an incompatibility. You should try a different compiler. You can also\n\+change the choices in user.h. If you discover the source of the problem,\n\+please send mail to qhull_bug@qhull.org.\n\+\n\+Type 'qhull' for a short list of options.\n");+ }+ qh_free(qh->line);+ qh->line= NULL;+ if (qh->half_space) {+ qh_free(qh->half_space);+ qh->half_space= NULL;+ }+ qh->temp_malloc= NULL;+ trace1((qh, qh->ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",+ numinput, diminput));+ return(points);+} /* readpoints */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="setfeasible">-</a>++ qh_setfeasible(qh, dim )+ set qh.feasible_point from qh.feasible_string in "n,n,n" or "n n n" format++ notes:+ "n,n,n" already checked by qh_initflags()+ see qh_readfeasible()+ called only once from qh_new_qhull, otherwise leaks memory+*/+void qh_setfeasible(qhT *qh, int dim) {+ int tokcount= 0;+ char *s;+ coordT *coords, value;++ if (!(s= qh->feasible_string)) {+ qh_fprintf(qh, qh->ferr, 6223, "\+qhull input error: halfspace intersection needs a feasible point.\n\+Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (!(qh->feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) {+ qh_fprintf(qh, qh->ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");+ qh_errexit(qh, qh_ERRmem, NULL, NULL);+ }+ coords= qh->feasible_point;+ while (*s) {+ value= qh_strtod(s, &s);+ if (++tokcount > dim) {+ qh_fprintf(qh, qh->ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n",+ qh->feasible_string, dim);+ break;+ }+ *(coords++)= value;+ if (*s)+ s++;+ }+ while (++tokcount <= dim)+ *(coords++)= 0.0;+} /* setfeasible */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="skipfacet">-</a>++ qh_skipfacet(qh, facet )+ returns 'True' if this facet is not to be printed++ notes:+ based on the user provided slice thresholds and 'good' specifications+*/+boolT qh_skipfacet(qhT *qh, facetT *facet) {+ facetT *neighbor, **neighborp;++ if (qh->PRINTneighbors) {+ if (facet->good)+ return !qh->PRINTgood;+ FOREACHneighbor_(facet) {+ if (neighbor->good)+ return False;+ }+ return True;+ }else if (qh->PRINTgood)+ return !facet->good;+ else if (!facet->normal)+ return True;+ return(!qh_inthresholds(qh, facet->normal, NULL));+} /* skipfacet */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="skipfilename">-</a>++ qh_skipfilename(qh, string )+ returns pointer to character after filename++ notes:+ skips leading spaces+ ends with spacing or eol+ if starts with ' or " ends with the same, skipping \' or \"+ For qhull, qh_argv_to_command() only uses double quotes+*/+char *qh_skipfilename(qhT *qh, char *filename) {+ char *s= filename; /* non-const due to return */+ char c;++ while (*s && isspace(*s))+ s++;+ c= *s++;+ if (c == '\0') {+ qh_fprintf(qh, qh->ferr, 6204, "qhull input error: filename expected, none found.\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (c == '\'' || c == '"') {+ while (*s !=c || s[-1] == '\\') {+ if (!*s) {+ qh_fprintf(qh, qh->ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ s++;+ }+ s++;+ }+ else while (*s && !isspace(*s))+ s++;+ return s;+} /* skipfilename */+
+ C/libqhull_r.c view
@@ -0,0 +1,1403 @@+/*<html><pre> -<a href="qh-qhull_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ libqhull_r.c+ Quickhull algorithm for convex hulls++ qhull() and top-level routines++ see qh-qhull_r.htm, libqhull.h, unix_r.c++ see qhull_ra.h for internal functions++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.c#2 $$Change: 2047 $+ $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $+*/++#include "qhull_ra.h"++/*============= functions in alphabetic order after qhull() =======*/++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="qhull">-</a>++ qh_qhull(qh)+ compute DIM3 convex hull of qh.num_points starting at qh.first_point+ qh->contains all global options and variables++ returns:+ returns polyhedron+ qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices,++ returns global variables+ qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex++ returns precision constants+ qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge++ notes:+ unless needed for output+ qh.max_vertex and qh.min_vertex are max/min due to merges++ see:+ to add individual points to either qh.num_points+ use qh_addpoint()++ if qh.GETarea+ qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea()++ design:+ record starting time+ initialize hull and partition points+ build convex hull+ unless early termination+ update facet->maxoutside for vertices, coplanar, and near-inside points+ error if temporary sets exist+ record end time+*/++void qh_qhull(qhT *qh) {+ int numoutside;++ qh->hulltime= qh_CPUclock;+ if (qh->RERUN || qh->JOGGLEmax < REALmax/2)+ qh_build_withrestart(qh);+ else {+ qh_initbuild(qh);+ qh_buildhull(qh);+ }+ if (!qh->STOPpoint && !qh->STOPcone) {+ if (qh->ZEROall_ok && !qh->TESTvneighbors && qh->MERGEexact)+ qh_checkzero(qh, qh_ALL);+ if (qh->ZEROall_ok && !qh->TESTvneighbors && !qh->WAScoplanar) {+ trace2((qh, qh->ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points. Post-merging and check of maxout not needed.\n"));+ qh->DOcheckmax= False;+ }else {+ if (qh->MERGEexact || (qh->hull_dim > qh_DIMreduceBuild && qh->PREmerge))+ qh_postmerge(qh, "First post-merge", qh->premerge_centrum, qh->premerge_cos,+ (qh->POSTmerge ? False : qh->TESTvneighbors));+ else if (!qh->POSTmerge && qh->TESTvneighbors)+ qh_postmerge(qh, "For testing vertex neighbors", qh->premerge_centrum,+ qh->premerge_cos, True);+ if (qh->POSTmerge)+ qh_postmerge(qh, "For post-merging", qh->postmerge_centrum,+ qh->postmerge_cos, qh->TESTvneighbors);+ if (qh->visible_list == qh->facet_list) { /* i.e., merging done */+ qh->findbestnew= True;+ qh_partitionvisible(qh /*qh.visible_list*/, !qh_ALL, &numoutside);+ qh->findbestnew= False;+ qh_deletevisible(qh /*qh.visible_list*/);+ qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);+ }+ }+ if (qh->DOcheckmax){+ if (qh->REPORTfreq) {+ qh_buildtracing(qh, NULL, NULL);+ qh_fprintf(qh, qh->ferr, 8115, "\nTesting all coplanar points.\n");+ }+ qh_check_maxout(qh);+ }+ if (qh->KEEPnearinside && !qh->maxoutdone)+ qh_nearcoplanar(qh);+ }+ if (qh_setsize(qh, qh->qhmem.tempstack) != 0) {+ qh_fprintf(qh, qh->ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n",+ qh_setsize(qh, qh->qhmem.tempstack));+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ qh->hulltime= qh_CPUclock - qh->hulltime;+ qh->QHULLfinished= True;+ trace1((qh, qh->ferr, 1036, "Qhull: algorithm completed\n"));+} /* qhull */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="addpoint">-</a>++ qh_addpoint(qh, furthest, facet, checkdist )+ add point (usually furthest point) above facet to hull+ if checkdist,+ check that point is above facet.+ if point is not outside of the hull, uses qh_partitioncoplanar()+ assumes that facet is defined by qh_findbestfacet()+ else if facet specified,+ assumes that point is above facet (major damage if below)+ for Delaunay triangulations,+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.++ returns:+ returns False if user requested an early termination+ qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined+ updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices+ clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside)+ if unknown point, adds a pointer to qh.other_points+ do not deallocate the point's coordinates++ notes:+ assumes point is near its best facet and not at a local minimum of a lens+ distributions. Use qh_findbestfacet to avoid this case.+ uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets++ see also:+ qh_triangulate() -- triangulate non-simplicial facets++ design:+ add point to other_points if needed+ if checkdist+ if point not above facet+ partition coplanar point+ exit+ exit if pre STOPpoint requested+ find horizon and visible facets for point+ make new facets for point to horizon+ make hyperplanes for point+ compute balance statistics+ match neighboring new facets+ update vertex neighbors and delete interior vertices+ exit if STOPcone requested+ merge non-convex new facets+ if merge found, many merges, or 'Qf'+ use qh_findbestnew() instead of qh_findbest()+ partition outside points from visible facets+ delete visible facets+ check polyhedron if requested+ exit if post STOPpoint requested+ reset working lists of facets and vertices+*/+boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist) {+ int goodvisible, goodhorizon;+ vertexT *vertex;+ facetT *newfacet;+ realT dist, newbalance, pbalance;+ boolT isoutside= False;+ int numpart, numpoints, numnew, firstnew;++ qh->maxoutdone= False;+ if (qh_pointid(qh, furthest) == qh_IDunknown)+ qh_setappend(qh, &qh->other_points, furthest);+ if (!facet) {+ qh_fprintf(qh, qh->ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ if (checkdist) {+ facet= qh_findbest(qh, furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper,+ &dist, &isoutside, &numpart);+ zzadd_(Zpartition, numpart);+ if (!isoutside) {+ zinc_(Znotmax); /* last point of outsideset is no longer furthest. */+ facet->notfurthest= True;+ qh_partitioncoplanar(qh, furthest, facet, &dist);+ return True;+ }+ }+ qh_buildtracing(qh, furthest, facet);+ if (qh->STOPpoint < 0 && qh->furthest_id == -qh->STOPpoint-1) {+ facet->notfurthest= True;+ return False;+ }+ qh_findhorizon(qh, furthest, facet, &goodvisible, &goodhorizon);+ if (qh->ONLYgood && !(goodvisible+goodhorizon) && !qh->GOODclosest) {+ zinc_(Znotgood);+ facet->notfurthest= True;+ /* last point of outsideset is no longer furthest. This is ok+ since all points of the outside are likely to be bad */+ qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);+ return True;+ }+ zzinc_(Zprocessed);+ firstnew= qh->facet_id;+ vertex= qh_makenewfacets(qh, furthest /*visible_list, attaches if !ONLYgood */);+ qh_makenewplanes(qh /* newfacet_list */);+ numnew= qh->facet_id - firstnew;+ newbalance= numnew - (realT) (qh->num_facets-qh->num_visible)+ * qh->hull_dim/qh->num_vertices;+ wadd_(Wnewbalance, newbalance);+ wadd_(Wnewbalance2, newbalance * newbalance);+ if (qh->ONLYgood+ && !qh_findgood(qh, qh->newfacet_list, goodhorizon) && !qh->GOODclosest) {+ FORALLnew_facets+ qh_delfacet(qh, newfacet);+ qh_delvertex(qh, vertex);+ qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);+ zinc_(Znotgoodnew);+ facet->notfurthest= True;+ return True;+ }+ if (qh->ONLYgood)+ qh_attachnewfacets(qh /*visible_list*/);+ qh_matchnewfacets(qh);+ qh_updatevertices(qh);+ if (qh->STOPcone && qh->furthest_id == qh->STOPcone-1) {+ facet->notfurthest= True;+ return False; /* visible_list etc. still defined */+ }+ qh->findbestnew= False;+ if (qh->PREmerge || qh->MERGEexact) {+ qh_premerge(qh, vertex, qh->premerge_centrum, qh->premerge_cos);+ if (qh_USEfindbestnew)+ qh->findbestnew= True;+ else {+ FORALLnew_facets {+ if (!newfacet->simplicial) {+ qh->findbestnew= True; /* use qh_findbestnew instead of qh_findbest*/+ break;+ }+ }+ }+ }else if (qh->BESToutside)+ qh->findbestnew= True;+ qh_partitionvisible(qh /*qh.visible_list*/, !qh_ALL, &numpoints);+ qh->findbestnew= False;+ qh->findbest_notsharp= False;+ zinc_(Zpbalance);+ pbalance= numpoints - (realT) qh->hull_dim /* assumes all points extreme */+ * (qh->num_points - qh->num_vertices)/qh->num_vertices;+ wadd_(Wpbalance, pbalance);+ wadd_(Wpbalance2, pbalance * pbalance);+ qh_deletevisible(qh /*qh.visible_list*/);+ zmax_(Zmaxvertex, qh->num_vertices);+ qh->NEWfacets= False;+ if (qh->IStracing >= 4) {+ if (qh->num_facets < 2000)+ qh_printlists(qh);+ qh_printfacetlist(qh, qh->newfacet_list, NULL, True);+ qh_checkpolygon(qh, qh->facet_list);+ }else if (qh->CHECKfrequently) {+ if (qh->num_facets < 50)+ qh_checkpolygon(qh, qh->facet_list);+ else+ qh_checkpolygon(qh, qh->newfacet_list);+ }+ if (qh->STOPpoint > 0 && qh->furthest_id == qh->STOPpoint-1)+ return False;+ qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);+ /* qh_triangulate(qh); to test qh.TRInormals */+ trace2((qh, qh->ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n",+ qh_pointid(qh, furthest), numnew, newbalance, pbalance));+ return True;+} /* addpoint */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="build_withrestart">-</a>++ qh_build_withrestart(qh)+ allow restarts due to qh.JOGGLEmax while calling qh_buildhull()+ qh_errexit always undoes qh_build_withrestart()+ qh.FIRSTpoint/qh.NUMpoints is point array+ it may be moved by qh_joggleinput(qh)+*/+void qh_build_withrestart(qhT *qh) {+ int restart;++ qh->ALLOWrestart= True;+ while (True) {+ restart= setjmp(qh->restartexit); /* simple statement for CRAY J916 */+ if (restart) { /* only from qh_precision() */+ zzinc_(Zretry);+ wmax_(Wretrymax, qh->JOGGLEmax);+ /* QH7078 warns about using 'TCn' with 'QJn' */+ qh->STOPcone= qh_IDunknown; /* if break from joggle, prevents normal output */+ }+ if (!qh->RERUN && qh->JOGGLEmax < REALmax/2) {+ if (qh->build_cnt > qh_JOGGLEmaxretry) {+ qh_fprintf(qh, qh->ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\+ with joggled input. Increase joggle above 'QJ%2.2g'\n\+ or modify qh_JOGGLE... parameters in user.h\n",+ qh->build_cnt, qh->JOGGLEmax);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ if (qh->build_cnt && !restart)+ break;+ }else if (qh->build_cnt && qh->build_cnt >= qh->RERUN)+ break;+ qh->STOPcone= 0;+ qh_freebuild(qh, True); /* first call is a nop */+ qh->build_cnt++;+ if (!qh->qhull_optionsiz)+ qh->qhull_optionsiz= (int)strlen(qh->qhull_options); /* WARN64 */+ else {+ qh->qhull_options [qh->qhull_optionsiz]= '\0';+ qh->qhull_optionlen= qh_OPTIONline; /* starts a new line */+ }+ qh_option(qh, "_run", &qh->build_cnt, NULL);+ if (qh->build_cnt == qh->RERUN) {+ qh->IStracing= qh->TRACElastrun; /* duplicated from qh_initqhull_globals */+ if (qh->TRACEpoint != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) {+ qh->TRACElevel= (qh->IStracing? qh->IStracing : 3);+ qh->IStracing= 0;+ }+ qh->qhmem.IStracing= qh->IStracing;+ }+ if (qh->JOGGLEmax < REALmax/2)+ qh_joggleinput(qh);+ qh_initbuild(qh);+ qh_buildhull(qh);+ if (qh->JOGGLEmax < REALmax/2 && !qh->MERGING)+ qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);+ }+ qh->ALLOWrestart= False;+} /* qh_build_withrestart */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="buildhull">-</a>++ qh_buildhull(qh)+ construct a convex hull by adding outside points one at a time++ returns:++ notes:+ may be called multiple times+ checks facet and vertex lists for incorrect flags+ to recover from STOPcone, call qh_deletevisible and qh_resetlists++ design:+ check visible facet and newfacet flags+ check newlist vertex flags and qh.STOPcone/STOPpoint+ for each facet with a furthest outside point+ add point to facet+ exit if qh.STOPcone or qh.STOPpoint requested+ if qh.NARROWhull for initial simplex+ partition remaining outside points to coplanar sets+*/+void qh_buildhull(qhT *qh) {+ facetT *facet;+ pointT *furthest;+ vertexT *vertex;+ int id;++ trace1((qh, qh->ferr, 1037, "qh_buildhull: start build hull\n"));+ FORALLfacets {+ if (facet->visible || facet->newfacet) {+ qh_fprintf(qh, qh->ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n",+ facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ }+ FORALLvertices {+ if (vertex->newlist) {+ qh_fprintf(qh, qh->ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n",+ vertex->id);+ qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ id= qh_pointid(qh, vertex->point);+ if ((qh->STOPpoint>0 && id == qh->STOPpoint-1) ||+ (qh->STOPpoint<0 && id == -qh->STOPpoint-1) ||+ (qh->STOPcone>0 && id == qh->STOPcone-1)) {+ trace1((qh, qh->ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id));+ return;+ }+ }+ qh->facet_next= qh->facet_list; /* advance facet when processed */+ while ((furthest= qh_nextfurthest(qh, &facet))) {+ qh->num_outside--; /* if ONLYmax, furthest may not be outside */+ if (!qh_addpoint(qh, furthest, facet, qh->ONLYmax))+ break;+ }+ if (qh->NARROWhull) /* move points from outsideset to coplanarset */+ qh_outcoplanar(qh /* facet_list */ );+ if (qh->num_outside && !furthest) {+ qh_fprintf(qh, qh->ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh->num_outside);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ trace1((qh, qh->ferr, 1039, "qh_buildhull: completed the hull construction\n"));+} /* buildhull */+++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="buildtracing">-</a>++ qh_buildtracing(qh, furthest, facet )+ trace an iteration of qh_buildhull() for furthest point and facet+ if !furthest, prints progress message++ returns:+ tracks progress with qh.lastreport+ updates qh.furthest_id (-3 if furthest is NULL)+ also resets visit_id, vertext_visit on wrap around++ see:+ qh_tracemerging()++ design:+ if !furthest+ print progress message+ exit+ if 'TFn' iteration+ print progress message+ else if tracing+ trace furthest point and facet+ reset qh.visit_id and qh.vertex_visit if overflow may occur+ set qh.furthest_id for tracing+*/+void qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet) {+ realT dist= 0;+ float cpu;+ int total, furthestid;+ time_t timedata;+ struct tm *tp;+ vertexT *vertex;++ qh->old_randomdist= qh->RANDOMdist;+ qh->RANDOMdist= False;+ if (!furthest) {+ time(&timedata);+ tp= localtime(&timedata);+ cpu= (float)qh_CPUclock - (float)qh->hulltime;+ cpu /= (float)qh_SECticks;+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);+ qh_fprintf(qh, qh->ferr, 8118, "\n\+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\+ The current hull contains %d facets and %d vertices. Last point was p%d\n",+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1,+ total, qh->num_facets, qh->num_vertices, qh->furthest_id);+ return;+ }+ furthestid= qh_pointid(qh, furthest);+ if (qh->TRACEpoint == furthestid) {+ qh->IStracing= qh->TRACElevel;+ qh->qhmem.IStracing= qh->TRACElevel;+ }else if (qh->TRACEpoint != qh_IDunknown && qh->TRACEdist < REALmax/2) {+ qh->IStracing= 0;+ qh->qhmem.IStracing= 0;+ }+ if (qh->REPORTfreq && (qh->facet_id-1 > qh->lastreport+qh->REPORTfreq)) {+ qh->lastreport= qh->facet_id-1;+ time(&timedata);+ tp= localtime(&timedata);+ cpu= (float)qh_CPUclock - (float)qh->hulltime;+ cpu /= (float)qh_SECticks;+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);+ zinc_(Zdistio);+ qh_distplane(qh, furthest, facet, &dist);+ qh_fprintf(qh, qh->ferr, 8119, "\n\+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\+ The current hull contains %d facets and %d vertices. There are %d\n\+ outside points. Next is point p%d(v%d), %2.2g above f%d.\n",+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1,+ total, qh->num_facets, qh->num_vertices, qh->num_outside+1,+ furthestid, qh->vertex_id, dist, getid_(facet));+ }else if (qh->IStracing >=1) {+ cpu= (float)qh_CPUclock - (float)qh->hulltime;+ cpu /= (float)qh_SECticks;+ qh_distplane(qh, furthest, facet, &dist);+ qh_fprintf(qh, qh->ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs. Previous was p%d.\n",+ furthestid, qh->vertex_id, qh->num_facets, dist,+ getid_(facet), qh->num_outside+1, cpu, qh->furthest_id);+ }+ zmax_(Zvisit2max, (int)qh->visit_id/2);+ if (qh->visit_id > (unsigned) INT_MAX) { /* 31 bits */+ zinc_(Zvisit);+ qh->visit_id= 0;+ FORALLfacets+ facet->visitid= 0;+ }+ zmax_(Zvvisit2max, (int)qh->vertex_visit/2);+ if (qh->vertex_visit > (unsigned) INT_MAX) { /* 31 bits */ + zinc_(Zvvisit);+ qh->vertex_visit= 0;+ FORALLvertices+ vertex->visitid= 0;+ }+ qh->furthest_id= furthestid;+ qh->RANDOMdist= qh->old_randomdist;+} /* buildtracing */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="errexit2">-</a>++ qh_errexit2(qh, exitcode, facet, otherfacet )+ return exitcode to system after an error+ report two facets++ returns:+ assumes exitcode non-zero++ see:+ normally use qh_errexit() in user.c(reports a facet and a ridge)+*/+void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet) {++ qh_errprint(qh, "ERRONEOUS", facet, otherfacet, NULL, NULL);+ qh_errexit(qh, exitcode, NULL, NULL);+} /* errexit2 */+++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="findhorizon">-</a>++ qh_findhorizon(qh, point, facet, goodvisible, goodhorizon )+ given a visible facet, find the point's horizon and visible facets+ for all facets, !facet-visible++ returns:+ returns qh.visible_list/num_visible with all visible facets+ marks visible facets with ->visible+ updates count of good visible and good horizon facets+ updates qh.max_outside, qh.max_vertex, facet->maxoutside++ see:+ similar to qh_delpoint()++ design:+ move facet to qh.visible_list at end of qh.facet_list+ for all visible facets+ for each unvisited neighbor of a visible facet+ compute distance of point to neighbor+ if point above neighbor+ move neighbor to end of qh.visible_list+ else if point is coplanar with neighbor+ update qh.max_outside, qh.max_vertex, neighbor->maxoutside+ mark neighbor coplanar (will create a samecycle later)+ update horizon statistics+*/+void qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) {+ facetT *neighbor, **neighborp, *visible;+ int numhorizon= 0, coplanar= 0;+ realT dist;++ trace1((qh, qh->ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(qh, point),facet->id));+ *goodvisible= *goodhorizon= 0;+ zinc_(Ztotvisible);+ qh_removefacet(qh, facet); /* visible_list at end of qh->facet_list */+ qh_appendfacet(qh, facet);+ qh->num_visible= 1;+ if (facet->good)+ (*goodvisible)++;+ qh->visible_list= facet;+ facet->visible= True;+ facet->f.replace= NULL;+ if (qh->IStracing >=4)+ qh_errprint(qh, "visible", facet, NULL, NULL, NULL);+ qh->visit_id++;+ FORALLvisible_facets {+ if (visible->tricoplanar && !qh->TRInormals) {+ qh_fprintf(qh, qh->ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n");+ qh_errexit(qh, qh_ERRqhull, visible, NULL);+ }+ visible->visitid= qh->visit_id;+ FOREACHneighbor_(visible) {+ if (neighbor->visitid == qh->visit_id)+ continue;+ neighbor->visitid= qh->visit_id;+ zzinc_(Znumvisibility);+ qh_distplane(qh, point, neighbor, &dist);+ if (dist > qh->MINvisible) {+ zinc_(Ztotvisible);+ qh_removefacet(qh, neighbor); /* append to end of qh->visible_list */+ qh_appendfacet(qh, neighbor);+ neighbor->visible= True;+ neighbor->f.replace= NULL;+ qh->num_visible++;+ if (neighbor->good)+ (*goodvisible)++;+ if (qh->IStracing >=4)+ qh_errprint(qh, "visible", neighbor, NULL, NULL, NULL);+ }else {+ if (dist > - qh->MAXcoplanar) {+ neighbor->coplanar= True;+ zzinc_(Zcoplanarhorizon);+ qh_precision(qh, "coplanar horizon");+ coplanar++;+ if (qh->MERGING) {+ if (dist > 0) {+ maximize_(qh->max_outside, dist);+ maximize_(qh->max_vertex, dist);+#if qh_MAXoutside+ maximize_(neighbor->maxoutside, dist);+#endif+ }else+ minimize_(qh->min_vertex, dist); /* due to merge later */+ }+ trace2((qh, qh->ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh->MINvisible(%2.7g)\n",+ qh_pointid(qh, point), neighbor->id, dist, qh->MINvisible));+ }else+ neighbor->coplanar= False;+ zinc_(Ztothorizon);+ numhorizon++;+ if (neighbor->good)+ (*goodhorizon)++;+ if (qh->IStracing >=4)+ qh_errprint(qh, "horizon", neighbor, NULL, NULL, NULL);+ }+ }+ }+ if (!numhorizon) {+ qh_precision(qh, "empty horizon");+ qh_fprintf(qh, qh->ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\+QhullPoint p%d was above all facets.\n", qh_pointid(qh, point));+ qh_printfacetlist(qh, qh->facet_list, NULL, True);+ qh_errexit(qh, qh_ERRprec, NULL, NULL);+ }+ trace1((qh, qh->ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n",+ numhorizon, *goodhorizon, qh->num_visible, *goodvisible, coplanar));+ if (qh->IStracing >= 4 && qh->num_facets < 50)+ qh_printlists(qh);+} /* findhorizon */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="nextfurthest">-</a>++ qh_nextfurthest(qh, visible )+ returns next furthest point and visible facet for qh_addpoint()+ starts search at qh.facet_next++ returns:+ removes furthest point from outside set+ NULL if none available+ advances qh.facet_next over facets with empty outside sets++ design:+ for each facet from qh.facet_next+ if empty outside set+ advance qh.facet_next+ else if qh.NARROWhull+ determine furthest outside point+ if furthest point is not outside+ advance qh.facet_next(point will be coplanar)+ remove furthest point from outside set+*/+pointT *qh_nextfurthest(qhT *qh, facetT **visible) {+ facetT *facet;+ int size, idx;+ realT randr, dist;+ pointT *furthest;++ while ((facet= qh->facet_next) != qh->facet_tail) {+ if (!facet->outsideset) {+ qh->facet_next= facet->next;+ continue;+ }+ SETreturnsize_(facet->outsideset, size);+ if (!size) {+ qh_setfree(qh, &facet->outsideset);+ qh->facet_next= facet->next;+ continue;+ }+ if (qh->NARROWhull) {+ if (facet->notfurthest)+ qh_furthestout(qh, facet);+ furthest= (pointT*)qh_setlast(facet->outsideset);+#if qh_COMPUTEfurthest+ qh_distplane(qh, furthest, facet, &dist);+ zinc_(Zcomputefurthest);+#else+ dist= facet->furthestdist;+#endif+ if (dist < qh->MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */+ qh->facet_next= facet->next;+ continue;+ }+ }+ if (!qh->RANDOMoutside && !qh->VIRTUALmemory) {+ if (qh->PICKfurthest) {+ qh_furthestnext(qh /* qh->facet_list */);+ facet= qh->facet_next;+ }+ *visible= facet;+ return((pointT*)qh_setdellast(facet->outsideset));+ }+ if (qh->RANDOMoutside) {+ int outcoplanar = 0;+ if (qh->NARROWhull) {+ FORALLfacets {+ if (facet == qh->facet_next)+ break;+ if (facet->outsideset)+ outcoplanar += qh_setsize(qh, facet->outsideset);+ }+ }+ randr= qh_RANDOMint;+ randr= randr/(qh_RANDOMmax+1);+ idx= (int)floor((qh->num_outside - outcoplanar) * randr);+ FORALLfacet_(qh->facet_next) {+ if (facet->outsideset) {+ SETreturnsize_(facet->outsideset, size);+ if (!size)+ qh_setfree(qh, &facet->outsideset);+ else if (size > idx) {+ *visible= facet;+ return((pointT*)qh_setdelnth(qh, facet->outsideset, idx));+ }else+ idx -= size;+ }+ }+ qh_fprintf(qh, qh->ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n",+ qh->num_outside, idx+1, randr);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }else { /* VIRTUALmemory */+ facet= qh->facet_tail->previous;+ if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) {+ if (facet->outsideset)+ qh_setfree(qh, &facet->outsideset);+ qh_removefacet(qh, facet);+ qh_prependfacet(qh, facet, &qh->facet_list);+ continue;+ }+ *visible= facet;+ return furthest;+ }+ }+ return NULL;+} /* nextfurthest */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="partitionall">-</a>++ qh_partitionall(qh, vertices, points, numpoints )+ partitions all points in points/numpoints to the outsidesets of facets+ vertices= vertices in qh.facet_list(!partitioned)++ returns:+ builds facet->outsideset+ does not partition qh.GOODpoint+ if qh.ONLYgood && !qh.MERGING,+ does not partition qh.GOODvertex++ notes:+ faster if qh.facet_list sorted by anticipated size of outside set++ design:+ initialize pointset with all points+ remove vertices from pointset+ remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint)+ for all facets+ for all remaining points in pointset+ compute distance from point to facet+ if point is outside facet+ remove point from pointset (by not reappending)+ update bestpoint+ append point or old bestpoint to facet's outside set+ append bestpoint to facet's outside set (furthest)+ for all points remaining in pointset+ partition point into facets' outside sets and coplanar sets+*/+void qh_partitionall(qhT *qh, setT *vertices, pointT *points, int numpoints){+ setT *pointset;+ vertexT *vertex, **vertexp;+ pointT *point, **pointp, *bestpoint;+ int size, point_i, point_n, point_end, remaining, i, id;+ facetT *facet;+ realT bestdist= -REALmax, dist, distoutside;++ trace1((qh, qh->ferr, 1042, "qh_partitionall: partition all points into outside sets\n"));+ pointset= qh_settemp(qh, numpoints);+ qh->num_outside= 0;+ pointp= SETaddr_(pointset, pointT);+ for (i=numpoints, point= points; i--; point += qh->hull_dim)+ *(pointp++)= point;+ qh_settruncate(qh, pointset, numpoints);+ FOREACHvertex_(vertices) {+ if ((id= qh_pointid(qh, vertex->point)) >= 0)+ SETelem_(pointset, id)= NULL;+ }+ id= qh_pointid(qh, qh->GOODpointp);+ if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)+ SETelem_(pointset, id)= NULL;+ if (qh->GOODvertexp && qh->ONLYgood && !qh->MERGING) { /* matches qhull()*/+ if ((id= qh_pointid(qh, qh->GOODvertexp)) >= 0)+ SETelem_(pointset, id)= NULL;+ }+ if (!qh->BESToutside) { /* matches conditional for qh_partitionpoint below */+ distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */+ zval_(Ztotpartition)= qh->num_points - qh->hull_dim - 1; /*misses GOOD... */+ remaining= qh->num_facets;+ point_end= numpoints;+ FORALLfacets {+ size= point_end/(remaining--) + 100;+ facet->outsideset= qh_setnew(qh, size);+ bestpoint= NULL;+ point_end= 0;+ FOREACHpoint_i_(qh, pointset) {+ if (point) {+ zzinc_(Zpartitionall);+ qh_distplane(qh, point, facet, &dist);+ if (dist < distoutside)+ SETelem_(pointset, point_end++)= point;+ else {+ qh->num_outside++;+ if (!bestpoint) {+ bestpoint= point;+ bestdist= dist;+ }else if (dist > bestdist) {+ qh_setappend(qh, &facet->outsideset, bestpoint);+ bestpoint= point;+ bestdist= dist;+ }else+ qh_setappend(qh, &facet->outsideset, point);+ }+ }+ }+ if (bestpoint) {+ qh_setappend(qh, &facet->outsideset, bestpoint);+#if !qh_COMPUTEfurthest+ facet->furthestdist= bestdist;+#endif+ }else+ qh_setfree(qh, &facet->outsideset);+ qh_settruncate(qh, pointset, point_end);+ }+ }+ /* if !qh->BESToutside, pointset contains points not assigned to outsideset */+ if (qh->BESToutside || qh->MERGING || qh->KEEPcoplanar || qh->KEEPinside) {+ qh->findbestnew= True;+ FOREACHpoint_i_(qh, pointset) {+ if (point)+ qh_partitionpoint(qh, point, qh->facet_list);+ }+ qh->findbestnew= False;+ }+ zzadd_(Zpartitionall, zzval_(Zpartition));+ zzval_(Zpartition)= 0;+ qh_settempfree(qh, &pointset);+ if (qh->IStracing >= 4)+ qh_printfacetlist(qh, qh->facet_list, NULL, True);+} /* partitionall */+++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="partitioncoplanar">-</a>++ qh_partitioncoplanar(qh, point, facet, dist )+ partition coplanar point to a facet+ dist is distance from point to facet+ if dist NULL,+ searches for bestfacet and does nothing if inside+ if qh.findbestnew set,+ searches new facets instead of using qh_findbest()++ returns:+ qh.max_ouside updated+ if qh.KEEPcoplanar or qh.KEEPinside+ point assigned to best coplanarset++ notes:+ facet->maxoutside is updated at end by qh_check_maxout++ design:+ if dist undefined+ find best facet for point+ if point sufficiently below facet (depends on qh.NEARinside and qh.KEEPinside)+ exit+ if keeping coplanar/nearinside/inside points+ if point is above furthest coplanar point+ append point to coplanar set (it is the new furthest)+ update qh.max_outside+ else+ append point one before end of coplanar set+ else if point is clearly outside of qh.max_outside and bestfacet->coplanarset+ and bestfacet is more than perpendicular to facet+ repartition the point using qh_findbest() -- it may be put on an outsideset+ else+ update qh.max_outside+*/+void qh_partitioncoplanar(qhT *qh, pointT *point, facetT *facet, realT *dist) {+ facetT *bestfacet;+ pointT *oldfurthest;+ realT bestdist, dist2= 0, angle;+ int numpart= 0, oldfindbest;+ boolT isoutside;++ qh->WAScoplanar= True;+ if (!dist) {+ if (qh->findbestnew)+ bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh_ALL, &isoutside, &numpart);+ else+ bestfacet= qh_findbest(qh, point, facet, qh_ALL, !qh_ISnewfacets, qh->DELAUNAY,+ &bestdist, &isoutside, &numpart);+ zinc_(Ztotpartcoplanar);+ zzadd_(Zpartcoplanar, numpart);+ if (!qh->DELAUNAY && !qh->KEEPinside) { /* for 'd', bestdist skips upperDelaunay facets */+ if (qh->KEEPnearinside) {+ if (bestdist < -qh->NEARinside) {+ zinc_(Zcoplanarinside);+ trace4((qh, qh->ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n",+ qh_pointid(qh, point), bestfacet->id, bestdist, qh->findbestnew));+ return;+ }+ }else if (bestdist < -qh->MAXcoplanar) {+ trace4((qh, qh->ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n",+ qh_pointid(qh, point), bestfacet->id, bestdist, qh->findbestnew));+ zinc_(Zcoplanarinside);+ return;+ }+ }+ }else {+ bestfacet= facet;+ bestdist= *dist;+ }+ if (bestdist > qh->max_outside) {+ if (!dist && facet != bestfacet) {+ zinc_(Zpartangle);+ angle= qh_getangle(qh, facet->normal, bestfacet->normal);+ if (angle < 0) {+ /* typically due to deleted vertex and coplanar facets, e.g.,+ RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */+ zinc_(Zpartflip);+ trace2((qh, qh->ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d. It is above flipped facet f%d dist %2.2g\n",+ qh_pointid(qh, point), facet->id, bestfacet->id, bestdist));+ oldfindbest= qh->findbestnew;+ qh->findbestnew= False;+ qh_partitionpoint(qh, point, bestfacet);+ qh->findbestnew= oldfindbest;+ return;+ }+ }+ qh->max_outside= bestdist;+ if (bestdist > qh->TRACEdist) {+ qh_fprintf(qh, qh->ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n",+ qh_pointid(qh, point), facet->id, bestdist, bestfacet->id, qh->furthest_id);+ qh_errprint(qh, "DISTANT", facet, bestfacet, NULL, NULL);+ }+ }+ if (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside) {+ oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset);+ if (oldfurthest) {+ zinc_(Zcomputefurthest);+ qh_distplane(qh, oldfurthest, bestfacet, &dist2);+ }+ if (!oldfurthest || dist2 < bestdist)+ qh_setappend(qh, &bestfacet->coplanarset, point);+ else+ qh_setappend2ndlast(qh, &bestfacet->coplanarset, point);+ }+ trace4((qh, qh->ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n",+ qh_pointid(qh, point), bestfacet->id, bestdist));+} /* partitioncoplanar */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="partitionpoint">-</a>++ qh_partitionpoint(qh, point, facet )+ assigns point to an outside set, coplanar set, or inside set (i.e., dropt)+ if qh.findbestnew+ uses qh_findbestnew() to search all new facets+ else+ uses qh_findbest()++ notes:+ after qh_distplane(), this and qh_findbest() are most expensive in 3-d++ design:+ find best facet for point+ (either exhaustive search of new facets or directed search from facet)+ if qh.NARROWhull+ retain coplanar and nearinside points as outside points+ if point is outside bestfacet+ if point above furthest point for bestfacet+ append point to outside set (it becomes the new furthest)+ if outside set was empty+ move bestfacet to end of qh.facet_list (i.e., after qh.facet_next)+ update bestfacet->furthestdist+ else+ append point one before end of outside set+ else if point is coplanar to bestfacet+ if keeping coplanar points or need to update qh.max_outside+ partition coplanar point into bestfacet+ else if near-inside point+ partition as coplanar point into bestfacet+ else is an inside point+ if keeping inside points+ partition as coplanar point into bestfacet+*/+void qh_partitionpoint(qhT *qh, pointT *point, facetT *facet) {+ realT bestdist;+ boolT isoutside;+ facetT *bestfacet;+ int numpart;+#if qh_COMPUTEfurthest+ realT dist;+#endif++ if (qh->findbestnew)+ bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh->BESToutside, &isoutside, &numpart);+ else+ bestfacet= qh_findbest(qh, point, facet, qh->BESToutside, qh_ISnewfacets, !qh_NOupper,+ &bestdist, &isoutside, &numpart);+ zinc_(Ztotpartition);+ zzadd_(Zpartition, numpart);+ if (qh->NARROWhull) {+ if (qh->DELAUNAY && !isoutside && bestdist >= -qh->MAXcoplanar)+ qh_precision(qh, "nearly incident point(narrow hull)");+ if (qh->KEEPnearinside) {+ if (bestdist >= -qh->NEARinside)+ isoutside= True;+ }else if (bestdist >= -qh->MAXcoplanar)+ isoutside= True;+ }++ if (isoutside) {+ if (!bestfacet->outsideset+ || !qh_setlast(bestfacet->outsideset)) {+ qh_setappend(qh, &(bestfacet->outsideset), point);+ if (!bestfacet->newfacet) {+ qh_removefacet(qh, bestfacet); /* make sure it's after qh->facet_next */+ qh_appendfacet(qh, bestfacet);+ }+#if !qh_COMPUTEfurthest+ bestfacet->furthestdist= bestdist;+#endif+ }else {+#if qh_COMPUTEfurthest+ zinc_(Zcomputefurthest);+ qh_distplane(qh, oldfurthest, bestfacet, &dist);+ if (dist < bestdist)+ qh_setappend(qh, &(bestfacet->outsideset), point);+ else+ qh_setappend2ndlast(qh, &(bestfacet->outsideset), point);+#else+ if (bestfacet->furthestdist < bestdist) {+ qh_setappend(qh, &(bestfacet->outsideset), point);+ bestfacet->furthestdist= bestdist;+ }else+ qh_setappend2ndlast(qh, &(bestfacet->outsideset), point);+#endif+ }+ qh->num_outside++;+ trace4((qh, qh->ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n",+ qh_pointid(qh, point), bestfacet->id, bestfacet->newfacet));+ }else if (qh->DELAUNAY || bestdist >= -qh->MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */+ zzinc_(Zcoplanarpart);+ if (qh->DELAUNAY)+ qh_precision(qh, "nearly incident point");+ if ((qh->KEEPcoplanar + qh->KEEPnearinside) || bestdist > qh->max_outside)+ qh_partitioncoplanar(qh, point, bestfacet, &bestdist);+ else {+ trace4((qh, qh->ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n",+ qh_pointid(qh, point), bestfacet->id));+ }+ }else if (qh->KEEPnearinside && bestdist > -qh->NEARinside) {+ zinc_(Zpartnear);+ qh_partitioncoplanar(qh, point, bestfacet, &bestdist);+ }else {+ zinc_(Zpartinside);+ trace4((qh, qh->ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n",+ qh_pointid(qh, point), bestfacet->id, bestdist));+ if (qh->KEEPinside)+ qh_partitioncoplanar(qh, point, bestfacet, &bestdist);+ }+} /* partitionpoint */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="partitionvisible">-</a>++ qh_partitionvisible(qh, allpoints, numoutside )+ partitions points in visible facets to qh.newfacet_list+ qh.visible_list= visible facets+ for visible facets+ 1st neighbor (if any) points to a horizon facet or a new facet+ if allpoints(!used),+ repartitions coplanar points++ returns:+ updates outside sets and coplanar sets of qh.newfacet_list+ updates qh.num_outside (count of outside points)++ notes:+ qh.findbest_notsharp should be clear (extra work if set)++ design:+ for all visible facets with outside set or coplanar set+ select a newfacet for visible facet+ if outside set+ partition outside set into new facets+ if coplanar set and keeping coplanar/near-inside/inside points+ if allpoints+ partition coplanar set into new facets, may be assigned outside+ else+ partition coplanar set into coplanar sets of new facets+ for each deleted vertex+ if allpoints+ partition vertex into new facets, may be assigned outside+ else+ partition vertex into coplanar sets of new facets+*/+void qh_partitionvisible(qhT *qh /*qh.visible_list*/, boolT allpoints, int *numoutside) {+ facetT *visible, *newfacet;+ pointT *point, **pointp;+ int coplanar=0, size;+ unsigned count;+ vertexT *vertex, **vertexp;++ if (qh->ONLYmax)+ maximize_(qh->MINoutside, qh->max_vertex);+ *numoutside= 0;+ FORALLvisible_facets {+ if (!visible->outsideset && !visible->coplanarset)+ continue;+ newfacet= visible->f.replace;+ count= 0;+ while (newfacet && newfacet->visible) {+ newfacet= newfacet->f.replace;+ if (count++ > qh->facet_id)+ qh_infiniteloop(qh, visible);+ }+ if (!newfacet)+ newfacet= qh->newfacet_list;+ if (newfacet == qh->facet_tail) {+ qh_fprintf(qh, qh->ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n");+ qh_errexit(qh, qh_ERRprec, NULL, NULL);+ }+ if (visible->outsideset) {+ size= qh_setsize(qh, visible->outsideset);+ *numoutside += size;+ qh->num_outside -= size;+ FOREACHpoint_(visible->outsideset)+ qh_partitionpoint(qh, point, newfacet);+ }+ if (visible->coplanarset && (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside)) {+ size= qh_setsize(qh, visible->coplanarset);+ coplanar += size;+ FOREACHpoint_(visible->coplanarset) {+ if (allpoints) /* not used */+ qh_partitionpoint(qh, point, newfacet);+ else+ qh_partitioncoplanar(qh, point, newfacet, NULL);+ }+ }+ }+ FOREACHvertex_(qh->del_vertices) {+ if (vertex->point) {+ if (allpoints) /* not used */+ qh_partitionpoint(qh, vertex->point, qh->newfacet_list);+ else+ qh_partitioncoplanar(qh, vertex->point, qh->newfacet_list, NULL);+ }+ }+ trace1((qh, qh->ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar));+} /* partitionvisible */++++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="precision">-</a>++ qh_precision(qh, reason )+ restart on precision errors if not merging and if 'QJn'+*/+void qh_precision(qhT *qh, const char *reason) {++ if (qh->ALLOWrestart && !qh->PREmerge && !qh->MERGEexact) {+ if (qh->JOGGLEmax < REALmax/2) {+ trace0((qh, qh->ferr, 26, "qh_precision: qhull restart because of %s\n", reason));+ /* May be called repeatedly if qh->ALLOWrestart */+ longjmp(qh->restartexit, qh_ERRprec);+ }+ }+} /* qh_precision */++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="printsummary">-</a>++ qh_printsummary(qh, fp )+ prints summary to fp++ notes:+ not in io_r.c so that user_eg.c can prevent io_r.c from loading+ qh_printsummary and qh_countfacets must match counts++ design:+ determine number of points, vertices, and coplanar points+ print summary+*/+void qh_printsummary(qhT *qh, FILE *fp) {+ realT ratio, outerplane, innerplane;+ float cpu;+ int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0;+ int goodused;+ facetT *facet;+ const char *s;+ int numdel= zzval_(Zdelvertextot);+ int numtricoplanars= 0;++ size= qh->num_points + qh_setsize(qh, qh->other_points);+ numvertices= qh->num_vertices - qh_setsize(qh, qh->del_vertices);+ id= qh_pointid(qh, qh->GOODpointp);+ FORALLfacets {+ if (facet->coplanarset)+ numcoplanars += qh_setsize(qh, facet->coplanarset);+ if (facet->good) {+ if (facet->simplicial) {+ if (facet->keepcentrum && facet->tricoplanar)+ numtricoplanars++;+ }else if (qh_setsize(qh, facet->vertices) != qh->hull_dim)+ nonsimplicial++;+ }+ }+ if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)+ size--;+ if (qh->STOPcone || qh->STOPpoint)+ qh_fprintf(qh, fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'.");+ if (qh->UPPERdelaunay)+ goodused= qh->GOODvertex + qh->GOODpoint + qh->SPLITthresholds;+ else if (qh->DELAUNAY)+ goodused= qh->GOODvertex + qh->GOODpoint + qh->GOODthreshold;+ else+ goodused= qh->num_good;+ nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);+ if (qh->VORONOI) {+ if (qh->UPPERdelaunay)+ qh_fprintf(qh, fp, 9289, "\n\+Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);+ else+ qh_fprintf(qh, fp, 9290, "\n\+Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);+ qh_fprintf(qh, fp, 9291, " Number of Voronoi regions%s: %d\n",+ qh->ATinfinity ? " and at-infinity" : "", numvertices);+ if (numdel)+ qh_fprintf(qh, fp, 9292, " Total number of deleted points due to merging: %d\n", numdel);+ if (numcoplanars - numdel > 0)+ qh_fprintf(qh, fp, 9293, " Number of nearly incident points: %d\n", numcoplanars - numdel);+ else if (size - numvertices - numdel > 0)+ qh_fprintf(qh, fp, 9294, " Total number of nearly incident points: %d\n", size - numvertices - numdel);+ qh_fprintf(qh, fp, 9295, " Number of%s Voronoi vertices: %d\n",+ goodused ? " 'good'" : "", qh->num_good);+ if (nonsimplicial)+ qh_fprintf(qh, fp, 9296, " Number of%s non-simplicial Voronoi vertices: %d\n",+ goodused ? " 'good'" : "", nonsimplicial);+ }else if (qh->DELAUNAY) {+ if (qh->UPPERdelaunay)+ qh_fprintf(qh, fp, 9297, "\n\+Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);+ else+ qh_fprintf(qh, fp, 9298, "\n\+Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);+ qh_fprintf(qh, fp, 9299, " Number of input sites%s: %d\n",+ qh->ATinfinity ? " and at-infinity" : "", numvertices);+ if (numdel)+ qh_fprintf(qh, fp, 9300, " Total number of deleted points due to merging: %d\n", numdel);+ if (numcoplanars - numdel > 0)+ qh_fprintf(qh, fp, 9301, " Number of nearly incident points: %d\n", numcoplanars - numdel);+ else if (size - numvertices - numdel > 0)+ qh_fprintf(qh, fp, 9302, " Total number of nearly incident points: %d\n", size - numvertices - numdel);+ qh_fprintf(qh, fp, 9303, " Number of%s Delaunay regions: %d\n",+ goodused ? " 'good'" : "", qh->num_good);+ if (nonsimplicial)+ qh_fprintf(qh, fp, 9304, " Number of%s non-simplicial Delaunay regions: %d\n",+ goodused ? " 'good'" : "", nonsimplicial);+ }else if (qh->HALFspace) {+ qh_fprintf(qh, fp, 9305, "\n\+Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);+ qh_fprintf(qh, fp, 9306, " Number of halfspaces: %d\n", size);+ qh_fprintf(qh, fp, 9307, " Number of non-redundant halfspaces: %d\n", numvertices);+ if (numcoplanars) {+ if (qh->KEEPinside && qh->KEEPcoplanar)+ s= "similar and redundant";+ else if (qh->KEEPinside)+ s= "redundant";+ else+ s= "similar";+ qh_fprintf(qh, fp, 9308, " Number of %s halfspaces: %d\n", s, numcoplanars);+ }+ qh_fprintf(qh, fp, 9309, " Number of intersection points: %d\n", qh->num_facets - qh->num_visible);+ if (goodused)+ qh_fprintf(qh, fp, 9310, " Number of 'good' intersection points: %d\n", qh->num_good);+ if (nonsimplicial)+ qh_fprintf(qh, fp, 9311, " Number of%s non-simplicial intersection points: %d\n",+ goodused ? " 'good'" : "", nonsimplicial);+ }else {+ qh_fprintf(qh, fp, 9312, "\n\+Convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);+ qh_fprintf(qh, fp, 9313, " Number of vertices: %d\n", numvertices);+ if (numcoplanars) {+ if (qh->KEEPinside && qh->KEEPcoplanar)+ s= "coplanar and interior";+ else if (qh->KEEPinside)+ s= "interior";+ else+ s= "coplanar";+ qh_fprintf(qh, fp, 9314, " Number of %s points: %d\n", s, numcoplanars);+ }+ qh_fprintf(qh, fp, 9315, " Number of facets: %d\n", qh->num_facets - qh->num_visible);+ if (goodused)+ qh_fprintf(qh, fp, 9316, " Number of 'good' facets: %d\n", qh->num_good);+ if (nonsimplicial)+ qh_fprintf(qh, fp, 9317, " Number of%s non-simplicial facets: %d\n",+ goodused ? " 'good'" : "", nonsimplicial);+ }+ if (numtricoplanars)+ qh_fprintf(qh, fp, 9318, " Number of triangulated facets: %d\n", numtricoplanars);+ qh_fprintf(qh, fp, 9319, "\nStatistics for: %s | %s",+ qh->rbox_command, qh->qhull_command);+ if (qh->ROTATErandom != INT_MIN)+ qh_fprintf(qh, fp, 9320, " QR%d\n\n", qh->ROTATErandom);+ else+ qh_fprintf(qh, fp, 9321, "\n\n");+ qh_fprintf(qh, fp, 9322, " Number of points processed: %d\n", zzval_(Zprocessed));+ qh_fprintf(qh, fp, 9323, " Number of hyperplanes created: %d\n", zzval_(Zsetplane));+ if (qh->DELAUNAY)+ qh_fprintf(qh, fp, 9324, " Number of facets in hull: %d\n", qh->num_facets - qh->num_visible);+ qh_fprintf(qh, fp, 9325, " Number of distance tests for qhull: %d\n", zzval_(Zpartition)++ zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar));+#if 0 /* NOTE: must print before printstatistics() */+ {realT stddev, ave;+ qh_fprintf(qh, fp, 9326, " average new facet balance: %2.2g\n",+ wval_(Wnewbalance)/zval_(Zprocessed));+ stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),+ wval_(Wnewbalance2), &ave);+ qh_fprintf(qh, fp, 9327, " new facet standard deviation: %2.2g\n", stddev);+ qh_fprintf(qh, fp, 9328, " average partition balance: %2.2g\n",+ wval_(Wpbalance)/zval_(Zpbalance));+ stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),+ wval_(Wpbalance2), &ave);+ qh_fprintf(qh, fp, 9329, " partition standard deviation: %2.2g\n", stddev);+ }+#endif+ if (nummerged) {+ qh_fprintf(qh, fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)++ zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)++ zzval_(Zdistzero));+ qh_fprintf(qh, fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart));+ qh_fprintf(qh, fp, 9332," Number of merged facets: %d\n", nummerged);+ }+ if (!qh->RANDOMoutside && qh->QHULLfinished) {+ cpu= (float)qh->hulltime;+ cpu /= (float)qh_SECticks;+ wval_(Wcpu)= cpu;+ qh_fprintf(qh, fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu);+ }+ if (qh->RERUN) {+ if (!qh->PREmerge && !qh->MERGEexact)+ qh_fprintf(qh, fp, 9334, " Percentage of runs with precision errors: %4.1f\n",+ zzval_(Zretry)*100.0/qh->build_cnt); /* careful of order */+ }else if (qh->JOGGLEmax < REALmax/2) {+ if (zzval_(Zretry))+ qh_fprintf(qh, fp, 9335, " After %d retries, input joggled by: %2.2g\n",+ zzval_(Zretry), qh->JOGGLEmax);+ else+ qh_fprintf(qh, fp, 9336, " Input joggled by: %2.2g\n", qh->JOGGLEmax);+ }+ if (qh->totarea != 0.0)+ qh_fprintf(qh, fp, 9337, " %s facet area: %2.8g\n",+ zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totarea);+ if (qh->totvol != 0.0)+ qh_fprintf(qh, fp, 9338, " %s volume: %2.8g\n",+ zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totvol);+ if (qh->MERGING) {+ qh_outerinner(qh, NULL, &outerplane, &innerplane);+ if (outerplane > 2 * qh->DISTround) {+ qh_fprintf(qh, fp, 9339, " Maximum distance of %spoint above facet: %2.2g",+ (qh->QHULLfinished ? "" : "merged "), outerplane);+ ratio= outerplane/(qh->ONEmerge + qh->DISTround);+ /* don't report ratio if MINoutside is large */+ if (ratio > 0.05 && 2* qh->ONEmerge > qh->MINoutside && qh->JOGGLEmax > REALmax/2)+ qh_fprintf(qh, fp, 9340, " (%.1fx)\n", ratio);+ else+ qh_fprintf(qh, fp, 9341, "\n");+ }+ if (innerplane < -2 * qh->DISTround) {+ qh_fprintf(qh, fp, 9342, " Maximum distance of %svertex below facet: %2.2g",+ (qh->QHULLfinished ? "" : "merged "), innerplane);+ ratio= -innerplane/(qh->ONEmerge+qh->DISTround);+ if (ratio > 0.05 && qh->JOGGLEmax > REALmax/2)+ qh_fprintf(qh, fp, 9343, " (%.1fx)\n", ratio);+ else+ qh_fprintf(qh, fp, 9344, "\n");+ }+ }+ qh_fprintf(qh, fp, 9345, "\n");+} /* printsummary */++
+ C/mem_r.c view
@@ -0,0 +1,562 @@+/*<html><pre> -<a href="qh-mem_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ mem_r.c+ memory management routines for qhull++ See libqhull/mem_r.c for a standalone program.++ To initialize memory:++ qh_meminit(qh, stderr);+ qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf);+ qh_memsize(qh, (int)sizeof(facetT));+ qh_memsize(qh, (int)sizeof(facetT));+ ...+ qh_memsetup(qh);++ To free up all memory buffers:+ qh_memfreeshort(qh, &curlong, &totlong);++ if qh_NOmem,+ malloc/free is used instead of mem.c++ notes:+ uses Quickfit algorithm (freelists for commonly allocated sizes)+ assumes small sizes for freelists (it discards the tail of memory buffers)++ see:+ qh-mem_r.htm and mem_r.h+ global_r.c (qh_initbuffers) for an example of using mem_r.c++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/mem_r.c#5 $$Change: 2065 $+ $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $+*/++#include "libqhull_r.h" /* includes user_r.h and mem_r.h */++#include <string.h>+#include <stdio.h>+#include <stdlib.h>++#ifndef qh_NOmem++/*============= internal functions ==============*/++static int qh_intcompare(const void *i, const void *j);++/*========== functions in alphabetical order ======== */++/*-<a href="qh-mem_r.htm#TOC"+ >-------------------------------</a><a name="intcompare">-</a>++ qh_intcompare( i, j )+ used by qsort and bsearch to compare two integers+*/+static int qh_intcompare(const void *i, const void *j) {+ return(*((const int *)i) - *((const int *)j));+} /* intcompare */+++/*-<a href="qh-mem_r.htm#TOC"+ >--------------------------------</a><a name="memalloc">-</a>++ qh_memalloc( qh, insize )+ returns object of insize bytes+ qhmem is the global memory structure++ returns:+ pointer to allocated memory+ errors if insufficient memory++ notes:+ use explicit type conversion to avoid type warnings on some compilers+ actual object may be larger than insize+ use qh_memalloc_() for inline code for quick allocations+ logs allocations if 'T5'+ caller is responsible for freeing the memory.+ short memory is freed on shutdown by qh_memfreeshort unless qh_NOmem++ design:+ if size < qh->qhmem.LASTsize+ if qh->qhmem.freelists[size] non-empty+ return first object on freelist+ else+ round up request to size of qh->qhmem.freelists[size]+ allocate new allocation buffer if necessary+ allocate object from allocation buffer+ else+ allocate object with qh_malloc() in user.c+*/+void *qh_memalloc(qhT *qh, int insize) {+ void **freelistp, *newbuffer;+ int idx, size, n;+ int outsize, bufsize;+ void *object;++ if (insize<0) {+ qh_fprintf(qh, qh->qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d). Did int overflow due to high-D?\n", insize); /* WARN64 */+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ if (insize>=0 && insize <= qh->qhmem.LASTsize) {+ idx= qh->qhmem.indextable[insize];+ outsize= qh->qhmem.sizetable[idx];+ qh->qhmem.totshort += outsize;+ freelistp= qh->qhmem.freelists+idx;+ if ((object= *freelistp)) {+ qh->qhmem.cntquick++;+ qh->qhmem.totfree -= outsize;+ *freelistp= *((void **)*freelistp); /* replace freelist with next object */+#ifdef qh_TRACEshort+ n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);+#endif+ return(object);+ }else {+ qh->qhmem.cntshort++;+ if (outsize > qh->qhmem.freesize) {+ qh->qhmem.totdropped += qh->qhmem.freesize;+ if (!qh->qhmem.curbuffer)+ bufsize= qh->qhmem.BUFinit;+ else+ bufsize= qh->qhmem.BUFsize;+ if (!(newbuffer= qh_malloc((size_t)bufsize))) {+ qh_fprintf(qh, qh->qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize);+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ *((void **)newbuffer)= qh->qhmem.curbuffer; /* prepend newbuffer to curbuffer+ list. newbuffer!=0 by QH6080 */+ qh->qhmem.curbuffer= newbuffer;+ size= (sizeof(void **) + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask;+ qh->qhmem.freemem= (void *)((char *)newbuffer+size);+ qh->qhmem.freesize= bufsize - size;+ qh->qhmem.totbuffer += bufsize - size; /* easier to check */+ /* Periodically test totbuffer. It matches at beginning and exit of every call */+ n = qh->qhmem.totshort + qh->qhmem.totfree + qh->qhmem.totdropped + qh->qhmem.freesize - outsize;+ if (qh->qhmem.totbuffer != n) {+ qh_fprintf(qh, qh->qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qh->qhmem.totbuffer, n);+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ }+ object= qh->qhmem.freemem;+ qh->qhmem.freemem= (void *)((char *)qh->qhmem.freemem + outsize);+ qh->qhmem.freesize -= outsize;+ qh->qhmem.totunused += outsize - insize;+#ifdef qh_TRACEshort+ n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);+#endif+ return object;+ }+ }else { /* long allocation */+ if (!qh->qhmem.indextable) {+ qh_fprintf(qh, qh->qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n");+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ outsize= insize;+ qh->qhmem.cntlong++;+ qh->qhmem.totlong += outsize;+ if (qh->qhmem.maxlong < qh->qhmem.totlong)+ qh->qhmem.maxlong= qh->qhmem.totlong;+ if (!(object= qh_malloc((size_t)outsize))) {+ qh_fprintf(qh, qh->qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize);+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, outsize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);+ }+ return(object);+} /* memalloc */+++/*-<a href="qh-mem_r.htm#TOC"+ >--------------------------------</a><a name="memcheck">-</a>++ qh_memcheck(qh)+*/+void qh_memcheck(qhT *qh) {+ int i, count, totfree= 0;+ void *object;++ if (!qh) {+ qh_fprintf_stderr(6243, "qh_memcheck(qh) error: qh is 0. It does not point to a qhT");+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */+ }+ if (qh->qhmem.ferr == 0 || qh->qhmem.IStracing < 0 || qh->qhmem.IStracing > 10 || (((qh->qhmem.ALIGNmask+1) & qh->qhmem.ALIGNmask) != 0)) {+ qh_fprintf_stderr(6244, "qh_memcheck error: either qh->qhmem is overwritten or qh->qhmem is not initialized. Call qh_mem_new() or qh_new_qhull() before calling qh_mem routines. ferr 0x%x IsTracing %d ALIGNmask 0x%x", qh->qhmem.ferr, qh->qhmem.IStracing, qh->qhmem.ALIGNmask);+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */+ return;+ }+ if (qh->qhmem.IStracing != 0)+ qh_fprintf(qh, qh->qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qh->qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qh->qhmem\n");+ for (i=0; i < qh->qhmem.TABLEsize; i++) {+ count=0;+ for (object= qh->qhmem.freelists[i]; object; object= *((void **)object))+ count++;+ totfree += qh->qhmem.sizetable[i] * count;+ }+ if (totfree != qh->qhmem.totfree) {+ qh_fprintf(qh, qh->qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qh->qhmem.totfree, totfree);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ if (qh->qhmem.IStracing != 0)+ qh_fprintf(qh, qh->qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qh->qhmem.totfree\n", totfree);+} /* memcheck */++/*-<a href="qh-mem_r.htm#TOC"+ >--------------------------------</a><a name="memfree">-</a>++ qh_memfree(qh, object, insize )+ free up an object of size bytes+ size is insize from qh_memalloc++ notes:+ object may be NULL+ type checking warns if using (void **)object+ use qh_memfree_() for quick free's of small objects++ design:+ if size <= qh->qhmem.LASTsize+ append object to corresponding freelist+ else+ call qh_free(object)+*/+void qh_memfree(qhT *qh, void *object, int insize) {+ void **freelistp;+ int idx, outsize;++ if (!object)+ return;+ if (insize <= qh->qhmem.LASTsize) {+ qh->qhmem.freeshort++;+ idx= qh->qhmem.indextable[insize];+ outsize= qh->qhmem.sizetable[idx];+ qh->qhmem.totfree += outsize;+ qh->qhmem.totshort -= outsize;+ freelistp= qh->qhmem.freelists + idx;+ *((void **)object)= *freelistp;+ *freelistp= object;+#ifdef qh_TRACEshort+ idx= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);+#endif+ }else {+ qh->qhmem.freelong++;+ qh->qhmem.totlong -= insize;+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);+ qh_free(object);+ }+} /* memfree */+++/*-<a href="qh-mem_r.htm#TOC"+ >-------------------------------</a><a name="memfreeshort">-</a>++ qh_memfreeshort(qh, curlong, totlong )+ frees up all short and qhmem memory allocations++ returns:+ number and size of current long allocations++ notes:+ if qh_NOmem (qh_malloc() for all allocations),+ short objects (e.g., facetT) are not recovered.+ use qh_freeqhull(qh, qh_ALL) instead.++ see:+ qh_freeqhull(qh, allMem)+ qh_memtotal(qh, curlong, totlong, curshort, totshort, maxlong, totbuffer);+*/+void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) {+ void *buffer, *nextbuffer;+ FILE *ferr;++ *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;+ *totlong= qh->qhmem.totlong;+ for (buffer= qh->qhmem.curbuffer; buffer; buffer= nextbuffer) {+ nextbuffer= *((void **) buffer);+ qh_free(buffer);+ }+ qh->qhmem.curbuffer= NULL;+ if (qh->qhmem.LASTsize) {+ qh_free(qh->qhmem.indextable);+ qh_free(qh->qhmem.freelists);+ qh_free(qh->qhmem.sizetable);+ }+ ferr= qh->qhmem.ferr;+ memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */+ qh->qhmem.ferr= ferr;+} /* memfreeshort */+++/*-<a href="qh-mem_r.htm#TOC"+ >--------------------------------</a><a name="meminit">-</a>++ qh_meminit(qh, ferr )+ initialize qhmem and test sizeof( void*)+ Does not throw errors. qh_exit on failure+*/+void qh_meminit(qhT *qh, FILE *ferr) {++ memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */+ if (ferr)+ qh->qhmem.ferr= ferr;+ else+ qh->qhmem.ferr= stderr;+ if (sizeof(void*) < sizeof(int)) {+ qh_fprintf(qh, qh->qhmem.ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */+ }+ if (sizeof(void*) > sizeof(ptr_intT)) {+ qh_fprintf(qh, qh->qhmem.ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT));+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */+ }+ qh_memcheck(qh);+} /* meminit */++/*-<a href="qh-mem_r.htm#TOC"+ >-------------------------------</a><a name="meminitbuffers">-</a>++ qh_meminitbuffers(qh, tracelevel, alignment, numsizes, bufsize, bufinit )+ initialize qhmem+ if tracelevel >= 5, trace memory allocations+ alignment= desired address alignment for memory allocations+ numsizes= number of freelists+ bufsize= size of additional memory buffers for short allocations+ bufinit= size of initial memory buffer for short allocations+*/+void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {++ qh->qhmem.IStracing= tracelevel;+ qh->qhmem.NUMsizes= numsizes;+ qh->qhmem.BUFsize= bufsize;+ qh->qhmem.BUFinit= bufinit;+ qh->qhmem.ALIGNmask= alignment-1;+ if (qh->qhmem.ALIGNmask & ~qh->qhmem.ALIGNmask) {+ qh_fprintf(qh, qh->qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ qh->qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int));+ qh->qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *));+ if (!qh->qhmem.sizetable || !qh->qhmem.freelists) {+ qh_fprintf(qh, qh->qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n");+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ if (qh->qhmem.IStracing >= 1)+ qh_fprintf(qh, qh->qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment);+} /* meminitbuffers */++/*-<a href="qh-mem_r.htm#TOC"+ >-------------------------------</a><a name="memsetup">-</a>++ qh_memsetup(qh)+ set up memory after running memsize()+*/+void qh_memsetup(qhT *qh) {+ int k,i;++ qsort(qh->qhmem.sizetable, (size_t)qh->qhmem.TABLEsize, sizeof(int), qh_intcompare);+ qh->qhmem.LASTsize= qh->qhmem.sizetable[qh->qhmem.TABLEsize-1];+ if(qh->qhmem.LASTsize >= qh->qhmem.BUFsize || qh->qhmem.LASTsize >= qh->qhmem.BUFinit) {+ qh_fprintf(qh, qh->qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n",+ qh->qhmem.LASTsize, qh->qhmem.BUFsize, qh->qhmem.BUFinit);+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ if (!(qh->qhmem.indextable= (int *)qh_malloc((qh->qhmem.LASTsize+1) * sizeof(int)))) {+ qh_fprintf(qh, qh->qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n");+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ for (k=qh->qhmem.LASTsize+1; k--; )+ qh->qhmem.indextable[k]= k;+ i= 0;+ for (k=0; k <= qh->qhmem.LASTsize; k++) {+ if (qh->qhmem.indextable[k] <= qh->qhmem.sizetable[i])+ qh->qhmem.indextable[k]= i;+ else+ qh->qhmem.indextable[k]= ++i;+ }+} /* memsetup */++/*-<a href="qh-mem_r.htm#TOC"+ >-------------------------------</a><a name="memsize">-</a>++ qh_memsize(qh, size )+ define a free list for this size+*/+void qh_memsize(qhT *qh, int size) {+ int k;++ if(qh->qhmem.LASTsize) {+ qh_fprintf(qh, qh->qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n");+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ size= (size + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask;+ for (k=qh->qhmem.TABLEsize; k--; ) {+ if (qh->qhmem.sizetable[k] == size)+ return;+ }+ if (qh->qhmem.TABLEsize < qh->qhmem.NUMsizes)+ qh->qhmem.sizetable[qh->qhmem.TABLEsize++]= size;+ else+ qh_fprintf(qh, qh->qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qh->qhmem.NUMsizes);+} /* memsize */+++/*-<a href="qh-mem_r.htm#TOC"+ >-------------------------------</a><a name="memstatistics">-</a>++ qh_memstatistics(qh, fp )+ print out memory statistics++ Verifies that qh->qhmem.totfree == sum of freelists+*/+void qh_memstatistics(qhT *qh, FILE *fp) {+ int i;+ int count;+ void *object;++ qh_memcheck(qh);+ qh_fprintf(qh, fp, 9278, "\nmemory statistics:\n\+%7d quick allocations\n\+%7d short allocations\n\+%7d long allocations\n\+%7d short frees\n\+%7d long frees\n\+%7d bytes of short memory in use\n\+%7d bytes of short memory in freelists\n\+%7d bytes of dropped short memory\n\+%7d bytes of unused short memory (estimated)\n\+%7d bytes of long memory allocated (max, except for input)\n\+%7d bytes of long memory in use (in %d pieces)\n\+%7d bytes of short memory buffers (minus links)\n\+%7d bytes per short memory buffer (initially %d bytes)\n",+ qh->qhmem.cntquick, qh->qhmem.cntshort, qh->qhmem.cntlong,+ qh->qhmem.freeshort, qh->qhmem.freelong,+ qh->qhmem.totshort, qh->qhmem.totfree,+ qh->qhmem.totdropped + qh->qhmem.freesize, qh->qhmem.totunused,+ qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong,+ qh->qhmem.totbuffer, qh->qhmem.BUFsize, qh->qhmem.BUFinit);+ if (qh->qhmem.cntlarger) {+ qh_fprintf(qh, fp, 9279, "%7d calls to qh_setlarger\n%7.2g average copy size\n",+ qh->qhmem.cntlarger, ((float)qh->qhmem.totlarger)/(float)qh->qhmem.cntlarger);+ qh_fprintf(qh, fp, 9280, " freelists(bytes->count):");+ }+ for (i=0; i < qh->qhmem.TABLEsize; i++) {+ count=0;+ for (object= qh->qhmem.freelists[i]; object; object= *((void **)object))+ count++;+ qh_fprintf(qh, fp, 9281, " %d->%d", qh->qhmem.sizetable[i], count);+ }+ qh_fprintf(qh, fp, 9282, "\n\n");+} /* memstatistics */+++/*-<a href="qh-mem_r.htm#TOC"+ >-------------------------------</a><a name="NOmem">-</a>++ qh_NOmem+ turn off quick-fit memory allocation++ notes:+ uses qh_malloc() and qh_free() instead+*/+#else /* qh_NOmem */++void *qh_memalloc(qhT *qh, int insize) {+ void *object;++ if (!(object= qh_malloc((size_t)insize))) {+ qh_fprintf(qh, qh->qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n");+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ qh->qhmem.cntlong++;+ qh->qhmem.totlong += insize;+ if (qh->qhmem.maxlong < qh->qhmem.totlong)+ qh->qhmem.maxlong= qh->qhmem.totlong;+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);+ return object;+}++void qh_memfree(qhT *qh, void *object, int insize) {++ if (!object)+ return;+ qh_free(object);+ qh->qhmem.freelong++;+ qh->qhmem.totlong -= insize;+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);+}++void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) {+ *totlong= qh->qhmem.totlong;+ *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;+ memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */+}++void qh_meminit(qhT *qh, FILE *ferr) {++ memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */+ if (ferr)+ qh->qhmem.ferr= ferr;+ else+ qh->qhmem.ferr= stderr;+ if (sizeof(void*) < sizeof(int)) {+ qh_fprintf(qh, qh->qhmem.ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+}++void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {++ qh->qhmem.IStracing= tracelevel;+}++void qh_memsetup(qhT *qh) {++}++void qh_memsize(qhT *qh, int size) {++}++void qh_memstatistics(qhT *qh, FILE *fp) {++ qh_fprintf(qh, fp, 9409, "\nmemory statistics:\n\+%7d long allocations\n\+%7d long frees\n\+%7d bytes of long memory allocated (max, except for input)\n\+%7d bytes of long memory in use (in %d pieces)\n",+ qh->qhmem.cntlong,+ qh->qhmem.freelong,+ qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong);+}++#endif /* qh_NOmem */++/*-<a href="qh-mem_r.htm#TOC"+>-------------------------------</a><a name="memtotlong">-</a>++ qh_memtotal(qh, totlong, curlong, totshort, curshort, maxlong, totbuffer )+ Return the total, allocated long and short memory++ returns:+ Returns the total current bytes of long and short allocations+ Returns the current count of long and short allocations+ Returns the maximum long memory and total short buffer (minus one link per buffer)+ Does not error (for deprecated UsingLibQhull.cpp (libqhullpcpp))+*/+void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) {+ *totlong= qh->qhmem.totlong;+ *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;+ *totshort= qh->qhmem.totshort;+ *curshort= qh->qhmem.cntshort + qh->qhmem.cntquick - qh->qhmem.freeshort;+ *maxlong= qh->qhmem.maxlong;+ *totbuffer= qh->qhmem.totbuffer;+} /* memtotlong */
+ C/merge_r.c view
@@ -0,0 +1,3627 @@+/*<html><pre> -<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="TOP">-</a>++ merge_r.c+ merges non-convex facets++ see qh-merge_r.htm and merge_r.h++ other modules call qh_premerge() and qh_postmerge()++ the user may call qh_postmerge() to perform additional merges.++ To remove deleted facets and vertices (qhull() in libqhull_r.c):+ qh_partitionvisible(qh, !qh_ALL, &numoutside); // visible_list, newfacet_list+ qh_deletevisible(); // qh.visible_list+ qh_resetlists(qh, False, qh_RESETvisible); // qh.visible_list newvertex_list newfacet_list++ assumes qh.CENTERtype= centrum++ merges occur in qh_mergefacet and in qh_mergecycle+ vertex->neighbors not set until the first merge occurs++ Copyright (c) 1993-2015 C.B. Barber.+ $Id: //main/2015/qhull/src/libqhull_r/merge_r.c#5 $$Change: 2064 $+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $+*/++#include "qhull_ra.h"++#ifndef qh_NOmerge++/*===== functions(alphabetical after premerge and postmerge) ======*/++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="premerge">-</a>++ qh_premerge(qh, apex, maxcentrum )+ pre-merge nonconvex facets in qh.newfacet_list for apex+ maxcentrum defines coplanar and concave (qh_test_appendmerge)++ returns:+ deleted facets added to qh.visible_list with facet->visible set++ notes:+ uses globals, qh.MERGEexact, qh.PREmerge++ design:+ mark duplicate ridges in qh.newfacet_list+ merge facet cycles in qh.newfacet_list+ merge duplicate ridges and concave facets in qh.newfacet_list+ check merged facet cycles for degenerate and redundant facets+ merge degenerate and redundant facets+ collect coplanar and concave facets+ merge concave, coplanar, degenerate, and redundant facets+*/+void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {+ boolT othermerge= False;+ facetT *newfacet;++ if (qh->ZEROcentrum && qh_checkzero(qh, !qh_ALL))+ return;+ trace2((qh, qh->ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n",+ maxcentrum, maxangle, apex->id, getid_(qh->newfacet_list)));+ if (qh->IStracing >= 4 && qh->num_facets < 50)+ qh_printlists(qh);+ qh->centrum_radius= maxcentrum;+ qh->cos_max= maxangle;+ qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);+ qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);+ if (qh->hull_dim >=3) {+ qh_mark_dupridges(qh, qh->newfacet_list); /* facet_mergeset */+ qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);+ qh_forcedmerges(qh, &othermerge /* qh->facet_mergeset */);+ FORALLnew_facets { /* test samecycle merges */+ if (!newfacet->simplicial && !newfacet->mergeridge)+ qh_degen_redundant_neighbors(qh, newfacet, NULL);+ }+ if (qh_merge_degenredundant(qh))+ othermerge= True;+ }else /* qh->hull_dim == 2 */+ qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);+ qh_flippedmerges(qh, qh->newfacet_list, &othermerge);+ if (!qh->MERGEexact || zzval_(Ztotmerge)) {+ zinc_(Zpremergetot);+ qh->POSTmerging= False;+ qh_getmergeset_initial(qh, qh->newfacet_list);+ qh_all_merges(qh, othermerge, False);+ }+ qh_settempfree(qh, &qh->facet_mergeset);+ qh_settempfree(qh, &qh->degen_mergeset);+} /* premerge */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="postmerge">-</a>++ qh_postmerge(qh, reason, maxcentrum, maxangle, vneighbors )+ post-merge nonconvex facets as defined by maxcentrum and maxangle+ 'reason' is for reporting progress+ if vneighbors,+ calls qh_test_vneighbors at end of qh_all_merge+ if firstmerge,+ calls qh_reducevertices before qh_getmergeset++ returns:+ if first call (qh.visible_list != qh.facet_list),+ builds qh.facet_newlist, qh.newvertex_list+ deleted facets added to qh.visible_list with facet->visible+ qh.visible_list == qh.facet_list++ notes:+++ design:+ if first call+ set qh.visible_list and qh.newfacet_list to qh.facet_list+ add all facets to qh.newfacet_list+ mark non-simplicial facets, facet->newmerge+ set qh.newvertext_list to qh.vertex_list+ add all vertices to qh.newvertex_list+ if a pre-merge occured+ set vertex->delridge {will retest the ridge}+ if qh.MERGEexact+ call qh_reducevertices()+ if no pre-merging+ merge flipped facets+ determine non-convex facets+ merge all non-convex facets+*/+void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,+ boolT vneighbors) {+ facetT *newfacet;+ boolT othermerges= False;+ vertexT *vertex;++ if (qh->REPORTfreq || qh->IStracing) {+ qh_buildtracing(qh, NULL, NULL);+ qh_printsummary(qh, qh->ferr);+ if (qh->PRINTstatistics)+ qh_printallstatistics(qh, qh->ferr, "reason");+ qh_fprintf(qh, qh->ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",+ reason, maxcentrum, maxangle);+ }+ trace2((qh, qh->ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n",+ vneighbors));+ qh->centrum_radius= maxcentrum;+ qh->cos_max= maxangle;+ qh->POSTmerging= True;+ qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);+ qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);+ if (qh->visible_list != qh->facet_list) { /* first call */+ qh->NEWfacets= True;+ qh->visible_list= qh->newfacet_list= qh->facet_list;+ FORALLnew_facets {+ newfacet->newfacet= True;+ if (!newfacet->simplicial)+ newfacet->newmerge= True;+ zinc_(Zpostfacets);+ }+ qh->newvertex_list= qh->vertex_list;+ FORALLvertices+ vertex->newlist= True;+ if (qh->VERTEXneighbors) { /* a merge has occurred */+ FORALLvertices+ vertex->delridge= True; /* test for redundant, needed? */+ if (qh->MERGEexact) {+ if (qh->hull_dim <= qh_DIMreduceBuild)+ qh_reducevertices(qh); /* was skipped during pre-merging */+ }+ }+ if (!qh->PREmerge && !qh->MERGEexact)+ qh_flippedmerges(qh, qh->newfacet_list, &othermerges);+ }+ qh_getmergeset_initial(qh, qh->newfacet_list);+ qh_all_merges(qh, False, vneighbors);+ qh_settempfree(qh, &qh->facet_mergeset);+ qh_settempfree(qh, &qh->degen_mergeset);+} /* post_merge */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="all_merges">-</a>++ qh_all_merges(qh, othermerge, vneighbors )+ merge all non-convex facets++ set othermerge if already merged facets (for qh_reducevertices)+ if vneighbors+ tests vertex neighbors for convexity at end+ qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list+ qh.degen_mergeset is defined+ if qh.MERGEexact && !qh.POSTmerging,+ does not merge coplanar facets++ returns:+ deleted facets added to qh.visible_list with facet->visible+ deleted vertices added qh.delvertex_list with vertex->delvertex++ notes:+ unless !qh.MERGEindependent,+ merges facets in independent sets+ uses qh.newfacet_list as argument since merges call qh_removefacet()++ design:+ while merges occur+ for each merge in qh.facet_mergeset+ unless one of the facets was already merged in this pass+ merge the facets+ test merged facets for additional merges+ add merges to qh.facet_mergeset+ if vertices record neighboring facets+ rename redundant vertices+ update qh.facet_mergeset+ if vneighbors ??+ tests vertex neighbors for convexity at end+*/+void qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors) {+ facetT *facet1, *facet2;+ mergeT *merge;+ boolT wasmerge= True, isreduce;+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */+ vertexT *vertex;+ mergeType mergetype;+ int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0;++ trace2((qh, qh->ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n",+ getid_(qh->newfacet_list)));+ while (True) {+ wasmerge= False;+ while (qh_setsize(qh, qh->facet_mergeset)) {+ while ((merge= (mergeT*)qh_setdellast(qh->facet_mergeset))) {+ facet1= merge->facet1;+ facet2= merge->facet2;+ mergetype= merge->type;+ qh_memfree_(qh, merge, (int)sizeof(mergeT), freelistp);+ if (facet1->visible || facet2->visible) /*deleted facet*/+ continue;+ if ((facet1->newfacet && !facet1->tested)+ || (facet2->newfacet && !facet2->tested)) {+ if (qh->MERGEindependent && mergetype <= MRGanglecoplanar)+ continue; /* perform independent sets of merges */+ }+ qh_merge_nonconvex(qh, facet1, facet2, mergetype);+ numdegenredun += qh_merge_degenredundant(qh);+ numnewmerges++;+ wasmerge= True;+ if (mergetype == MRGconcave)+ numconcave++;+ else /* MRGcoplanar or MRGanglecoplanar */+ numcoplanar++;+ } /* while setdellast */+ if (qh->POSTmerging && qh->hull_dim <= qh_DIMreduceBuild+ && numnewmerges > qh_MAXnewmerges) {+ numnewmerges= 0;+ qh_reducevertices(qh); /* otherwise large post merges too slow */+ }+ qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */+ } /* while mergeset */+ if (qh->VERTEXneighbors) {+ isreduce= False;+ if (qh->hull_dim >=4 && qh->POSTmerging) {+ FORALLvertices+ vertex->delridge= True;+ isreduce= True;+ }+ if ((wasmerge || othermerge) && (!qh->MERGEexact || qh->POSTmerging)+ && qh->hull_dim <= qh_DIMreduceBuild) {+ othermerge= False;+ isreduce= True;+ }+ if (isreduce) {+ if (qh_reducevertices(qh)) {+ qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */+ continue;+ }+ }+ }+ if (vneighbors && qh_test_vneighbors(qh /* qh->newfacet_list */))+ continue;+ break;+ } /* while (True) */+ if (qh->CHECKfrequently && !qh->MERGEexact) {+ qh->old_randomdist= qh->RANDOMdist;+ qh->RANDOMdist= False;+ qh_checkconvex(qh, qh->newfacet_list, qh_ALGORITHMfault);+ /* qh_checkconnect(qh); [this is slow and it changes the facet order] */+ qh->RANDOMdist= qh->old_randomdist;+ }+ trace1((qh, qh->ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n",+ numcoplanar, numconcave, numdegenredun));+ if (qh->IStracing >= 4 && qh->num_facets < 50)+ qh_printlists(qh);+} /* all_merges */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="appendmergeset">-</a>++ qh_appendmergeset(qh, facet, neighbor, mergetype, angle )+ appends an entry to qh.facet_mergeset or qh.degen_mergeset++ angle ignored if NULL or !qh.ANGLEmerge++ returns:+ merge appended to facet_mergeset or degen_mergeset+ sets ->degenerate or ->redundant if degen_mergeset++ see:+ qh_test_appendmerge()++ design:+ allocate merge entry+ if regular merge+ append to qh.facet_mergeset+ else if degenerate merge and qh.facet_mergeset is all degenerate+ append to qh.degen_mergeset+ else if degenerate merge+ prepend to qh.degen_mergeset+ else if redundant merge+ append to qh.degen_mergeset+*/+void qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) {+ mergeT *merge, *lastmerge;+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */++ if (facet->redundant)+ return;+ if (facet->degenerate && mergetype == MRGdegen)+ return;+ qh_memalloc_(qh, (int)sizeof(mergeT), freelistp, merge, mergeT);+ merge->facet1= facet;+ merge->facet2= neighbor;+ merge->type= mergetype;+ if (angle && qh->ANGLEmerge)+ merge->angle= *angle;+ if (mergetype < MRGdegen)+ qh_setappend(qh, &(qh->facet_mergeset), merge);+ else if (mergetype == MRGdegen) {+ facet->degenerate= True;+ if (!(lastmerge= (mergeT*)qh_setlast(qh->degen_mergeset))+ || lastmerge->type == MRGdegen)+ qh_setappend(qh, &(qh->degen_mergeset), merge);+ else+ qh_setaddnth(qh, &(qh->degen_mergeset), 0, merge);+ }else if (mergetype == MRGredundant) {+ facet->redundant= True;+ qh_setappend(qh, &(qh->degen_mergeset), merge);+ }else /* mergetype == MRGmirror */ {+ if (facet->redundant || neighbor->redundant) {+ qh_fprintf(qh, qh->ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n",+ facet->id, neighbor->id);+ qh_errexit2(qh, qh_ERRqhull, facet, neighbor);+ }+ if (!qh_setequal(facet->vertices, neighbor->vertices)) {+ qh_fprintf(qh, qh->ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",+ facet->id, neighbor->id);+ qh_errexit2(qh, qh_ERRqhull, facet, neighbor);+ }+ facet->redundant= True;+ neighbor->redundant= True;+ qh_setappend(qh, &(qh->degen_mergeset), merge);+ }+} /* appendmergeset */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="basevertices">-</a>++ qh_basevertices(qh, samecycle )+ return temporary set of base vertices for samecycle+ samecycle is first facet in the cycle+ assumes apex is SETfirst_( samecycle->vertices )++ returns:+ vertices(settemp)+ all ->seen are cleared++ notes:+ uses qh_vertex_visit;++ design:+ for each facet in samecycle+ for each unseen vertex in facet->vertices+ append to result+*/+setT *qh_basevertices(qhT *qh, facetT *samecycle) {+ facetT *same;+ vertexT *apex, *vertex, **vertexp;+ setT *vertices= qh_settemp(qh, qh->TEMPsize);++ apex= SETfirstt_(samecycle->vertices, vertexT);+ apex->visitid= ++qh->vertex_visit;+ FORALLsame_cycle_(samecycle) {+ if (same->mergeridge)+ continue;+ FOREACHvertex_(same->vertices) {+ if (vertex->visitid != qh->vertex_visit) {+ qh_setappend(qh, &vertices, vertex);+ vertex->visitid= qh->vertex_visit;+ vertex->seen= False;+ }+ }+ }+ trace4((qh, qh->ferr, 4019, "qh_basevertices: found %d vertices\n",+ qh_setsize(qh, vertices)));+ return vertices;+} /* basevertices */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="checkconnect">-</a>++ qh_checkconnect(qh)+ check that new facets are connected+ new facets are on qh.newfacet_list++ notes:+ this is slow and it changes the order of the facets+ uses qh.visit_id++ design:+ move first new facet to end of qh.facet_list+ for all newly appended facets+ append unvisited neighbors to end of qh.facet_list+ for all new facets+ report error if unvisited+*/+void qh_checkconnect(qhT *qh /* qh->newfacet_list */) {+ facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;++ facet= qh->newfacet_list;+ qh_removefacet(qh, facet);+ qh_appendfacet(qh, facet);+ facet->visitid= ++qh->visit_id;+ FORALLfacet_(facet) {+ FOREACHneighbor_(facet) {+ if (neighbor->visitid != qh->visit_id) {+ qh_removefacet(qh, neighbor);+ qh_appendfacet(qh, neighbor);+ neighbor->visitid= qh->visit_id;+ }+ }+ }+ FORALLnew_facets {+ if (newfacet->visitid == qh->visit_id)+ break;+ qh_fprintf(qh, qh->ferr, 6094, "qhull error: f%d is not attached to the new facets\n",+ newfacet->id);+ errfacet= newfacet;+ }+ if (errfacet)+ qh_errexit(qh, qh_ERRqhull, errfacet, NULL);+} /* checkconnect */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="checkzero">-</a>++ qh_checkzero(qh, testall )+ check that facets are clearly convex for qh.DISTround with qh.MERGEexact++ if testall,+ test all facets for qh.MERGEexact post-merging+ else+ test qh.newfacet_list++ if qh.MERGEexact,+ allows coplanar ridges+ skips convexity test while qh.ZEROall_ok++ returns:+ True if all facets !flipped, !dupridge, normal+ if all horizon facets are simplicial+ if all vertices are clearly below neighbor+ if all opposite vertices of horizon are below+ clears qh.ZEROall_ok if any problems or coplanar facets++ notes:+ uses qh.vertex_visit+ horizon facets may define multiple new facets++ design:+ for all facets in qh.newfacet_list or qh.facet_list+ check for flagged faults (flipped, etc.)+ for all facets in qh.newfacet_list or qh.facet_list+ for each neighbor of facet+ skip horizon facets for qh.newfacet_list+ test the opposite vertex+ if qh.newfacet_list+ test the other vertices in the facet's horizon facet+*/+boolT qh_checkzero(qhT *qh, boolT testall) {+ facetT *facet, *neighbor, **neighborp;+ facetT *horizon, *facetlist;+ int neighbor_i;+ vertexT *vertex, **vertexp;+ realT dist;++ if (testall)+ facetlist= qh->facet_list;+ else {+ facetlist= qh->newfacet_list;+ FORALLfacet_(facetlist) {+ horizon= SETfirstt_(facet->neighbors, facetT);+ if (!horizon->simplicial)+ goto LABELproblem;+ if (facet->flipped || facet->dupridge || !facet->normal)+ goto LABELproblem;+ }+ if (qh->MERGEexact && qh->ZEROall_ok) {+ trace2((qh, qh->ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n"));+ return True;+ }+ }+ FORALLfacet_(facetlist) {+ qh->vertex_visit++;+ neighbor_i= 0;+ horizon= NULL;+ FOREACHneighbor_(facet) {+ if (!neighbor_i && !testall) {+ horizon= neighbor;+ neighbor_i++;+ continue; /* horizon facet tested in qh_findhorizon */+ }+ vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);+ vertex->visitid= qh->vertex_visit;+ zzinc_(Zdistzero);+ qh_distplane(qh, vertex->point, neighbor, &dist);+ if (dist >= -qh->DISTround) {+ qh->ZEROall_ok= False;+ if (!qh->MERGEexact || testall || dist > qh->DISTround)+ goto LABELnonconvex;+ }+ }+ if (!testall && horizon) {+ FOREACHvertex_(horizon->vertices) {+ if (vertex->visitid != qh->vertex_visit) {+ zzinc_(Zdistzero);+ qh_distplane(qh, vertex->point, facet, &dist);+ if (dist >= -qh->DISTround) {+ qh->ZEROall_ok= False;+ if (!qh->MERGEexact || dist > qh->DISTround)+ goto LABELnonconvex;+ }+ break;+ }+ }+ }+ }+ trace2((qh, qh->ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall,+ (qh->MERGEexact && !testall) ?+ "not concave, flipped, or duplicate ridged" : "clearly convex"));+ return True;++ LABELproblem:+ qh->ZEROall_ok= False;+ trace2((qh, qh->ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n",+ facet->id));+ return False;++ LABELnonconvex:+ trace2((qh, qh->ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n",+ facet->id, neighbor->id, vertex->id, dist));+ return False;+} /* checkzero */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="compareangle">-</a>++ qh_compareangle(angle1, angle2 )+ used by qsort() to order merges by angle+*/+int qh_compareangle(const void *p1, const void *p2) {+ const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);++ return((a->angle > b->angle) ? 1 : -1);+} /* compareangle */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="comparemerge">-</a>++ qh_comparemerge(merge1, merge2 )+ used by qsort() to order merges+*/+int qh_comparemerge(const void *p1, const void *p2) {+ const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);++ return(a->type - b->type);+} /* comparemerge */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="comparevisit">-</a>++ qh_comparevisit(vertex1, vertex2 )+ used by qsort() to order vertices by their visitid+*/+int qh_comparevisit(const void *p1, const void *p2) {+ const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);++ return(a->visitid - b->visitid);+} /* comparevisit */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="copynonconvex">-</a>++ qh_copynonconvex(qh, atridge )+ set non-convex flag on other ridges (if any) between same neighbors++ notes:+ may be faster if use smaller ridge set++ design:+ for each ridge of atridge's top facet+ if ridge shares the same neighbor+ set nonconvex flag+*/+void qh_copynonconvex(qhT *qh, ridgeT *atridge) {+ facetT *facet, *otherfacet;+ ridgeT *ridge, **ridgep;++ facet= atridge->top;+ otherfacet= atridge->bottom;+ FOREACHridge_(facet->ridges) {+ if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) {+ ridge->nonconvex= True;+ trace4((qh, qh->ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",+ atridge->id, ridge->id));+ break;+ }+ }+} /* copynonconvex */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="degen_redundant_facet">-</a>++ qh_degen_redundant_facet(qh, facet )+ check facet for degen. or redundancy++ notes:+ bumps vertex_visit+ called if a facet was redundant but no longer is (qh_merge_degenredundant)+ qh_appendmergeset() only appends first reference to facet (i.e., redundant)++ see:+ qh_degen_redundant_neighbors()++ design:+ test for redundant neighbor+ test for degenerate facet+*/+void qh_degen_redundant_facet(qhT *qh, facetT *facet) {+ vertexT *vertex, **vertexp;+ facetT *neighbor, **neighborp;++ trace4((qh, qh->ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",+ facet->id));+ FOREACHneighbor_(facet) {+ qh->vertex_visit++;+ FOREACHvertex_(neighbor->vertices)+ vertex->visitid= qh->vertex_visit;+ FOREACHvertex_(facet->vertices) {+ if (vertex->visitid != qh->vertex_visit)+ break;+ }+ if (!vertex) {+ qh_appendmergeset(qh, facet, neighbor, MRGredundant, NULL);+ trace2((qh, qh->ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id));+ return;+ }+ }+ if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {+ qh_appendmergeset(qh, facet, facet, MRGdegen, NULL);+ trace2((qh, qh->ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));+ }+} /* degen_redundant_facet */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="degen_redundant_neighbors">-</a>++ qh_degen_redundant_neighbors(qh, facet, delfacet, )+ append degenerate and redundant neighbors to facet_mergeset+ if delfacet,+ only checks neighbors of both delfacet and facet+ also checks current facet for degeneracy++ notes:+ bumps vertex_visit+ called for each qh_mergefacet() and qh_mergecycle()+ merge and statistics occur in merge_nonconvex+ qh_appendmergeset() only appends first reference to facet (i.e., redundant)+ it appends redundant facets after degenerate ones++ a degenerate facet has fewer than hull_dim neighbors+ a redundant facet's vertices is a subset of its neighbor's vertices+ tests for redundant merges first (appendmergeset is nop for others)+ in a merge, only needs to test neighbors of merged facet++ see:+ qh_merge_degenredundant() and qh_degen_redundant_facet()++ design:+ test for degenerate facet+ test for redundant neighbor+ test for degenerate neighbor+*/+void qh_degen_redundant_neighbors(qhT *qh, facetT *facet, facetT *delfacet) {+ vertexT *vertex, **vertexp;+ facetT *neighbor, **neighborp;+ int size;++ trace4((qh, qh->ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n",+ facet->id, getid_(delfacet)));+ if ((size= qh_setsize(qh, facet->neighbors)) < qh->hull_dim) {+ qh_appendmergeset(qh, facet, facet, MRGdegen, NULL);+ trace2((qh, qh->ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));+ }+ if (!delfacet)+ delfacet= facet;+ qh->vertex_visit++;+ FOREACHvertex_(facet->vertices)+ vertex->visitid= qh->vertex_visit;+ FOREACHneighbor_(delfacet) {+ /* uses early out instead of checking vertex count */+ if (neighbor == facet)+ continue;+ FOREACHvertex_(neighbor->vertices) {+ if (vertex->visitid != qh->vertex_visit)+ break;+ }+ if (!vertex) {+ qh_appendmergeset(qh, neighbor, facet, MRGredundant, NULL);+ trace2((qh, qh->ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id));+ }+ }+ FOREACHneighbor_(delfacet) { /* redundant merges occur first */+ if (neighbor == facet)+ continue;+ if ((size= qh_setsize(qh, neighbor->neighbors)) < qh->hull_dim) {+ qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, NULL);+ trace2((qh, qh->ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id));+ }+ }+} /* degen_redundant_neighbors */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="find_newvertex">-</a>++ qh_find_newvertex(qh, oldvertex, vertices, ridges )+ locate new vertex for renaming old vertex+ vertices is a set of possible new vertices+ vertices sorted by number of deleted ridges++ returns:+ newvertex or NULL+ each ridge includes both vertex and oldvertex+ vertices sorted by number of deleted ridges++ notes:+ modifies vertex->visitid+ new vertex is in one of the ridges+ renaming will not cause a duplicate ridge+ renaming will minimize the number of deleted ridges+ newvertex may not be adjacent in the dual (though unlikely)++ design:+ for each vertex in vertices+ set vertex->visitid to number of references in ridges+ remove unvisited vertices+ set qh.vertex_visit above all possible values+ sort vertices by number of references in ridges+ add each ridge to qh.hash_table+ for each vertex in vertices+ look for a vertex that would not cause a duplicate ridge after a rename+*/+vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges) {+ vertexT *vertex, **vertexp;+ setT *newridges;+ ridgeT *ridge, **ridgep;+ int size, hashsize;+ int hash;++#ifndef qh_NOtrace+ if (qh->IStracing >= 4) {+ qh_fprintf(qh, qh->ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",+ oldvertex->id);+ FOREACHvertex_(vertices)+ qh_fprintf(qh, qh->ferr, 8064, "v%d ", vertex->id);+ FOREACHridge_(ridges)+ qh_fprintf(qh, qh->ferr, 8065, "r%d ", ridge->id);+ qh_fprintf(qh, qh->ferr, 8066, "\n");+ }+#endif+ FOREACHvertex_(vertices)+ vertex->visitid= 0;+ FOREACHridge_(ridges) {+ FOREACHvertex_(ridge->vertices)+ vertex->visitid++;+ }+ FOREACHvertex_(vertices) {+ if (!vertex->visitid) {+ qh_setdelnth(qh, vertices, SETindex_(vertices,vertex));+ vertexp--; /* repeat since deleted this vertex */+ }+ }+ qh->vertex_visit += (unsigned int)qh_setsize(qh, ridges);+ if (!qh_setsize(qh, vertices)) {+ trace4((qh, qh->ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",+ oldvertex->id));+ return NULL;+ }+ qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(qh, vertices),+ sizeof(vertexT *), qh_comparevisit);+ /* can now use qh->vertex_visit */+ if (qh->PRINTstatistics) {+ size= qh_setsize(qh, vertices);+ zinc_(Zintersect);+ zadd_(Zintersecttot, size);+ zmax_(Zintersectmax, size);+ }+ hashsize= qh_newhashtable(qh, qh_setsize(qh, ridges));+ FOREACHridge_(ridges)+ qh_hashridge(qh, qh->hash_table, hashsize, ridge, oldvertex);+ FOREACHvertex_(vertices) {+ newridges= qh_vertexridges(qh, vertex);+ FOREACHridge_(newridges) {+ if (qh_hashridge_find(qh, qh->hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {+ zinc_(Zdupridge);+ break;+ }+ }+ qh_settempfree(qh, &newridges);+ if (!ridge)+ break; /* found a rename */+ }+ if (vertex) {+ /* counted in qh_renamevertex */+ trace2((qh, qh->ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",+ vertex->id, oldvertex->id, qh_setsize(qh, vertices), qh_setsize(qh, ridges)));+ }else {+ zinc_(Zfindfail);+ trace0((qh, qh->ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n",+ oldvertex->id, qh->furthest_id));+ }+ qh_setfree(qh, &qh->hash_table);+ return vertex;+} /* find_newvertex */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="findbest_test">-</a>++ qh_findbest_test(qh, testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist )+ test neighbor of facet for qh_findbestneighbor()+ if testcentrum,+ tests centrum (assumes it is defined)+ else+ tests vertices++ returns:+ if a better facet (i.e., vertices/centrum of facet closer to neighbor)+ updates bestfacet, dist, mindist, and maxdist+*/+void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor,+ facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {+ realT dist, mindist, maxdist;++ if (testcentrum) {+ zzinc_(Zbestdist);+ qh_distplane(qh, facet->center, neighbor, &dist);+ dist *= qh->hull_dim; /* estimate furthest vertex */+ if (dist < 0) {+ maxdist= 0;+ mindist= dist;+ dist= -dist;+ }else {+ mindist= 0;+ maxdist= dist;+ }+ }else+ dist= qh_getdistance(qh, facet, neighbor, &mindist, &maxdist);+ if (dist < *distp) {+ *bestfacet= neighbor;+ *mindistp= mindist;+ *maxdistp= maxdist;+ *distp= dist;+ }+} /* findbest_test */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="findbestneighbor">-</a>++ qh_findbestneighbor(qh, facet, dist, mindist, maxdist )+ finds best neighbor (least dist) of a facet for merging++ returns:+ returns min and max distances and their max absolute value++ notes:+ error if qh_ASvoronoi+ avoids merging old into new+ assumes ridge->nonconvex only set on one ridge between a pair of facets+ could use an early out predicate but not worth it++ design:+ if a large facet+ will test centrum+ else+ will test vertices+ if a large facet+ test nonconvex neighbors for best merge+ else+ test all neighbors for the best merge+ if testing centrum+ get distance information+*/+facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {+ facetT *neighbor, **neighborp, *bestfacet= NULL;+ ridgeT *ridge, **ridgep;+ boolT nonconvex= True, testcentrum= False;+ int size= qh_setsize(qh, facet->vertices);++ if(qh->CENTERtype==qh_ASvoronoi){+ qh_fprintf(qh, qh->ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ *distp= REALmax;+ if (size > qh_BESTcentrum2 * qh->hull_dim + qh_BESTcentrum) {+ testcentrum= True;+ zinc_(Zbestcentrum);+ if (!facet->center)+ facet->center= qh_getcentrum(qh, facet);+ }+ if (size > qh->hull_dim + qh_BESTnonconvex) {+ FOREACHridge_(facet->ridges) {+ if (ridge->nonconvex) {+ neighbor= otherfacet_(ridge, facet);+ qh_findbest_test(qh, testcentrum, facet, neighbor,+ &bestfacet, distp, mindistp, maxdistp);+ }+ }+ }+ if (!bestfacet) {+ nonconvex= False;+ FOREACHneighbor_(facet)+ qh_findbest_test(qh, testcentrum, facet, neighbor,+ &bestfacet, distp, mindistp, maxdistp);+ }+ if (!bestfacet) {+ qh_fprintf(qh, qh->ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ if (testcentrum)+ qh_getdistance(qh, facet, bestfacet, mindistp, maxdistp);+ 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",+ bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));+ return(bestfacet);+} /* findbestneighbor */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="flippedmerges">-</a>++ qh_flippedmerges(qh, facetlist, wasmerge )+ merge flipped facets into best neighbor+ assumes qh.facet_mergeset at top of temporary stack++ returns:+ no flipped facets on facetlist+ sets wasmerge if merge occurred+ degen/redundant merges passed through++ notes:+ othermerges not needed since qh.facet_mergeset is empty before & after+ keep it in case of change++ design:+ append flipped facets to qh.facetmergeset+ for each flipped merge+ find best neighbor+ merge facet into neighbor+ merge degenerate and redundant facets+ remove flipped merges from qh.facet_mergeset+*/+void qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge) {+ facetT *facet, *neighbor, *facet1;+ realT dist, mindist, maxdist;+ mergeT *merge, **mergep;+ setT *othermerges;+ int nummerge=0;++ trace4((qh, qh->ferr, 4024, "qh_flippedmerges: begin\n"));+ FORALLfacet_(facetlist) {+ if (facet->flipped && !facet->visible)+ qh_appendmergeset(qh, facet, facet, MRGflip, NULL);+ }+ othermerges= qh_settemppop(qh); /* was facet_mergeset */+ qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);+ qh_settemppush(qh, othermerges);+ FOREACHmerge_(othermerges) {+ facet1= merge->facet1;+ if (merge->type != MRGflip || facet1->visible)+ continue;+ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;+ neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);+ trace0((qh, qh->ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",+ facet1->id, neighbor->id, dist, qh->furthest_id));+ qh_mergefacet(qh, facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);+ nummerge++;+ if (qh->PRINTstatistics) {+ zinc_(Zflipped);+ wadd_(Wflippedtot, dist);+ wmax_(Wflippedmax, dist);+ }+ qh_merge_degenredundant(qh);+ }+ FOREACHmerge_(othermerges) {+ if (merge->facet1->visible || merge->facet2->visible)+ qh_memfree(qh, merge, (int)sizeof(mergeT));+ else+ qh_setappend(qh, &qh->facet_mergeset, merge);+ }+ qh_settempfree(qh, &othermerges);+ if (nummerge)+ *wasmerge= True;+ trace1((qh, qh->ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));+} /* flippedmerges */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="forcedmerges">-</a>++ qh_forcedmerges(qh, wasmerge )+ merge duplicated ridges++ returns:+ removes all duplicate ridges on facet_mergeset+ wasmerge set if merge+ qh.facet_mergeset may include non-forced merges(none for now)+ qh.degen_mergeset includes degen/redun merges++ notes:+ duplicate ridges occur when the horizon is pinched,+ i.e. a subridge occurs in more than two horizon ridges.+ could rename vertices that pinch the horizon+ assumes qh_merge_degenredundant() has not be called+ othermerges isn't needed since facet_mergeset is empty afterwards+ keep it in case of change++ design:+ for each duplicate ridge+ find current facets by chasing f.replace links+ check for wide merge due to duplicate ridge+ determine best direction for facet+ merge one facet into the other+ remove duplicate ridges from qh.facet_mergeset+*/+void qh_forcedmerges(qhT *qh, boolT *wasmerge) {+ facetT *facet1, *facet2;+ mergeT *merge, **mergep;+ realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2;+ setT *othermerges;+ int nummerge=0, numflip=0;++ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;+ trace4((qh, qh->ferr, 4025, "qh_forcedmerges: begin\n"));+ othermerges= qh_settemppop(qh); /* was facet_mergeset */+ qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);+ qh_settemppush(qh, othermerges);+ FOREACHmerge_(othermerges) {+ if (merge->type != MRGridge)+ continue;+ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;+ facet1= merge->facet1;+ facet2= merge->facet2;+ while (facet1->visible) /* must exist, no qh_merge_degenredunant */+ facet1= facet1->f.replace; /* previously merged facet */+ while (facet2->visible)+ facet2= facet2->f.replace; /* previously merged facet */+ if (facet1 == facet2)+ continue;+ if (!qh_setin(facet2->neighbors, facet1)) {+ qh_fprintf(qh, qh->ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n",+ merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);+ }+ dist1= qh_getdistance(qh, facet1, facet2, &mindist1, &maxdist1);+ dist2= qh_getdistance(qh, facet2, facet1, &mindist2, &maxdist2);+ qh_check_dupridge(qh, facet1, dist1, facet2, dist2);+ if (dist1 < dist2)+ qh_mergefacet(qh, facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);+ else {+ qh_mergefacet(qh, facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex);+ dist1= dist2;+ facet1= facet2;+ }+ if (facet1->flipped) {+ zinc_(Zmergeflipdup);+ numflip++;+ }else+ nummerge++;+ if (qh->PRINTstatistics) {+ zinc_(Zduplicate);+ wadd_(Wduplicatetot, dist1);+ wmax_(Wduplicatemax, dist1);+ }+ }+ FOREACHmerge_(othermerges) {+ if (merge->type == MRGridge)+ qh_memfree(qh, merge, (int)sizeof(mergeT));+ else+ qh_setappend(qh, &qh->facet_mergeset, merge);+ }+ qh_settempfree(qh, &othermerges);+ if (nummerge)+ *wasmerge= True;+ trace1((qh, qh->ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",+ nummerge, numflip));+} /* forcedmerges */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="getmergeset">-</a>++ qh_getmergeset(qh, facetlist )+ determines nonconvex facets on facetlist+ tests !tested ridges and nonconvex ridges of !tested facets++ returns:+ returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged+ all ridges tested++ notes:+ assumes no nonconvex ridges with both facets tested+ uses facet->tested/ridge->tested to prevent duplicate tests+ can not limit tests to modified ridges since the centrum changed+ uses qh.visit_id++ see:+ qh_getmergeset_initial()++ design:+ for each facet on facetlist+ for each ridge of facet+ if untested ridge+ test ridge for convexity+ if non-convex+ append ridge to qh.facet_mergeset+ sort qh.facet_mergeset by angle+*/+void qh_getmergeset(qhT *qh, facetT *facetlist) {+ facetT *facet, *neighbor, **neighborp;+ ridgeT *ridge, **ridgep;+ int nummerges;++ nummerges= qh_setsize(qh, qh->facet_mergeset);+ trace4((qh, qh->ferr, 4026, "qh_getmergeset: started.\n"));+ qh->visit_id++;+ FORALLfacet_(facetlist) {+ if (facet->tested)+ continue;+ facet->visitid= qh->visit_id;+ facet->tested= True; /* must be non-simplicial due to merge */+ FOREACHneighbor_(facet)+ neighbor->seen= False;+ FOREACHridge_(facet->ridges) {+ if (ridge->tested && !ridge->nonconvex)+ continue;+ /* if tested & nonconvex, need to append merge */+ neighbor= otherfacet_(ridge, facet);+ if (neighbor->seen) {+ ridge->tested= True;+ ridge->nonconvex= False;+ }else if (neighbor->visitid != qh->visit_id) {+ ridge->tested= True;+ ridge->nonconvex= False;+ neighbor->seen= True; /* only one ridge is marked nonconvex */+ if (qh_test_appendmerge(qh, facet, neighbor))+ ridge->nonconvex= True;+ }+ }+ }+ nummerges= qh_setsize(qh, qh->facet_mergeset);+ if (qh->ANGLEmerge)+ qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);+ else+ qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);+ if (qh->POSTmerging) {+ zadd_(Zmergesettot2, nummerges);+ }else {+ zadd_(Zmergesettot, nummerges);+ zmax_(Zmergesetmax, nummerges);+ }+ trace2((qh, qh->ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));+} /* getmergeset */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="getmergeset_initial">-</a>++ qh_getmergeset_initial(qh, facetlist )+ determine initial qh.facet_mergeset for facets+ tests all facet/neighbor pairs on facetlist++ returns:+ sorted qh.facet_mergeset with nonconvex ridges+ sets facet->tested, ridge->tested, and ridge->nonconvex++ notes:+ uses visit_id, assumes ridge->nonconvex is False++ see:+ qh_getmergeset()++ design:+ for each facet on facetlist+ for each untested neighbor of facet+ test facet and neighbor for convexity+ if non-convex+ append merge to qh.facet_mergeset+ mark one of the ridges as nonconvex+ sort qh.facet_mergeset by angle+*/+void qh_getmergeset_initial(qhT *qh, facetT *facetlist) {+ facetT *facet, *neighbor, **neighborp;+ ridgeT *ridge, **ridgep;+ int nummerges;++ qh->visit_id++;+ FORALLfacet_(facetlist) {+ facet->visitid= qh->visit_id;+ facet->tested= True;+ FOREACHneighbor_(facet) {+ if (neighbor->visitid != qh->visit_id) {+ if (qh_test_appendmerge(qh, facet, neighbor)) {+ FOREACHridge_(neighbor->ridges) {+ if (facet == otherfacet_(ridge, neighbor)) {+ ridge->nonconvex= True;+ break; /* only one ridge is marked nonconvex */+ }+ }+ }+ }+ }+ FOREACHridge_(facet->ridges)+ ridge->tested= True;+ }+ nummerges= qh_setsize(qh, qh->facet_mergeset);+ if (qh->ANGLEmerge)+ qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);+ else+ qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);+ if (qh->POSTmerging) {+ zadd_(Zmergeinittot2, nummerges);+ }else {+ zadd_(Zmergeinittot, nummerges);+ zmax_(Zmergeinitmax, nummerges);+ }+ trace2((qh, qh->ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));+} /* getmergeset_initial */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="hashridge">-</a>++ qh_hashridge(qh, hashtable, hashsize, ridge, oldvertex )+ add ridge to hashtable without oldvertex++ notes:+ assumes hashtable is large enough++ design:+ determine hash value for ridge without oldvertex+ find next empty slot for ridge+*/+void qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {+ int hash;+ ridgeT *ridgeA;++ hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, oldvertex);+ while (True) {+ if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {+ SETelem_(hashtable, hash)= ridge;+ break;+ }else if (ridgeA == ridge)+ break;+ if (++hash == hashsize)+ hash= 0;+ }+} /* hashridge */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="hashridge_find">-</a>++ qh_hashridge_find(qh, hashtable, hashsize, ridge, vertex, oldvertex, hashslot )+ returns matching ridge without oldvertex in hashtable+ for ridge without vertex+ if oldvertex is NULL+ matches with any one skip++ returns:+ matching ridge or NULL+ if no match,+ if ridge already in table+ hashslot= -1+ else+ hashslot= next NULL index++ notes:+ assumes hashtable is large enough+ can't match ridge to itself++ design:+ get hash value for ridge without vertex+ for each hashslot+ return match if ridge matches ridgeA without oldvertex+*/+ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge,+ vertexT *vertex, vertexT *oldvertex, int *hashslot) {+ int hash;+ ridgeT *ridgeA;++ *hashslot= 0;+ zinc_(Zhashridge);+ hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, vertex);+ while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {+ if (ridgeA == ridge)+ *hashslot= -1;+ else {+ zinc_(Zhashridgetest);+ if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))+ return ridgeA;+ }+ if (++hash == hashsize)+ hash= 0;+ }+ if (!*hashslot)+ *hashslot= hash;+ return NULL;+} /* hashridge_find */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="makeridges">-</a>++ qh_makeridges(qh, facet )+ creates explicit ridges between simplicial facets++ returns:+ facet with ridges and without qh_MERGEridge+ ->simplicial is False++ notes:+ allows qh_MERGEridge flag+ uses existing ridges+ duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)++ see:+ qh_mergecycle_ridges()++ design:+ look for qh_MERGEridge neighbors+ mark neighbors that already have ridges+ for each unprocessed neighbor of facet+ create a ridge for neighbor and facet+ if any qh_MERGEridge neighbors+ delete qh_MERGEridge flags (already handled by qh_mark_dupridges)+*/+void qh_makeridges(qhT *qh, facetT *facet) {+ facetT *neighbor, **neighborp;+ ridgeT *ridge, **ridgep;+ int neighbor_i, neighbor_n;+ boolT toporient, mergeridge= False;++ if (!facet->simplicial)+ return;+ trace4((qh, qh->ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));+ facet->simplicial= False;+ FOREACHneighbor_(facet) {+ if (neighbor == qh_MERGEridge)+ mergeridge= True;+ else+ neighbor->seen= False;+ }+ FOREACHridge_(facet->ridges)+ otherfacet_(ridge, facet)->seen= True;+ FOREACHneighbor_i_(qh, facet) {+ if (neighbor == qh_MERGEridge)+ continue; /* fixed by qh_mark_dupridges */+ else if (!neighbor->seen) { /* no current ridges */+ ridge= qh_newridge(qh);+ ridge->vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,+ neighbor_i, 0);+ toporient= facet->toporient ^ (neighbor_i & 0x1);+ if (toporient) {+ ridge->top= facet;+ ridge->bottom= neighbor;+ }else {+ ridge->top= neighbor;+ ridge->bottom= facet;+ }+#if 0 /* this also works */+ flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);+ if (facet->toporient ^ (skip1 & 0x1) ^ flip) {+ ridge->top= neighbor;+ ridge->bottom= facet;+ }else {+ ridge->top= facet;+ ridge->bottom= neighbor;+ }+#endif+ qh_setappend(qh, &(facet->ridges), ridge);+ qh_setappend(qh, &(neighbor->ridges), ridge);+ }+ }+ if (mergeridge) {+ while (qh_setdel(facet->neighbors, qh_MERGEridge))+ ; /* delete each one */+ }+} /* makeridges */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mark_dupridges">-</a>++ qh_mark_dupridges(qh, facetlist )+ add duplicated ridges to qh.facet_mergeset+ facet->dupridge is true++ returns:+ duplicate ridges on qh.facet_mergeset+ ->mergeridge/->mergeridge2 set+ duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge+ no MERGEridges in neighbor sets++ notes:+ duplicate ridges occur when the horizon is pinched,+ i.e. a subridge occurs in more than two horizon ridges.+ could rename vertices that pinch the horizon (thus removing subridge)+ uses qh.visit_id++ design:+ for all facets on facetlist+ if facet contains a duplicate ridge+ for each neighbor of facet+ if neighbor marked qh_MERGEridge (one side of the merge)+ set facet->mergeridge+ else+ if neighbor contains a duplicate ridge+ and the back link is qh_MERGEridge+ append duplicate ridge to qh.facet_mergeset+ for each duplicate ridge+ make ridge sets in preparation for merging+ remove qh_MERGEridge from neighbor set+ for each duplicate ridge+ restore the missing neighbor from the neighbor set that was qh_MERGEridge+ add the missing ridge for this neighbor+*/+void qh_mark_dupridges(qhT *qh, facetT *facetlist) {+ facetT *facet, *neighbor, **neighborp;+ int nummerge=0;+ mergeT *merge, **mergep;+++ trace4((qh, qh->ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n"));+ FORALLfacet_(facetlist) {+ if (facet->dupridge) {+ FOREACHneighbor_(facet) {+ if (neighbor == qh_MERGEridge) {+ facet->mergeridge= True;+ continue;+ }+ if (neighbor->dupridge+ && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */+ qh_appendmergeset(qh, facet, neighbor, MRGridge, NULL);+ facet->mergeridge2= True;+ facet->mergeridge= True;+ nummerge++;+ }+ }+ }+ }+ if (!nummerge)+ return;+ FORALLfacet_(facetlist) { /* gets rid of qh_MERGEridge */+ if (facet->mergeridge && !facet->mergeridge2)+ qh_makeridges(qh, facet);+ }+ FOREACHmerge_(qh->facet_mergeset) { /* restore the missing neighbors */+ if (merge->type == MRGridge) {+ qh_setappend(qh, &merge->facet2->neighbors, merge->facet1);+ qh_makeridges(qh, merge->facet1); /* and the missing ridges */+ }+ }+ trace1((qh, qh->ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n",+ nummerge));+} /* mark_dupridges */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="maydropneighbor">-</a>++ qh_maydropneighbor(qh, facet )+ drop neighbor relationship if no ridge between facet and neighbor++ returns:+ neighbor sets updated+ appends degenerate facets to qh.facet_mergeset++ notes:+ won't cause redundant facets since vertex inclusion is the same+ may drop vertex and neighbor if no ridge+ uses qh.visit_id++ design:+ visit all neighbors with ridges+ for each unvisited neighbor of facet+ delete neighbor and facet from the neighbor sets+ if neighbor becomes degenerate+ append neighbor to qh.degen_mergeset+ if facet is degenerate+ append facet to qh.degen_mergeset+*/+void qh_maydropneighbor(qhT *qh, facetT *facet) {+ ridgeT *ridge, **ridgep;+ realT angledegen= qh_ANGLEdegen;+ facetT *neighbor, **neighborp;++ qh->visit_id++;+ trace4((qh, qh->ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",+ facet->id));+ FOREACHridge_(facet->ridges) {+ ridge->top->visitid= qh->visit_id;+ ridge->bottom->visitid= qh->visit_id;+ }+ FOREACHneighbor_(facet) {+ if (neighbor->visitid != qh->visit_id) {+ trace0((qh, qh->ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n",+ facet->id, neighbor->id, qh->furthest_id));+ zinc_(Zdropneighbor);+ qh_setdel(facet->neighbors, neighbor);+ neighborp--; /* repeat, deleted a neighbor */+ qh_setdel(neighbor->neighbors, facet);+ if (qh_setsize(qh, neighbor->neighbors) < qh->hull_dim) {+ zinc_(Zdropdegen);+ qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, &angledegen);+ trace2((qh, qh->ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));+ }+ }+ }+ if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {+ zinc_(Zdropdegen);+ qh_appendmergeset(qh, facet, facet, MRGdegen, &angledegen);+ trace2((qh, qh->ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));+ }+} /* maydropneighbor */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="merge_degenredundant">-</a>++ qh_merge_degenredundant(qh)+ merge all degenerate and redundant facets+ qh.degen_mergeset contains merges from qh_degen_redundant_neighbors()++ returns:+ number of merges performed+ resets facet->degenerate/redundant+ if deleted (visible) facet has no neighbors+ sets ->f.replace to NULL++ notes:+ redundant merges happen before degenerate ones+ merging and renaming vertices can result in degen/redundant facets++ design:+ for each merge on qh.degen_mergeset+ if redundant merge+ if non-redundant facet merged into redundant facet+ recheck facet for redundancy+ else+ merge redundant facet into other facet+*/+int qh_merge_degenredundant(qhT *qh) {+ int size;+ mergeT *merge;+ facetT *bestneighbor, *facet1, *facet2;+ realT dist, mindist, maxdist;+ vertexT *vertex, **vertexp;+ int nummerges= 0;+ mergeType mergetype;++ while ((merge= (mergeT*)qh_setdellast(qh->degen_mergeset))) {+ facet1= merge->facet1;+ facet2= merge->facet2;+ mergetype= merge->type;+ qh_memfree(qh, merge, (int)sizeof(mergeT));+ if (facet1->visible)+ continue;+ facet1->degenerate= False;+ facet1->redundant= False;+ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;+ if (mergetype == MRGredundant) {+ zinc_(Zneighbor);+ while (facet2->visible) {+ if (!facet2->f.replace) {+ qh_fprintf(qh, qh->ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n",+ facet1->id, facet2->id);+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);+ }+ facet2= facet2->f.replace;+ }+ if (facet1 == facet2) {+ qh_degen_redundant_facet(qh, facet1); /* in case of others */+ continue;+ }+ trace2((qh, qh->ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",+ facet1->id, facet2->id));+ qh_mergefacet(qh, facet1, facet2, NULL, NULL, !qh_MERGEapex);+ /* merge distance is already accounted for */+ nummerges++;+ }else { /* mergetype == MRGdegen, other merges may have fixed */+ if (!(size= qh_setsize(qh, facet1->neighbors))) {+ zinc_(Zdelfacetdup);+ trace2((qh, qh->ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id));+ qh_willdelete(qh, facet1, NULL);+ FOREACHvertex_(facet1->vertices) {+ qh_setdel(vertex->neighbors, facet1);+ if (!SETfirst_(vertex->neighbors)) {+ zinc_(Zdegenvertex);+ trace2((qh, qh->ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",+ vertex->id, facet1->id));+ vertex->deleted= True;+ qh_setappend(qh, &qh->del_vertices, vertex);+ }+ }+ nummerges++;+ }else if (size < qh->hull_dim) {+ bestneighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);+ trace2((qh, qh->ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",+ facet1->id, size, bestneighbor->id, dist));+ qh_mergefacet(qh, facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);+ nummerges++;+ if (qh->PRINTstatistics) {+ zinc_(Zdegen);+ wadd_(Wdegentot, dist);+ wmax_(Wdegenmax, dist);+ }+ } /* else, another merge fixed the degeneracy and redundancy tested */+ }+ }+ return nummerges;+} /* merge_degenredundant */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="merge_nonconvex">-</a>++ qh_merge_nonconvex(qh, facet1, facet2, mergetype )+ remove non-convex ridge between facet1 into facet2+ mergetype gives why the facet's are non-convex++ returns:+ merges one of the facets into the best neighbor++ design:+ if one of the facets is a new facet+ prefer merging new facet into old facet+ find best neighbors for both facets+ merge the nearest facet into its best neighbor+ update the statistics+*/+void qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) {+ facetT *bestfacet, *bestneighbor, *neighbor;+ realT dist, dist2, mindist, mindist2, maxdist, maxdist2;++ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;+ trace3((qh, qh->ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",+ zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));+ /* concave or coplanar */+ if (!facet1->newfacet) {+ bestfacet= facet2; /* avoid merging old facet if new is ok */+ facet2= facet1;+ facet1= bestfacet;+ }else+ bestfacet= facet1;+ bestneighbor= qh_findbestneighbor(qh, bestfacet, &dist, &mindist, &maxdist);+ neighbor= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2);+ if (dist < dist2) {+ qh_mergefacet(qh, bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);+ }else if (qh->AVOIDold && !facet2->newfacet+ && ((mindist >= -qh->MAXcoplanar && maxdist <= qh->max_outside)+ || dist * 1.5 < dist2)) {+ zinc_(Zavoidold);+ wadd_(Wavoidoldtot, dist);+ wmax_(Wavoidoldmax, dist);+ 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",+ facet2->id, dist2, facet1->id, dist2));+ qh_mergefacet(qh, bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);+ }else {+ qh_mergefacet(qh, facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex);+ dist= dist2;+ }+ if (qh->PRINTstatistics) {+ if (mergetype == MRGanglecoplanar) {+ zinc_(Zacoplanar);+ wadd_(Wacoplanartot, dist);+ wmax_(Wacoplanarmax, dist);+ }else if (mergetype == MRGconcave) {+ zinc_(Zconcave);+ wadd_(Wconcavetot, dist);+ wmax_(Wconcavemax, dist);+ }else { /* MRGcoplanar */+ zinc_(Zcoplanar);+ wadd_(Wcoplanartot, dist);+ wmax_(Wcoplanarmax, dist);+ }+ }+} /* merge_nonconvex */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergecycle">-</a>++ qh_mergecycle(qh, samecycle, newfacet )+ merge a cycle of facets starting at samecycle into a newfacet+ newfacet is a horizon facet with ->normal+ samecycle facets are simplicial from an apex++ returns:+ initializes vertex neighbors on first merge+ samecycle deleted (placed on qh.visible_list)+ newfacet at end of qh.facet_list+ deleted vertices on qh.del_vertices++ see:+ qh_mergefacet()+ called by qh_mergecycle_all() for multiple, same cycle facets++ design:+ make vertex neighbors if necessary+ make ridges for newfacet+ merge neighbor sets of samecycle into newfacet+ merge ridges of samecycle into newfacet+ merge vertex neighbors of samecycle into newfacet+ make apex of samecycle the apex of newfacet+ if newfacet wasn't a new facet+ add its vertices to qh.newvertex_list+ delete samecycle facets a make newfacet a newfacet+*/+void qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet) {+ int traceonce= False, tracerestore= 0;+ vertexT *apex;+#ifndef qh_NOtrace+ facetT *same;+#endif++ if (newfacet->tricoplanar) {+ if (!qh->TRInormals) {+ qh_fprintf(qh, qh->ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n");+ qh_errexit(qh, qh_ERRqhull, newfacet, NULL);+ }+ newfacet->tricoplanar= False;+ newfacet->keepcentrum= False;+ }+ if (!qh->VERTEXneighbors)+ qh_vertexneighbors(qh);+ zzinc_(Ztotmerge);+ if (qh->REPORTfreq2 && qh->POSTmerging) {+ if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)+ qh_tracemerging(qh);+ }+#ifndef qh_NOtrace+ if (qh->TRACEmerge == zzval_(Ztotmerge))+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;+ trace2((qh, qh->ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",+ zzval_(Ztotmerge), samecycle->id, newfacet->id));+ if (newfacet == qh->tracefacet) {+ tracerestore= qh->IStracing;+ qh->IStracing= 4;+ qh_fprintf(qh, qh->ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",+ zzval_(Ztotmerge), samecycle->id, newfacet->id, qh->furthest_id);+ traceonce= True;+ }+ if (qh->IStracing >=4) {+ qh_fprintf(qh, qh->ferr, 8069, " same cycle:");+ FORALLsame_cycle_(samecycle)+ qh_fprintf(qh, qh->ferr, 8070, " f%d", same->id);+ qh_fprintf(qh, qh->ferr, 8071, "\n");+ }+ if (qh->IStracing >=4)+ qh_errprint(qh, "MERGING CYCLE", samecycle, newfacet, NULL, NULL);+#endif /* !qh_NOtrace */+ apex= SETfirstt_(samecycle->vertices, vertexT);+ qh_makeridges(qh, newfacet);+ qh_mergecycle_neighbors(qh, samecycle, newfacet);+ qh_mergecycle_ridges(qh, samecycle, newfacet);+ qh_mergecycle_vneighbors(qh, samecycle, newfacet);+ if (SETfirstt_(newfacet->vertices, vertexT) != apex)+ qh_setaddnth(qh, &newfacet->vertices, 0, apex); /* apex has last id */+ if (!newfacet->newfacet)+ qh_newvertices(qh, newfacet->vertices);+ qh_mergecycle_facets(qh, samecycle, newfacet);+ qh_tracemerge(qh, samecycle, newfacet);+ /* check for degen_redundant_neighbors after qh_forcedmerges() */+ if (traceonce) {+ qh_fprintf(qh, qh->ferr, 8072, "qh_mergecycle: end of trace facet\n");+ qh->IStracing= tracerestore;+ }+} /* mergecycle */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergecycle_all">-</a>++ qh_mergecycle_all(qh, facetlist, wasmerge )+ merge all samecycles of coplanar facets into horizon+ don't merge facets with ->mergeridge (these already have ->normal)+ all facets are simplicial from apex+ all facet->cycledone == False++ returns:+ all newfacets merged into coplanar horizon facets+ deleted vertices on qh.del_vertices+ sets wasmerge if any merge++ see:+ calls qh_mergecycle for multiple, same cycle facets++ design:+ for each facet on facetlist+ skip facets with duplicate ridges and normals+ check that facet is in a samecycle (->mergehorizon)+ if facet only member of samecycle+ sets vertex->delridge for all vertices except apex+ merge facet into horizon+ else+ mark all facets in samecycle+ remove facets with duplicate ridges from samecycle+ merge samecycle into horizon (deletes facets from facetlist)+*/+void qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge) {+ facetT *facet, *same, *prev, *horizon;+ facetT *samecycle= NULL, *nextfacet, *nextsame;+ vertexT *apex, *vertex, **vertexp;+ int cycles=0, total=0, facets, nummerge;++ trace2((qh, qh->ferr, 2031, "qh_mergecycle_all: begin\n"));+ for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {+ if (facet->normal)+ continue;+ if (!facet->mergehorizon) {+ qh_fprintf(qh, qh->ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ horizon= SETfirstt_(facet->neighbors, facetT);+ if (facet->f.samecycle == facet) {+ zinc_(Zonehorizon);+ /* merge distance done in qh_findhorizon */+ apex= SETfirstt_(facet->vertices, vertexT);+ FOREACHvertex_(facet->vertices) {+ if (vertex != apex)+ vertex->delridge= True;+ }+ horizon->f.newcycle= NULL;+ qh_mergefacet(qh, facet, horizon, NULL, NULL, qh_MERGEapex);+ }else {+ samecycle= facet;+ facets= 0;+ prev= facet;+ for (same= facet->f.samecycle; same; /* FORALLsame_cycle_(facet) */+ same= (same == facet ? NULL :nextsame)) { /* ends at facet */+ nextsame= same->f.samecycle;+ if (same->cycledone || same->visible)+ qh_infiniteloop(qh, same);+ same->cycledone= True;+ if (same->normal) {+ prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */+ same->f.samecycle= NULL;+ }else {+ prev= same;+ facets++;+ }+ }+ while (nextfacet && nextfacet->cycledone) /* will delete samecycle */+ nextfacet= nextfacet->next;+ horizon->f.newcycle= NULL;+ qh_mergecycle(qh, samecycle, horizon);+ nummerge= horizon->nummerge + facets;+ if (nummerge > qh_MAXnummerge)+ horizon->nummerge= qh_MAXnummerge;+ else+ horizon->nummerge= (short unsigned int)nummerge;+ zzinc_(Zcyclehorizon);+ total += facets;+ zzadd_(Zcyclefacettot, facets);+ zmax_(Zcyclefacetmax, facets);+ }+ cycles++;+ }+ if (cycles)+ *wasmerge= True;+ trace1((qh, qh->ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));+} /* mergecycle_all */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergecycle_facets">-</a>++ qh_mergecycle_facets(qh, samecycle, newfacet )+ finish merge of samecycle into newfacet++ returns:+ samecycle prepended to visible_list for later deletion and partitioning+ each facet->f.replace == newfacet++ newfacet moved to end of qh.facet_list+ makes newfacet a newfacet (get's facet1->id if it was old)+ sets newfacet->newmerge+ clears newfacet->center (unless merging into a large facet)+ clears newfacet->tested and ridge->tested for facet1++ adds neighboring facets to facet_mergeset if redundant or degenerate++ design:+ make newfacet a new facet and set its flags+ move samecycle facets to qh.visible_list for later deletion+ unless newfacet is large+ remove its centrum+*/+void qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet) {+ facetT *same, *next;++ trace4((qh, qh->ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));+ qh_removefacet(qh, newfacet); /* append as a newfacet to end of qh->facet_list */+ qh_appendfacet(qh, newfacet);+ newfacet->newfacet= True;+ newfacet->simplicial= False;+ newfacet->newmerge= True;++ for (same= samecycle->f.samecycle; same; same= (same == samecycle ? NULL : next)) {+ next= same->f.samecycle; /* reused by willdelete */+ qh_willdelete(qh, same, newfacet);+ }+ if (newfacet->center+ && qh_setsize(qh, newfacet->vertices) <= qh->hull_dim + qh_MAXnewcentrum) {+ qh_memfree(qh, newfacet->center, qh->normal_size);+ newfacet->center= NULL;+ }+ trace3((qh, qh->ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",+ samecycle->id, newfacet->id));+} /* mergecycle_facets */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergecycle_neighbors">-</a>++ qh_mergecycle_neighbors(qh, samecycle, newfacet )+ add neighbors for samecycle facets to newfacet++ returns:+ newfacet with updated neighbors and vice-versa+ newfacet has ridges+ all neighbors of newfacet marked with qh.visit_id+ samecycle facets marked with qh.visit_id-1+ ridges updated for simplicial neighbors of samecycle with a ridge++ notes:+ assumes newfacet not in samecycle+ usually, samecycle facets are new, simplicial facets without internal ridges+ not so if horizon facet is coplanar to two different samecycles++ see:+ qh_mergeneighbors()++ design:+ check samecycle+ delete neighbors from newfacet that are also in samecycle+ for each neighbor of a facet in samecycle+ if neighbor is simplicial+ if first visit+ move the neighbor relation to newfacet+ update facet links for its ridges+ else+ make ridges for neighbor+ remove samecycle reference+ else+ update neighbor sets+*/+void qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {+ facetT *same, *neighbor, **neighborp;+ int delneighbors= 0, newneighbors= 0;+ unsigned int samevisitid;+ ridgeT *ridge, **ridgep;++ samevisitid= ++qh->visit_id;+ FORALLsame_cycle_(samecycle) {+ if (same->visitid == samevisitid || same->visible)+ qh_infiniteloop(qh, samecycle);+ same->visitid= samevisitid;+ }+ newfacet->visitid= ++qh->visit_id;+ trace4((qh, qh->ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));+ FOREACHneighbor_(newfacet) {+ if (neighbor->visitid == samevisitid) {+ SETref_(neighbor)= NULL; /* samecycle neighbors deleted */+ delneighbors++;+ }else+ neighbor->visitid= qh->visit_id;+ }+ qh_setcompact(qh, newfacet->neighbors);++ trace4((qh, qh->ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));+ FORALLsame_cycle_(samecycle) {+ FOREACHneighbor_(same) {+ if (neighbor->visitid == samevisitid)+ continue;+ if (neighbor->simplicial) {+ if (neighbor->visitid != qh->visit_id) {+ qh_setappend(qh, &newfacet->neighbors, neighbor);+ qh_setreplace(qh, neighbor->neighbors, same, newfacet);+ newneighbors++;+ neighbor->visitid= qh->visit_id;+ FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */+ if (ridge->top == same) {+ ridge->top= newfacet;+ break;+ }else if (ridge->bottom == same) {+ ridge->bottom= newfacet;+ break;+ }+ }+ }else {+ qh_makeridges(qh, neighbor);+ qh_setdel(neighbor->neighbors, same);+ /* same can't be horizon facet for neighbor */+ }+ }else { /* non-simplicial neighbor */+ qh_setdel(neighbor->neighbors, same);+ if (neighbor->visitid != qh->visit_id) {+ qh_setappend(qh, &neighbor->neighbors, newfacet);+ qh_setappend(qh, &newfacet->neighbors, neighbor);+ neighbor->visitid= qh->visit_id;+ newneighbors++;+ }+ }+ }+ }+ trace2((qh, qh->ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",+ delneighbors, newneighbors));+} /* mergecycle_neighbors */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergecycle_ridges">-</a>++ qh_mergecycle_ridges(qh, samecycle, newfacet )+ add ridges/neighbors for facets in samecycle to newfacet+ all new/old neighbors of newfacet marked with qh.visit_id+ facets in samecycle marked with qh.visit_id-1+ newfacet marked with qh.visit_id++ returns:+ newfacet has merged ridges++ notes:+ ridge already updated for simplicial neighbors of samecycle with a ridge++ see:+ qh_mergeridges()+ qh_makeridges()++ design:+ remove ridges between newfacet and samecycle+ for each facet in samecycle+ for each ridge in facet+ update facet pointers in ridge+ skip ridges processed in qh_mergecycle_neighors+ free ridges between newfacet and samecycle+ free ridges between facets of samecycle (on 2nd visit)+ append remaining ridges to newfacet+ if simpilicial facet+ for each neighbor of facet+ if simplicial facet+ and not samecycle facet or newfacet+ make ridge between neighbor and newfacet+*/+void qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet) {+ facetT *same, *neighbor= NULL;+ int numold=0, numnew=0;+ int neighbor_i, neighbor_n;+ unsigned int samevisitid;+ ridgeT *ridge, **ridgep;+ boolT toporient;+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */++ trace4((qh, qh->ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));+ samevisitid= qh->visit_id -1;+ FOREACHridge_(newfacet->ridges) {+ neighbor= otherfacet_(ridge, newfacet);+ if (neighbor->visitid == samevisitid)+ SETref_(ridge)= NULL; /* ridge free'd below */+ }+ qh_setcompact(qh, newfacet->ridges);++ trace4((qh, qh->ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));+ FORALLsame_cycle_(samecycle) {+ FOREACHridge_(same->ridges) {+ if (ridge->top == same) {+ ridge->top= newfacet;+ neighbor= ridge->bottom;+ }else if (ridge->bottom == same) {+ ridge->bottom= newfacet;+ neighbor= ridge->top;+ }else if (ridge->top == newfacet || ridge->bottom == newfacet) {+ qh_setappend(qh, &newfacet->ridges, ridge);+ numold++; /* already set by qh_mergecycle_neighbors */+ continue;+ }else {+ qh_fprintf(qh, qh->ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);+ qh_errexit(qh, qh_ERRqhull, NULL, ridge);+ }+ if (neighbor == newfacet) {+ qh_setfree(qh, &(ridge->vertices));+ qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);+ numold++;+ }else if (neighbor->visitid == samevisitid) {+ qh_setdel(neighbor->ridges, ridge);+ qh_setfree(qh, &(ridge->vertices));+ qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);+ numold++;+ }else {+ qh_setappend(qh, &newfacet->ridges, ridge);+ numold++;+ }+ }+ if (same->ridges)+ qh_settruncate(qh, same->ridges, 0);+ if (!same->simplicial)+ continue;+ FOREACHneighbor_i_(qh, same) { /* note: !newfact->simplicial */+ if (neighbor->visitid != samevisitid && neighbor->simplicial) {+ ridge= qh_newridge(qh);+ ridge->vertices= qh_setnew_delnthsorted(qh, same->vertices, qh->hull_dim,+ neighbor_i, 0);+ toporient= same->toporient ^ (neighbor_i & 0x1);+ if (toporient) {+ ridge->top= newfacet;+ ridge->bottom= neighbor;+ }else {+ ridge->top= neighbor;+ ridge->bottom= newfacet;+ }+ qh_setappend(qh, &(newfacet->ridges), ridge);+ qh_setappend(qh, &(neighbor->ridges), ridge);+ numnew++;+ }+ }+ }++ trace2((qh, qh->ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",+ numold, numnew));+} /* mergecycle_ridges */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergecycle_vneighbors">-</a>++ qh_mergecycle_vneighbors(qh, samecycle, newfacet )+ create vertex neighbors for newfacet from vertices of facets in samecycle+ samecycle marked with visitid == qh.visit_id - 1++ returns:+ newfacet vertices with updated neighbors+ marks newfacet with qh.visit_id-1+ deletes vertices that are merged away+ sets delridge on all vertices (faster here than in mergecycle_ridges)++ see:+ qh_mergevertex_neighbors()++ design:+ for each vertex of samecycle facet+ set vertex->delridge+ delete samecycle facets from vertex neighbors+ append newfacet to vertex neighbors+ if vertex only in newfacet+ delete it from newfacet+ add it to qh.del_vertices for later deletion+*/+void qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {+ facetT *neighbor, **neighborp;+ unsigned int mergeid;+ vertexT *vertex, **vertexp, *apex;+ setT *vertices;++ trace4((qh, qh->ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));+ mergeid= qh->visit_id - 1;+ newfacet->visitid= mergeid;+ vertices= qh_basevertices(qh, samecycle); /* temp */+ apex= SETfirstt_(samecycle->vertices, vertexT);+ qh_setappend(qh, &vertices, apex);+ FOREACHvertex_(vertices) {+ vertex->delridge= True;+ FOREACHneighbor_(vertex) {+ if (neighbor->visitid == mergeid)+ SETref_(neighbor)= NULL;+ }+ qh_setcompact(qh, vertex->neighbors);+ qh_setappend(qh, &vertex->neighbors, newfacet);+ if (!SETsecond_(vertex->neighbors)) {+ zinc_(Zcyclevertex);+ trace2((qh, qh->ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",+ vertex->id, samecycle->id, newfacet->id));+ qh_setdelsorted(newfacet->vertices, vertex);+ vertex->deleted= True;+ qh_setappend(qh, &qh->del_vertices, vertex);+ }+ }+ qh_settempfree(qh, &vertices);+ trace3((qh, qh->ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",+ samecycle->id, newfacet->id));+} /* mergecycle_vneighbors */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergefacet">-</a>++ qh_mergefacet(qh, facet1, facet2, mindist, maxdist, mergeapex )+ merges facet1 into facet2+ mergeapex==qh_MERGEapex if merging new facet into coplanar horizon++ returns:+ qh.max_outside and qh.min_vertex updated+ initializes vertex neighbors on first merge++ returns:+ facet2 contains facet1's vertices, neighbors, and ridges+ facet2 moved to end of qh.facet_list+ makes facet2 a newfacet+ sets facet2->newmerge set+ clears facet2->center (unless merging into a large facet)+ clears facet2->tested and ridge->tested for facet1++ facet1 prepended to visible_list for later deletion and partitioning+ facet1->f.replace == facet2++ adds neighboring facets to facet_mergeset if redundant or degenerate++ notes:+ mindist/maxdist may be NULL (only if both NULL)+ traces merge if fmax_(maxdist,-mindist) > TRACEdist++ see:+ qh_mergecycle()++ design:+ trace merge and check for degenerate simplex+ make ridges for both facets+ update qh.max_outside, qh.max_vertex, qh.min_vertex+ update facet2->maxoutside and keepcentrum+ update facet2->nummerge+ update tested flags for facet2+ if facet1 is simplicial+ merge facet1 into facet2+ else+ merge facet1's neighbors into facet2+ merge facet1's ridges into facet2+ merge facet1's vertices into facet2+ merge facet1's vertex neighbors into facet2+ add facet2's vertices to qh.new_vertexlist+ unless qh_MERGEapex+ test facet2 for degenerate or redundant neighbors+ move facet1 to qh.visible_list for later deletion+ move facet2 to end of qh.newfacet_list+*/+void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) {+ boolT traceonce= False;+ vertexT *vertex, **vertexp;+ int tracerestore=0, nummerge;++ if (facet1->tricoplanar || facet2->tricoplanar) {+ if (!qh->TRInormals) {+ qh_fprintf(qh, qh->ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets. Use option 'Q11'\n");+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);+ }+ if (facet2->tricoplanar) {+ facet2->tricoplanar= False;+ facet2->keepcentrum= False;+ }+ }+ zzinc_(Ztotmerge);+ if (qh->REPORTfreq2 && qh->POSTmerging) {+ if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)+ qh_tracemerging(qh);+ }+#ifndef qh_NOtrace+ if (qh->build_cnt >= qh->RERUN) {+ if (mindist && (-*mindist > qh->TRACEdist || *maxdist > qh->TRACEdist)) {+ tracerestore= 0;+ qh->IStracing= qh->TRACElevel;+ traceonce= True;+ qh_fprintf(qh, qh->ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge),+ fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh->furthest_id);+ }else if (facet1 == qh->tracefacet || facet2 == qh->tracefacet) {+ tracerestore= qh->IStracing;+ qh->IStracing= 4;+ traceonce= True;+ qh_fprintf(qh, qh->ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n",+ zzval_(Ztotmerge), qh->tracefacet_id, qh->furthest_id);+ }+ }+ if (qh->IStracing >= 2) {+ realT mergemin= -2;+ realT mergemax= -2;++ if (mindist) {+ mergemin= *mindist;+ mergemax= *maxdist;+ }+ qh_fprintf(qh, qh->ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n",+ zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax);+ }+#endif /* !qh_NOtrace */+ if (facet1 == facet2 || facet1->visible || facet2->visible) {+ 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\n",+ facet1->id, facet2->id);+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);+ }+ if (qh->num_facets - qh->num_visible <= qh->hull_dim + 1) {+ qh_fprintf(qh, qh->ferr, 6227, "\n\+qhull precision error: Only %d facets remain. Can not merge another\n\+pair. The input is too degenerate or the convexity constraints are\n\+too strong.\n", qh->hull_dim+1);+ if (qh->hull_dim >= 5 && !qh->MERGEexact)+ qh_fprintf(qh, qh->ferr, 8079, "Option 'Qx' may avoid this problem.\n");+ qh_errexit(qh, qh_ERRprec, NULL, NULL);+ }+ if (!qh->VERTEXneighbors)+ qh_vertexneighbors(qh);+ qh_makeridges(qh, facet1);+ qh_makeridges(qh, facet2);+ if (qh->IStracing >=4)+ qh_errprint(qh, "MERGING", facet1, facet2, NULL, NULL);+ if (mindist) {+ maximize_(qh->max_outside, *maxdist);+ maximize_(qh->max_vertex, *maxdist);+#if qh_MAXoutside+ maximize_(facet2->maxoutside, *maxdist);+#endif+ minimize_(qh->min_vertex, *mindist);+ if (!facet2->keepcentrum+ && (*maxdist > qh->WIDEfacet || *mindist < -qh->WIDEfacet)) {+ facet2->keepcentrum= True;+ zinc_(Zwidefacet);+ }+ }+ nummerge= facet1->nummerge + facet2->nummerge + 1;+ if (nummerge >= qh_MAXnummerge)+ facet2->nummerge= qh_MAXnummerge;+ else+ facet2->nummerge= (short unsigned int)nummerge;+ facet2->newmerge= True;+ facet2->dupridge= False;+ qh_updatetested(qh, facet1, facet2);+ if (qh->hull_dim > 2 && qh_setsize(qh, facet1->vertices) == qh->hull_dim)+ qh_mergesimplex(qh, facet1, facet2, mergeapex);+ else {+ qh->vertex_visit++;+ FOREACHvertex_(facet2->vertices)+ vertex->visitid= qh->vertex_visit;+ if (qh->hull_dim == 2)+ qh_mergefacet2d(qh, facet1, facet2);+ else {+ qh_mergeneighbors(qh, facet1, facet2);+ qh_mergevertices(qh, facet1->vertices, &facet2->vertices);+ }+ qh_mergeridges(qh, facet1, facet2);+ qh_mergevertex_neighbors(qh, facet1, facet2);+ if (!facet2->newfacet)+ qh_newvertices(qh, facet2->vertices);+ }+ if (!mergeapex)+ qh_degen_redundant_neighbors(qh, facet2, facet1);+ if (facet2->coplanar || !facet2->newfacet) {+ zinc_(Zmergeintohorizon);+ }else if (!facet1->newfacet && facet2->newfacet) {+ zinc_(Zmergehorizon);+ }else {+ zinc_(Zmergenew);+ }+ qh_willdelete(qh, facet1, facet2);+ qh_removefacet(qh, facet2); /* append as a newfacet to end of qh->facet_list */+ qh_appendfacet(qh, facet2);+ facet2->newfacet= True;+ facet2->tested= False;+ qh_tracemerge(qh, facet1, facet2);+ if (traceonce) {+ qh_fprintf(qh, qh->ferr, 8080, "qh_mergefacet: end of wide tracing\n");+ qh->IStracing= tracerestore;+ }+} /* mergefacet */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergefacet2d">-</a>++ qh_mergefacet2d(qh, facet1, facet2 )+ in 2d, merges neighbors and vertices of facet1 into facet2++ returns:+ build ridges for neighbors if necessary+ facet2 looks like a simplicial facet except for centrum, ridges+ neighbors are opposite the corresponding vertex+ maintains orientation of facet2++ notes:+ qh_mergefacet() retains non-simplicial structures+ they are not needed in 2d, but later routines may use them+ preserves qh.vertex_visit for qh_mergevertex_neighbors()++ design:+ get vertices and neighbors+ determine new vertices and neighbors+ set new vertices and neighbors and adjust orientation+ make ridges for new neighbor if needed+*/+void qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2) {+ vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;+ facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;++ vertex1A= SETfirstt_(facet1->vertices, vertexT);+ vertex1B= SETsecondt_(facet1->vertices, vertexT);+ vertex2A= SETfirstt_(facet2->vertices, vertexT);+ vertex2B= SETsecondt_(facet2->vertices, vertexT);+ neighbor1A= SETfirstt_(facet1->neighbors, facetT);+ neighbor1B= SETsecondt_(facet1->neighbors, facetT);+ neighbor2A= SETfirstt_(facet2->neighbors, facetT);+ neighbor2B= SETsecondt_(facet2->neighbors, facetT);+ if (vertex1A == vertex2A) {+ vertexA= vertex1B;+ vertexB= vertex2B;+ neighborA= neighbor2A;+ neighborB= neighbor1A;+ }else if (vertex1A == vertex2B) {+ vertexA= vertex1B;+ vertexB= vertex2A;+ neighborA= neighbor2B;+ neighborB= neighbor1A;+ }else if (vertex1B == vertex2A) {+ vertexA= vertex1A;+ vertexB= vertex2B;+ neighborA= neighbor2A;+ neighborB= neighbor1B;+ }else { /* 1B == 2B */+ vertexA= vertex1A;+ vertexB= vertex2A;+ neighborA= neighbor2B;+ neighborB= neighbor1B;+ }+ /* vertexB always from facet2, neighborB always from facet1 */+ if (vertexA->id > vertexB->id) {+ SETfirst_(facet2->vertices)= vertexA;+ SETsecond_(facet2->vertices)= vertexB;+ if (vertexB == vertex2A)+ facet2->toporient= !facet2->toporient;+ SETfirst_(facet2->neighbors)= neighborA;+ SETsecond_(facet2->neighbors)= neighborB;+ }else {+ SETfirst_(facet2->vertices)= vertexB;+ SETsecond_(facet2->vertices)= vertexA;+ if (vertexB == vertex2B)+ facet2->toporient= !facet2->toporient;+ SETfirst_(facet2->neighbors)= neighborB;+ SETsecond_(facet2->neighbors)= neighborA;+ }+ qh_makeridges(qh, neighborB);+ qh_setreplace(qh, neighborB->neighbors, facet1, facet2);+ trace4((qh, qh->ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",+ vertexA->id, neighborB->id, facet1->id, facet2->id));+} /* mergefacet2d */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergeneighbors">-</a>++ qh_mergeneighbors(qh, facet1, facet2 )+ merges the neighbors of facet1 into facet2++ see:+ qh_mergecycle_neighbors()++ design:+ for each neighbor of facet1+ if neighbor is also a neighbor of facet2+ if neighbor is simpilicial+ make ridges for later deletion as a degenerate facet+ update its neighbor set+ else+ move the neighbor relation to facet2+ remove the neighbor relation for facet1 and facet2+*/+void qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2) {+ facetT *neighbor, **neighborp;++ trace4((qh, qh->ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",+ facet1->id, facet2->id));+ qh->visit_id++;+ FOREACHneighbor_(facet2) {+ neighbor->visitid= qh->visit_id;+ }+ FOREACHneighbor_(facet1) {+ if (neighbor->visitid == qh->visit_id) {+ if (neighbor->simplicial) /* is degen, needs ridges */+ qh_makeridges(qh, neighbor);+ if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/+ qh_setdel(neighbor->neighbors, facet1);+ else {+ qh_setdel(neighbor->neighbors, facet2);+ qh_setreplace(qh, neighbor->neighbors, facet1, facet2);+ }+ }else if (neighbor != facet2) {+ qh_setappend(qh, &(facet2->neighbors), neighbor);+ qh_setreplace(qh, neighbor->neighbors, facet1, facet2);+ }+ }+ qh_setdel(facet1->neighbors, facet2); /* here for makeridges */+ qh_setdel(facet2->neighbors, facet1);+} /* mergeneighbors */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergeridges">-</a>++ qh_mergeridges(qh, facet1, facet2 )+ merges the ridge set of facet1 into facet2++ returns:+ may delete all ridges for a vertex+ sets vertex->delridge on deleted ridges++ see:+ qh_mergecycle_ridges()++ design:+ delete ridges between facet1 and facet2+ mark (delridge) vertices on these ridges for later testing+ for each remaining ridge+ rename facet1 to facet2+*/+void qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2) {+ ridgeT *ridge, **ridgep;+ vertexT *vertex, **vertexp;++ trace4((qh, qh->ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n",+ facet1->id, facet2->id));+ FOREACHridge_(facet2->ridges) {+ if ((ridge->top == facet1) || (ridge->bottom == facet1)) {+ FOREACHvertex_(ridge->vertices)+ vertex->delridge= True;+ qh_delridge(qh, ridge); /* expensive in high-d, could rebuild */+ ridgep--; /*repeat*/+ }+ }+ FOREACHridge_(facet1->ridges) {+ if (ridge->top == facet1)+ ridge->top= facet2;+ else+ ridge->bottom= facet2;+ qh_setappend(qh, &(facet2->ridges), ridge);+ }+} /* mergeridges */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergesimplex">-</a>++ qh_mergesimplex(qh, facet1, facet2, mergeapex )+ merge simplicial facet1 into facet2+ mergeapex==qh_MERGEapex if merging samecycle into horizon facet+ vertex id is latest (most recently created)+ facet1 may be contained in facet2+ ridges exist for both facets++ returns:+ facet2 with updated vertices, ridges, neighbors+ updated neighbors for facet1's vertices+ facet1 not deleted+ sets vertex->delridge on deleted ridges++ notes:+ special case code since this is the most common merge+ called from qh_mergefacet()++ design:+ if qh_MERGEapex+ add vertices of facet2 to qh.new_vertexlist if necessary+ add apex to facet2+ else+ for each ridge between facet1 and facet2+ set vertex->delridge+ determine the apex for facet1 (i.e., vertex to be merged)+ unless apex already in facet2+ insert apex into vertices for facet2+ add vertices of facet2 to qh.new_vertexlist if necessary+ add apex to qh.new_vertexlist if necessary+ for each vertex of facet1+ if apex+ rename facet1 to facet2 in its vertex neighbors+ else+ delete facet1 from vertex neighors+ if only in facet2+ add vertex to qh.del_vertices for later deletion+ for each ridge of facet1+ delete ridges between facet1 and facet2+ append other ridges to facet2 after renaming facet to facet2+*/+void qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex) {+ vertexT *vertex, **vertexp, *apex;+ ridgeT *ridge, **ridgep;+ boolT issubset= False;+ int vertex_i= -1, vertex_n;+ facetT *neighbor, **neighborp, *otherfacet;++ if (mergeapex) {+ if (!facet2->newfacet)+ qh_newvertices(qh, facet2->vertices); /* apex is new */+ apex= SETfirstt_(facet1->vertices, vertexT);+ if (SETfirstt_(facet2->vertices, vertexT) != apex)+ qh_setaddnth(qh, &facet2->vertices, 0, apex); /* apex has last id */+ else+ issubset= True;+ }else {+ zinc_(Zmergesimplex);+ FOREACHvertex_(facet1->vertices)+ vertex->seen= False;+ FOREACHridge_(facet1->ridges) {+ if (otherfacet_(ridge, facet1) == facet2) {+ FOREACHvertex_(ridge->vertices) {+ vertex->seen= True;+ vertex->delridge= True;+ }+ break;+ }+ }+ FOREACHvertex_(facet1->vertices) {+ if (!vertex->seen)+ break; /* must occur */+ }+ apex= vertex;+ trace4((qh, qh->ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",+ apex->id, facet1->id, facet2->id));+ FOREACHvertex_i_(qh, facet2->vertices) {+ if (vertex->id < apex->id) {+ break;+ }else if (vertex->id == apex->id) {+ issubset= True;+ break;+ }+ }+ if (!issubset)+ qh_setaddnth(qh, &facet2->vertices, vertex_i, apex);+ if (!facet2->newfacet)+ qh_newvertices(qh, facet2->vertices);+ else if (!apex->newlist) {+ qh_removevertex(qh, apex);+ qh_appendvertex(qh, apex);+ }+ }+ trace4((qh, qh->ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",+ facet1->id));+ FOREACHvertex_(facet1->vertices) {+ if (vertex == apex && !issubset)+ qh_setreplace(qh, vertex->neighbors, facet1, facet2);+ else {+ qh_setdel(vertex->neighbors, facet1);+ if (!SETsecond_(vertex->neighbors))+ qh_mergevertex_del(qh, vertex, facet1, facet2);+ }+ }+ trace4((qh, qh->ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",+ facet1->id, facet2->id));+ qh->visit_id++;+ FOREACHneighbor_(facet2)+ neighbor->visitid= qh->visit_id;+ FOREACHridge_(facet1->ridges) {+ otherfacet= otherfacet_(ridge, facet1);+ if (otherfacet == facet2) {+ qh_setdel(facet2->ridges, ridge);+ qh_setfree(qh, &(ridge->vertices));+ qh_memfree(qh, ridge, (int)sizeof(ridgeT));+ qh_setdel(facet2->neighbors, facet1);+ }else {+ qh_setappend(qh, &facet2->ridges, ridge);+ if (otherfacet->visitid != qh->visit_id) {+ qh_setappend(qh, &facet2->neighbors, otherfacet);+ qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);+ otherfacet->visitid= qh->visit_id;+ }else {+ if (otherfacet->simplicial) /* is degen, needs ridges */+ qh_makeridges(qh, otherfacet);+ if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)+ qh_setdel(otherfacet->neighbors, facet1);+ else { /*keep newfacet->neighbors->horizon*/+ qh_setdel(otherfacet->neighbors, facet2);+ qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);+ }+ }+ if (ridge->top == facet1) /* wait until after qh_makeridges */+ ridge->top= facet2;+ else+ ridge->bottom= facet2;+ }+ }+ SETfirst_(facet1->ridges)= NULL; /* it will be deleted */+ trace3((qh, qh->ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",+ facet1->id, getid_(apex), facet2->id));+} /* mergesimplex */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergevertex_del">-</a>++ qh_mergevertex_del(qh, vertex, facet1, facet2 )+ delete a vertex because of merging facet1 into facet2++ returns:+ deletes vertex from facet2+ adds vertex to qh.del_vertices for later deletion+*/+void qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2) {++ zinc_(Zmergevertex);+ trace2((qh, qh->ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",+ vertex->id, facet1->id, facet2->id));+ qh_setdelsorted(facet2->vertices, vertex);+ vertex->deleted= True;+ qh_setappend(qh, &qh->del_vertices, vertex);+} /* mergevertex_del */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergevertex_neighbors">-</a>++ qh_mergevertex_neighbors(qh, facet1, facet2 )+ merge the vertex neighbors of facet1 to facet2++ returns:+ if vertex is current qh.vertex_visit+ deletes facet1 from vertex->neighbors+ else+ renames facet1 to facet2 in vertex->neighbors+ deletes vertices if only one neighbor++ notes:+ assumes vertex neighbor sets are good+*/+void qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2) {+ vertexT *vertex, **vertexp;++ trace4((qh, qh->ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n",+ facet1->id, facet2->id));+ if (qh->tracevertex) {+ qh_fprintf(qh, qh->ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n",+ facet1->id, facet2->id, qh->furthest_id, qh->tracevertex->neighbors->e[0].p);+ qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);+ }+ FOREACHvertex_(facet1->vertices) {+ if (vertex->visitid != qh->vertex_visit)+ qh_setreplace(qh, vertex->neighbors, facet1, facet2);+ else {+ qh_setdel(vertex->neighbors, facet1);+ if (!SETsecond_(vertex->neighbors))+ qh_mergevertex_del(qh, vertex, facet1, facet2);+ }+ }+ if (qh->tracevertex)+ qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);+} /* mergevertex_neighbors */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="mergevertices">-</a>++ qh_mergevertices(qh, vertices1, vertices2 )+ merges the vertex set of facet1 into facet2++ returns:+ replaces vertices2 with merged set+ preserves vertex_visit for qh_mergevertex_neighbors+ updates qh.newvertex_list++ design:+ create a merged set of both vertices (in inverse id order)+*/+void qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices2) {+ int newsize= qh_setsize(qh, vertices1)+qh_setsize(qh, *vertices2) - qh->hull_dim + 1;+ setT *mergedvertices;+ vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);++ mergedvertices= qh_settemp(qh, newsize);+ FOREACHvertex_(vertices1) {+ if (!*vertex2 || vertex->id > (*vertex2)->id)+ qh_setappend(qh, &mergedvertices, vertex);+ else {+ while (*vertex2 && (*vertex2)->id > vertex->id)+ qh_setappend(qh, &mergedvertices, *vertex2++);+ if (!*vertex2 || (*vertex2)->id < vertex->id)+ qh_setappend(qh, &mergedvertices, vertex);+ else+ qh_setappend(qh, &mergedvertices, *vertex2++);+ }+ }+ while (*vertex2)+ qh_setappend(qh, &mergedvertices, *vertex2++);+ if (newsize < qh_setsize(qh, mergedvertices)) {+ qh_fprintf(qh, qh->ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ qh_setfree(qh, vertices2);+ *vertices2= mergedvertices;+ qh_settemppop(qh);+} /* mergevertices */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="neighbor_intersections">-</a>++ qh_neighbor_intersections(qh, vertex )+ return intersection of all vertices in vertex->neighbors except for vertex++ returns:+ returns temporary set of vertices+ does not include vertex+ NULL if a neighbor is simplicial+ NULL if empty set++ notes:+ used for renaming vertices++ design:+ initialize the intersection set with vertices of the first two neighbors+ delete vertex from the intersection+ for each remaining neighbor+ intersect its vertex set with the intersection set+ return NULL if empty+ return the intersection set+*/+setT *qh_neighbor_intersections(qhT *qh, vertexT *vertex) {+ facetT *neighbor, **neighborp, *neighborA, *neighborB;+ setT *intersect;+ int neighbor_i, neighbor_n;++ FOREACHneighbor_(vertex) {+ if (neighbor->simplicial)+ return NULL;+ }+ neighborA= SETfirstt_(vertex->neighbors, facetT);+ neighborB= SETsecondt_(vertex->neighbors, facetT);+ zinc_(Zintersectnum);+ if (!neighborA)+ return NULL;+ if (!neighborB)+ intersect= qh_setcopy(qh, neighborA->vertices, 0);+ else+ intersect= qh_vertexintersect_new(qh, neighborA->vertices, neighborB->vertices);+ qh_settemppush(qh, intersect);+ qh_setdelsorted(intersect, vertex);+ FOREACHneighbor_i_(qh, vertex) {+ if (neighbor_i >= 2) {+ zinc_(Zintersectnum);+ qh_vertexintersect(qh, &intersect, neighbor->vertices);+ if (!SETfirst_(intersect)) {+ zinc_(Zintersectfail);+ qh_settempfree(qh, &intersect);+ return NULL;+ }+ }+ }+ trace3((qh, qh->ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",+ qh_setsize(qh, intersect), vertex->id));+ return intersect;+} /* neighbor_intersections */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="newvertices">-</a>++ qh_newvertices(qh, vertices )+ add vertices to end of qh.vertex_list (marks as new vertices)++ returns:+ vertices on qh.newvertex_list+ vertex->newlist set+*/+void qh_newvertices(qhT *qh, setT *vertices) {+ vertexT *vertex, **vertexp;++ FOREACHvertex_(vertices) {+ if (!vertex->newlist) {+ qh_removevertex(qh, vertex);+ qh_appendvertex(qh, vertex);+ }+ }+} /* newvertices */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="reducevertices">-</a>++ qh_reducevertices(qh)+ reduce extra vertices, shared vertices, and redundant vertices+ facet->newmerge is set if merged since last call+ if !qh.MERGEvertices, only removes extra vertices++ returns:+ True if also merged degen_redundant facets+ vertices are renamed if possible+ clears facet->newmerge and vertex->delridge++ notes:+ ignored if 2-d++ design:+ merge any degenerate or redundant facets+ for each newly merged facet+ remove extra vertices+ if qh.MERGEvertices+ for each newly merged facet+ for each vertex+ if vertex was on a deleted ridge+ rename vertex if it is shared+ remove delridge flag from new vertices+*/+boolT qh_reducevertices(qhT *qh) {+ int numshare=0, numrename= 0;+ boolT degenredun= False;+ facetT *newfacet;+ vertexT *vertex, **vertexp;++ if (qh->hull_dim == 2)+ return False;+ if (qh_merge_degenredundant(qh))+ degenredun= True;+ LABELrestart:+ FORALLnew_facets {+ if (newfacet->newmerge) {+ if (!qh->MERGEvertices)+ newfacet->newmerge= False;+ qh_remove_extravertices(qh, newfacet);+ }+ }+ if (!qh->MERGEvertices)+ return False;+ FORALLnew_facets {+ if (newfacet->newmerge) {+ newfacet->newmerge= False;+ FOREACHvertex_(newfacet->vertices) {+ if (vertex->delridge) {+ if (qh_rename_sharedvertex(qh, vertex, newfacet)) {+ numshare++;+ vertexp--; /* repeat since deleted vertex */+ }+ }+ }+ }+ }+ FORALLvertex_(qh->newvertex_list) {+ if (vertex->delridge && !vertex->deleted) {+ vertex->delridge= False;+ if (qh->hull_dim >= 4 && qh_redundant_vertex(qh, vertex)) {+ numrename++;+ if (qh_merge_degenredundant(qh)) {+ degenredun= True;+ goto LABELrestart;+ }+ }+ }+ }+ trace1((qh, qh->ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",+ numshare, numrename, degenredun));+ return degenredun;+} /* reducevertices */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="redundant_vertex">-</a>++ qh_redundant_vertex(qh, vertex )+ detect and rename a redundant vertex+ vertices have full vertex->neighbors++ returns:+ returns true if find a redundant vertex+ deletes vertex(vertex->deleted)++ notes:+ only needed if vertex->delridge and hull_dim >= 4+ may add degenerate facets to qh.facet_mergeset+ doesn't change vertex->neighbors or create redundant facets++ design:+ intersect vertices of all facet neighbors of vertex+ determine ridges for these vertices+ if find a new vertex for vertex amoung these ridges and vertices+ rename vertex to the new vertex+*/+vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex) {+ vertexT *newvertex= NULL;+ setT *vertices, *ridges;++ trace3((qh, qh->ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));+ if ((vertices= qh_neighbor_intersections(qh, vertex))) {+ ridges= qh_vertexridges(qh, vertex);+ if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))+ qh_renamevertex(qh, vertex, newvertex, ridges, NULL, NULL);+ qh_settempfree(qh, &ridges);+ qh_settempfree(qh, &vertices);+ }+ return newvertex;+} /* redundant_vertex */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="remove_extravertices">-</a>++ qh_remove_extravertices(qh, facet )+ remove extra vertices from non-simplicial facets++ returns:+ returns True if it finds them++ design:+ for each vertex in facet+ if vertex not in a ridge (i.e., no longer used)+ delete vertex from facet+ delete facet from vertice's neighbors+ unless vertex in another facet+ add vertex to qh.del_vertices for later deletion+*/+boolT qh_remove_extravertices(qhT *qh, facetT *facet) {+ ridgeT *ridge, **ridgep;+ vertexT *vertex, **vertexp;+ boolT foundrem= False;++ trace4((qh, qh->ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n",+ facet->id));+ FOREACHvertex_(facet->vertices)+ vertex->seen= False;+ FOREACHridge_(facet->ridges) {+ FOREACHvertex_(ridge->vertices)+ vertex->seen= True;+ }+ FOREACHvertex_(facet->vertices) {+ if (!vertex->seen) {+ foundrem= True;+ zinc_(Zremvertex);+ qh_setdelsorted(facet->vertices, vertex);+ qh_setdel(vertex->neighbors, facet);+ if (!qh_setsize(qh, vertex->neighbors)) {+ vertex->deleted= True;+ qh_setappend(qh, &qh->del_vertices, vertex);+ zinc_(Zremvertexdel);+ trace2((qh, qh->ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));+ }else+ 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));+ vertexp--; /*repeat*/+ }+ }+ return foundrem;+} /* remove_extravertices */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="rename_sharedvertex">-</a>++ qh_rename_sharedvertex(qh, vertex, facet )+ detect and rename if shared vertex in facet+ vertices have full ->neighbors++ returns:+ newvertex or NULL+ the vertex may still exist in other facets (i.e., a neighbor was pinched)+ does not change facet->neighbors+ updates vertex->neighbors++ notes:+ a shared vertex for a facet is only in ridges to one neighbor+ this may undo a pinched facet++ it does not catch pinches involving multiple facets. These appear+ to be difficult to detect, since an exhaustive search is too expensive.++ design:+ if vertex only has two neighbors+ determine the ridges that contain the vertex+ determine the vertices shared by both neighbors+ if can find a new vertex in this set+ rename the vertex to the new vertex+*/+vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet) {+ facetT *neighbor, **neighborp, *neighborA= NULL;+ setT *vertices, *ridges;+ vertexT *newvertex;++ if (qh_setsize(qh, vertex->neighbors) == 2) {+ neighborA= SETfirstt_(vertex->neighbors, facetT);+ if (neighborA == facet)+ neighborA= SETsecondt_(vertex->neighbors, facetT);+ }else if (qh->hull_dim == 3)+ return NULL;+ else {+ qh->visit_id++;+ FOREACHneighbor_(facet)+ neighbor->visitid= qh->visit_id;+ FOREACHneighbor_(vertex) {+ if (neighbor->visitid == qh->visit_id) {+ if (neighborA)+ return NULL;+ neighborA= neighbor;+ }+ }+ if (!neighborA) {+ qh_fprintf(qh, qh->ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",+ vertex->id, facet->id);+ qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, vertex);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ }+ /* the vertex is shared by facet and neighborA */+ ridges= qh_settemp(qh, qh->TEMPsize);+ neighborA->visitid= ++qh->visit_id;+ qh_vertexridges_facet(qh, vertex, facet, &ridges);+ trace2((qh, qh->ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",+ qh_pointid(qh, vertex->point), vertex->id, facet->id, qh_setsize(qh, ridges), neighborA->id));+ zinc_(Zintersectnum);+ vertices= qh_vertexintersect_new(qh, facet->vertices, neighborA->vertices);+ qh_setdel(vertices, vertex);+ qh_settemppush(qh, vertices);+ if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))+ qh_renamevertex(qh, vertex, newvertex, ridges, facet, neighborA);+ qh_settempfree(qh, &vertices);+ qh_settempfree(qh, &ridges);+ return newvertex;+} /* rename_sharedvertex */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="renameridgevertex">-</a>++ qh_renameridgevertex(qh, ridge, oldvertex, newvertex )+ renames oldvertex as newvertex in ridge++ returns:++ design:+ delete oldvertex from ridge+ if newvertex already in ridge+ copy ridge->noconvex to another ridge if possible+ delete the ridge+ else+ insert newvertex into the ridge+ adjust the ridge's orientation+*/+void qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {+ int nth= 0, oldnth;+ facetT *temp;+ vertexT *vertex, **vertexp;++ oldnth= qh_setindex(ridge->vertices, oldvertex);+ qh_setdelnthsorted(qh, ridge->vertices, oldnth);+ FOREACHvertex_(ridge->vertices) {+ if (vertex == newvertex) {+ zinc_(Zdelridge);+ if (ridge->nonconvex) /* only one ridge has nonconvex set */+ qh_copynonconvex(qh, ridge);+ trace2((qh, qh->ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n",+ ridge->id, oldvertex->id, newvertex->id));+ qh_delridge(qh, ridge);+ return;+ }+ if (vertex->id < newvertex->id)+ break;+ nth++;+ }+ qh_setaddnth(qh, &ridge->vertices, nth, newvertex);+ if (abs(oldnth - nth)%2) {+ trace3((qh, qh->ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",+ ridge->id));+ temp= ridge->top;+ ridge->top= ridge->bottom;+ ridge->bottom= temp;+ }+} /* renameridgevertex */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="renamevertex">-</a>++ qh_renamevertex(qh, oldvertex, newvertex, ridges, oldfacet, neighborA )+ renames oldvertex as newvertex in ridges+ gives oldfacet/neighborA if oldvertex is shared between two facets++ returns:+ oldvertex may still exist afterwards+++ notes:+ can not change neighbors of newvertex (since it's a subset)++ design:+ for each ridge in ridges+ rename oldvertex to newvertex and delete degenerate ridges+ if oldfacet not defined+ for each neighbor of oldvertex+ delete oldvertex from neighbor's vertices+ remove extra vertices from neighbor+ add oldvertex to qh.del_vertices+ else if oldvertex only between oldfacet and neighborA+ delete oldvertex from oldfacet and neighborA+ add oldvertex to qh.del_vertices+ else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)+ delete oldvertex from oldfacet+ delete oldfacet from oldvertice's neighbors+ remove extra vertices (e.g., oldvertex) from neighborA+*/+void qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {+ facetT *neighbor, **neighborp;+ ridgeT *ridge, **ridgep;+ boolT istrace= False;++ if (qh->IStracing >= 2 || oldvertex->id == qh->tracevertex_id ||+ newvertex->id == qh->tracevertex_id)+ istrace= True;+ FOREACHridge_(ridges)+ qh_renameridgevertex(qh, ridge, oldvertex, newvertex);+ if (!oldfacet) {+ zinc_(Zrenameall);+ if (istrace)+ qh_fprintf(qh, qh->ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n",+ oldvertex->id, newvertex->id);+ FOREACHneighbor_(oldvertex) {+ qh_maydropneighbor(qh, neighbor);+ qh_setdelsorted(neighbor->vertices, oldvertex);+ if (qh_remove_extravertices(qh, neighbor))+ neighborp--; /* neighbor may be deleted */+ }+ if (!oldvertex->deleted) {+ oldvertex->deleted= True;+ qh_setappend(qh, &qh->del_vertices, oldvertex);+ }+ }else if (qh_setsize(qh, oldvertex->neighbors) == 2) {+ zinc_(Zrenameshare);+ if (istrace)+ qh_fprintf(qh, qh->ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n",+ oldvertex->id, newvertex->id, oldfacet->id);+ FOREACHneighbor_(oldvertex)+ qh_setdelsorted(neighbor->vertices, oldvertex);+ oldvertex->deleted= True;+ qh_setappend(qh, &qh->del_vertices, oldvertex);+ }else {+ zinc_(Zrenamepinch);+ if (istrace || qh->IStracing)+ qh_fprintf(qh, qh->ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n",+ oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);+ qh_setdelsorted(oldfacet->vertices, oldvertex);+ qh_setdel(oldvertex->neighbors, oldfacet);+ qh_remove_extravertices(qh, neighborA);+ }+} /* renamevertex */+++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="test_appendmerge">-</a>++ qh_test_appendmerge(qh, facet, neighbor )+ tests facet/neighbor for convexity+ appends to mergeset if non-convex+ if pre-merging,+ nop if qh.SKIPconvex, or qh.MERGEexact and coplanar++ returns:+ true if appends facet/neighbor to mergeset+ sets facet->center as needed+ does not change facet->seen++ design:+ if qh.cos_max is defined+ if the angle between facet normals is too shallow+ append an angle-coplanar merge to qh.mergeset+ return True+ make facet's centrum if needed+ if facet's centrum is above the neighbor+ set isconcave+ else+ if facet's centrum is not below the neighbor+ set iscoplanar+ make neighbor's centrum if needed+ if neighbor's centrum is above the facet+ set isconcave+ else if neighbor's centrum is not below the facet+ set iscoplanar+ if isconcave or iscoplanar+ get angle if needed+ append concave or coplanar merge to qh.mergeset+*/+boolT qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor) {+ realT dist, dist2= -REALmax, angle= -REALmax;+ boolT isconcave= False, iscoplanar= False, okangle= False;++ if (qh->SKIPconvex && !qh->POSTmerging)+ return False;+ if ((!qh->MERGEexact || qh->POSTmerging) && qh->cos_max < REALmax/2) {+ angle= qh_getangle(qh, facet->normal, neighbor->normal);+ zinc_(Zangletests);+ if (angle > qh->cos_max) {+ zinc_(Zcoplanarangle);+ qh_appendmergeset(qh, facet, neighbor, MRGanglecoplanar, &angle);+ trace2((qh, qh->ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",+ angle, facet->id, neighbor->id));+ return True;+ }else+ okangle= True;+ }+ if (!facet->center)+ facet->center= qh_getcentrum(qh, facet);+ zzinc_(Zcentrumtests);+ qh_distplane(qh, facet->center, neighbor, &dist);+ if (dist > qh->centrum_radius)+ isconcave= True;+ else {+ if (dist > -qh->centrum_radius)+ iscoplanar= True;+ if (!neighbor->center)+ neighbor->center= qh_getcentrum(qh, neighbor);+ zzinc_(Zcentrumtests);+ qh_distplane(qh, neighbor->center, facet, &dist2);+ if (dist2 > qh->centrum_radius)+ isconcave= True;+ else if (!iscoplanar && dist2 > -qh->centrum_radius)+ iscoplanar= True;+ }+ if (!isconcave && (!iscoplanar || (qh->MERGEexact && !qh->POSTmerging)))+ return False;+ if (!okangle && qh->ANGLEmerge) {+ angle= qh_getangle(qh, facet->normal, neighbor->normal);+ zinc_(Zangletests);+ }+ if (isconcave) {+ zinc_(Zconcaveridge);+ if (qh->ANGLEmerge)+ angle += qh_ANGLEconcave + 0.5;+ qh_appendmergeset(qh, facet, neighbor, MRGconcave, &angle);+ trace0((qh, qh->ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n",+ facet->id, neighbor->id, dist, dist2, angle, qh->furthest_id));+ }else /* iscoplanar */ {+ zinc_(Zcoplanarcentrum);+ qh_appendmergeset(qh, facet, neighbor, MRGcoplanar, &angle);+ trace2((qh, qh->ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",+ facet->id, neighbor->id, dist, dist2, angle));+ }+ return True;+} /* test_appendmerge */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="test_vneighbors">-</a>++ qh_test_vneighbors(qh)+ test vertex neighbors for convexity+ tests all facets on qh.newfacet_list++ returns:+ true if non-convex vneighbors appended to qh.facet_mergeset+ initializes vertex neighbors if needed++ notes:+ assumes all facet neighbors have been tested+ this can be expensive+ this does not guarantee that a centrum is below all facets+ but it is unlikely+ uses qh.visit_id++ design:+ build vertex neighbors if necessary+ for all new facets+ for all vertices+ for each unvisited facet neighbor of the vertex+ test new facet and neighbor for convexity+*/+boolT qh_test_vneighbors(qhT *qh /* qh->newfacet_list */) {+ facetT *newfacet, *neighbor, **neighborp;+ vertexT *vertex, **vertexp;+ int nummerges= 0;++ trace1((qh, qh->ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));+ if (!qh->VERTEXneighbors)+ qh_vertexneighbors(qh);+ FORALLnew_facets+ newfacet->seen= False;+ FORALLnew_facets {+ newfacet->seen= True;+ newfacet->visitid= qh->visit_id++;+ FOREACHneighbor_(newfacet)+ newfacet->visitid= qh->visit_id;+ FOREACHvertex_(newfacet->vertices) {+ FOREACHneighbor_(vertex) {+ if (neighbor->seen || neighbor->visitid == qh->visit_id)+ continue;+ if (qh_test_appendmerge(qh, newfacet, neighbor))+ nummerges++;+ }+ }+ }+ zadd_(Ztestvneighbor, nummerges);+ trace1((qh, qh->ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",+ nummerges));+ return (nummerges > 0);+} /* test_vneighbors */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="tracemerge">-</a>++ qh_tracemerge(qh, facet1, facet2 )+ print trace message after merge+*/+void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2) {+ boolT waserror= False;++#ifndef qh_NOtrace+ if (qh->IStracing >= 4)+ qh_errprint(qh, "MERGED", facet2, NULL, NULL, NULL);+ if (facet2 == qh->tracefacet || (qh->tracevertex && qh->tracevertex->newlist)) {+ qh_fprintf(qh, qh->ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh->furthest_id);+ if (facet2 != qh->tracefacet)+ qh_errprint(qh, "TRACE", qh->tracefacet,+ (qh->tracevertex && qh->tracevertex->neighbors) ?+ SETfirstt_(qh->tracevertex->neighbors, facetT) : NULL,+ NULL, qh->tracevertex);+ }+ if (qh->tracevertex) {+ if (qh->tracevertex->deleted)+ qh_fprintf(qh, qh->ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",+ qh->furthest_id);+ else+ qh_checkvertex(qh, qh->tracevertex);+ }+ if (qh->tracefacet) {+ qh_checkfacet(qh, qh->tracefacet, True, &waserror);+ if (waserror)+ qh_errexit(qh, qh_ERRqhull, qh->tracefacet, NULL);+ }+#endif /* !qh_NOtrace */+ if (qh->CHECKfrequently || qh->IStracing >= 4) { /* can't check polygon here */+ qh_checkfacet(qh, facet2, True, &waserror);+ if (waserror)+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+} /* tracemerge */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="tracemerging">-</a>++ qh_tracemerging(qh)+ print trace message during POSTmerging++ returns:+ updates qh.mergereport++ notes:+ called from qh_mergecycle() and qh_mergefacet()++ see:+ qh_buildtracing()+*/+void qh_tracemerging(qhT *qh) {+ realT cpu;+ int total;+ time_t timedata;+ struct tm *tp;++ qh->mergereport= zzval_(Ztotmerge);+ time(&timedata);+ tp= localtime(&timedata);+ cpu= qh_CPUclock;+ cpu /= qh_SECticks;+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);+ qh_fprintf(qh, qh->ferr, 8087, "\n\+At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\+ contains %d facets and %d vertices.\n",+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu,+ total, qh->num_facets - qh->num_visible,+ qh->num_vertices-qh_setsize(qh, qh->del_vertices));+} /* tracemerging */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="updatetested">-</a>++ qh_updatetested(qh, facet1, facet2 )+ clear facet2->tested and facet1->ridge->tested for merge++ returns:+ deletes facet2->center unless it's already large+ if so, clears facet2->ridge->tested++ design:+ clear facet2->tested+ clear ridge->tested for facet1's ridges+ if facet2 has a centrum+ if facet2 is large+ set facet2->keepcentrum+ else if facet2 has 3 vertices due to many merges, or not large and post merging+ clear facet2->keepcentrum+ unless facet2->keepcentrum+ clear facet2->center to recompute centrum later+ clear ridge->tested for facet2's ridges+*/+void qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2) {+ ridgeT *ridge, **ridgep;+ int size;++ facet2->tested= False;+ FOREACHridge_(facet1->ridges)+ ridge->tested= False;+ if (!facet2->center)+ return;+ size= qh_setsize(qh, facet2->vertices);+ if (!facet2->keepcentrum) {+ if (size > qh->hull_dim + qh_MAXnewcentrum) {+ facet2->keepcentrum= True;+ zinc_(Zwidevertices);+ }+ }else if (size <= qh->hull_dim + qh_MAXnewcentrum) {+ /* center and keepcentrum was set */+ if (size == qh->hull_dim || qh->POSTmerging)+ facet2->keepcentrum= False; /* if many merges need to recompute centrum */+ }+ if (!facet2->keepcentrum) {+ qh_memfree(qh, facet2->center, qh->normal_size);+ facet2->center= NULL;+ FOREACHridge_(facet2->ridges)+ ridge->tested= False;+ }+} /* updatetested */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="vertexridges">-</a>++ qh_vertexridges(qh, vertex )+ return temporary set of ridges adjacent to a vertex+ vertex->neighbors defined++ ntoes:+ uses qh.visit_id+ does not include implicit ridges for simplicial facets++ design:+ for each neighbor of vertex+ add ridges that include the vertex to ridges+*/+setT *qh_vertexridges(qhT *qh, vertexT *vertex) {+ facetT *neighbor, **neighborp;+ setT *ridges= qh_settemp(qh, qh->TEMPsize);+ int size;++ qh->visit_id++;+ FOREACHneighbor_(vertex)+ neighbor->visitid= qh->visit_id;+ FOREACHneighbor_(vertex) {+ if (*neighborp) /* no new ridges in last neighbor */+ qh_vertexridges_facet(qh, vertex, neighbor, &ridges);+ }+ if (qh->PRINTstatistics || qh->IStracing) {+ size= qh_setsize(qh, ridges);+ zinc_(Zvertexridge);+ zadd_(Zvertexridgetot, size);+ zmax_(Zvertexridgemax, size);+ trace3((qh, qh->ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",+ size, vertex->id));+ }+ return ridges;+} /* vertexridges */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="vertexridges_facet">-</a>++ qh_vertexridges_facet(qh, vertex, facet, ridges )+ add adjacent ridges for vertex in facet+ neighbor->visitid==qh.visit_id if it hasn't been visited++ returns:+ ridges updated+ sets facet->visitid to qh.visit_id-1++ design:+ for each ridge of facet+ if ridge of visited neighbor (i.e., unprocessed)+ if vertex in ridge+ append ridge to vertex+ mark facet processed+*/+void qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges) {+ ridgeT *ridge, **ridgep;+ facetT *neighbor;++ FOREACHridge_(facet->ridges) {+ neighbor= otherfacet_(ridge, facet);+ if (neighbor->visitid == qh->visit_id+ && qh_setin(ridge->vertices, vertex))+ qh_setappend(qh, ridges, ridge);+ }+ facet->visitid= qh->visit_id-1;+} /* vertexridges_facet */++/*-<a href="qh-merge_r.htm#TOC"+ >-------------------------------</a><a name="willdelete">-</a>++ qh_willdelete(qh, facet, replace )+ moves facet to visible list+ sets facet->f.replace to replace (may be NULL)++ returns:+ bumps qh.num_visible+*/+void qh_willdelete(qhT *qh, facetT *facet, facetT *replace) {++ qh_removefacet(qh, facet);+ qh_prependfacet(qh, facet, &qh->visible_list);+ qh->num_visible++;+ facet->visible= True;+ facet->f.replace= replace;+} /* willdelete */++#else /* qh_NOmerge */+void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {+}+void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,+ boolT vneighbors) {+}+boolT qh_checkzero(qhT *qh, boolT testall) {+ }+#endif /* qh_NOmerge */+
+ C/poly2_r.c view
@@ -0,0 +1,3222 @@+/*<html><pre> -<a href="qh-poly_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ poly2_r.c+ implements polygons and simplicies++ see qh-poly_r.htm, poly_r.h and libqhull_r.h++ frequently used code is in poly_r.c++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/poly2_r.c#10 $$Change: 2069 $+ $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $+*/++#include "qhull_ra.h"++/*======== functions in alphabetical order ==========*/++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="addhash">-</a>++ qh_addhash( newelem, hashtable, hashsize, hash )+ add newelem to linear hash table at hash if not already there+*/+void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash) {+ int scan;+ void *elem;++ for (scan= (int)hash; (elem= SETelem_(hashtable, scan));+ scan= (++scan >= hashsize ? 0 : scan)) {+ if (elem == newelem)+ break;+ }+ /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */+ if (!elem)+ SETelem_(hashtable, scan)= newelem;+} /* addhash */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="check_bestdist">-</a>++ qh_check_bestdist(qh)+ check that all points are within max_outside of the nearest facet+ if qh.ONLYgood,+ ignores !good facets++ see:+ qh_check_maxout(), qh_outerinner()++ notes:+ only called from qh_check_points()+ seldom used since qh.MERGING is almost always set+ if notverified>0 at end of routine+ some points were well inside the hull. If the hull contains+ a lens-shaped component, these points were not verified. Use+ options 'Qi Tv' to verify all points. (Exhaustive check also verifies)++ design:+ determine facet for each point (if any)+ for each point+ start with the assigned facet or with the first facet+ find the best facet for the point and check all coplanar facets+ error if point is outside of facet+*/+void qh_check_bestdist(qhT *qh) {+ boolT waserror= False, unassigned;+ facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL;+ facetT *facetlist;+ realT dist, maxoutside, maxdist= -REALmax;+ pointT *point;+ int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0;+ setT *facets;++ trace1((qh, qh->ferr, 1020, "qh_check_bestdist: check points below nearest facet. Facet_list f%d\n",+ qh->facet_list->id));+ maxoutside= qh_maxouter(qh);+ maxoutside += qh->DISTround;+ /* one more qh.DISTround for check computation */+ trace1((qh, qh->ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside));+ facets= qh_pointfacet(qh /*qh.facet_list*/);+ if (!qh_QUICKhelp && qh->PRINTprecision)+ qh_fprintf(qh, qh->ferr, 8091, "\n\+qhull output completed. Verifying that %d points are\n\+below %2.2g of the nearest %sfacet.\n",+ qh_setsize(qh, facets), maxoutside, (qh->ONLYgood ? "good " : ""));+ FOREACHfacet_i_(qh, facets) { /* for each point with facet assignment */+ if (facet)+ unassigned= False;+ else {+ unassigned= True;+ facet= qh->facet_list;+ }+ point= qh_point(qh, facet_i);+ if (point == qh->GOODpointp)+ continue;+ qh_distplane(qh, point, facet, &dist);+ numpart++;+ bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart);+ /* occurs after statistics reported */+ maximize_(maxdist, dist);+ if (dist > maxoutside) {+ if (qh->ONLYgood && !bestfacet->good+ && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist))+ && dist > maxoutside))+ notgood++;+ else {+ waserror= True;+ qh_fprintf(qh, qh->ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",+ facet_i, bestfacet->id, dist, maxoutside);+ if (errfacet1 != bestfacet) {+ errfacet2= errfacet1;+ errfacet1= bestfacet;+ }+ }+ }else if (unassigned && dist < -qh->MAXcoplanar)+ notverified++;+ }+ qh_settempfree(qh, &facets);+ if (notverified && !qh->DELAUNAY && !qh_QUICKhelp && qh->PRINTprecision)+ qh_fprintf(qh, qh->ferr, 8092, "\n%d points were well inside the hull. If the hull contains\n\+a lens-shaped component, these points were not verified. Use\n\+options 'Qci Tv' to verify all points.\n", notverified);+ if (maxdist > qh->outside_err) {+ qh_fprintf(qh, qh->ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n",+ maxdist, qh->outside_err);+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);+ }else if (waserror && qh->outside_err > REALmax/2)+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);+ /* else if waserror, the error was logged to qh.ferr but does not effect the output */+ trace0((qh, qh->ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist));+} /* check_bestdist */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="check_dupridge">-</a>++ qh_check_dupridge(qh, facet1, dist1, facet2, dist2)+ Check duplicate ridge between facet1 and facet2 for wide merge+ dist1 is the maximum distance of facet1's vertices to facet2+ dist2 is the maximum distance of facet2's vertices to facet1++ Returns+ Level 1 log of the duplicate ridge with the minimum distance between vertices+ Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x)++ called from:+ qh_forcedmerges()+*/+#ifndef qh_NOmerge+void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2) {+ vertexT *vertex, **vertexp, *vertexA, **vertexAp;+ realT dist, innerplane, mergedist, outerplane, prevdist, ratio;+ realT minvertex= REALmax;++ mergedist= fmin_(dist1, dist2);+ qh_outerinner(qh, NULL, &outerplane, &innerplane); /* ratio from qh_printsummary */+ prevdist= fmax_(outerplane, innerplane);+ maximize_(prevdist, qh->ONEmerge + qh->DISTround);+ maximize_(prevdist, qh->MINoutside + qh->DISTround);+ ratio= mergedist/prevdist;+ FOREACHvertex_(facet1->vertices) { /* The duplicate ridge is between facet1 and facet2, so either facet can be tested */+ FOREACHvertexA_(facet1->vertices) {+ if (vertex > vertexA){ /* Test each pair once */+ dist= qh_pointdist(vertex->point, vertexA->point, qh->hull_dim);+ minimize_(minvertex, dist);+ }+ }+ }+ trace0((qh, qh->ferr, 16, "qh_check_dupridge: duplicate ridge between f%d and f%d due to nearly-coincident vertices (%2.2g), dist %2.2g, reverse dist %2.2g, ratio %2.2g while processing p%d\n",+ facet1->id, facet2->id, minvertex, dist1, dist2, ratio, qh->furthest_id));+ if (ratio > qh_WIDEduplicate) {+ qh_fprintf(qh, qh->ferr, 6271, "qhull precision error (qh_check_dupridge): wide merge (%.0f times wider) due to duplicate ridge with nearly coincident points (%2.2g) between f%d and f%d, merge dist %2.2g, while processing p%d\n- Ignore error with option 'Q12'\n- To be fixed in a later version of Qhull\n",+ ratio, minvertex, facet1->id, facet2->id, mergedist, qh->furthest_id);+ if (qh->DELAUNAY)+ qh_fprintf(qh, qh->ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n");+ if(minvertex > qh_WIDEduplicate*prevdist)+ qh_fprintf(qh, qh->ferr, 8146, "- Vertex distance %2.2g is greater than %d times maximum distance %2.2g\n Please report to bradb@shore.net with steps to reproduce and all output\n",+ minvertex, qh_WIDEduplicate, prevdist);+ if (!qh->NOwide)+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);+ }+} /* check_dupridge */+#endif++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="check_maxout">-</a>++ qh_check_maxout(qh)+ updates qh.max_outside by checking all points against bestfacet+ if qh.ONLYgood, ignores !good facets++ returns:+ updates facet->maxoutside via qh_findbesthorizon()+ sets qh.maxoutdone+ if printing qh.min_vertex (qh_outerinner),+ it is updated to the current vertices+ removes inside/coplanar points from coplanarset as needed++ notes:+ defines coplanar as min_vertex instead of MAXcoplanar+ may not need to check near-inside points because of qh.MAXcoplanar+ and qh.KEEPnearinside (before it was -DISTround)++ see also:+ qh_check_bestdist()++ design:+ if qh.min_vertex is needed+ for all neighbors of all vertices+ test distance from vertex to neighbor+ determine facet for each point (if any)+ for each point with an assigned facet+ find the best facet for the point and check all coplanar facets+ (updates outer planes)+ remove near-inside points from coplanar sets+*/+#ifndef qh_NOmerge+void qh_check_maxout(qhT *qh) {+ facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist;+ realT dist, maxoutside, minvertex, old_maxoutside;+ pointT *point;+ int numpart= 0, facet_i, facet_n, notgood= 0;+ setT *facets, *vertices;+ vertexT *vertex;++ trace1((qh, qh->ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n"));+ maxoutside= minvertex= 0;+ if (qh->VERTEXneighbors+ && (qh->PRINTsummary || qh->KEEPinside || qh->KEEPcoplanar+ || qh->TRACElevel || qh->PRINTstatistics+ || qh->PRINTout[0] == qh_PRINTsummary || qh->PRINTout[0] == qh_PRINTnone)) {+ trace1((qh, qh->ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n"));+ vertices= qh_pointvertex(qh /*qh.facet_list*/);+ FORALLvertices {+ FOREACHneighbor_(vertex) {+ zinc_(Zdistvertex); /* distance also computed by main loop below */+ qh_distplane(qh, vertex->point, neighbor, &dist);+ minimize_(minvertex, dist);+ if (-dist > qh->TRACEdist || dist > qh->TRACEdist+ || neighbor == qh->tracefacet || vertex == qh->tracevertex)+ qh_fprintf(qh, qh->ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n",+ qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id);+ }+ }+ if (qh->MERGING) {+ wmin_(Wminvertex, qh->min_vertex);+ }+ qh->min_vertex= minvertex;+ qh_settempfree(qh, &vertices);+ }+ facets= qh_pointfacet(qh /*qh.facet_list*/);+ do {+ old_maxoutside= fmax_(qh->max_outside, maxoutside);+ FOREACHfacet_i_(qh, facets) { /* for each point with facet assignment */+ if (facet) {+ point= qh_point(qh, facet_i);+ if (point == qh->GOODpointp)+ continue;+ zzinc_(Ztotcheck);+ qh_distplane(qh, point, facet, &dist);+ numpart++;+ bestfacet= qh_findbesthorizon(qh, qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart);+ if (bestfacet && dist > maxoutside) {+ if (qh->ONLYgood && !bestfacet->good+ && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist))+ && dist > maxoutside))+ notgood++;+ else+ maxoutside= dist;+ }+ if (dist > qh->TRACEdist || (bestfacet && bestfacet == qh->tracefacet))+ qh_fprintf(qh, qh->ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n",+ qh_pointid(qh, point), dist, (bestfacet ? bestfacet->id : UINT_MAX));+ }+ }+ }while+ (maxoutside > 2*old_maxoutside);+ /* if qh.maxoutside increases substantially, qh_SEARCHdist is not valid+ e.g., RBOX 5000 s Z1 G1e-13 t1001200614 | qhull */+ zzadd_(Zcheckpart, numpart);+ qh_settempfree(qh, &facets);+ wval_(Wmaxout)= maxoutside - qh->max_outside;+ wmax_(Wmaxoutside, qh->max_outside);+ qh->max_outside= maxoutside;+ qh_nearcoplanar(qh /*qh.facet_list*/);+ qh->maxoutdone= True;+ trace1((qh, qh->ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n",+ maxoutside, qh->min_vertex, notgood));+} /* check_maxout */+#else /* qh_NOmerge */+void qh_check_maxout(qhT *qh) {+}+#endif++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="check_output">-</a>++ qh_check_output(qh)+ performs the checks at the end of qhull algorithm+ Maybe called after voronoi output. Will recompute otherwise centrums are Voronoi centers instead+*/+void qh_check_output(qhT *qh) {+ int i;++ if (qh->STOPcone)+ return;+ if (qh->VERIFYoutput | qh->IStracing | qh->CHECKfrequently) {+ qh_checkpolygon(qh, qh->facet_list);+ qh_checkflipped_all(qh, qh->facet_list);+ qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);+ }else if (!qh->MERGING && qh_newstats(qh, qh->qhstat.precision, &i)) {+ qh_checkflipped_all(qh, qh->facet_list);+ qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);+ }+} /* check_output */++++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="check_point">-</a>++ qh_check_point(qh, point, facet, maxoutside, maxdist, errfacet1, errfacet2 )+ check that point is less than maxoutside from facet+*/+void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) {+ realT dist;++ /* occurs after statistics reported */+ qh_distplane(qh, point, facet, &dist);+ if (dist > *maxoutside) {+ if (*errfacet1 != facet) {+ *errfacet2= *errfacet1;+ *errfacet1= facet;+ }+ qh_fprintf(qh, qh->ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",+ qh_pointid(qh, point), facet->id, dist, *maxoutside);+ }+ maximize_(*maxdist, dist);+} /* qh_check_point */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="check_points">-</a>++ qh_check_points(qh)+ checks that all points are inside all facets++ notes:+ if many points and qh_check_maxout not called (i.e., !qh.MERGING),+ calls qh_findbesthorizon (seldom done).+ ignores flipped facets+ maxoutside includes 2 qh.DISTrounds+ one qh.DISTround for the computed distances in qh_check_points+ qh_printafacet and qh_printsummary needs only one qh.DISTround+ the computation for qh.VERIFYdirect does not account for qh.other_points++ design:+ if many points+ use qh_check_bestdist()+ else+ for all facets+ for all points+ check that point is inside facet+*/+void qh_check_points(qhT *qh) {+ facetT *facet, *errfacet1= NULL, *errfacet2= NULL;+ realT total, maxoutside, maxdist= -REALmax;+ pointT *point, **pointp, *pointtemp;+ boolT testouter;++ maxoutside= qh_maxouter(qh);+ maxoutside += qh->DISTround;+ /* one more qh.DISTround for check computation */+ trace1((qh, qh->ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n",+ maxoutside));+ if (qh->num_good) /* miss counts other_points and !good facets */+ total= (float)qh->num_good * (float)qh->num_points;+ else+ total= (float)qh->num_facets * (float)qh->num_points;+ if (total >= qh_VERIFYdirect && !qh->maxoutdone) {+ if (!qh_QUICKhelp && qh->SKIPcheckmax && qh->MERGING)+ qh_fprintf(qh, qh->ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\+Verify may report that a point is outside of a facet.\n");+ qh_check_bestdist(qh);+ }else {+ if (qh_MAXoutside && qh->maxoutdone)+ testouter= True;+ else+ testouter= False;+ if (!qh_QUICKhelp) {+ if (qh->MERGEexact)+ qh_fprintf(qh, qh->ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point\n\+is outside of a facet. See qh-optq.htm#Qx\n");+ else if (qh->SKIPcheckmax || qh->NOnearinside)+ qh_fprintf(qh, qh->ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\+near-inside points ('Q8'). Verify may report that a point is outside\n\+of a facet.\n");+ }+ if (qh->PRINTprecision) {+ if (testouter)+ qh_fprintf(qh, qh->ferr, 8098, "\n\+Output completed. Verifying that all points are below outer planes of\n\+all %sfacets. Will make %2.0f distance computations.\n",+ (qh->ONLYgood ? "good " : ""), total);+ else+ qh_fprintf(qh, qh->ferr, 8099, "\n\+Output completed. Verifying that all points are below %2.2g of\n\+all %sfacets. Will make %2.0f distance computations.\n",+ maxoutside, (qh->ONLYgood ? "good " : ""), total);+ }+ FORALLfacets {+ if (!facet->good && qh->ONLYgood)+ continue;+ if (facet->flipped)+ continue;+ if (!facet->normal) {+ qh_fprintf(qh, qh->ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id);+ continue;+ }+ if (testouter) {+#if qh_MAXoutside+ maxoutside= facet->maxoutside + 2* qh->DISTround;+ /* one DISTround to actual point and another to computed point */+#endif+ }+ FORALLpoints {+ if (point != qh->GOODpointp)+ qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);+ }+ FOREACHpoint_(qh->other_points) {+ if (point != qh->GOODpointp)+ qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);+ }+ }+ if (maxdist > qh->outside_err) {+ qh_fprintf(qh, qh->ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n",+ maxdist, qh->outside_err );+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 );+ }else if (errfacet1 && qh->outside_err > REALmax/2)+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 );+ /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */+ trace0((qh, qh->ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist));+ }+} /* check_points */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="checkconvex">-</a>++ qh_checkconvex(qh, facetlist, fault )+ check that each ridge in facetlist is convex+ fault = qh_DATAfault if reporting errors+ = qh_ALGORITHMfault otherwise++ returns:+ counts Zconcaveridges and Zcoplanarridges+ errors if concaveridge or if merging an coplanar ridge++ note:+ if not merging,+ tests vertices for neighboring simplicial facets+ else if ZEROcentrum,+ tests vertices for neighboring simplicial facets+ else+ tests centrums of neighboring facets++ design:+ for all facets+ report flipped facets+ if ZEROcentrum and simplicial neighbors+ test vertices for neighboring simplicial facets+ else+ test centrum against all neighbors+*/+void qh_checkconvex(qhT *qh, facetT *facetlist, int fault) {+ facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL;+ vertexT *vertex;+ realT dist;+ pointT *centrum;+ boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial;+ int neighbor_i;++ trace1((qh, qh->ferr, 1026, "qh_checkconvex: check all ridges are convex\n"));+ if (!qh->RERUN) {+ zzval_(Zconcaveridges)= 0;+ zzval_(Zcoplanarridges)= 0;+ }+ FORALLfacet_(facetlist) {+ if (facet->flipped) {+ qh_precision(qh, "flipped facet");+ qh_fprintf(qh, qh->ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n",+ facet->id);+ errfacet1= facet;+ waserror= True;+ continue;+ }+ if (qh->MERGING && (!qh->ZEROcentrum || !facet->simplicial || facet->tricoplanar))+ allsimplicial= False;+ else {+ allsimplicial= True;+ neighbor_i= 0;+ FOREACHneighbor_(facet) {+ vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);+ if (!neighbor->simplicial || neighbor->tricoplanar) {+ allsimplicial= False;+ continue;+ }+ qh_distplane(qh, vertex->point, neighbor, &dist);+ if (dist > -qh->DISTround) {+ if (fault == qh_DATAfault) {+ qh_precision(qh, "coplanar or concave ridge");+ qh_fprintf(qh, qh->ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist);+ qh_errexit(qh, qh_ERRsingular, NULL, NULL);+ }+ if (dist > qh->DISTround) {+ zzinc_(Zconcaveridges);+ qh_precision(qh, "concave ridge");+ qh_fprintf(qh, qh->ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n",+ facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist);+ errfacet1= facet;+ errfacet2= neighbor;+ waserror= True;+ }else if (qh->ZEROcentrum) {+ if (dist > 0) { /* qh_checkzero checks that dist < - qh->DISTround */+ zzinc_(Zcoplanarridges);+ qh_precision(qh, "coplanar ridge");+ qh_fprintf(qh, qh->ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n",+ facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist);+ errfacet1= facet;+ errfacet2= neighbor;+ waserror= True;+ }+ }else {+ zzinc_(Zcoplanarridges);+ qh_precision(qh, "coplanar ridge");+ trace0((qh, qh->ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n",+ facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist, qh->furthest_id));+ }+ }+ }+ }+ if (!allsimplicial) {+ if (qh->CENTERtype == qh_AScentrum) {+ if (!facet->center)+ facet->center= qh_getcentrum(qh, facet);+ centrum= facet->center;+ }else {+ if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) {+ centrum_warning= True;+ qh_fprintf(qh, qh->ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n");+ }+ centrum= qh_getcentrum(qh, facet);+ tempcentrum= True;+ }+ FOREACHneighbor_(facet) {+ if (qh->ZEROcentrum && facet->simplicial && neighbor->simplicial)+ continue;+ if (facet->tricoplanar || neighbor->tricoplanar)+ continue;+ zzinc_(Zdistconvex);+ qh_distplane(qh, centrum, neighbor, &dist);+ if (dist > qh->DISTround) {+ zzinc_(Zconcaveridges);+ qh_precision(qh, "concave ridge");+ qh_fprintf(qh, qh->ferr, 6117, "qhull precision error: f%d is concave to f%d. Centrum of f%d is %6.4g above f%d\n",+ facet->id, neighbor->id, facet->id, dist, neighbor->id);+ errfacet1= facet;+ errfacet2= neighbor;+ waserror= True;+ }else if (dist >= 0.0) { /* if arithmetic always rounds the same,+ can test against centrum radius instead */+ zzinc_(Zcoplanarridges);+ qh_precision(qh, "coplanar ridge");+ qh_fprintf(qh, qh->ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d. Centrum of f%d is %6.4g above f%d\n",+ facet->id, neighbor->id, facet->id, dist, neighbor->id);+ errfacet1= facet;+ errfacet2= neighbor;+ waserror= True;+ }+ }+ if (tempcentrum)+ qh_memfree(qh, centrum, qh->normal_size);+ }+ }+ if (waserror && !qh->FORCEoutput)+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);+} /* checkconvex */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="checkfacet">-</a>++ qh_checkfacet(qh, facet, newmerge, waserror )+ checks for consistency errors in facet+ newmerge set if from merge_r.c++ returns:+ sets waserror if any error occurs++ checks:+ vertex ids are inverse sorted+ unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial)+ if non-simplicial, at least as many ridges as neighbors+ neighbors are not duplicated+ ridges are not duplicated+ in 3-d, ridges=verticies+ (qh.hull_dim-1) ridge vertices+ neighbors are reciprocated+ ridge neighbors are facet neighbors and a ridge for every neighbor+ simplicial neighbors match facetintersect+ vertex intersection matches vertices of common ridges+ vertex neighbors and facet vertices agree+ all ridges have distinct vertex sets++ notes:+ uses neighbor->seen++ design:+ check sets+ check vertices+ check sizes of neighbors and vertices+ check for qh_MERGEridge and qh_DUPLICATEridge flags+ check neighbor set+ check ridge set+ check ridges, neighbors, and vertices+*/+void qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp) {+ facetT *neighbor, **neighborp, *errother=NULL;+ ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2;+ vertexT *vertex, **vertexp;+ unsigned previousid= INT_MAX;+ int numneighbors, numvertices, numridges=0, numRvertices=0;+ boolT waserror= False;+ int skipA, skipB, ridge_i, ridge_n, i;+ setT *intersection;++ if (facet->visible) {+ qh_fprintf(qh, qh->ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n",+ facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ if (!facet->normal) {+ qh_fprintf(qh, qh->ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n",+ facet->id);+ waserror= True;+ }+ qh_setcheck(qh, facet->vertices, "vertices for f", facet->id);+ qh_setcheck(qh, facet->ridges, "ridges for f", facet->id);+ qh_setcheck(qh, facet->outsideset, "outsideset for f", facet->id);+ qh_setcheck(qh, facet->coplanarset, "coplanarset for f", facet->id);+ qh_setcheck(qh, facet->neighbors, "neighbors for f", facet->id);+ FOREACHvertex_(facet->vertices) {+ if (vertex->deleted) {+ qh_fprintf(qh, qh->ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id);+ qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);+ waserror= True;+ }+ if (vertex->id >= previousid) {+ qh_fprintf(qh, qh->ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id);+ waserror= True;+ break;+ }+ previousid= vertex->id;+ }+ numneighbors= qh_setsize(qh, facet->neighbors);+ numvertices= qh_setsize(qh, facet->vertices);+ numridges= qh_setsize(qh, facet->ridges);+ if (facet->simplicial) {+ if (numvertices+numneighbors != 2*qh->hull_dim+ && !facet->degenerate && !facet->redundant) {+ qh_fprintf(qh, qh->ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh->hull_dim\n",+ facet->id, numvertices, numneighbors);+ qh_setprint(qh, qh->ferr, "", facet->neighbors);+ waserror= True;+ }+ }else { /* non-simplicial */+ if (!newmerge+ &&(numvertices < qh->hull_dim || numneighbors < qh->hull_dim)+ && !facet->degenerate && !facet->redundant) {+ qh_fprintf(qh, qh->ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh->hull_dim\n",+ facet->id, numvertices, numneighbors);+ waserror= True;+ }+ /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */+ if (numridges < numneighbors+ ||(qh->hull_dim == 3 && numvertices > numridges && !qh->NEWfacets)+ ||(qh->hull_dim == 2 && numridges + numvertices + numneighbors != 6)) {+ if (!facet->degenerate && !facet->redundant) {+ qh_fprintf(qh, qh->ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n",+ facet->id, numridges, numneighbors, numvertices);+ waserror= True;+ }+ }+ }+ FOREACHneighbor_(facet) {+ if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) {+ qh_fprintf(qh, qh->ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ neighbor->seen= True;+ }+ FOREACHneighbor_(facet) {+ if (!qh_setin(neighbor->neighbors, facet)) {+ qh_fprintf(qh, qh->ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n",+ facet->id, neighbor->id, neighbor->id, facet->id);+ errother= neighbor;+ waserror= True;+ }+ if (!neighbor->seen) {+ qh_fprintf(qh, qh->ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n",+ facet->id, neighbor->id);+ errother= neighbor;+ waserror= True;+ }+ neighbor->seen= False;+ }+ FOREACHridge_(facet->ridges) {+ qh_setcheck(qh, ridge->vertices, "vertices for r", ridge->id);+ ridge->seen= False;+ }+ FOREACHridge_(facet->ridges) {+ if (ridge->seen) {+ qh_fprintf(qh, qh->ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n",+ facet->id, ridge->id);+ errridge= ridge;+ waserror= True;+ }+ ridge->seen= True;+ numRvertices= qh_setsize(qh, ridge->vertices);+ if (numRvertices != qh->hull_dim - 1) {+ qh_fprintf(qh, qh->ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n",+ ridge->top->id, ridge->bottom->id, numRvertices);+ errridge= ridge;+ waserror= True;+ }+ neighbor= otherfacet_(ridge, facet);+ neighbor->seen= True;+ if (!qh_setin(facet->neighbors, neighbor)) {+ qh_fprintf(qh, qh->ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n",+ facet->id, neighbor->id, ridge->id);+ errridge= ridge;+ waserror= True;+ }+ }+ if (!facet->simplicial) {+ FOREACHneighbor_(facet) {+ if (!neighbor->seen) {+ qh_fprintf(qh, qh->ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n",+ facet->id, neighbor->id);+ errother= neighbor;+ waserror= True;+ }+ intersection= qh_vertexintersect_new(qh, facet->vertices, neighbor->vertices);+ qh_settemppush(qh, intersection);+ FOREACHvertex_(facet->vertices) {+ vertex->seen= False;+ vertex->seen2= False;+ }+ FOREACHvertex_(intersection)+ vertex->seen= True;+ FOREACHridge_(facet->ridges) {+ if (neighbor != otherfacet_(ridge, facet))+ continue;+ FOREACHvertex_(ridge->vertices) {+ if (!vertex->seen) {+ qh_fprintf(qh, qh->ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n",+ vertex->id, ridge->id, facet->id, neighbor->id);+ qh_errexit(qh, qh_ERRqhull, facet, ridge);+ }+ vertex->seen2= True;+ }+ }+ if (!newmerge) {+ FOREACHvertex_(intersection) {+ if (!vertex->seen2) {+ if (qh->IStracing >=3 || !qh->MERGING) {+ qh_fprintf(qh, qh->ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\+ not in a ridge. This is ok under merging. Last point was p%d\n",+ vertex->id, facet->id, neighbor->id, qh->furthest_id);+ if (!qh->FORCEoutput && !qh->MERGING) {+ qh_errprint(qh, "ERRONEOUS", facet, neighbor, NULL, vertex);+ if (!qh->MERGING)+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ }+ }+ }+ }+ qh_settempfree(qh, &intersection);+ }+ }else { /* simplicial */+ FOREACHneighbor_(facet) {+ if (neighbor->simplicial) {+ skipA= SETindex_(facet->neighbors, neighbor);+ skipB= qh_setindex(neighbor->neighbors, facet);+ if (skipA<0 || skipB<0 || !qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) {+ qh_fprintf(qh, qh->ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n",+ facet->id, skipA, neighbor->id, skipB);+ errother= neighbor;+ waserror= True;+ }+ }+ }+ }+ if (qh->hull_dim < 5 && (qh->IStracing > 2 || qh->CHECKfrequently)) {+ FOREACHridge_i_(qh, facet->ridges) { /* expensive */+ for (i=ridge_i+1; i < ridge_n; i++) {+ ridge2= SETelemt_(facet->ridges, i, ridgeT);+ if (qh_setequal(ridge->vertices, ridge2->vertices)) {+ qh_fprintf(qh, qh->ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n",+ ridge->id, ridge2->id);+ errridge= ridge;+ waserror= True;+ }+ }+ }+ }+ if (waserror) {+ qh_errprint(qh, "ERRONEOUS", facet, errother, errridge, NULL);+ *waserrorp= True;+ }+} /* checkfacet */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="checkflipped_all">-</a>++ qh_checkflipped_all(qh, facetlist )+ checks orientation of facets in list against interior point+*/+void qh_checkflipped_all(qhT *qh, facetT *facetlist) {+ facetT *facet;+ boolT waserror= False;+ realT dist;++ if (facetlist == qh->facet_list)+ zzval_(Zflippedfacets)= 0;+ FORALLfacet_(facetlist) {+ if (facet->normal && !qh_checkflipped(qh, facet, &dist, !qh_ALL)) {+ qh_fprintf(qh, qh->ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n",+ facet->id, dist);+ if (!qh->FORCEoutput) {+ qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, NULL);+ waserror= True;+ }+ }+ }+ if (waserror) {+ qh_fprintf(qh, qh->ferr, 8101, "\n\+A flipped facet occurs when its distance to the interior point is\n\+greater than %2.2g, the maximum roundoff error.\n", -qh->DISTround);+ qh_errexit(qh, qh_ERRprec, NULL, NULL);+ }+} /* checkflipped_all */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="checkpolygon">-</a>++ qh_checkpolygon(qh, facetlist )+ checks the correctness of the structure++ notes:+ call with either qh.facet_list or qh.newfacet_list+ checks num_facets and num_vertices if qh.facet_list++ design:+ for each facet+ checks facet and outside set+ initializes vertexlist+ for each facet+ checks vertex set+ if checking all facets(qh.facetlist)+ check facet count+ if qh.VERTEXneighbors+ check vertex neighbors and count+ check vertex count+*/+void qh_checkpolygon(qhT *qh, facetT *facetlist) {+ facetT *facet;+ vertexT *vertex, **vertexp, *vertexlist;+ int numfacets= 0, numvertices= 0, numridges= 0;+ int totvneighbors= 0, totvertices= 0;+ boolT waserror= False, nextseen= False, visibleseen= False;++ trace1((qh, qh->ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id));+ if (facetlist != qh->facet_list || qh->ONLYgood)+ nextseen= True;+ FORALLfacet_(facetlist) {+ if (facet == qh->visible_list)+ visibleseen= True;+ if (!facet->visible) {+ if (!nextseen) {+ if (facet == qh->facet_next)+ nextseen= True;+ else if (qh_setsize(qh, facet->outsideset)) {+ if (!qh->NARROWhull+#if !qh_COMPUTEfurthest+ || facet->furthestdist >= qh->MINoutside+#endif+ ) {+ qh_fprintf(qh, qh->ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh->facet_next\n",+ facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ }+ }+ numfacets++;+ qh_checkfacet(qh, facet, False, &waserror);+ }+ }+ if (qh->visible_list && !visibleseen && facetlist == qh->facet_list) {+ qh_fprintf(qh, qh->ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh->visible_list->id);+ qh_printlists(qh);+ qh_errexit(qh, qh_ERRqhull, qh->visible_list, NULL);+ }+ if (facetlist == qh->facet_list)+ vertexlist= qh->vertex_list;+ else if (facetlist == qh->newfacet_list)+ vertexlist= qh->newvertex_list;+ else+ vertexlist= NULL;+ FORALLvertex_(vertexlist) {+ vertex->seen= False;+ vertex->visitid= 0;+ }+ FORALLfacet_(facetlist) {+ if (facet->visible)+ continue;+ if (facet->simplicial)+ numridges += qh->hull_dim;+ else+ numridges += qh_setsize(qh, facet->ridges);+ FOREACHvertex_(facet->vertices) {+ vertex->visitid++;+ if (!vertex->seen) {+ vertex->seen= True;+ numvertices++;+ if (qh_pointid(qh, vertex->point) == qh_IDunknown) {+ qh_fprintf(qh, qh->ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n",+ vertex->point, vertex->id, qh->first_point);+ waserror= True;+ }+ }+ }+ }+ qh->vertex_visit += (unsigned int)numfacets;+ if (facetlist == qh->facet_list) {+ if (numfacets != qh->num_facets - qh->num_visible) {+ qh_fprintf(qh, qh->ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n",+ numfacets, qh->num_facets, qh->num_visible);+ waserror= True;+ }+ qh->vertex_visit++;+ if (qh->VERTEXneighbors) {+ FORALLvertices {+ qh_setcheck(qh, vertex->neighbors, "neighbors for v", vertex->id);+ if (vertex->deleted)+ continue;+ totvneighbors += qh_setsize(qh, vertex->neighbors);+ }+ FORALLfacet_(facetlist)+ totvertices += qh_setsize(qh, facet->vertices);+ if (totvneighbors != totvertices) {+ qh_fprintf(qh, qh->ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent. Totvneighbors %d, totvertices %d\n",+ totvneighbors, totvertices);+ waserror= True;+ }+ }+ if (numvertices != qh->num_vertices - qh_setsize(qh, qh->del_vertices)) {+ qh_fprintf(qh, qh->ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n",+ numvertices, qh->num_vertices - qh_setsize(qh, qh->del_vertices));+ waserror= True;+ }+ if (qh->hull_dim == 2 && numvertices != numfacets) {+ qh_fprintf(qh, qh->ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n",+ numvertices, numfacets);+ waserror= True;+ }+ if (qh->hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) {+ qh_fprintf(qh, qh->ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\+ A vertex appears twice in a edge list. May occur during merging.",+ numvertices, numfacets, numridges/2);+ /* occurs if lots of merging and a vertex ends up twice in an edge list. e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */+ }+ }+ if (waserror)+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+} /* checkpolygon */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="checkvertex">-</a>++ qh_checkvertex(qh, vertex )+ check vertex for consistency+ checks vertex->neighbors++ notes:+ neighbors checked efficiently in checkpolygon+*/+void qh_checkvertex(qhT *qh, vertexT *vertex) {+ boolT waserror= False;+ facetT *neighbor, **neighborp, *errfacet=NULL;++ if (qh_pointid(qh, vertex->point) == qh_IDunknown) {+ qh_fprintf(qh, qh->ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point);+ waserror= True;+ }+ if (vertex->id >= qh->vertex_id) {+ qh_fprintf(qh, qh->ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id);+ waserror= True;+ }+ if (!waserror && !vertex->deleted) {+ if (qh_setsize(qh, vertex->neighbors)) {+ FOREACHneighbor_(vertex) {+ if (!qh_setin(neighbor->vertices, vertex)) {+ qh_fprintf(qh, qh->ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id);+ errfacet= neighbor;+ waserror= True;+ }+ }+ }+ }+ if (waserror) {+ qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);+ qh_errexit(qh, qh_ERRqhull, errfacet, NULL);+ }+} /* checkvertex */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="clearcenters">-</a>++ qh_clearcenters(qh, type )+ clear old data from facet->center++ notes:+ sets new centertype+ nop if CENTERtype is the same+*/+void qh_clearcenters(qhT *qh, qh_CENTER type) {+ facetT *facet;++ if (qh->CENTERtype != type) {+ FORALLfacets {+ if (facet->tricoplanar && !facet->keepcentrum)+ facet->center= NULL; /* center is owned by the ->keepcentrum facet */+ else if (qh->CENTERtype == qh_ASvoronoi){+ if (facet->center) {+ qh_memfree(qh, facet->center, qh->center_size);+ facet->center= NULL;+ }+ }else /* qh->CENTERtype == qh_AScentrum */ {+ if (facet->center) {+ qh_memfree(qh, facet->center, qh->normal_size);+ facet->center= NULL;+ }+ }+ }+ qh->CENTERtype= type;+ }+ trace2((qh, qh->ferr, 2043, "qh_clearcenters: switched to center type %d\n", type));+} /* clearcenters */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="createsimplex">-</a>++ qh_createsimplex(qh, vertices )+ creates a simplex from a set of vertices++ returns:+ initializes qh.facet_list to the simplex+ initializes qh.newfacet_list, .facet_tail+ initializes qh.vertex_list, .newvertex_list, .vertex_tail++ design:+ initializes lists+ for each vertex+ create a new facet+ for each new facet+ create its neighbor set+*/+void qh_createsimplex(qhT *qh, setT *vertices) {+ facetT *facet= NULL, *newfacet;+ boolT toporient= True;+ int vertex_i, vertex_n, nth;+ setT *newfacets= qh_settemp(qh, qh->hull_dim+1);+ vertexT *vertex;++ qh->facet_list= qh->newfacet_list= qh->facet_tail= qh_newfacet(qh);+ qh->num_facets= qh->num_vertices= qh->num_visible= 0;+ qh->vertex_list= qh->newvertex_list= qh->vertex_tail= qh_newvertex(qh, NULL);+ FOREACHvertex_i_(qh, vertices) {+ newfacet= qh_newfacet(qh);+ newfacet->vertices= qh_setnew_delnthsorted(qh, vertices, vertex_n,+ vertex_i, 0);+ newfacet->toporient= (unsigned char)toporient;+ qh_appendfacet(qh, newfacet);+ newfacet->newfacet= True;+ qh_appendvertex(qh, vertex);+ qh_setappend(qh, &newfacets, newfacet);+ toporient ^= True;+ }+ FORALLnew_facets {+ nth= 0;+ FORALLfacet_(qh->newfacet_list) {+ if (facet != newfacet)+ SETelem_(newfacet->neighbors, nth++)= facet;+ }+ qh_settruncate(qh, newfacet->neighbors, qh->hull_dim);+ }+ qh_settempfree(qh, &newfacets);+ trace1((qh, qh->ferr, 1028, "qh_createsimplex: created simplex\n"));+} /* createsimplex */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="delridge">-</a>++ qh_delridge(qh, ridge )+ deletes ridge from data structures it belongs to+ frees up its memory++ notes:+ in merge_r.c, caller sets vertex->delridge for each vertex+ ridges also freed in qh_freeqhull+*/+void qh_delridge(qhT *qh, ridgeT *ridge) {+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */++ qh_setdel(ridge->top->ridges, ridge);+ qh_setdel(ridge->bottom->ridges, ridge);+ qh_setfree(qh, &(ridge->vertices));+ qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);+} /* delridge */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="delvertex">-</a>++ qh_delvertex(qh, vertex )+ deletes a vertex and frees its memory++ notes:+ assumes vertex->adjacencies have been updated if needed+ unlinks from vertex_list+*/+void qh_delvertex(qhT *qh, vertexT *vertex) {++ if (vertex == qh->tracevertex)+ qh->tracevertex= NULL;+ qh_removevertex(qh, vertex);+ qh_setfree(qh, &vertex->neighbors);+ qh_memfree(qh, vertex, (int)sizeof(vertexT));+} /* delvertex */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="facet3vertex">-</a>++ qh_facet3vertex(qh, )+ return temporary set of 3-d vertices in qh_ORIENTclock order++ design:+ if simplicial facet+ build set from facet->vertices with facet->toporient+ else+ for each ridge in order+ build set from ridge's vertices+*/+setT *qh_facet3vertex(qhT *qh, facetT *facet) {+ ridgeT *ridge, *firstridge;+ vertexT *vertex;+ int cntvertices, cntprojected=0;+ setT *vertices;++ cntvertices= qh_setsize(qh, facet->vertices);+ vertices= qh_settemp(qh, cntvertices);+ if (facet->simplicial) {+ if (cntvertices != 3) {+ qh_fprintf(qh, qh->ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n",+ cntvertices, facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ qh_setappend(qh, &vertices, SETfirst_(facet->vertices));+ if (facet->toporient ^ qh_ORIENTclock)+ qh_setappend(qh, &vertices, SETsecond_(facet->vertices));+ else+ qh_setaddnth(qh, &vertices, 0, SETsecond_(facet->vertices));+ qh_setappend(qh, &vertices, SETelem_(facet->vertices, 2));+ }else {+ ridge= firstridge= SETfirstt_(facet->ridges, ridgeT); /* no infinite */+ while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) {+ qh_setappend(qh, &vertices, vertex);+ if (++cntprojected > cntvertices || ridge == firstridge)+ break;+ }+ if (!ridge || cntprojected != cntvertices) {+ qh_fprintf(qh, qh->ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up. got at least %d\n",+ facet->id, cntprojected);+ qh_errexit(qh, qh_ERRqhull, facet, ridge);+ }+ }+ return vertices;+} /* facet3vertex */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="findbestfacet">-</a>++ qh_findbestfacet(qh, point, bestoutside, bestdist, isoutside )+ find facet that is furthest below a point++ for Delaunay triangulations,+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.++ returns:+ if bestoutside is set (e.g., qh_ALL)+ returns best facet that is not upperdelaunay+ if Delaunay and inside, point is outside circumsphere of bestfacet+ else+ returns first facet below point+ if point is inside, returns nearest, !upperdelaunay facet+ distance to facet+ isoutside set if outside of facet++ notes:+ For tricoplanar facets, this finds one of the tricoplanar facets closest+ to the point. For Delaunay triangulations, the point may be inside a+ different tricoplanar facet. See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a>++ If inside, qh_findbestfacet performs an exhaustive search+ this may be too conservative. Sometimes it is clearly required.++ qh_findbestfacet is not used by qhull.+ uses qh.visit_id and qh.coplanarset++ see:+ <a href="geom_r.c#findbest">qh_findbest</a>+*/+facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,+ realT *bestdist, boolT *isoutside) {+ facetT *bestfacet= NULL;+ int numpart, totpart= 0;++ bestfacet= qh_findbest(qh, point, qh->facet_list,+ bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */,+ bestdist, isoutside, &totpart);+ if (*bestdist < -qh->DISTround) {+ bestfacet= qh_findfacet_all(qh, point, bestdist, isoutside, &numpart);+ totpart += numpart;+ if ((isoutside && *isoutside && bestoutside)+ || (isoutside && !*isoutside && bestfacet->upperdelaunay)) {+ bestfacet= qh_findbest(qh, point, bestfacet,+ bestoutside, False, bestoutside,+ bestdist, isoutside, &totpart);+ totpart += numpart;+ }+ }+ trace3((qh, qh->ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n",+ bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart));+ return bestfacet;+} /* findbestfacet */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="findbestlower">-</a>++ qh_findbestlower(qh, facet, point, bestdist, numpart )+ returns best non-upper, non-flipped neighbor of facet for point+ if needed, searches vertex neighbors++ returns:+ returns bestdist and updates numpart++ notes:+ if Delaunay and inside, point is outside of circumsphere of bestfacet+ called by qh_findbest() for points above an upperdelaunay facet++*/+facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) {+ facetT *neighbor, **neighborp, *bestfacet= NULL;+ realT bestdist= -REALmax/2 /* avoid underflow */;+ realT dist;+ vertexT *vertex;+ boolT isoutside= False; /* not used */++ zinc_(Zbestlower);+ FOREACHneighbor_(upperfacet) {+ if (neighbor->upperdelaunay || neighbor->flipped)+ continue;+ (*numpart)++;+ qh_distplane(qh, point, neighbor, &dist);+ if (dist > bestdist) {+ bestfacet= neighbor;+ bestdist= dist;+ }+ }+ if (!bestfacet) {+ zinc_(Zbestlowerv);+ /* rarely called, numpart does not count nearvertex computations */+ vertex= qh_nearvertex(qh, upperfacet, point, &dist);+ qh_vertexneighbors(qh);+ FOREACHneighbor_(vertex) {+ if (neighbor->upperdelaunay || neighbor->flipped)+ continue;+ (*numpart)++;+ qh_distplane(qh, point, neighbor, &dist);+ if (dist > bestdist) {+ bestfacet= neighbor;+ bestdist= dist;+ }+ }+ }+ if (!bestfacet) {+ zinc_(Zbestlowerall); /* invoked once per point in outsideset */+ zmax_(Zbestloweralln, qh->num_facets);+ /* [dec'15] Previously reported as QH6228 */+ trace3((qh, qh->ferr, 3025, "qh_findbestlower: all neighbors of facet %d are flipped or upper Delaunay. Search all facets\n",+ upperfacet->id));+ /* rarely called */+ bestfacet= qh_findfacet_all(qh, point, &bestdist, &isoutside, numpart);+ }+ *bestdistp= bestdist;+ trace3((qh, qh->ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n",+ bestfacet->id, bestdist, upperfacet->id, qh_pointid(qh, point)));+ return bestfacet;+} /* findbestlower */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="findfacet_all">-</a>++ qh_findfacet_all(qh, point, bestdist, isoutside, numpart )+ exhaustive search for facet below a point++ for Delaunay triangulations,+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.++ returns:+ returns first facet below point+ if point is inside,+ returns nearest facet+ distance to facet+ isoutside if point is outside of the hull+ number of distance tests++ notes:+ primarily for library users, rarely used by Qhull+*/+facetT *qh_findfacet_all(qhT *qh, pointT *point, realT *bestdist, boolT *isoutside,+ int *numpart) {+ facetT *bestfacet= NULL, *facet;+ realT dist;+ int totpart= 0;++ *bestdist= -REALmax;+ *isoutside= False;+ FORALLfacets {+ if (facet->flipped || !facet->normal)+ continue;+ totpart++;+ qh_distplane(qh, point, facet, &dist);+ if (dist > *bestdist) {+ *bestdist= dist;+ bestfacet= facet;+ if (dist > qh->MINoutside) {+ *isoutside= True;+ break;+ }+ }+ }+ *numpart= totpart;+ trace3((qh, qh->ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n",+ getid_(bestfacet), *bestdist, *isoutside, totpart));+ return bestfacet;+} /* findfacet_all */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="findgood">-</a>++ qh_findgood(qh, facetlist, goodhorizon )+ identify good facets for qh.PRINTgood+ if qh.GOODvertex>0+ facet includes point as vertex+ if !match, returns goodhorizon+ inactive if qh.MERGING+ if qh.GOODpoint+ facet is visible or coplanar (>0) or not visible (<0)+ if qh.GOODthreshold+ facet->normal matches threshold+ if !goodhorizon and !match,+ selects facet with closest angle+ sets GOODclosest++ returns:+ number of new, good facets found+ determines facet->good+ may update qh.GOODclosest++ notes:+ qh_findgood_all further reduces the good region++ design:+ count good facets+ mark good facets for qh.GOODpoint+ mark good facets for qh.GOODthreshold+ if necessary+ update qh.GOODclosest+*/+int qh_findgood(qhT *qh, facetT *facetlist, int goodhorizon) {+ facetT *facet, *bestfacet= NULL;+ realT angle, bestangle= REALmax, dist;+ int numgood=0;++ FORALLfacet_(facetlist) {+ if (facet->good)+ numgood++;+ }+ if (qh->GOODvertex>0 && !qh->MERGING) {+ FORALLfacet_(facetlist) {+ if (!qh_isvertex(qh->GOODvertexp, facet->vertices)) {+ facet->good= False;+ numgood--;+ }+ }+ }+ if (qh->GOODpoint && numgood) {+ FORALLfacet_(facetlist) {+ if (facet->good && facet->normal) {+ zinc_(Zdistgood);+ qh_distplane(qh, qh->GOODpointp, facet, &dist);+ if ((qh->GOODpoint > 0) ^ (dist > 0.0)) {+ facet->good= False;+ numgood--;+ }+ }+ }+ }+ if (qh->GOODthreshold && (numgood || goodhorizon || qh->GOODclosest)) {+ FORALLfacet_(facetlist) {+ if (facet->good && facet->normal) {+ if (!qh_inthresholds(qh, facet->normal, &angle)) {+ facet->good= False;+ numgood--;+ if (angle < bestangle) {+ bestangle= angle;+ bestfacet= facet;+ }+ }+ }+ }+ if (!numgood && (!goodhorizon || qh->GOODclosest)) {+ if (qh->GOODclosest) {+ if (qh->GOODclosest->visible)+ qh->GOODclosest= NULL;+ else {+ qh_inthresholds(qh, qh->GOODclosest->normal, &angle);+ if (angle < bestangle)+ bestfacet= qh->GOODclosest;+ }+ }+ if (bestfacet && bestfacet != qh->GOODclosest) {+ if (qh->GOODclosest)+ qh->GOODclosest->good= False;+ qh->GOODclosest= bestfacet;+ bestfacet->good= True;+ numgood++;+ trace2((qh, qh->ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n",+ bestfacet->id, bestangle));+ return numgood;+ }+ }else if (qh->GOODclosest) { /* numgood > 0 */+ qh->GOODclosest->good= False;+ qh->GOODclosest= NULL;+ }+ }+ zadd_(Zgoodfacet, numgood);+ trace2((qh, qh->ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n",+ numgood, goodhorizon));+ if (!numgood && qh->GOODvertex>0 && !qh->MERGING)+ return goodhorizon;+ return numgood;+} /* findgood */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="findgood_all">-</a>++ qh_findgood_all(qh, facetlist )+ apply other constraints for good facets (used by qh.PRINTgood)+ if qh.GOODvertex+ facet includes (>0) or doesn't include (<0) point as vertex+ if last good facet and ONLYgood, prints warning and continues+ if qh.SPLITthresholds+ facet->normal matches threshold, or if none, the closest one+ calls qh_findgood+ nop if good not used++ returns:+ clears facet->good if not good+ sets qh.num_good++ notes:+ this is like qh_findgood but more restrictive++ design:+ uses qh_findgood to mark good facets+ marks facets for qh.GOODvertex+ marks facets for qh.SPLITthreholds+*/+void qh_findgood_all(qhT *qh, facetT *facetlist) {+ facetT *facet, *bestfacet=NULL;+ realT angle, bestangle= REALmax;+ int numgood=0, startgood;++ if (!qh->GOODvertex && !qh->GOODthreshold && !qh->GOODpoint+ && !qh->SPLITthresholds)+ return;+ if (!qh->ONLYgood)+ qh_findgood(qh, qh->facet_list, 0);+ FORALLfacet_(facetlist) {+ if (facet->good)+ numgood++;+ }+ if (qh->GOODvertex <0 || (qh->GOODvertex > 0 && qh->MERGING)) {+ FORALLfacet_(facetlist) {+ if (facet->good && ((qh->GOODvertex > 0) ^ !!qh_isvertex(qh->GOODvertexp, facet->vertices))) {+ if (!--numgood) {+ if (qh->ONLYgood) {+ qh_fprintf(qh, qh->ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n",+ qh_pointid(qh, qh->GOODvertexp), facet->id);+ return;+ }else if (qh->GOODvertex > 0)+ qh_fprintf(qh, qh->ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n",+ qh->GOODvertex-1, qh->GOODvertex-1);+ else+ qh_fprintf(qh, qh->ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n",+ -qh->GOODvertex - 1, -qh->GOODvertex - 1);+ }+ facet->good= False;+ }+ }+ }+ startgood= numgood;+ if (qh->SPLITthresholds) {+ FORALLfacet_(facetlist) {+ if (facet->good) {+ if (!qh_inthresholds(qh, facet->normal, &angle)) {+ facet->good= False;+ numgood--;+ if (angle < bestangle) {+ bestangle= angle;+ bestfacet= facet;+ }+ }+ }+ }+ if (!numgood && bestfacet) {+ bestfacet->good= True;+ numgood++;+ trace0((qh, qh->ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n",+ bestfacet->id, bestangle));+ return;+ }+ }+ qh->num_good= numgood;+ trace0((qh, qh->ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n",+ numgood, startgood));+} /* findgood_all */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="furthestnext">-</a>++ qh_furthestnext()+ set qh.facet_next to facet with furthest of all furthest points+ searches all facets on qh.facet_list++ notes:+ this may help avoid precision problems+*/+void qh_furthestnext(qhT *qh /* qh->facet_list */) {+ facetT *facet, *bestfacet= NULL;+ realT dist, bestdist= -REALmax;++ FORALLfacets {+ if (facet->outsideset) {+#if qh_COMPUTEfurthest+ pointT *furthest;+ furthest= (pointT*)qh_setlast(facet->outsideset);+ zinc_(Zcomputefurthest);+ qh_distplane(qh, furthest, facet, &dist);+#else+ dist= facet->furthestdist;+#endif+ if (dist > bestdist) {+ bestfacet= facet;+ bestdist= dist;+ }+ }+ }+ if (bestfacet) {+ qh_removefacet(qh, bestfacet);+ qh_prependfacet(qh, bestfacet, &qh->facet_next);+ trace1((qh, qh->ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n",+ bestfacet->id, bestdist));+ }+} /* furthestnext */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="furthestout">-</a>++ qh_furthestout(qh, facet )+ make furthest outside point the last point of outsideset++ returns:+ updates facet->outsideset+ clears facet->notfurthest+ sets facet->furthestdist++ design:+ determine best point of outsideset+ make it the last point of outsideset+*/+void qh_furthestout(qhT *qh, facetT *facet) {+ pointT *point, **pointp, *bestpoint= NULL;+ realT dist, bestdist= -REALmax;++ FOREACHpoint_(facet->outsideset) {+ qh_distplane(qh, point, facet, &dist);+ zinc_(Zcomputefurthest);+ if (dist > bestdist) {+ bestpoint= point;+ bestdist= dist;+ }+ }+ if (bestpoint) {+ qh_setdel(facet->outsideset, point);+ qh_setappend(qh, &facet->outsideset, point);+#if !qh_COMPUTEfurthest+ facet->furthestdist= bestdist;+#endif+ }+ facet->notfurthest= False;+ trace3((qh, qh->ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n",+ qh_pointid(qh, point), facet->id));+} /* furthestout */+++/*-<a href="qh-qhull_r.htm#TOC"+ >-------------------------------</a><a name="infiniteloop">-</a>++ qh_infiniteloop(qh, facet )+ report infinite loop error due to facet+*/+void qh_infiniteloop(qhT *qh, facetT *facet) {++ qh_fprintf(qh, qh->ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n");+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+} /* qh_infiniteloop */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="initbuild">-</a>++ qh_initbuild()+ initialize hull and outside sets with point array+ qh.FIRSTpoint/qh.NUMpoints is point array+ if qh.GOODpoint+ adds qh.GOODpoint to initial hull++ returns:+ qh_facetlist with initial hull+ points partioned into outside sets, coplanar sets, or inside+ initializes qh.GOODpointp, qh.GOODvertexp,++ design:+ initialize global variables used during qh_buildhull+ determine precision constants and points with max/min coordinate values+ if qh.SCALElast, scale last coordinate(for 'd')+ build initial simplex+ partition input points into facets of initial simplex+ set up lists+ if qh.ONLYgood+ check consistency+ add qh.GOODvertex if defined+*/+void qh_initbuild(qhT *qh) {+ setT *maxpoints, *vertices;+ facetT *facet;+ int i, numpart;+ realT dist;+ boolT isoutside;++ qh->furthest_id= qh_IDunknown;+ qh->lastreport= 0;+ qh->facet_id= qh->vertex_id= qh->ridge_id= 0;+ qh->visit_id= qh->vertex_visit= 0;+ qh->maxoutdone= False;++ if (qh->GOODpoint > 0)+ qh->GOODpointp= qh_point(qh, qh->GOODpoint-1);+ else if (qh->GOODpoint < 0)+ qh->GOODpointp= qh_point(qh, -qh->GOODpoint-1);+ if (qh->GOODvertex > 0)+ qh->GOODvertexp= qh_point(qh, qh->GOODvertex-1);+ else if (qh->GOODvertex < 0)+ qh->GOODvertexp= qh_point(qh, -qh->GOODvertex-1);+ if ((qh->GOODpoint+ && (qh->GOODpointp < qh->first_point /* also catches !GOODpointp */+ || qh->GOODpointp > qh_point(qh, qh->num_points-1)))+ || (qh->GOODvertex+ && (qh->GOODvertexp < qh->first_point /* also catches !GOODvertexp */+ || qh->GOODvertexp > qh_point(qh, qh->num_points-1)))) {+ qh_fprintf(qh, qh->ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n",+ qh->num_points-1);+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ maxpoints= qh_maxmin(qh, qh->first_point, qh->num_points, qh->hull_dim);+ if (qh->SCALElast)+ qh_scalelast(qh, qh->first_point, qh->num_points, qh->hull_dim,+ qh->MINlastcoord, qh->MAXlastcoord, qh->MAXwidth);+ qh_detroundoff(qh);+ if (qh->DELAUNAY && qh->upper_threshold[qh->hull_dim-1] > REALmax/2+ && qh->lower_threshold[qh->hull_dim-1] < -REALmax/2) {+ for (i=qh_PRINTEND; i--; ) {+ if (qh->PRINTout[i] == qh_PRINTgeom && qh->DROPdim < 0+ && !qh->GOODthreshold && !qh->SPLITthresholds)+ break; /* in this case, don't set upper_threshold */+ }+ if (i < 0) {+ if (qh->UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */+ qh->lower_threshold[qh->hull_dim-1]= qh->ANGLEround * qh_ZEROdelaunay;+ qh->GOODthreshold= True;+ }else {+ qh->upper_threshold[qh->hull_dim-1]= -qh->ANGLEround * qh_ZEROdelaunay;+ if (!qh->GOODthreshold)+ qh->SPLITthresholds= True; /* build upper-convex hull even if Qg */+ /* qh_initqhull_globals errors if Qg without Pdk/etc. */+ }+ }+ }+ vertices= qh_initialvertices(qh, qh->hull_dim, maxpoints, qh->first_point, qh->num_points);+ qh_initialhull(qh, vertices); /* initial qh->facet_list */+ qh_partitionall(qh, vertices, qh->first_point, qh->num_points);+ if (qh->PRINToptions1st || qh->TRACElevel || qh->IStracing) {+ if (qh->TRACElevel || qh->IStracing)+ qh_fprintf(qh, qh->ferr, 8103, "\nTrace level %d for %s | %s\n",+ qh->IStracing ? qh->IStracing : qh->TRACElevel, qh->rbox_command, qh->qhull_command);+ qh_fprintf(qh, qh->ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);+ }+ qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);+ qh->facet_next= qh->facet_list;+ qh_furthestnext(qh /* qh->facet_list */);+ if (qh->PREmerge) {+ qh->cos_max= qh->premerge_cos;+ qh->centrum_radius= qh->premerge_centrum;+ }+ if (qh->ONLYgood) {+ if (qh->GOODvertex > 0 && qh->MERGING) {+ qh_fprintf(qh, qh->ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (!(qh->GOODthreshold || qh->GOODpoint+ || (!qh->MERGEexact && !qh->PREmerge && qh->GOODvertexp))) {+ qh_fprintf(qh, qh->ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\+good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (qh->GOODvertex > 0 && !qh->MERGING /* matches qh_partitionall */+ && !qh_isvertex(qh->GOODvertexp, vertices)) {+ facet= qh_findbestnew(qh, qh->GOODvertexp, qh->facet_list,+ &dist, !qh_ALL, &isoutside, &numpart);+ zadd_(Zdistgood, numpart);+ if (!isoutside) {+ qh_fprintf(qh, qh->ferr, 6153, "qhull input error: point for QV%d is inside initial simplex. It can not be made a vertex.\n",+ qh_pointid(qh, qh->GOODvertexp));+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ if (!qh_addpoint(qh, qh->GOODvertexp, facet, False)) {+ qh_settempfree(qh, &vertices);+ qh_settempfree(qh, &maxpoints);+ return;+ }+ }+ qh_findgood(qh, qh->facet_list, 0);+ }+ qh_settempfree(qh, &vertices);+ qh_settempfree(qh, &maxpoints);+ trace1((qh, qh->ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n"));+} /* initbuild */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="initialhull">-</a>++ qh_initialhull(qh, vertices )+ constructs the initial hull as a DIM3 simplex of vertices++ design:+ creates a simplex (initializes lists)+ determines orientation of simplex+ sets hyperplanes for facets+ doubles checks orientation (in case of axis-parallel facets with Gaussian elimination)+ checks for flipped facets and qh.NARROWhull+ checks the result+*/+void qh_initialhull(qhT *qh, setT *vertices) {+ facetT *facet, *firstfacet, *neighbor, **neighborp;+ realT dist, angle, minangle= REALmax;+#ifndef qh_NOtrace+ int k;+#endif++ qh_createsimplex(qh, vertices); /* qh->facet_list */+ qh_resetlists(qh, False, qh_RESETvisible);+ qh->facet_next= qh->facet_list; /* advance facet when processed */+ qh->interior_point= qh_getcenter(qh, vertices);+ firstfacet= qh->facet_list;+ qh_setfacetplane(qh, firstfacet);+ zinc_(Znumvisibility); /* needs to be in printsummary */+ qh_distplane(qh, qh->interior_point, firstfacet, &dist);+ if (dist > 0) {+ FORALLfacets+ facet->toporient ^= (unsigned char)True;+ }+ FORALLfacets+ qh_setfacetplane(qh, facet);+ FORALLfacets {+ if (!qh_checkflipped(qh, facet, NULL, qh_ALL)) {/* due to axis-parallel facet */+ trace1((qh, qh->ferr, 1031, "qh_initialhull: initial orientation incorrect. Correct all facets\n"));+ facet->flipped= False;+ FORALLfacets {+ facet->toporient ^= (unsigned char)True;+ qh_orientoutside(qh, facet);+ }+ break;+ }+ }+ FORALLfacets {+ if (!qh_checkflipped(qh, facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */+ if (qh->DELAUNAY && ! qh->ATinfinity) {+ if (qh->UPPERdelaunay)+ qh_fprintf(qh, qh->ferr, 6240, "Qhull precision error: Initial simplex is cocircular or cospherical. Option 'Qs' searches all points. Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n");+ else+ qh_fprintf(qh, qh->ferr, 6239, "Qhull precision error: Initial simplex is cocircular or cospherical. Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points. Option 'Qz' adds a point \"at infinity\". Use option 'Qs' to search all points for the initial simplex.\n");+ qh_errexit(qh, qh_ERRinput, NULL, NULL);+ }+ qh_precision(qh, "initial simplex is flat");+ qh_fprintf(qh, qh->ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n",+ facet->id);+ qh_errexit(qh, qh_ERRsingular, NULL, NULL); /* calls qh_printhelp_singular */+ }+ FOREACHneighbor_(facet) {+ angle= qh_getangle(qh, facet->normal, neighbor->normal);+ minimize_( minangle, angle);+ }+ }+ if (minangle < qh_MAXnarrow && !qh->NOnarrow) {+ realT diff= 1.0 + minangle;++ qh->NARROWhull= True;+ qh_option(qh, "_narrow-hull", NULL, &diff);+ if (minangle < qh_WARNnarrow && !qh->RERUN && qh->PRINTprecision)+ qh_printhelp_narrowhull(qh, qh->ferr, minangle);+ }+ zzval_(Zprocessed)= qh->hull_dim+1;+ qh_checkpolygon(qh, qh->facet_list);+ qh_checkconvex(qh, qh->facet_list, qh_DATAfault);+#ifndef qh_NOtrace+ if (qh->IStracing >= 1) {+ qh_fprintf(qh, qh->ferr, 8105, "qh_initialhull: simplex constructed, interior point:");+ for (k=0; k < qh->hull_dim; k++)+ qh_fprintf(qh, qh->ferr, 8106, " %6.4g", qh->interior_point[k]);+ qh_fprintf(qh, qh->ferr, 8107, "\n");+ }+#endif+} /* initialhull */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="initialvertices">-</a>++ qh_initialvertices(qh, dim, maxpoints, points, numpoints )+ determines a non-singular set of initial vertices+ maxpoints may include duplicate points++ returns:+ temporary set of dim+1 vertices in descending order by vertex id+ if qh.RANDOMoutside && !qh.ALLpoints+ picks random points+ if dim >= qh_INITIALmax,+ uses min/max x and max points with non-zero determinants++ notes:+ unless qh.ALLpoints,+ uses maxpoints as long as determinate is non-zero+*/+setT *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints) {+ pointT *point, **pointp;+ setT *vertices, *simplex, *tested;+ realT randr;+ int idx, point_i, point_n, k;+ boolT nearzero= False;++ vertices= qh_settemp(qh, dim + 1);+ simplex= qh_settemp(qh, dim+1);+ if (qh->ALLpoints)+ qh_maxsimplex(qh, dim, NULL, points, numpoints, &simplex);+ else if (qh->RANDOMoutside) {+ while (qh_setsize(qh, simplex) != dim+1) {+ randr= qh_RANDOMint;+ randr= randr/(qh_RANDOMmax+1);+ idx= (int)floor(qh->num_points * randr);+ while (qh_setin(simplex, qh_point(qh, idx))) {+ idx++; /* in case qh_RANDOMint always returns the same value */+ idx= idx < qh->num_points ? idx : 0;+ }+ qh_setappend(qh, &simplex, qh_point(qh, idx));+ }+ }else if (qh->hull_dim >= qh_INITIALmax) {+ tested= qh_settemp(qh, dim+1);+ qh_setappend(qh, &simplex, SETfirst_(maxpoints)); /* max and min X coord */+ qh_setappend(qh, &simplex, SETsecond_(maxpoints));+ qh_maxsimplex(qh, fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex);+ k= qh_setsize(qh, simplex);+ FOREACHpoint_i_(qh, maxpoints) {+ if (point_i & 0x1) { /* first pick up max. coord. points */+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){+ qh_detsimplex(qh, point, simplex, k, &nearzero);+ if (nearzero)+ qh_setappend(qh, &tested, point);+ else {+ qh_setappend(qh, &simplex, point);+ if (++k == dim) /* use search for last point */+ break;+ }+ }+ }+ }+ while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) {+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){+ qh_detsimplex(qh, point, simplex, k, &nearzero);+ if (nearzero)+ qh_setappend(qh, &tested, point);+ else {+ qh_setappend(qh, &simplex, point);+ k++;+ }+ }+ }+ idx= 0;+ while (k != dim && (point= qh_point(qh, idx++))) {+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){+ qh_detsimplex(qh, point, simplex, k, &nearzero);+ if (!nearzero){+ qh_setappend(qh, &simplex, point);+ k++;+ }+ }+ }+ qh_settempfree(qh, &tested);+ qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex);+ }else+ qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex);+ FOREACHpoint_(simplex)+ qh_setaddnth(qh, &vertices, 0, qh_newvertex(qh, point)); /* descending order */+ qh_settempfree(qh, &simplex);+ return vertices;+} /* initialvertices */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="isvertex">-</a>++ qh_isvertex( point, vertices )+ returns vertex if point is in vertex set, else returns NULL++ notes:+ for qh.GOODvertex+*/+vertexT *qh_isvertex(pointT *point, setT *vertices) {+ vertexT *vertex, **vertexp;++ FOREACHvertex_(vertices) {+ if (vertex->point == point)+ return vertex;+ }+ return NULL;+} /* isvertex */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="makenewfacets">-</a>++ qh_makenewfacets(qh, point )+ make new facets from point and qh.visible_list++ returns:+ qh.newfacet_list= list of new facets with hyperplanes and ->newfacet+ qh.newvertex_list= list of vertices in new facets with ->newlist set++ if (qh.ONLYgood)+ newfacets reference horizon facets, but not vice versa+ ridges reference non-simplicial horizon ridges, but not vice versa+ does not change existing facets+ else+ sets qh.NEWfacets+ new facets attached to horizon facets and ridges+ for visible facets,+ visible->r.replace is corresponding new facet++ see also:+ qh_makenewplanes() -- make hyperplanes for facets+ qh_attachnewfacets() -- attachnewfacets if not done here(qh->ONLYgood)+ qh_matchnewfacets() -- match up neighbors+ qh_updatevertices() -- update vertex neighbors and delvertices+ qh_deletevisible() -- delete visible facets+ qh_checkpolygon() --check the result+ qh_triangulate() -- triangulate a non-simplicial facet++ design:+ for each visible facet+ make new facets to its horizon facets+ update its f.replace+ clear its neighbor set+*/+vertexT *qh_makenewfacets(qhT *qh, pointT *point /*visible_list*/) {+ facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp;+ vertexT *apex;+ int numnew=0;++ qh->newfacet_list= qh->facet_tail;+ qh->newvertex_list= qh->vertex_tail;+ apex= qh_newvertex(qh, point);+ qh_appendvertex(qh, apex);+ qh->visit_id++;+ if (!qh->ONLYgood)+ qh->NEWfacets= True;+ FORALLvisible_facets {+ FOREACHneighbor_(visible)+ neighbor->seen= False;+ if (visible->ridges) {+ visible->visitid= qh->visit_id;+ newfacet2= qh_makenew_nonsimplicial(qh, visible, apex, &numnew);+ }+ if (visible->simplicial)+ newfacet= qh_makenew_simplicial(qh, visible, apex, &numnew);+ if (!qh->ONLYgood) {+ if (newfacet2) /* newfacet is null if all ridges defined */+ newfacet= newfacet2;+ if (newfacet)+ visible->f.replace= newfacet;+ else+ zinc_(Zinsidevisible);+ SETfirst_(visible->neighbors)= NULL;+ }+ }+ trace1((qh, qh->ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n",+ numnew, qh_pointid(qh, point)));+ if (qh->IStracing >= 4)+ qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL);+ return apex;+} /* makenewfacets */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="matchduplicates">-</a>++ qh_matchduplicates(qh, atfacet, atskip, hashsize, hashcount )+ match duplicate ridges in qh.hash_table for atfacet/atskip+ duplicates marked with ->dupridge and qh_DUPLICATEridge++ returns:+ picks match with worst merge (min distance apart)+ updates hashcount++ see also:+ qh_matchneighbor++ notes:++ design:+ compute hash value for atfacet and atskip+ repeat twice -- once to make best matches, once to match the rest+ for each possible facet in qh.hash_table+ if it is a matching facet and pass 2+ make match+ unless tricoplanar, mark match for merging (qh_MERGEridge)+ [e.g., tricoplanar RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Qt]+ if it is a matching facet and pass 1+ test if this is a better match+ if pass 1,+ make best match (it will not be merged)+*/+#ifndef qh_NOmerge+void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) {+ boolT same, ismatch;+ int hash, scan;+ facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet;+ int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch;+ realT maxdist= -REALmax, mindist, dist2, low, high;++ hash= qh_gethash(qh, hashsize, atfacet->vertices, qh->hull_dim, 1,+ SETelem_(atfacet->vertices, atskip));+ trace2((qh, qh->ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n",+ atfacet->id, atskip, hash, *hashcount));+ for (makematch= 0; makematch < 2; makematch++) {+ qh->visit_id++;+ for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) {+ zinc_(Zhashlookup);+ nextfacet= NULL;+ newfacet->visitid= qh->visit_id;+ for (scan= hash; (facet= SETelemt_(qh->hash_table, scan, facetT));+ scan= (++scan >= hashsize ? 0 : scan)) {+ if (!facet->dupridge || facet->visitid == qh->visit_id)+ continue;+ zinc_(Zhashtests);+ if (qh_matchvertices(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {+ ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient));+ if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) {+ if (!makematch) {+ qh_fprintf(qh, qh->ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n",+ facet->id, skip, newfacet->id, newskip, hash);+ qh_errexit2(qh, qh_ERRqhull, facet, newfacet);+ }+ }else if (ismatch && makematch) {+ if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) {+ SETelem_(facet->neighbors, skip)= newfacet;+ if (newfacet->tricoplanar)+ SETelem_(newfacet->neighbors, newskip)= facet;+ else+ SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge;+ *hashcount -= 2; /* removed two unmatched facets */+ trace4((qh, qh->ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n",+ facet->id, skip, newfacet->id, newskip));+ }+ }else if (ismatch) {+ mindist= qh_getdistance(qh, facet, newfacet, &low, &high);+ dist2= qh_getdistance(qh, newfacet, facet, &low, &high);+ minimize_(mindist, dist2);+ if (mindist > maxdist) {+ maxdist= mindist;+ maxmatch= facet;+ maxskip= skip;+ maxmatch2= newfacet;+ maxskip2= newskip;+ }+ trace3((qh, qh->ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n",+ facet->id, skip, newfacet->id, newskip, mindist,+ maxmatch->id, maxmatch2->id));+ }else { /* !ismatch */+ nextfacet= facet;+ nextskip= skip;+ }+ }+ if (makematch && !facet+ && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) {+ qh_fprintf(qh, qh->ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n",+ newfacet->id, newskip, hash);+ qh_errexit(qh, qh_ERRqhull, newfacet, NULL);+ }+ }+ } /* end of for each new facet at hash */+ if (!makematch) {+ if (!maxmatch) {+ qh_fprintf(qh, qh->ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n",+ atfacet->id, atskip, hash);+ qh_errexit(qh, qh_ERRqhull, atfacet, NULL);+ }+ SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; /* maxmatch!=0 by QH6157 */+ SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch;+ *hashcount -= 2; /* removed two unmatched facets */+ zzinc_(Zmultiridge);+ trace0((qh, qh->ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n",+ maxmatch->id, maxskip, maxmatch2->id, maxskip2));+ qh_precision(qh, "ridge with multiple neighbors");+ if (qh->IStracing >= 4)+ qh_errprint(qh, "DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL);+ }+ }+} /* matchduplicates */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="nearcoplanar">-</a>++ qh_nearcoplanar()+ for all facets, remove near-inside points from facet->coplanarset</li>+ coplanar points defined by innerplane from qh_outerinner()++ returns:+ if qh->KEEPcoplanar && !qh->KEEPinside+ facet->coplanarset only contains coplanar points+ if qh.JOGGLEmax+ drops inner plane by another qh.JOGGLEmax diagonal since a+ vertex could shift out while a coplanar point shifts in++ notes:+ used for qh.PREmerge and qh.JOGGLEmax+ must agree with computation of qh.NEARcoplanar in qh_detroundoff(qh)+ design:+ if not keeping coplanar or inside points+ free all coplanar sets+ else if not keeping both coplanar and inside points+ remove !coplanar or !inside points from coplanar sets+*/+void qh_nearcoplanar(qhT *qh /* qh.facet_list */) {+ facetT *facet;+ pointT *point, **pointp;+ int numpart;+ realT dist, innerplane;++ if (!qh->KEEPcoplanar && !qh->KEEPinside) {+ FORALLfacets {+ if (facet->coplanarset)+ qh_setfree(qh, &facet->coplanarset);+ }+ }else if (!qh->KEEPcoplanar || !qh->KEEPinside) {+ qh_outerinner(qh, NULL, NULL, &innerplane);+ if (qh->JOGGLEmax < REALmax/2)+ innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);+ numpart= 0;+ FORALLfacets {+ if (facet->coplanarset) {+ FOREACHpoint_(facet->coplanarset) {+ numpart++;+ qh_distplane(qh, point, facet, &dist);+ if (dist < innerplane) {+ if (!qh->KEEPinside)+ SETref_(point)= NULL;+ }else if (!qh->KEEPcoplanar)+ SETref_(point)= NULL;+ }+ qh_setcompact(qh, facet->coplanarset);+ }+ }+ zzadd_(Zcheckpart, numpart);+ }+} /* nearcoplanar */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="nearvertex">-</a>++ qh_nearvertex(qh, facet, point, bestdist )+ return nearest vertex in facet to point++ returns:+ vertex and its distance++ notes:+ if qh.DELAUNAY+ distance is measured in the input set+ searches neighboring tricoplanar facets (requires vertexneighbors)+ Slow implementation. Recomputes vertex set for each point.+ The vertex set could be stored in the qh.keepcentrum facet.+*/+vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp) {+ realT bestdist= REALmax, dist;+ vertexT *bestvertex= NULL, *vertex, **vertexp, *apex;+ coordT *center;+ facetT *neighbor, **neighborp;+ setT *vertices;+ int dim= qh->hull_dim;++ if (qh->DELAUNAY)+ dim--;+ if (facet->tricoplanar) {+ if (!qh->VERTEXneighbors || !facet->center) {+ qh_fprintf(qh, qh->ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n");+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ vertices= qh_settemp(qh, qh->TEMPsize);+ apex= SETfirstt_(facet->vertices, vertexT);+ center= facet->center;+ FOREACHneighbor_(apex) {+ if (neighbor->center == center) {+ FOREACHvertex_(neighbor->vertices)+ qh_setappend(qh, &vertices, vertex);+ }+ }+ }else+ vertices= facet->vertices;+ FOREACHvertex_(vertices) {+ dist= qh_pointdist(vertex->point, point, -dim);+ if (dist < bestdist) {+ bestdist= dist;+ bestvertex= vertex;+ }+ }+ if (facet->tricoplanar)+ qh_settempfree(qh, &vertices);+ *bestdistp= sqrt(bestdist);+ if (!bestvertex) {+ qh_fprintf(qh, qh->ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(qh, point));+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ trace3((qh, qh->ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n",+ bestvertex->id, *bestdistp, facet->id, qh_pointid(qh, point))); /* bestvertex!=0 by QH2161 */+ return bestvertex;+} /* nearvertex */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="newhashtable">-</a>++ qh_newhashtable(qh, newsize )+ returns size of qh.hash_table of at least newsize slots++ notes:+ assumes qh.hash_table is NULL+ qh_HASHfactor determines the number of extra slots+ size is not divisible by 2, 3, or 5+*/+int qh_newhashtable(qhT *qh, int newsize) {+ int size;++ size= ((newsize+1)*qh_HASHfactor) | 0x1; /* odd number */+ while (True) {+ if (newsize<0 || size<0) {+ qh_fprintf(qh, qh->qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d). Did int overflow due to high-D?\n", newsize, size); /* WARN64 */+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);+ }+ if ((size%3) && (size%5))+ break;+ size += 2;+ /* loop terminates because there is an infinite number of primes */+ }+ qh->hash_table= qh_setnew(qh, size);+ qh_setzero(qh, qh->hash_table, 0, size);+ return size;+} /* newhashtable */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="newvertex">-</a>++ qh_newvertex(qh, point )+ returns a new vertex for point+*/+vertexT *qh_newvertex(qhT *qh, pointT *point) {+ vertexT *vertex;++ zinc_(Ztotvertices);+ vertex= (vertexT *)qh_memalloc(qh, (int)sizeof(vertexT));+ memset((char *) vertex, (size_t)0, sizeof(vertexT));+ if (qh->vertex_id == UINT_MAX) {+ qh_memfree(qh, vertex, (int)sizeof(vertexT));+ qh_fprintf(qh, qh->ferr, 6159, "qhull error: more than 2^32 vertices. vertexT.id field overflows. Vertices would not be sorted correctly.\n");+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+ if (qh->vertex_id == qh->tracevertex_id)+ qh->tracevertex= vertex;+ vertex->id= qh->vertex_id++;+ vertex->point= point;+ trace4((qh, qh->ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(qh, vertex->point),+ vertex->id));+ return(vertex);+} /* newvertex */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="nextridge3d">-</a>++ qh_nextridge3d( atridge, facet, vertex )+ return next ridge and vertex for a 3d facet+ returns NULL on error+ [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qhT.++ notes:+ in qh_ORIENTclock order+ this is a O(n^2) implementation to trace all ridges+ be sure to stop on any 2nd visit+ same as QhullRidge::nextRidge3d+ does not use qhT or qh_errexit [QhullFacet.cpp]++ design:+ for each ridge+ exit if it is the ridge after atridge+*/+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {+ vertexT *atvertex, *vertex, *othervertex;+ ridgeT *ridge, **ridgep;++ if ((atridge->top == facet) ^ qh_ORIENTclock)+ atvertex= SETsecondt_(atridge->vertices, vertexT);+ else+ atvertex= SETfirstt_(atridge->vertices, vertexT);+ FOREACHridge_(facet->ridges) {+ if (ridge == atridge)+ continue;+ if ((ridge->top == facet) ^ qh_ORIENTclock) {+ othervertex= SETsecondt_(ridge->vertices, vertexT);+ vertex= SETfirstt_(ridge->vertices, vertexT);+ }else {+ vertex= SETsecondt_(ridge->vertices, vertexT);+ othervertex= SETfirstt_(ridge->vertices, vertexT);+ }+ if (vertex == atvertex) {+ if (vertexp)+ *vertexp= othervertex;+ return ridge;+ }+ }+ return NULL;+} /* nextridge3d */+#else /* qh_NOmerge */+void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) {+}+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {++ return NULL;+}+#endif /* qh_NOmerge */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="outcoplanar">-</a>++ qh_outcoplanar()+ move points from all facets' outsidesets to their coplanarsets++ notes:+ for post-processing under qh.NARROWhull++ design:+ for each facet+ for each outside point for facet+ partition point into coplanar set+*/+void qh_outcoplanar(qhT *qh /* facet_list */) {+ pointT *point, **pointp;+ facetT *facet;+ realT dist;++ trace1((qh, qh->ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh->NARROWhull\n"));+ FORALLfacets {+ FOREACHpoint_(facet->outsideset) {+ qh->num_outside--;+ if (qh->KEEPcoplanar || qh->KEEPnearinside) {+ qh_distplane(qh, point, facet, &dist);+ zinc_(Zpartition);+ qh_partitioncoplanar(qh, point, facet, &dist);+ }+ }+ qh_setfree(qh, &facet->outsideset);+ }+} /* outcoplanar */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="point">-</a>++ qh_point(qh, id )+ return point for a point id, or NULL if unknown++ alternative code:+ return((pointT *)((unsigned long)qh.first_point+ + (unsigned long)((id)*qh.normal_size)));+*/+pointT *qh_point(qhT *qh, int id) {++ if (id < 0)+ return NULL;+ if (id < qh->num_points)+ return qh->first_point + id * qh->hull_dim;+ id -= qh->num_points;+ if (id < qh_setsize(qh, qh->other_points))+ return SETelemt_(qh->other_points, id, pointT);+ return NULL;+} /* point */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="point_add">-</a>++ qh_point_add(qh, set, point, elem )+ stores elem at set[point.id]++ returns:+ access function for qh_pointfacet and qh_pointvertex++ notes:+ checks point.id+*/+void qh_point_add(qhT *qh, setT *set, pointT *point, void *elem) {+ int id, size;++ SETreturnsize_(set, size);+ if ((id= qh_pointid(qh, point)) < 0)+ qh_fprintf(qh, qh->ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n",+ point, id);+ else if (id >= size) {+ qh_fprintf(qh, qh->ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n",+ id, size);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }else+ SETelem_(set, id)= elem;+} /* point_add */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="pointfacet">-</a>++ qh_pointfacet()+ return temporary set of facet for each point+ the set is indexed by point id++ notes:+ vertices assigned to one of the facets+ coplanarset assigned to the facet+ outside set assigned to the facet+ NULL if no facet for point (inside)+ includes qh.GOODpointp++ access:+ FOREACHfacet_i_(qh, facets) { ... }+ SETelem_(facets, i)++ design:+ for each facet+ add each vertex+ add each coplanar point+ add each outside point+*/+setT *qh_pointfacet(qhT *qh /*qh.facet_list*/) {+ int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);+ setT *facets;+ facetT *facet;+ vertexT *vertex, **vertexp;+ pointT *point, **pointp;++ facets= qh_settemp(qh, numpoints);+ qh_setzero(qh, facets, 0, numpoints);+ qh->vertex_visit++;+ FORALLfacets {+ FOREACHvertex_(facet->vertices) {+ if (vertex->visitid != qh->vertex_visit) {+ vertex->visitid= qh->vertex_visit;+ qh_point_add(qh, facets, vertex->point, facet);+ }+ }+ FOREACHpoint_(facet->coplanarset)+ qh_point_add(qh, facets, point, facet);+ FOREACHpoint_(facet->outsideset)+ qh_point_add(qh, facets, point, facet);+ }+ return facets;+} /* pointfacet */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="pointvertex">-</a>++ qh_pointvertex(qh, )+ return temporary set of vertices indexed by point id+ entry is NULL if no vertex for a point+ this will include qh.GOODpointp++ access:+ FOREACHvertex_i_(qh, vertices) { ... }+ SETelem_(vertices, i)+*/+setT *qh_pointvertex(qhT *qh /*qh.facet_list*/) {+ int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);+ setT *vertices;+ vertexT *vertex;++ vertices= qh_settemp(qh, numpoints);+ qh_setzero(qh, vertices, 0, numpoints);+ FORALLvertices+ qh_point_add(qh, vertices, vertex->point, vertex);+ return vertices;+} /* pointvertex */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="prependfacet">-</a>++ qh_prependfacet(qh, facet, facetlist )+ prepend facet to the start of a facetlist++ returns:+ increments qh.numfacets+ updates facetlist, qh.facet_list, facet_next++ notes:+ be careful of prepending since it can lose a pointer.+ e.g., can lose _next by deleting and then prepending before _next+*/+void qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist) {+ facetT *prevfacet, *list;+++ trace4((qh, qh->ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n",+ facet->id, getid_(*facetlist)));+ if (!*facetlist)+ (*facetlist)= qh->facet_tail;+ list= *facetlist;+ prevfacet= list->previous;+ facet->previous= prevfacet;+ if (prevfacet)+ prevfacet->next= facet;+ list->previous= facet;+ facet->next= *facetlist;+ if (qh->facet_list == list) /* this may change *facetlist */+ qh->facet_list= facet;+ if (qh->facet_next == list)+ qh->facet_next= facet;+ *facetlist= facet;+ qh->num_facets++;+} /* prependfacet */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="printhashtable">-</a>++ qh_printhashtable(qh, fp )+ print hash table to fp++ notes:+ not in I/O to avoid bringing io_r.c in++ design:+ for each hash entry+ if defined+ if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge)+ print entry and neighbors+*/+void qh_printhashtable(qhT *qh, FILE *fp) {+ facetT *facet, *neighbor;+ int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0;+ vertexT *vertex, **vertexp;++ FOREACHfacet_i_(qh, qh->hash_table) {+ if (facet) {+ FOREACHneighbor_i_(qh, facet) {+ if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge)+ break;+ }+ if (neighbor_i == neighbor_n)+ continue;+ qh_fprintf(qh, fp, 9283, "hash %d f%d ", facet_i, facet->id);+ FOREACHvertex_(facet->vertices)+ qh_fprintf(qh, fp, 9284, "v%d ", vertex->id);+ qh_fprintf(qh, fp, 9285, "\n neighbors:");+ FOREACHneighbor_i_(qh, facet) {+ if (neighbor == qh_MERGEridge)+ id= -3;+ else if (neighbor == qh_DUPLICATEridge)+ id= -2;+ else+ id= getid_(neighbor);+ qh_fprintf(qh, fp, 9286, " %d", id);+ }+ qh_fprintf(qh, fp, 9287, "\n");+ }+ }+} /* printhashtable */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="printlists">-</a>++ qh_printlists(qh, fp )+ print out facet and vertex list for debugging (without 'f/v' tags)+*/+void qh_printlists(qhT *qh) {+ facetT *facet;+ vertexT *vertex;+ int count= 0;++ qh_fprintf(qh, qh->ferr, 8108, "qh_printlists: facets:");+ FORALLfacets {+ if (++count % 100 == 0)+ qh_fprintf(qh, qh->ferr, 8109, "\n ");+ qh_fprintf(qh, qh->ferr, 8110, " %d", facet->id);+ }+ qh_fprintf(qh, qh->ferr, 8111, "\n new facets %d visible facets %d next facet for qh_addpoint %d\n vertices(new %d):",+ getid_(qh->newfacet_list), getid_(qh->visible_list), getid_(qh->facet_next),+ getid_(qh->newvertex_list));+ count = 0;+ FORALLvertices {+ if (++count % 100 == 0)+ qh_fprintf(qh, qh->ferr, 8112, "\n ");+ qh_fprintf(qh, qh->ferr, 8113, " %d", vertex->id);+ }+ qh_fprintf(qh, qh->ferr, 8114, "\n");+} /* printlists */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="resetlists">-</a>++ qh_resetlists(qh, stats, qh_RESETvisible )+ reset newvertex_list, newfacet_list, visible_list+ if stats,+ maintains statistics++ returns:+ visible_list is empty if qh_deletevisible was called+*/+void qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_list visible_list*/) {+ vertexT *vertex;+ facetT *newfacet, *visible;+ int totnew=0, totver=0;++ if (stats) {+ FORALLvertex_(qh->newvertex_list)+ totver++;+ FORALLnew_facets+ totnew++;+ zadd_(Zvisvertextot, totver);+ zmax_(Zvisvertexmax, totver);+ zadd_(Znewfacettot, totnew);+ zmax_(Znewfacetmax, totnew);+ }+ FORALLvertex_(qh->newvertex_list)+ vertex->newlist= False;+ qh->newvertex_list= NULL;+ FORALLnew_facets+ newfacet->newfacet= False;+ qh->newfacet_list= NULL;+ if (resetVisible) {+ FORALLvisible_facets {+ visible->f.replace= NULL;+ visible->visible= False;+ }+ qh->num_visible= 0;+ }+ qh->visible_list= NULL; /* may still have visible facets via qh_triangulate */+ qh->NEWfacets= False;+} /* resetlists */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="setvoronoi_all">-</a>++ qh_setvoronoi_all(qh)+ compute Voronoi centers for all facets+ includes upperDelaunay facets if qh.UPPERdelaunay ('Qu')++ returns:+ facet->center is the Voronoi center++ notes:+ this is unused/untested code+ please email bradb@shore.net if this works ok for you++ use:+ FORALLvertices {...} to locate the vertex for a point.+ FOREACHneighbor_(vertex) {...} to visit the Voronoi centers for a Voronoi cell.+*/+void qh_setvoronoi_all(qhT *qh) {+ facetT *facet;++ qh_clearcenters(qh, qh_ASvoronoi);+ qh_vertexneighbors(qh);++ FORALLfacets {+ if (!facet->normal || !facet->upperdelaunay || qh->UPPERdelaunay) {+ if (!facet->center)+ facet->center= qh_facetcenter(qh, facet->vertices);+ }+ }+} /* setvoronoi_all */++#ifndef qh_NOmerge++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="triangulate">-</a>++ qh_triangulate()+ triangulate non-simplicial facets on qh.facet_list,+ if qh->VORONOI, sets Voronoi centers of non-simplicial facets+ nop if hasTriangulation++ returns:+ all facets simplicial+ each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc.++ notes:+ call after qh_check_output since may switch to Voronoi centers+ Output may overwrite ->f.triowner with ->f.area+*/+void qh_triangulate(qhT *qh /*qh.facet_list*/) {+ facetT *facet, *nextfacet, *owner;+ int onlygood= qh->ONLYgood;+ facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL;+ facetT *orig_neighbor= NULL, *otherfacet;+ vertexT *new_vertex_list= NULL;+ mergeT *merge;+ mergeType mergetype;+ int neighbor_i, neighbor_n;++ if (qh->hasTriangulation)+ return;+ trace1((qh, qh->ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n"));+ if (qh->hull_dim == 2)+ return;+ if (qh->VORONOI) { /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */+ qh_clearcenters(qh, qh_ASvoronoi);+ qh_vertexneighbors(qh);+ }+ qh->ONLYgood= False; /* for makenew_nonsimplicial */+ qh->visit_id++;+ qh->NEWfacets= True;+ qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);+ qh->newvertex_list= qh->vertex_tail;+ for (facet= qh->facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */+ nextfacet= facet->next;+ if (facet->visible || facet->simplicial)+ continue;+ /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */+ if (!new_facet_list)+ new_facet_list= facet; /* will be moved to end */+ qh_triangulate_facet(qh, facet, &new_vertex_list);+ }+ trace2((qh, qh->ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list)));+ for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */+ nextfacet= facet->next;+ if (facet->visible)+ continue;+ if (facet->ridges) {+ if (qh_setsize(qh, facet->ridges) > 0) {+ qh_fprintf(qh, qh->ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id);+ qh_errexit(qh, qh_ERRqhull, facet, NULL);+ }+ qh_setfree(qh, &facet->ridges);+ }+ if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) {+ zinc_(Ztrinull);+ qh_triangulate_null(qh, facet);+ }+ }+ trace2((qh, qh->ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh, qh->degen_mergeset)));+ qh->visible_list= qh->facet_tail;+ while ((merge= (mergeT*)qh_setdellast(qh->degen_mergeset))) {+ facet1= merge->facet1;+ facet2= merge->facet2;+ mergetype= merge->type;+ qh_memfree(qh, merge, (int)sizeof(mergeT));+ if (mergetype == MRGmirror) {+ zinc_(Ztrimirror);+ qh_triangulate_mirror(qh, facet1, facet2);+ }+ }+ qh_settempfree(qh, &qh->degen_mergeset);+ trace2((qh, qh->ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list)));+ qh->newvertex_list= new_vertex_list; /* all vertices of new facets */+ qh->visible_list= NULL;+ qh_updatevertices(qh /*qh.newvertex_list, empty newfacet_list and visible_list*/);+ qh_resetlists(qh, False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/);++ trace2((qh, qh->ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list)));+ trace2((qh, qh->ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n"));+ FORALLfacet_(new_facet_list) {+ if (facet->tricoplanar && !facet->visible) {+ FOREACHneighbor_i_(qh, facet) {+ if (neighbor_i == 0) { /* first iteration */+ if (neighbor->tricoplanar)+ orig_neighbor= neighbor->f.triowner;+ else+ orig_neighbor= neighbor;+ }else {+ if (neighbor->tricoplanar)+ otherfacet= neighbor->f.triowner;+ else+ otherfacet= neighbor;+ if (orig_neighbor == otherfacet) {+ zinc_(Ztridegen);+ facet->degenerate= True;+ break;+ }+ }+ }+ }+ }++ trace2((qh, qh->ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n"));+ owner= NULL;+ visible= NULL;+ for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */+ nextfacet= facet->next;+ if (facet->visible) {+ if (facet->tricoplanar) { /* a null or mirrored facet */+ qh_delfacet(qh, facet);+ qh->num_visible--;+ }else { /* a non-simplicial facet followed by its tricoplanars */+ if (visible && !owner) {+ /* RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */+ trace2((qh, qh->ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n",+ visible->id));+ qh_delfacet(qh, visible);+ qh->num_visible--;+ }+ visible= facet;+ owner= NULL;+ }+ }else if (facet->tricoplanar) {+ if (facet->f.triowner != visible || visible==NULL) {+ qh_fprintf(qh, qh->ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible));+ qh_errexit2(qh, qh_ERRqhull, facet, visible);+ }+ if (owner)+ facet->f.triowner= owner;+ else if (!facet->degenerate) {+ owner= facet;+ nextfacet= visible->next; /* rescan tricoplanar facets with owner, visible!=0 by QH6162 */+ facet->keepcentrum= True; /* one facet owns ->normal, etc. */+ facet->coplanarset= visible->coplanarset;+ facet->outsideset= visible->outsideset;+ visible->coplanarset= NULL;+ visible->outsideset= NULL;+ if (!qh->TRInormals) { /* center and normal copied to tricoplanar facets */+ visible->center= NULL;+ visible->normal= NULL;+ }+ qh_delfacet(qh, visible);+ qh->num_visible--;+ }+ }+ }+ if (visible && !owner) {+ trace2((qh, qh->ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n",+ visible->id));+ qh_delfacet(qh, visible);+ qh->num_visible--;+ }+ qh->NEWfacets= False;+ qh->ONLYgood= onlygood; /* restore value */+ if (qh->CHECKfrequently)+ qh_checkpolygon(qh, qh->facet_list);+ qh->hasTriangulation= True;+} /* triangulate */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="triangulate_facet">-</a>++ qh_triangulate_facet(qh, facetA, &firstVertex )+ triangulate a non-simplicial facet+ if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center+ returns:+ qh.newfacet_list == simplicial facets+ facet->tricoplanar set and ->keepcentrum false+ facet->degenerate set if duplicated apex+ facet->f.trivisible set to facetA+ facet->center copied from facetA (created if qh_ASvoronoi)+ qh_eachvoronoi, qh_detvridge, qh_detvridge3 assume centers copied+ facet->normal,offset,maxoutside copied from facetA++ notes:+ only called by qh_triangulate+ qh_makenew_nonsimplicial uses neighbor->seen for the same+ if qh.TRInormals, newfacet->normal will need qh_free+ if qh.TRInormals and qh_AScentrum, newfacet->center will need qh_free+ keepcentrum is also set on Zwidefacet in qh_mergefacet+ freed by qh_clearcenters++ see also:+ qh_addpoint() -- add a point+ qh_makenewfacets() -- construct a cone of facets for a new vertex++ design:+ if qh_ASvoronoi,+ compute Voronoi center (facet->center)+ select first vertex (highest ID to preserve ID ordering of ->vertices)+ triangulate from vertex to ridges+ copy facet->center, normal, offset+ update vertex neighbors+*/+void qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex) {+ facetT *newfacet;+ facetT *neighbor, **neighborp;+ vertexT *apex;+ int numnew=0;++ trace3((qh, qh->ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id));++ if (qh->IStracing >= 4)+ qh_printfacet(qh, qh->ferr, facetA);+ FOREACHneighbor_(facetA) {+ neighbor->seen= False;+ neighbor->coplanar= False;+ }+ if (qh->CENTERtype == qh_ASvoronoi && !facetA->center /* matches upperdelaunay in qh_setfacetplane() */+ && fabs_(facetA->normal[qh->hull_dim -1]) >= qh->ANGLEround * qh_ZEROdelaunay) {+ facetA->center= qh_facetcenter(qh, facetA->vertices);+ }+ qh_willdelete(qh, facetA, NULL);+ qh->newfacet_list= qh->facet_tail;+ facetA->visitid= qh->visit_id;+ apex= SETfirstt_(facetA->vertices, vertexT);+ qh_makenew_nonsimplicial(qh, facetA, apex, &numnew);+ SETfirst_(facetA->neighbors)= NULL;+ FORALLnew_facets {+ newfacet->tricoplanar= True;+ newfacet->f.trivisible= facetA;+ newfacet->degenerate= False;+ newfacet->upperdelaunay= facetA->upperdelaunay;+ newfacet->good= facetA->good;+ if (qh->TRInormals) { /* 'Q11' triangulate duplicates ->normal and ->center */+ newfacet->keepcentrum= True;+ if(facetA->normal){+ newfacet->normal= qh_memalloc(qh, qh->normal_size);+ memcpy((char *)newfacet->normal, facetA->normal, qh->normal_size);+ }+ if (qh->CENTERtype == qh_AScentrum)+ newfacet->center= qh_getcentrum(qh, newfacet);+ else if (qh->CENTERtype == qh_ASvoronoi && facetA->center){+ newfacet->center= qh_memalloc(qh, qh->center_size);+ memcpy((char *)newfacet->center, facetA->center, qh->center_size);+ }+ }else {+ newfacet->keepcentrum= False;+ /* one facet will have keepcentrum=True at end of qh_triangulate */+ newfacet->normal= facetA->normal;+ newfacet->center= facetA->center;+ }+ newfacet->offset= facetA->offset;+#if qh_MAXoutside+ newfacet->maxoutside= facetA->maxoutside;+#endif+ }+ qh_matchnewfacets(qh /*qh.newfacet_list*/);+ zinc_(Ztricoplanar);+ zadd_(Ztricoplanartot, numnew);+ zmax_(Ztricoplanarmax, numnew);+ qh->visible_list= NULL;+ if (!(*first_vertex))+ (*first_vertex)= qh->newvertex_list;+ qh->newvertex_list= NULL;+ qh_updatevertices(qh /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);+ qh_resetlists(qh, False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);+} /* triangulate_facet */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="triangulate_link">-</a>++ qh_triangulate_link(qh, oldfacetA, facetA, oldfacetB, facetB)+ relink facetA to facetB via oldfacets+ returns:+ adds mirror facets to qh->degen_mergeset (4-d and up only)+ design:+ if they are already neighbors, the opposing neighbors become MRGmirror facets+*/+void qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) {+ int errmirror= False;++ trace3((qh, qh->ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n",+ oldfacetA->id, oldfacetB->id, facetA->id, facetB->id));+ if (qh_setin(facetA->neighbors, facetB)) {+ if (!qh_setin(facetB->neighbors, facetA))+ errmirror= True;+ else+ qh_appendmergeset(qh, facetA, facetB, MRGmirror, NULL);+ }else if (qh_setin(facetB->neighbors, facetA))+ errmirror= True;+ if (errmirror) {+ qh_fprintf(qh, qh->ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n",+ facetA->id, facetB->id, oldfacetA->id, oldfacetB->id);+ qh_errexit2(qh, qh_ERRqhull, facetA, facetB);+ }+ qh_setreplace(qh, facetB->neighbors, oldfacetB, facetA);+ qh_setreplace(qh, facetA->neighbors, oldfacetA, facetB);+} /* triangulate_link */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="triangulate_mirror">-</a>++ qh_triangulate_mirror(qh, facetA, facetB)+ delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror+ a mirrored facet shares the same vertices of a logical ridge+ design:+ since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet+ if they are already neighbors, the opposing neighbors become MRGmirror facets+*/+void qh_triangulate_mirror(qhT *qh, facetT *facetA, facetT *facetB) {+ facetT *neighbor, *neighborB;+ int neighbor_i, neighbor_n;++ trace3((qh, qh->ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n",+ facetA->id, facetB->id));+ FOREACHneighbor_i_(qh, facetA) {+ neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT);+ if (neighbor == neighborB)+ continue; /* occurs twice */+ qh_triangulate_link(qh, facetA, neighbor, facetB, neighborB);+ }+ qh_willdelete(qh, facetA, NULL);+ qh_willdelete(qh, facetB, NULL);+} /* triangulate_mirror */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="triangulate_null">-</a>++ qh_triangulate_null(qh, facetA)+ remove null facetA from qh_triangulate_facet()+ a null facet has vertex #1 (apex) == vertex #2+ returns:+ adds facetA to ->visible for deletion after qh_updatevertices+ qh->degen_mergeset contains mirror facets (4-d and up only)+ design:+ since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet+ if they are already neighbors, the opposing neighbors become MRGmirror facets+*/+void qh_triangulate_null(qhT *qh, facetT *facetA) {+ facetT *neighbor, *otherfacet;++ trace3((qh, qh->ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id));+ neighbor= SETfirstt_(facetA->neighbors, facetT);+ otherfacet= SETsecondt_(facetA->neighbors, facetT);+ qh_triangulate_link(qh, facetA, neighbor, facetA, otherfacet);+ qh_willdelete(qh, facetA, NULL);+} /* triangulate_null */++#else /* qh_NOmerge */+void qh_triangulate(qhT *qh) {+}+#endif /* qh_NOmerge */++ /*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="vertexintersect">-</a>++ qh_vertexintersect(qh, vertexsetA, vertexsetB )+ intersects two vertex sets (inverse id ordered)+ vertexsetA is a temporary set at the top of qh->qhmem.tempstack++ returns:+ replaces vertexsetA with the intersection++ notes:+ could overwrite vertexsetA if currently too slow+*/+void qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB) {+ setT *intersection;++ intersection= qh_vertexintersect_new(qh, *vertexsetA, vertexsetB);+ qh_settempfree(qh, vertexsetA);+ *vertexsetA= intersection;+ qh_settemppush(qh, intersection);+} /* vertexintersect */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="vertexintersect_new">-</a>++ qh_vertexintersect_new(qh, )+ intersects two vertex sets (inverse id ordered)++ returns:+ a new set+*/+setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB) {+ setT *intersection= qh_setnew(qh, qh->hull_dim - 1);+ vertexT **vertexA= SETaddr_(vertexsetA, vertexT);+ vertexT **vertexB= SETaddr_(vertexsetB, vertexT);++ while (*vertexA && *vertexB) {+ if (*vertexA == *vertexB) {+ qh_setappend(qh, &intersection, *vertexA);+ vertexA++; vertexB++;+ }else {+ if ((*vertexA)->id > (*vertexB)->id)+ vertexA++;+ else+ vertexB++;+ }+ }+ return intersection;+} /* vertexintersect_new */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="vertexneighbors">-</a>++ qh_vertexneighbors(qh)+ for each vertex in qh.facet_list,+ determine its neighboring facets++ returns:+ sets qh.VERTEXneighbors+ nop if qh.VERTEXneighbors already set+ qh_addpoint() will maintain them++ notes:+ assumes all vertex->neighbors are NULL++ design:+ for each facet+ for each vertex+ append facet to vertex->neighbors+*/+void qh_vertexneighbors(qhT *qh /*qh.facet_list*/) {+ facetT *facet;+ vertexT *vertex, **vertexp;++ if (qh->VERTEXneighbors)+ return;+ trace1((qh, qh->ferr, 1035, "qh_vertexneighbors: determining neighboring facets for each vertex\n"));+ qh->vertex_visit++;+ FORALLfacets {+ if (facet->visible)+ continue;+ FOREACHvertex_(facet->vertices) {+ if (vertex->visitid != qh->vertex_visit) {+ vertex->visitid= qh->vertex_visit;+ vertex->neighbors= qh_setnew(qh, qh->hull_dim);+ }+ qh_setappend(qh, &vertex->neighbors, facet);+ }+ }+ qh->VERTEXneighbors= True;+} /* vertexneighbors */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="vertexsubset">-</a>++ qh_vertexsubset( vertexsetA, vertexsetB )+ returns True if vertexsetA is a subset of vertexsetB+ assumes vertexsets are sorted++ note:+ empty set is a subset of any other set+*/+boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) {+ vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT);+ vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT);++ while (True) {+ if (!*vertexA)+ return True;+ if (!*vertexB)+ return False;+ if ((*vertexA)->id > (*vertexB)->id)+ return False;+ if (*vertexA == *vertexB)+ vertexA++;+ vertexB++;+ }+ return False; /* avoid warnings */+} /* vertexsubset */
+ C/poly_r.c view
@@ -0,0 +1,1205 @@+/*<html><pre> -<a href="qh-poly_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ poly_r.c+ implements polygons and simplices++ see qh-poly_r.htm, poly_r.h and libqhull_r.h++ infrequent code is in poly2_r.c+ (all but top 50 and their callers 12/3/95)++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/poly_r.c#3 $$Change: 2064 $+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $+*/++#include "qhull_ra.h"++/*======== functions in alphabetical order ==========*/++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="appendfacet">-</a>++ qh_appendfacet(qh, 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(qhT *qh, 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, qh->ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));+} /* appendfacet */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="appendvertex">-</a>++ qh_appendvertex(qh, 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(qhT *qh, 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, qh->ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));+} /* appendvertex */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="attachnewfacets">-</a>++ qh_attachnewfacets(qh, )+ 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(qhT *qh /* qh.visible_list, newfacet_list */) {+ facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;+ ridgeT *ridge, **ridgep;++ qh->NEWfacets= True;+ trace3((qh, 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(qh, &(ridge->vertices)); /* delete on 2nd visit */+ qh_memfree(qh, ridge, (int)sizeof(ridgeT));+ }+ }+ SETfirst_(visible->ridges)= NULL;+ }+ SETfirst_(visible->neighbors)= NULL;+ }+ trace1((qh, 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(qh, horizon->neighbors, visible, newfacet);+ }else {+ qh_fprintf(qh, 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, 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(qh, horizon->neighbors,+ SETindex_(horizon->neighbors, neighbor));+ neighborp--; /* repeat */+ }+ }+ qh_setappend(qh, &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_r.htm#TOC"+ >-------------------------------</a><a name="checkflipped">-</a>++ qh_checkflipped(qh, 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(qhT *qh, facetT *facet, realT *distp, boolT allerror) {+ realT dist;++ if (facet->flipped && !distp)+ return False;+ zzinc_(Zdistcheck);+ qh_distplane(qh, 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, 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(qh, "flipped facet");+ return False;+ }+ return True;+} /* checkflipped */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="delfacet">-</a>++ qh_delfacet(qh, facet )+ removes facet from facet_list and frees up its memory++ notes:+ assumes vertices and ridges already freed+*/+void qh_delfacet(qhT *qh, facetT *facet) {+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */++ trace4((qh, 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(qh, facet);+ if (!facet->tricoplanar || facet->keepcentrum) {+ qh_memfree_(qh, facet->normal, qh->normal_size, freelistp);+ if (qh->CENTERtype == qh_ASvoronoi) { /* braces for macro calls */+ qh_memfree_(qh, facet->center, qh->center_size, freelistp);+ }else /* AScentrum */ {+ qh_memfree_(qh, facet->center, qh->normal_size, freelistp);+ }+ }+ qh_setfree(qh, &(facet->neighbors));+ if (facet->ridges)+ qh_setfree(qh, &(facet->ridges));+ qh_setfree(qh, &(facet->vertices));+ if (facet->outsideset)+ qh_setfree(qh, &(facet->outsideset));+ if (facet->coplanarset)+ qh_setfree(qh, &(facet->coplanarset));+ qh_memfree_(qh, facet, (int)sizeof(facetT), freelistp);+} /* delfacet */+++/*-<a href="qh-poly_r.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(qhT *qh /*qh.visible_list*/) {+ facetT *visible, *nextfacet;+ vertexT *vertex, **vertexp;+ int numvisible= 0, numdel= qh_setsize(qh, qh->del_vertices);++ trace1((qh, 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(qh, visible);+ }+ if (numvisible != qh->num_visible) {+ qh_fprintf(qh, 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, 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(qh, vertex);+ qh_settruncate(qh, qh->del_vertices, 0);+} /* deletevisible */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="facetintersect">-</a>++ qh_facetintersect(qh, 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(qhT *qh, 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, 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, qh_ERRqhull, facetA, facetB);+ }+ intersect= qh_setnew_delnthsorted(qh, facetA->vertices, qh->hull_dim, *skipA, prepend);+ trace4((qh, 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_r.htm#TOC"+ >-------------------------------</a><a name="gethash">-</a>++ qh_gethash(qh, 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(qhT *qh, 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, qh->ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);+ qh_errexit2(qh, 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_r.htm#TOC"+ >-------------------------------</a><a name="makenewfacet">-</a>++ qh_makenewfacet(qh, 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(qhT *qh, setT *vertices, boolT toporient,facetT *horizon) {+ facetT *newfacet;+ vertexT *vertex, **vertexp;++ FOREACHvertex_(vertices) {+ if (!vertex->newlist) {+ qh_removevertex(qh, vertex);+ qh_appendvertex(qh, vertex);+ }+ }+ newfacet= qh_newfacet(qh);+ newfacet->vertices= vertices;+ newfacet->toporient= (unsigned char)toporient;+ if (horizon)+ qh_setappend(qh, &(newfacet->neighbors), horizon);+ qh_appendfacet(qh, newfacet);+ return(newfacet);+} /* makenewfacet */+++/*-<a href="qh-poly_r.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(qhT *qh /* qh.newfacet_list */) {+ facetT *newfacet;++ FORALLnew_facets {+ if (!newfacet->mergehorizon)+ qh_setfacetplane(qh, newfacet);+ }+ if (qh->JOGGLEmax < REALmax/2)+ minimize_(qh->min_vertex, -wwval_(Wnewvertexmax));+} /* makenewplanes */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="makenew_nonsimplicial">-</a>++ qh_makenew_nonsimplicial(qh, 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(qhT *qh, 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(qh, &(ridge->vertices)); /* delete on 2nd visit */+ qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);+ }+ }+ }else { /* neighbor is an horizon facet */+ toporient= (ridge->top == visible);+ vertices= qh_setnew(qh, qh->hull_dim); /* makes sure this is quick */+ qh_setappend(qh, &vertices, apex);+ qh_setappend_set(qh, &vertices, ridge->vertices);+ newfacet= qh_makenewfacet(qh, 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(qh, &(newfacet->ridges), ridge);+ }else { /* qh_attachnewfacets */+ if (neighbor->seen) {+ if (neighbor->simplicial) {+ qh_fprintf(qh, 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, qh_ERRqhull, neighbor, visible);+ }+ qh_setappend(qh, &(neighbor->neighbors), newfacet);+ }else+ qh_setreplace(qh, neighbor->neighbors, visible, newfacet);+ if (neighbor->simplicial) {+ qh_setdel(neighbor->ridges, ridge);+ qh_setfree(qh, &(ridge->vertices));+ qh_memfree(qh, ridge, (int)sizeof(ridgeT));+ }else {+ qh_setappend(qh, &(newfacet->ridges), ridge);+ if (toporient)+ ridge->top= newfacet;+ else+ ridge->bottom= newfacet;+ }+ trace4((qh, 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(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {+ return NULL;+}+#endif /* qh_NOmerge */++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="makenew_simplicial">-</a>++ qh_makenew_simplicial(qh, 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(qhT *qh, 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(qh, 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(qh, 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, 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_r.htm#TOC"+ >-------------------------------</a><a name="matchneighbor">-</a>++ qh_matchneighbor(qh, 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(qhT *qh, 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(qh, hashsize, newfacet->vertices, qh->hull_dim, 1,+ SETelem_(newfacet->vertices, newskip));+ trace4((qh, 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(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {+ if (SETelem_(newfacet->vertices, newskip) ==+ SETelem_(facet->vertices, skip)) {+ qh_precision(qh, "two facets with the same vertices");+ qh_fprintf(qh, 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, 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, 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(qh, "a ridge with more than two neighbors");+ qh_fprintf(qh, 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, qh_ERRprec, facet, newfacet);+ }+ SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;+ newfacet->dupridge= True;+ if (!newfacet->normal)+ qh_setfacetplane(qh, newfacet);+ qh_addhash(newfacet, qh->hash_table, hashsize, hash);+ (*hashcount)++;+ if (!facet->normal)+ qh_setfacetplane(qh, facet);+ if (matchfacet != qh_DUPLICATEridge) {+ SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;+ facet->dupridge= True;+ if (!facet->normal)+ qh_setfacetplane(qh, facet);+ if (matchfacet) {+ matchskip= qh_setindex(matchfacet->neighbors, facet);+ if (matchskip<0) {+ qh_fprintf(qh, 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, qh_ERRqhull, matchfacet, facet);+ }+ SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */+ matchfacet->dupridge= True;+ if (!matchfacet->normal)+ qh_setfacetplane(qh, matchfacet);+ qh_addhash(matchfacet, qh->hash_table, hashsize, hash);+ *hashcount += 2;+ }+ }+ trace4((qh, 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, 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_r.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(qhT *qh /* 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, qh->ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));+ FORALLnew_facets {+ numnew++;+ { /* inline qh_setzero(qh, 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(qh, numnew*(qh->hull_dim-1)); /* twice what is normally needed,+ but every ridge could be DUPLICATEridge */+ hashsize= qh_setsize(qh, 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(qh, 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, qh->ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n",+ newfacet->id, hashcount, count);+ qh_errexit(qh, qh_ERRqhull, newfacet, NULL);+ }+ }+#endif /* end of trap code */+ }+ if (hashcount) {+ FORALLnew_facets {+ if (newfacet->dupridge) {+ FOREACHneighbor_i_(qh, newfacet) {+ if (neighbor == qh_DUPLICATEridge) {+ qh_matchduplicates(qh, newfacet, neighbor_i, hashsize, &hashcount);+ /* this may report MERGEfacet */+ }+ }+ }+ }+ }+ if (hashcount) {+ qh_fprintf(qh, qh->ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n",+ hashcount);+ qh_printhashtable(qh, qh->ferr);+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);+ }+#ifndef qh_NOtrace+ if (qh->IStracing >= 2) {+ FOREACHfacet_i_(qh, qh->hash_table) {+ if (!facet)+ numfree++;+ }+ qh_fprintf(qh, qh->ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries . hashsize %d\n",+ numnew, numfree, qh_setsize(qh, qh->hash_table));+ }+#endif /* !qh_NOtrace */+ qh_setfree(qh, &qh->hash_table);+ if (qh->PREmerge || qh->MERGEexact) {+ if (qh->IStracing >= 4)+ qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL);+ FORALLnew_facets {+ if (newfacet->normal)+ qh_checkflipped(qh, newfacet, NULL, qh_ALL);+ }+ }else if (qh->FORCEoutput)+ qh_checkflipped_all(qh, qh->newfacet_list); /* prints warnings for flipped */+} /* matchnewfacets */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="matchvertices">-</a>++ qh_matchvertices(qh, 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(qhT *qh, 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, 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_r.htm#TOC"+ >-------------------------------</a><a name="newfacet">-</a>++ qh_newfacet(qh)+ return a new facet++ returns:+ all fields initialized or cleared (NULL)+ preallocates neighbors set+*/+facetT *qh_newfacet(qhT *qh) {+ facetT *facet;+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */++ qh_memalloc_(qh, (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, 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, qh->ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id));+ return(facet);+} /* newfacet */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="newridge">-</a>++ qh_newridge()+ return a new ridge+*/+ridgeT *qh_newridge(qhT *qh) {+ ridgeT *ridge;+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */++ qh_memalloc_(qh, (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, 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, qh->ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id));+ return(ridge);+} /* newridge */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="pointid">-</a>++ qh_pointid(qh, 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(qhT *qh, pointT *point) {+ ptr_intT offset, id;++ if (!point || !qh)+ 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_r.htm#TOC"+ >-------------------------------</a><a name="removefacet">-</a>++ qh_removefacet(qh, 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(qhT *qh, 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, qh->ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id));+} /* removefacet */+++/*-<a href="qh-poly_r.htm#TOC"+ >-------------------------------</a><a name="removevertex">-</a>++ qh_removevertex(qh, vertex )+ unlinks vertex from qh.vertex_list,++ returns:+ updates qh.vertex_list .newvertex_list+ decrements qh.num_vertices+*/+void qh_removevertex(qhT *qh, 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, qh->ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id));+} /* removevertex */+++/*-<a href="qh-poly_r.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(qhT *qh /*qh.newvertex_list, newfacet_list, visible_list*/) {+ facetT *newfacet= NULL, *neighbor, **neighborp, *visible;+ vertexT *vertex, **vertexp;++ trace3((qh, 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(qh, vertex->neighbors);+ }+ FORALLnew_facets {+ FOREACHvertex_(newfacet->vertices)+ qh_setappend(qh, &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, &qh->del_vertices, vertex);+ trace2((qh, qh->ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",+ qh_pointid(qh, vertex->point), vertex->id, visible->id));+ }+ }+ }+ }+ }else { /* !VERTEXneighbors */+ FORALLvisible_facets {+ FOREACHvertex_(visible->vertices) {+ if (!vertex->newlist && !vertex->deleted) {+ vertex->deleted= True;+ qh_setappend(qh, &qh->del_vertices, vertex);+ trace2((qh, qh->ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",+ qh_pointid(qh, vertex->point), vertex->id, visible->id));+ }+ }+ }+ }+} /* updatevertices */+++
+ C/qset_r.c view
@@ -0,0 +1,1340 @@+/*<html><pre> -<a href="qh-set_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ qset_r.c+ implements set manipulations needed for quickhull++ see qh-set_r.htm and qset_r.h++ Be careful of strict aliasing (two pointers of different types+ that reference the same location). The last slot of a set is+ either the actual size of the set plus 1, or the NULL terminator+ of the set (i.e., setelemT).++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/qset_r.c#3 $$Change: 2062 $+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $+*/++#include "libqhull_r.h" /* for qhT and QHULL_CRTDBG */+#include "qset_r.h"+#include "mem_r.h"+#include <stdio.h>+#include <string.h>+/*** uncomment here and qhull_ra.h+ if string.h does not define memcpy()+#include <memory.h>+*/++#ifndef qhDEFlibqhull+typedef struct ridgeT ridgeT;+typedef struct facetT facetT;+void qh_errexit(qhT *qh, int exitcode, facetT *, ridgeT *);+void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );+# ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */+# pragma warning( disable : 4127) /* conditional expression is constant */+# pragma warning( disable : 4706) /* assignment within conditional function */+# endif+#endif++/*=============== internal macros ===========================*/++/*============ functions in alphabetical order ===================*/++/*-<a href="qh-set_r.htm#TOC"+ >--------------------------------<a name="setaddnth">-</a>++ qh_setaddnth(qh, setp, nth, newelem)+ adds newelem as n'th element of sorted or unsorted *setp++ notes:+ *setp and newelem must be defined+ *setp may be a temp set+ nth=0 is first element+ errors if nth is out of bounds++ design:+ expand *setp if empty or full+ move tail of *setp up one+ insert newelem+*/+void qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem) {+ int oldsize, i;+ setelemT *sizep; /* avoid strict aliasing */+ setelemT *oldp, *newp;++ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {+ qh_setlarger(qh, setp);+ sizep= SETsizeaddr_(*setp);+ }+ oldsize= sizep->i - 1;+ if (nth < 0 || nth > oldsize) {+ qh_fprintf(qh, qh->qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth);+ qh_setprint(qh, qh->qhmem.ferr, "", *setp);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ sizep->i++;+ oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void); /* NULL */+ newp= oldp+1;+ for (i=oldsize-nth+1; i--; ) /* move at least NULL */+ (newp--)->p= (oldp--)->p; /* may overwrite *sizep */+ newp->p= newelem;+} /* setaddnth */+++/*-<a href="qh-set_r.htm#TOC"+ >--------------------------------<a name="setaddsorted">-</a>++ setaddsorted( setp, newelem )+ adds an newelem into sorted *setp++ notes:+ *setp and newelem must be defined+ *setp may be a temp set+ nop if newelem already in set++ design:+ find newelem's position in *setp+ insert newelem+*/+void qh_setaddsorted(qhT *qh, setT **setp, void *newelem) {+ int newindex=0;+ void *elem, **elemp;++ FOREACHelem_(*setp) { /* could use binary search instead */+ if (elem < newelem)+ newindex++;+ else if (elem == newelem)+ return;+ else+ break;+ }+ qh_setaddnth(qh, setp, newindex, newelem);+} /* setaddsorted */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setappend">-</a>++ qh_setappend(qh, setp, newelem)+ append newelem to *setp++ notes:+ *setp may be a temp set+ *setp and newelem may be NULL++ design:+ expand *setp if empty or full+ append newelem to *setp++*/+void qh_setappend(qhT *qh, setT **setp, void *newelem) {+ setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */+ setelemT *endp;+ int count;++ if (!newelem)+ return;+ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {+ qh_setlarger(qh, setp);+ sizep= SETsizeaddr_(*setp);+ }+ count= (sizep->i)++ - 1;+ endp= (setelemT *)SETelemaddr_(*setp, count, void);+ (endp++)->p= newelem;+ endp->p= NULL;+} /* setappend */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setappend_set">-</a>++ qh_setappend_set(qh, setp, setA)+ appends setA to *setp++ notes:+ *setp can not be a temp set+ *setp and setA may be NULL++ design:+ setup for copy+ expand *setp if it is too small+ append all elements of setA to *setp+*/+void qh_setappend_set(qhT *qh, setT **setp, setT *setA) {+ int sizeA, size;+ setT *oldset;+ setelemT *sizep;++ if (!setA)+ return;+ SETreturnsize_(setA, sizeA);+ if (!*setp)+ *setp= qh_setnew(qh, sizeA);+ sizep= SETsizeaddr_(*setp);+ if (!(size= sizep->i))+ size= (*setp)->maxsize;+ else+ size--;+ if (size + sizeA > (*setp)->maxsize) {+ oldset= *setp;+ *setp= qh_setcopy(qh, oldset, sizeA);+ qh_setfree(qh, &oldset);+ sizep= SETsizeaddr_(*setp);+ }+ if (sizeA > 0) {+ sizep->i= size+sizeA+1; /* memcpy may overwrite */+ memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize);+ }+} /* setappend_set */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setappend2ndlast">-</a>++ qh_setappend2ndlast(qh, setp, newelem )+ makes newelem the next to the last element in *setp++ notes:+ *setp must have at least one element+ newelem must be defined+ *setp may be a temp set++ design:+ expand *setp if empty or full+ move last element of *setp up one+ insert newelem+*/+void qh_setappend2ndlast(qhT *qh, setT **setp, void *newelem) {+ setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */+ setelemT *endp, *lastp;+ int count;++ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {+ qh_setlarger(qh, setp);+ sizep= SETsizeaddr_(*setp);+ }+ count= (sizep->i)++ - 1;+ endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */+ lastp= endp-1;+ *(endp++)= *lastp;+ endp->p= NULL; /* may overwrite *sizep */+ lastp->p= newelem;+} /* setappend2ndlast */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setcheck">-</a>++ qh_setcheck(qh, set, typename, id )+ check set for validity+ report errors with typename and id++ design:+ checks that maxsize, actual size, and NULL terminator agree+*/+void qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned id) {+ int maxsize, size;+ int waserr= 0;++ if (!set)+ return;+ SETreturnsize_(set, size);+ maxsize= set->maxsize;+ if (size > maxsize || !maxsize) {+ qh_fprintf(qh, qh->qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n",+ size, tname, id, maxsize);+ waserr= 1;+ }else if (set->e[size].p) {+ qh_fprintf(qh, qh->qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n",+ tname, id, size-1, maxsize);+ waserr= 1;+ }+ if (waserr) {+ qh_setprint(qh, qh->qhmem.ferr, "ERRONEOUS", set);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+} /* setcheck */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setcompact">-</a>++ qh_setcompact(qh, set )+ remove internal NULLs from an unsorted set++ returns:+ updated set++ notes:+ set may be NULL+ it would be faster to swap tail of set into holes, like qh_setdel++ design:+ setup pointers into set+ skip NULLs while copying elements to start of set+ update the actual size+*/+void qh_setcompact(qhT *qh, setT *set) {+ int size;+ void **destp, **elemp, **endp, **firstp;++ if (!set)+ return;+ SETreturnsize_(set, size);+ destp= elemp= firstp= SETaddr_(set, void);+ endp= destp + size;+ while (1) {+ if (!(*destp++ = *elemp++)) {+ destp--;+ if (elemp > endp)+ break;+ }+ }+ qh_settruncate(qh, set, (int)(destp-firstp)); /* WARN64 */+} /* setcompact */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setcopy">-</a>++ qh_setcopy(qh, set, extra )+ make a copy of a sorted or unsorted set with extra slots++ returns:+ new set++ design:+ create a newset with extra slots+ copy the elements to the newset++*/+setT *qh_setcopy(qhT *qh, setT *set, int extra) {+ setT *newset;+ int size;++ if (extra < 0)+ extra= 0;+ SETreturnsize_(set, size);+ newset= qh_setnew(qh, size+extra);+ SETsizeaddr_(newset)->i= size+1; /* memcpy may overwrite */+ memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize);+ return(newset);+} /* setcopy */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setdel">-</a>++ qh_setdel(set, oldelem )+ delete oldelem from an unsorted set++ returns:+ returns oldelem if found+ returns NULL otherwise++ notes:+ set may be NULL+ oldelem must not be NULL;+ only deletes one copy of oldelem in set++ design:+ locate oldelem+ update actual size if it was full+ move the last element to the oldelem's location+*/+void *qh_setdel(setT *set, void *oldelem) {+ setelemT *sizep;+ setelemT *elemp;+ setelemT *lastp;++ if (!set)+ return NULL;+ elemp= (setelemT *)SETaddr_(set, void);+ while (elemp->p != oldelem && elemp->p)+ elemp++;+ if (elemp->p) {+ sizep= SETsizeaddr_(set);+ if (!(sizep->i)--) /* if was a full set */+ sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */+ lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);+ elemp->p= lastp->p; /* may overwrite itself */+ lastp->p= NULL;+ return oldelem;+ }+ return NULL;+} /* setdel */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setdellast">-</a>++ qh_setdellast(set)+ return last element of set or NULL++ notes:+ deletes element from set+ set may be NULL++ design:+ return NULL if empty+ if full set+ delete last element and set actual size+ else+ delete last element and update actual size+*/+void *qh_setdellast(setT *set) {+ int setsize; /* actually, actual_size + 1 */+ int maxsize;+ setelemT *sizep;+ void *returnvalue;++ if (!set || !(set->e[0].p))+ return NULL;+ sizep= SETsizeaddr_(set);+ if ((setsize= sizep->i)) {+ returnvalue= set->e[setsize - 2].p;+ set->e[setsize - 2].p= NULL;+ sizep->i--;+ }else {+ maxsize= set->maxsize;+ returnvalue= set->e[maxsize - 1].p;+ set->e[maxsize - 1].p= NULL;+ sizep->i= maxsize;+ }+ return returnvalue;+} /* setdellast */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setdelnth">-</a>++ qh_setdelnth(qh, set, nth )+ deletes nth element from unsorted set+ 0 is first element++ returns:+ returns the element (needs type conversion)++ notes:+ errors if nth invalid++ design:+ setup points and check nth+ delete nth element and overwrite with last element+*/+void *qh_setdelnth(qhT *qh, setT *set, int nth) {+ void *elem;+ setelemT *sizep;+ setelemT *elemp, *lastp;++ sizep= SETsizeaddr_(set);+ if ((sizep->i--)==0) /* if was a full set */+ sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */+ if (nth < 0 || nth >= sizep->i) {+ qh_fprintf(qh, qh->qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth);+ qh_setprint(qh, qh->qhmem.ferr, "", set);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ elemp= (setelemT *)SETelemaddr_(set, nth, void); /* nth valid by QH6174 */+ lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);+ elem= elemp->p;+ elemp->p= lastp->p; /* may overwrite itself */+ lastp->p= NULL;+ return elem;+} /* setdelnth */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setdelnthsorted">-</a>++ qh_setdelnthsorted(qh, set, nth )+ deletes nth element from sorted set++ returns:+ returns the element (use type conversion)++ notes:+ errors if nth invalid++ see also:+ setnew_delnthsorted++ design:+ setup points and check nth+ copy remaining elements down one+ update actual size+*/+void *qh_setdelnthsorted(qhT *qh, setT *set, int nth) {+ void *elem;+ setelemT *sizep;+ setelemT *newp, *oldp;++ sizep= SETsizeaddr_(set);+ if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) {+ qh_fprintf(qh, qh->qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth);+ qh_setprint(qh, qh->qhmem.ferr, "", set);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ newp= (setelemT *)SETelemaddr_(set, nth, void);+ elem= newp->p;+ oldp= newp+1;+ while (((newp++)->p= (oldp++)->p))+ ; /* copy remaining elements and NULL */+ if ((sizep->i--)==0) /* if was a full set */+ sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */+ return elem;+} /* setdelnthsorted */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setdelsorted">-</a>++ qh_setdelsorted(set, oldelem )+ deletes oldelem from sorted set++ returns:+ returns oldelem if it was deleted++ notes:+ set may be NULL++ design:+ locate oldelem in set+ copy remaining elements down one+ update actual size+*/+void *qh_setdelsorted(setT *set, void *oldelem) {+ setelemT *sizep;+ setelemT *newp, *oldp;++ if (!set)+ return NULL;+ newp= (setelemT *)SETaddr_(set, void);+ while(newp->p != oldelem && newp->p)+ newp++;+ if (newp->p) {+ oldp= newp+1;+ while (((newp++)->p= (oldp++)->p))+ ; /* copy remaining elements */+ sizep= SETsizeaddr_(set);+ if ((sizep->i--)==0) /* if was a full set */+ sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */+ return oldelem;+ }+ return NULL;+} /* setdelsorted */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setduplicate">-</a>++ qh_setduplicate(qh, set, elemsize )+ duplicate a set of elemsize elements++ notes:+ use setcopy if retaining old elements++ design:+ create a new set+ for each elem of the old set+ create a newelem+ append newelem to newset+*/+setT *qh_setduplicate(qhT *qh, setT *set, int elemsize) {+ void *elem, **elemp, *newElem;+ setT *newSet;+ int size;++ if (!(size= qh_setsize(qh, set)))+ return NULL;+ newSet= qh_setnew(qh, size);+ FOREACHelem_(set) {+ newElem= qh_memalloc(qh, elemsize);+ memcpy(newElem, elem, (size_t)elemsize);+ qh_setappend(qh, &newSet, newElem);+ }+ return newSet;+} /* setduplicate */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setendpointer">-</a>++ qh_setendpointer( set )+ Returns pointer to NULL terminator of a set's elements+ set can not be NULL++*/+void **qh_setendpointer(setT *set) {++ setelemT *sizep= SETsizeaddr_(set);+ int n= sizep->i;+ return (n ? &set->e[n-1].p : &sizep->p);+} /* qh_setendpointer */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setequal">-</a>++ qh_setequal( setA, setB )+ returns 1 if two sorted sets are equal, otherwise returns 0++ notes:+ either set may be NULL++ design:+ check size of each set+ setup pointers+ compare elements of each set+*/+int qh_setequal(setT *setA, setT *setB) {+ void **elemAp, **elemBp;+ int sizeA= 0, sizeB= 0;++ if (setA) {+ SETreturnsize_(setA, sizeA);+ }+ if (setB) {+ SETreturnsize_(setB, sizeB);+ }+ if (sizeA != sizeB)+ return 0;+ if (!sizeA)+ return 1;+ elemAp= SETaddr_(setA, void);+ elemBp= SETaddr_(setB, void);+ if (!memcmp((char *)elemAp, (char *)elemBp, sizeA*SETelemsize))+ return 1;+ return 0;+} /* setequal */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setequal_except">-</a>++ qh_setequal_except( setA, skipelemA, setB, skipelemB )+ returns 1 if sorted setA and setB are equal except for skipelemA & B++ returns:+ false if either skipelemA or skipelemB are missing++ notes:+ neither set may be NULL++ if skipelemB is NULL,+ can skip any one element of setB++ design:+ setup pointers+ search for skipelemA, skipelemB, and mismatches+ check results+*/+int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB) {+ void **elemA, **elemB;+ int skip=0;++ elemA= SETaddr_(setA, void);+ elemB= SETaddr_(setB, void);+ while (1) {+ if (*elemA == skipelemA) {+ skip++;+ elemA++;+ }+ if (skipelemB) {+ if (*elemB == skipelemB) {+ skip++;+ elemB++;+ }+ }else if (*elemA != *elemB) {+ skip++;+ if (!(skipelemB= *elemB++))+ return 0;+ }+ if (!*elemA)+ break;+ if (*elemA++ != *elemB++)+ return 0;+ }+ if (skip != 2 || *elemB)+ return 0;+ return 1;+} /* setequal_except */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setequal_skip">-</a>++ qh_setequal_skip( setA, skipA, setB, skipB )+ returns 1 if sorted setA and setB are equal except for elements skipA & B++ returns:+ false if different size++ notes:+ neither set may be NULL++ design:+ setup pointers+ search for mismatches while skipping skipA and skipB+*/+int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB) {+ void **elemA, **elemB, **skipAp, **skipBp;++ elemA= SETaddr_(setA, void);+ elemB= SETaddr_(setB, void);+ skipAp= SETelemaddr_(setA, skipA, void);+ skipBp= SETelemaddr_(setB, skipB, void);+ while (1) {+ if (elemA == skipAp)+ elemA++;+ if (elemB == skipBp)+ elemB++;+ if (!*elemA)+ break;+ if (*elemA++ != *elemB++)+ return 0;+ }+ if (*elemB)+ return 0;+ return 1;+} /* setequal_skip */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setfree">-</a>++ qh_setfree(qh, setp )+ frees the space occupied by a sorted or unsorted set++ returns:+ sets setp to NULL++ notes:+ set may be NULL++ design:+ free array+ free set+*/+void qh_setfree(qhT *qh, setT **setp) {+ int size;+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */++ if (*setp) {+ size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;+ if (size <= qh->qhmem.LASTsize) {+ qh_memfree_(qh, *setp, size, freelistp);+ }else+ qh_memfree(qh, *setp, size);+ *setp= NULL;+ }+} /* setfree */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setfree2">-</a>++ qh_setfree2(qh, setp, elemsize )+ frees the space occupied by a set and its elements++ notes:+ set may be NULL++ design:+ free each element+ free set+*/+void qh_setfree2(qhT *qh, setT **setp, int elemsize) {+ void *elem, **elemp;++ FOREACHelem_(*setp)+ qh_memfree(qh, elem, elemsize);+ qh_setfree(qh, setp);+} /* setfree2 */++++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setfreelong">-</a>++ qh_setfreelong(qh, setp )+ frees a set only if it's in long memory++ returns:+ sets setp to NULL if it is freed++ notes:+ set may be NULL++ design:+ if set is large+ free it+*/+void qh_setfreelong(qhT *qh, setT **setp) {+ int size;++ if (*setp) {+ size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;+ if (size > qh->qhmem.LASTsize) {+ qh_memfree(qh, *setp, size);+ *setp= NULL;+ }+ }+} /* setfreelong */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setin">-</a>++ qh_setin(set, setelem )+ returns 1 if setelem is in a set, 0 otherwise++ notes:+ set may be NULL or unsorted++ design:+ scans set for setelem+*/+int qh_setin(setT *set, void *setelem) {+ void *elem, **elemp;++ FOREACHelem_(set) {+ if (elem == setelem)+ return 1;+ }+ return 0;+} /* setin */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setindex">-</a>++ qh_setindex(set, atelem )+ returns the index of atelem in set.+ returns -1, if not in set or maxsize wrong++ notes:+ set may be NULL and may contain nulls.+ NOerrors returned (qh_pointid, QhullPoint::id)++ design:+ checks maxsize+ scans set for atelem+*/+int qh_setindex(setT *set, void *atelem) {+ void **elem;+ int size, i;++ if (!set)+ return -1;+ SETreturnsize_(set, size);+ if (size > set->maxsize)+ return -1;+ elem= SETaddr_(set, void);+ for (i=0; i < size; i++) {+ if (*elem++ == atelem)+ return i;+ }+ return -1;+} /* setindex */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setlarger">-</a>++ qh_setlarger(qh, oldsetp )+ returns a larger set that contains all elements of *oldsetp++ notes:+ the set is at least twice as large+ if temp set, updates qh->qhmem.tempstack++ design:+ creates a new set+ copies the old set to the new set+ updates pointers in tempstack+ deletes the old set+*/+void qh_setlarger(qhT *qh, setT **oldsetp) {+ int size= 1;+ setT *newset, *set, **setp, *oldset;+ setelemT *sizep;+ setelemT *newp, *oldp;++ if (*oldsetp) {+ oldset= *oldsetp;+ SETreturnsize_(oldset, size);+ qh->qhmem.cntlarger++;+ qh->qhmem.totlarger += size+1;+ newset= qh_setnew(qh, 2 * size);+ oldp= (setelemT *)SETaddr_(oldset, void);+ newp= (setelemT *)SETaddr_(newset, void);+ memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize);+ sizep= SETsizeaddr_(newset);+ sizep->i= size+1;+ FOREACHset_((setT *)qh->qhmem.tempstack) {+ if (set == oldset)+ *(setp-1)= newset;+ }+ qh_setfree(qh, oldsetp);+ }else+ newset= qh_setnew(qh, 3);+ *oldsetp= newset;+} /* setlarger */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setlast">-</a>++ qh_setlast( set )+ return last element of set or NULL (use type conversion)++ notes:+ set may be NULL++ design:+ return last element+*/+void *qh_setlast(setT *set) {+ int size;++ if (set) {+ size= SETsizeaddr_(set)->i;+ if (!size)+ return SETelem_(set, set->maxsize - 1);+ else if (size > 1)+ return SETelem_(set, size - 2);+ }+ return NULL;+} /* setlast */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setnew">-</a>++ qh_setnew(qh, setsize )+ creates and allocates space for a set++ notes:+ setsize means the number of elements (!including the NULL terminator)+ use qh_settemp/qh_setfreetemp if set is temporary++ design:+ allocate memory for set+ roundup memory if small set+ initialize as empty set+*/+setT *qh_setnew(qhT *qh, int setsize) {+ setT *set;+ int sizereceived; /* used if !qh_NOmem */+ int size;+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */++ if (!setsize)+ setsize++;+ size= sizeof(setT) + setsize * SETelemsize;+ if (size>0 && size <= qh->qhmem.LASTsize) {+ qh_memalloc_(qh, size, freelistp, set, setT);+#ifndef qh_NOmem+ sizereceived= qh->qhmem.sizetable[ qh->qhmem.indextable[size]];+ if (sizereceived > size)+ setsize += (sizereceived - size)/SETelemsize;+#endif+ }else+ set= (setT*)qh_memalloc(qh, size);+ set->maxsize= setsize;+ set->e[setsize].i= 1;+ set->e[0].p= NULL;+ return(set);+} /* setnew */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setnew_delnthsorted">-</a>++ qh_setnew_delnthsorted(qh, set, size, nth, prepend )+ creates a sorted set not containing nth element+ if prepend, the first prepend elements are undefined++ notes:+ set must be defined+ checks nth+ see also: setdelnthsorted++ design:+ create new set+ setup pointers and allocate room for prepend'ed entries+ append head of old set to new set+ append tail of old set to new set+*/+setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend) {+ setT *newset;+ void **oldp, **newp;+ int tailsize= size - nth -1, newsize;++ if (tailsize < 0) {+ qh_fprintf(qh, qh->qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth);+ qh_setprint(qh, qh->qhmem.ferr, "", set);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ newsize= size-1 + prepend;+ newset= qh_setnew(qh, newsize);+ newset->e[newset->maxsize].i= newsize+1; /* may be overwritten */+ oldp= SETaddr_(set, void);+ newp= SETaddr_(newset, void) + prepend;+ switch (nth) {+ case 0:+ break;+ case 1:+ *(newp++)= *oldp++;+ break;+ case 2:+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ break;+ case 3:+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ break;+ case 4:+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ break;+ default:+ memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize);+ newp += nth;+ oldp += nth;+ break;+ }+ oldp++;+ switch (tailsize) {+ case 0:+ break;+ case 1:+ *(newp++)= *oldp++;+ break;+ case 2:+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ break;+ case 3:+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ break;+ case 4:+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ *(newp++)= *oldp++;+ break;+ default:+ memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize);+ newp += tailsize;+ }+ *newp= NULL;+ return(newset);+} /* setnew_delnthsorted */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setprint">-</a>++ qh_setprint(qh, fp, string, set )+ print set elements to fp with identifying string++ notes:+ never errors+*/+void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set) {+ int size, k;++ if (!set)+ qh_fprintf(qh, fp, 9346, "%s set is null\n", string);+ else {+ SETreturnsize_(set, size);+ qh_fprintf(qh, fp, 9347, "%s set=%p maxsize=%d size=%d elems=",+ string, set, set->maxsize, size);+ if (size > set->maxsize)+ size= set->maxsize+1;+ for (k=0; k < size; k++)+ qh_fprintf(qh, fp, 9348, " %p", set->e[k].p);+ qh_fprintf(qh, fp, 9349, "\n");+ }+} /* setprint */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setreplace">-</a>++ qh_setreplace(qh, set, oldelem, newelem )+ replaces oldelem in set with newelem++ notes:+ errors if oldelem not in the set+ newelem may be NULL, but it turns the set into an indexed set (no FOREACH)++ design:+ find oldelem+ replace with newelem+*/+void qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem) {+ void **elemp;++ elemp= SETaddr_(set, void);+ while (*elemp != oldelem && *elemp)+ elemp++;+ if (*elemp)+ *elemp= newelem;+ else {+ qh_fprintf(qh, qh->qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n",+ oldelem);+ qh_setprint(qh, qh->qhmem.ferr, "", set);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+} /* setreplace */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setsize">-</a>++ qh_setsize(qh, set )+ returns the size of a set++ notes:+ errors if set's maxsize is incorrect+ same as SETreturnsize_(set)+ same code for qh_setsize [qset_r.c] and QhullSetBase::count++ design:+ determine actual size of set from maxsize+*/+int qh_setsize(qhT *qh, setT *set) {+ int size;+ setelemT *sizep;++ if (!set)+ return(0);+ sizep= SETsizeaddr_(set);+ if ((size= sizep->i)) {+ size--;+ if (size > set->maxsize) {+ qh_fprintf(qh, qh->qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n",+ size, set->maxsize);+ qh_setprint(qh, qh->qhmem.ferr, "set: ", set);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ }else+ size= set->maxsize;+ return size;+} /* setsize */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="settemp">-</a>++ qh_settemp(qh, setsize )+ return a stacked, temporary set of upto setsize elements++ notes:+ use settempfree or settempfree_all to release from qh->qhmem.tempstack+ see also qh_setnew++ design:+ allocate set+ append to qh->qhmem.tempstack++*/+setT *qh_settemp(qhT *qh, int setsize) {+ setT *newset;++ newset= qh_setnew(qh, setsize);+ qh_setappend(qh, &qh->qhmem.tempstack, newset);+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n",+ newset, newset->maxsize, qh_setsize(qh, qh->qhmem.tempstack));+ return newset;+} /* settemp */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="settempfree">-</a>++ qh_settempfree(qh, set )+ free temporary set at top of qh->qhmem.tempstack++ notes:+ nop if set is NULL+ errors if set not from previous qh_settemp++ to locate errors:+ use 'T2' to find source and then find mis-matching qh_settemp++ design:+ check top of qh->qhmem.tempstack+ free it+*/+void qh_settempfree(qhT *qh, setT **set) {+ setT *stackedset;++ if (!*set)+ return;+ stackedset= qh_settemppop(qh);+ if (stackedset != *set) {+ qh_settemppush(qh, stackedset);+ qh_fprintf(qh, qh->qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n",+ *set, qh_setsize(qh, *set), qh_setsize(qh, qh->qhmem.tempstack)+1,+ stackedset, qh_setsize(qh, stackedset));+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ qh_setfree(qh, set);+} /* settempfree */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="settempfree_all">-</a>++ qh_settempfree_all(qh)+ free all temporary sets in qh->qhmem.tempstack++ design:+ for each set in tempstack+ free set+ free qh->qhmem.tempstack+*/+void qh_settempfree_all(qhT *qh) {+ setT *set, **setp;++ FOREACHset_(qh->qhmem.tempstack)+ qh_setfree(qh, &set);+ qh_setfree(qh, &qh->qhmem.tempstack);+} /* settempfree_all */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="settemppop">-</a>++ qh_settemppop(qh)+ pop and return temporary set from qh->qhmem.tempstack++ notes:+ the returned set is permanent++ design:+ pop and check top of qh->qhmem.tempstack+*/+setT *qh_settemppop(qhT *qh) {+ setT *stackedset;++ stackedset= (setT*)qh_setdellast(qh->qhmem.tempstack);+ if (!stackedset) {+ qh_fprintf(qh, qh->qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n");+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n",+ qh_setsize(qh, qh->qhmem.tempstack)+1, stackedset, qh_setsize(qh, stackedset));+ return stackedset;+} /* settemppop */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="settemppush">-</a>++ qh_settemppush(qh, set )+ push temporary set unto qh->qhmem.tempstack (makes it temporary)++ notes:+ duplicates settemp() for tracing++ design:+ append set to tempstack+*/+void qh_settemppush(qhT *qh, setT *set) {+ if (!set) {+ qh_fprintf(qh, qh->qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n");+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ qh_setappend(qh, &qh->qhmem.tempstack, set);+ if (qh->qhmem.IStracing >= 5)+ qh_fprintf(qh, qh->qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n",+ qh_setsize(qh, qh->qhmem.tempstack), set, qh_setsize(qh, set));+} /* settemppush */+++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="settruncate">-</a>++ qh_settruncate(qh, set, size )+ truncate set to size elements++ notes:+ set must be defined++ see:+ SETtruncate_++ design:+ check size+ update actual size of set+*/+void qh_settruncate(qhT *qh, setT *set, int size) {++ if (size < 0 || size > set->maxsize) {+ qh_fprintf(qh, qh->qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size);+ qh_setprint(qh, qh->qhmem.ferr, "", set);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ set->e[set->maxsize].i= size+1; /* maybe overwritten */+ set->e[size].p= NULL;+} /* settruncate */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setunique">-</a>++ qh_setunique(qh, set, elem )+ add elem to unsorted set unless it is already in set++ notes:+ returns 1 if it is appended++ design:+ if elem not in set+ append elem to set+*/+int qh_setunique(qhT *qh, setT **set, void *elem) {++ if (!qh_setin(*set, elem)) {+ qh_setappend(qh, set, elem);+ return 1;+ }+ return 0;+} /* setunique */++/*-<a href="qh-set_r.htm#TOC"+ >-------------------------------<a name="setzero">-</a>++ qh_setzero(qh, set, index, size )+ zero elements from index on+ set actual size of set to size++ notes:+ set must be defined+ the set becomes an indexed set (can not use FOREACH...)++ see also:+ qh_settruncate++ design:+ check index and size+ update actual size+ zero elements starting at e[index]+*/+void qh_setzero(qhT *qh, setT *set, int idx, int size) {+ int count;++ if (idx < 0 || idx >= size || size > set->maxsize) {+ qh_fprintf(qh, qh->qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size);+ qh_setprint(qh, qh->qhmem.ferr, "", set);+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);+ }+ set->e[set->maxsize].i= size+1; /* may be overwritten */+ count= size - idx + 1; /* +1 for NULL terminator */+ memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize);+} /* setzero */++
+ C/random_r.c view
@@ -0,0 +1,247 @@+/*<html><pre> -<a href="index_r.htm#TOC"+ >-------------------------------</a><a name="TOP">-</a>++ random_r.c and utilities+ Park & Miller's minimimal standard random number generator+ argc/argv conversion++ Used by rbox. Do not use 'qh' +*/++#include "libqhull_r.h"+#include "random_r.h"++#include <string.h>+#include <stdio.h>+#include <stdlib.h>++#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */+#pragma warning( disable : 4706) /* assignment within conditional function */+#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */+#endif++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="argv_to_command">-</a>++ qh_argv_to_command(argc, argv, command, max_size )++ build command from argc/argv+ max_size is at least++ returns:+ a space-delimited string of options (just as typed)+ returns false if max_size is too short++ notes:+ silently removes+ makes option string easy to input and output+ matches qh_argv_to_command_size()++ argc may be 0+*/+int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) {+ int i, remaining;+ char *s;+ *command= '\0'; /* max_size > 0 */++ if (argc) {+ if ((s= strrchr( argv[0], '\\')) /* get filename w/o .exe extension */+ || (s= strrchr( argv[0], '/')))+ s++;+ else+ s= argv[0];+ if ((int)strlen(s) < max_size) /* WARN64 */+ strcpy(command, s);+ else+ goto error_argv;+ if ((s= strstr(command, ".EXE"))+ || (s= strstr(command, ".exe")))+ *s= '\0';+ }+ for (i=1; i < argc; i++) {+ s= argv[i];+ remaining= max_size - (int)strlen(command) - (int)strlen(s) - 2; /* WARN64 */+ if (!*s || strchr(s, ' ')) {+ char *t= command + strlen(command);+ remaining -= 2;+ if (remaining < 0) {+ goto error_argv;+ }+ *t++= ' ';+ *t++= '"';+ while (*s) {+ if (*s == '"') {+ if (--remaining < 0)+ goto error_argv;+ *t++= '\\';+ }+ *t++= *s++;+ }+ *t++= '"';+ *t= '\0';+ }else if (remaining < 0) {+ goto error_argv;+ }else+ strcat(command, " ");+ strcat(command, s);+ }+ return 1;++error_argv:+ return 0;+} /* argv_to_command */++/*-<a href="qh-globa_r.htm#TOC"+>-------------------------------</a><a name="argv_to_command_size">-</a>++qh_argv_to_command_size(argc, argv )++ return size to allocate for qh_argv_to_command()++notes:+ argc may be 0+ actual size is usually shorter+*/+int qh_argv_to_command_size(int argc, char *argv[]) {+ unsigned int count= 1; /* null-terminator if argc==0 */+ int i;+ char *s;++ for (i=0; i<argc; i++){+ count += (int)strlen(argv[i]) + 1; /* WARN64 */+ if (i>0 && strchr(argv[i], ' ')) {+ count += 2; /* quote delimiters */+ for (s=argv[i]; *s; s++) {+ if (*s == '"') {+ count++;+ }+ }+ }+ }+ return count;+} /* argv_to_command_size */++/*-<a href="qh-geom_r.htm#TOC"+ >-------------------------------</a><a name="rand">-</a>++ qh_rand()+ qh_srand(qh, seed )+ generate pseudo-random number between 1 and 2^31 -2++ notes:+ For qhull and rbox, called from qh_RANDOMint(),etc. [user.h]++ From Park & Miller's minimal standard random number generator+ Communications of the ACM, 31:1192-1201, 1988.+ Does not use 0 or 2^31 -1+ this is silently enforced by qh_srand()+ Can make 'Rn' much faster by moving qh_rand to qh_distplane+*/++/* Global variables and constants */++#define qh_rand_a 16807+#define qh_rand_m 2147483647+#define qh_rand_q 127773 /* m div a */+#define qh_rand_r 2836 /* m mod a */++int qh_rand(qhT *qh) {+ int lo, hi, test;+ int seed = qh->last_random;++ hi = seed / qh_rand_q; /* seed div q */+ lo = seed % qh_rand_q; /* seed mod q */+ test = qh_rand_a * lo - qh_rand_r * hi;+ if (test > 0)+ seed= test;+ else+ seed= test + qh_rand_m;+ qh->last_random= seed;+ /* seed = seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax; for testing */+ /* seed = qh_RANDOMmax; for testing */+ return seed;+} /* rand */++void qh_srand(qhT *qh, int seed) {+ if (seed < 1)+ qh->last_random= 1;+ else if (seed >= qh_rand_m)+ qh->last_random= qh_rand_m - 1;+ else+ qh->last_random= seed;+} /* qh_srand */++/*-<a href="qh-geom_r.htm#TOC"+>-------------------------------</a><a name="randomfactor">-</a>++qh_randomfactor(qh, scale, offset )+ return a random factor r * scale + offset++notes:+ qh.RANDOMa/b are defined in global_r.c+ qh_RANDOMint requires 'qh'+*/+realT qh_randomfactor(qhT *qh, realT scale, realT offset) {+ realT randr;++ randr= qh_RANDOMint;+ return randr * scale + offset;+} /* randomfactor */++/*-<a href="qh-geom_r.htm#TOC"+>-------------------------------</a><a name="randommatrix">-</a>++qh_randommatrix(qh, buffer, dim, rows )+ generate a random dim X dim matrix in range [-1,1]+ assumes buffer is [dim+1, dim]++ returns:+ sets buffer to random numbers+ sets rows to rows of buffer+ sets row[dim] as scratch row++ notes:+ qh_RANDOMint requires 'qh'+*/+void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **rows) {+ int i, k;+ realT **rowi, *coord, realr;++ coord= buffer;+ rowi= rows;+ for (i=0; i < dim; i++) {+ *(rowi++)= coord;+ for (k=0; k < dim; k++) {+ realr= qh_RANDOMint;+ *(coord++)= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;+ }+ }+ *rowi= coord;+} /* randommatrix */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="strtol">-</a>++ qh_strtol( s, endp) qh_strtod( s, endp)+ internal versions of strtol() and strtod()+ does not skip trailing spaces+ notes:+ some implementations of strtol()/strtod() skip trailing spaces+*/+double qh_strtod(const char *s, char **endp) {+ double result;++ result= strtod(s, endp);+ if (s < (*endp) && (*endp)[-1] == ' ')+ (*endp)--;+ return result;+} /* strtod */++int qh_strtol(const char *s, char **endp) {+ int result;++ result= (int) strtol(s, endp, 10); /* WARN64 */+ if (s< (*endp) && (*endp)[-1] == ' ')+ (*endp)--;+ return result;+} /* strtol */
+ C/stat_r.c view
@@ -0,0 +1,682 @@+/*<html><pre> -<a href="qh-stat_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ stat_r.c+ contains all statistics that are collected for qhull++ see qh-stat_r.htm and stat_r.h++ Copyright (c) 1993-2015 The Geometry Center.+ $Id: //main/2015/qhull/src/libqhull_r/stat_r.c#5 $$Change: 2062 $+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $+*/++#include "qhull_ra.h"++/*========== functions in alphabetic order ================*/++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="allstatA">-</a>++ qh_allstatA()+ define statistics in groups of 20++ notes:+ (otherwise, 'gcc -O2' uses too much memory)+ uses qhstat.next+*/+void qh_allstatA(qhT *qh) {++ /* zdef_(type,name,doc,average) */+ zzdef_(zdoc, Zdoc2, "precision statistics", -1);+ zdef_(zinc, Znewvertex, NULL, -1);+ zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex);+ zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1);+ zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1);+ zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1);+ zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1);++ qh->qhstat.precision= qh->qhstat.next; /* call qh_precision for each of these */+ zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1);+ zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1);+ zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1);+ zzdef_(zinc, Zflippedfacets, "flipped facets", -1);+ zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1);+ zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1);+ zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1);+ zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1);+ zzdef_(zinc, Zback0, "zero divisors during back substitute", -1);+ zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1);+ zzdef_(zinc, Zmultiridge, "ridges with multiple neighbors", -1);+}+void qh_allstatB(qhT *qh) {+ zzdef_(zdoc, Zdoc1, "summary information", -1);+ zdef_(zinc, Zvertices, "number of vertices in output", -1);+ zdef_(zinc, Znumfacets, "number of facets in output", -1);+ zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1);+ zdef_(zinc, Znowsimplicial, "number of simplicial facets that were merged", -1);+ zdef_(zinc, Znumridges, "number of ridges in output", -1);+ zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets);+ zdef_(zmax, Zmaxridges, "maximum number of ridges", -1);+ zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets);+ zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1);+ zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets);+ zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1);+ zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices);+ zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1);+ zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1);+ zdef_(zinc, Ztotvertices, "vertices created altogether", -1);+ zzdef_(zinc, Zsetplane, "facets created altogether", -1);+ zdef_(zinc, Ztotridges, "ridges created altogether", -1);+ zdef_(zinc, Zpostfacets, "facets before post merge", -1);+ zdef_(zadd, Znummergetot, "average merges per facet(at most 511)", Znumfacets);+ zdef_(zmax, Znummergemax, " maximum merges for a facet(at most 511)", -1);+ zdef_(zinc, Zangle, NULL, -1);+ zdef_(wadd, Wangle, "average angle(cosine) of facet normals for all ridges", Zangle);+ zdef_(wmax, Wanglemax, " maximum angle(cosine) of facet normals across a ridge", -1);+ zdef_(wmin, Wanglemin, " minimum angle(cosine) of facet normals across a ridge", -1);+ zdef_(wadd, Wareatot, "total area of facets", -1);+ zdef_(wmax, Wareamax, " maximum facet area", -1);+ zdef_(wmin, Wareamin, " minimum facet area", -1);+}+void qh_allstatC(qhT *qh) {+ zdef_(zdoc, Zdoc9, "build hull statistics", -1);+ zzdef_(zinc, Zprocessed, "points processed", -1);+ zzdef_(zinc, Zretry, "retries due to precision problems", -1);+ zdef_(wmax, Wretrymax, " max. random joggle", -1);+ zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1);+ zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed);+ zdef_(zinc, Zinsidevisible, " ave. visible facets without an horizon neighbor", Zprocessed);+ zdef_(zadd, Zvisfacettot, " ave. facets deleted per iteration", Zprocessed);+ zdef_(zmax, Zvisfacetmax, " maximum", -1);+ zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed);+ zdef_(zmax, Zvisvertexmax, " maximum", -1);+ zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed);+ zdef_(zadd, Znewfacettot, "ave. new or merged facets per iteration", Zprocessed);+ zdef_(zmax, Znewfacetmax, " maximum(includes initial simplex)", -1);+ zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed);+ zdef_(wadd, Wnewbalance2, " standard deviation", -1);+ zdef_(wadd, Wpbalance, "average partition balance", Zpbalance);+ zdef_(wadd, Wpbalance2, " standard deviation", -1);+ zdef_(zinc, Zpbalance, " number of trials", -1);+ zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1);+ zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1);+ zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1);+ zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1);+ zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1);+ zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1);+ zdef_(zinc, Zgoodfacet, "good facets found", -1);+ zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1);+ zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1);+ zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1);+ zzdef_(zinc, Zcheckpart, " ave. distance tests per check", Ztotcheck);+}+void qh_allstatD(qhT *qh) {+ zdef_(zinc, Zvisit, "resets of visit_id", -1);+ zdef_(zinc, Zvvisit, " resets of vertex_visit", -1);+ zdef_(zmax, Zvisit2max, " max visit_id/2", -1);+ zdef_(zmax, Zvvisit2max, " max vertex_visit/2", -1);++ zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1);+ zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1);+ zdef_(zmax, Zdelvertexmax, " maximum vertices deleted per iteration", -1);+ zdef_(zinc, Zfindbest, "calls to findbest", -1);+ zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest);+ zdef_(zmax, Zfindbestmax, " max. facets tested", -1);+ zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest);+ zdef_(zinc, Zfindnew, "calls to findbestnew", -1);+ zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew);+ zdef_(zmax, Zfindnewmax, " max. facets tested", -1);+ zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew);+ zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1);+ zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1);+ zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon);+ zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1);+ zdef_(zinc, Zfindjump, " ave. clearly better", Zfindhorizon);+ zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1);+ zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1);+ zdef_(zinc, Zpartflip, " repartitioned coplanar points for flipped orientation", -1);+}+void qh_allstatE(qhT *qh) {+ zdef_(zinc, Zpartinside, "inside points", -1);+ zdef_(zinc, Zpartnear, " inside points kept with a facet", -1);+ zdef_(zinc, Zcoplanarinside, " inside points that were coplanar with a facet", -1);+ zdef_(zinc, Zbestlower, "calls to findbestlower", -1);+ zdef_(zinc, Zbestlowerv, " with search of vertex neighbors", -1);+ zdef_(zinc, Zbestlowerall, " with rare search of all facets", -1);+ zdef_(zmax, Zbestloweralln, " facets per search of all facets", -1);+ zdef_(wadd, Wmaxout, "difference in max_outside at final check", -1);+ zzdef_(zinc, Zpartitionall, "distance tests for initial partition", -1);+ zdef_(zinc, Ztotpartition, "partitions of a point", -1);+ zzdef_(zinc, Zpartition, "distance tests for partitioning", -1);+ zzdef_(zinc, Zdistcheck, "distance tests for checking flipped facets", -1);+ zzdef_(zinc, Zdistconvex, "distance tests for checking convexity", -1);+ zdef_(zinc, Zdistgood, "distance tests for checking good point", -1);+ zdef_(zinc, Zdistio, "distance tests for output", -1);+ zdef_(zinc, Zdiststat, "distance tests for statistics", -1);+ zdef_(zinc, Zdistplane, "total number of distance tests", -1);+ zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1);+ zzdef_(zinc, Zpartcoplanar, " distance tests for these partitions", -1);+ zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1);+}+void qh_allstatE2(qhT *qh) {+ zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1);+ zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1);+ zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup);+ zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1);+ zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge);+ zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1);+ zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1);++ zdef_(zdoc, Zdoc6, "statistics for determining merges", -1);+ zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1);+ zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1);+ zzdef_(zinc, Zbestdist, "distance tests for best merge", -1);+ zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1);+ zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1);+ zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1);+ zdef_(zinc, Zcoplanarcentrum, "coplanar centrums in getmergeset", -1);+ zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1);+}+void qh_allstatF(qhT *qh) {+ zdef_(zdoc, Zdoc7, "statistics for merging", -1);+ zdef_(zinc, Zpremergetot, "merge iterations", -1);+ zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot);+ zdef_(zadd, Zmergeinitmax, " maximum", -1);+ zdef_(zadd, Zmergesettot, " ave. additional non-convex ridges per iteration", Zpremergetot);+ zdef_(zadd, Zmergesetmax, " maximum additional in one pass", -1);+ zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1);+ zdef_(zadd, Zmergesettot2, " additional non-convex ridges", -1);+ zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1);+ zdef_(wmin, Wminvertex, "max distance of merged vertex below facet(or roundoff)", -1);+ zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1);+ zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1);+ zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1);+ zdef_(zinc, Zmergesimplex, "merged a simplex", -1);+ zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1);+ zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1);+ zzdef_(zadd, Zcyclefacettot, " ave. facets per cycle", Zcyclehorizon);+ zdef_(zmax, Zcyclefacetmax, " max. facets", -1);+ zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1);+ zdef_(zinc, Zmergenew, "new facets merged", -1);+ zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1);+ zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1);+ zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1);+ zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1);+ zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1);+ zdef_(zinc, Zneighbor, "merges due to redundant neighbors", -1);+ zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1);+}+void qh_allstatG(qhT *qh) {+ zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1);+ zdef_(wadd, Wacoplanartot, " average merge distance", Zacoplanar);+ zdef_(wmax, Wacoplanarmax, " maximum merge distance", -1);+ zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1);+ zdef_(wadd, Wcoplanartot, " average merge distance", Zcoplanar);+ zdef_(wmax, Wcoplanarmax, " maximum merge distance", -1);+ zdef_(zinc, Zconcave, "merges due to concave facets", -1);+ zdef_(wadd, Wconcavetot, " average merge distance", Zconcave);+ zdef_(wmax, Wconcavemax, " maximum merge distance", -1);+ zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1);+ zdef_(wadd, Wavoidoldtot, " average merge distance", Zavoidold);+ zdef_(wmax, Wavoidoldmax, " maximum merge distance", -1);+ zdef_(zinc, Zdegen, "merges due to degenerate facets", -1);+ zdef_(wadd, Wdegentot, " average merge distance", Zdegen);+ zdef_(wmax, Wdegenmax, " maximum merge distance", -1);+ zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1);+ zdef_(wadd, Wflippedtot, " average merge distance", Zflipped);+ zdef_(wmax, Wflippedmax, " maximum merge distance", -1);+ zdef_(zinc, Zduplicate, "merges due to duplicated ridges", -1);+ zdef_(wadd, Wduplicatetot, " average merge distance", Zduplicate);+ zdef_(wmax, Wduplicatemax, " maximum merge distance", -1);+}+void qh_allstatH(qhT *qh) {+ zdef_(zdoc, Zdoc8, "renamed vertex statistics", -1);+ zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1);+ zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1);+ zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1);+ zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1);+ zdef_(zinc, Zdupridge, " duplicate ridges detected", -1);+ zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1);+ zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1);+ zdef_(zinc, Zdropdegen, "degenerate facets due to dropped neighbors", -1);+ zdef_(zinc, Zdelfacetdup, " facets deleted because of no neighbors", -1);+ zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1);+ zdef_(zinc, Zremvertexdel, " deleted", -1);+ zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1);+ zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1);+ zdef_(zinc, Zintersect, "intersections found redundant vertices", -1);+ zdef_(zadd, Zintersecttot, " ave. number found per vertex", Zintersect);+ zdef_(zmax, Zintersectmax, " max. found for a vertex", -1);+ zdef_(zinc, Zvertexridge, NULL, -1);+ zdef_(zadd, Zvertexridgetot, " ave. number of ridges per tested vertex", Zvertexridge);+ zdef_(zmax, Zvertexridgemax, " max. number of ridges per tested vertex", -1);++ zdef_(zdoc, Zdoc10, "memory usage statistics(in bytes)", -1);+ zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1);+ zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1);+ zdef_(zadd, Zmempoints, "for input points, outside and coplanar sets, and qhT",-1);+ zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);+} /* allstat */++void qh_allstatI(qhT *qh) {+ qh->qhstat.vridges= qh->qhstat.next;+ zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1);+ zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1);+ zzdef_(wadd, Wridge, " ave. distance to ridge", Zridge);+ zzdef_(wmax, Wridgemax, " max. distance to ridge", -1);+ zzdef_(zinc, Zridgemid, "bounded ridges", -1);+ zzdef_(wadd, Wridgemid, " ave. distance of midpoint to ridge", Zridgemid);+ zzdef_(wmax, Wridgemidmax, " max. distance of midpoint to ridge", -1);+ zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1);+ zzdef_(wadd, Wridgeok, " ave. angle to ridge", Zridgeok);+ zzdef_(wmax, Wridgeokmax, " max. angle to ridge", -1);+ zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1);+ zzdef_(wadd, Wridge0, " ave. angle to ridge", Zridge0);+ zzdef_(wmax, Wridge0max, " max. angle to ridge", -1);++ zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1);+ zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1);+ zdef_(zadd, Ztricoplanartot, " ave. new facets created(may be deleted)", Ztricoplanar);+ zdef_(zmax, Ztricoplanarmax, " max. new facets created", -1);+ zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1);+ zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1);+ zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1);+} /* allstat */++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="allstatistics">-</a>++ qh_allstatistics()+ reset printed flag for all statistics+*/+void qh_allstatistics(qhT *qh) {+ int i;++ for(i=ZEND; i--; )+ qh->qhstat.printed[i]= False;+} /* allstatistics */++#if qh_KEEPstatistics+/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="collectstatistics">-</a>++ qh_collectstatistics()+ collect statistics for qh.facet_list++*/+void qh_collectstatistics(qhT *qh) {+ facetT *facet, *neighbor, **neighborp;+ vertexT *vertex, **vertexp;+ realT dotproduct, dist;+ int sizneighbors, sizridges, sizvertices, i;++ qh->old_randomdist= qh->RANDOMdist;+ qh->RANDOMdist= False;+ zval_(Zmempoints)= qh->num_points * qh->normal_size + sizeof(qhT);+ zval_(Zmemfacets)= 0;+ zval_(Zmemridges)= 0;+ zval_(Zmemvertices)= 0;+ zval_(Zangle)= 0;+ wval_(Wangle)= 0.0;+ zval_(Znumridges)= 0;+ zval_(Znumfacets)= 0;+ zval_(Znumneighbors)= 0;+ zval_(Znumvertices)= 0;+ zval_(Znumvneighbors)= 0;+ zval_(Znummergetot)= 0;+ zval_(Znummergemax)= 0;+ zval_(Zvertices)= qh->num_vertices - qh_setsize(qh, qh->del_vertices);+ if (qh->MERGING || qh->APPROXhull || qh->JOGGLEmax < REALmax/2)+ wmax_(Wmaxoutside, qh->max_outside);+ if (qh->MERGING)+ wmin_(Wminvertex, qh->min_vertex);+ FORALLfacets+ facet->seen= False;+ if (qh->DELAUNAY) {+ FORALLfacets {+ if (facet->upperdelaunay != qh->UPPERdelaunay)+ facet->seen= True; /* remove from angle statistics */+ }+ }+ FORALLfacets {+ if (facet->visible && qh->NEWfacets)+ continue;+ sizvertices= qh_setsize(qh, facet->vertices);+ sizneighbors= qh_setsize(qh, facet->neighbors);+ sizridges= qh_setsize(qh, facet->ridges);+ zinc_(Znumfacets);+ zadd_(Znumvertices, sizvertices);+ zmax_(Zmaxvertices, sizvertices);+ zadd_(Znumneighbors, sizneighbors);+ zmax_(Zmaxneighbors, sizneighbors);+ zadd_(Znummergetot, facet->nummerge);+ i= facet->nummerge; /* avoid warnings */+ zmax_(Znummergemax, i);+ if (!facet->simplicial) {+ if (sizvertices == qh->hull_dim) {+ zinc_(Znowsimplicial);+ }else {+ zinc_(Znonsimplicial);+ }+ }+ if (sizridges) {+ zadd_(Znumridges, sizridges);+ zmax_(Zmaxridges, sizridges);+ }+ zadd_(Zmemfacets, sizeof(facetT) + qh->normal_size + 2*sizeof(setT)+ + SETelemsize * (sizneighbors + sizvertices));+ if (facet->ridges) {+ zadd_(Zmemridges,+ sizeof(setT) + SETelemsize * sizridges + sizridges *+ (sizeof(ridgeT) + sizeof(setT) + SETelemsize * (qh->hull_dim-1))/2);+ }+ if (facet->outsideset)+ zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(qh, facet->outsideset));+ if (facet->coplanarset)+ zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(qh, facet->coplanarset));+ if (facet->seen) /* Delaunay upper envelope */+ continue;+ facet->seen= True;+ FOREACHneighbor_(facet) {+ if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge+ || neighbor->seen || !facet->normal || !neighbor->normal)+ continue;+ dotproduct= qh_getangle(qh, facet->normal, neighbor->normal);+ zinc_(Zangle);+ wadd_(Wangle, dotproduct);+ wmax_(Wanglemax, dotproduct)+ wmin_(Wanglemin, dotproduct)+ }+ if (facet->normal) {+ FOREACHvertex_(facet->vertices) {+ zinc_(Zdiststat);+ qh_distplane(qh, vertex->point, facet, &dist);+ wmax_(Wvertexmax, dist);+ wmin_(Wvertexmin, dist);+ }+ }+ }+ FORALLvertices {+ if (vertex->deleted)+ continue;+ zadd_(Zmemvertices, sizeof(vertexT));+ if (vertex->neighbors) {+ sizneighbors= qh_setsize(qh, vertex->neighbors);+ zadd_(Znumvneighbors, sizneighbors);+ zmax_(Zmaxvneighbors, sizneighbors);+ zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors);+ }+ }+ qh->RANDOMdist= qh->old_randomdist;+} /* collectstatistics */+#endif /* qh_KEEPstatistics */++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="initstatistics">-</a>++ qh_initstatistics(qh)+ initialize statistics++ notes:+ NOerrors -- qh_initstatistics can not use qh_errexit(), qh_fprintf, or qh.ferr+ On first call, only qhmem.ferr is defined. qh_memalloc is not setup.+ Also invoked by QhullQh().+*/+void qh_initstatistics(qhT *qh) {+ int i;+ realT realx;+ int intx;++ qh->qhstat.next= 0;+ qh_allstatA(qh);+ qh_allstatB(qh);+ qh_allstatC(qh);+ qh_allstatD(qh);+ qh_allstatE(qh);+ qh_allstatE2(qh);+ qh_allstatF(qh);+ qh_allstatG(qh);+ qh_allstatH(qh);+ qh_allstatI(qh);+ if (qh->qhstat.next > (int)sizeof(qh->qhstat.id)) {+ qh_fprintf(qh, qh->qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\+ qhstat.next %d should be <= sizeof(qh->qhstat.id) %d\n", qh->qhstat.next, (int)sizeof(qh->qhstat.id));+#if 0 /* for locating error, Znumridges should be duplicated */+ for(i=0; i < ZEND; i++) {+ int j;+ for(j=i+1; j < ZEND; j++) {+ if (qh->qhstat.id[i] == qh->qhstat.id[j]) {+ qh_fprintf(qh, qh->qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",+ qh->qhstat.id[i], i, j);+ }+ }+ }+#endif+ qh_exit(qh_ERRqhull); /* can not use qh_errexit() */+ }+ qh->qhstat.init[zinc].i= 0;+ qh->qhstat.init[zadd].i= 0;+ qh->qhstat.init[zmin].i= INT_MAX;+ qh->qhstat.init[zmax].i= INT_MIN;+ qh->qhstat.init[wadd].r= 0;+ qh->qhstat.init[wmin].r= REALmax;+ qh->qhstat.init[wmax].r= -REALmax;+ for(i=0; i < ZEND; i++) {+ if (qh->qhstat.type[i] > ZTYPEreal) {+ realx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r;+ qh->qhstat.stats[i].r= realx;+ }else if (qh->qhstat.type[i] != zdoc) {+ intx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i;+ qh->qhstat.stats[i].i= intx;+ }+ }+} /* initstatistics */++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="newstats">-</a>++ qh_newstats(qh, )+ returns True if statistics for zdoc++ returns:+ next zdoc+*/+boolT qh_newstats(qhT *qh, int idx, int *nextindex) {+ boolT isnew= False;+ int start, i;++ if (qh->qhstat.type[qh->qhstat.id[idx]] == zdoc)+ start= idx+1;+ else+ start= idx;+ for(i= start; i < qh->qhstat.next && qh->qhstat.type[qh->qhstat.id[i]] != zdoc; i++) {+ if (!qh_nostatistic(qh, qh->qhstat.id[i]) && !qh->qhstat.printed[qh->qhstat.id[i]])+ isnew= True;+ }+ *nextindex= i;+ return isnew;+} /* newstats */++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="nostatistic">-</a>++ qh_nostatistic(qh, index )+ true if no statistic to print+*/+boolT qh_nostatistic(qhT *qh, int i) {++ if ((qh->qhstat.type[i] > ZTYPEreal+ &&qh->qhstat.stats[i].r == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r)+ || (qh->qhstat.type[i] < ZTYPEreal+ &&qh->qhstat.stats[i].i == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i))+ return True;+ return False;+} /* nostatistic */++#if qh_KEEPstatistics+/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="printallstatistics">-</a>++ qh_printallstatistics(qh, fp, string )+ print all statistics with header 'string'+*/+void qh_printallstatistics(qhT *qh, FILE *fp, const char *string) {++ qh_allstatistics(qh);+ qh_collectstatistics(qh);+ qh_printstatistics(qh, fp, string);+ qh_memstatistics(qh, fp);+}+++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="printstatistics">-</a>++ qh_printstatistics(qh, fp, string )+ print statistics to a file with header 'string'+ skips statistics with qhstat.printed[] (reset with qh_allstatistics)++ see:+ qh_printallstatistics()+*/+void qh_printstatistics(qhT *qh, FILE *fp, const char *string) {+ int i, k;+ realT ave;++ if (qh->num_points != qh->num_vertices) {+ wval_(Wpbalance)= 0;+ wval_(Wpbalance2)= 0;+ }else+ wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),+ wval_(Wpbalance2), &ave);+ wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),+ wval_(Wnewbalance2), &ave);+ qh_fprintf(qh, fp, 9350, "\n\+%s\n\+ qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh->rbox_command,+ qh->qhull_command, qh_version, qh->qhull_options);+ qh_fprintf(qh, fp, 9351, "\nprecision constants:\n\+ %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\+ %6.2g max. roundoff error for distance computation('En')\n\+ %6.2g max. roundoff error for angle computations\n\+ %6.2g min. distance for outside points ('Wn')\n\+ %6.2g min. distance for visible facets ('Vn')\n\+ %6.2g max. distance for coplanar facets ('Un')\n\+ %6.2g max. facet width for recomputing centrum and area\n\+",+ qh->MAXabs_coord, qh->DISTround, qh->ANGLEround, qh->MINoutside,+ qh->MINvisible, qh->MAXcoplanar, qh->WIDEfacet);+ if (qh->KEEPnearinside)+ qh_fprintf(qh, fp, 9352, "\+ %6.2g max. distance for near-inside points\n", qh->NEARinside);+ if (qh->premerge_cos < REALmax/2) qh_fprintf(qh, fp, 9353, "\+ %6.2g max. cosine for pre-merge angle\n", qh->premerge_cos);+ if (qh->PREmerge) qh_fprintf(qh, fp, 9354, "\+ %6.2g radius of pre-merge centrum\n", qh->premerge_centrum);+ if (qh->postmerge_cos < REALmax/2) qh_fprintf(qh, fp, 9355, "\+ %6.2g max. cosine for post-merge angle\n", qh->postmerge_cos);+ if (qh->POSTmerge) qh_fprintf(qh, fp, 9356, "\+ %6.2g radius of post-merge centrum\n", qh->postmerge_centrum);+ qh_fprintf(qh, fp, 9357, "\+ %6.2g max. distance for merging two simplicial facets\n\+ %6.2g max. roundoff error for arithmetic operations\n\+ %6.2g min. denominator for divisions\n\+ zero diagonal for Gauss: ", qh->ONEmerge, REALepsilon, qh->MINdenom);+ for(k=0; k < qh->hull_dim; k++)+ qh_fprintf(qh, fp, 9358, "%6.2e ", qh->NEARzero[k]);+ qh_fprintf(qh, fp, 9359, "\n\n");+ for(i=0 ; i < qh->qhstat.next; )+ qh_printstats(qh, fp, i, &i);+} /* printstatistics */+#endif /* qh_KEEPstatistics */++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="printstatlevel">-</a>++ qh_printstatlevel(qh, fp, id )+ print level information for a statistic++ notes:+ nop if id >= ZEND, printed, or same as initial value+*/+void qh_printstatlevel(qhT *qh, FILE *fp, int id) {+#define NULLfield " "++ if (id >= ZEND || qh->qhstat.printed[id])+ return;+ if (qh->qhstat.type[id] == zdoc) {+ qh_fprintf(qh, fp, 9360, "%s\n", qh->qhstat.doc[id]);+ return;+ }+ if (qh_nostatistic(qh, id) || !qh->qhstat.doc[id])+ return;+ qh->qhstat.printed[id]= True;+ if (qh->qhstat.count[id] != -1+ && qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i == 0)+ qh_fprintf(qh, fp, 9361, " *0 cnt*");+ else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] == -1)+ qh_fprintf(qh, fp, 9362, "%7.2g", qh->qhstat.stats[id].r);+ else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] != -1)+ qh_fprintf(qh, fp, 9363, "%7.2g", qh->qhstat.stats[id].r/ qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i);+ else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] == -1)+ qh_fprintf(qh, fp, 9364, "%7d", qh->qhstat.stats[id].i);+ else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] != -1)+ qh_fprintf(qh, fp, 9365, "%7.3g", (realT) qh->qhstat.stats[id].i / qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i);+ qh_fprintf(qh, fp, 9366, " %s\n", qh->qhstat.doc[id]);+} /* printstatlevel */+++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="printstats">-</a>++ qh_printstats(qh, fp, index, nextindex )+ print statistics for a zdoc group++ returns:+ next zdoc if non-null+*/+void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex) {+ int j, nexti;++ if (qh_newstats(qh, idx, &nexti)) {+ qh_fprintf(qh, fp, 9367, "\n");+ for (j=idx; j<nexti; j++)+ qh_printstatlevel(qh, fp, qh->qhstat.id[j]);+ }+ if (nextindex)+ *nextindex= nexti;+} /* printstats */++#if qh_KEEPstatistics++/*-<a href="qh-stat_r.htm#TOC"+ >-------------------------------</a><a name="stddev">-</a>++ qh_stddev(num, tot, tot2, ave )+ compute the standard deviation and average from statistics++ tot2 is the sum of the squares+ notes:+ computes r.m.s.:+ (x-ave)^2+ == x^2 - 2x tot/num + (tot/num)^2+ == tot2 - 2 tot tot/num + tot tot/num+ == tot2 - tot ave+*/+realT qh_stddev(int num, realT tot, realT tot2, realT *ave) {+ realT stddev;++ *ave= tot/num;+ stddev= sqrt(tot2/num - *ave * *ave);+ return stddev;+} /* stddev */++#endif /* qh_KEEPstatistics */++#if !qh_KEEPstatistics+void qh_collectstatistics(qhT *qh) {}+void qh_printallstatistics(qhT *qh, FILE *fp, char *string) {};+void qh_printstatistics(qhT *qh, FILE *fp, char *string) {}+#endif+
+ C/user_r.c view
@@ -0,0 +1,527 @@+/*<html><pre> -<a href="qh-user_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ user.c+ user redefinable functions++ see user2_r.c for qh_fprintf, qh_malloc, qh_free++ see README.txt see COPYING.txt for copyright information.++ see libqhull_r.h for data structures, macros, and user-callable functions.++ see user_eg.c, user_eg2.c, and unix.c for examples.++ see user.h for user-definable constants++ use qh_NOmem in mem_r.h to turn off memory management+ use qh_NOmerge in user.h to turn off facet merging+ set qh_KEEPstatistics in user.h to 0 to turn off statistics++ This is unsupported software. You're welcome to make changes,+ but you're on your own if something goes wrong. Use 'Tc' to+ check frequently. Usually qhull will report an error if+ a data structure becomes inconsistent. If so, it also reports+ the last point added to the hull, e.g., 102. You can then trace+ the execution of qhull with "T4P102".++ Please report any errors that you fix to qhull@qhull.org++ Qhull-template is a template for calling qhull from within your application++ if you recompile and load this module, then user.o will not be loaded+ from qhull.a++ you can add additional quick allocation sizes in qh_user_memsizes++ if the other functions here are redefined to not use qh_print...,+ then io.o will not be loaded from qhull.a. See user_eg_r.c for an+ example. We recommend keeping io.o for the extra debugging+ information it supplies.+*/++#include "qhull_ra.h"++#include <stdarg.h>++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="qhull_template">-</a>++ Qhull-template+ Template for calling qhull from inside your program++ returns:+ exit code(see qh_ERR... in libqhull_r.h)+ all memory freed++ notes:+ This can be called any number of times.++*/+#if 0+{+ int dim; /* dimension of points */+ int numpoints; /* number of points */+ coordT *points; /* array of coordinates for each point */+ boolT ismalloc; /* True if qhull should free points in qh_freeqhull() or reallocation */+ char flags[]= "qhull Tv"; /* option flags for qhull, see qh_opt.htm */+ FILE *outfile= stdout; /* output from qh_produce_output(qh)+ use NULL to skip qh_produce_output(qh) */+ FILE *errfile= stderr; /* error messages from qhull code */+ int exitcode; /* 0 if no error from qhull */+ facetT *facet; /* set by FORALLfacets */+ int curlong, totlong; /* memory remaining after qh_memfreeshort */++ qhT qh_qh; /* Qhull's data structure. First argument of most calls */+ qhT *qh= &qh_qh; /* Alternatively -- qhT *qh= (qhT*)malloc(sizeof(qhT)) */++ QHULL_LIB_CHECK /* Check for compatible library */++ qh_zero(qh, errfile);++ /* initialize dim, numpoints, points[], ismalloc here */+ exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,+ flags, outfile, errfile);+ if (!exitcode) { /* if no error */+ /* 'qh->facet_list' contains the convex hull */+ FORALLfacets {+ /* ... your code ... */+ }+ }+ qh_freeqhull(qh, !qh_ALL);+ qh_memfreeshort(qh, &curlong, &totlong);+ if (curlong || totlong)+ qh_fprintf(qh, errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);+}+#endif++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="new_qhull">-</a>++ qh_new_qhull(qh, dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile )+ Run qhull and return results in qh.+ Returns exitcode (0 if no errors).+ Before first call, either call qh_zero(qh, errfile), or set qh to all zero.++ notes:+ do not modify points until finished with results.+ The qhull data structure contains pointers into the points array.+ do not call qhull functions before qh_new_qhull().+ The qhull data structure is not initialized until qh_new_qhull().+ do not call qh_init_A (global_r.c)++ Default errfile is stderr, outfile may be null+ qhull_cmd must start with "qhull "+ projects points to a new point array for Delaunay triangulations ('d' and 'v')+ transforms points into a new point array for halfspace intersection ('H')++ see:+ Qhull-template at the beginning of this file.+ An example of using qh_new_qhull is user_eg_r.c+*/+int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc,+ char *qhull_cmd, FILE *outfile, FILE *errfile) {+ /* gcc may issue a "might be clobbered" warning for dim, points, and ismalloc [-Wclobbered].+ These parameters are not referenced after a longjmp() and hence not clobbered.+ See http://stackoverflow.com/questions/7721854/what-sense-do-these-clobbered-variable-warnings-make */+ int exitcode, hulldim;+ boolT new_ismalloc;+ coordT *new_points;++ if(!errfile){+ errfile= stderr;+ }+ if (!qh->qhmem.ferr) {+ qh_meminit(qh, errfile);+ } else {+ qh_memcheck(qh);+ }+ if (strncmp(qhull_cmd, "qhull ", (size_t)6)) {+ qh_fprintf(qh, errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n");+ return qh_ERRinput;+ }+ qh_initqhull_start(qh, NULL, outfile, errfile);+ trace1((qh, qh->ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd));+ exitcode = setjmp(qh->errexit);+ if (!exitcode)+ {+ qh->NOerrexit = False;+ qh_initflags(qh, qhull_cmd);+ if (qh->DELAUNAY)+ qh->PROJECTdelaunay= True;+ if (qh->HALFspace) {+ /* points is an array of halfspaces,+ the last coordinate of each halfspace is its offset */+ hulldim= dim-1;+ qh_setfeasible(qh, hulldim);+ new_points= qh_sethalfspace_all(qh, dim, numpoints, points, qh->feasible_point);+ new_ismalloc= True;+ if (ismalloc)+ qh_free(points);+ }else {+ hulldim= dim;+ new_points= points;+ new_ismalloc= ismalloc;+ }+ qh_init_B(qh, new_points, numpoints, hulldim, new_ismalloc);+ qh_qhull(qh);+ qh_check_output(qh);+ if (outfile) {+ qh_produce_output(qh);+ }else {+ qh_prepare_output(qh);+ }+ if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)+ qh_check_points(qh);+ }+ qh->NOerrexit = True;+ return exitcode;+} /* new_qhull */++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="errexit">-</a>++ qh_errexit(qh, exitcode, facet, ridge )+ report and exit from an error+ report facet and ridge if non-NULL+ reports useful information such as last point processed+ set qh.FORCEoutput to print neighborhood of facet++ see:+ qh_errexit2() in libqhull_r.c for printing 2 facets++ design:+ check for error within error processing+ compute qh.hulltime+ print facet and ridge (if any)+ report commandString, options, qh.furthest_id+ print summary and statistics (including precision statistics)+ if qh_ERRsingular+ print help text for singular data set+ exit program via long jump (if defined) or exit()+*/+void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge) {++ if (qh->ERREXITcalled) {+ qh_fprintf(qh, qh->ferr, 8126, "\nqhull error while processing previous error. Exit program\n");+ qh_exit(qh_ERRqhull);+ }+ qh->ERREXITcalled= True;+ if (!qh->QHULLfinished)+ qh->hulltime= qh_CPUclock - qh->hulltime;+ qh_errprint(qh, "ERRONEOUS", facet, NULL, ridge, NULL);+ qh_fprintf(qh, qh->ferr, 8127, "\nWhile executing: %s | %s\n", qh->rbox_command, qh->qhull_command);+ qh_fprintf(qh, qh->ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);+ if (qh->furthest_id >= 0) {+ qh_fprintf(qh, qh->ferr, 8129, "Last point added to hull was p%d.", qh->furthest_id);+ if (zzval_(Ztotmerge))+ qh_fprintf(qh, qh->ferr, 8130, " Last merge was #%d.", zzval_(Ztotmerge));+ if (qh->QHULLfinished)+ qh_fprintf(qh, qh->ferr, 8131, "\nQhull has finished constructing the hull.");+ else if (qh->POSTmerging)+ qh_fprintf(qh, qh->ferr, 8132, "\nQhull has started post-merging.");+ qh_fprintf(qh, qh->ferr, 8133, "\n");+ }+ if (qh->FORCEoutput && (qh->QHULLfinished || (!facet && !ridge)))+ qh_produce_output(qh);+ else if (exitcode != qh_ERRinput) {+ if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh->hull_dim+1) {+ qh_fprintf(qh, qh->ferr, 8134, "\nAt error exit:\n");+ qh_printsummary(qh, qh->ferr);+ if (qh->PRINTstatistics) {+ qh_collectstatistics(qh);+ qh_printstatistics(qh, qh->ferr, "at error exit");+ qh_memstatistics(qh, qh->ferr);+ }+ }+ if (qh->PRINTprecision)+ qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL);+ }+ if (!exitcode)+ exitcode= qh_ERRqhull;+ else if (exitcode == qh_ERRsingular)+ qh_printhelp_singular(qh, qh->ferr);+ else if (exitcode == qh_ERRprec && !qh->PREmerge)+ qh_printhelp_degenerate(qh, qh->ferr);+ if (qh->NOerrexit) {+ qh_fprintf(qh, qh->ferr, 6187, "qhull error while ending program, or qh->NOerrexit not cleared after setjmp(). Exit program with error.\n");+ qh_exit(qh_ERRqhull);+ }+ qh->ERREXITcalled= False;+ qh->NOerrexit= True;+ qh->ALLOWrestart= False; /* longjmp will undo qh_build_withrestart */+ longjmp(qh->errexit, exitcode);+} /* errexit */+++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="errprint">-</a>++ qh_errprint(qh, fp, string, atfacet, otherfacet, atridge, atvertex )+ prints out the information of facets and ridges to fp+ also prints neighbors and geomview output++ notes:+ except for string, any parameter may be NULL+*/+void qh_errprint(qhT *qh, const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {+ int i;++ if (atfacet) {+ qh_fprintf(qh, qh->ferr, 8135, "%s FACET:\n", string);+ qh_printfacet(qh, qh->ferr, atfacet);+ }+ if (otherfacet) {+ qh_fprintf(qh, qh->ferr, 8136, "%s OTHER FACET:\n", string);+ qh_printfacet(qh, qh->ferr, otherfacet);+ }+ if (atridge) {+ qh_fprintf(qh, qh->ferr, 8137, "%s RIDGE:\n", string);+ qh_printridge(qh, qh->ferr, atridge);+ if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet)+ qh_printfacet(qh, qh->ferr, atridge->top);+ if (atridge->bottom+ && atridge->bottom != atfacet && atridge->bottom != otherfacet)+ qh_printfacet(qh, qh->ferr, atridge->bottom);+ if (!atfacet)+ atfacet= atridge->top;+ if (!otherfacet)+ otherfacet= otherfacet_(atridge, atfacet);+ }+ if (atvertex) {+ qh_fprintf(qh, qh->ferr, 8138, "%s VERTEX:\n", string);+ qh_printvertex(qh, qh->ferr, atvertex);+ }+ if (qh->fout && qh->FORCEoutput && atfacet && !qh->QHULLfinished && !qh->IStracing) {+ qh_fprintf(qh, qh->ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n");+ for (i=0; i < qh_PRINTEND; i++) /* use fout for geomview output */+ qh_printneighborhood(qh, qh->fout, qh->PRINTout[i], atfacet, otherfacet,+ !qh_ALL);+ }+} /* errprint */+++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="printfacetlist">-</a>++ qh_printfacetlist(qh, fp, facetlist, facets, printall )+ print all fields for a facet list and/or set of facets to fp+ if !printall,+ only prints good facets++ notes:+ also prints all vertices+*/+void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall) {+ facetT *facet, **facetp;++ qh_printbegin(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);+ FORALLfacet_(facetlist)+ qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);+ FOREACHfacet_(facets)+ qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);+ qh_printend(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);+} /* printfacetlist */+++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printhelp_degenerate">-</a>++ qh_printhelp_degenerate(qh, fp )+ prints descriptive message for precision error++ notes:+ no message if qh_QUICKhelp+*/+void qh_printhelp_degenerate(qhT *qh, FILE *fp) {++ if (qh->MERGEexact || qh->PREmerge || qh->JOGGLEmax < REALmax/2)+ qh_fprintf(qh, fp, 9368, "\n\+A Qhull error has occurred. Qhull should have corrected the above\n\+precision error. Please send the input and all of the output to\n\+qhull_bug@qhull.org\n");+ else if (!qh_QUICKhelp) {+ qh_fprintf(qh, fp, 9369, "\n\+Precision problems were detected during construction of the convex hull.\n\+This occurs because convex hull algorithms assume that calculations are\n\+exact, but floating-point arithmetic has roundoff errors.\n\+\n\+To correct for precision problems, do not use 'Q0'. By default, Qhull\n\+selects 'C-0' or 'Qx' and merges non-convex facets. With option 'QJ',\n\+Qhull joggles the input to prevent precision problems. See \"Imprecision\n\+in Qhull\" (qh-impre.htm).\n\+\n\+If you use 'Q0', the output may include\n\+coplanar ridges, concave ridges, and flipped facets. In 4-d and higher,\n\+Qhull may produce a ridge with four neighbors or two facets with the same \n\+vertices. Qhull reports these events when they occur. It stops when a\n\+concave ridge, flipped facet, or duplicate facet occurs.\n");+#if REALfloat+ qh_fprintf(qh, fp, 9370, "\+\n\+Qhull is currently using single precision arithmetic. The following\n\+will probably remove the precision problems:\n\+ - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n");+#endif+ if (qh->DELAUNAY && !qh->SCALElast && qh->MAXabs_coord > 1e4)+ qh_fprintf(qh, fp, 9371, "\+\n\+When computing the Delaunay triangulation of coordinates > 1.0,\n\+ - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n");+ if (qh->DELAUNAY && !qh->ATinfinity)+ qh_fprintf(qh, fp, 9372, "\+When computing the Delaunay triangulation:\n\+ - use 'Qz' to add a point at-infinity. This reduces precision problems.\n");++ qh_fprintf(qh, fp, 9373, "\+\n\+If you need triangular output:\n\+ - use option 'Qt' to triangulate the output\n\+ - use option 'QJ' to joggle the input points and remove precision errors\n\+ - use option 'Ft'. It triangulates non-simplicial facets with added points.\n\+\n\+If you must use 'Q0',\n\+try one or more of the following options. They can not guarantee an output.\n\+ - use 'QbB' to scale the input to a cube.\n\+ - use 'Po' to produce output and prevent partitioning for flipped facets\n\+ - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\+ - options 'Qf', 'Qbb', and 'QR0' may also help\n",+ qh->DISTround);+ qh_fprintf(qh, fp, 9374, "\+\n\+To guarantee simplicial output:\n\+ - use option 'Qt' to triangulate the output\n\+ - use option 'QJ' to joggle the input points and remove precision errors\n\+ - use option 'Ft' to triangulate the output by adding points\n\+ - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\+");+ }+} /* printhelp_degenerate */+++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="printhelp_narrowhull">-</a>++ qh_printhelp_narrowhull(qh, minangle )+ Warn about a narrow hull++ notes:+ Alternatively, reduce qh_WARNnarrow in user.h++*/+void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle) {++ qh_fprintf(qh, fp, 9375, "qhull precision warning: \n\+The initial hull is narrow (cosine of min. angle is %.16f).\n\+Is the input lower dimensional (e.g., on a plane in 3-d)? Qhull may\n\+produce a wide facet. Options 'QbB' (scale to unit box) or 'Qbb' (scale\n\+last coordinate) may remove this warning. Use 'Pp' to skip this warning.\n\+See 'Limitations' in qh-impre.htm.\n",+ -minangle); /* convert from angle between normals to angle between facets */+} /* printhelp_narrowhull */++/*-<a href="qh-io_r.htm#TOC"+ >-------------------------------</a><a name="printhelp_singular">-</a>++ qh_printhelp_singular(qh, fp )+ prints descriptive message for singular input+*/+void qh_printhelp_singular(qhT *qh, FILE *fp) {+ facetT *facet;+ vertexT *vertex, **vertexp;+ realT min, max, *coord, dist;+ int i,k;++ qh_fprintf(qh, fp, 9376, "\n\+The input to qhull appears to be less than %d dimensional, or a\n\+computation has overflowed.\n\n\+Qhull could not construct a clearly convex simplex from points:\n",+ qh->hull_dim);+ qh_printvertexlist(qh, fp, "", qh->facet_list, NULL, qh_ALL);+ if (!qh_QUICKhelp)+ qh_fprintf(qh, fp, 9377, "\n\+The center point is coplanar with a facet, or a vertex is coplanar\n\+with a neighboring facet. The maximum round off error for\n\+computing distances is %2.2g. The center point, facets and distances\n\+to the center point are as follows:\n\n", qh->DISTround);+ qh_printpointid(qh, fp, "center point", qh->hull_dim, qh->interior_point, qh_IDunknown);+ qh_fprintf(qh, fp, 9378, "\n");+ FORALLfacets {+ qh_fprintf(qh, fp, 9379, "facet");+ FOREACHvertex_(facet->vertices)+ qh_fprintf(qh, fp, 9380, " p%d", qh_pointid(qh, vertex->point));+ zinc_(Zdistio);+ qh_distplane(qh, qh->interior_point, facet, &dist);+ qh_fprintf(qh, fp, 9381, " distance= %4.2g\n", dist);+ }+ if (!qh_QUICKhelp) {+ if (qh->HALFspace)+ qh_fprintf(qh, fp, 9382, "\n\+These points are the dual of the given halfspaces. They indicate that\n\+the intersection is degenerate.\n");+ qh_fprintf(qh, fp, 9383,"\n\+These points either have a maximum or minimum x-coordinate, or\n\+they maximize the determinant for k coordinates. Trial points\n\+are first selected from points that maximize a coordinate.\n");+ if (qh->hull_dim >= qh_INITIALmax)+ qh_fprintf(qh, fp, 9384, "\n\+Because of the high dimension, the min x-coordinate and max-coordinate\n\+points are used if the determinant is non-zero. Option 'Qs' will\n\+do a better, though much slower, job. Instead of 'Qs', you can change\n\+the points by randomly rotating the input with 'QR0'.\n");+ }+ qh_fprintf(qh, fp, 9385, "\nThe min and max coordinates for each dimension are:\n");+ for (k=0; k < qh->hull_dim; k++) {+ min= REALmax;+ max= -REALmin;+ for (i=qh->num_points, coord= qh->first_point+k; i--; coord += qh->hull_dim) {+ maximize_(max, *coord);+ minimize_(min, *coord);+ }+ qh_fprintf(qh, fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min);+ }+ if (!qh_QUICKhelp) {+ qh_fprintf(qh, fp, 9387, "\n\+If the input should be full dimensional, you have several options that\n\+may determine an initial simplex:\n\+ - use 'QJ' to joggle the input and make it full dimensional\n\+ - use 'QbB' to scale the points to the unit cube\n\+ - use 'QR0' to randomly rotate the input for different maximum points\n\+ - use 'Qs' to search all points for the initial simplex\n\+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\+ - trace execution with 'T3' to see the determinant for each point.\n",+ qh->DISTround);+#if REALfloat+ qh_fprintf(qh, fp, 9388, "\+ - recompile qhull for realT precision(#define REALfloat 0 in libqhull_r.h).\n");+#endif+ qh_fprintf(qh, fp, 9389, "\n\+If the input is lower dimensional:\n\+ - use 'QJ' to joggle the input and make it full dimensional\n\+ - use 'Qbk:0Bk:0' to delete coordinate k from the input. You should\n\+ pick the coordinate with the least range. The hull will have the\n\+ correct topology.\n\+ - determine the flat containing the points, rotate the points\n\+ into a coordinate plane, and delete the other coordinates.\n\+ - add one or more points to make the input full dimensional.\n\+");+ }+} /* printhelp_singular */++/*-<a href="qh-globa_r.htm#TOC"+ >-------------------------------</a><a name="user_memsizes">-</a>++ qh_user_memsizes(qh)+ allocate up to 10 additional, quick allocation sizes++ notes:+ increase maximum number of allocations in qh_initqhull_mem()+*/+void qh_user_memsizes(qhT *qh) {++ QHULL_UNUSED(qh)+ /* qh_memsize(qh, size); */+} /* user_memsizes */++
+ C/usermem_r.c view
@@ -0,0 +1,94 @@+/*<html><pre> -<a href="qh-user_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ usermem_r.c+ qh_exit(), qh_free(), and qh_malloc()++ See README.txt.++ If you redefine one of these functions you must redefine all of them.+ If you recompile and load this file, then usermem.o will not be loaded+ from qhull.a or qhull.lib++ See libqhull_r.h for data structures, macros, and user-callable functions.+ See user_r.c for qhull-related, redefinable functions+ see user_r.h for user-definable constants+ See userprintf_r.c for qh_fprintf and userprintf_rbox_r.c for qh_fprintf_rbox++ Please report any errors that you fix to qhull@qhull.org+*/++#include "libqhull_r.h"++#include <stdarg.h>+#include <stdlib.h>++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="qh_exit">-</a>++ qh_exit( exitcode )+ exit program++ notes:+ qh_exit() is called when qh_errexit() and longjmp() are not available.++ This is the only use of exit() in Qhull+ To replace qh_exit with 'throw', see libqhullcpp/usermem_r-cpp.cpp+*/+void qh_exit(int exitcode) {+ exit(exitcode);+} /* exit */++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="qh_fprintf_stderr">-</a>++ qh_fprintf_stderr( msgcode, format, list of args )+ fprintf to stderr with msgcode (non-zero)++ notes:+ qh_fprintf_stderr() is called when qh->ferr is not defined, usually due to an initialization error+ + It is typically followed by qh_errexit().++ Redefine this function to avoid using stderr++ Use qh_fprintf [userprintf_r.c] for normal printing+*/+void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) {+ va_list args;++ va_start(args, fmt);+ if(msgcode)+ fprintf(stderr, "QH%.4d ", msgcode);+ vfprintf(stderr, fmt, args);+ va_end(args);+} /* fprintf_stderr */++/*-<a href="qh-user_r.htm#TOC"+>-------------------------------</a><a name="qh_free">-</a>++ qh_free(qhT *qh, mem )+ free memory++ notes:+ same as free()+ No calls to qh_errexit() +*/+void qh_free(void *mem) {+ free(mem);+} /* free */++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="qh_malloc">-</a>++ qh_malloc( mem )+ allocate memory++ notes:+ same as malloc()+*/+void *qh_malloc(size_t size) {+ return malloc(size);+} /* malloc */++
+ C/userprintf_r.c view
@@ -0,0 +1,65 @@+/*<html><pre> -<a href="qh-user_r.htm"+ >-------------------------------</a><a name="TOP">-</a>++ userprintf_r.c+ qh_fprintf()++ see README.txt see COPYING.txt for copyright information.++ If you recompile and load this file, then userprintf_r.o will not be loaded+ from qhull.a or qhull.lib++ See libqhull_r.h for data structures, macros, and user-callable functions.+ See user_r.c for qhull-related, redefinable functions+ see user_r.h for user-definable constants+ See usermem_r.c for qh_exit(), qh_free(), and qh_malloc()+ see Qhull.cpp and RboxPoints.cpp for examples.++ Please report any errors that you fix to qhull@qhull.org+*/++#include "libqhull_r.h"++#include <stdarg.h>+#include <stdio.h>+#include <stdlib.h>++/*-<a href="qh-user_r.htm#TOC"+ >-------------------------------</a><a name="qh_fprintf">-</a>++ qh_fprintf(qh, fp, msgcode, format, list of args )+ print arguments to *fp according to format+ Use qh_fprintf_rbox() for rboxlib_r.c++ notes:+ same as fprintf()+ fgets() is not trapped like fprintf()+ exit qh_fprintf via qh_errexit()+ may be called for errors in qh_initstatistics and qh_meminit+*/++void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {+ va_list args;++ if (!fp) {+ if(!qh){+ qh_fprintf_stderr(6241, "userprintf_r.c: fp and qh not defined for qh_fprintf '%s'", fmt);+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */+ }+ /* could use qh->qhmem.ferr, but probably better to be cautious */+ qh_fprintf_stderr(6232, "Qhull internal error (userprintf_r.c): fp is 0. Wrong qh_fprintf called.\n");+ qh_errexit(qh, 6232, NULL, NULL);+ }+ va_start(args, fmt);+ if (qh && qh->ANNOTATEoutput) {+ fprintf(fp, "[QH%.4d]", msgcode);+ }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) {+ fprintf(fp, "QH%.4d ", msgcode);+ }+ vfprintf(fp, fmt, args);+ va_end(args);++ /* Place debugging traps here. Use with option 'Tn' */++} /* qh_fprintf */+
+ C/utils.c view
@@ -0,0 +1,91 @@+#include <stdlib.h> // to use realloc+#include <math.h> // to use NAN+#include <stdio.h> // to use printf++double* getpoint(double* points, unsigned dim, unsigned id){+ double* out = malloc(dim * sizeof(double));+ for(unsigned i=0; i < dim; i++){+ out[i] = points[id*dim+i];+ }+ return out;+}++/* dot product of two vectors */+double dotproduct(double* p1, double* p2, unsigned dim){+ double out = 0;+ for(unsigned i=0; i < dim; i++){+ out += p1[i] * p2[i];+ }+ return out;+}++/* middle of segment [p1,p2] */+double* middle(double* p1, double* p2, unsigned dim){+ double* out = malloc(dim * sizeof(double));+ for(unsigned i=0; i<dim; i++){+ out[i] = (p1[i] + p2[i])/2;+ }+ return out;+}++/* vector of NANs */+double* nanvector(int dim){+ double* out = malloc(dim * sizeof(double));+ for(unsigned i=0; i < dim; i++){+ out[i] = NAN;+ }+ return out;+}++// to use the qsort function+int cmpfunc (const void * a, const void * b) {+ return ( *(int*)a - *(int*)b );+}+int cmpfuncdbl (const void * a, const void * b) {+ return ( *(double*)a - *(double*)b > 0 ? 1 : -1);+}+void qsortu(unsigned* vector, unsigned length){+ qsort(vector, length, sizeof(unsigned), cmpfunc);+}+++double square(double x){+ return x*x;+}++/* append to a vector of unsigned */+void appendu(unsigned x, unsigned** array, unsigned length, unsigned* flag){+ *flag = 1;+ for(unsigned i=0; i<length; i++){+ if(x==*(*array + i)){+ *flag = 0;+ break;+ }+ }+ if(*flag==1){+ *array = realloc(*array, (length+1)*sizeof(unsigned));+ if(*array == NULL){+ printf("realloc failure - exiting");+ exit(1);+ }+ *(*array + length) = x;+ }+}++/* make a vector of zeros */+unsigned* uzeros(unsigned length){+ unsigned* out = malloc(length * sizeof(unsigned));+ for(unsigned i=0; i < length; i++){+ out[i] = 0;+ }+ return out;+}++/* squared distance between two points */+double squaredDistance(double* p1, double* p2, unsigned dim){+ double out = 0;+ for(unsigned i=0; i < dim; i++){+ out += square(p1[i] - p2[i]);+ }+ return out;+}
+ GPL_3 view
@@ -0,0 +1,674 @@+ GNU GENERAL PUBLIC LICENSE+ Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The GNU General Public License is a free, copyleft license for+software and other kinds of works.++ The licenses for most software and other practical works are designed+to take away your freedom to share and change the works. By contrast,+the GNU General Public License is intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users. We, the Free Software Foundation, use the+GNU General Public License for most of our software; it applies also to+any other work released this way by its authors. You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++ To protect your rights, we need to prevent others from denying you+these rights or asking you to surrender the rights. Therefore, you have+certain responsibilities if you distribute copies of the software, or if+you modify it: responsibilities to respect the freedom of others.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must pass on to the recipients the same+freedoms that you received. You must make sure that they, too, receive+or can get the source code. And you must show them these terms so they+know their rights.++ Developers that use the GNU GPL protect your rights with two steps:+(1) assert copyright on the software, and (2) offer you this License+giving you legal permission to copy, distribute and/or modify it.++ For the developers' and authors' protection, the GPL clearly explains+that there is no warranty for this free software. For both users' and+authors' sake, the GPL requires that modified versions be marked as+changed, so that their problems will not be attributed erroneously to+authors of previous versions.++ Some devices are designed to deny users access to install or run+modified versions of the software inside them, although the manufacturer+can do so. This is fundamentally incompatible with the aim of+protecting users' freedom to change the software. The systematic+pattern of such abuse occurs in the area of products for individuals to+use, which is precisely where it is most unacceptable. Therefore, we+have designed this version of the GPL to prohibit the practice for those+products. If such problems arise substantially in other domains, we+stand ready to extend this provision to those domains in future versions+of the GPL, as needed to protect the freedom of users.++ Finally, every program is threatened constantly by software patents.+States should not allow patents to restrict development and use of+software on general-purpose computers, but in those that do, we wish to+avoid the special danger that patents applied to a free program could+make it effectively proprietary. To prevent this, the GPL assures that+patents cannot be used to render the program non-free.++ The precise terms and conditions for copying, distribution and+modification follow.++ TERMS AND CONDITIONS++ 0. Definitions.++ "This License" refers to version 3 of the GNU General Public License.++ "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++ "The Program" refers to any copyrightable work licensed under this+License. Each licensee is addressed as "you". "Licensees" and+"recipients" may be individuals or organizations.++ To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy. The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++ A "covered work" means either the unmodified Program or a work based+on the Program.++ To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy. Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++ To "convey" a work means any kind of propagation that enables other+parties to make or receive copies. Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++ An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License. If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++ 1. Source Code.++ The "source code" for a work means the preferred form of the work+for making modifications to it. "Object code" means any non-source+form of a work.++ A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++ The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form. A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++ The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities. However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work. For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++ The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++ The Corresponding Source for a work in source code form is that+same work.++ 2. Basic Permissions.++ All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met. This License explicitly affirms your unlimited+permission to run the unmodified Program. The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work. This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++ You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force. You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright. Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++ Conveying under any other circumstances is permitted solely under+the conditions stated below. Sublicensing is not allowed; section 10+makes it unnecessary.++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++ No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++ When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++ 4. Conveying Verbatim Copies.++ You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++ You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++ 5. Conveying Modified Source Versions.++ You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++ a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.++ b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under section+ 7. This requirement modifies the requirement in section 4 to+ "keep intact all notices".++ c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.++ d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.++ A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit. Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++ 6. Conveying Non-Source Forms.++ You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++ a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.++ b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the+ Corresponding Source from a network server at no charge.++ c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.++ d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.++ e) Convey the object code using peer-to-peer transmission, provided+ you inform other peers where the object code and Corresponding+ Source of the work are being offered to the general public at no+ charge under subsection 6d.++ A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++ A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling. In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage. For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product. A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++ "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source. The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++ If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information. But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++ The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed. Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++ Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++ 7. Additional Terms.++ "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law. If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++ When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it. (Additional permissions may be written to require their own+removal in certain cases when you modify the work.) You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++ Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++ a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or++ b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or++ c) Prohibiting misrepresentation of the origin of that material, or+ requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or++ d) Limiting the use for publicity purposes of names of licensors or+ authors of the material; or++ e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or++ f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions of+ it) with contractual assumptions of liability to the recipient, for+ any liability that these contractual assumptions directly impose on+ those licensors and authors.++ All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10. If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term. If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++ If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++ Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++ 8. Termination.++ You may not propagate or modify a covered work except as expressly+provided under this License. Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++ However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++ Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++ Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License. If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++ 9. Acceptance Not Required for Having Copies.++ You are not required to accept this License in order to receive or+run a copy of the Program. Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance. However,+nothing other than this License grants you permission to propagate or+modify any covered work. These actions infringe copyright if you do+not accept this License. Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++ 10. Automatic Licensing of Downstream Recipients.++ Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License. You are not responsible+for enforcing compliance by third parties with this License.++ An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations. If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++ You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License. For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++ 11. Patents.++ A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based. The+work thus licensed is called the contributor's "contributor version".++ A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version. For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++ Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++ In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement). To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++ If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients. "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++ If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++ A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License. You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++ Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++ 12. No Surrender of Others' Freedom.++ If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all. For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++ 13. Use with the GNU Affero General Public License.++ Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU Affero General Public License into a single+combined work, and to convey the resulting work. The terms of this+License will continue to apply to the part which is the covered work,+but the special requirements of the GNU Affero General Public License,+section 13, concerning interaction through a network will apply to the+combination as such.++ 14. Revised Versions of this License.++ The Free Software Foundation may publish revised and/or new versions of+the GNU General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++ Each version is given a distinguishing version number. If the+Program specifies that a certain numbered version of the GNU General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation. If the Program does not specify a version number of the+GNU General Public License, you may choose any version ever published+by the Free Software Foundation.++ If the Program specifies that a proxy can decide which future+versions of the GNU General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++ Later license versions may give you additional or different+permissions. However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++ 15. Disclaimer of Warranty.++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++ 16. Limitation of Liability.++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++ 17. Interpretation of Sections 15 and 16.++ If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++ If the program does terminal interaction, make it output a short+notice like this when it starts in an interactive mode:++ <program> Copyright (C) <year> <name of author>+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, your program's commands+might be different; for a GUI interface, you would use an "about box".++ You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU GPL, see+<http://www.gnu.org/licenses/>.++ The GNU General Public License does not permit incorporating your program+into proprietary programs. If your program is a subroutine library, you+may consider it more useful to permit linking proprietary applications with+the library. If this is what you want to do, use the GNU Lesser General+Public License instead of this License. But first, please read+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+ README.md view
@@ -0,0 +1,476 @@+# qhull++Delaunay triangulation, Voronoi diagrams and convex hulls.+Based on the `qhull` C library.++## Delaunay tesselation++Consider this list of vertices (actually these are the vertices of a+polyhedron):++```haskell+vertices = [+ [ -5, -5, 16 ] -- 0+ , [ -5, 8, 3 ] -- 1+ , [ 4, -1, 3 ] -- 2+ , [ 4, -5, 7 ] -- 3+ , [ 4, -1, -10 ] -- 4+ , [ 4, -5, -10 ] -- 5+ , [ -5, 8, -10 ] -- 6+ , [ -5, -5, -10 ] -- 7+ ]+```++The `delaunay` function splits the polyhedron into simplices, the tiles of the+tesselation:++```haskell+> import Delaunay+> d <- delaunay vertices False False+> _tiles d+fromList+ [ ( 0+ , Tile+ { _simplex =+ Simplex+ { _points =+ fromList+ [ ( 2 , [ 4.0 , -1.0 , 3.0 ] )+ , ( 4 , [ 4.0 , -1.0 , -10.0 ] )+ , ( 5 , [ 4.0 , -5.0 , -10.0 ] )+ , ( 7 , [ -5.0 , -5.0 , -10.0 ] )+ ]+ , _circumcenter =+ [ -0.5000000000000009 , -3.0 , -3.499999999999999 ]+ , _circumradius = 8.154753215150047+ , _volume = 78.0+ }+ , _neighborsIds = fromList [ 1 , 3 ]+ , _facetsIds = fromList [ 0 , 1 , 2 , 3 ]+ , _family = Nothing+ , _toporiented = False+ }+ )+ , ( 1+ , Tile+ { _simplex =+ ......+```++The field `_tiles` is a map of `Tile` objects. The keys of the map are+the tiles identifiers. A `Tile` object has five fields:++- `_simplex`, a `Simplex` object;++- `_neighborsIds`, a set of tiles identifiers, the neighbors of the tile;++- `facetsIds`, a set of facets identifiers, the facets of the tile;++- `family`, two tiles of the same family share the same circumcenter;++- `toporiented`, Boolean, whether the tile is top-oriented.++A `Simplex` object has four fields:++- `_points`, the vertices of the simplex, actually a map of the vertices+identifiers to their coordinates++- `_circumcenter`, the coordinates of the circumcenter of the simplex;++- `_circumradius`, the circumradius;++- `_volume`, the volume of the simplex (the area in dimension 2, the+ length in dimension 1).++Another field of the output of `delaunay` is `_tilefacets`:++```haskell+> _tilefacets d+fromList+ [ ( 0+ , TileFacet+ { _subsimplex =+ Simplex+ { _points =+ fromList+ [ ( 4 , [ 4.0 , -1.0 , -10.0 ] )+ , ( 5 , [ 4.0 , -5.0 , -10.0 ] )+ , ( 7 , [ -5.0 , -5.0 , -10.0 ] )+ ]+ , _circumcenter = [ -0.5000000000000009 , -3.0 , -10.0 ]+ , _circumradius = 4.924428900898053+ , _volume = 36.0+ }+ , _facetOf = fromList [ 0 ]+ , _normal = [ 0.0 , 0.0 , -1.0 ]+ , _offset = -10.0+ }+ )+ , ( 1+ , TileFacet+ { _subsimplex =+ ......+```++This is a map of `TileFacet` objects. A tile facet is a subsimplex. The keys of+the map are the identifiers of the facets.+A `TileFacet` object has four fields: `_subsimplex`, a `Simplex` object,+`_facetOf`, the identifiers of the tiles this facet belongs to (a set of one+or two integers), `_normal`, the normal of the facet, and `offset`, the offset+of the facet.++Finally, the output of `delaunay` has a `_sites` field, the vertices with+additional information:++```haskell+> _sites d+fromList+ [ ( 0+ , Site+ { _point = [ -5.0 , -5.0 , 16.0 ]+ , _neighsitesIds = fromList [ 1 , 3 , 7 ]+ , _neighfacetsIds = fromList [ 15 , 16 , 17 ]+ , _neightilesIds = fromList [ 5 ]+ }+ )+ , ( 1+ , Site+ ......+```++This is a map of `Site` objects. The keys of the map are the identifiers of+the vertices. A `Site` object has four fields:++- `_point`, the coordinates of the vertex;++- `_neighsitesIds`, the identifiers of the connected vertices;++- `_neighfacetsIds`, a set of integers, the identifiers of the facets the+vertex belongs to;++- `_neightilesIds`, the set of the identifiers of the tiles the vertex belongs+to.++[](https://gfycat.com/FreeFaithfulArgali)+++## Voronoi diagrams++The library allows to get the Voronoi diagram of a list of sites (vertices)+from the Delaunay tesselation. Here is a 3D example.++```haskell+centricCuboctahedron :: [[Double]]+centricCuboctahedron = [[i,j,0] | i <- [-1,1], j <- [-1,1]] +++ [[i,0,j] | i <- [-1,1], j <- [-1,1]] +++ [[0,i,j] | i <- [-1,1], j <- [-1,1]] +++ [[0,0,0]]+import Delaunay+import Voronoi3D+d <- delaunay centricCuboctahedron False False+v = voronoi3 d+```++In some circumstances, one has to run the Delaunay tesselation including the+degenerate tiles in order to get the correct Voronoi diagram, that is to say+`delaunay vertices False True`.++The output of `voronoi3` is a list of Voronoi cells given as pairs, each pair+consisting of a site and a list of edges.+This is the cell of the center `[0, 0, 0]`:++```haskell+> last v+( [ 0.0 , 0.0 , 0.0 ]+, [ Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( 0.0 , 0.0 , 1.0 ) )+ , Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( 0.0 , -1.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( -1.0 , 0.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , 0.5 , 0.5 ) , ( 0.0 , 0.0 , 1.0 ) )+ , Edge3 ( ( -0.5 , 0.5 , 0.5 ) , ( 0.0 , 1.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , 0.5 , 0.5 ) , ( -1.0 , 0.0 , 0.0 ) )+ , Edge3 ( ( 0.5 , -0.5 , 0.5 ) , ( 0.0 , 0.0 , 1.0 ) )+ , Edge3 ( ( 0.5 , -0.5 , 0.5 ) , ( 0.0 , -1.0 , 0.0 ) )+ , Edge3 ( ( 0.5 , -0.5 , 0.5 ) , ( 1.0 , 0.0 , 0.0 ) )+ , Edge3 ( ( 0.5 , 0.5 , 0.5 ) , ( 0.0 , 0.0 , 1.0 ) )+ , Edge3 ( ( 0.5 , 0.5 , 0.5 ) , ( 0.0 , 1.0 , 0.0 ) )+ , Edge3 ( ( 0.5 , 0.5 , 0.5 ) , ( 1.0 , 0.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( 0.0 , 0.0 , -1.0 ) )+ , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( 0.0 , -1.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( -1.0 , 0.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , 0.5 , -0.5 ) , ( 0.0 , 0.0 , -1.0 ) )+ , Edge3 ( ( -0.5 , 0.5 , -0.5 ) , ( 0.0 , 1.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , 0.5 , -0.5 ) , ( -1.0 , 0.0 , 0.0 ) )+ , Edge3 ( ( 0.5 , -0.5 , -0.5 ) , ( 0.0 , 0.0 , -1.0 ) )+ , Edge3 ( ( 0.5 , -0.5 , -0.5 ) , ( 0.0 , -1.0 , 0.0 ) )+ , Edge3 ( ( 0.5 , -0.5 , -0.5 ) , ( 1.0 , 0.0 , 0.0 ) )+ , Edge3 ( ( 0.5 , 0.5 , -0.5 ) , ( 0.0 , 0.0 , -1.0 ) )+ , Edge3 ( ( 0.5 , 0.5 , -0.5 ) , ( 0.0 , 1.0 , 0.0 ) )+ , Edge3 ( ( 0.5 , 0.5 , -0.5 ) , ( 1.0 , 0.0 , 0.0 ) )+ ]+)+```++This is a bounded cell: it has finite edges only. The other ones are not+bounded, they have infinite edges:++```haskell+> head v+( [ -1.0 , -1.0 , 0.0 ]+, [ Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( 0.0 , -1.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( -1.0 , 0.0 , 0.0 ) )+ , IEdge3+ ( ( -0.5 , -0.5 , 0.5 )+ , ( -0.5773502691896258 , -0.5773502691896258 , 0.5773502691896258 )+ )+ , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( 0.0 , -1.0 , 0.0 ) )+ , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( -1.0 , 0.0 , 0.0 ) )+ , IEdge3+ ( ( -0.5 , -0.5 , -0.5 )+ , ( -0.5773502691896258 , -0.5773502691896258 , -0.5773502691896258 )+ )+ , IEdge3 ( ( -1.0 , 0.0 , 0.0 ) , ( 1.0 , 0.0 , 0.0 ) )+ , IEdge3 ( ( 0.0 , -1.0 , 0.0 ) , ( 0.0 , -1.0 , 0.0 ) )+ ]+)+```++[](https://gfycat.com/HarmoniousHighlevelBushbaby)+++## Convex hull++The `convexHull` function of the `ConvexHull` module generates the convex hull+of a list of points.++```haskell+import ConvexHull+import ConvexHull.Examples -- for the function randomInCube+points <- randomInCube 100 -- 100 random points in a cube+hull <- convexHull points False False Nothing+```++The vertices of the convex hull are stored in the field `_hvertices`:++```haskell+> _hvertices hull+fromList+ [ ( 3+ , Vertex+ { _point =+ [ 0.7872072051657094 , 0.450772463858757 , 1.9900427529711773e-2 ]+ , _neighfacets = fromList [ 42 , 43 , 47 , 48 ]+ , _neighvertices = fromList [ 1 , 11 , 64 , 88 ]+ , _neighridges = fromList [ 70 , 71 , 72 , 77 ]+ }+ )+ , ( 6+ , Vertex+ ......+```++The edges in the field `_hedges`:++```haskell+> _hedges hull+fromList+ [ ( Pair 14 70+ , ( [ 0.9215432980174852 , 0.8554065771602318 , 0.9842902519648512 ]+ , [ 0.9497713758656887 , 0.998006476041318 , 0.7243639875028591 ]+ )+ )+ , ( Pair 84 99+ ......+```++The facets in the field `_hfacets`:++```haskell+> _hfacets hull+fromList+ [ ( 0+ , Facet+ { _fvertices =+ fromList+ [ ( 4+ , [ 1.5757133629105136e-3+ , 0.6442797662244039+ , 0.7058559215899725+ ]+ )+ , ( 67+ , [ 2.7500520534961326e-2+ , 0.37516259577251554+ , 0.7331611715042575+ ]+ )+ , ( 77+ , [ 3.46399386146774e-2+ , 5.575911794526589e-2+ , 0.46787034305814157+ ]+ )+ ]+ , _fridges =+ fromList+ [ ( 0+ , Ridge+ { _rvertices =+ fromList+ [ ( 4+ , [ 1.5757133629105136e-3+ , 0.6442797662244039+ , 0.7058559215899725+ ]+ )+ , ( 77+ , [ 3.46399386146774e-2+ , 5.575911794526589e-2+ , 0.46787034305814157+ ]+ )+ ]+ , _ridgeOf = fromList [ 0 , 4 ]+ }+ )+ , ( 1+ , Ridge+ { _rvertices =+ fromList+ [ ( 4+ , [ 1.5757133629105136e-3+ , 0.6442797662244039+ , 0.7058559215899725+ ]+ )+ , ( 67+ , [ 2.7500520534961326e-2+ , 0.37516259577251554+ , 0.7331611715042575+ ]+ )+ ]+ , _ridgeOf = fromList [ 0 , 2 ]+ }+ )+ , ( 2+ , Ridge+ { _rvertices =+ fromList+ [ ( 67+ , [ 2.7500520534961326e-2+ , 0.37516259577251554+ , 0.7331611715042575+ ]+ )+ , ( 77+ , [ 3.46399386146774e-2+ , 5.575911794526589e-2+ , 0.46787034305814157+ ]+ )+ ]+ , _ridgeOf = fromList [ 0 , 1 ]+ }+ )+ ]+ , _centroid =+ [ 2.1238724170849748e-2 , 0.3584004933140618 , 0.6356291453841239 ]+ , _normal =+ [ -0.9930268604214181+ , -8.766369712550202e-2+ , 7.882087723357102e-2+ ]+ , _offset = 2.40848904384814e-3+ , _area = 4.0339144929987907e-2+ , _neighbors = fromList [ 1 , 2 , 4 ]+ , _family = None+ , _fedges =+ fromList+ [ ( Pair 4 67+ , ( [ 1.5757133629105136e-3+ , 0.6442797662244039+ , 0.7058559215899725+ ]+ , [ 2.7500520534961326e-2+ , 0.37516259577251554+ , 0.7331611715042575+ ]+ )+ )+ , ( Pair 67 77+ , ( [ 2.7500520534961326e-2+ , 0.37516259577251554+ , 0.7331611715042575+ ]+ , [ 3.46399386146774e-2+ , 5.575911794526589e-2+ , 0.46787034305814157+ ]+ )+ )+ , ( Pair 4 77+ , ( [ 1.5757133629105136e-3+ , 0.6442797662244039+ , 0.7058559215899725+ ]+ , [ 3.46399386146774e-2+ , 5.575911794526589e-2+ , 0.46787034305814157+ ]+ )+ )+ ]+ }+ )+ , ( 1+ , Facet+ ......+```++[](https://gfycat.com/QuaintUnrulyBlackpanther)+++## Halfspaces intersections++++```haskell+import HalfSpaces+import Data.Ratio ((%))+x = newVar 1+y = newVar 2+z = newVar 3+constraints =+ [ x .>= 0 -- shortcut for x .>=. cst 0+ , x .<= 3+ , y .>= 0+ , y .<=. cst 2 ^-^ (2%3)*^x+ , z .>= 0+ , z .<=. cst 6 ^-^ 2*^x ^-^ 3*^y ]+```++```haskell+> hsintersections constraints False+[ [ -1.1102230246251565e-16 , -1.1102230246251565e-16 , 6.0 ]+, [ 0.0 , 2.0 , 0.0 ]+, [ 0.0 , 0.0 , 0.0 ]+, [ 3.0 , 0.0 , 0.0 ] ]+```++## Gallery++The convex hull of a curve on the sphere:++++The Voronoi cell of a point inside the Utah teapot:++++The Voronoi diagram of a projection of the truncated tesseract:++++The Voronoi diagram of a cube surrounded by three perpendicular circles:++
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ qhull.cabal view
@@ -0,0 +1,190 @@+name: qhull+version: 0.1.0.1+synopsis: Delaunay triangulation, Voronoi diagrams and convex hulls. +description: Based on the qhull C library. ++ Maintenance version to bring it up to current ghc-version (ghc-8.10.7, lts-18.28). +homepage: https://github.com/stla/qhull#readme+license: GPL-3+license-file: GPL_3+author: Stéphane Laurent+maintainer: Andrew U. Frank+copyright: 2018 Stéphane Laurent+category: Math+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++flag exe+ description:+ Build the executables.+ default: True+flag exe-delaunay+ default: True+flag exe-voronoi+ default: False+flag exe-hull+ default: False+flag exe-hs+ default: False+flag exe-am+ default: False++library+ hs-source-dirs: src+ exposed-modules: Delaunay+ , Delaunay.Delaunay+ , Delaunay.CDelaunay+ , Delaunay.Types+ , Delaunay.R+ , Delaunay.Examples+ , Voronoi2D+ , Voronoi3D+ , Voronoi.Voronoi+ , Voronoi.R+ , Voronoi.Shared+ , ConvexHull+ , ConvexHull.ConvexHull+ , ConvexHull.CConvexHull+ , ConvexHull.Types+ , ConvexHull.R+ , ConvexHull.Examples+ , Qhull.Types+ , Qhull.Shared+ , HalfSpaces.LinearCombination+ , HalfSpaces.Constraint+ , HalfSpaces.Internal+ , HalfSpaces.ToySolver+ , HalfSpaces.CHalfSpaces+ , HalfSpaces.HalfSpaces+ , HalfSpaces.Examples+ , HalfSpaces+ , ConvexHull.Truncated120Cell3+ , ConvexHull.CantiTrunc600Cell.Data+ , ConvexHull.BiTruncatedTesseract+ , ConvexHull.SnubDodecahedron.SnubDodecahedron+ , ConvexHull.OmniTruncated120Cell+ , Delaunay.Adjacency+ build-depends: base >= 4.7 && < 5+ , split+ , containers+ , pretty-show+ , extra+ , ilist+ , hashable+ , insert-ordered-containers+ , random+ , Unique+ , vector-space + , vector-algorithms ==0.8.0.3+ , toysolver+ , data-default-class+ , combinat+ -- , permutation+ , regex-compat+ , regex-base+ , regex-posix+ other-extensions: ForeignFunctionInterface+ , TypeFamilies+ default-language: Haskell2010+ include-dirs: C+ C-sources: ./C/libqhull_r.c+ , ./C/geom_r.c+ , ./C/geom2_r.c+ , ./C/global_r.c+ , ./C/io_r.c+ , ./C/mem_r.c+ , ./C/merge_r.c+ , ./C/poly_r.c+ , ./C/poly2_r.c+ , ./C/qset_r.c+ , ./C/random_r.c+ , ./C/usermem_r.c+ , ./C/userprintf_r.c+ , ./C/user_r.c+ , ./C/stat_r.c+ , ./C/delaunay.c+ , ./C/convexhull.c+ , ./C/utils.c+ , ./C/halfspaces.c+ ghc-options: -O0 -Wall++executable test_delaunay+ if flag(exe) || flag(exe-delaunay)+ buildable: True+ else+ buildable: False+ hs-source-dirs: src-exe/Delaunay+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5+ , qhull+ , pretty-show+ , containers+ , insert-ordered-containers++executable test_voronoi+ if flag(exe) || flag(exe-voronoi)+ buildable: True+ else+ buildable: False+ hs-source-dirs: src-exe/Voronoi+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5+ , qhull+ , pretty-show+ , containers+ , ilist++executable test_convexhull+ if flag(exe) || flag(exe-hull)+ buildable: True+ else+ buildable: False+ hs-source-dirs: src-exe/ConvexHull+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5+ , qhull+ , pretty-show+ , containers+ , insert-ordered-containers+ , combinat+ -- , permutation+ , ilist+ , extra+ , regex-compat+ , regex-base+ , regex-posix+ ghc-options: -O0++executable test_halfspaces+ if flag(exe) || flag(exe-hs)+ buildable: True+ else+ buildable: False+ hs-source-dirs: src-exe/HalfSpaces+ main-is: Main.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5+ , qhull+ , pretty-show++executable adjacencymatrix+ if flag(exe) || flag(exe-am)+ buildable: True+ else+ buildable: False+ hs-source-dirs: src-exe/Adjacency+ main-is: AdjacencyMatrix.hs+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5+ , qhull+ , optparse-applicative+ , containers+ ghc-options: -main-is Adjacency.AdjacencyMatrix++source-repository head+ type: git+ location: https://github.com/stla/qhull
+ src-exe/Adjacency/AdjacencyMatrix.hs view
@@ -0,0 +1,53 @@+module Adjacency.AdjacencyMatrix+ where+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import Data.List+import Data.Monoid ((<>))+import Delaunay.Delaunay+import Delaunay.Types+import Options.Applicative++delaunayTess :: [[Double]] -> IO Tesselation+delaunayTess vertices = delaunay vertices False False Nothing++delaunayVertices :: FilePath -> IO [[Double]]+delaunayVertices verts = do+ v <- readFile verts+ return $ read v :: IO [[Double]]++adjacency :: Tesselation -> Int -> [Int]+adjacency tess i = map (fromEnum . ((i `IS.member`) . _neighsitesIds) . snd) $ IM.toList $ _sites tess++adjacency' :: Tesselation -> [[Int]]+adjacency' tess = map (adjacency tess) (IM.keys ( _sites tess ))++data Arguments = Arguments { infile :: FilePath, outfile :: FilePath }++run :: Parser Arguments+run = Arguments+ <$> argument str+ ( metavar "INFILE"+ <> help "File of vertices" )+ <*> argument str+ ( metavar "OUTFILE"+ <> help "adjacency matrix" )++writeMatrix :: [[Int]] -> FilePath -> IO ()+writeMatrix matrix outfile = writeFile outfile (intercalate "\n" (map show matrix))++doMatrix :: Arguments -> IO ()+doMatrix (Arguments infile outfile) =+ do+ v <- delaunayVertices infile+ tess <- delaunayTess v+ let mat = adjacency' tess+ writeMatrix mat outfile++main :: IO ()+main = execParser opts >>= doMatrix+ where+ opts = info (helper <*> run)+ ( fullDesc+ <> progDesc "Adjacency matrix of a Delaunay tesselation"+ <> header "adjacencymatrix -- based on qhull" )
+ src-exe/ConvexHull/Main.hs view
@@ -0,0 +1,847 @@+module Main+ where+import ConvexHull+import ConvexHull.BiTruncatedTesseract+import ConvexHull.CantiTrunc600Cell.Data+import ConvexHull.Examples hiding+ (regularSphere,+ regularTetrahedron)+import ConvexHull.OmniTruncated120Cell+import ConvexHull.R+import ConvexHull.SnubDodecahedron.SnubDodecahedron+import ConvexHull.Truncated120Cell3+import Data.Function (on)+import qualified Data.HashMap.Strict.InsOrd as H+import qualified Data.IntMap.Strict as IM+import Data.List+import Data.List.Index+-- import Data.Permute (elems, rank)+import qualified Data.Set as S+import Data.Tuple.Extra+import System.IO+import Text.Printf+import Text.Regex+import Text.Show.Pretty++approx :: RealFrac a => Int -> a -> a+approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)++roundedVertices :: Int -> [[Double]] -> [[Double]]+roundedVertices n = map (map (approx n))+++-- fixIndices :: [[Double]] -> [[Int]] -> ([[Double]], [[Int]])+-- fixIndices allVertices faces = (newvertices, newfaces)+-- where+-- faceselems = nub $ foldr union [] faces+-- l = length faceselems+-- permute = elems $ rank l faceselems+-- mapper = IM.fromList $ zip permute faceselems+-- mapper' = IM.fromList $ zip faceselems permute+-- newfaces = map (map ((IM.!) mapper')) faces+-- --newvertices =+-- -- (fromJust <$> (filter isJust $ (map atMay ([allVertices !! (mapper IM.! i) | i <- IM.keys mapper])))) `intersect` IM.keys mapper+-- -- newvertices = [allVertices !! (mapper IM.! i) | i <- IM.keys mapper]+-- newvertices = [allVertices !! i | i <- [0 .. length allVertices-1] `intersect` IM.keys mapper]+-- --+-- -- regularTetrahedron' :: [[Double]]+-- -- regularTetrahedron' =+-- -- [ [0.5 / sqrt 3, -0.5, 0.5 / sqrt 6]+-- -- , [sqrt 3 / 3, 0, -0.5 / sqrt 6]+-- -- , [0.5 / sqrt 3, 0.5, -0.5 / sqrt 6]+-- -- , [0, 0, 0.5 * sqrt 3 / sqrt 2] ]+-- --+-- regularSphere :: Int -> [Double] -> Double -> ([[Double]], [[Int]])+-- regularSphere n center rho =+-- (zipWith (s2c rho) theta phi, [[i,j] | i <- [0 .. n-1], j <- [1 .. n-1]])+-- where+-- gridtheta = [frac i n | i <- [0 .. n-1]]+-- theta = map (*(2*pi)) gridtheta+-- gridphi = [frac i n | i <- [1 .. n-1]]+-- phi = map (*pi) gridphi+-- frac :: Int -> Int -> Double+-- frac p q = realToFrac p / realToFrac q+-- s2c :: Double -> Double -> Double -> [Double]+-- s2c r th ph = [r * cos th * sin ph + center!!0, r * sin th * sin ph + center!!1, r * cos ph + center!!2]+--+-- sphere1,sphere2,sphere3,sphere4 :: ([[Double]], [[Int]])+-- sphere1 = regularSphere 40 (regularTetrahedron !! 0) (sqrt 6 / 4)+-- sphere2 = regularSphere 40 (regularTetrahedron !! 1) (sqrt 6 / 4)+-- sphere3 = regularSphere 40 (regularTetrahedron !! 2) (sqrt 6 / 4)+-- sphere4 = regularSphere 40 (regularTetrahedron !! 3) (sqrt 6 / 4)+--+--+-- -- regular tetrahederon -- --+-- regularTetrahedron :: [[Double]]+-- regularTetrahedron =+-- -- [[i, 0, -1/sqrt 2] | i <- pm] ++ [[0, i , 1/sqrt 2] | i <- pm]+-- -- where pm = [-1,1]+-- [ [ -1.0 , 0.0 , -0.7071067811865475 ]+-- , [ 0.0 , 1.0 , -0.7071067811865475 ]+-- , [ 0.0 , -1.0 , 0.7071067811865475 ]+-- , [ 1.0, 0.0 , 0.7071067811865475 ]+-- ]+--++-- stringify :: Show a => String -> [a] -> String+-- stringify sep = intercalate sep . map show+++main :: IO ()+main = do++ h <- convexHull sixhundredCell False False Nothing+ pPrint $ hullSummary h+ putStrLn "vertices:"+ pPrint $ roundedVertices 4 $ verticesCoordinates h+ putStrLn "edges:"+ pPrint $ edgesIds' h+ putStrLn "\nTETRAHEDRAL FACETS:"+ let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ pPrint $ IM.toList (IM.map verticesIds tetras)++ -- h <- convexHull hexadecachoron False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nTETRAHEDRAL FACETS:"+ -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ -- pPrint $ IM.toList (IM.map verticesIds tetras)++ -- h <- convexHull runcitruncated5cell False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h++ -- h <- convexHull rectified5cell False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nTETRAHEDRAL FACETS:"+ -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ -- pPrint $ IM.map verticesIds tetras++ -- h <- convexHull sircope False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nTRIANGLE PRISM FACETS:"+ -- let tprisms = IM.filter (\f -> length (verticesIds f) == 6) (_hfacets h)+ -- pPrint $ IM.elems (IM.map verticesIds tprisms)+ -- putStrLn "ridges:"+ -- let ridges = map (IM.elems . facetRidges h) (IM.elems tprisms)+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull tutcup False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nTETRAHEDRAL FACETS:"+ -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ -- pPrint $ IM.toList (IM.map verticesIds tetras)++ -- h <- convexHull runcinatedTesseract False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nTETRAHEDRAL FACETS:"+ -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ -- pPrint $ IM.map verticesIds tetras++ -- h <- convexHull runcinated5cells False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nTETRAHEDRAL FACETS:"+ -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ -- putStrLn "ridges:"+ -- let ridges = map (IM.elems . facetRidges h) (IM.elems tetras)+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull duoprism330 False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "same vertices:"+ -- print $ verticesCoordinates h == duoprism330+ -- putStrLn "\nALL ROUNDED VERTICES:"+ -- pPrint $ roundedVertices 3 $ verticesCoordinates h++ -- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)+ -- , sin (pi*x) * sin (2*pi*x)+ -- , cos (pi*x)]) [i/100 | i <- [0 .. 100]]+ -- h <- convexHull curve3D True False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "vertices:"+ -- pPrint $ roundedVertices 10 $ verticesCoordinates h+ -- putStrLn "facets:"+ -- pPrint $ map (IM.keys . _vertices) (IM.elems $ _hfacets h) -- TODO FAIRE UNE FONCTION FACETIDS+ -- c'est fait : verticesIds = IM.keys . _vertices+ -- direct : facetsVerticesIds h++ -- h <- convexHull truncated5cells False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "vertices:"+ -- pPrint $ roundedVertices 10 $ verticesCoordinates h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nTETRAHEDRAL FACETS:"+ -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ -- putStrLn "ridges:"+ -- let ridges = map (IM.elems . facetRidges h) (IM.elems tetras)+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull truncated24cells False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nCUBICAL FACETS:"+ -- let cubes = IM.filter (\f -> length (verticesIds f) == 8) (_hfacets h)+ -- putStrLn "ridges:"+ -- let ridges = map (IM.elems . facetRidges h) (IM.elems cubes)+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull biTruncatedTesseract False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "vertices:"+ -- pPrint $ roundedVertices 3 $ verticesCoordinates h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nTRUNCATED OCTAHEDRAL FACETS:"+ -- let octahedra = IM.filter (\f -> length (verticesIds f) == 24) (_hfacets h)+ -- putStrLn "\nTRUNCATED TETRAHEDRAL FACETS:"+ -- let tetrahedra = IM.filter (\f -> length (verticesIds f) == 12) (_hfacets h)+ -- putStrLn "ridges:"+ -- let ridges = map (IM.elems . facetRidges h) (IM.elems tetrahedra)+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull truncatedTetrahedron False False Nothing+ -- putStrLn "same vertices:"+ -- pPrint $ roundedVertices 3 truncatedTetrahedron == roundedVertices 3 (verticesCoordinates h)+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons++ -- h <- convexHull duoprism1616 False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h++ -- h <- convexHull thex False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "vertices:"+ -- pPrint $ roundedVertices 3 $ verticesCoordinates h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nOCTAHEDRAL FACETS:"+ -- let octahedra = IM.filter (\f -> length (verticesIds f) == 6) (_hfacets h)+ -- pPrint $ IM.elems $ IM.map verticesIds octahedra+ -- pPrint octahedra+ -- putStrLn "ridges:"+ -- let ridges = map (IM.elems . facetRidges h) (IM.elems octahedra)+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++-- h <- convexHull cuboctahedron4d False False Nothing+-- pPrint $ hullSummary h+-- putStrLn "vertices:"+--- pPrint $ verticesCoordinates h+-- putStrLn "facets:"+-- let facets = IM.elems (_hfacets h)+-- let polygons = map (map fst . facetToPolygon') facets+-- pPrint polygons+-- putStrLn "edges:"+-- pPrint $ edgesIds' h+--- putStrLn "ridges:"+-- let ridges = map (IM.elems . facetRidges h) facets+-- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull daVinci False False Nothing+ -- pPrint $ hullSummary h+ -- code <- convexHull3DrglCode daVinci False (Just "rgl/daVinci.R")+ -- putStrLn "done"++ -- h <- convexHull twocircles False False Nothing+ -- pPrint $ hullSummary h+ -- code <- convexHull3DrglCode twocircles False (Just "rgl/oloid.R")+ -- putStrLn "done"++ -- h <- convexHull vs120omnitrunc False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "ridges:"+ -- let ridges = map (IM.elems . facetRidges h) facets+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++-- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)+-- , sin (pi*x) * sin (2*pi*x)+-- , cos (pi*x)]) [i/200 | i <- [0 .. 200]]+-- h <- convexHull curve3D True False Nothing+-- hullToSTL h "strangeHull.stl"+++-- h <- convexHull snubDodecahedron True False Nothing+-- hullToSTL h "MYTEST.stl"+-- pPrint $ hullSummary h+-- putStrLn "facets:"+-- let facets = IM.elems (_hfacets h)+-- let normals = map _normal facets+-- let facetNormals = map (\n -> "facet normal " ++ stringify " " n ++ "\nouter loop")+-- normals+-- pPrint facetNormals+-- putStrLn "facets normals done"+-- putStrLn "facets polygons:"+-- let polygons = map (\f -> "\nvertex " ++ (stringify "\nvertex " . map snd . facetToPolygon') f) facets+-- pPrint polygons+ --let polygons = (map (map stringify . snd) . facetToPolygon') facets+ -- let polygons = map (\f -> stringify ("vertex" ++ ((show . snd) $ facetToPolygon' f))) facets ++ "\nendloop\nendfacet"+-- let vertices = map (\v -> v ++ "\nendloop\nendfacet") polygons+-- let vertices' = map (\v -> subRegex (mkRegex "\\]") (subRegex (mkRegex "\\[") v "") "") vertices+-- putStrLn "vertices:"+-- pPrint vertices'+-- putStrLn "THE CONCATENATION:"+-- pPrint $ subRegex (mkRegex ",") (concat [x ++ y | x <- facetNormals, y <- vertices']) " "+-- putStrLn "concatenation 0:"+-- pPrint $ stringify " " $ concat [unlines [x,y] | x <- facetNormals, y <- vertices']+-- putStrLn "concatenation 1:"+-- pPrint $ [unlines [x,y] | x <- facetNormals, y <- vertices']+-- putStrLn "concatenation 2:"+-- pPrint $ [stringify " " [x,y] | x <- facetNormals, y <- vertices']+ -- putStrLn "vertices:"+-- pPrint $ verticesCoordinates h+-- let polygons = map (map fst . facetToPolygon') facets+-- pPrint polygons+-- putStrLn "edges:"+-- pPrint $ edgesIds' h++-- h <- convexHull biTruncatedTesseract False False Nothing+-- pPrint $ hullSummary h+-- putStrLn "vertices:"+-- pPrint $ verticesCoordinates h+-- putStrLn "facets:"+-- let facets = IM.elems (_hfacets h)+-- let polygons = map (map fst . facetToPolygon') facets+-- pPrint polygons+-- putStrLn "edges:"+-- pPrint $ edgesIds' h++ -- let cube10 = ncube 10+ -- putStrLn "cube10:"+ -- let thecube = map swap $ indexed cube10+ -- pPrint thecube+ -- chull <- convexHull cube10 False False Nothing+ -- putStrLn "done"+ -- pPrint $ hullSummary chull+ -- pPrint $ verticesCoordinates chull+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets chull)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' chull++ -- h <- convexHull allVertices False False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- -- putStrLn "tetrahedral facets:"+ -- -- pPrint $ IM.elems $ IM.map verticesIds $ IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)++ -- -- sphere1'+ -- points <- randomOnSphere 100 1+ -- h <- convexHull points True False Nothing+ -- pPrint $ hullSummary h+ -- putStrLn "vertices sphere 1:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "facets sphere 1:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons++ -- h <- convexHull allVertices False False Nothing+ -- pPrint $ hullSummary h++ -- h <- convexHull reuleuxTetrahedron True False Nothing+ -- let facets = IM.elems (_hfacets h)+ -- putStrLn "facets:"+ -- pPrint facets+ -- putStrLn "facets' (oriented):"+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons++ -- h <- convexHull qcube2 True False Nothing+ -- putStrLn "same vertices:"+ -- pPrint $ qcube2 == verticesCoordinates h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ --+-- -- sphere1'+-- let (vs1, fs1) = fixIndices (fst sphere1) (snd sphere1)+-- h <- convexHull (vs1) True False Nothing+-- putStrLn "vertices 1:"+-- pPrint vs1+-- putStrLn "facets:"+-- let facets = IM.elems (_hfacets h)+-- let polygons = map (map fst . facetToPolygon') facets+-- pPrint polygons+--+-- -- sphere2' :: IO ([[Double]], [[Int]])+-- let (vs2,fs2) = fixIndices (fst sphere2) (snd sphere2)+-- h <- convexHull (vs2) True False Nothing+-- putStrLn "vertices 2:"+-- pPrint vs2+-- putStrLn "facets:"+-- let facets = IM.elems (_hfacets h)+-- let polygons = map (map fst . facetToPolygon') facets+-- pPrint polygons+--+-- -- sphere3' :: IO ([[Double]], [[Int]])+-- let (vs3, fs3) = fixIndices (fst sphere3) (snd sphere3)+-- h <- convexHull (vs3) True False Nothing+-- putStrLn "vertices 3:"+-- pPrint vs3+-- putStrLn "facets:"+-- let facets = IM.elems (_hfacets h)+-- let polygons = map (map fst . facetToPolygon') facets+-- pPrint polygons+--+-- -- sphere4' :: IO ([[Double]], [[Int]])+-- let (vs4, fs4) = fixIndices (fst sphere4) (snd sphere4)+-- h <- convexHull (vs4) True False Nothing+-- putStrLn "vertices 4:"+-- pPrint vs4+-- putStrLn "facets:"+-- let facets = IM.elems (_hfacets h)+-- let polygons = map (map fst . facetToPolygon') facets+-- pPrint polygons++ -- putStrLn "sphere1'"+ -- pPrint sphere1'+ -- let facets = IM.elems (_hfacets h)+ -- polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ -- putStrLn "shpere2'"+ -- pPrint sphere2'+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ -- putStrLn "shpere3'"+ -- pPrint sphere3'+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ -- putStrLn "shpere4'"+ -- pPrint sphere4'+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+++--+-- h <- convexHull (dodecaplex) False False Nothing+-- putStrLn $ hullSummary h+-- putStrLn "edges:"+-- pPrint $ edgesIds' h+-- putStrLn "all vertices:"+-- pPrint $ verticesCoordinates h+-- putStrLn "ridges:"+-- let facets = IM.elems (_hfacets h)+-- let ridges = map (IM.elems . facetRidges h) facets+-- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull qcube1 True False Nothing+ -- putStrLn "same vertices:"+ -- pPrint $ qcube1 == verticesCoordinates h+ -- putStrLn "facets cube1:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ --+ -- h <- convexHull qcube2 True False Nothing+ -- putStrLn "same vertices:"+ -- pPrint $ qcube2 == verticesCoordinates h+ -- putStrLn "facets cube2:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ --+ -- h <- convexHull qcube3 True False Nothing+ -- putStrLn "same vertices:"+ -- pPrint $ qcube3 == verticesCoordinates h+ -- putStrLn "facets cube3:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ --+ -- h <- convexHull qcube4 True False Nothing+ -- putStrLn "same vertices:"+ -- pPrint $ qcube4 == verticesCoordinates h+ -- putStrLn "facets cube4:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ --+ -- h <- convexHull qcube5 True False Nothing+ -- putStrLn "same vertices:"+ -- pPrint $ qcube5 == verticesCoordinates h+ -- putStrLn "facets cube5:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons++ -- h <- convexHull regularTetrahedron False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ -- code <- convexHull3DrglCode regularTetrahedron False (Just "rgl/regularTetrahedron.R")+ -- putStrLn "done"++ -- let points = regularSphere 30+ -- h <- convexHull points True False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons+ -- writeFile "Data/sphere.txt" (show (verticesCoordinates h, polygons))++ -- h <- convexHull octaplex False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "ridges:"+ -- let facets = IM.elems (_hfacets h)+ -- let ridges = map (IM.elems . facetRidges h) facets+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull icosahedron False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons++ -- let cube = [[-1,-1,-1],+ -- [-1,-1, 1],+ -- [-1, 1,-1],+ -- [-1, 1, 1],+ -- [ 1,-1,-1],+ -- [ 1,-1, 1],+ -- [ 1, 1,-1],+ -- [ 1, 1, 1]]+ -- h <- convexHull cube False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "same vertices:"+ -- print $ cube == verticesCoordinates h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons++ -- points <- randomOnSphere 50 1+ -- h <- convexHull points True False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "facets:"+ -- let facets = IM.elems (_hfacets h)+ -- let polygons = map (map fst . facetToPolygon') facets+ -- pPrint polygons++ -- h <- convexHull duocylinder False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "\nedges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "\nall vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "\nridges:"+ -- let facets = IM.elems (_hfacets h)+ -- let ridges = map (IM.elems . facetRidges h) facets+ -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges++ -- h <- convexHull hexaSquare False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h++ -- h <- convexHull snub24cell False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "same vertices:"+ -- print $ verticesCoordinates h == snub24cell+ -- putStrLn "\nALL ROUNDED VERTICES:"+ -- pPrint $ roundedVertices 2 $ verticesCoordinates h+ -- putStrLn "\nTETRAHEDRAL FACETS:"+ -- pPrint $ IM.elems $ IM.map verticesIds $ IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)++ -- h <- convexHull triangularDuoprism False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "same vertices:"+ -- print $ verticesCoordinates h == triangularDuoprism+ -- putStrLn "one prism:"+ -- let facet = head $ IM.elems (_hfacets h)+ -- let ridges = IM.elems $ facetRidges h facet+ -- pPrint $ map (map fst . ridgeToPolygon) ridges+ -- putStrLn "edges of this facet:"+ -- pPrint $ edgesIds' facet++ -- h <- convexHull duoprism35 False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "same vertices:"+ -- print $ verticesCoordinates h == duoprism34+ -- putStrLn "\nALL ROUNDED VERTICES:"+ -- pPrint $ roundedVertices 3 $ verticesCoordinates h+ -- putStrLn "\nONE PENTAGONAL PRISM:"+ -- let prisms = IM.filter (\f -> length (verticesIds f) == 10) (_hfacets h)+ -- onePrism = head $ IM.elems prisms+ -- putStrLn "\nRIDGES OF THIS PRISM:"+ -- let ridges = IM.elems $ facetRidges h onePrism+ -- pPrint $ map (map fst . ridgeToPolygon) ridges++ -- h <- convexHull duoprism34 False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "same vertices:"+ -- print $ verticesCoordinates h == duoprism34+ -- putStrLn "\nALL ROUNDED VERTICES:"+ -- pPrint $ roundedVertices 3 $ verticesCoordinates h+ -- putStrLn "\nONE SQUARE PRISM:"+ -- let squarePrisms = IM.filter (\f -> length (verticesIds f) == 8) (_hfacets h)+ -- oneSquarePrism = head $ IM.elems squarePrisms+ -- putStrLn "\nRIDGES OF THIS SQUARE PRISM:"+ -- let ridges = IM.elems $ facetRidges h oneSquarePrism+ -- pPrint $ map (map fst . ridgeToPolygon) ridges+ -- putStrLn "\nONE TRIANGULAR PRISM:"+ -- let triPrisms = IM.filter (\f -> length (verticesIds f) == 6) (_hfacets h)+ -- oneTriPrism = head $ IM.elems triPrisms+ -- putStrLn "\nRIDGES OF THIS TRIANGULAR PRISM:"+ -- let ridges' = IM.elems $ facetRidges h oneTriPrism+ -- pPrint $ map (map fst . ridgeToPolygon) ridges'++ -- h <- convexHull hexagonalDuoprism False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "same vertices:"+ -- print $ verticesCoordinates h == hexagonalDuoprism+ -- putStrLn "one hexagonal prism:"+ -- let facet = head $ IM.elems (_hfacets h)+ -- let ridges = IM.elems $ facetRidges h facet+ -- pPrint $ map (map fst . ridgeToPolygon) ridges+ -- putStrLn "edges of this facet:"+ -- pPrint $ edgesIds' facet++ -- h <- convexHull cantellatedTesseract False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "good ridges:"+ -- let goodfacets = IM.elems $ IM.filter (\f -> nEdges f `elem` [9,12]) (_hfacets h)+ -- let ridges = nubBy ((==) `on` verticesIds) $ concatMap (IM.elems . facetRidges h) goodfacets+ -- pPrint $ map (map fst . ridgeToPolygon) ridges++ -- h <- convexHull rectifiedTesseract False False Nothing+ -- putStrLn $ hullSummary h+ -- putStrLn "edges:"+ -- pPrint $ edgesIds' h+ -- putStrLn "all vertices:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "facets:"+ -- pPrint $ IM.map verticesIds (_hfacets h)+ -- putStrLn "tetrahedral facets:"+ -- pPrint $ IM.elems $ IM.map verticesIds $ IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ -- putStrLn "ridges of facet 0:"+ -- let facet = _hfacets h IM.! 0+ -- ridges = facetRidges h facet+ -- pPrint $ map (map fst . ridgeToPolygon) (IM.elems ridges)++ -- h <- convexHull truncatedTesseract False False Nothing+ -- putStrLn "HULL SUMMARY:"+ -- putStrLn $ hullSummary h+ -- putStrLn "\nEDGES:"+ -- pPrint $ edgesIds' h+ -- -- putStrLn "original vertices:"+ -- -- pPrint $ take 2 truncatedTesseract+ -- -- putStrLn "vertices:"+ -- -- pPrint $ take 2 $ verticesCoordinates h+ -- putStrLn "\nALL VERTICES:"+ -- pPrint $ verticesCoordinates h+ -- putStrLn "\nALL ROUNDED VERTICES:"+ -- pPrint $ roundedVertices 2 $ verticesCoordinates h+ -- putStrLn "\nALL ROUNDED ORIGINAL VERTICES:"+ -- pPrint $ roundedVertices 2 truncatedTesseract+ -- -- putStrLn "facets:"+ -- -- pPrint $ IM.map verticesIds (_hfacets h)+ -- putStrLn "\nTETRAHEDRAL FACETS:"+ -- pPrint $ IM.elems $ IM.map verticesIds $ IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)+ -- -- putStrLn "ridges of facet 9:" -- pkoi ? parce que je voulais illuster une facette je pense...+ -- -- let facet = _hfacets h IM.! 9+ -- -- ridges = facetRidges h facet+ -- -- pPrint $ map (map fst . ridgeToPolygon) (IM.elems ridges)+ -- -- putStrLn "vertices of facet 9:"+ -- -- pPrint $ _vertices facet++ -- h <- convexHull icosahedron False False Nothing+ -- pPrint $ IM.map facetToPolygon (_hfacets h)+ -- putStrLn $ hullSummary h+ -- pPrint $ IM.elems $ IM.map (IM.elems . _vertices) (_hfacets h)++ -- -- code <- convexHull3DrglCode irregularPolyhedron True (Just "rgl/irregularPolyhedron.R")+ -- h <- convexHull truncatedCuboctahedron False False Nothing+ -- putStrLn $ hullSummary h+ -- pPrint $ _vertices h+ -- pPrint $ IM.elems $ IM.map facetToPolygon' (_hfacets h)+ -- pPrint $ edgesIds' h+ -- pPrint $ IM.elems $ IM.map _normal (_hfacets h)++ -- code <- convexHull3DrglCode mobiusStrip True (Just "rgl/mobiusHull02.R")+ -- h <- convexHull mobiusStrip False False Nothing+ -- putStrLn $ hullSummary h++ -- code <- convexHull3DrglCode truncatedCuboctahedron False (Just "rgl/truncatedCuboctahedron.R")+ -- putStrLn "done"++ -- h <- convexHull truncatedCuboctahedron False False Nothing+ -- pPrint $ IM.map toVertex3 (_vertices h)+ -- pPrint $ IM.elems $ IM.map facetToPolygon' (_hfacets h)+ -- pPrint $ edgesIds' h++ -- h <- convexHull truncatedCuboctahedron False False Nothing+ -- pPrint $ IM.elems $ IM.map facetToPolygon (_hfacets h)+ -- putStrLn $ hullSummary h++ -- code <- convexHull3DrglCode spheresPack True (Just "rgl/convexhull_spheresPack.R")+ -- putStrLn "done"++ -- h <- convexHull truncatedTesseract False False Nothing+ -- putStrLn $ hullSummary h++ -- h <- convexHull nonConvexPolyhedron False False Nothing+ -- putStrLn $ hullSummary h+ -- --code <- convexHull3DrglCode nonConvexPolyhedron True (Just "rgl/convexhull_nonConvexPolyhedron.R")+ -- putStrLn "done"++ -- h <- convexHull truncatedTesseract False False Nothing+ -- putStrLn $ hullSummary h+ -- let edges = H.keys (_edges h)+ -- let f (Pair i j) = printf "coolsegment3d(rbind(x[%d,],x[%d,]))\n" (i+1) (j+1)+ -- let code = map f edges+ -- pPrint $ verticesCoordinates h+ -- putStrLn $ concat code++ -- points <- randomInCube 100+ -- hull <- convexHull points False False Nothing+ -- pPrint $ _hfacets hull+ -- pPrint $ _hedges hull+ -- pPrint $ _hvertices hull++ -- code <- convexHull3DrglCode teapot True (Just "rgl/convexhull_teapot.R")+ -- putStrLn "done"++ -- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)+ -- , sin (pi*x) * sin (2*pi*x)+ -- , cos (pi*x)]) [i/200 | i <- [0 .. 200]]+ -- code <- convexHull3DrglCode (nub $ curve3D ++ map (\[x,y,z] -> [x,y,z+2]) curve3D) True+ -- (Just "rgl/convexhull_curveOnSphere3.R")+ -- putStrLn "done"++ -- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)+ -- , sin (pi*x) * sin (2*pi*x)+ -- , cos (pi*x)]) [i/200 | i <- [0 .. 200]]+ -- code <- convexHull3DrglCode curve3D True (Just "rgl/convexhull_curveOnSphere.R")+ -- putStrLn "done"++ -- let c = 4+ -- a = 1+ -- let curve3D = map (\x -> [ cos (2*pi*x) * (c + a * cos (2*pi*x))+ -- , sin (2*pi*x) * (c + a * cos (2*pi*x))+ -- , a * sin (2*pi*x)]) [i/50 | i <- [0 .. 50]]+ -- code <- convexHull3DrglCode curve3D True (Just "rgl/convexhull_curveOnTorus.R")+ -- putStrLn "done"++ -- points <- randomInCube 1000+ -- code <- convexHull3DrglCode (map (map (approx 4)) points) True (Just "rgl/convexhull04.R")+ -- putStrLn "done"++ -- let square3D = [[-1,-1, 0]+ -- ,[-1,-1, 0]+ -- ,[-1, 1, 0]+ -- ,[-1, 1, 0]]+ -- chull <- convexHull square3D False+ -- pPrint chull++ -- let squareLattice = [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2]]+ -- chull <- convexHull squareLattice False False Nothing+ -- putStrLn "\n--- SQUARE LATTICE ---"+ -- pPrint chull+ --+ -- let cube = [[-1,-1,-1]+ -- ,[-1,-1, 1]+ -- ,[-1, 1,-1]+ -- ,[-1, 1, 1]+ -- ,[ 1,-1,-1]+ -- ,[ 1,-1, 1]+ -- ,[ 1, 1,-1]+ -- ,[ 1, 1, 1]]+ -- chull2 <- convexHull cube False+ -- putStrLn "\n--- CUBE ---"+ -- pPrint chull2++ -- chull <- convexHull cube4 True False Nothing+ -- pPrint chull++ -- chull <- convexHull cube5 False False Nothing+ -- putStrLn "done"+ -- pPrint chull+ -- pPrint $ length $ xxx chull+ -- pPrint $ length $ nub $ xxx chull+ -- pPrint $ S.size (_alledges chull)++ -- let square = [[0,0],[0,1],[1,0],[1,1]]+ -- chull <- convexHull square False+ -- pPrint chull
+ src-exe/Delaunay/Main.hs view
@@ -0,0 +1,65 @@+module Main+ where+import Delaunay.Examples+import qualified Data.IntMap.Strict as IM+import Delaunay+import Delaunay.R+import System.IO+import Text.Show.Pretty+import Data.HashMap.Strict.InsOrd as H hiding (map)++tesseractVertices :: [[Double]]+tesseractVertices =+ map (map (/2))+ [ [-1,-1,-1,-1],+ [-1,-1,-1, 1],+ [-1,-1, 1,-1],+ [-1,-1, 1, 1],+ [-1, 1,-1,-1],+ [-1, 1,-1, 1],+ [-1, 1, 1,-1],+ [-1, 1, 1, 1],+ [ 1,-1,-1,-1],+ [ 1,-1,-1, 1],+ [ 1,-1, 1,-1],+ [ 1,-1, 1, 1],+ [ 1, 1,-1,-1],+ [ 1, 1,-1, 1],+ [ 1, 1, 1,-1],+ [ 1, 1, 1, 1]+ ]++main :: IO ()+main = do++ dtesseract <- delaunay tesseractVertices True False Nothing+ let vertices = IM.elems $ _vertices dtesseract+ let edges = Prelude.map fromPair $ H.keys $ _edges dtesseract+ putStrLn "VERTICES:"+ pPrint vertices+ putStrLn "\nEDGES:"+ pPrint edges+ where+ fromPair (Pair i j) = (i,j)++ -- tess <- delaunay nonConvexPolyhedron False False Nothing+ -- let code = delaunaySpheres tess+ -- writeFile "rgl/delaunay_spheres_nonConvexPolyhedron.R" code++ -- x <- [0,0,0] : randomOnSphere 100 3+ -- tess <- delaunay x False False Nothing+ -- let code = delaunay3rgl tess True False True True Nothing+ -- writeFile "rgl/delaunay_sphere_interior.R" code++ -- tess <- delaunay duoCylinder False False Nothing+ -- let edges = H.elems $ _edges tess+ -- let vertices = IM.elems $ _vertices tess+ -- let edgesKeys = Prelude.map fromPair $ H.keys $ _edges tess+ -- putStrLn "VERTICES:"+ -- pPrint vertices+ -- putStrLn "\nEDGES:"+ -- pPrint edges+ -- putStrLn "\nEDGES KEYS:"+ -- pPrint edgesKeys+ -- where+ -- fromPair (Pair i j) = (i,j)
+ src-exe/HalfSpaces/Main.hs view
@@ -0,0 +1,9 @@+module Main+ where+import HalfSpaces+import Text.Show.Pretty++main :: IO ()+main = do+ is <- hsintersections region3D False+ pPrint is
+ src-exe/Voronoi/Main.hs view
@@ -0,0 +1,249 @@+module Main+ where+import ConvexHull+import ConvexHull.R+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import Data.List+import Delaunay+import Delaunay.Examples+import Delaunay.R+import System.IO+import Text.Show.Pretty+import Voronoi.R+import Voronoi2D+import Voronoi3D+-- import Data.Graph+-- import Data.List.Index+-- import Data.List++-- connectedEdges :: Edge2 -> Edge2 -> Bool+-- connectedEdges (Edge2 (x1,x2)) (Edge2 (y1,y2)) = length ([x1,x2] `intersect` [y1,y2]) == 1+-- connectedEdges _ _ = False+--+-- edgeVertices :: Edge2 -> [[Double]]+-- edgeVertices (Edge2 ((x1,x2),(y1,y2))) = [[x1,x2],[y1,y2]]+--+-- connectedVertices :: Cell2 -> [Double] -> [Double] -> Bool+-- connectedVertices cell [x1,x2] [y1,y2] =+-- (Edge2 ((x1,x2),(y1,y2)) `elem` cell) || (Edge2 ((y1,y2),(x1,x2)) `elem` cell)++-- distance :: [Double] -> [Double] -> Double+-- distance p1 p2 = sum (zipWith (\x y -> (subtract x y)^2) p1 p2)+--+-- checkTile :: Tile -> [Double]+-- checkTile tile = map (distance center) vertices+-- where+-- simplex = _simplex tile+-- center = _circumcenter simplex+-- vertices = IM.elems (_points simplex)+++main :: IO ()+main = do++ tess <- delaunay waves2D False False Nothing+ let v = voronoi2 tess+ v' = restrictVoronoi2 v+ code = voronoi2ForR v' Nothing+ writeFile "Rplots/voronoi_waves2D.R" code++-- tess <- delaunay ([0,0,0] : projectedHexagonalDuoprims) False True Nothing+-- let v = voronoi3 tess+-- summaryVoronoi3 v+-- code <- voronoi3ForRgl' v Nothing Nothing+-- writeFile "rgl/voronoi_hexagonalDuoprism.R" code++ -- let x1 = let b=0 in+ -- [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]+ -- x2 = let b=pi/2 in+ -- [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]+ -- x3 = [[cos (a*2*pi/100), sin (a*2*pi/100), 0] | a <- [0 .. 99]]+ -- tess <- delaunay (nub $ x1 ++ x2 ++ x3 ++ map (map (/5)) cube3) False False Nothing+ -- putStrLn "done delaunay"+ -- let v = voronoi3 tess+ -- (_,cell) = last (restrictVoronoi3' v)+ -- h <- convexHull (cell3Vertices cell) False False Nothing+ -- putStrLn $ hullSummary h+ -- code <- convexHull3DrglCode (cell3Vertices cell) True (Just "rgl/convexhull_voronoiCell.R")+ -- putStrLn "done"++ -- tess <- delaunay (truncatedCuboctahedron ++ [[0,0,0]]) False True Nothing+ -- let v = voronoi3 tess+ -- summaryVoronoi3 v+ -- code <- voronoi3ForRgl' v Nothing Nothing+ -- writeFile "rgl/voronoi_truncatedCuboctahedron01.R" code++ -- tess <- delaunay spheresPack False True Nothing+ -- let v = voronoi3 tess+ -- prettyShowVoronoi3 v Nothing+ -- summaryVoronoi3 v+ -- code <- voronoi3ForRgl' v Nothing Nothing+ -- writeFile "rgl/voronoi_spheresPacking01.R" code++ -- tess <- delaunay projectedTruncatedTesseract False True Nothing+ -- pPrint $ IM.filter (\tile -> _volume tile < 1e-16 && _volume tile > 0) (_tiles tess)+ -- let v = voronoi3 tess+ -- -- pPrint $ map (\(_,cell) -> length cell) (restrictVoronoi3 v)+ -- summaryVoronoi3 (roundVoronoi3 10 (restrictVoronoi3 v))+ -- code <- voronoi3ForRgl' v (Just 10) Nothing+ -- writeFile "rgl/voronoi_truncatedTesseract02.R" code++-- let x1 = let b=0 in+-- [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]+-- x2 = let b=pi/2 in+-- [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]+-- x3 = [[cos (a*2*pi/100), sin (a*2*pi/100), 0] | a <- [0 .. 99]]+-- tess <- delaunay (nub $ x1 ++ x2 ++ x3 ++ map (map (/5)) cube3) False True+-- -- let code = delaunay3rgl tess False True True (Just 0.5)+-- pPrint $ IM.filter (isNaN . head) (IM.map (_circumcenter . _simplex) (_tiles tess))+-- -- pPrint $ IM.filterWithKey (\k _ -> k `elem` [459, 460, 464]) (_tiles tess)+-- -- pPrint $ IM.map checkTile (IM.filter (\tile -> _volume (_simplex tile) == 0) (_tiles tess))+-- let v = voronoi3 tess+-- v' = filter (\(_,cell) -> not (null cell)) v+-- -- v' = restrictVoronoi3box' ((-2,2),(-2,2),(-2,2)) v+-- v'' = clipVoronoi3 ((-1,1),(-1,1),(-1,1)) v'+-- code <- voronoi3ForRgl' v'' Nothing+-- writeFile "rgl/voronoi_twoCircles04.R" code+-- putStrLn "done"++-- let x1 = let b=0 in+-- [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]+-- x2 = let b=pi/2 in+-- [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]+-- tess <- delaunay (nub $ x1 ++ x2 ++ map (map (/5)) cube3) False True+-- let v = voronoi3 tess+-- vv = [last v]+-- v' = filter (\(_,cell) -> not (null cell)) v+-- v'' = clipVoronoi3 (-1,1,-1,1,-1,1) v'+-- -- prettyShowVoronoi3 v' (Just 10)+-- code <- voronoi3ForRgl' v'' Nothing+-- writeFile "rgl/voronoi_twoCircles02.R" code+-- putStrLn "done"++ -- x1 <- randomOnSphere 500 4+ -- x2 <- randomOnSphere 500 2+ -- let points = x1 ++ x2 ++ [[1,0,0],[-1,0,0]]+ -- tess <- delaunay points False False+ -- let v = voronoi3 tess+ -- vv = [last v, last (init v)]+ -- code <- voronoi3ForRgl' vv Nothing+ -- writeFile "rgl/voronoi_sphere01.R" code++-- let x1 = map (map (*2)) cube3+-- let x2 = map (map (*(1/2))) cube3+-- tess <- delaunay (x1 ++ x2) False True+-- pPrint $ _tiles tess+-- let ridgeof = filter (\fo -> IS.size fo == 1) (map _facetOf (IM.elems (_tilefacets tess)))+-- print $ length ridgeof+-- print $ IM.size (_tilefacets tess)+-- let ridgevertices = map (IM.keys . _points . _subsimplex) (IM.elems (_tilefacets tess))+-- print $ length $ nub ridgevertices+-- let tilevertices = map (IM.keys . _points . _simplex) (IM.elems (_tiles tess))+-- pPrint $ length $ filter (== [3]) $ map (\verts -> filter (==3) $ map (\tv -> length (intersect tv verts)) tilevertices) ridgevertices+-- let v = voronoi3 tess+-- code <- voronoi3ForRgl' v Nothing+-- -- let code = delaunay3rgl tess False True True (Just 0.5)+-- writeFile "rgl/voronoi_projhcube4.R" code++ -- x1 <- randomOnSphere 500 1+ -- x2 <- randomOnSphere 500 0.5+ -- let points = x1 ++ x2 ++ [[0,0,0]]+ -- tess <- delaunay points False False+ -- let v = voronoi3 tess+ -- vv = [last v]+ -- code <- voronoi3ForRgl' vv Nothing+ -- writeFile "rgl/voronoi_sphere.R" code++ -- let x = duplicate3 (duplicate3 (duplicate3 (cube3 ++ [[0,0,0]]) [2,0,0]) [0,2,0]) [0,0,2]+ -- tess <- delaunay x False False+ -- let v = voronoi3 tess+ -- code <- voronoi3ForRgl' v Nothing+ -- writeFile "rgl/voronoi_multicube.R" code++ -- let x = dodecahedron ++ [[0,0,0]]+ -- tess <- delaunay x False+ -- let v = voronoi3 tess+ -- code <- voronoi3ForRgl' v Nothing+ -- writeFile "rgl/voronoi_centricDodecahedron.R" code+ -- prettyShowVoronoi3 v (Just 3)++ -- let c = 4+ -- a = 1+ -- let curve3D = map (\x -> [ cos (2*pi*x) * (c + a * cos (2*pi*x))+ -- , sin (2*pi*x) * (c + a * cos (2*pi*x))+ -- , a * sin (2*pi*x)]) [i/50 | i <- [0 .. 50]]+ -- tess <- delaunay curve3D False+ -- let v = voronoi3 tess+ -- writeFile "rgl/voronoi_curve3Dtorus.R" (voronoi3ForRgl v (Just tess))++ -- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)+ -- , sin (pi*x) * sin (2*pi*x)+ -- , cos (pi*x)]) [i/50 | i <- [0 .. 50]]+ -- tess <- delaunay curve3D False+ -- let v = voronoi3 tess+ -- writeFile "rgl/voronoi_curve3D.R" (voronoi3ForRgl v (Just tess))++ -- x <- randomOnTorus 50 4 1+ -- tess <- delaunay (x ++ [[4,0,0],[-4,0,0],[0,4,0],[0,-4,0]]) False+ -- writeFile "rgl/delaunay_torus00.R" (delaunay3rgl tess True True True Nothing)+ -- -- pPrint (_tiles tess)+ -- -- pPrint (_tilefacets tess)+ -- let v = voronoi3 tess+ -- -- pPrint v+ -- -- pPrint $ zip [0 .. 50] (map (map (facetCenters tess) . vertexNeighborFacets tess) [0 .. 50])+ -- code <- voronoi3ForRgl' v Nothing+ -- writeFile "rgl/voronoi_torus00.R" code++ -- x <- randomInSquare 100+ -- tess <- delaunay (x ++ [[-1,-1],[-1,2],[2,-1],[2,2]]) False+ -- let v = voronoi2 tess+ -- writeFile "Rplots/voronoi_randomInSquare00.R" (voronoi2ForR v Nothing)++ -- let cube = [[i,j,k] | i <- [-1,2], j <- [-1,2], k <- [-1,2]]+ -- x <- randomInCube 30+ -- tess <- delaunay (cube ++ x) False+ -- let v = voronoi3 tess+ -- prettyShowVoronoi3 v (Just 3)+ -- pPrint (map (_circumcenter . _simplex) (IM.elems (_tiles tess)))+ -- code <- voronoi3ForRgl' v Nothing+ -- writeFile "rgl/voronoi_randomInCube00.R" code++ -- let cube = [[i,j,k] | i <- [0,1], j <- [0,1], k <- [0,1]]+ -- x <- randomInCube 30+ -- tess <- delaunay (cube ++ x) False+ -- let v = voronoi3 tess+ -- v' = restrictVoronoi3box (-5,5) (-5,5) (-5,5) v+ -- prettyShowVoronoi3 v' (Just 3)+ -- code <- voronoi3ForRgl' v' Nothing+ -- writeFile "rgl/voronoi_randomInCube01.R" code++ -- x <- randomInCircle 50+ -- tess <- delaunay (x ++ [[0,0]]) False+ -- let v = voronoi2 tess+ -- writeFile "Rplots/voronoi_circle01.R" (voronoi2ForR v Nothing)++ -- tess <- delaunay squareLattice False+ -- let v = voronoi2 tess+ -- putStrLn $ voronoi2ForR v (Just tess)++ -- let x = rhombicDodecahedron ++ [[0,0,0]]+ -- tess <- delaunay x False+ -- let v = voronoi3 tess+ -- code1 = voronoi3ForRgl v Nothing+ -- (_, cell) = last v+ -- code2 <- convexHull3DrglCode (cell3Vertices cell) False Nothing+ -- writeFile "rgl/voronoi_centricRhombicDodecahedron.R" (code1 ++ code2)+ -- pPrint v++ -- tess <- delaunay centricCuboctahedron False+ -- let v = voronoi3 tess+ -- code1 = voronoi3ForRgl v Nothing+ -- (_, cell) = last v+ -- code2 <- convexHull3DrglCode (cell3Vertices cell) False Nothing+ -- writeFile "rgl/voronoi_centricCuboctahedron.R" (code1 ++ code2)+ -- pPrint v++ -- x <- randomInSphere 1000+ -- tess <- delaunay2 x False+ -- print $ IM.size (_tiles tess)
+ src/ConvexHull.hs view
@@ -0,0 +1,7 @@+module ConvexHull+ ( module X )+ where+import ConvexHull.ConvexHull as X+import ConvexHull.Types as X+import Qhull.Shared as X+import Qhull.Types as X
+ src/ConvexHull/BiTruncatedTesseract.hs view
@@ -0,0 +1,23 @@+module ConvexHull.BiTruncatedTesseract+ where+import Math.Combinat.Permutations as P+import Data.List++signsAll :: (Eq a, Num a) => [[a]] -> [[a]]+signsAll = concatMap signs+ where+ signs :: (Eq a, Num a) => [a] -> [[a]]+ signs = mapM (\x -> nub [x,-x])+ ++vertices :: ([Double], Bool) -> [[Double]]+vertices (coords, allperms) =+ map (map (/ sqrt 18)) $ signsAll $+ nub $ zipWith permuteList perms (replicate 24 coords)+ where perms = filter (if allperms then const True else isEvenPermutation) (P.permutations 4)+++biTruncatedTesseract :: [[Double]]+biTruncatedTesseract = concatMap vertices + [ ([0, sqrt 2, 2 * sqrt 2, 2 * sqrt 2], True) ]+
+ src/ConvexHull/CConvexHull.hs view
@@ -0,0 +1,446 @@+{-# LINE 1 "convexhull.hsc" #-}+{-# LANGUAGE ForeignFunctionInterface #-}+module ConvexHull.CConvexHull+ ( cConvexHullToConvexHull+ , c_convexhull )+ where+import Control.Monad ((<$!>), (=<<))+import ConvexHull.Types+import qualified Data.HashMap.Strict.InsOrd as H+import Data.IntMap.Strict (IntMap, fromAscList)+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import Data.List+import Data.Tuple.Extra (both)+import Foreign+import Foreign.C.String+import Foreign.C.Types+import Qhull.Types++data CVertex = CVertex {+ __id :: CUInt+ , __point :: Ptr CDouble+}++instance Storable CVertex where+ sizeOf __ = (16)+{-# LINE 28 "convexhull.hsc" #-}+ alignment __ = 8+{-# LINE 29 "convexhull.hsc" #-}+ peek ptr = do+ id' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 31 "convexhull.hsc" #-}+ point' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 32 "convexhull.hsc" #-}+ return CVertex { __id = id'+ , __point = point' }+ poke ptr (CVertex r1 r2)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 37 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 38 "convexhull.hsc" #-}++--data Vertex = Vertex {+-- _id :: Int+-- , _point :: [Double]+--} deriving Show++cVerticesToMap :: Int -> [CVertex] -> IO (IntMap [Double])+cVerticesToMap dim cvertices = do+ let ids = map (fromIntegral . __id) cvertices+ points <- mapM (\cv -> (<$!>) (map realToFrac) (peekArray dim (__point cv)))+ cvertices+ return $ fromAscList (zip ids points)++data CVertex' = CVertex' {+ __id' :: CUInt+ , __point' :: Ptr CDouble+ , __neighfacets :: Ptr CUInt+ , __nneighfacets :: CUInt+ , __neighvertices :: Ptr CUInt+ , __nneighvertices :: CUInt+ , __neighridges :: Ptr CUInt+ , __nneighridges :: CUInt+}++instance Storable CVertex' where+ sizeOf __ = (64)+{-# LINE 64 "convexhull.hsc" #-}+ alignment __ = 8+{-# LINE 65 "convexhull.hsc" #-}+ peek ptr = do+ id' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 67 "convexhull.hsc" #-}+ point' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 68 "convexhull.hsc" #-}+ neighfacets' <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr+{-# LINE 69 "convexhull.hsc" #-}+ nneighfacets' <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr+{-# LINE 70 "convexhull.hsc" #-}+ neighvertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr+{-# LINE 71 "convexhull.hsc" #-}+ nneighsvertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr+{-# LINE 72 "convexhull.hsc" #-}+ neighridges' <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr+{-# LINE 73 "convexhull.hsc" #-}+ nneighridges' <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr+{-# LINE 74 "convexhull.hsc" #-}+ return CVertex' { __id' = id'+ , __point' = point'+ , __neighfacets = neighfacets'+ , __nneighfacets = nneighfacets'+ , __neighvertices = neighvertices'+ , __nneighvertices = nneighsvertices'+ , __neighridges = neighridges'+ , __nneighridges = nneighridges'+ }+ poke ptr (CVertex' r1 r2 r3 r4 r5 r6 r7 r8)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 86 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 87 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3+{-# LINE 88 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4+{-# LINE 89 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5+{-# LINE 90 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6+{-# LINE 91 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r7+{-# LINE 92 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r8+{-# LINE 93 "convexhull.hsc" #-}++cVerticesToVertexMap :: Int -> [CVertex'] -> IO (IntMap Vertex)+cVerticesToVertexMap dim cvertices = do+ let ids = map (fromIntegral . __id') cvertices+ nneighfacets = map (fromIntegral . __nneighfacets) cvertices+ nneighsvertices = map (fromIntegral . __nneighvertices) cvertices+ nneighridges = map (fromIntegral . __nneighridges) cvertices+ points <- mapM (\cv -> (<$!>) (map realToFrac) (peekArray dim (__point' cv)))+ cvertices+ neighfacets <- mapM (\(i, cv) -> (<$!>) (map fromIntegral)+ (peekArray i (__neighfacets cv)))+ (zip nneighfacets cvertices)+ neighvertices <- mapM (\(i, cv) ->+ (<$!>) (map fromIntegral)+ (peekArray i (__neighvertices cv)))+ (zip nneighsvertices cvertices)+ neighridges <- mapM (\(i, cv) ->+ (<$!>) (map fromIntegral)+ (peekArray i (__neighridges cv)))+ (zip nneighridges cvertices)+ return $ IM.fromList+ (zip ids (map (\(pt, fneighs, vneighs, eneighs) ->+ Vertex { _point = pt+ , _neighfacets = IS.fromAscList fneighs+ , _neighvertices = IS.fromAscList vneighs+ , _neighridges = IS.fromAscList eneighs })+ (zip4 points neighfacets neighvertices neighridges)))++data CRidge = CRidge {+ __rvertices :: Ptr CVertex+ , __ridgeOf1 :: CUInt+ , __ridgeOf2 :: CUInt+ , __ridgeSize :: CUInt+ , __ridgeid :: CUInt+ , __redges :: Ptr (Ptr CUInt)+ , __nredges :: CUInt+}++instance Storable CRidge where+ sizeOf __ = (40)+{-# LINE 133 "convexhull.hsc" #-}+ alignment __ = 8+{-# LINE 134 "convexhull.hsc" #-}+ peek ptr = do+ rvertices <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 136 "convexhull.hsc" #-}+ ridgeOf1' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 137 "convexhull.hsc" #-}+ ridgeOf2' <- (\hsc_ptr -> peekByteOff hsc_ptr 12) ptr+{-# LINE 138 "convexhull.hsc" #-}+ ridgeSize <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr+{-# LINE 139 "convexhull.hsc" #-}+ ridgeid <- (\hsc_ptr -> peekByteOff hsc_ptr 20) ptr+{-# LINE 140 "convexhull.hsc" #-}+ edges' <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr+{-# LINE 141 "convexhull.hsc" #-}+ nedges' <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr+{-# LINE 142 "convexhull.hsc" #-}+ return CRidge { __rvertices = rvertices+ , __ridgeOf1 = ridgeOf1'+ , __ridgeOf2 = ridgeOf2'+ , __ridgeSize = ridgeSize+ , __ridgeid = ridgeid+ , __redges = edges'+ , __nredges = nedges' }+ poke ptr (CRidge r1 r2 r3 r4 r5 r6 r7)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 152 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 153 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 12) ptr r3+{-# LINE 154 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r4+{-# LINE 155 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 20) ptr r5+{-# LINE 156 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r6+{-# LINE 157 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r7+{-# LINE 158 "convexhull.hsc" #-}++cRidgeToRidge :: Int -> CRidge -> IO (Int, Ridge)+cRidgeToRidge dim cridge = do+ let f1 = fromIntegral $ __ridgeOf1 cridge+ f2 = fromIntegral $ __ridgeOf2 cridge+ n = fromIntegral $ __ridgeSize cridge+ rid = fromIntegral $ __ridgeid cridge+ nedges = fromIntegral $ __nredges cridge+ vertices <- peekArray n (__rvertices cridge)+ rvertices <- cVerticesToMap dim vertices+ edges' <- if dim > 3+ then (<$!>) (map (\x -> (fromIntegral (x!!0), fromIntegral (x!!1))))+ ((=<<) (mapM (peekArray 2))+ (peekArray nedges (__redges cridge)))+ else return []+ let edges = if dim > 3+ then H.fromList (zip (map (\(i,j) -> Pair i j) edges')+ (map (both ((IM.!) rvertices)) edges'))+ else H.empty+ return (rid, Ridge { _rvertices = rvertices+ , _ridgeOf = IS.fromAscList [f1,f2]+ , _redges = edges })++data CFace = CFace {+ __fvertices :: Ptr CVertex+ , __nvertices' :: CUInt+ , __ridges :: Ptr CUInt+ , __nridges' :: CUInt+ , __center :: Ptr CDouble+ , __normal :: Ptr CDouble+ , __offset :: CDouble+ , __area :: CDouble+ , __neighbors :: Ptr CUInt+ , __neighborsize :: CUInt+ , __family :: CInt+ , __edges :: Ptr (Ptr CUInt)+ , __nedges :: CUInt+}++instance Storable CFace where+ sizeOf __ = (104)+{-# LINE 199 "convexhull.hsc" #-}+ alignment __ = 8+{-# LINE 200 "convexhull.hsc" #-}+ peek ptr = do+ fvertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 202 "convexhull.hsc" #-}+ nvertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 203 "convexhull.hsc" #-}+ ridges' <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr+{-# LINE 204 "convexhull.hsc" #-}+ nridges' <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr+{-# LINE 205 "convexhull.hsc" #-}+ center' <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr+{-# LINE 206 "convexhull.hsc" #-}+ normal' <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr+{-# LINE 207 "convexhull.hsc" #-}+ offset' <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr+{-# LINE 208 "convexhull.hsc" #-}+ area' <- (\hsc_ptr -> peekByteOff hsc_ptr 64) ptr+{-# LINE 209 "convexhull.hsc" #-}+ neighbors' <- (\hsc_ptr -> peekByteOff hsc_ptr 72) ptr+{-# LINE 210 "convexhull.hsc" #-}+ neighsize <- (\hsc_ptr -> peekByteOff hsc_ptr 80) ptr+{-# LINE 211 "convexhull.hsc" #-}+ family' <- (\hsc_ptr -> peekByteOff hsc_ptr 84) ptr+{-# LINE 212 "convexhull.hsc" #-}+ edges' <- (\hsc_ptr -> peekByteOff hsc_ptr 88) ptr+{-# LINE 213 "convexhull.hsc" #-}+ nedges' <- (\hsc_ptr -> peekByteOff hsc_ptr 96) ptr+{-# LINE 214 "convexhull.hsc" #-}+ return CFace { __fvertices = fvertices'+ , __nvertices' = nvertices'+ , __ridges = ridges'+ , __nridges' = nridges'+ , __center = center'+ , __normal = normal'+ , __offset = offset'+ , __area = area'+ , __neighbors = neighbors'+ , __neighborsize = neighsize+ , __family = family'+ , __edges = edges'+ , __nedges = nedges'+ }+ poke ptr (CFace r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 231 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 232 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r3+{-# LINE 233 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r4+{-# LINE 234 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r5+{-# LINE 235 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r6+{-# LINE 236 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r7+{-# LINE 237 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 64) ptr r8+{-# LINE 238 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 72) ptr r9+{-# LINE 239 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 80) ptr r10+{-# LINE 240 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 84) ptr r11+{-# LINE 241 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 88) ptr r12+{-# LINE 242 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 96) ptr r13+{-# LINE 243 "convexhull.hsc" #-}++cFaceToFacet :: Int -> CFace -> IO Facet+cFaceToFacet dim cface = do+ let area = realToFrac (__area cface)+ neighsize = fromIntegral (__neighborsize cface)+ offset = realToFrac (__offset cface)+ family = fromIntegral (__family cface)+ nridges = fromIntegral (__nridges' cface)+ nvertices = fromIntegral (__nvertices' cface)+ nedges = fromIntegral (__nedges cface)+ center <- (<$!>) (map realToFrac) (peekArray dim (__center cface))+ normal <- (<$!>) (map realToFrac) (peekArray dim (__normal cface))+ vertices <- (=<<) (cVerticesToMap dim)+ (peekArray nvertices (__fvertices cface))+ ridges <- (<$!>) (map fromIntegral)+ (peekArray nridges (__ridges cface))+ neighbors <- (<$!>) (map fromIntegral)+ (peekArray neighsize (__neighbors cface))+ edges' <- (<$!>) (map (\x -> (fromIntegral (x!!0), fromIntegral (x!!1))))+ ((=<<) (mapM (peekArray 2))+ (peekArray nedges (__edges cface)))+ let edges = H.fromList+ (zip (map (\(i,j) -> Pair i j) edges')+ (map (both ((IM.!) vertices)) edges'))+ return Facet { _fvertices = vertices+ , _fridges = IS.fromAscList ridges+ , _centroid = center+ , _normal' = normal+ , _offset' = offset+ , _area = area+ , _neighbors = IS.fromAscList neighbors+ , _family' = if family == -1 then None else Family family+ , _fedges = edges }++data CConvexHull = CConvexHull {+ __dim :: CUInt+ , __allvertices :: Ptr CVertex'+ , __nvertices :: CUInt+ , __faces :: Ptr CFace+ , __nfaces :: CUInt+ , __allridges :: Ptr CRidge+ , __nridges :: CUInt+ , __alledges :: Ptr (Ptr CUInt)+ , __nalledges :: CUInt+}++instance Storable CConvexHull where+ sizeOf __ = (72)+{-# LINE 291 "convexhull.hsc" #-}+ alignment __ = 8+{-# LINE 292 "convexhull.hsc" #-}+ peek ptr = do+ dim' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 294 "convexhull.hsc" #-}+ vertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 295 "convexhull.hsc" #-}+ nvertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr+{-# LINE 296 "convexhull.hsc" #-}+ faces' <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr+{-# LINE 297 "convexhull.hsc" #-}+ nfaces' <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr+{-# LINE 298 "convexhull.hsc" #-}+ allridges' <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr+{-# LINE 299 "convexhull.hsc" #-}+ nridges' <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr+{-# LINE 300 "convexhull.hsc" #-}+ alledges' <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr+{-# LINE 301 "convexhull.hsc" #-}+ nedges' <- (\hsc_ptr -> peekByteOff hsc_ptr 64) ptr+{-# LINE 302 "convexhull.hsc" #-}+ return CConvexHull { __dim = dim'+ , __allvertices = vertices'+ , __nvertices = nvertices'+ , __faces = faces'+ , __nfaces = nfaces'+ , __allridges = allridges'+ , __nridges = nridges'+ , __alledges = alledges'+ , __nalledges = nedges'+ }+ poke ptr (CConvexHull r1 r2 r3 r4 r5 r6 r7 r8 r9)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 315 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 316 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3+{-# LINE 317 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4+{-# LINE 318 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5+{-# LINE 319 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6+{-# LINE 320 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r7+{-# LINE 321 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r8+{-# LINE 322 "convexhull.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 64) ptr r9+{-# LINE 323 "convexhull.hsc" #-}++foreign import ccall unsafe "convexHull" c_convexhull+ :: Ptr CDouble -- points+ -> CUInt -- dim+ -> CUInt -- npoints+ -> CUInt -- triangulate+ -> CUInt -- print to stdout+ -> CString -- summary file+ -> Ptr CUInt -- exitcode+ -> IO (Ptr CConvexHull)++cConvexHullToConvexHull :: CConvexHull -> IO ConvexHull+cConvexHullToConvexHull cconvexhull = do+ let dim = fromIntegral (__dim cconvexhull)+ nvertices = fromIntegral (__nvertices cconvexhull)+ nfaces = fromIntegral (__nfaces cconvexhull)+ nridges = fromIntegral (__nridges cconvexhull)+ nedges = fromIntegral (__nalledges cconvexhull)+ vertices <- (=<<) (cVerticesToVertexMap dim)+ (peekArray nvertices (__allvertices cconvexhull))+ faces <- (=<<) (mapM (cFaceToFacet dim))+ (peekArray nfaces (__faces cconvexhull))+ allridges <- (=<<) (mapM (cRidgeToRidge dim))+ (peekArray nridges (__allridges cconvexhull))+ alledges' <- (<$!>) (map (\x -> (fromIntegral (x!!0), fromIntegral (x!!1))))+ ((=<<) (mapM (peekArray 2))+ (peekArray nedges (__alledges cconvexhull)))+ let alledges = let points = IM.map _point vertices in+ H.fromList+ (zip (map (\(i,j) -> Pair i j) alledges')+ (map (both ((IM.!) points)) alledges'))+ return ConvexHull {+ _hvertices = vertices+ , _hfacets = fromAscList (zip [0 .. nfaces-1] faces)+ , _hridges = fromAscList allridges+ , _hedges = alledges+ }
+ src/ConvexHull/CantiTrunc600Cell/Data.hs view
@@ -0,0 +1,67 @@+module ConvexHull.CantiTrunc600Cell.Data+ where+import Math.Combinat.Permutations as P+import Data.List+-- http://eusebeia.dyndns.org/4d/cantitrunc600cell++signs :: (Eq a, Num a) => [a] -> [[a]]+signs = mapM (\x -> nub [x,-x])++signsAll :: (Eq a, Num a) => [[a]] -> [[a]]+signsAll = concatMap signs++vertices :: ([Double], Bool) -> [[Double]]+vertices (coords, allperms) =+ map (map (/ sqrt 270.1640786)) $ signsAll $+ nub $ zipWith permuteList perms (replicate 24 coords)+ where perms = filter (if allperms then const True else isEvenPermutation) (P.permutations 4)+++allVertices :: [[Double]]+allVertices = concatMap vertices+ [ ([1, 1, 1+6*phi, 5+6*phi], True)+ , ([1, 3, 3+6*phi, 3+6*phi], True)+ , ([2, 2, 2+6*phi, 2*phi4], True)+ , ([0, 1, 3*phi, 3+9*phi], False)+ , ([0, 1, 5*phi, 5+7*phi], False)+ , ([0, 2, 4*phi, 4*phi3], False)+ , ([0, 4+3*phi, phi5, 5+4*phi], False)+ , ([0, 3+4*phi, 4+5*phi, 5+3*phi], False)+ , ([1, phi, 6*phi2, 1+5*phi], False)+ , ([1, 2*phi, 3+9*phi, 2+phi], False)+ , ([1, 3+phi, 3*phi, 4*phi3], False)+ , ([1, 3*phi, 6*phi2, 3*phi2], False)+ , ([1, 3+2*phi, 3*phi3, 5+4*phi], False)+ , ([1, phi4, 2*phi4, 5+3*phi], False)+ , ([phi, 2, phi3, 3+9*phi], False)+ , ([phi, 3, 1+3*phi, 4*phi3], False)+ , ([phi, 3*phi2, 2+6*phi, 5+4*phi], False)+ , ([phi, 2*phi3, 3*phi3, 5+3*phi], False)+ , ([2, 2*phi, 6*phi2, 2*phi3], False)+ , ([2, 3+phi, 3*phi3, 4+5*phi], False)+ , ([2, 3*phi, 5+7*phi, 3+2*phi], False)+ , ([2, 1+3*phi, 5+6*phi, 4+3*phi], False)+ , ([3, 2*phi, 5+7*phi, phi4], False)+ , ([3, 2+phi, 2*phi4, phi5], False)+ , ([3, phi3, 5+6*phi, 3+4*phi], False)+ , ([2*phi, 3*phi2, 1+6*phi, 4+5*phi], False)+ , ([2*phi, 4+3*phi, 1+5*phi, 3*phi3], False)+ , ([2+phi, 3+phi, 2+6*phi, 3*phi3], False)+ , ([2+phi, 1+3*phi, 6*phi2, 3+2*phi], False)+ , ([2+phi, 4*phi, 5+6*phi, 3*phi2], False)+ , ([phi3, 3+phi, phi4, 6*phi2], False)+ , ([phi3, 3+2*phi, 1+6*phi, 3*phi3], False)+ , ([phi3, 3*phi2, 5*phi, 2*phi4], False)+ , ([3+phi, 3*phi, 5+6*phi, 2*phi3], False)+ , ([3*phi, 3+2*phi, 1+5*phi, 2*phi4], False)+ , ([3*phi, 2*phi3, 1+6*phi, phi5], False)+ , ([3*phi, 1+5*phi, 2+6*phi, 3+4*phi], False)+ , ([1+3*phi, phi4, 1+6*phi, 2+6*phi], False)+ , ([1+3*phi, 5*phi, 3*phi3, 2*phi3], False)+ , ([4*phi, phi4, 1+5*phi, 3*phi3], False)]+ where+ phi2 = phi*phi+ phi3 = phi2*phi+ phi4 = phi3*phi+ phi5 = phi4*phi+ phi = (1 + sqrt 5) / 2
+ src/ConvexHull/ConvexHull.hs view
@@ -0,0 +1,207 @@+module ConvexHull.ConvexHull+ where+import Control.Monad (unless, when)+import ConvexHull.CConvexHull+import ConvexHull.Types+import Data.Function (on)+import Data.Graph (flattenSCCs, stronglyConnComp)+import qualified Data.HashMap.Strict.InsOrd as H+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import Data.List+import Data.List.Index (imap)+import Data.List.Unique (allUnique, count_)+import Data.Tuple.Extra (both)+import Foreign.C.String+import Foreign.C.Types+import Foreign.Marshal.Alloc (free, mallocBytes)+import Foreign.Marshal.Array (pokeArray)+import Foreign.Storable (peek, sizeOf)+import Qhull.Shared+import Qhull.Types+import Text.Printf+import Text.Regex++convexHull :: [[Double]] -- vertices+ -> Bool -- triangulate+ -> Bool -- print output to stdout+ -> Maybe FilePath -- write summary to a file+ -> IO ConvexHull+convexHull points triangulate stdout file = do+ let n = length points+ dim = length (head points)+ when (dim < 2) $+ error "dimension must be at least 2"+ unless (all (== dim) (map length (tail points))) $+ error "the points must have the same dimension"+ when (n <= dim) $+ error "insufficient number of points"+ unless (allUnique points) $+ error "some points are duplicated"+ pointsPtr <- mallocBytes (n * dim * sizeOf (undefined :: CDouble))+ pokeArray pointsPtr (concatMap (map realToFrac) points)+ exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt))+ summaryFile <- maybe (newCString []) newCString file+ resultPtr <- c_convexhull pointsPtr (fromIntegral dim) (fromIntegral n)+ (fromIntegral $ fromEnum triangulate)+ (fromIntegral $ fromEnum stdout) summaryFile exitcodePtr+ exitcode <- peek exitcodePtr+ free exitcodePtr+ free pointsPtr+ if exitcode /= 0+ then do+ free resultPtr+ error $ "qhull returned an error (code " ++ show exitcode ++ ")"+ else do+ result <- (>>=) (peek resultPtr) cConvexHullToConvexHull+ free resultPtr+ return result+++-- | convex hull summary+hullSummary :: ConvexHull -> String+hullSummary hull =+ "Convex hull:\n" +++ printf "%d vertices\n" (IM.size vertices) +++ printf "%d facets (%s)\n" nfacets families +++ printf "%d ridges\n" nridges +++ printf "%d edges\n" nedges +++ (if dim > 2+ then printf "number of vertices per facet: %s\n" (show counts_vertices) +++ (if dim > 3+ then printf "number of edges per facet: %s\n" (show counts_edges) +++ printf "number of ridges per facet: %s\n" (show counts_ridges)+ else "")+ else "")+ where+ vertices = _vertices hull+ nedges = nEdges hull+ nridges = IM.size (_hridges hull)+ facets = _hfacets hull+ facets' = IM.elems facets+ nfacets = IM.size facets+ (nf1,nf2) = both length $+ partition (None ==) (nubBy sameFamily (map _family facets'))+ families = show nf1 ++ " single, " +++ show nf2 ++ if nf2 > 1 then " families" else " family"+ dim = length $ head (IM.elems vertices)+ counts_vertices = count_ (map nVertices facets')+ counts_edges = count_ (map nEdges facets')+ counts_ridges = count_ (map (IS.size . _fridges) facets')++-- | facets ids an edge belongs to+edgeOf :: ConvexHull -> (Index, Index) -> [Int]+edgeOf hull (v1,v2) = IM.keys $ IM.filter (elem (Pair v1 v2)) facetsEdges+ where+ facetsEdges = IM.map edgesIds (_hfacets hull)++-- | ridges of a facet+facetRidges :: ConvexHull -> Facet -> IntMap Ridge+facetRidges hull facet = IM.restrictKeys (_hridges hull) (_fridges facet)++-- | vertices ids of all facets+facetsVerticesIds :: ConvexHull -> [[Index]]+facetsVerticesIds hull = map verticesIds (IM.elems $ _hfacets hull)++-- | vertices ids of all ridges+ridgesVerticesIds :: ConvexHull -> [[Index]]+ridgesVerticesIds hull = map verticesIds (IM.elems (_hridges hull))++-- | group facets of the same family+groupedFacets :: ConvexHull -> [(Family, [IndexMap [Double]], [EdgeMap])]+groupedFacets hull =+ zip3 (map head families) verticesGroups edgesGroups+ where+ facets = IM.elems (_hfacets hull)+ facetsGroups = groupBy (sameFamily `on` _family) facets+ edgesGroups = map (map _fedges) facetsGroups+ verticesGroups = map (map _fvertices) facetsGroups+ families = map (map _family) facetsGroups++-- | group facets of the same family and merge vertices and edges+groupedFacets' :: ConvexHull -> [(Family, IndexMap [Double], EdgeMap)]+groupedFacets' hull =+ map (\(f,v,e) -> (f, foldr IM.union IM.empty v, foldr delta H.empty e))+ (groupedFacets hull)+ -- zip3 (map head families) (map (foldr IM.union IM.empty) verticesGroups)+ -- (map (foldr delta H.empty) edgesGroups)+ where+ -- facets = IM.elems (_hfacets hull)+ -- facetsGroups = groupBy (sameFamily `on` _family) facets+ -- edgesGroups = map (map _fedges) facetsGroups+ -- verticesGroups = map (map _fvertices) facetsGroups+ -- families = map (map _family) facetsGroups+ delta :: EdgeMap -> EdgeMap -> EdgeMap+ delta e1 e2 = H.difference (H.union e1 e2) (H.intersection e1 e2)++-- data Vertex3 = Vertex3 Double Double Double+-- deriving Show+--+-- toVertex3 :: [Double] -> Vertex3+-- toVertex3 xs = Vertex3 (xs!!0) (xs!!1) (xs!!2)++-- | for 3D only, orders the vertices of the facet (i.e. provides a polygon) ;+-- also returns a Boolean indicating the orientation of the vertices+facetToPolygon :: Facet -> ([(Index, [Double])], Bool)+facetToPolygon facet = (polygon, dotProduct > 0)+ where+ vs = IM.toList $ _vertices facet+ x = imap (\i v -> (v, i, findIndices (connectedVertices v) vs)) vs+ where+ connectedVertices :: (Index, [Double]) -> (Index, [Double]) -> Bool+ connectedVertices (i,_) (j,_) = Pair i j `H.member` _edges facet+ polygon = flattenSCCs (stronglyConnComp x)+ vertices = map snd polygon+ v1 = vertices!!0+ v2 = vertices!!1+ v3 = vertices!!2+ normal = crossProd (zipWith subtract v1 v2) (zipWith subtract v1 v3)+ where+ crossProd u v = [ u!!1 * v!!2 - u!!2 * v!!1+ , u!!2 * v!!0 - u!!0 * v!!2+ , u!!0 * v!!1 - u!!1 * v!!0 ]+ dotProduct = sum $ zipWith (*) normal (_normal facet)++-- | for 3D only, orders the vertices of the facet (i.e. provides a polygon)+-- in anticlockwise orientation+facetToPolygon' :: Facet -> [(Index, [Double])]+facetToPolygon' facet = if test then polygon else reverse polygon+ where+ (polygon, test) = facetToPolygon facet++-- -- | like `facetToPolygon`, but returns the vertices indices+-- facetToPolygon' :: Facet -> [Index]+-- facetToPolygon' facet = map fst $ flattenSCCs (stronglyConnComp x)+-- where+-- vs = IM.toList $ _vertices facet+-- x = imap (\i v -> (v, i, findIndices (connectedVertices v) vs)) vs+-- connectedVertices :: (Index, [Double]) -> (Index, [Double]) -> Bool+-- connectedVertices (i,_) (j,_) = Pair i j `H.member` _edges facet++-- | for 4D only, orders the vertices of a ridge (i.e. provides a polygon)+ridgeToPolygon :: Ridge -> [(Index, [Double])]+ridgeToPolygon ridge = flattenSCCs (stronglyConnComp x)+ where+ vs = IM.toList $ _vertices ridge+ x = imap (\i v -> (v, i, findIndices (connectedVertices v) vs)) vs+ where+ connectedVertices :: (Index, [Double]) -> (Index, [Double]) -> Bool+ connectedVertices (i,_) (j,_) = Pair i j `H.member` _edges ridge++-- | for 3D only, convert the convex hull to STL format+hullToSTL :: ConvexHull -> FilePath -> IO ()+hullToSTL chull filename = do+ let facets = IM.elems (_hfacets chull)+ normals = map _normal facets+ facetNormals = map (\n -> "facet normal " ++ stringify " " n ++ "\nouter loop\n")+ normals+ polygons = map (\f -> "vertex " +++ (stringify "\nvertex " . map snd . facetToPolygon') f) facets+ vertices = map (++ "\nendloop\nendfacet\n") polygons+ vertices' = map (\v -> subRegex (mkRegex "\\]") (subRegex (mkRegex "\\[") v "") "") vertices+ out = subRegex (mkRegex ",") (concat [x ++ y | x <- facetNormals, y <- vertices']) " "+ writeFile filename ("solid " ++ filename ++ " produced by QHULL\n" ++ out)+ where+ stringify :: Show a => String -> [a] -> String+ stringify sep = intercalate sep . map show
+ src/ConvexHull/Examples.hs view
@@ -0,0 +1,4996 @@+module ConvexHull.Examples+ where+import Data.List hiding (permutations)+import Data.List.Split (chunksOf)+import Math.Combinat.Permutations+import System.Random++sixhundredCell :: [[Double]]+sixhundredCell =+ [[i, j, k, l] | i <- pm, j <- pm, k <- pm, l <-pm] +++ [[0, 0, 0, i*2] | i <- pm] +++ [[0, 0, i*2, 0] | i <- pm] +++ [[0, i*2, 0, 0] | i <- pm] +++ [[i*2, 0, 0, 0] | i <- pm] +++ [ permuteList p [i*phi, j, k/phi, 0] |+ p <- permutations4, isEvenPermutation p, i <- pm, j <- pm, k <- pm] + where+ permutations4 = permutations 4+ phi = (1 + sqrt 5) / 2+ pm = [-1,1]++hexadecachoron :: [[Double]]+hexadecachoron =+ [ [1,0,0,0]+ , [-1,0,0,0]+ , [0,1,0,0]+ , [0,-1,0,0]+ , [0,0,1,0]+ , [0,0,-1,0]+ , [0,0,0,1]+ , [0,0,0,-1] ]++runcitruncated5cell :: [[Double]]+runcitruncated5cell =+ [[2/sqrt 10, -2/sqrt 6, 1/sqrt 3, 3]+ ,[2/sqrt 10, 2/sqrt 6, -1/sqrt 3, 3]+ ,[2/sqrt 10, -2/sqrt 6, 4/sqrt 3, 2]+ ,[2/sqrt 10, 2/sqrt 6, -4/sqrt 3, 2]+ ,[2/sqrt 10, -2/sqrt 6, -5/sqrt 3, 1]+ ,[2/sqrt 10, 2/sqrt 6, 5/sqrt 3, 1]+ ,[2/sqrt 10, sqrt 6, 0, 2]+ ,[2/sqrt 10, -sqrt 6, 0, 2]+ ,[2/sqrt 10, sqrt 6, sqrt 3, 1]+ ,[2/sqrt 10, -sqrt 6, sqrt 3, 1]+ ,[7/sqrt 10, -1/sqrt 6, 2/sqrt 3, 2]+ ,[7/sqrt 10, -1/sqrt 6, -4/sqrt 3, 0]+ ,[7/sqrt 10, 3/sqrt 6, 0, 2]+ ,[7/sqrt 10, 3/sqrt 6, sqrt 3, 1]+ ,[7/sqrt 10, 3/sqrt 6, -sqrt 3, 1]+ ,[7/sqrt 10, -5/sqrt 6, 1/sqrt 3, 1]+ ,[7/sqrt 10, -5/sqrt 6, -2/sqrt 3, 0]+ ,[-3/sqrt 10, 1/sqrt 6, 1/sqrt 3, 3]+ ,[-3/sqrt 10, 1/sqrt 6, 4/sqrt 3, 2]+ ,[-3/sqrt 10, 1/sqrt 6, -5/sqrt 3, 1]+ ,[-3/sqrt 10, 5/sqrt 6, 2/sqrt 3, 2]+ ,[-3/sqrt 10, 5/sqrt 6, -4/sqrt 3, 0]+ ,[-3/sqrt 10, -7/sqrt 6, -1/sqrt 3, 1]+ ,[-3/sqrt 10, -7/sqrt 6, 2/sqrt 3, 0]+ ,[-8/sqrt 10, 0, 0, 2]+ ,[-8/sqrt 10, 0, sqrt 3, 1]+ ,[-8/sqrt 10, 0, -sqrt 3, 1]+ ,[-8/sqrt 10, -4/sqrt 6, -1/sqrt 3, 1]+ ,[-8/sqrt 10, 4/sqrt 6, 1/sqrt 3, 1]+ ,[-8/sqrt 10, -4/sqrt 6, 2/sqrt 3, 0]+ ,[-8/sqrt 10, 4/sqrt 6, -2/sqrt 3, 0]+ ,[2/sqrt 10, -2/sqrt 6, 1/sqrt 3, -3]+ ,[2/sqrt 10, 2/sqrt 6, -1/sqrt 3, -3]+ ,[2/sqrt 10, -2/sqrt 6, 4/sqrt 3, -2]+ ,[2/sqrt 10, 2/sqrt 6, -4/sqrt 3, -2]+ ,[2/sqrt 10, -2/sqrt 6, -5/sqrt 3, -1]+ ,[2/sqrt 10, 2/sqrt 6, 5/sqrt 3, -1]+ ,[2/sqrt 10, sqrt 6, 0, -2]+ ,[2/sqrt 10, -sqrt 6, 0, -2]+ ,[2/sqrt 10, sqrt 6, -sqrt 3, -1]+ ,[2/sqrt 10, -sqrt 6, -sqrt 3, -1]+ ,[7/sqrt 10, -1/sqrt 6, 2/sqrt 3, -2]+ ,[7/sqrt 10, 3/sqrt 6, 0, -2]+ ,[7/sqrt 10, 3/sqrt 6, sqrt 3, -1]+ ,[7/sqrt 10, 3/sqrt 6, -sqrt 3, -1]+ ,[7/sqrt 10, -5/sqrt 6, 1/sqrt 3, -1]+ ,[-3/sqrt 10, 1/sqrt 6, 1/sqrt 3, -3]+ ,[-3/sqrt 10, 1/sqrt 6, 4/sqrt 3, -2]+ ,[-3/sqrt 10, 1/sqrt 6, -5/sqrt 3, -1]+ ,[-3/sqrt 10, 5/sqrt 6, 2/sqrt 3, -2]+ ,[-3/sqrt 10, -7/sqrt 6, -1/sqrt 3, -1]+ ,[-8/sqrt 10, 0, 0, -2]+ ,[-8/sqrt 10, 0, sqrt 3, -1]+ ,[-8/sqrt 10, 0, -sqrt 3, -1]+ ,[-8/sqrt 10, -4/sqrt 6, -1/sqrt 3, -1]+ ,[-8/sqrt 10, 4/sqrt 6, 1/sqrt 3, -1] ]+++cantellated5cell :: [[Double]]+cantellated5cell =+ [[4/sqrt 10, 0, 0, 2]+ ,[4/sqrt 10, 0, 3/sqrt 3, 1]+ ,[4/sqrt 10, 0, -3/sqrt 3, 1]+ ,[4/sqrt 10, -4/sqrt 6, 2/sqrt 3, 0]+ ,[4/sqrt 10, 4/sqrt 6, -2/sqrt 3, 0]+ ,[4/sqrt 10, -4/sqrt 6, -1/sqrt 3, 1]+ ,[4/sqrt 10, 4/sqrt 6, 1/sqrt 3, 1]+ ,[-6/sqrt 10, -2/sqrt 6, -2/sqrt 3, 0]+ ,[-6/sqrt 10, 2/sqrt 6, 2/sqrt 3, 0]+ ,[-1/sqrt 10, -1/sqrt 6, -4/sqrt 3, 0]+ ,[-1/sqrt 10, -5/sqrt 6, -2/sqrt 3, 0]+ ,[-1/sqrt 10, 3/sqrt 6, 0, 2]+ ,[-1/sqrt 10, -1/sqrt 6, 2/sqrt 3, 2]+ ,[-1/sqrt 10, -5/sqrt 6, 1/sqrt 3, 1]+ ,[-1/sqrt 10, 3/sqrt 6, 3/sqrt 3, 1]+ ,[-1/sqrt 10, 3/sqrt 6, -3/sqrt 3, 1]+ ,[-6/sqrt 10, -2/sqrt 6, 1/sqrt 3, 1]+ ,[-6/sqrt 10, 2/sqrt 6, -1/sqrt 3, 1]+ ,[4/sqrt 10, 0, 0, -2]+ ,[4/sqrt 10, 0, 3/sqrt 3, -1]+ ,[4/sqrt 10, 0, -3/sqrt 3, -1]+ ,[4/sqrt 10, -4/sqrt 6, -1/sqrt 3, -1]+ ,[4/sqrt 10, 4/sqrt 6, 1/sqrt 3, -1]+ ,[-1/sqrt 10, 3/sqrt 6, 0, -2]+ ,[-1/sqrt 10, -1/sqrt 6, 2/sqrt 3, -2]+ ,[-1/sqrt 10, -5/sqrt 6, 1/sqrt 3, -1]+ ,[-1/sqrt 10, 3/sqrt 6, 3/sqrt 3, -1]+ ,[-1/sqrt 10, 3/sqrt 6, -3/sqrt 3, -1]+ ,[-6/sqrt 10, -2/sqrt 6, 1/sqrt 3, -1]+ ,[-6/sqrt 10, 2/sqrt 6, -1/sqrt 3, -1]]++bitruncated5cell :: [[Double]]+bitruncated5cell =+ [ [0, 4/sqrt 6, 4/sqrt 3, 0]+ , [0, -4/sqrt 6, -4/sqrt 3, 0]+ , [0, 4/sqrt 6, -2/sqrt 3, 2]+ , [0, 4/sqrt 6, -2/sqrt 3, -2]+ , [0, -4/sqrt 6, 2/sqrt 3, -2]+ , [0, -4/sqrt 6, 2/sqrt 3, 2]+ , [5/sqrt 10, 1/sqrt 6, 4/sqrt 3, 0]+ , [-5/sqrt 10, -1/sqrt 6, -4/sqrt 3, 0]+ , [5/sqrt 10, 1/sqrt 6, -2/sqrt 3, 2]+ , [5/sqrt 10, 1/sqrt 6, -2/sqrt 3, -2]+ , [-5/sqrt 10, -1/sqrt 6, 2/sqrt 3, -2]+ , [-5/sqrt 10, -1/sqrt 6, 2/sqrt 3, 2]+ , [5/sqrt 10, 5/sqrt 6, 2/sqrt 3, 0]+ , [-5/sqrt 10, -5/sqrt 6, -2/sqrt 3, 0]+ , [5/sqrt 10, 5/sqrt 6, -1/sqrt 3, 1]+ , [5/sqrt 10, 5/sqrt 6, -1/sqrt 3, -1]+ , [-5/sqrt 10, -5/sqrt 6, 1/sqrt 3, -1]+ , [-5/sqrt 10, -5/sqrt 6, 1/sqrt 3, 1]+ , [5/sqrt 10, -3/sqrt 6, 0, 2]+ , [5/sqrt 10, -3/sqrt 6, 0, -2]+ , [-5/sqrt 10, 3/sqrt 6, 0, -2]+ , [-5/sqrt 10, 3/sqrt 6, 0, 2]+ , [5/sqrt 10, -3/sqrt 6, sqrt 3, 1]+ , [5/sqrt 10, -3/sqrt 6, sqrt 3, -1]+ , [5/sqrt 10, -3/sqrt 6, -sqrt 3, 1]+ , [5/sqrt 10, -3/sqrt 6, -sqrt 3, -1]+ , [-5/sqrt 10, 3/sqrt 6, -sqrt 3, -1]+ , [-5/sqrt 10, 3/sqrt 6, -sqrt 3, 1]+ , [-5/sqrt 10, 3/sqrt 6, sqrt 3, -1]+ , [-5/sqrt 10, 3/sqrt 6, sqrt 3, 1] ]++rectified5cell :: [[Double]]+rectified5cell =+ [ [-3/sqrt 10, -3/sqrt 6, 0, 0]+ , [-3/sqrt 10, 1/sqrt 6, -2/sqrt 3, 0]+ , [-3/sqrt 10, 1/sqrt 6, 1/sqrt 3, 1]+ , [-3/sqrt 10, 1/sqrt 6, 1/sqrt 3, -1]+ , [2/sqrt 10, 2/sqrt 6, 2/sqrt 3, 0]+ , [2/sqrt 10, -2/sqrt 6, -2/sqrt 3, 0]+ , [2/sqrt 10, 2/sqrt 6, -1/sqrt 3, 1]+ , [2/sqrt 10, 2/sqrt 6, -1/sqrt 3, -1]+ , [2/sqrt 10, -2/sqrt 6, 1/sqrt 3, 1]+ , [2/sqrt 10, -2/sqrt 6, 1/sqrt 3, -1] ]++sircope :: [[Double]]+sircope =+ let a = (1+ sqrt 2)/2 in+ [+ [-a, -0.5, -0.5, -0.5],+ [a, -0.5, -0.5, -0.5],+ [-a, 0.5, -0.5, -0.5],+ [a, 0.5, -0.5, -0.5],+ [-a, -0.5, 0.5, -0.5],+ [a, -0.5, 0.5, -0.5],+ [-a, 0.5, 0.5, -0.5],+ [a, 0.5, 0.5, -0.5],+ [-a, -0.5, -0.5, 0.5],+ [a, -0.5, -0.5, 0.5],+ [-a, 0.5, -0.5, 0.5],+ [a, 0.5, -0.5, 0.5],+ [-a, -0.5, 0.5, 0.5],+ [a, -0.5, 0.5, 0.5],+ [-a, 0.5, 0.5, 0.5],+ [a, 0.5, 0.5, 0.5],+ [-0.5, -a, -0.5, -0.5],+ [0.5, -a, -0.5, -0.5],+ [-0.5, a, -0.5, -0.5],+ [0.5, a, -0.5, -0.5],+ [-0.5, -a, 0.5, -0.5],+ [0.5, -a, 0.5, -0.5],+ [-0.5, a, 0.5, -0.5],+ [0.5, a, 0.5, -0.5],+ [-0.5, -a, -0.5, 0.5],+ [0.5, -a, -0.5, 0.5],+ [-0.5, a, -0.5, 0.5],+ [0.5, a, -0.5, 0.5],+ [-0.5, -a, 0.5, 0.5],+ [0.5, -a, 0.5, 0.5],+ [-0.5, a, 0.5, 0.5],+ [0.5, a, 0.5, 0.5],+ [-0.5, -0.5, -a, -0.5],+ [0.5, -0.5, -a, -0.5],+ [-0.5, 0.5, -a, -0.5],+ [0.5, 0.5, -a, -0.5],+ [-0.5, -0.5, a, -0.5],+ [0.5, -0.5, a, -0.5],+ [-0.5, 0.5, a, -0.5],+ [0.5, 0.5, a, -0.5],+ [-0.5, -0.5, -a, 0.5],+ [0.5, -0.5, -a, 0.5],+ [-0.5, 0.5, -a, 0.5],+ [0.5, 0.5, -a, 0.5],+ [-0.5, -0.5, a, 0.5],+ [0.5, -0.5, a, 0.5],+ [-0.5, 0.5, a, 0.5],+ [0.5, 0.5, a, 0.5]+ ]++tutcup :: [[Double]]+tutcup =+ [ [1, -1/sqrt 3, 5/sqrt 6, 1/sqrt 2]+ , [-1, -1/sqrt 3, 5/sqrt 6, 1/sqrt 2]+ , [0, 2/sqrt 3, 5/sqrt 6, 1/sqrt 2]+ , [2, -2/sqrt 3, 1/sqrt 6, 1/sqrt 2]+ , [-2, -2/sqrt 3, 1/sqrt 6, 1/sqrt 2]+ , [0, 4/sqrt 3, 1/sqrt 6, 1/sqrt 2]+ , [1, 3/sqrt 3, -3/sqrt 6, 1/sqrt 2]+ , [1, -3/sqrt 3, -3/sqrt 6, 1/sqrt 2]+ , [-1, 3/sqrt 3, -3/sqrt 6, 1/sqrt 2]+ , [-1, -3/sqrt 3, -3/sqrt 6, 1/sqrt 2]+ , [2, 0, -3/sqrt 6, 1/sqrt 2]+ , [-2, 0, -3/sqrt 6, 1/sqrt 2]+ , [1, 3/sqrt 3, 3/sqrt 6, -1/sqrt 2]+ , [1, -3/sqrt 3, 3/sqrt 6, -1/sqrt 2]+ , [-1, 3/sqrt 3, 3/sqrt 6, -1/sqrt 2]+ , [-1, -3/sqrt 3, 3/sqrt 6, -1/sqrt 2]+ , [2, 0, 3/sqrt 6, -1/sqrt 2]+ , [-2, 0, 3/sqrt 6, -1/sqrt 2]+ , [2, 2/sqrt 3, -1/sqrt 6, -1/sqrt 2]+ , [-2, 2/sqrt 3, -1/sqrt 6, -1/sqrt 2]+ , [0, -4/sqrt 3, -1/sqrt 6, -1/sqrt 2]+ , [1, 1/sqrt 3, -5/sqrt 6, -1/sqrt 2]+ , [-1, 1/sqrt 3, -5/sqrt 6, -1/sqrt 2]+ , [0, -2/sqrt 3, -5/sqrt 6, -1/sqrt 2] ]++++runcinatedTesseract :: [[Double]]+runcinatedTesseract =+ [[i*a, j*b, k*c, l*d] | i <- pm, j <- pm, k <- pm, l <- pm, (a,b,c,d) <- s]+ where+ pm = [-1,1]+ x = 1 + sqrt 2+ s = [(1,1,1,x),(1,1,x,1),(1,x,1,1),(x,1,1,1)]++runcinated5cells :: [[Double]]+runcinated5cells = [ [sqrt(5/2), 1/sqrt 6, 1/sqrt 3, 1]+ ,[sqrt(5/2), 1/sqrt 6, 1/sqrt 3, -1]+ ,[-sqrt(5/2), -1/sqrt 6, -1/sqrt 3, -1]+ ,[-sqrt(5/2), -1/sqrt 6, -1/sqrt 3, 1]+ ,[sqrt(5/2), 1/sqrt 6, -2/sqrt 3, 0]+ ,[-sqrt(5/2), -1/sqrt 6, 2/sqrt 3, 0]+ ,[sqrt(5/2), -sqrt(3/2), 0, 0]+ ,[-sqrt(5/2), sqrt(3/2), 0, 0]+ ,[0, 2*sqrt(2/3), 1/sqrt 3, 1]+ ,[0, 2*sqrt(2/3), 1/sqrt 3, -1]+ ,[0, -2*sqrt(2/3), -1/sqrt 3, -1]+ ,[0, -2*sqrt(2/3), -1/sqrt 3, 1]+ ,[0, 2*sqrt(2/3), -2/sqrt 3, 0]+ ,[0, -2*sqrt(2/3), 2/sqrt 3, 0]+ ,[0, 0, sqrt 3, 1]+ ,[0, 0, sqrt 3, -1]+ ,[0, 0, -sqrt 3, 1]+ ,[0, 0, -sqrt 3, -1]+ ,[0, 0, 0, 2]+ ,[0, 0, 0, -2] ]++truncated5cells :: [[Double]]+truncated5cells = [ [3/sqrt 10, -1/sqrt 6, 2/sqrt 3, 2]+ ,[3/sqrt 10, -1/sqrt 6, 2/sqrt 3, -2]+ ,[3/sqrt 10, -1/sqrt 6, -4/sqrt 3, 0]+ ,[3/sqrt 10, 3/sqrt 6, 0, 2]+ ,[3/sqrt 10, 3/sqrt 6, 0, -2]+ ,[3/sqrt 10, 3/sqrt 6, sqrt 3, 1]+ ,[3/sqrt 10, 3/sqrt 6, sqrt 3, -1]+ ,[3/sqrt 10, 3/sqrt 6, -sqrt 3, 1]+ ,[3/sqrt 10, 3/sqrt 6, -sqrt 3, -1]+ ,[3/sqrt 10, -5/sqrt 6, 1/sqrt 3, 1]+ ,[3/sqrt 10, -5/sqrt 6, 1/sqrt 3, -1]+ ,[3/sqrt 10, -5/sqrt 6, -2/sqrt 3, 0]+ ,[-2/sqrt 10, 2/sqrt 6, 2/sqrt 3, 2]+ ,[-2/sqrt 10, 2/sqrt 6, 2/sqrt 3, -2]+ ,[-2/sqrt 10, 2/sqrt 6, -4/sqrt 3, 0]+ ,[-2/sqrt 10, -sqrt 6, 0, 0]+ ,[-7/sqrt 10, 1/sqrt 6, 1/sqrt 3, 1]+ ,[-7/sqrt 10, 1/sqrt 6, 1/sqrt 3, -1]+ ,[-7/sqrt 10, 1/sqrt 6, -2/sqrt 3, 0]+ ,[-7/sqrt 10, -3/sqrt 6, 0, 0] ]++truncated24cells :: [[Double]]+truncated24cells = [permuteList p [0,i,j*2,k*3] | p <- permutations 4, i <- pm, j <- pm , k <- pm]+ where+ pm = [-1,1]++thex :: [[Double]]+thex = nub [ permuteList p [0,0,i,j*2] | p <- permutations 4, i <- pm, j <- pm]+ where+ pm = [-1,1]++truncatedTetrahedron :: [[Double]]+truncatedTetrahedron = [ [1/sqrt 6, -2/sqrt 3, 2]+ , [1/sqrt 6, -2/sqrt 3, -2]+ , [1/sqrt 6, 4/ sqrt 3, 0]+ , [-3/sqrt 6, 0, 2]+ , [-3/sqrt 6, 0, -2]+ , [-3/sqrt 6, sqrt 3, 1]+ , [-3/sqrt 6, -sqrt 3, 1]+ , [-3/sqrt 6, sqrt 3, -1]+ , [-3/sqrt 6, -sqrt 3, -1]+ , [5/sqrt 6, -1/sqrt 3, 1]+ , [5/sqrt 6, -1/sqrt 3, -1]+ , [5/sqrt 6, 2/sqrt 3, 0] ]+++daVinci :: [[Double]]+daVinci = [ [1.61352, -0.43234, 1.1862],+ [1.18118, -1.18118, 1.1862],+ [0.43234, -1.61352, 1.1862],+ [-0.43234, -1.61352, 1.1862],+ [-1.18118, -1.18118, 1.1862],+ [-1.61352, -0.43234, 1.1862],+ [-1.61352, 0.43234, 1.1862],+ [-1.18118, 1.18118, 1.1862],+ [-0.43234, 1.61352, 1.1862],+ [0.43234, 1.61352, 1.1862],+ [1.18118, 1.18118, 1.1862],+ [1.61352, 0.43234, 1.1862],+ [1.61352, -0.43234, -1.1862],+ [1.61352, 0.43234, -1.1862],+ [1.18118, 1.18118, -1.1862],+ [0.43234, 1.61352, -1.1862],+ [-0.43234, 1.61352, -1.1862],+ [-1.18118, 1.18118, -1.1862],+ [-1.61352, 0.43234, -1.1862],+ [-1.61352, -0.43234, -1.1862],+ [-1.18118, -1.18118, -1.1862],+ [-0.43234, -1.61352, -1.1862],+ [0.43234, -1.61352, -1.1862],+ [1.18118, -1.18118, -1.1862],+ [2.0102, 0.53863, 0],+ [1.47157, 1.47157, 0],+ [0.53863, 2.0102, 0],+ [-0.53863, 2.0102, 0],+ [-1.47157, 1.47157, 0],+ [-2.0102, 0.53863, 0],+ [-2.0102, -0.53863, 0],+ [-1.47157, -1.47157, 0],+ [-0.53863, -2.0102, 0],+ [0.53863, -2.0102, 0],+ [1.47157, -1.47157, 0],+ [2.0102, -0.53863, 0],+ [0.89068, 0.23866, 1.77777],+ [0.89068, -0.23866, 1.77777],+ [0.65202, -0.65202, 1.77777],+ [0.23866, -0.89068, 1.77777],+ [-0.23866, -0.89068, 1.77777],+ [-0.65202, -0.65202, 1.77777],+ [-0.89068, -0.23866, 1.77777],+ [-0.89068, 0.23866, 1.77777],+ [-0.65202, 0.65202, 1.77777],+ [-0.23866, 0.89068, 1.77777],+ [0.23866, 0.89068, 1.77777],+ [0.65202, 0.65202, 1.77777],+ [0.65202, -0.65202, -1.77777],+ [0.89068, -0.23866, -1.77777],+ [0.89068, 0.23866, -1.77777],+ [0.65202, 0.65202, -1.77777],+ [0.23866, 0.89068, -1.77777],+ [-0.23866, 0.89068, -1.77777],+ [-0.65202, 0.65202, -1.77777],+ [-0.89068, 0.23866, -1.77777],+ [-0.89068, -0.23866, -1.77777],+ [-0.65202, -0.65202, -1.77777],+ [-0.23866, -0.89068, -1.77777],+ [0.23866, -0.89068, -1.77777],+ [0, 0, 2.04922],+ [0, 0, -2.04922]]+++-- Cuboctohadron4D - does not work+cuboctahedron4d :: [[Double]]+cuboctahedron4d = nub [ permuteList p [0,1,1,2] | p <- permutations 4]++-- OLOID --+twocircles :: [[Double]]+twocircles = circle1 `union` circle2+ where+ circle1 = [[cos (realToFrac i * 2*pi/30), sin (realToFrac i * 2*pi/30), 0] | i <- [0 .. 29]]+ circle2 = [[0, cos (realToFrac i * 2*pi/30) - 1, sin (realToFrac i * 2*pi/30)] | i <- [0 .. 29]]++translate3 :: [[Double]] -> [Double] -> [[Double]]+translate3 points u = map (zipWith (+) u) points++duplicate3 :: [[Double]] -> [Double] -> [[Double]]+duplicate3 points u = nub $ points ++ translate3 points u++rgg :: [[Double]]+rgg = [[-5,-5, 16], [-5, 8, 3 ], [ 4,-1, 3 ], [ 4,-5, 7], [ 4,-1,-10],+ [ 4,-5,-10], [-5, 8,-10], [-5,-5,-10]]++centricCube :: [[Double]]+centricCube = [[-1,-1,-1],[-1,-1, 1],[-1, 1,-1],[-1, 1, 1],[ 1,-1,-1],+ [ 1,-1, 1],[ 1, 1,-1],[ 1, 1, 1],[ 0, 0, 0]]++squareLattice :: [[Double]]+squareLattice = [[0,0],[0,1],[0,2]+ ,[1,0],[1,1],[1,2]+ ,[2,0],[2,1],[2,2]]++centricSquare :: [[Double]]+centricSquare = [[0,0],[0,2],[2,0],[2,2],[1,1]]++cuboctahedron :: [[Double]]+cuboctahedron = [[i,j,0] | i <- [-1,1], j <- [-1,1]] +++ [[i,0,j] | i <- [-1,1], j <- [-1,1]] +++ [[0,i,j] | i <- [-1,1], j <- [-1,1]]++truncatedCuboctahedron :: [[Double]]+truncatedCuboctahedron =+ [[i, j * (1 + sqrt 2), k * (1 + 2*sqrt 2)] | i <- pm, j <- pm, k <- pm] +++ [[j * (1 + sqrt 2), i, k * (1 + 2*sqrt 2)] | i <- pm, j <- pm, k <- pm] +++ [[j * (1 + sqrt 2), k * (1 + 2*sqrt 2), i] | i <- pm, j <- pm, k <- pm] +++ [[i, k * (1 + 2*sqrt 2), j * (1 + sqrt 2)] | i <- pm, j <- pm, k <- pm] +++ [[k * (1 + 2*sqrt 2), i, j * (1 + sqrt 2)] | i <- pm, j <- pm, k <- pm] +++ [[k * (1 + 2*sqrt 2), j * (1 + sqrt 2), i] | i <- pm, j <- pm, k <- pm]+ where+ pm = [-1,1]++rhombicDodecahedron :: [[Double]]+rhombicDodecahedron = [[-1.0, 0.0, 0.0], [-0.5,-0.5,-0.5], [-0.5,-0.5, 0.5],+ [ 0.0,-1.0, 0.0], [-0.5, 0.5,-0.5], [-0.5, 0.5, 0.5],+ [ 0.0, 1.0, 0.0], [ 1.0, 0.0, 0.0], [ 0.5,-0.5,-0.5],+ [ 0.5,-0.5, 0.5], [ 0.5, 0.5,-0.5], [ 0.5, 0.5, 0.5],+ [ 0.0, 0.0,-1.0], [ 0.0, 0.0, 1.0]]++faceCenteredCubic :: [[Double]]+faceCenteredCubic = [[-1,-1,-1],[-1,-1,1],[-1,1,-1],[-1,1,1]+ ,[1,-1,-1],[1,-1,1],[1,1,-1],[1,1,1]+ ,[1,0,0],[-1,0,0]+ ,[0,1,0],[0,-1,0]+ ,[0,0,1],[0,0,-1]]++waves :: [[Double]]+waves = [f u v | u <- seq_u, v <- seq_v]+ where+ f u v = [u, 0.05*(sin(v*4*pi)+sin(u*4*pi)), v]+ frac p q = realToFrac p/ realToFrac q+ n = 10+ seq_u = [8 * frac i n - 4 | i <- [0 .. n]]+ seq_v = [4 * frac i n - 2 | i <- [0 .. n]]++waves2D :: [[Double]]+waves2D = concatMap (\y' -> map (\[x,y] -> [x, y+y']) sinusoid) seq_y+ where+ seq_x = [realToFrac i * pi/2 | i <- [0 .. 20]]+ sinusoid = map (\(x,y) -> [x,y]) (zip seq_x (map sin seq_x))+ seq_y = [realToFrac 2*i | i <- [0 .. 20]]+++type Vertex = [Double]+ncube :: Int -> [Vertex]+ncube n = concatMap (mapM (\x -> nub [x,-x])) [replicate n 1]++cube3 :: [[Double]]+cube3 = [[i,j,k] | i <- [-1,1], j <- [-1,1], k <- [-1,1]]++cube4 :: [[Double]]+cube4 = [[i,j,k,l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]]++cube5 :: [[Double]]+cube5 = [[i,j,k,l,m] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1],+ m <- [-1,1]]++cube6 :: [[Double]]+cube6 = [[i,j,k,l,m,n] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1],+ m <- [-1,1], n <- [-1,1]]+++mobiusStrip :: [[Double]]+mobiusStrip = map (\(u,v) -> [ cos u * (1 + v/2 * cos(u/2))+ , sin u * (1 + v/2 * cos(u/2))+ , v/2 * sin(u/2) ]) uv+ where+ uv = [(u,v) | u <- u_, v <- v_]+ u_ = [i/50 * 2 *pi | i <- [0 .. 50]]+ v_ = [-1,1] -- [i/50 | i <- [-50 .. 50]]++irregularPolyhedron :: [[Double]]+irregularPolyhedron =+ [ [ -0.586233 , 0.192482 , -1.9732e-2 ]+ , [ -0.344233 , 0.301871 , 0.358301 ]+ , [ 0.344233 , -0.301871 , 0.358301 ]+ , [ 0.179112 , -0.514398 , -0.290554 ]+ , [ 1.1612e-2 , -0.564879 , 0.137037 ]+ , [ -1.1612e-2 , 0.564879 , 0.137037 ]+ , [ -0.179112 , 0.514398 , -0.290554 ]+ , [ -0.151548 , -0.185001 , -0.529915 ]+ , [ -5.5686e-2 , -0.430284 , -0.411612 ]+ , [ -0.364663 , -0.382652 , -0.277601 ]+ , [ -0.394163 , -0.151218 , -0.422176 ]+ , [ 5.5686e-2 , 0.430284 , -0.411612 ]+ , [ 0.364663 , 0.382652 , -0.277601 ]+ , [ 0.394163 , 0.151218 , -0.422176 ]+ , [ 0.151548 , 0.185001 , -0.529915 ]+ , [ 0.361372 , 0.268614 , 0.393607 ]+ , [ 0.246149 , 0.511456 , 0.185169 ]+ , [ 0.417404 , 0.426728 , -1.1876e-2 ]+ , [ 0.510787 , 0.234322 , 0.148969 ]+ , [ -0.361372 , -0.268614 , 0.393608 ]+ , [ -0.510787 , -0.234322 , 0.148969 ]+ , [ -0.417404 , -0.426728 , -1.1876e-2 ]+ , [ -0.246149 , -0.511456 , 0.185169 ]+ , [ 0.430396 , -0.334808 , -0.249032 ]+ , [ 0.586233 , -0.192482 , -1.9732e-2 ]+ , [ 0.587567 , -0.188189 , -2.1408e-2 ]+ , [ 0.509201 , -0.136702 , -0.282307 ]+ , [ -0.430396 , 0.334808 , -0.249032 ]+ , [ -0.509201 , 0.136702 , -0.282307 ]+ , [ -0.587567 , 0.188189 , -2.1408e-2 ]+ , [ -0.272269 , -0.118712 , 0.520703 ]+ , [ -0.248355 , 9.5223e-2 , 0.535651 ]+ , [ 0.272269 , 0.118712 , 0.520703 ]+ , [ -2.22e-4 , 2.388e-3 , 0.617335 ]+ , [ 2.22e-4 , -2.388e-3 , 0.617335 ]+ , [ 0.248355 , -9.5223e-2 , 0.535651 ]+ ]++truncatedCube :: [[Double]]+truncatedCube =+ let x = 1 + sqrt 2 in+ [[i, j*x, k*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1]] +++ [[i*x, j, k*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1]] +++ [[i*x, j*x, k] | i <- [-1,1], j <- [-1,1], k <- [-1,1]]++truncatedTesseract :: [[Double]]+truncatedTesseract =+ let x = 1 + sqrt 2 in+ [[i, j*x, k*x, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i*x, j, k*x, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i*x, j*x, k, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i*x, j*x, k*x, l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]]++projectedTruncatedTesseract :: [[Double]]+projectedTruncatedTesseract =+ [ [-x1,-x2,-x2],+ [-x4,-x3,-x3],+ [-x1,-x2, x2],+ [-x4,-x3, x3],+ [-x1, x2,-x2],+ [-x4, x3,-x3],+ [-x1, x2, x2],+ [-x4, x3, x3],+ [ x1,-x2,-x2],+ [ x4,-x3,-x3],+ [ x1,-x2, x2],+ [ x4,-x3, x3],+ [ x1, x2,-x2],+ [ x4, x3,-x3],+ [ x1, x2, x2],+ [ x4, x3, x3],+ [-x2,-x1,-x2],+ [-x3,-x4,-x3],+ [-x2,-x1, x2],+ [-x3,-x4, x3],+ [-x2, x1,-x2],+ [-x3, x4,-x3],+ [-x2, x1, x2],+ [-x3, x4, x3],+ [ x2,-x1,-x2],+ [ x3,-x4,-x3],+ [ x2,-x1, x2],+ [ x3,-x4, x3],+ [ x2, x1,-x2],+ [ x3, x4,-x3],+ [ x2, x1, x2],+ [ x3, x4, x3],+ [-x2,-x2,-x1],+ [-x3,-x3,-x4],+ [-x2,-x2, x1],+ [-x3,-x3, x4],+ [-x2, x2,-x1],+ [-x3, x3,-x4],+ [-x2, x2, x1],+ [-x3, x3, x4],+ [ x2,-x2,-x1],+ [ x3,-x3,-x4],+ [ x2,-x2, x1],+ [ x3,-x3, x4],+ [ x2, x2,-x1],+ [ x3, x3,-x4],+ [ x2, x2, x1],+ [ x3, x3, x4],+ [-x6,-x6,-x6],+ [-x5,-x5,-x5],+ [-x6,-x6, x6],+ [-x5,-x5, x5],+ [-x6, x6,-x6],+ [-x5, x5,-x5],+ [-x6, x6, x6],+ [-x5, x5, x5],+ [ x6,-x6,-x6],+ [ x5,-x5,-x5],+ [ x6,-x6, x6],+ [ x5,-x5, x5],+ [ x6, x6,-x6],+ [ x5, x5,-x5],+ [ x6, x6, x6],+ [ x5, x5, x5]]+ where+ a = 1 + sqrt 2+ x1 = 2 / (sqrt(1 + 3*a*a) + a) -- 0.29+ x2 = 2*a / (sqrt(1 + 3*a*a) + a) -- 0.71+ x3 = 2*a / (sqrt(1 + 3*a*a) - a) -- 2.56+ x4 = 2 / (sqrt(1 + 3*a*a) - a) -- 1.06+ x5 = 2*a / (sqrt(1 + 3*a*a) - 1) -- 1.46+ x6 = 2*a / (sqrt(1 + 3*a*a) + 1) -- 0.91++rectifiedTesseract :: [[Double]]+rectifiedTesseract =+ let x = sqrt 2 in+ [[0, j*x, k*x, l*x] | j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i*x, 0, k*x, l*x] | i <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i*x, j*x, 0, l*x] | i <- [-1,1], j <- [-1,1], l <- [-1,1]] +++ [[i*x, j*x, k*x, 0] | i <- [-1,1], j <- [-1,1], k <- [-1,1]]++cantellatedTesseract :: [[Double]]+cantellatedTesseract =+ let x = 1 + sqrt 2 in+ map (map (/ sqrt(2 + 2*x*x))) $+ [[i, j, k*x, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i*x, j, k, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i*x, j*x, k, l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i, j*x, k, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i, j*x, k*x, l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] +++ [[i*x, j, k*x, l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]]++projectedCantellatedTesseract :: [[Double]]+projectedCantellatedTesseract = map stereographic cantellatedTesseract+ where+ stereographic x = map (/(1-x!!3)) [2 * x!!0, 2 * x!!1, 2 * x!!2]++octaplex :: [[Double]]+octaplex = map (map (/ sqrt 2)) $+ [[i,j,0,0] | i <- pm, j <- pm] +++ [[i,0,j,0] | i <- pm, j <- pm] +++ [[i,0,0,j] | i <- pm, j <- pm] +++ [[0,0,i,j] | i <- pm, j <- pm] +++ [[0,i,0,j] | i <- pm, j <- pm] +++ [[0,i,j,0] | i <- pm, j <- pm]+ where+ pm = [-1,1]+++nonConvexPolyhedron :: [[Double]]+nonConvexPolyhedron =+ [[i*x, j*x, k*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1]] +++ [[i*y, 0, 0] | i <- [-1,1]] +++ [[0, j*y, 0] | j <- [-1,1]] +++ [[0, 0, k*y] | k <- [-1,1]]+ where x = 2.1806973249+ y = 3.5617820682+ -- tetrahedra: (i 2.18, j 2.18, k 2.18), (i 3.56, 0, 0), (0, j 3.56, 0), (0, 0, k 3.56)++dodecahedron :: [[Double]] -- it's icosahedron !+dodecahedron = let phi = (1 + sqrt 5)/2 in+ [[0,i,j] | i <- [-1,1], j <- [-phi, phi]] +++ [[j,0,i] | i <- [-1,1], j <- [-phi, phi]] +++ [[i,j,0] | i <- [-1,1], j <- [-phi, phi]]++icosahedron :: [[Double]]+icosahedron = dodecahedron++duoprism330 :: [[Double]]+duoprism330 = [a ++ b | a <- triangle, b <- p30]+ where+ triangle = [[cos (realToFrac i * 2*pi/3), sin (realToFrac i * 2*pi/3)] | i <- [0 .. 2]]+ p30 = [[cos (realToFrac i * 2*pi/30), sin (realToFrac i * 2*pi/30)] | i <- [0 .. 29]]+++duoprism35 :: [[Double]]+duoprism35 = [a ++ b | a <- triangle, b <- pentagon]+ where+ triangle = [ [sqrt 3 / 2 , 0.5]+ , [-sqrt 3 / 2, 0.5]+ , [0 , -1 ] ]+ pentagon = [ [1 , 0 ]+ , [cos(2*pi/5), sin(2*pi/5)]+ , [cos(4*pi/5), sin(4*pi/5)]+ , [cos(6*pi/5), sin(6*pi/5)]+ , [cos(8*pi/5), sin(8*pi/5)] ]++duoprism34 :: [[Double]]+duoprism34 = [a ++ b | a <- triangle, b <- square]+ where+ triangle = [ [sqrt 3 / 2 , 0.5]+ , [-sqrt 3 / 2, 0.5]+ , [0 , -1 ] ]+ square = [[i * sqrt 2 / 2, j * sqrt 2 / 2] | i <- [-1,1], j <- [-1,1]]++triangularDuoprism :: [[Double]]+triangularDuoprism = [a ++ b | a <- triangle, b <- triangle]+ where+ triangle = [ [sqrt 3 / 2 , 0.5]+ , [-sqrt 3 / 2, 0.5]+ , [0 , -1 ] ]++hexagonalDuoprism :: [[Double]]+hexagonalDuoprism = [a ++ b | a <- hexagon, b <- hexagon]+ where+ hexagon = [ [sqrt 3 / 2, 0.5]+ , [0 , 1 ]+ , [-sqrt 3 / 2, 0.5]+ , [-sqrt 3 / 2, -0.5]+ , [0 , -1 ]+ , [sqrt 3 / 2 , -0.5] ]++projectedHexagonalDuoprims :: [[Double]]+projectedHexagonalDuoprims =+ [ [ 1.8945800837529239 , 1.0938363213560542 , 1.8945800837529239 ]+ , [ 4.181540550352056 , 2.414213562373096 , 0.0 ]+ , [ 1.8945800837529239 , 1.0938363213560542 , -1.8945800837529239 ]+ , [ 0.904836765142137 , 0.522407749927483 , -0.904836765142137 ]+ , [ 0.7174389352143009 , 0.4142135623730951 , 0.0 ]+ , [ 0.904836765142137 , 0.522407749927483 , 0.904836765142137 ]+ , [ 0.0 , 2.1876726427121085 , 1.8945800837529239 ]+ , [ 0.0 , 4.828427124746192 , 0.0 ]+ , [ 0.0 , 2.1876726427121085 , -1.8945800837529239 ]+ , [ 0.0 , 1.044815499854966 , -0.904836765142137 ]+ , [ 0.0 , 0.8284271247461902 , 0.0 ]+ , [ 0.0 , 1.044815499854966 , 0.904836765142137 ]+ , [ -1.8945800837529239 , 1.0938363213560542 , 1.8945800837529239 ]+ , [ -4.181540550352056 , 2.414213562373096 , 0.0 ]+ , [ -1.8945800837529239+ , 1.0938363213560542+ , -1.8945800837529239+ ]+ , [ -0.904836765142137 , 0.522407749927483 , -0.904836765142137 ]+ , [ -0.7174389352143009 , 0.4142135623730951 , 0.0 ]+ , [ -0.904836765142137 , 0.522407749927483 , 0.904836765142137 ]+ , [ -1.8945800837529239+ , -1.0938363213560542+ , 1.8945800837529239+ ]+ , [ -4.181540550352056 , -2.414213562373096 , 0.0 ]+ , [ -1.8945800837529239+ , -1.0938363213560542+ , -1.8945800837529239+ ]+ , [ -0.904836765142137 , -0.522407749927483 , -0.904836765142137 ]+ , [ -0.7174389352143009 , -0.4142135623730951 , 0.0 ]+ , [ -0.904836765142137 , -0.522407749927483 , 0.904836765142137 ]+ , [ 0.0 , -2.1876726427121085 , 1.8945800837529239 ]+ , [ 0.0 , -4.828427124746192 , 0.0 ]+ , [ 0.0 , -2.1876726427121085 , -1.8945800837529239 ]+ , [ 0.0 , -1.044815499854966 , -0.904836765142137 ]+ , [ 0.0 , -0.8284271247461902 , 0.0 ]+ , [ 0.0 , -1.044815499854966 , 0.904836765142137 ]+ , [ 1.8945800837529239 , -1.0938363213560542 , 1.8945800837529239 ]+ , [ 4.181540550352056 , -2.414213562373096 , 0.0 ]+ , [ 1.8945800837529239+ , -1.0938363213560542+ , -1.8945800837529239+ ]+ , [ 0.904836765142137 , -0.522407749927483 , -0.904836765142137 ]+ , [ 0.7174389352143009 , -0.4142135623730951 , 0.0 ]+ , [ 0.904836765142137 , -0.522407749927483 , 0.904836765142137 ]+ ]++hexaSquare :: [[Double]]+hexaSquare = [a ++ b | a <- hexagon, b <- square]+ where+ hexagon = [ [sqrt 3 / 2, 0.5]+ , [0 , 1 ]+ , [-sqrt 3 / 2, 0.5]+ , [-sqrt 3 / 2, -0.5]+ , [0 , -1 ]+ , [sqrt 3 / 2 , -0.5] ]+ square = [[i * sqrt 2 / 2, j * sqrt 2 / 2] | i <- [-1,1], j <- [-1,1]]++cubinder :: [[Double]]+cubinder = [a ++ b | a <- circle, b <- square]+ where+ circle = [[cos (realToFrac i * 2*pi/30), sin (realToFrac i * 2*pi/30)] | i <- [0 .. 29]]+ square = [[i * sqrt 2 / 2, j * sqrt 2 / 2] | i <- [-1,1], j <- [-1,1]]++duoprism1616 :: [[Double]]+duoprism1616 = [a ++ b | a <- p16, b <- p16]+ where+ p16 = [[cos (realToFrac i * 2*pi/16), sin (realToFrac i * 2*pi/16)] | i <- [0 .. 15]]++duocylinder :: [[Double]]+duocylinder = [a ++ b | a <- circle, b <- circle]+ where+ circle = [[cos (realToFrac i * 2*pi/30), sin (realToFrac i * 2*pi/30)] | i <- [0 .. 29]]++reuleuxTetrahedron :: [[Double]]+reuleuxTetrahedron =+ [ [0.5 / sqrt 3, -0.5, 0.5 / sqrt 6]+ , [sqrt 3 / 3, 0, -0.5 / sqrt 6]+ , [0.5 / sqrt 3, 0.5, -0.5 / sqrt 6]+ , [0, 0, 0.5 * sqrt 3 / sqrt 2] ]+++hexacosichoron :: [[Double]]+hexacosichoron =+ [[i*0.5, j*0.5, k*0.5, l*0.5] | i <- pm, j <- pm, k <- pm, l <-pm] +++ [[0, 0, 0, i] | i <- pm] +++ [[0, 0, i, 0] | i <- pm] +++ [[0, i, 0, 0] | i <- pm] +++ [[i, 0, 0, 0] | i <- pm] +++ [ permuteList p [phi/2, 1/2, 1/2/phi, 0] | p <- permutations4, isEvenPermutation p] +++ [ permuteList p [phi/2, 1/2, -1/2/phi, 0] | p <- permutations4, isEvenPermutation p] +++ [ permuteList p [phi/2, -1/2, 1/2/phi, 0] | p <- permutations4, isEvenPermutation p] +++ [ permuteList p [phi/2, -1/2, -1/2/phi, 0] | p <- permutations4, isEvenPermutation p] +++ [ permuteList p [-phi/2, 1/2, 1/2/phi, 0] | p <- permutations4, isEvenPermutation p] +++ [ permuteList p [-phi/2, 1/2, -1/2/phi, 0] | p <- permutations4, isEvenPermutation p] +++ [ permuteList p [-phi/2, -1/2, 1/2/phi, 0] | p <- permutations4, isEvenPermutation p] +++ [ permuteList p [-phi/2, -1/2, -1/2/phi, 0] | p <- permutations4, isEvenPermutation p]+ where+ permutations4 = permutations 4+ phi = (1 + sqrt 5) / 2+ pm = [-1,1]++snub24cell :: [[Double]]+snub24cell =+ [ permuteList p [0, i, i'*phi, i''*phi*phi] | i <- pm, i' <- pm, i'' <- pm, p <- permutations4, isEvenPermutation p]+ where+ permutations4 = permutations 4+ phi = (1 + sqrt 5) / 2+ pm = [-1,1]++dodecaplex :: [[Double]]+dodecaplex = nub $+ [permuteList p [0.0, 0.0, i, j] | i <- pm2 , j <- pm2, p <- perms4] +++ [permuteList p [i, j, k, l] | i <- pm, j <- pm, k <-pm, l <- pmsqrt5, p <- perms4] +++ [permuteList p [i ,j ,k ,l] | i <- pmphipowminus2, j <- pmphi, k <- pmphi, l <- pmphi, p <- perms4] +++ [permuteList p [i ,j ,k ,l] | i <- pmphipowminus1, j <- pmphipowminus1, k <- pmphipowminus1, l <- pmphipow2, p <- perms4] +++ [permuteList p [0, i, j, k] | i <- pmphipowminus2, j <- pm, k <- pmphipow2, p <- perms4, isEvenPermutation p] +++ [permuteList p [0, i, j, k] | i <- pmphipowminus1, j <- pmphi, k <- pmsqrt5, p <- perms4, isEvenPermutation p] +++ [permuteList p [i, j, k, l] | i <- pmphipowminus1, j <- pm, k <- pmphi, l <- pm2, p <- perms4, isEvenPermutation p]+ where+ pm = [-1.0,1.0]+ pm2 = [-2, 2]+ perms4 = permutations 4+ pmsqrt5 = [-sqrt 5, sqrt 5]+ pmphipowminus2 = [-1/phi/phi, 1/phi/phi]+ pmphipow2 = [-phi*phi, phi*phi]+ phi = (1 + sqrt 5) / 2+-- pmphiminus1 = phi-1+ pmphi = [phi, -phi]+ pmphipowminus1 = [-1/phi, 1/phi]++spheresPack :: [[Double]]+spheresPack = [ [2,1,1], [4,1,1]+ , [1, 1 + sqrt 3, 1], [3, 1 + sqrt 3, 1], [5, 1 + sqrt 3, 1]+ , [2, 1 + 2*sqrt 3, 1], [4, 1 + 2*sqrt 3, 1]+ , [3, 1 + sqrt 3 / 3, 1 + 2*sqrt 6 / 3]+ , [2, 1 + 4*sqrt 3 / 3, 1 + 2*sqrt 6 / 3], [4, 1 + 4*sqrt 3 / 3, 1 + 2*sqrt 6 / 3]+ , [3, 1 + sqrt 3, 1 + 4*sqrt 6 /3]+ , [3, 1 + sqrt 3 / 3, 1 - 2*sqrt 6 / 3]+ , [2, 1 + 4*sqrt 3 / 3, 1 - 2*sqrt 6 / 3], [4, 1 + 4*sqrt 3 / 3, 1 - 2*sqrt 6 / 3]+ , [3, 1 + sqrt 3, 1 - 4*sqrt 6 /3] ]+++randomInCircle :: Int -> IO [[Double]]+randomInCircle n = do+ g1 <- newStdGen+ let theta = map (*(2*pi)) (take n (randoms g1 :: [Double]))+ g2 <- newStdGen+ let rho = map (/2) (take n (randoms g2 :: [Double]))+ return $ zipWith (\r a -> [(r+0.5) * cos a, (r+0.5) * sin a]) rho theta++randomInSquare :: Int -> IO [[Double]]+randomInSquare n = do+ g <- newStdGen+ return $ chunksOf 2 (take (2*n) (randoms g :: [Double]))+++randomInSphere :: Int -> IO [[Double]]+randomInSphere n = do+ g1 <- newStdGen+ let theta = map (*(2*pi)) (take n (randoms g1 :: [Double]))+ g2 <- newStdGen+ let phi = map (*pi) (take n (randoms g2 :: [Double]))+ g3 <- newStdGen+ let rho = take n (randoms g3 :: [Double])+ return $ zipWith3 (\r a b -> [r * cos a * sin b,+ r * sin a * sin b,+ r * cos b ])+ rho theta phi++regularSphere :: Int -> [[Double]]+regularSphere n =+ concatMap (\a -> map (s2c a) phi) theta+ where+ theta = map (*(2*pi)) [frac i n | i <- [0 .. n-1]]+ phi = map (*pi) [frac i n | i <- [1 .. n-1]]+ frac :: Int -> Int -> Double+ frac p q = realToFrac p / realToFrac q+ s2c :: Double -> Double -> [Double]+ s2c th ph = [cos th * sin ph, sin th * sin ph, cos ph]++randomOnSphere :: Int -> Double -> IO [[Double]]+randomOnSphere n r = do+ g <- newStdGen+ let x = take (2*n) (randoms g :: [Double])+ let u_ = map (*(2*pi)) (take n x)+ let v_ = drop n x+ return $ zipWith (\u v -> [r * cos u * sin (acos (2*v-1)),+ r * sin u * sin (acos (2*v-1)),+ r * (2*v-1) ]) u_ v_++randomInCube :: Int -> IO [[Double]]+randomInCube n = do+ g <- newStdGen+ return $ chunksOf 3 (take (3*n) (randoms g :: [Double]))++randomOnTorus :: Int -> Double -> Double -> IO [[Double]]+randomOnTorus n c a = do+ g <- newStdGen+ let x = take (2*n) (randoms g :: [Double])+ let u_ = map (*(2*pi)) (take n x)+ let v_ = map (*(2*pi)) (drop n x)+ return $ zipWith (\u v -> [cos u * (c + a * cos v),+ sin u * (c + a * cos v),+ a * sin v ]) u_ v_++qcube1,qcube2,qcube3,qcube4,qcube5 :: [[Double]]+qcube1 =+ [ [ 0.0 , 0.6180339887498949 , 1.618033988749895 ]+ , [ -0.6180339887498949 , 1.618033988749895 , 0.0 ]+ , [ 1.618033988749895 , 0.0 , 0.6180339887498949 ]+ , [ 1.0 , 1.0 , -1.0 ]+ , [ 0.6180339887498949 , -1.618033988749895 , 0.0 ]+ , [ -1.0 , -1.0 , 1.0 ]+ , [ -1.618033988749895 , 0.0 , -0.6180339887498949 ]+ , [ 0.0 , -0.6180339887498949 , -1.618033988749895 ]+ ]+qcube2 =+ [ [ -1.0 , 1.0 , 1.0 ]+ , [ 0.6180339887498949 , 1.618033988749895 , 0.0 ]+ , [ 0.0 , 0.6180339887498949 , -1.618033988749895 ]+ , [ 0.0 , -0.6180339887498949 , 1.618033988749895 ]+ , [ 1.618033988749895 , 0.0 , 0.6180339887498949 ]+ , [ 1.0 , -1.0 , -1.0 ]+ , [ -1.618033988749895 , 0.0 , -0.6180339887498949 ]+ , [ -0.6180339887498949 , -1.618033988749895 , 0.0 ]+ ]+qcube3 =+ [ [ -0.6180339887498949 , 1.618033988749895 , 0.0 ]+ , [ 0.0 , 0.6180339887498949 , -1.618033988749895 ]+ , [ 0.0 , -0.6180339887498949 , 1.618033988749895 ]+ , [ 1.0 , 1.0 , 1.0 ]+ , [ 1.618033988749895 , 0.0 , -0.6180339887498949 ]+ , [ 0.6180339887498949 , -1.618033988749895 , 0.0 ]+ , [ -1.618033988749895 , 0.0 , 0.6180339887498949 ]+ , [ -1.0 , -1.0 , -1.0 ]+ ]+qcube4 =+ [ [ -1.0 , 1.0 , 1.0 ]+ , [ 1.0 , -1.0 , 1.0 ]+ , [ 1.0 , 1.0 , 1.0 ]+ , [ 1.0 , 1.0 , -1.0 ]+ , [ 1.0 , -1.0 , -1.0 ]+ , [ -1.0 , -1.0 , 1.0 ]+ , [ -1.0 , 1.0 , -1.0 ]+ , [ -1.0 , -1.0 , -1.0 ]+ ]+qcube5 =+ [ [ 0.0 , 0.6180339887498949 , 1.618033988749895 ]+ , [ 0.6180339887498949 , 1.618033988749895 , 0.0 ]+ , [ 1.0 , -1.0 , 1.0 ]+ , [ 1.618033988749895 , 0.0 , -0.6180339887498949 ]+ , [ -1.618033988749895 , 0.0 , 0.6180339887498949 ]+ , [ -1.0 , 1.0 , -1.0 ]+ , [ -0.6180339887498949 , -1.618033988749895 , 0.0 ]+ , [ 0.0 , -0.6180339887498949 , -1.618033988749895 ]+ ]++teapot :: [[Double]]+teapot =+ [ [-3, 1.64999997615814, 0]+ , [-2.98710989952087, 1.64999997615814, -0.0984380021691322]+ , [-2.98710989952087, 1.64999997615814, 0.0984380021691322]+ , [-2.98537993431091, 1.56731998920441, -0.0492190010845661]+ , [-2.98537993431091, 1.56731998920441, 0.0492190010845661]+ , [-2.9835000038147, 1.48308002948761, 0]+ , [-2.98188996315002, 1.72346997261047, -0.0492190010845661]+ , [-2.98188996315002, 1.72346997261047, 0.0492190010845661]+ , [-2.97656011581421, 1.79852998256683, 0]+ , [-2.97090005874634, 1.48620998859406, -0.0984380021691322]+ , [-2.97090005874634, 1.48620998859406, 0.0984380021691322]+ , [-2.96388006210327, 1.79533994197845, -0.0984380021691322]+ , [-2.96388006210327, 1.79533994197845, 0.0984380021691322]+ , [-2.96220993995667, 1.57017004489899, -0.133594006299973]+ , [-2.96220993995667, 1.57017004489899, 0.133594006299973]+ , [-2.95864009857178, 1.72056996822357, -0.133594006299973]+ , [-2.95864009857178, 1.72056996822357, 0.133594006299973]+ , [-2.95313000679016, 1.64999997615814, -0.168750002980232]+ , [-2.95313000679016, 1.64999997615814, 0.168750002980232]+ , [-2.95247006416321, 1.40374004840851, -0.0492190010845661]+ , [-2.95247006416321, 1.40374004840851, 0.0492190010845661]+ , [-2.93770003318787, 1.49447000026703, -0.168750002980232]+ , [-2.93770003318787, 1.49447000026703, 0.168750002980232]+ , [-2.93523001670837, 1.85214996337891, -0.0492190010845661]+ , [-2.93523001670837, 1.85214996337891, 0.0492190010845661]+ , [-2.93358993530273, 1.32011997699738, 0]+ , [-2.93044996261597, 1.78692996501923, -0.168750002980232]+ , [-2.93044996261597, 1.78692996501923, 0.168750002980232]+ , [-2.93037009239197, 1.41149997711182, -0.133594006299973]+ , [-2.93037009239197, 1.41149997711182, 0.133594006299973]+ , [-2.92188000679016, 1.32553005218506, -0.0984380021691322]+ , [-2.92188000679016, 1.32553005218506, 0.0984380021691322]+ , [-2.91278004646301, 1.84416997432709, -0.133594006299973]+ , [-2.91278004646301, 1.84416997432709, 0.133594006299973]+ , [-2.90625, 1.91015994548798, 0]+ , [-2.89422988891602, 1.90456998348236, -0.0984380021691322]+ , [-2.89422988891602, 1.90456998348236, 0.0984380021691322]+ , [-2.89138007164001, 1.57910001277924, -0.196875005960464]+ , [-2.89138007164001, 1.57910001277924, 0.196875005960464]+ , [-2.8909900188446, 1.33980000019073, -0.168750002980232]+ , [-2.8909900188446, 1.33980000019073, 0.168750002980232]+ , [-2.89065003395081, 1.71208000183105, -0.196875005960464]+ , [-2.89065003395081, 1.71208000183105, 0.196875005960464]+ , [-2.88346004486084, 1.24579000473022, -0.0483429990708828]+ , [-2.88346004486084, 1.24579000473022, 0.0483429990708828]+ , [-2.86346006393433, 1.25713002681732, -0.132717996835709]+ , [-2.86346006393433, 1.25713002681732, 0.132717996835709]+ , [-2.86265993118286, 1.43482995033264, -0.196875005960464]+ , [-2.86265993118286, 1.43482995033264, 0.196875005960464]+ , [-2.8625500202179, 1.88982999324799, -0.168750002980232]+ , [-2.8625500202179, 1.88982999324799, 0.168750002980232]+ , [-2.84999990463257, 1.64999997615814, -0.224999994039536]+ , [-2.84999990463257, 1.64999997615814, 0.224999994039536]+ , [-2.84970998764038, 1.16155004501343, 0]+ , [-2.84710001945496, 1.82081997394562, -0.196875005960464]+ , [-2.84710001945496, 1.82081997394562, 0.196875005960464]+ , [-2.84193992614746, 1.94692003726959, -0.0492190010845661]+ , [-2.84193992614746, 1.94692003726959, 0.0492190010845661]+ , [-2.8289999961853, 1.76139998435974, -0.224999994039536]+ , [-2.8289999961853, 1.76139998435974, 0.224999994039536]+ , [-2.82867002487183, 1.17597997188568, -0.0949330031871796]+ , [-2.82867002487183, 1.17597997188568, 0.0949330031871796]+ , [-2.82470011711121, 1.52193999290466, -0.224999994039536]+ , [-2.82470011711121, 1.52193999290466, 0.224999994039536]+ , [-2.82115006446838, 1.93519997596741, -0.133594006299973]+ , [-2.82115006446838, 1.93519997596741, 0.133594006299973]+ , [-2.81230998039246, 1.18719005584717, -0.168750002980232]+ , [-2.81230998039246, 1.18719005584717, 0.168750002980232]+ , [-2.80501008033752, 1.28997004032135, -0.196875005960464]+ , [-2.80501008033752, 1.28997004032135, 0.196875005960464]+ , [-2.79727005958557, 1.38311004638672, -0.224999994039536]+ , [-2.79727005958557, 1.38311004638672, 0.224999994039536]+ , [-2.78906011581421, 1.99013996124268, 0]+ , [-2.78836011886597, 1.69931995868683, -0.196875005960464]+ , [-2.78836011886597, 1.69931995868683, 0.196875005960464]+ , [-2.77820992469788, 1.98283004760742, -0.0984380021691322]+ , [-2.77820992469788, 1.98283004760742, 0.0984380021691322]+ , [-2.77442002296448, 1.52737998962402, -0.196875005960464]+ , [-2.77442002296448, 1.52737998962402, 0.196875005960464]+ , [-2.77356004714966, 1.09860002994537, -0.0843750014901161]+ , [-2.77356004714966, 1.09860002994537, 0.0843750014901161]+ , [-2.76641011238098, 1.84511995315552, -0.224999994039536]+ , [-2.76641011238098, 1.84511995315552, 0.224999994039536]+ , [-2.76033997535706, 1.90090000629425, -0.196875005960464]+ , [-2.76033997535706, 1.90090000629425, 0.196875005960464]+ , [-2.74959993362427, 1.96355998516083, -0.168750002980232]+ , [-2.74959993362427, 1.96355998516083, 0.168750002980232]+ , [-2.74831008911133, 1.78569996356964, -0.196875005960464]+ , [-2.74831008911133, 1.78569996356964, 0.196875005960464]+ , [-2.74688005447388, 1.64999997615814, -0.168750002980232]+ , [-2.74688005447388, 1.64999997615814, 0.168750002980232]+ , [-2.73125004768372, 1.00780999660492, 0]+ , [-2.72756004333496, 1.73587000370026, -0.168750002980232]+ , [-2.72756004333496, 1.73587000370026, 0.168750002980232]+ , [-2.72036004066467, 1.69082999229431, -0.133594006299973]+ , [-2.72036004066467, 1.69082999229431, 0.133594006299973]+ , [-2.71948003768921, 1.24977004528046, -0.224999994039536]+ , [-2.71948003768921, 1.24977004528046, 0.224999994039536]+ , [-2.71677994728088, 1.14468002319336, -0.196875005960464]+ , [-2.71677994728088, 1.14468002319336, 0.196875005960464]+ , [-2.71288990974426, 1.64999997615814, -0.0984380021691322]+ , [-2.71288990974426, 1.64999997615814, 0.0984380021691322]+ , [-2.7089900970459, 1.54176998138428, -0.133594006299973]+ , [-2.7089900970459, 1.54176998138428, 0.133594006299973]+ , [-2.70354008674622, 1.42640995979309, -0.168750002980232]+ , [-2.70354008674622, 1.42640995979309, 0.168750002980232]+ , [-2.70097994804382, 1.03784000873566, -0.168750002980232]+ , [-2.70097994804382, 1.03784000873566, 0.168750002980232]+ , [-2.70000004768372, 1.64999997615814, 0]+ , [-2.69965004920959, 2.0107901096344, -0.0483460016548634]+ , [-2.69965004920959, 2.0107901096344, 0.0483460016548634]+ , [-2.69711995124817, 1.68792998790741, -0.0492190010845661]+ , [-2.69711995124817, 1.68792998790741, 0.0492190010845661]+ , [-2.69412994384766, 1.72746002674103, -0.0984380021691322]+ , [-2.69412994384766, 1.72746002674103, 0.0984380021691322]+ , [-2.68661999702454, 1.54668998718262, -0.0492190010845661]+ , [-2.68661999702454, 1.54668998718262, 0.0492190010845661]+ , [-2.68263006210327, 1.76234996318817, -0.133594006299973]+ , [-2.68263006210327, 1.76234996318817, 0.133594006299973]+ , [-2.68147993087769, 1.9964599609375, -0.13272100687027]+ , [-2.68147993087769, 1.9964599609375, 0.13272100687027]+ , [-2.68144011497498, 1.72426998615265, 0]+ , [-2.67574000358582, 1.27084994316101, -0.196875005960464]+ , [-2.67574000358582, 1.27084994316101, 0.196875005960464]+ , [-2.67265009880066, 1.44068002700806, -0.0984380021691322]+ , [-2.67265009880066, 1.44068002700806, 0.0984380021691322]+ , [-2.67025995254517, 1.80040001869202, -0.168750002980232]+ , [-2.67025995254517, 1.80040001869202, 0.168750002980232]+ , [-2.667799949646, 1.84623003005981, -0.196875005960464]+ , [-2.667799949646, 1.84623003005981, 0.196875005960464]+ , [-2.66279006004333, 1.9050999879837, -0.224999994039536]+ , [-2.66279006004333, 1.9050999879837, 0.224999994039536]+ , [-2.66093993186951, 1.44608998298645, 0]+ , [-2.66018009185791, 1.75436997413635, -0.0492190010845661]+ , [-2.66018009185791, 1.75436997413635, 0.0492190010845661]+ , [-2.63858008384705, 1.78567004203796, -0.0984380021691322]+ , [-2.63858008384705, 1.78567004203796, 0.0984380021691322]+ , [-2.63438010215759, 1.10390996932983, -0.224999994039536]+ , [-2.63438010215759, 1.10390996932983, 0.224999994039536]+ , [-2.63073992729187, 1.95674002170563, -0.196875005960464]+ , [-2.63073992729187, 1.95674002170563, 0.196875005960464]+ , [-2.62655997276306, 1.78007996082306, 0]+ , [-2.625, 2.04375004768372, 0]+ , [-2.62463998794556, 1.30501997470856, -0.132813006639481]+ , [-2.62463998794556, 1.30501997470856, 0.132813006639481]+ , [-2.60642004013062, 1.31745004653931, -0.0484380014240742]+ , [-2.60642004013062, 1.31745004653931, 0.0484380014240742]+ , [-2.60631990432739, 2.02643990516663, -0.0949449986219406]+ , [-2.60631990432739, 2.02643990516663, 0.0949449986219406]+ , [-2.59179997444153, 2.01298999786377, -0.168750002980232]+ , [-2.59179997444153, 2.01298999786377, 0.168750002980232]+ , [-2.57172989845276, 1.83429002761841, -0.168750002980232]+ , [-2.57172989845276, 1.83429002761841, 0.168750002980232]+ , [-2.56777000427246, 1.16997003555298, -0.168750002980232]+ , [-2.56777000427246, 1.16997003555298, 0.168750002980232]+ , [-2.55460000038147, 1.18304002285004, -0.0953150019049644]+ , [-2.55460000038147, 1.18304002285004, 0.0953150019049644]+ , [-2.54975008964539, 1.89058995246887, -0.196875005960464]+ , [-2.54975008964539, 1.89058995246887, 0.196875005960464]+ , [-2.5495400428772, 0.878983974456787, -0.0843750014901161]+ , [-2.5495400428772, 0.878983974456787, 0.0843750014901161]+ , [-2.5464301109314, 1.83196997642517, -0.13272100687027]+ , [-2.5464301109314, 1.83196997642517, 0.13272100687027]+ , [-2.53749990463257, 1.20000004768372, 0]+ , [-2.52720999717712, 1.81920003890991, -0.0483460016548634]+ , [-2.52720999717712, 1.81920003890991, 0.0483460016548634]+ , [-2.51874995231628, 1.94530999660492, -0.224999994039536]+ , [-2.51874995231628, 1.94530999660492, 0.224999994039536]+ , [-2.51682996749878, 0.932671010494232, -0.196875005960464]+ , [-2.51682996749878, 0.932671010494232, 0.196875005960464]+ , [-2.47183990478516, 1.00648999214172, -0.196875005960464]+ , [-2.47183990478516, 1.00648999214172, 0.196875005960464]+ , [-2.44569993019104, 1.87764000892639, -0.168750002980232]+ , [-2.44569993019104, 1.87764000892639, 0.168750002980232]+ , [-2.43913006782532, 1.06017994880676, -0.0843750014901161]+ , [-2.43913006782532, 1.06017994880676, 0.0843750014901161]+ , [-2.43118000030518, 1.86417996883392, -0.0949449986219406]+ , [-2.43118000030518, 1.86417996883392, 0.0949449986219406]+ , [-2.41249990463257, 1.84686994552612, 0]+ , [-2.38827991485596, 0.716602027416229, 0]+ , [-2.3822500705719, 0.737662971019745, -0.0958539992570877]+ , [-2.3822500705719, 0.737662971019745, 0.0958539992570877]+ , [-2.37883996963501, 2.05202007293701, -0.0843750014901161]+ , [-2.37883996963501, 2.05202007293701, 0.0843750014901161]+ , [-2.37766003608704, 0.753679990768433, -0.168750002980232]+ , [-2.37766003608704, 0.753679990768433, 0.168750002980232]+ , [-2.36474990844727, 0.798761010169983, -0.199836000800133]+ , [-2.36474990844727, 0.798761010169983, 0.199836000800133]+ , [-2.35430002212524, 0.835254013538361, -0.224999994039536]+ , [-2.35430002212524, 0.835254013538361, 0.224999994039536]+ , [-2.34383988380432, 0.871747016906738, -0.199836000800133]+ , [-2.34383988380432, 0.871747016906738, 0.199836000800133]+ , [-2.3411500453949, 1.99971997737885, -0.196875005960464]+ , [-2.3411500453949, 1.99971997737885, 0.196875005960464]+ , [-2.33092999458313, 0.916827023029327, -0.168750002980232]+ , [-2.33092999458313, 0.916827023029327, 0.168750002980232]+ , [-2.32031011581421, 0.953905999660492, 0]+ , [-2.28931999206543, 1.9278199672699, -0.196875005960464]+ , [-2.28931999206543, 1.9278199672699, 0.196875005960464]+ , [-2.251620054245, 1.87551999092102, -0.0843750014901161]+ , [-2.251620054245, 1.87551999092102, 0.0843750014901161]+ , [-2.24741005897522, 0.882284998893738, -0.0843750014901161]+ , [-2.24741005897522, 0.882284998893738, 0.0843750014901161]+ , [-2.17362999916077, 0.844043016433716, 0]+ , [-2.16852998733521, 0.826951026916504, -0.0971840023994446]+ , [-2.16852998733521, 0.826951026916504, 0.0971840023994446]+ , [-2.16476988792419, 0.814364016056061, -0.168750002980232]+ , [-2.16476988792419, 0.814364016056061, 0.168750002980232]+ , [-2.15687990188599, 0.78669399023056, -0.187068000435829]+ , [-2.15687990188599, 0.78669399023056, 0.187068000435829]+ , [-2.15625, 2.09296989440918, 0]+ , [-2.15411996841431, 0.740520000457764, -0.215193003416061]+ , [-2.15411996841431, 0.740520000457764, 0.215193003416061]+ , [-2.15017008781433, 0.69473397731781, -0.215193003416061]+ , [-2.15017008781433, 0.69473397731781, 0.215193003416061]+ , [-2.14741992950439, 0.648559987545013, -0.187068000435829]+ , [-2.14741992950439, 0.648559987545013, 0.187068000435829]+ , [-2.14495992660522, 0.6127769947052, -0.132947996258736]+ , [-2.14495992660522, 0.6127769947052, 0.132947996258736]+ , [-2.143709897995, 0.59178900718689, -0.0485729984939098]+ , [-2.143709897995, 0.59178900718689, 0.0485729984939098]+ , [-2.14232993125916, 2.05836009979248, -0.168750002980232]+ , [-2.14232993125916, 2.05836009979248, 0.168750002980232]+ , [-2.11172008514404, 1.98222994804382, -0.224999994039536]+ , [-2.11172008514404, 1.98222994804382, 0.224999994039536]+ , [-2.08447003364563, 0.789525985717773, -0.0489050000905991]+ , [-2.08447003364563, 0.789525985717773, 0.0489050000905991]+ , [-2.08109998703003, 1.90609002113342, -0.168750002980232]+ , [-2.08109998703003, 1.90609002113342, 0.168750002980232]+ , [-2.07834005355835, 0.77038699388504, -0.133279994130135]+ , [-2.07834005355835, 0.77038699388504, 0.133279994130135]+ , [-2.06718993186951, 1.87147998809814, 0]+ , [-2, 0.75, 0]+ , [-1.99570000171661, 0.737109005451202, -0.0984380021691322]+ , [-1.99570000171661, 0.737109005451202, 0.0984380021691322]+ , [-1.98438000679016, 0.703125, -0.168750002980232]+ , [-1.98438000679016, 0.703125, 0.168750002980232]+ , [-1.97852003574371, 0.591650009155273, 0]+ , [-1.96937000751495, 0.670825004577637, -0.202656000852585]+ , [-1.96937000751495, 0.670825004577637, 0.202656000852585]+ , [-1.96835994720459, 0.655077993869781, -0.210938006639481]+ , [-1.96835994720459, 0.655077993869781, 0.210938006639481]+ , [-1.96000003814697, 0.75, -0.407499998807907]+ , [-1.96000003814697, 0.75, 0.407499998807907]+ , [-1.9587299823761, 0.925194978713989, -0.201561003923416]+ , [-1.9587299823761, 0.925194978713989, 0.201561003923416]+ , [-1.9570300579071, 1.10038995742798, 0]+ , [-1.95000004768372, 0.600000023841858, -0.224999994039536]+ , [-1.95000004768372, 0.600000023841858, 0.224999994039536]+ , [-1.93894994258881, 0.591650009155273, -0.403122991323471]+ , [-1.93894994258881, 0.591650009155273, 0.403122991323471]+ , [-1.93164002895355, 0.54492199420929, -0.210938006639481]+ , [-1.93164002895355, 0.54492199420929, 0.210938006639481]+ , [-1.93069005012512, 0.5225830078125, -0.198676005005836]+ , [-1.93069005012512, 0.5225830078125, 0.198676005005836]+ , [-1.92188000679016, 0.453516006469727, 0]+ , [-1.91788995265961, 1.10038995742798, -0.398745000362396]+ , [-1.91788995265961, 1.10038995742798, 0.398745000362396]+ , [-1.91561996936798, 0.496874988079071, -0.168750002980232]+ , [-1.91561996936798, 0.496874988079071, 0.168750002980232]+ , [-1.90429997444153, 0.462891012430191, -0.0984380021691322]+ , [-1.90429997444153, 0.462891012430191, 0.0984380021691322]+ , [-1.89999997615814, 0.449999988079071, 0]+ , [-1.89227998256683, 0.670825004577637, -0.593047022819519]+ , [-1.89227998256683, 0.670825004577637, 0.593047022819519]+ , [-1.8834400177002, 0.453516006469727, -0.391582012176514]+ , [-1.8834400177002, 0.453516006469727, 0.391582012176514]+ , [-1.88206005096436, 0.925194978713989, -0.58984500169754]+ , [-1.88206005096436, 0.925194978713989, 0.58984500169754]+ , [-1.88138997554779, 1.28612995147705, -0.193601995706558]+ , [-1.88138997554779, 1.28612995147705, 0.193601995706558]+ , [-1.85511994361877, 0.5225830078125, -0.581402003765106]+ , [-1.85511994361877, 0.5225830078125, 0.581402003765106]+ , [-1.84500002861023, 0.75, -0.785000026226044]+ , [-1.84500002861023, 0.75, 0.785000026226044]+ , [-1.84375, 1.47186994552612, 0]+ , [-1.83317005634308, 1.89067995548248, -0.0843750014901161]+ , [-1.83317005634308, 1.89067995548248, 0.0843750014901161]+ , [-1.83179998397827, 1.94649004936218, -0.196875005960464]+ , [-1.83179998397827, 1.94649004936218, 0.196875005960464]+ , [-1.82992005348206, 2.02323007583618, -0.196875005960464]+ , [-1.82992005348206, 2.02323007583618, 0.196875005960464]+ , [-1.82854998111725, 2.07904005050659, -0.0843750014901161]+ , [-1.82854998111725, 2.07904005050659, 0.0843750014901161]+ , [-1.82518005371094, 0.591650009155273, -0.776566982269287]+ , [-1.82518005371094, 0.591650009155273, 0.776566982269287]+ , [-1.81757998466492, 0.343944996595383, -0.187035992741585]+ , [-1.81757998466492, 0.343944996595383, 0.187035992741585]+ , [-1.80774998664856, 1.28612995147705, -0.566554009914398]+ , [-1.80774998664856, 1.28612995147705, 0.566554009914398]+ , [-1.8068699836731, 1.47186994552612, -0.375663995742798]+ , [-1.8068699836731, 1.47186994552612, 0.375663995742798]+ , [-1.80535995960236, 1.10038995742798, -0.768135011196136]+ , [-1.80535995960236, 1.10038995742798, 0.768135011196136]+ , [-1.77293002605438, 0.453516006469727, -0.754335999488831]+ , [-1.77293002605438, 0.453516006469727, 0.754335999488831]+ , [-1.75, 0.234375, 0]+ , [-1.74644005298615, 0.343944996595383, -0.547339022159576]+ , [-1.74644005298615, 0.343944996595383, 0.547339022159576]+ , [-1.7443300485611, 0.670825004577637, -0.949871003627777]+ , [-1.7443300485611, 0.670825004577637, 0.949871003627777]+ , [-1.7349100112915, 0.925194978713989, -0.944741010665894]+ , [-1.7349100112915, 0.925194978713989, 0.944741010665894]+ , [-1.7150000333786, 0.234375, -0.356563001871109]+ , [-1.7150000333786, 0.234375, 0.356561988592148]+ , [-1.71008002758026, 0.5225830078125, -0.931218028068542]+ , [-1.71008002758026, 0.5225830078125, 0.931218028068542]+ , [-1.70086002349854, 1.47186994552612, -0.723671972751617]+ , [-1.70086002349854, 1.47186994552612, 0.723671972751617]+ , [-1.66639995574951, 1.28612995147705, -0.907437026500702]+ , [-1.66639995574951, 1.28612995147705, 0.907437026500702]+ , [-1.66250002384186, 0.75, -1.125]+ , [-1.66250002384186, 0.75, 1.125]+ , [-1.65515995025635, 1.86093997955322, -0.170322000980377]+ , [-1.65515995025635, 1.86093997955322, 0.170322000980377]+ , [-1.64742004871368, 0.159961000084877, -0.169525995850563]+ , [-1.64742004871368, 0.159961000084877, 0.169525995850563]+ , [-1.64463996887207, 0.591650009155273, -1.11292004585266]+ , [-1.64463996887207, 0.591650009155273, 1.11292004585266]+ , [-1.62678003311157, 1.10038995742798, -1.10082995891571]+ , [-1.62678003311157, 1.10038995742798, 1.10082995891571]+ , [-1.61436998844147, 0.234375, -0.686874985694885]+ , [-1.61436998844147, 0.234375, 0.686874985694885]+ , [-1.60988998413086, 0.343944996595383, -0.876659989356995]+ , [-1.60988998413086, 0.343944996595383, 0.876659989356995]+ , [-1.60000002384186, 1.875, 0]+ , [-1.59756004810333, 0.453516006469727, -1.08106005191803]+ , [-1.59756004810333, 0.453516006469727, 1.08106005191803]+ , [-1.59037005901337, 1.86093997955322, -0.498427987098694]+ , [-1.59037005901337, 1.86093997955322, 0.498427987098694]+ , [-1.58438003063202, 1.91015994548798, -0.168750002980232]+ , [-1.58438003063202, 1.91015994548798, 0.168750002980232]+ , [-1.58293998241425, 0.159961000084877, -0.49609899520874]+ , [-1.58293998241425, 0.159961000084877, 0.49609899520874]+ , [-1.57813000679016, 0.085547000169754, 0]+ , [-1.54999995231628, 1.98749995231628, -0.224999994039536]+ , [-1.54999995231628, 1.98749995231628, 0.224999994039536]+ , [-1.54656004905701, 0.085547000169754, -0.321543008089066]+ , [-1.54656004905701, 0.085547000169754, 0.321543008089066]+ , [-1.53296995162964, 0.670825004577637, -1.26566994190216]+ , [-1.53296995162964, 0.670825004577637, 1.26566994190216]+ , [-1.53261995315552, 1.47186994552612, -1.03710997104645]+ , [-1.53261995315552, 1.47186994552612, 1.03710997104645]+ , [-1.52469003200531, 0.925194978713989, -1.25882995128632]+ , [-1.52469003200531, 0.925194978713989, 1.25882995128632]+ , [-1.52366995811462, 0.042773000895977, -0.156791999936104]+ , [-1.52366995811462, 0.042773000895977, 0.156791999936104]+ , [-1.51563000679016, 2.06484007835388, -0.168750002980232]+ , [-1.51563000679016, 2.06484007835388, 0.168750002980232]+ , [-1.50286996364594, 0.5225830078125, -1.24081003665924]+ , [-1.50286996364594, 0.5225830078125, 1.24081003665924]+ , [-1.5, 0, 0]+ , [-1.5, 2.09999990463257, 0]+ , [-1.5, 2.25, 0]+ , [-1.47000002861023, 0, -0.30562499165535]+ , [-1.47000002861023, 0, 0.30562499165535]+ , [-1.47000002861023, 2.25, -0.30562499165535]+ , [-1.47000002861023, 2.25, 0.30562499165535]+ , [-1.46601998806, 1.86093997955322, -0.79831999540329]+ , [-1.46601998806, 1.86093997955322, 0.79831999540329]+ , [-1.4644900560379, 1.28612995147705, -1.20912003517151]+ , [-1.4644900560379, 1.28612995147705, 1.20912003517151]+ , [-1.46403002738953, 0.042773000895977, -0.458833009004593]+ , [-1.46403002738953, 0.042773000895977, 0.458833009004593]+ , [-1.45985996723175, 2.28691005706787, -0.15022599697113]+ , [-1.45985996723175, 2.28691005706787, 0.15022599697113]+ , [-1.45916998386383, 0.159961000084877, -0.794589996337891]+ , [-1.45916998386383, 0.159961000084877, 0.794589996337891]+ , [-1.45581996440887, 0.085547000169754, -0.61941397190094]+ , [-1.45581996440887, 0.085547000169754, 0.61941397190094]+ , [-1.45468997955322, 0.234375, -0.984375]+ , [-1.45468997955322, 0.234375, 0.984375]+ , [-1.4492199420929, 2.32382988929749, 0]+ , [-1.42023003101349, 2.32382988929749, -0.295278012752533]+ , [-1.42023003101349, 2.32382988929749, 0.295278012752533]+ , [-1.41999995708466, 0.75, -1.41999995708466]+ , [-1.41999995708466, 0.75, 1.41999995708466]+ , [-1.41481995582581, 0.343944996595383, -1.16812002658844]+ , [-1.41481995582581, 0.343944996595383, 1.16812002658844]+ , [-1.41191005706787, 2.33612990379333, -0.14529100060463]+ , [-1.41191005706787, 2.33612990379333, 0.14529100060463]+ , [-1.40474998950958, 0.591650009155273, -1.40474998950958]+ , [-1.40474998950958, 0.591650009155273, 1.40474998950958]+ , [-1.40313005447388, 2.34843993186951, 0]+ , [-1.40271997451782, 2.28691005706787, -0.439617991447449]+ , [-1.40271997451782, 2.28691005706787, 0.439617991447449]+ , [-1.39999997615814, 2.25, 0]+ , [-1.38949000835419, 1.10038995742798, -1.38949000835419]+ , [-1.38949000835419, 1.10038995742798, 1.38949000835419]+ , [-1.38374996185303, 0, -0.588750004768372]+ , [-1.38374996185303, 0, 0.588750004768372]+ , [-1.38374996185303, 2.25, -0.588750004768372]+ , [-1.38374996185303, 2.25, 0.588750004768372]+ , [-1.38047003746033, 2.32382988929749, 0]+ , [-1.37787997722626, 2.33612990379333, -0.141789004206657]+ , [-1.37787997722626, 2.33612990379333, 0.141789004206657]+ , [-1.37633001804352, 2.28691005706787, -0.141629993915558]+ , [-1.37633001804352, 2.28691005706787, 0.141629993915558]+ , [-1.37505996227264, 2.34843993186951, -0.285887002944946]+ , [-1.37505996227264, 2.34843993186951, 0.285887002944946]+ , [-1.37199997901917, 2.25, -0.285250008106232]+ , [-1.37199997901917, 2.25, 0.285250008106232]+ , [-1.36452996730804, 0.453516006469727, -1.36452996730804]+ , [-1.36452996730804, 0.453516006469727, 1.36452996730804]+ , [-1.35664999485016, 2.33612990379333, -0.425177007913589]+ , [-1.35664999485016, 2.33612990379333, 0.425177007913589]+ , [-1.35285997390747, 2.32382988929749, -0.281271010637283]+ , [-1.35285997390747, 2.32382988929749, 0.281271010637283]+ , [-1.34957003593445, 0.042773000895977, -0.734902024269104]+ , [-1.34957003593445, 0.042773000895977, 0.734902024269104]+ , [-1.33689999580383, 2.32382988929749, -0.568817973136902]+ , [-1.33689999580383, 2.32382988929749, 0.568817973136902]+ , [-1.32395005226135, 2.33612990379333, -0.414929002523422]+ , [-1.32395005226135, 2.33612990379333, 0.414929002523422]+ , [-1.32246005535126, 2.28691005706787, -0.414463996887207]+ , [-1.32246005535126, 2.28691005706787, 0.414463996887207]+ , [-1.3118200302124, 0.085547000169754, -0.887695014476776]+ , [-1.3118200302124, 0.085547000169754, 0.887695014476776]+ , [-1.30905997753143, 1.47186994552612, -1.30905997753143]+ , [-1.30905997753143, 1.47186994552612, 1.30905997753143]+ , [-1.29999995231628, 2.25, 0]+ , [-1.2943799495697, 2.34843993186951, -0.550727009773254]+ , [-1.2943799495697, 2.34843993186951, 0.550727009773254]+ , [-1.29305005073547, 2.28691005706787, -0.704126000404358]+ , [-1.29305005073547, 2.28691005706787, 0.704126000404358]+ , [-1.29149997234344, 2.25, -0.549499988555908]+ , [-1.29149997234344, 2.25, 0.549499988555908]+ , [-1.28839004039764, 1.86093997955322, -1.06373000144958]+ , [-1.28839004039764, 1.86093997955322, 1.06373000144958]+ , [-1.28236997127533, 0.159961000084877, -1.05876004695892]+ , [-1.28236997127533, 0.159961000084877, 1.05876004695892]+ , [-1.27400004863739, 2.25, -0.264874994754791]+ , [-1.27400004863739, 2.25, 0.264874994754791]+ , [-1.27348005771637, 2.32382988929749, -0.541833996772766]+ , [-1.27348005771637, 2.32382988929749, 0.541833996772766]+ , [-1.26766002178192, 2.27489995956421, -0.130447998642921]+ , [-1.26766002178192, 2.27489995956421, 0.130447998642921]+ , [-1.26566994190216, 0.670825004577637, -1.53296995162964]+ , [-1.26566994190216, 0.670825004577637, 1.53296995162964]+ , [-1.26093995571136, 2.29979991912842, 0]+ , [-1.25882995128632, 0.925194978713989, -1.52469003200531]+ , [-1.25882995128632, 0.925194978713989, 1.52469003200531]+ , [-1.25057005882263, 2.33612990379333, -0.680997014045715]+ , [-1.25057005882263, 2.33612990379333, 0.680997014045715]+ , [-1.24688005447388, 0, -0.84375]+ , [-1.24688005447388, 0, 0.84375]+ , [-1.24688005447388, 2.25, -0.84375]+ , [-1.24688005447388, 2.25, 0.84375]+ , [-1.24249994754791, 0.234375, -1.24249994754791]+ , [-1.24249994754791, 0.234375, 1.24249994754791]+ , [-1.24081003665924, 0.5225830078125, -1.50286996364594]+ , [-1.24081003665924, 0.5225830078125, 1.50286996364594]+ , [-1.235720038414, 2.29979991912842, -0.256915986537933]+ , [-1.235720038414, 2.29979991912842, 0.256915986537933]+ , [-1.22043001651764, 2.33612990379333, -0.664583027362823]+ , [-1.22043001651764, 2.33612990379333, 0.664583027362823]+ , [-1.21905994415283, 2.28691005706787, -0.663837015628815]+ , [-1.21905994415283, 2.28691005706787, 0.663837015628815]+ , [-1.21805000305176, 2.27489995956421, -0.381740003824234]+ , [-1.21805000305176, 2.27489995956421, 0.381740003824234]+ , [-1.20912003517151, 1.28612995147705, -1.4644900560379]+ , [-1.20912003517151, 1.28612995147705, 1.4644900560379]+ , [-1.20466005802155, 2.32382988929749, -0.815186023712158]+ , [-1.20466005802155, 2.32382988929749, 0.815186023712158]+ , [-1.19924998283386, 2.25, -0.510249972343445]+ , [-1.19924998283386, 2.25, 0.510249972343445]+ , [-1.19650995731354, 2.31943011283875, -0.123125001788139]+ , [-1.19650995731354, 2.31943011283875, 0.123125001788139]+ , [-1.18604004383087, 0.042773000895977, -0.979228973388672]+ , [-1.18604004383087, 0.042773000895977, 0.979228973388672]+ , [-1.16812002658844, 0.343944996595383, -1.41481995582581]+ , [-1.16812002658844, 0.343944996595383, 1.41481995582581]+ , [-1.16635000705719, 2.34843993186951, -0.789258003234863]+ , [-1.16635000705719, 2.34843993186951, 0.789258003234863]+ , [-1.16375005245209, 2.25, -0.787500023841858]+ , [-1.16375005245209, 2.25, 0.787500023841858]+ , [-1.16322004795074, 2.29979991912842, -0.494917988777161]+ , [-1.16322004795074, 2.29979991912842, 0.494917988777161]+ , [-1.15625, 2.33906006813049, 0]+ , [-1.14968001842499, 2.31943011283875, -0.360312014818192]+ , [-1.14968001842499, 2.31943011283875, 0.360312014818192]+ , [-1.14751994609833, 2.32382988929749, -0.776513993740082]+ , [-1.14751994609833, 2.32382988929749, 0.776513993740082]+ , [-1.13636994361877, 2.28691005706787, -0.938220024108887]+ , [-1.13636994361877, 2.28691005706787, 0.938220024108887]+ , [-1.13311994075775, 2.33906006813049, -0.235586002469063]+ , [-1.13311994075775, 2.33906006813049, 0.235586002469063]+ , [-1.125, 0.75, -1.66250002384186]+ , [-1.125, 0.75, 1.66250002384186]+ , [-1.12281000614166, 2.27489995956421, -0.611424028873444]+ , [-1.12281000614166, 2.27489995956421, 0.611424028873444]+ , [-1.12047004699707, 0.085547000169754, -1.12047004699707]+ , [-1.12047004699707, 0.085547000169754, 1.12047004699707]+ , [-1.11292004585266, 0.591650009155273, -1.64463996887207]+ , [-1.11292004585266, 0.591650009155273, 1.64463996887207]+ , [-1.10082995891571, 1.10038995742798, -1.62678003311157]+ , [-1.10082995891571, 1.10038995742798, 1.62678003311157]+ , [-1.09904003143311, 2.33612990379333, -0.907401978969574]+ , [-1.09904003143311, 2.33612990379333, 0.907401978969574]+ , [-1.08106005191803, 0.453516006469727, -1.59756004810333]+ , [-1.08106005191803, 0.453516006469727, 1.59756004810333]+ , [-1.08062994480133, 2.25, -0.731249988079071]+ , [-1.08062994480133, 2.25, 0.731249988079071]+ , [-1.07255005836487, 2.33612990379333, -0.885531008243561]+ , [-1.07255005836487, 2.33612990379333, 0.885531008243561]+ , [-1.07134997844696, 2.28691005706787, -0.884536981582642]+ , [-1.07134997844696, 2.28691005706787, 0.884536981582642]+ , [-1.06664001941681, 2.33906006813049, -0.453828006982803]+ , [-1.06664001941681, 2.33906006813049, 0.453828006982803]+ , [-1.06500005722046, 0, -1.06500005722046]+ , [-1.06500005722046, 0, 1.06500005722046]+ , [-1.06500005722046, 2.25, -1.06500005722046]+ , [-1.06500005722046, 2.25, 1.06500005722046]+ , [-1.06373000144958, 1.86093997955322, -1.28839004039764]+ , [-1.06373000144958, 1.86093997955322, 1.28839004039764]+ , [-1.05979001522064, 2.31943011283875, -0.577103972434998]+ , [-1.05979001522064, 2.31943011283875, 0.577103972434998]+ , [-1.05876004695892, 0.159961000084877, -1.28236997127533]+ , [-1.05876004695892, 0.159961000084877, 1.28236997127533]+ , [-1.04814994335175, 2.29979991912842, -0.709276974201202]+ , [-1.04814994335175, 2.29979991912842, 0.709276974201202]+ , [-1.03710997104645, 1.47186994552612, -1.53261995315552]+ , [-1.03710997104645, 1.47186994552612, 1.53261995315552]+ , [-1.02893996238708, 2.32382988929749, -1.02893996238708]+ , [-1.02893996238708, 2.32382988929749, 1.02893996238708]+ , [-0.996218979358673, 2.34843993186951, -0.996218979358673]+ , [-0.996218979358673, 2.34843993186951, 0.996218979358673]+ , [-0.994000017642975, 2.25, -0.994000017642975]+ , [-0.994000017642975, 2.25, 0.994000017642975]+ , [-0.986760973930359, 2.27489995956421, -0.814697980880737]+ , [-0.986760973930359, 2.27489995956421, 0.814697980880737]+ , [-0.984375, 0.234375, -1.45468997955322]+ , [-0.984375, 0.234375, 1.45468997955322]+ , [-0.980718970298767, 2.36952996253967, -0.100919999182224]+ , [-0.980718970298767, 2.36952996253967, 0.100919999182224]+ , [-0.98013299703598, 2.32382988929749, -0.98013299703598]+ , [-0.98013299703598, 2.32382988929749, 0.98013299703598]+ , [-0.979228973388672, 0.042773000895977, -1.18604004383087]+ , [-0.979228973388672, 0.042773000895977, 1.18604004383087]+ , [-0.961133003234863, 2.33906006813049, -0.650390982627869]+ , [-0.961133003234863, 2.33906006813049, 0.650390982627869]+ , [-0.949871003627777, 0.670825004577637, -1.7443300485611]+ , [-0.949871003627777, 0.670825004577637, 1.7443300485611]+ , [-0.944741010665894, 0.925194978713989, -1.7349100112915]+ , [-0.944741010665894, 0.925194978713989, 1.7349100112915]+ , [-0.942332029342651, 2.36952996253967, -0.295329988002777]+ , [-0.942332029342651, 2.36952996253967, 0.295329988002777]+ , [-0.938220024108887, 2.28691005706787, -1.13636994361877]+ , [-0.938220024108887, 2.28691005706787, 1.13636994361877]+ , [-0.931373000144958, 2.31943011283875, -0.768967986106873]+ , [-0.931373000144958, 2.31943011283875, 0.768967986106873]+ , [-0.931218028068542, 0.5225830078125, -1.71008002758026]+ , [-0.931218028068542, 0.5225830078125, 1.71008002758026]+ , [-0.922999978065491, 2.25, -0.922999978065491]+ , [-0.922999978065491, 2.25, 0.922999978065491]+ , [-0.907437026500702, 1.28612995147705, -1.66639995574951]+ , [-0.907437026500702, 1.28612995147705, 1.66639995574951]+ , [-0.907401978969574, 2.33612990379333, -1.09904003143311]+ , [-0.907401978969574, 2.33612990379333, 1.09904003143311]+ , [-0.895265996456146, 2.29979991912842, -0.895265996456146]+ , [-0.895265996456146, 2.29979991912842, 0.895265996456146]+ , [-0.887695014476776, 0.085547000169754, -1.3118200302124]+ , [-0.887695014476776, 0.085547000169754, 1.3118200302124]+ , [-0.885531008243561, 2.33612990379333, -1.07255005836487]+ , [-0.885531008243561, 2.33612990379333, 1.07255005836487]+ , [-0.884536981582642, 2.28691005706787, -1.07134997844696]+ , [-0.884536981582642, 2.28691005706787, 1.07134997844696]+ , [-0.876659989356995, 0.343944996595383, -1.60988998413086]+ , [-0.876659989356995, 0.343944996595383, 1.60988998413086]+ , [-0.868654012680054, 2.36952996253967, -0.473022997379303]+ , [-0.868654012680054, 2.36952996253967, 0.473022997379303]+ , [-0.84375, 0, -1.24688005447388]+ , [-0.84375, 0, 1.24688005447388]+ , [-0.84375, 2.25, -1.24688005447388]+ , [-0.84375, 2.25, 1.24688005447388]+ , [-0.824999988079071, 2.40000009536743, 0]+ , [-0.820937991142273, 2.33906006813049, -0.820937991142273]+ , [-0.820937991142273, 2.33906006813049, 0.820937991142273]+ , [-0.815186023712158, 2.32382988929749, -1.20466005802155]+ , [-0.815186023712158, 2.32382988929749, 1.20466005802155]+ , [-0.814697980880737, 2.27489995956421, -0.986760973930359]+ , [-0.814697980880737, 2.27489995956421, 0.986760973930359]+ , [-0.808499991893768, 2.40000009536743, -0.168093994259834]+ , [-0.808499991893768, 2.40000009536743, 0.168093994259834]+ , [-0.79831999540329, 1.86093997955322, -1.46601998806]+ , [-0.79831999540329, 1.86093997955322, 1.46601998806]+ , [-0.794589996337891, 0.159961000084877, -1.45916998386383]+ , [-0.794589996337891, 0.159961000084877, 1.45916998386383]+ , [-0.789258003234863, 2.34843993186951, -1.16635000705719]+ , [-0.789258003234863, 2.34843993186951, 1.16635000705719]+ , [-0.787500023841858, 2.25, -1.16375005245209]+ , [-0.787500023841858, 2.25, 1.16375005245209]+ , [-0.785000026226044, 0.75, -1.84500002861023]+ , [-0.785000026226044, 0.75, 1.84500002861023]+ , [-0.776566982269287, 0.591650009155273, -1.82518005371094]+ , [-0.776566982269287, 0.591650009155273, 1.82518005371094]+ , [-0.776513993740082, 2.32382988929749, -1.14751994609833]+ , [-0.776513993740082, 2.32382988929749, 1.14751994609833]+ , [-0.768967986106873, 2.31943011283875, -0.931373000144958]+ , [-0.768967986106873, 2.31943011283875, 0.931373000144958]+ , [-0.768135011196136, 1.10038995742798, -1.80535995960236]+ , [-0.768135011196136, 1.10038995742798, 1.80535995960236]+ , [-0.763400018215179, 2.36952996253967, -0.630285024642944]+ , [-0.763400018215179, 2.36952996253967, 0.630285024642944]+ , [-0.761062979698181, 2.40000009536743, -0.323812991380692]+ , [-0.761062979698181, 2.40000009536743, 0.323812991380692]+ , [-0.754335999488831, 0.453516006469727, -1.77293002605438]+ , [-0.754335999488831, 0.453516006469727, 1.77293002605438]+ , [-0.734902024269104, 0.042773000895977, -1.34957003593445]+ , [-0.734902024269104, 0.042773000895977, 1.34957003593445]+ , [-0.731249988079071, 2.25, -1.08062994480133]+ , [-0.731249988079071, 2.25, 1.08062994480133]+ , [-0.723671972751617, 1.47186994552612, -1.70086002349854]+ , [-0.723671972751617, 1.47186994552612, 1.70086002349854]+ , [-0.709276974201202, 2.29979991912842, -1.04814994335175]+ , [-0.709276974201202, 2.29979991912842, 1.04814994335175]+ , [-0.704126000404358, 2.28691005706787, -1.29305005073547]+ , [-0.704126000404358, 2.28691005706787, 1.29305005073547]+ , [-0.686874985694885, 0.234375, -1.61436998844147]+ , [-0.686874985694885, 0.234375, 1.61436998844147]+ , [-0.685781002044678, 2.40000009536743, -0.464062988758087]+ , [-0.685781002044678, 2.40000009536743, 0.464062988758087]+ , [-0.680997014045715, 2.33612990379333, -1.25057005882263]+ , [-0.680997014045715, 2.33612990379333, 1.25057005882263]+ , [-0.664583027362823, 2.33612990379333, -1.22043001651764]+ , [-0.664583027362823, 2.33612990379333, 1.22043001651764]+ , [-0.663837015628815, 2.28691005706787, -1.21905994415283]+ , [-0.663837015628815, 2.28691005706787, 1.21905994415283]+ , [-0.650390982627869, 2.33906006813049, -0.961133003234863]+ , [-0.650390982627869, 2.33906006813049, 0.961133003234863]+ , [-0.631998002529144, 2.43046998977661, -0.0648249983787537]+ , [-0.631998002529144, 2.43046998977661, 0.0648249983787537]+ , [-0.630285024642944, 2.36952996253967, -0.763400018215179]+ , [-0.630285024642944, 2.36952996253967, 0.763400018215179]+ , [-0.61941397190094, 0.085547000169754, -1.45581996440887]+ , [-0.61941397190094, 0.085547000169754, 1.45581996440887]+ , [-0.611424028873444, 2.27489995956421, -1.12281000614166]+ , [-0.611424028873444, 2.27489995956421, 1.12281000614166]+ , [-0.607173979282379, 2.43046998977661, -0.190548002719879]+ , [-0.607173979282379, 2.43046998977661, 0.190548002719879]+ , [-0.593047022819519, 0.670825004577637, -1.89227998256683]+ , [-0.593047022819519, 0.670825004577637, 1.89227998256683]+ , [-0.58984500169754, 0.925194978713989, -1.88206005096436]+ , [-0.58984500169754, 0.925194978713989, 1.88206005096436]+ , [-0.588750004768372, 0, -1.38374996185303]+ , [-0.588750004768372, 0, 1.38374996185303]+ , [-0.588750004768372, 2.25, -1.38374996185303]+ , [-0.588750004768372, 2.25, 1.38374996185303]+ , [-0.585749983787537, 2.40000009536743, -0.585749983787537]+ , [-0.585749983787537, 2.40000009536743, 0.585749983787537]+ , [-0.581402003765106, 0.5225830078125, -1.85511994361877]+ , [-0.581402003765106, 0.5225830078125, 1.85511994361877]+ , [-0.577103972434998, 2.31943011283875, -1.05979001522064]+ , [-0.577103972434998, 2.31943011283875, 1.05979001522064]+ , [-0.568817973136902, 2.32382988929749, -1.33689999580383]+ , [-0.568817973136902, 2.32382988929749, 1.33689999580383]+ , [-0.566554009914398, 1.28612995147705, -1.80774998664856]+ , [-0.566554009914398, 1.28612995147705, 1.80774998664856]+ , [-0.559973001480103, 2.43046998977661, -0.304711014032364]+ , [-0.559973001480103, 2.43046998977661, 0.304711014032364]+ , [-0.550727009773254, 2.34843993186951, -1.2943799495697]+ , [-0.550727009773254, 2.34843993186951, 1.2943799495697]+ , [-0.549499988555908, 2.25, -1.29149997234344]+ , [-0.549499988555908, 2.25, 1.29149997234344]+ , [-0.547339022159576, 0.343944996595383, -1.74644005298615]+ , [-0.547339022159576, 0.343944996595383, 1.74644005298615]+ , [-0.541833996772766, 2.32382988929749, -1.27348005771637]+ , [-0.541833996772766, 2.32382988929749, 1.27348005771637]+ , [-0.510249972343445, 2.25, -1.19924998283386]+ , [-0.510249972343445, 2.25, 1.19924998283386]+ , [-0.498427987098694, 1.86093997955322, -1.59037005901337]+ , [-0.498427987098694, 1.86093997955322, 1.59037005901337]+ , [-0.49609899520874, 0.159961000084877, -1.58293998241425]+ , [-0.49609899520874, 0.159961000084877, 1.58293998241425]+ , [-0.494917988777161, 2.29979991912842, -1.16322004795074]+ , [-0.494917988777161, 2.29979991912842, 1.16322004795074]+ , [-0.491907000541687, 2.43046998977661, -0.4064100086689]+ , [-0.491907000541687, 2.43046998977661, 0.4064100086689]+ , [-0.473022997379303, 2.36952996253967, -0.868654012680054]+ , [-0.473022997379303, 2.36952996253967, 0.868654012680054]+ , [-0.464062988758087, 2.40000009536743, -0.685781002044678]+ , [-0.464062988758087, 2.40000009536743, 0.685781002044678]+ , [-0.458833009004593, 0.042773000895977, -1.46403002738953]+ , [-0.458833009004593, 0.042773000895977, 1.46403002738953]+ , [-0.456250011920929, 2.46093988418579, 0]+ , [-0.453828006982803, 2.33906006813049, -1.06664001941681]+ , [-0.453828006982803, 2.33906006813049, 1.06664001941681]+ , [-0.439617991447449, 2.28691005706787, -1.40271997451782]+ , [-0.439617991447449, 2.28691005706787, 1.40271997451782]+ , [-0.438241004943848, 2.46093988418579, -0.0912069976329803]+ , [-0.438241004943848, 2.46093988418579, 0.0912069976329803]+ , [-0.425177007913589, 2.33612990379333, -1.35664999485016]+ , [-0.425177007913589, 2.33612990379333, 1.35664999485016]+ , [-0.420890986919403, 2.46093988418579, -0.179077997803688]+ , [-0.420890986919403, 2.46093988418579, 0.179077997803688]+ , [-0.414929002523422, 2.33612990379333, -1.32395005226135]+ , [-0.414929002523422, 2.33612990379333, 1.32395005226135]+ , [-0.414463996887207, 2.28691005706787, -1.32246005535126]+ , [-0.414463996887207, 2.28691005706787, 1.32246005535126]+ , [-0.407499998807907, 0.75, -1.96000003814697]+ , [-0.407499998807907, 0.75, 1.96000003814697]+ , [-0.4064100086689, 2.43046998977661, -0.491907000541687]+ , [-0.4064100086689, 2.43046998977661, 0.491907000541687]+ , [-0.403122991323471, 0.591650009155273, -1.93894994258881]+ , [-0.403122991323471, 0.591650009155273, 1.93894994258881]+ , [-0.398745000362396, 1.10038995742798, -1.91788995265961]+ , [-0.398745000362396, 1.10038995742798, 1.91788995265961]+ , [-0.391582012176514, 0.453516006469727, -1.8834400177002]+ , [-0.391582012176514, 0.453516006469727, 1.8834400177002]+ , [-0.381740003824234, 2.27489995956421, -1.21805000305176]+ , [-0.381740003824234, 2.27489995956421, 1.21805000305176]+ , [-0.375663995742798, 1.47186994552612, -1.8068699836731]+ , [-0.375663995742798, 1.47186994552612, 1.8068699836731]+ , [-0.372159004211426, 2.46093988418579, -0.251888990402222]+ , [-0.372159004211426, 2.46093988418579, 0.251888990402222]+ , [-0.362109005451202, 2.8971700668335, 0]+ , [-0.360312014818192, 2.31943011283875, -1.14968001842499]+ , [-0.360312014818192, 2.31943011283875, 1.14968001842499]+ , [-0.356563001871109, 0.234375, 1.7150000333786]+ , [-0.356561988592148, 0.234375, -1.7150000333786]+ , [-0.340624988079071, 2.95077991485596, 0]+ , [-0.337859004735947, 2.92396998405457, -0.0692780017852783]+ , [-0.337859004735947, 2.92396998405457, 0.0692780017852783]+ , [-0.334237992763519, 2.8971700668335, -0.142704993486404]+ , [-0.334237992763519, 2.8971700668335, 0.142704993486404]+ , [-0.33032500743866, 2.8642098903656, -0.067671999335289]+ , [-0.33032500743866, 2.8642098903656, 0.067671999335289]+ , [-0.324999988079071, 2.83124995231628, 0]+ , [-0.323938012123108, 2.46093988418579, -0.323938012123108]+ , [-0.323938012123108, 2.46093988418579, 0.323938012123108]+ , [-0.323812991380692, 2.40000009536743, -0.761062979698181]+ , [-0.323812991380692, 2.40000009536743, 0.761062979698181]+ , [-0.321543008089066, 0.085547000169754, -1.54656004905701]+ , [-0.321543008089066, 0.085547000169754, 1.54656004905701]+ , [-0.315409988164902, 2.50547003746033, -0.0643950030207634]+ , [-0.315409988164902, 2.50547003746033, 0.0643950030207634]+ , [-0.314464002847672, 2.95077991485596, -0.134406998753548]+ , [-0.314464002847672, 2.95077991485596, 0.134406998753548]+ , [-0.30562499165535, 0, -1.47000002861023]+ , [-0.30562499165535, 0, 1.47000002861023]+ , [-0.30562499165535, 2.25, -1.47000002861023]+ , [-0.30562499165535, 2.25, 1.47000002861023]+ , [-0.304711014032364, 2.43046998977661, -0.559973001480103]+ , [-0.304711014032364, 2.43046998977661, 0.559973001480103]+ , [-0.299953013658524, 2.83124995231628, -0.127984002232552]+ , [-0.299953013658524, 2.83124995231628, 0.127984002232552]+ , [-0.295329988002777, 2.36952996253967, -0.942332029342651]+ , [-0.295329988002777, 2.36952996253967, 0.942332029342651]+ , [-0.295278012752533, 2.32382988929749, -1.42023003101349]+ , [-0.295278012752533, 2.32382988929749, 1.42023003101349]+ , [-0.28719699382782, 2.92396998405457, -0.1942999958992]+ , [-0.28719699382782, 2.92396998405457, 0.1942999958992]+ , [-0.285887002944946, 2.34843993186951, -1.37505996227264]+ , [-0.285887002944946, 2.34843993186951, 1.37505996227264]+ , [-0.285250008106232, 2.25, -1.37199997901917]+ , [-0.285250008106232, 2.25, 1.37199997901917]+ , [-0.281271010637283, 2.32382988929749, -1.35285997390747]+ , [-0.281271010637283, 2.32382988929749, 1.35285997390747]+ , [-0.280732005834579, 2.8642098903656, -0.189855992794037]+ , [-0.280732005834579, 2.8642098903656, 0.189855992794037]+ , [-0.274421006441116, 2.96880006790161, -0.0563799999654293]+ , [-0.274421006441116, 2.96880006790161, 0.0563799999654293]+ , [-0.267832010984421, 2.50547003746033, -0.18087899684906]+ , [-0.267832010984421, 2.50547003746033, 0.18087899684906]+ , [-0.264874994754791, 2.25, -1.27400004863739]+ , [-0.264874994754791, 2.25, 1.27400004863739]+ , [-0.257609993219376, 2.8971700668335, -0.257609993219376]+ , [-0.257609993219376, 2.8971700668335, 0.257609993219376]+ , [-0.256915986537933, 2.29979991912842, -1.235720038414]+ , [-0.256915986537933, 2.29979991912842, 1.235720038414]+ , [-0.251888990402222, 2.46093988418579, -0.372159004211426]+ , [-0.251888990402222, 2.46093988418579, 0.372159004211426]+ , [-0.250871986150742, 2.7574200630188, -0.0513469986617565]+ , [-0.250871986150742, 2.7574200630188, 0.0513469986617565]+ , [-0.242476999759674, 2.95077991485596, -0.242476999759674]+ , [-0.242476999759674, 2.95077991485596, 0.242476999759674]+ , [-0.235586002469063, 2.33906006813049, -1.13311994075775]+ , [-0.235586002469063, 2.33906006813049, 1.13311994075775]+ , [-0.233382001519203, 2.96880006790161, -0.158017992973328]+ , [-0.233382001519203, 2.96880006790161, 0.158017992973328]+ , [-0.231124997138977, 2.83124995231628, -0.231124997138977]+ , [-0.231124997138977, 2.83124995231628, 0.231124997138977]+ , [-0.230077996850014, 2.98681998252869, 0]+ , [-0.213158994913101, 2.7574200630188, -0.14410300552845]+ , [-0.213158994913101, 2.7574200630188, 0.14410300552845]+ , [-0.212515994906425, 2.98681998252869, -0.0911130011081696]+ , [-0.212515994906425, 2.98681998252869, 0.0911130011081696]+ , [-0.202656000852585, 0.670825004577637, -1.96937000751495]+ , [-0.202656000852585, 0.670825004577637, 1.96937000751495]+ , [-0.201561003923416, 0.925194978713989, -1.9587299823761]+ , [-0.201561003923416, 0.925194978713989, 1.9587299823761]+ , [-0.200000002980232, 2.54999995231628, 0]+ , [-0.198676005005836, 0.5225830078125, -1.93069005012512]+ , [-0.198676005005836, 0.5225830078125, 1.93069005012512]+ , [-0.196875005960464, 2.68358993530273, 0]+ , [-0.1942999958992, 2.92396998405457, -0.28719699382782]+ , [-0.1942999958992, 2.92396998405457, 0.28719699382782]+ , [-0.193601995706558, 1.28612995147705, -1.88138997554779]+ , [-0.193601995706558, 1.28612995147705, 1.88138997554779]+ , [-0.190548002719879, 2.43046998977661, -0.607173979282379]+ , [-0.190548002719879, 2.43046998977661, 0.607173979282379]+ , [-0.189855992794037, 2.8642098903656, -0.280732005834579]+ , [-0.189855992794037, 2.8642098903656, 0.280732005834579]+ , [-0.187035992741585, 0.343944996595383, -1.81757998466492]+ , [-0.187035992741585, 0.343944996595383, 1.81757998466492]+ , [-0.184499993920326, 2.54999995231628, -0.0785000026226044]+ , [-0.184499993920326, 2.54999995231628, 0.0785000026226044]+ , [-0.181660994887352, 2.68358993530273, -0.0774049982428551]+ , [-0.181660994887352, 2.68358993530273, 0.0774049982428551]+ , [-0.18087899684906, 2.50547003746033, -0.267832010984421]+ , [-0.18087899684906, 2.50547003746033, 0.267832010984421]+ , [-0.179077997803688, 2.46093988418579, -0.420890986919403]+ , [-0.179077997803688, 2.46093988418579, 0.420890986919403]+ , [-0.17629499733448, 2.58119988441467, -0.0360010005533695]+ , [-0.17629499733448, 2.58119988441467, 0.0360010005533695]+ , [-0.174804002046585, 2.64800000190735, -0.0357270017266273]+ , [-0.174804002046585, 2.64800000190735, 0.0357270017266273]+ , [-0.170322000980377, 1.86093997955322, -1.65515995025635]+ , [-0.170322000980377, 1.86093997955322, 1.65515995025635]+ , [-0.169525995850563, 0.159961000084877, -1.64742004871368]+ , [-0.169525995850563, 0.159961000084877, 1.64742004871368]+ , [-0.168093994259834, 2.40000009536743, -0.808499991893768]+ , [-0.168093994259834, 2.40000009536743, 0.808499991893768]+ , [-0.166796997189522, 2.61240005493164, 0]+ , [-0.164073005318642, 2.98681998252869, -0.164073005318642]+ , [-0.164073005318642, 2.98681998252869, 0.164073005318642]+ , [-0.158017992973328, 2.96880006790161, -0.233382001519203]+ , [-0.158017992973328, 2.96880006790161, 0.233382001519203]+ , [-0.156791999936104, 0.042773000895977, -1.52366995811462]+ , [-0.156791999936104, 0.042773000895977, 1.52366995811462]+ , [-0.153881996870041, 2.61240005493164, -0.0655039995908737]+ , [-0.153881996870041, 2.61240005493164, 0.0655039995908737]+ , [-0.15022599697113, 2.28691005706787, -1.45985996723175]+ , [-0.15022599697113, 2.28691005706787, 1.45985996723175]+ , [-0.14970999956131, 2.58119988441467, -0.101116001605988]+ , [-0.14970999956131, 2.58119988441467, 0.101116001605988]+ , [-0.148475006222725, 2.64800000190735, -0.100316002964973]+ , [-0.148475006222725, 2.64800000190735, 0.100316002964973]+ , [-0.14529100060463, 2.33612990379333, -1.41191005706787]+ , [-0.14529100060463, 2.33612990379333, 1.41191005706787]+ , [-0.14410300552845, 2.7574200630188, -0.213158994913101]+ , [-0.14410300552845, 2.7574200630188, 0.213158994913101]+ , [-0.142704993486404, 2.8971700668335, -0.334237992763519]+ , [-0.142704993486404, 2.8971700668335, 0.334237992763519]+ , [-0.142000004649162, 2.54999995231628, -0.142000004649162]+ , [-0.142000004649162, 2.54999995231628, 0.142000004649162]+ , [-0.141789004206657, 2.33612990379333, -1.37787997722626]+ , [-0.141789004206657, 2.33612990379333, 1.37787997722626]+ , [-0.141629993915558, 2.28691005706787, -1.37633001804352]+ , [-0.141629993915558, 2.28691005706787, 1.37633001804352]+ , [-0.139898002147675, 2.68358993530273, -0.139898002147675]+ , [-0.139898002147675, 2.68358993530273, 0.139898002147675]+ , [-0.134406998753548, 2.95077991485596, -0.314464002847672]+ , [-0.134406998753548, 2.95077991485596, 0.314464002847672]+ , [-0.130447998642921, 2.27489995956421, -1.26766002178192]+ , [-0.130447998642921, 2.27489995956421, 1.26766002178192]+ , [-0.127984002232552, 2.83124995231628, -0.299953013658524]+ , [-0.127984002232552, 2.83124995231628, 0.299953013658524]+ , [-0.123125001788139, 2.31943011283875, -1.19650995731354]+ , [-0.123125001788139, 2.31943011283875, 1.19650995731354]+ , [-0.11845800280571, 2.61240005493164, -0.11845800280571]+ , [-0.11845800280571, 2.61240005493164, 0.11845800280571]+ , [-0.11064899712801, 2.99341011047363, -0.0227780006825924]+ , [-0.11064899712801, 2.99341011047363, 0.0227780006825924]+ , [-0.101116001605988, 2.58119988441467, -0.14970999956131]+ , [-0.101116001605988, 2.58119988441467, 0.14970999956131]+ , [-0.100919999182224, 2.36952996253967, -0.980718970298767]+ , [-0.100919999182224, 2.36952996253967, 0.980718970298767]+ , [-0.100316002964973, 2.64800000190735, -0.148475006222725]+ , [-0.100316002964973, 2.64800000190735, 0.148475006222725]+ , [-0.0941469967365265, 2.99341011047363, -0.0637969970703125]+ , [-0.0941469967365265, 2.99341011047363, 0.0637969970703125]+ , [-0.0912069976329803, 2.46093988418579, -0.438241004943848]+ , [-0.0912069976329803, 2.46093988418579, 0.438241004943848]+ , [-0.0911130011081696, 2.98681998252869, -0.212515994906425]+ , [-0.0911130011081696, 2.98681998252869, 0.212515994906425]+ , [-0.0785000026226044, 2.54999995231628, -0.184499993920326]+ , [-0.0785000026226044, 2.54999995231628, 0.184499993920326]+ , [-0.0774049982428551, 2.68358993530273, -0.181660994887352]+ , [-0.0774049982428551, 2.68358993530273, 0.181660994887352]+ , [-0.0692780017852783, 2.92396998405457, -0.337859004735947]+ , [-0.0692780017852783, 2.92396998405457, 0.337859004735947]+ , [-0.067671999335289, 2.8642098903656, -0.33032500743866]+ , [-0.067671999335289, 2.8642098903656, 0.33032500743866]+ , [-0.0655039995908737, 2.61240005493164, -0.153881996870041]+ , [-0.0655039995908737, 2.61240005493164, 0.153881996870041]+ , [-0.0648249983787537, 2.43046998977661, -0.631998002529144]+ , [-0.0648249983787537, 2.43046998977661, 0.631998002529144]+ , [-0.0643950030207634, 2.50547003746033, -0.315409988164902]+ , [-0.0643950030207634, 2.50547003746033, 0.315409988164902]+ , [-0.0637969970703125, 2.99341011047363, -0.0941469967365265]+ , [-0.0637969970703125, 2.99341011047363, 0.0941469967365265]+ , [-0.0563799999654293, 2.96880006790161, -0.274421006441116]+ , [-0.0563799999654293, 2.96880006790161, 0.274421006441116]+ , [-0.0513469986617565, 2.7574200630188, -0.250871986150742]+ , [-0.0513469986617565, 2.7574200630188, 0.250871986150742]+ , [-0.0360010005533695, 2.58119988441467, -0.17629499733448]+ , [-0.0360010005533695, 2.58119988441467, 0.17629499733448]+ , [-0.0357270017266273, 2.64800000190735, -0.174804002046585]+ , [-0.0357270017266273, 2.64800000190735, 0.174804002046585]+ , [-0.0227780006825924, 2.99341011047363, -0.11064899712801]+ , [-0.0227780006825924, 2.99341011047363, 0.11064899712801]+ , [0, 0, -1.5]+ , [0, 0, 1.5]+ , [0, 0.085547000169754, -1.57813000679016]+ , [0, 0.085547000169754, 1.57813000679016]+ , [0, 0.234375, -1.75]+ , [0, 0.234375, 1.75]+ , [0, 0.453516006469727, -1.92188000679016]+ , [0, 0.453516006469727, 1.92188000679016]+ , [0, 0.591650009155273, -1.97852003574371]+ , [0, 0.591650009155273, 1.97852003574371]+ , [0, 0.75, -2]+ , [0, 0.75, 2]+ , [0, 1.10038995742798, -1.9570300579071]+ , [0, 1.10038995742798, 1.9570300579071]+ , [0, 1.47186994552612, -1.84375]+ , [0, 1.47186994552612, 1.84375]+ , [0, 2.25, -1.5]+ , [0, 2.25, -1.39999997615814]+ , [0, 2.25, -1.29999995231628]+ , [0, 2.25, 1.29999995231628]+ , [0, 2.25, 1.39999997615814]+ , [0, 2.25, 1.5]+ , [0, 2.29979991912842, -1.26093995571136]+ , [0, 2.29979991912842, 1.26093995571136]+ , [0, 2.32382988929749, -1.4492199420929]+ , [0, 2.32382988929749, -1.38047003746033]+ , [0, 2.32382988929749, 1.38047003746033]+ , [0, 2.32382988929749, 1.4492199420929]+ , [0, 2.33906006813049, -1.15625]+ , [0, 2.33906006813049, 1.15625]+ , [0, 2.34843993186951, -1.40313005447388]+ , [0, 2.34843993186951, 1.40313005447388]+ , [0, 2.40000009536743, -0.824999988079071]+ , [0, 2.40000009536743, 0.824999988079071]+ , [0, 2.46093988418579, -0.456250011920929]+ , [0, 2.46093988418579, 0.456250011920929]+ , [0, 2.54999995231628, -0.200000002980232]+ , [0, 2.54999995231628, 0.200000002980232]+ , [0, 2.61240005493164, -0.166796997189522]+ , [0, 2.61240005493164, 0.166796997189522]+ , [0, 2.68358993530273, -0.196875005960464]+ , [0, 2.68358993530273, 0.196875005960464]+ , [0, 2.83124995231628, -0.324999988079071]+ , [0, 2.83124995231628, 0.324999988079071]+ , [0, 2.8971700668335, -0.362109005451202]+ , [0, 2.8971700668335, 0.362109005451202]+ , [0, 2.95077991485596, -0.340624988079071]+ , [0, 2.95077991485596, 0.340624988079071]+ , [0, 2.98681998252869, -0.230077996850014]+ , [0, 2.98681998252869, 0.230077996850014]+ , [0, 3, 0]+ , [0.0227780006825924, 2.99341011047363, -0.11064899712801]+ , [0.0227780006825924, 2.99341011047363, 0.11064899712801]+ , [0.0357270017266273, 2.64800000190735, -0.174804002046585]+ , [0.0357270017266273, 2.64800000190735, 0.174804002046585]+ , [0.0360010005533695, 2.58119988441467, -0.17629499733448]+ , [0.0360010005533695, 2.58119988441467, 0.17629499733448]+ , [0.0513469986617565, 2.7574200630188, -0.250871986150742]+ , [0.0513469986617565, 2.7574200630188, 0.250871986150742]+ , [0.0563799999654293, 2.96880006790161, -0.274421006441116]+ , [0.0563799999654293, 2.96880006790161, 0.274421006441116]+ , [0.0637969970703125, 2.99341011047363, -0.0941469967365265]+ , [0.0637969970703125, 2.99341011047363, 0.0941469967365265]+ , [0.0643950030207634, 2.50547003746033, -0.315409988164902]+ , [0.0643950030207634, 2.50547003746033, 0.315409988164902]+ , [0.0648249983787537, 2.43046998977661, -0.631998002529144]+ , [0.0648249983787537, 2.43046998977661, 0.631998002529144]+ , [0.0655039995908737, 2.61240005493164, -0.153881996870041]+ , [0.0655039995908737, 2.61240005493164, 0.153881996870041]+ , [0.067671999335289, 2.8642098903656, -0.33032500743866]+ , [0.067671999335289, 2.8642098903656, 0.33032500743866]+ , [0.0692780017852783, 2.92396998405457, -0.337859004735947]+ , [0.0692780017852783, 2.92396998405457, 0.337859004735947]+ , [0.0774049982428551, 2.68358993530273, -0.181660994887352]+ , [0.0774049982428551, 2.68358993530273, 0.181660994887352]+ , [0.0785000026226044, 2.54999995231628, -0.184499993920326]+ , [0.0785000026226044, 2.54999995231628, 0.184499993920326]+ , [0.0911130011081696, 2.98681998252869, -0.212515994906425]+ , [0.0911130011081696, 2.98681998252869, 0.212515994906425]+ , [0.0912069976329803, 2.46093988418579, -0.438241004943848]+ , [0.0912069976329803, 2.46093988418579, 0.438241004943848]+ , [0.0941469967365265, 2.99341011047363, -0.0637969970703125]+ , [0.0941469967365265, 2.99341011047363, 0.0637969970703125]+ , [0.100316002964973, 2.64800000190735, -0.148475006222725]+ , [0.100316002964973, 2.64800000190735, 0.148475006222725]+ , [0.100919999182224, 2.36952996253967, -0.980718970298767]+ , [0.100919999182224, 2.36952996253967, 0.980718970298767]+ , [0.101116001605988, 2.58119988441467, -0.14970999956131]+ , [0.101116001605988, 2.58119988441467, 0.14970999956131]+ , [0.11064899712801, 2.99341011047363, -0.0227780006825924]+ , [0.11064899712801, 2.99341011047363, 0.0227780006825924]+ , [0.11845800280571, 2.61240005493164, -0.11845800280571]+ , [0.11845800280571, 2.61240005493164, 0.11845800280571]+ , [0.123125001788139, 2.31943011283875, -1.19650995731354]+ , [0.123125001788139, 2.31943011283875, 1.19650995731354]+ , [0.127984002232552, 2.83124995231628, -0.299953013658524]+ , [0.127984002232552, 2.83124995231628, 0.299953013658524]+ , [0.130447998642921, 2.27489995956421, -1.26766002178192]+ , [0.130447998642921, 2.27489995956421, 1.26766002178192]+ , [0.134406998753548, 2.95077991485596, -0.314464002847672]+ , [0.134406998753548, 2.95077991485596, 0.314464002847672]+ , [0.139898002147675, 2.68358993530273, -0.139898002147675]+ , [0.139898002147675, 2.68358993530273, 0.139898002147675]+ , [0.141629993915558, 2.28691005706787, -1.37633001804352]+ , [0.141629993915558, 2.28691005706787, 1.37633001804352]+ , [0.141789004206657, 2.33612990379333, -1.37787997722626]+ , [0.141789004206657, 2.33612990379333, 1.37787997722626]+ , [0.142000004649162, 2.54999995231628, -0.142000004649162]+ , [0.142000004649162, 2.54999995231628, 0.142000004649162]+ , [0.142704993486404, 2.8971700668335, -0.334237992763519]+ , [0.142704993486404, 2.8971700668335, 0.334237992763519]+ , [0.14410300552845, 2.7574200630188, -0.213158994913101]+ , [0.14410300552845, 2.7574200630188, 0.213158994913101]+ , [0.14529100060463, 2.33612990379333, -1.41191005706787]+ , [0.14529100060463, 2.33612990379333, 1.41191005706787]+ , [0.148475006222725, 2.64800000190735, -0.100316002964973]+ , [0.148475006222725, 2.64800000190735, 0.100316002964973]+ , [0.14970999956131, 2.58119988441467, -0.101116001605988]+ , [0.14970999956131, 2.58119988441467, 0.101116001605988]+ , [0.15022599697113, 2.28691005706787, -1.45985996723175]+ , [0.15022599697113, 2.28691005706787, 1.45985996723175]+ , [0.153881996870041, 2.61240005493164, -0.0655039995908737]+ , [0.153881996870041, 2.61240005493164, 0.0655039995908737]+ , [0.156791999936104, 0.042773000895977, -1.52366995811462]+ , [0.156791999936104, 0.042773000895977, 1.52366995811462]+ , [0.158017992973328, 2.96880006790161, -0.233382001519203]+ , [0.158017992973328, 2.96880006790161, 0.233382001519203]+ , [0.164073005318642, 2.98681998252869, -0.164073005318642]+ , [0.164073005318642, 2.98681998252869, 0.164073005318642]+ , [0.166796997189522, 2.61240005493164, 0]+ , [0.168093994259834, 2.40000009536743, -0.808499991893768]+ , [0.168093994259834, 2.40000009536743, 0.808499991893768]+ , [0.169525995850563, 0.159961000084877, -1.64742004871368]+ , [0.169525995850563, 0.159961000084877, 1.64742004871368]+ , [0.170322000980377, 1.86093997955322, -1.65515995025635]+ , [0.170322000980377, 1.86093997955322, 1.65515995025635]+ , [0.174804002046585, 2.64800000190735, -0.0357270017266273]+ , [0.174804002046585, 2.64800000190735, 0.0357270017266273]+ , [0.17629499733448, 2.58119988441467, -0.0360010005533695]+ , [0.17629499733448, 2.58119988441467, 0.0360010005533695]+ , [0.179077997803688, 2.46093988418579, -0.420890986919403]+ , [0.179077997803688, 2.46093988418579, 0.420890986919403]+ , [0.18087899684906, 2.50547003746033, -0.267832010984421]+ , [0.18087899684906, 2.50547003746033, 0.267832010984421]+ , [0.181660994887352, 2.68358993530273, -0.0774049982428551]+ , [0.181660994887352, 2.68358993530273, 0.0774049982428551]+ , [0.184499993920326, 2.54999995231628, -0.0785000026226044]+ , [0.184499993920326, 2.54999995231628, 0.0785000026226044]+ , [0.187035992741585, 0.343944996595383, -1.81757998466492]+ , [0.187035992741585, 0.343944996595383, 1.81757998466492]+ , [0.189855992794037, 2.8642098903656, -0.280732005834579]+ , [0.189855992794037, 2.8642098903656, 0.280732005834579]+ , [0.190548002719879, 2.43046998977661, -0.607173979282379]+ , [0.190548002719879, 2.43046998977661, 0.607173979282379]+ , [0.193601995706558, 1.28612995147705, -1.88138997554779]+ , [0.193601995706558, 1.28612995147705, 1.88138997554779]+ , [0.1942999958992, 2.92396998405457, -0.28719699382782]+ , [0.1942999958992, 2.92396998405457, 0.28719699382782]+ , [0.196875005960464, 2.68358993530273, 0]+ , [0.198676005005836, 0.5225830078125, -1.93069005012512]+ , [0.198676005005836, 0.5225830078125, 1.93069005012512]+ , [0.200000002980232, 2.54999995231628, 0]+ , [0.201561003923416, 0.925194978713989, -1.9587299823761]+ , [0.201561003923416, 0.925194978713989, 1.9587299823761]+ , [0.202656000852585, 0.670825004577637, -1.96937000751495]+ , [0.202656000852585, 0.670825004577637, 1.96937000751495]+ , [0.212515994906425, 2.98681998252869, -0.0911130011081696]+ , [0.212515994906425, 2.98681998252869, 0.0911130011081696]+ , [0.213158994913101, 2.7574200630188, -0.14410300552845]+ , [0.213158994913101, 2.7574200630188, 0.14410300552845]+ , [0.230077996850014, 2.98681998252869, 0]+ , [0.231124997138977, 2.83124995231628, -0.231124997138977]+ , [0.231124997138977, 2.83124995231628, 0.231124997138977]+ , [0.233382001519203, 2.96880006790161, -0.158017992973328]+ , [0.233382001519203, 2.96880006790161, 0.158017992973328]+ , [0.235586002469063, 2.33906006813049, -1.13311994075775]+ , [0.235586002469063, 2.33906006813049, 1.13311994075775]+ , [0.242476999759674, 2.95077991485596, -0.242476999759674]+ , [0.242476999759674, 2.95077991485596, 0.242476999759674]+ , [0.250871986150742, 2.7574200630188, -0.0513469986617565]+ , [0.250871986150742, 2.7574200630188, 0.0513469986617565]+ , [0.251888990402222, 2.46093988418579, -0.372159004211426]+ , [0.251888990402222, 2.46093988418579, 0.372159004211426]+ , [0.256915986537933, 2.29979991912842, -1.235720038414]+ , [0.256915986537933, 2.29979991912842, 1.235720038414]+ , [0.257609993219376, 2.8971700668335, -0.257609993219376]+ , [0.257609993219376, 2.8971700668335, 0.257609993219376]+ , [0.264874994754791, 2.25, -1.27400004863739]+ , [0.264874994754791, 2.25, 1.27400004863739]+ , [0.267832010984421, 2.50547003746033, -0.18087899684906]+ , [0.267832010984421, 2.50547003746033, 0.18087899684906]+ , [0.274421006441116, 2.96880006790161, -0.0563799999654293]+ , [0.274421006441116, 2.96880006790161, 0.0563799999654293]+ , [0.280732005834579, 2.8642098903656, -0.189855992794037]+ , [0.280732005834579, 2.8642098903656, 0.189855992794037]+ , [0.281271010637283, 2.32382988929749, -1.35285997390747]+ , [0.281271010637283, 2.32382988929749, 1.35285997390747]+ , [0.285250008106232, 2.25, -1.37199997901917]+ , [0.285250008106232, 2.25, 1.37199997901917]+ , [0.285887002944946, 2.34843993186951, -1.37505996227264]+ , [0.285887002944946, 2.34843993186951, 1.37505996227264]+ , [0.28719699382782, 2.92396998405457, -0.1942999958992]+ , [0.28719699382782, 2.92396998405457, 0.1942999958992]+ , [0.295278012752533, 2.32382988929749, -1.42023003101349]+ , [0.295278012752533, 2.32382988929749, 1.42023003101349]+ , [0.295329988002777, 2.36952996253967, -0.942332029342651]+ , [0.295329988002777, 2.36952996253967, 0.942332029342651]+ , [0.299953013658524, 2.83124995231628, -0.127984002232552]+ , [0.299953013658524, 2.83124995231628, 0.127984002232552]+ , [0.304711014032364, 2.43046998977661, -0.559973001480103]+ , [0.304711014032364, 2.43046998977661, 0.559973001480103]+ , [0.30562499165535, 0, -1.47000002861023]+ , [0.30562499165535, 0, 1.47000002861023]+ , [0.30562499165535, 2.25, -1.47000002861023]+ , [0.30562499165535, 2.25, 1.47000002861023]+ , [0.314464002847672, 2.95077991485596, -0.134406998753548]+ , [0.314464002847672, 2.95077991485596, 0.134406998753548]+ , [0.315409988164902, 2.50547003746033, -0.0643950030207634]+ , [0.315409988164902, 2.50547003746033, 0.0643950030207634]+ , [0.321543008089066, 0.085547000169754, -1.54656004905701]+ , [0.321543008089066, 0.085547000169754, 1.54656004905701]+ , [0.323812991380692, 2.40000009536743, -0.761062979698181]+ , [0.323812991380692, 2.40000009536743, 0.761062979698181]+ , [0.323938012123108, 2.46093988418579, -0.323938012123108]+ , [0.323938012123108, 2.46093988418579, 0.323938012123108]+ , [0.324999988079071, 2.83124995231628, 0]+ , [0.33032500743866, 2.8642098903656, -0.067671999335289]+ , [0.33032500743866, 2.8642098903656, 0.067671999335289]+ , [0.334237992763519, 2.8971700668335, -0.142704993486404]+ , [0.334237992763519, 2.8971700668335, 0.142704993486404]+ , [0.337859004735947, 2.92396998405457, -0.0692780017852783]+ , [0.337859004735947, 2.92396998405457, 0.0692780017852783]+ , [0.340624988079071, 2.95077991485596, 0]+ , [0.356561988592148, 0.234375, 1.7150000333786]+ , [0.356563001871109, 0.234375, -1.7150000333786]+ , [0.360312014818192, 2.31943011283875, -1.14968001842499]+ , [0.360312014818192, 2.31943011283875, 1.14968001842499]+ , [0.362109005451202, 2.8971700668335, 0]+ , [0.372159004211426, 2.46093988418579, -0.251888990402222]+ , [0.372159004211426, 2.46093988418579, 0.251888990402222]+ , [0.375663995742798, 1.47186994552612, -1.8068699836731]+ , [0.375663995742798, 1.47186994552612, 1.8068699836731]+ , [0.381740003824234, 2.27489995956421, -1.21805000305176]+ , [0.381740003824234, 2.27489995956421, 1.21805000305176]+ , [0.391582012176514, 0.453516006469727, -1.8834400177002]+ , [0.391582012176514, 0.453516006469727, 1.8834400177002]+ , [0.398745000362396, 1.10038995742798, -1.91788995265961]+ , [0.398745000362396, 1.10038995742798, 1.91788995265961]+ , [0.403122991323471, 0.591650009155273, -1.93894994258881]+ , [0.403122991323471, 0.591650009155273, 1.93894994258881]+ , [0.4064100086689, 2.43046998977661, -0.491907000541687]+ , [0.4064100086689, 2.43046998977661, 0.491907000541687]+ , [0.407499998807907, 0.75, -1.96000003814697]+ , [0.407499998807907, 0.75, 1.96000003814697]+ , [0.414463996887207, 2.28691005706787, -1.32246005535126]+ , [0.414463996887207, 2.28691005706787, 1.32246005535126]+ , [0.414929002523422, 2.33612990379333, -1.32395005226135]+ , [0.414929002523422, 2.33612990379333, 1.32395005226135]+ , [0.420890986919403, 2.46093988418579, -0.179077997803688]+ , [0.420890986919403, 2.46093988418579, 0.179077997803688]+ , [0.425177007913589, 2.33612990379333, -1.35664999485016]+ , [0.425177007913589, 2.33612990379333, 1.35664999485016]+ , [0.438241004943848, 2.46093988418579, -0.0912069976329803]+ , [0.438241004943848, 2.46093988418579, 0.0912069976329803]+ , [0.439617991447449, 2.28691005706787, -1.40271997451782]+ , [0.439617991447449, 2.28691005706787, 1.40271997451782]+ , [0.453828006982803, 2.33906006813049, -1.06664001941681]+ , [0.453828006982803, 2.33906006813049, 1.06664001941681]+ , [0.456250011920929, 2.46093988418579, 0]+ , [0.458833009004593, 0.042773000895977, -1.46403002738953]+ , [0.458833009004593, 0.042773000895977, 1.46403002738953]+ , [0.464062988758087, 2.40000009536743, -0.685781002044678]+ , [0.464062988758087, 2.40000009536743, 0.685781002044678]+ , [0.473022997379303, 2.36952996253967, -0.868654012680054]+ , [0.473022997379303, 2.36952996253967, 0.868654012680054]+ , [0.491907000541687, 2.43046998977661, -0.4064100086689]+ , [0.491907000541687, 2.43046998977661, 0.4064100086689]+ , [0.494917988777161, 2.29979991912842, -1.16322004795074]+ , [0.494917988777161, 2.29979991912842, 1.16322004795074]+ , [0.49609899520874, 0.159961000084877, -1.58293998241425]+ , [0.49609899520874, 0.159961000084877, 1.58293998241425]+ , [0.498427987098694, 1.86093997955322, -1.59037005901337]+ , [0.498427987098694, 1.86093997955322, 1.59037005901337]+ , [0.510249972343445, 2.25, -1.19924998283386]+ , [0.510249972343445, 2.25, 1.19924998283386]+ , [0.541833996772766, 2.32382988929749, -1.27348005771637]+ , [0.541833996772766, 2.32382988929749, 1.27348005771637]+ , [0.547339022159576, 0.343944996595383, -1.74644005298615]+ , [0.547339022159576, 0.343944996595383, 1.74644005298615]+ , [0.549499988555908, 2.25, -1.29149997234344]+ , [0.549499988555908, 2.25, 1.29149997234344]+ , [0.550727009773254, 2.34843993186951, -1.2943799495697]+ , [0.550727009773254, 2.34843993186951, 1.2943799495697]+ , [0.559973001480103, 2.43046998977661, -0.304711014032364]+ , [0.559973001480103, 2.43046998977661, 0.304711014032364]+ , [0.566554009914398, 1.28612995147705, -1.80774998664856]+ , [0.566554009914398, 1.28612995147705, 1.80774998664856]+ , [0.568817973136902, 2.32382988929749, -1.33689999580383]+ , [0.568817973136902, 2.32382988929749, 1.33689999580383]+ , [0.577103972434998, 2.31943011283875, -1.05979001522064]+ , [0.577103972434998, 2.31943011283875, 1.05979001522064]+ , [0.581402003765106, 0.5225830078125, -1.85511994361877]+ , [0.581402003765106, 0.5225830078125, 1.85511994361877]+ , [0.585749983787537, 2.40000009536743, -0.585749983787537]+ , [0.585749983787537, 2.40000009536743, 0.585749983787537]+ , [0.588750004768372, 0, -1.38374996185303]+ , [0.588750004768372, 0, 1.38374996185303]+ , [0.588750004768372, 2.25, -1.38374996185303]+ , [0.588750004768372, 2.25, 1.38374996185303]+ , [0.58984500169754, 0.925194978713989, -1.88206005096436]+ , [0.58984500169754, 0.925194978713989, 1.88206005096436]+ , [0.593047022819519, 0.670825004577637, -1.89227998256683]+ , [0.593047022819519, 0.670825004577637, 1.89227998256683]+ , [0.607173979282379, 2.43046998977661, -0.190548002719879]+ , [0.607173979282379, 2.43046998977661, 0.190548002719879]+ , [0.611424028873444, 2.27489995956421, -1.12281000614166]+ , [0.611424028873444, 2.27489995956421, 1.12281000614166]+ , [0.61941397190094, 0.085547000169754, -1.45581996440887]+ , [0.61941397190094, 0.085547000169754, 1.45581996440887]+ , [0.630285024642944, 2.36952996253967, -0.763400018215179]+ , [0.630285024642944, 2.36952996253967, 0.763400018215179]+ , [0.631998002529144, 2.43046998977661, -0.0648249983787537]+ , [0.631998002529144, 2.43046998977661, 0.0648249983787537]+ , [0.650390982627869, 2.33906006813049, -0.961133003234863]+ , [0.650390982627869, 2.33906006813049, 0.961133003234863]+ , [0.663837015628815, 2.28691005706787, -1.21905994415283]+ , [0.663837015628815, 2.28691005706787, 1.21905994415283]+ , [0.664583027362823, 2.33612990379333, -1.22043001651764]+ , [0.664583027362823, 2.33612990379333, 1.22043001651764]+ , [0.680997014045715, 2.33612990379333, -1.25057005882263]+ , [0.680997014045715, 2.33612990379333, 1.25057005882263]+ , [0.685781002044678, 2.40000009536743, -0.464062988758087]+ , [0.685781002044678, 2.40000009536743, 0.464062988758087]+ , [0.686874985694885, 0.234375, -1.61436998844147]+ , [0.686874985694885, 0.234375, 1.61436998844147]+ , [0.704126000404358, 2.28691005706787, -1.29305005073547]+ , [0.704126000404358, 2.28691005706787, 1.29305005073547]+ , [0.709276974201202, 2.29979991912842, -1.04814994335175]+ , [0.709276974201202, 2.29979991912842, 1.04814994335175]+ , [0.723671972751617, 1.47186994552612, -1.70086002349854]+ , [0.723671972751617, 1.47186994552612, 1.70086002349854]+ , [0.731249988079071, 2.25, -1.08062994480133]+ , [0.731249988079071, 2.25, 1.08062994480133]+ , [0.734902024269104, 0.042773000895977, -1.34957003593445]+ , [0.734902024269104, 0.042773000895977, 1.34957003593445]+ , [0.754335999488831, 0.453516006469727, -1.77293002605438]+ , [0.754335999488831, 0.453516006469727, 1.77293002605438]+ , [0.761062979698181, 2.40000009536743, -0.323812991380692]+ , [0.761062979698181, 2.40000009536743, 0.323812991380692]+ , [0.763400018215179, 2.36952996253967, -0.630285024642944]+ , [0.763400018215179, 2.36952996253967, 0.630285024642944]+ , [0.768135011196136, 1.10038995742798, -1.80535995960236]+ , [0.768135011196136, 1.10038995742798, 1.80535995960236]+ , [0.768967986106873, 2.31943011283875, -0.931373000144958]+ , [0.768967986106873, 2.31943011283875, 0.931373000144958]+ , [0.776513993740082, 2.32382988929749, -1.14751994609833]+ , [0.776513993740082, 2.32382988929749, 1.14751994609833]+ , [0.776566982269287, 0.591650009155273, -1.82518005371094]+ , [0.776566982269287, 0.591650009155273, 1.82518005371094]+ , [0.785000026226044, 0.75, -1.84500002861023]+ , [0.785000026226044, 0.75, 1.84500002861023]+ , [0.787500023841858, 2.25, -1.16375005245209]+ , [0.787500023841858, 2.25, 1.16375005245209]+ , [0.789258003234863, 2.34843993186951, -1.16635000705719]+ , [0.789258003234863, 2.34843993186951, 1.16635000705719]+ , [0.794589996337891, 0.159961000084877, -1.45916998386383]+ , [0.794589996337891, 0.159961000084877, 1.45916998386383]+ , [0.79831999540329, 1.86093997955322, -1.46601998806]+ , [0.79831999540329, 1.86093997955322, 1.46601998806]+ , [0.808499991893768, 2.40000009536743, -0.168093994259834]+ , [0.808499991893768, 2.40000009536743, 0.168093994259834]+ , [0.814697980880737, 2.27489995956421, -0.986760973930359]+ , [0.814697980880737, 2.27489995956421, 0.986760973930359]+ , [0.815186023712158, 2.32382988929749, -1.20466005802155]+ , [0.815186023712158, 2.32382988929749, 1.20466005802155]+ , [0.820937991142273, 2.33906006813049, -0.820937991142273]+ , [0.820937991142273, 2.33906006813049, 0.820937991142273]+ , [0.824999988079071, 2.40000009536743, 0]+ , [0.84375, 0, -1.24688005447388]+ , [0.84375, 0, 1.24688005447388]+ , [0.84375, 2.25, -1.24688005447388]+ , [0.84375, 2.25, 1.24688005447388]+ , [0.868654012680054, 2.36952996253967, -0.473022997379303]+ , [0.868654012680054, 2.36952996253967, 0.473022997379303]+ , [0.876659989356995, 0.343944996595383, -1.60988998413086]+ , [0.876659989356995, 0.343944996595383, 1.60988998413086]+ , [0.884536981582642, 2.28691005706787, -1.07134997844696]+ , [0.884536981582642, 2.28691005706787, 1.07134997844696]+ , [0.885531008243561, 2.33612990379333, -1.07255005836487]+ , [0.885531008243561, 2.33612990379333, 1.07255005836487]+ , [0.887695014476776, 0.085547000169754, -1.3118200302124]+ , [0.887695014476776, 0.085547000169754, 1.3118200302124]+ , [0.895265996456146, 2.29979991912842, -0.895265996456146]+ , [0.895265996456146, 2.29979991912842, 0.895265996456146]+ , [0.907401978969574, 2.33612990379333, -1.09904003143311]+ , [0.907401978969574, 2.33612990379333, 1.09904003143311]+ , [0.907437026500702, 1.28612995147705, -1.66639995574951]+ , [0.907437026500702, 1.28612995147705, 1.66639995574951]+ , [0.922999978065491, 2.25, -0.922999978065491]+ , [0.922999978065491, 2.25, 0.922999978065491]+ , [0.931218028068542, 0.5225830078125, -1.71008002758026]+ , [0.931218028068542, 0.5225830078125, 1.71008002758026]+ , [0.931373000144958, 2.31943011283875, -0.768967986106873]+ , [0.931373000144958, 2.31943011283875, 0.768967986106873]+ , [0.938220024108887, 2.28691005706787, -1.13636994361877]+ , [0.938220024108887, 2.28691005706787, 1.13636994361877]+ , [0.942332029342651, 2.36952996253967, -0.295329988002777]+ , [0.942332029342651, 2.36952996253967, 0.295329988002777]+ , [0.944741010665894, 0.925194978713989, -1.7349100112915]+ , [0.944741010665894, 0.925194978713989, 1.7349100112915]+ , [0.949871003627777, 0.670825004577637, -1.7443300485611]+ , [0.949871003627777, 0.670825004577637, 1.7443300485611]+ , [0.961133003234863, 2.33906006813049, -0.650390982627869]+ , [0.961133003234863, 2.33906006813049, 0.650390982627869]+ , [0.979228973388672, 0.042773000895977, -1.18604004383087]+ , [0.979228973388672, 0.042773000895977, 1.18604004383087]+ , [0.98013299703598, 2.32382988929749, -0.98013299703598]+ , [0.98013299703598, 2.32382988929749, 0.98013299703598]+ , [0.980718970298767, 2.36952996253967, -0.100919999182224]+ , [0.980718970298767, 2.36952996253967, 0.100919999182224]+ , [0.984375, 0.234375, -1.45468997955322]+ , [0.984375, 0.234375, 1.45468997955322]+ , [0.986760973930359, 2.27489995956421, -0.814697980880737]+ , [0.986760973930359, 2.27489995956421, 0.814697980880737]+ , [0.994000017642975, 2.25, -0.994000017642975]+ , [0.994000017642975, 2.25, 0.994000017642975]+ , [0.996218979358673, 2.34843993186951, -0.996218979358673]+ , [0.996218979358673, 2.34843993186951, 0.996218979358673]+ , [1.02893996238708, 2.32382988929749, -1.02893996238708]+ , [1.02893996238708, 2.32382988929749, 1.02893996238708]+ , [1.03710997104645, 1.47186994552612, -1.53261995315552]+ , [1.03710997104645, 1.47186994552612, 1.53261995315552]+ , [1.04814994335175, 2.29979991912842, -0.709276974201202]+ , [1.04814994335175, 2.29979991912842, 0.709276974201202]+ , [1.05876004695892, 0.159961000084877, -1.28236997127533]+ , [1.05876004695892, 0.159961000084877, 1.28236997127533]+ , [1.05979001522064, 2.31943011283875, -0.577103972434998]+ , [1.05979001522064, 2.31943011283875, 0.577103972434998]+ , [1.06373000144958, 1.86093997955322, -1.28839004039764]+ , [1.06373000144958, 1.86093997955322, 1.28839004039764]+ , [1.06500005722046, 0, -1.06500005722046]+ , [1.06500005722046, 0, 1.06500005722046]+ , [1.06500005722046, 2.25, -1.06500005722046]+ , [1.06500005722046, 2.25, 1.06500005722046]+ , [1.06664001941681, 2.33906006813049, -0.453828006982803]+ , [1.06664001941681, 2.33906006813049, 0.453828006982803]+ , [1.07134997844696, 2.28691005706787, -0.884536981582642]+ , [1.07134997844696, 2.28691005706787, 0.884536981582642]+ , [1.07255005836487, 2.33612990379333, -0.885531008243561]+ , [1.07255005836487, 2.33612990379333, 0.885531008243561]+ , [1.08062994480133, 2.25, -0.731249988079071]+ , [1.08062994480133, 2.25, 0.731249988079071]+ , [1.08106005191803, 0.453516006469727, -1.59756004810333]+ , [1.08106005191803, 0.453516006469727, 1.59756004810333]+ , [1.09904003143311, 2.33612990379333, -0.907401978969574]+ , [1.09904003143311, 2.33612990379333, 0.907401978969574]+ , [1.10082995891571, 1.10038995742798, -1.62678003311157]+ , [1.10082995891571, 1.10038995742798, 1.62678003311157]+ , [1.11292004585266, 0.591650009155273, -1.64463996887207]+ , [1.11292004585266, 0.591650009155273, 1.64463996887207]+ , [1.12047004699707, 0.085547000169754, -1.12047004699707]+ , [1.12047004699707, 0.085547000169754, 1.12047004699707]+ , [1.12281000614166, 2.27489995956421, -0.611424028873444]+ , [1.12281000614166, 2.27489995956421, 0.611424028873444]+ , [1.125, 0.75, -1.66250002384186]+ , [1.125, 0.75, 1.66250002384186]+ , [1.13311994075775, 2.33906006813049, -0.235586002469063]+ , [1.13311994075775, 2.33906006813049, 0.235586002469063]+ , [1.13636994361877, 2.28691005706787, -0.938220024108887]+ , [1.13636994361877, 2.28691005706787, 0.938220024108887]+ , [1.14751994609833, 2.32382988929749, -0.776513993740082]+ , [1.14751994609833, 2.32382988929749, 0.776513993740082]+ , [1.14968001842499, 2.31943011283875, -0.360312014818192]+ , [1.14968001842499, 2.31943011283875, 0.360312014818192]+ , [1.15625, 2.33906006813049, 0]+ , [1.16322004795074, 2.29979991912842, -0.494917988777161]+ , [1.16322004795074, 2.29979991912842, 0.494917988777161]+ , [1.16375005245209, 2.25, -0.787500023841858]+ , [1.16375005245209, 2.25, 0.787500023841858]+ , [1.16635000705719, 2.34843993186951, -0.789258003234863]+ , [1.16635000705719, 2.34843993186951, 0.789258003234863]+ , [1.16812002658844, 0.343944996595383, -1.41481995582581]+ , [1.16812002658844, 0.343944996595383, 1.41481995582581]+ , [1.18604004383087, 0.042773000895977, -0.979228973388672]+ , [1.18604004383087, 0.042773000895977, 0.979228973388672]+ , [1.19650995731354, 2.31943011283875, -0.123125001788139]+ , [1.19650995731354, 2.31943011283875, 0.123125001788139]+ , [1.19924998283386, 2.25, -0.510249972343445]+ , [1.19924998283386, 2.25, 0.510249972343445]+ , [1.20466005802155, 2.32382988929749, -0.815186023712158]+ , [1.20466005802155, 2.32382988929749, 0.815186023712158]+ , [1.20912003517151, 1.28612995147705, -1.4644900560379]+ , [1.20912003517151, 1.28612995147705, 1.4644900560379]+ , [1.21805000305176, 2.27489995956421, -0.381740003824234]+ , [1.21805000305176, 2.27489995956421, 0.381740003824234]+ , [1.21905994415283, 2.28691005706787, -0.663837015628815]+ , [1.21905994415283, 2.28691005706787, 0.663837015628815]+ , [1.22043001651764, 2.33612990379333, -0.664583027362823]+ , [1.22043001651764, 2.33612990379333, 0.664583027362823]+ , [1.235720038414, 2.29979991912842, -0.256915986537933]+ , [1.235720038414, 2.29979991912842, 0.256915986537933]+ , [1.24081003665924, 0.5225830078125, -1.50286996364594]+ , [1.24081003665924, 0.5225830078125, 1.50286996364594]+ , [1.24249994754791, 0.234375, -1.24249994754791]+ , [1.24249994754791, 0.234375, 1.24249994754791]+ , [1.24688005447388, 0, -0.84375]+ , [1.24688005447388, 0, 0.84375]+ , [1.24688005447388, 2.25, -0.84375]+ , [1.24688005447388, 2.25, 0.84375]+ , [1.25057005882263, 2.33612990379333, -0.680997014045715]+ , [1.25057005882263, 2.33612990379333, 0.680997014045715]+ , [1.25882995128632, 0.925194978713989, -1.52469003200531]+ , [1.25882995128632, 0.925194978713989, 1.52469003200531]+ , [1.26093995571136, 2.29979991912842, 0]+ , [1.26566994190216, 0.670825004577637, -1.53296995162964]+ , [1.26566994190216, 0.670825004577637, 1.53296995162964]+ , [1.26766002178192, 2.27489995956421, -0.130447998642921]+ , [1.26766002178192, 2.27489995956421, 0.130447998642921]+ , [1.27348005771637, 2.32382988929749, -0.541833996772766]+ , [1.27348005771637, 2.32382988929749, 0.541833996772766]+ , [1.27400004863739, 2.25, -0.264874994754791]+ , [1.27400004863739, 2.25, 0.264874994754791]+ , [1.28236997127533, 0.159961000084877, -1.05876004695892]+ , [1.28236997127533, 0.159961000084877, 1.05876004695892]+ , [1.28839004039764, 1.86093997955322, -1.06373000144958]+ , [1.28839004039764, 1.86093997955322, 1.06373000144958]+ , [1.29149997234344, 2.25, -0.549499988555908]+ , [1.29149997234344, 2.25, 0.549499988555908]+ , [1.29305005073547, 2.28691005706787, -0.704126000404358]+ , [1.29305005073547, 2.28691005706787, 0.704126000404358]+ , [1.2943799495697, 2.34843993186951, -0.550727009773254]+ , [1.2943799495697, 2.34843993186951, 0.550727009773254]+ , [1.29999995231628, 2.25, 0]+ , [1.30905997753143, 1.47186994552612, -1.30905997753143]+ , [1.30905997753143, 1.47186994552612, 1.30905997753143]+ , [1.3118200302124, 0.085547000169754, -0.887695014476776]+ , [1.3118200302124, 0.085547000169754, 0.887695014476776]+ , [1.32246005535126, 2.28691005706787, -0.414463996887207]+ , [1.32246005535126, 2.28691005706787, 0.414463996887207]+ , [1.32395005226135, 2.33612990379333, -0.414929002523422]+ , [1.32395005226135, 2.33612990379333, 0.414929002523422]+ , [1.33689999580383, 2.32382988929749, -0.568817973136902]+ , [1.33689999580383, 2.32382988929749, 0.568817973136902]+ , [1.34957003593445, 0.042773000895977, -0.734902024269104]+ , [1.34957003593445, 0.042773000895977, 0.734902024269104]+ , [1.35285997390747, 2.32382988929749, -0.281271010637283]+ , [1.35285997390747, 2.32382988929749, 0.281271010637283]+ , [1.35664999485016, 2.33612990379333, -0.425177007913589]+ , [1.35664999485016, 2.33612990379333, 0.425177007913589]+ , [1.36452996730804, 0.453516006469727, -1.36452996730804]+ , [1.36452996730804, 0.453516006469727, 1.36452996730804]+ , [1.37199997901917, 2.25, -0.285250008106232]+ , [1.37199997901917, 2.25, 0.285250008106232]+ , [1.37505996227264, 2.34843993186951, -0.285887002944946]+ , [1.37505996227264, 2.34843993186951, 0.285887002944946]+ , [1.37633001804352, 2.28691005706787, -0.141629993915558]+ , [1.37633001804352, 2.28691005706787, 0.141629993915558]+ , [1.37787997722626, 2.33612990379333, -0.141789004206657]+ , [1.37787997722626, 2.33612990379333, 0.141789004206657]+ , [1.38047003746033, 2.32382988929749, 0]+ , [1.38374996185303, 0, -0.588750004768372]+ , [1.38374996185303, 0, 0.588750004768372]+ , [1.38374996185303, 2.25, -0.588750004768372]+ , [1.38374996185303, 2.25, 0.588750004768372]+ , [1.38949000835419, 1.10038995742798, -1.38949000835419]+ , [1.38949000835419, 1.10038995742798, 1.38949000835419]+ , [1.39999997615814, 2.25, 0]+ , [1.40271997451782, 2.28691005706787, -0.439617991447449]+ , [1.40271997451782, 2.28691005706787, 0.439617991447449]+ , [1.40313005447388, 2.34843993186951, 0]+ , [1.40474998950958, 0.591650009155273, -1.40474998950958]+ , [1.40474998950958, 0.591650009155273, 1.40474998950958]+ , [1.41191005706787, 2.33612990379333, -0.14529100060463]+ , [1.41191005706787, 2.33612990379333, 0.14529100060463]+ , [1.41481995582581, 0.343944996595383, -1.16812002658844]+ , [1.41481995582581, 0.343944996595383, 1.16812002658844]+ , [1.41999995708466, 0.75, -1.41999995708466]+ , [1.41999995708466, 0.75, 1.41999995708466]+ , [1.42023003101349, 2.32382988929749, -0.295278012752533]+ , [1.42023003101349, 2.32382988929749, 0.295278012752533]+ , [1.4492199420929, 2.32382988929749, 0]+ , [1.45468997955322, 0.234375, -0.984375]+ , [1.45468997955322, 0.234375, 0.984375]+ , [1.45581996440887, 0.085547000169754, -0.61941397190094]+ , [1.45581996440887, 0.085547000169754, 0.61941397190094]+ , [1.45916998386383, 0.159961000084877, -0.794589996337891]+ , [1.45916998386383, 0.159961000084877, 0.794589996337891]+ , [1.45985996723175, 2.28691005706787, -0.15022599697113]+ , [1.45985996723175, 2.28691005706787, 0.15022599697113]+ , [1.46403002738953, 0.042773000895977, -0.458833009004593]+ , [1.46403002738953, 0.042773000895977, 0.458833009004593]+ , [1.4644900560379, 1.28612995147705, -1.20912003517151]+ , [1.4644900560379, 1.28612995147705, 1.20912003517151]+ , [1.46601998806, 1.86093997955322, -0.79831999540329]+ , [1.46601998806, 1.86093997955322, 0.79831999540329]+ , [1.47000002861023, 0, -0.30562499165535]+ , [1.47000002861023, 0, 0.30562499165535]+ , [1.47000002861023, 2.25, -0.30562499165535]+ , [1.47000002861023, 2.25, 0.30562499165535]+ , [1.5, 0, 0]+ , [1.5, 2.25, 0]+ , [1.50286996364594, 0.5225830078125, -1.24081003665924]+ , [1.50286996364594, 0.5225830078125, 1.24081003665924]+ , [1.52366995811462, 0.042773000895977, -0.156791999936104]+ , [1.52366995811462, 0.042773000895977, 0.156791999936104]+ , [1.52469003200531, 0.925194978713989, -1.25882995128632]+ , [1.52469003200531, 0.925194978713989, 1.25882995128632]+ , [1.53261995315552, 1.47186994552612, -1.03710997104645]+ , [1.53261995315552, 1.47186994552612, 1.03710997104645]+ , [1.53296995162964, 0.670825004577637, -1.26566994190216]+ , [1.53296995162964, 0.670825004577637, 1.26566994190216]+ , [1.54656004905701, 0.085547000169754, -0.321543008089066]+ , [1.54656004905701, 0.085547000169754, 0.321543008089066]+ , [1.57813000679016, 0.085547000169754, 0]+ , [1.58293998241425, 0.159961000084877, -0.49609899520874]+ , [1.58293998241425, 0.159961000084877, 0.49609899520874]+ , [1.59037005901337, 1.86093997955322, -0.498427987098694]+ , [1.59037005901337, 1.86093997955322, 0.498427987098694]+ , [1.59756004810333, 0.453516006469727, -1.08106005191803]+ , [1.59756004810333, 0.453516006469727, 1.08106005191803]+ , [1.60988998413086, 0.343944996595383, -0.876659989356995]+ , [1.60988998413086, 0.343944996595383, 0.876659989356995]+ , [1.61436998844147, 0.234375, -0.686874985694885]+ , [1.61436998844147, 0.234375, 0.686874985694885]+ , [1.62678003311157, 1.10038995742798, -1.10082995891571]+ , [1.62678003311157, 1.10038995742798, 1.10082995891571]+ , [1.64463996887207, 0.591650009155273, -1.11292004585266]+ , [1.64463996887207, 0.591650009155273, 1.11292004585266]+ , [1.64742004871368, 0.159961000084877, -0.169525995850563]+ , [1.64742004871368, 0.159961000084877, 0.169525995850563]+ , [1.65515995025635, 1.86093997955322, -0.170322000980377]+ , [1.65515995025635, 1.86093997955322, 0.170322000980377]+ , [1.66250002384186, 0.75, -1.125]+ , [1.66250002384186, 0.75, 1.125]+ , [1.66639995574951, 1.28612995147705, -0.907437026500702]+ , [1.66639995574951, 1.28612995147705, 0.907437026500702]+ , [1.70000004768372, 0.449999988079071, 0]+ , [1.70000004768372, 0.485448986291885, -0.216563001275063]+ , [1.70000004768372, 0.485448986291885, 0.216563001275063]+ , [1.70000004768372, 0.578905999660492, -0.371250003576279]+ , [1.70000004768372, 0.578905999660492, 0.371250003576279]+ , [1.70000004768372, 0.711035013198853, -0.464062988758087]+ , [1.70000004768372, 0.711035013198853, 0.464062988758087]+ , [1.70000004768372, 0.862500011920929, -0.495000004768372]+ , [1.70000004768372, 0.862500011920929, 0.495000004768372]+ , [1.70000004768372, 1.01397001743317, -0.464062988758087]+ , [1.70000004768372, 1.01397001743317, 0.464062988758087]+ , [1.70000004768372, 1.14609003067017, -0.371250003576279]+ , [1.70000004768372, 1.14609003067017, 0.371250003576279]+ , [1.70000004768372, 1.23954999446869, -0.216563001275063]+ , [1.70000004768372, 1.23954999446869, 0.216563001275063]+ , [1.70000004768372, 1.27499997615814, 0]+ , [1.70086002349854, 1.47186994552612, -0.723671972751617]+ , [1.70086002349854, 1.47186994552612, 0.723671972751617]+ , [1.71008002758026, 0.5225830078125, -0.931218028068542]+ , [1.71008002758026, 0.5225830078125, 0.931218028068542]+ , [1.7150000333786, 0.234375, -0.356561988592148]+ , [1.7150000333786, 0.234375, 0.356563001871109]+ , [1.7349100112915, 0.925194978713989, -0.944741010665894]+ , [1.7349100112915, 0.925194978713989, 0.944741010665894]+ , [1.7443300485611, 0.670825004577637, -0.949871003627777]+ , [1.7443300485611, 0.670825004577637, 0.949871003627777]+ , [1.74644005298615, 0.343944996595383, -0.547339022159576]+ , [1.74644005298615, 0.343944996595383, 0.547339022159576]+ , [1.75, 0.234375, 0]+ , [1.77293002605438, 0.453516006469727, -0.754335999488831]+ , [1.77293002605438, 0.453516006469727, 0.754335999488831]+ , [1.80535995960236, 1.10038995742798, -0.768135011196136]+ , [1.80535995960236, 1.10038995742798, 0.768135011196136]+ , [1.8068699836731, 1.47186994552612, -0.375663995742798]+ , [1.8068699836731, 1.47186994552612, 0.375663995742798]+ , [1.80774998664856, 1.28612995147705, -0.566554009914398]+ , [1.80774998664856, 1.28612995147705, 0.566554009914398]+ , [1.80868005752563, 0.669439971446991, -0.41533499956131]+ , [1.80868005752563, 0.669439971446991, 0.41533499956131]+ , [1.81523001194, 0.556497991085052, -0.292881011962891]+ , [1.81523001194, 0.556497991085052, 0.292881011962891]+ , [1.81757998466492, 0.343944996595383, -0.187035992741585]+ , [1.81757998466492, 0.343944996595383, 0.187035992741585]+ , [1.81850004196167, 0.493822991847992, -0.107904002070427]+ , [1.81850004196167, 0.493822991847992, 0.107904002070427]+ , [1.82518005371094, 0.591650009155273, -0.776566982269287]+ , [1.82518005371094, 0.591650009155273, 0.776566982269287]+ , [1.84375, 1.47186994552612, 0]+ , [1.84407997131348, 1.2731100320816, -0.106835998594761]+ , [1.84407997131348, 1.2731100320816, 0.106835998594761]+ , [1.84500002861023, 0.75, -0.785000026226044]+ , [1.84500002861023, 0.75, 0.785000026226044]+ , [1.8498899936676, 1.21245002746582, -0.289983987808228]+ , [1.8498899936676, 1.21245002746582, 0.289983987808228]+ , [1.85511994361877, 0.5225830078125, -0.581402003765106]+ , [1.85511994361877, 0.5225830078125, 0.581402003765106]+ , [1.86006999015808, 1.10627996921539, -0.412081986665726]+ , [1.86006999015808, 1.10627996921539, 0.412081986665726]+ , [1.87285995483398, 0.972819983959198, -0.473131000995636]+ , [1.87285995483398, 0.972819983959198, 0.473131000995636]+ , [1.88138997554779, 1.28612995147705, -0.193601995706558]+ , [1.88138997554779, 1.28612995147705, 0.193601995706558]+ , [1.88206005096436, 0.925194978713989, -0.58984500169754]+ , [1.88206005096436, 0.925194978713989, 0.58984500169754]+ , [1.8834400177002, 0.453516006469727, -0.391582012176514]+ , [1.8834400177002, 0.453516006469727, 0.391582012176514]+ , [1.88652002811432, 0.830256998538971, -0.473131000995636]+ , [1.88652002811432, 0.830256998538971, 0.473131000995636]+ , [1.89227998256683, 0.670825004577637, -0.593047022819519]+ , [1.89227998256683, 0.670825004577637, 0.593047022819519]+ , [1.90898001194, 0.762850999832153, -0.457367986440659]+ , [1.90898001194, 0.762850999832153, 0.457367986440659]+ , [1.91788995265961, 1.10038995742798, -0.398745000362396]+ , [1.91788995265961, 1.10038995742798, 0.398745000362396]+ , [1.92188000679016, 0.453516006469727, 0]+ , [1.92571997642517, 0.624967992305756, -0.368660002946854]+ , [1.92571997642517, 0.624967992305756, 0.368660002946854]+ , [1.93069005012512, 0.5225830078125, -0.198676005005836]+ , [1.93069005012512, 0.5225830078125, 0.198676005005836]+ , [1.93519997596741, 0.536666989326477, -0.215051993727684]+ , [1.93519997596741, 0.536666989326477, 0.215051993727684]+ , [1.93878996372223, 0.503174006938934, 0]+ , [1.93894994258881, 0.591650009155273, -0.403122991323471]+ , [1.93894994258881, 0.591650009155273, 0.403122991323471]+ , [1.9570300579071, 1.10038995742798, 0]+ , [1.9587299823761, 0.925194978713989, -0.201561003923416]+ , [1.9587299823761, 0.925194978713989, 0.201561003923416]+ , [1.96000003814697, 0.75, -0.407499998807907]+ , [1.96000003814697, 0.75, 0.407499998807907]+ , [1.96937000751495, 0.670825004577637, -0.202656000852585]+ , [1.96937000751495, 0.670825004577637, 0.202656000852585]+ , [1.97852003574371, 0.591650009155273, 0]+ , [1.98495995998383, 1.30458998680115, 0]+ , [1.99135994911194, 1.27330994606018, -0.210782006382942]+ , [1.99135994911194, 1.27330994606018, 0.210782006382942]+ , [2, 0.75, 0]+ , [2.00798988342285, 0.721262991428375, -0.409761011600494]+ , [2.00798988342285, 0.721262991428375, 0.409761011600494]+ , [2.00820994377136, 1.19084000587463, -0.36133998632431]+ , [2.00820994377136, 1.19084000587463, 0.36133998632431]+ , [2.02470993995667, 0.614948987960815, -0.288958013057709]+ , [2.02470993995667, 0.614948987960815, 0.288958013057709]+ , [2.03204989433289, 1.07423996925354, -0.451674997806549]+ , [2.03204989433289, 1.07423996925354, 0.451674997806549]+ , [2.03379011154175, 0.556061983108521, -0.106458000838757]+ , [2.03379011154175, 0.556061983108521, 0.106458000838757]+ , [2.05938005447388, 0.940576016902924, -0.481786996126175]+ , [2.05938005447388, 0.940576016902924, 0.481786996126175]+ , [2.08644008636475, 1.33047997951508, -0.101580999791622]+ , [2.08644008636475, 1.33047997951508, 0.101580999791622]+ , [2.08669996261597, 0.806914985179901, -0.451674997806549]+ , [2.08669996261597, 0.806914985179901, 0.451674997806549]+ , [2.10140991210938, 1.27814996242523, -0.275720000267029]+ , [2.10140991210938, 1.27814996242523, 0.275720000267029]+ , [2.11052989959717, 0.69031697511673, -0.36133998632431]+ , [2.11052989959717, 0.69031697511673, 0.36133998632431]+ , [2.12738990783691, 0.60784500837326, -0.210782006382942]+ , [2.12738990783691, 0.60784500837326, 0.210782006382942]+ , [2.1275999546051, 1.18656003475189, -0.39181199669838]+ , [2.1275999546051, 1.18656003475189, 0.39181199669838]+ , [2.13379001617432, 0.576563000679016, 0]+ , [2.16054010391235, 1.07142996788025, -0.449858993291855]+ , [2.16054010391235, 1.07142996788025, 0.449858993291855]+ , [2.16921997070312, 0.790259003639221, -0.399360001087189]+ , [2.16921997070312, 0.790259003639221, 0.399360001087189]+ , [2.17968988418579, 1.38515996932983, 0]+ , [2.1897599697113, 1.35887002944946, -0.195541992783546]+ , [2.1897599697113, 1.35887002944946, 0.195541992783546]+ , [2.19480991363525, 0.691761016845703, -0.281558990478516]+ , [2.19480991363525, 0.691761016845703, 0.281558990478516]+ , [2.19570994377136, 0.948444008827209, -0.449858993291855]+ , [2.19570994377136, 0.948444008827209, 0.449858993291855]+ , [2.20836997032166, 0.637081980705261, -0.103731997311115]+ , [2.20836997032166, 0.637081980705261, 0.103731997311115]+ , [2.21631002426147, 1.28956997394562, -0.335215002298355]+ , [2.21631002426147, 1.28956997394562, 0.335215002298355]+ , [2.2202000617981, 0.891314029693604, -0.434457004070282]+ , [2.2202000617981, 0.891314029693604, 0.434457004070282]+ , [2.24856996536255, 1.43299996852875, -0.0923840031027794]+ , [2.24856996536255, 1.43299996852875, 0.0923840031027794]+ , [2.25383996963501, 1.19159996509552, -0.419019013643265]+ , [2.25383996963501, 1.19159996509552, 0.419019013643265]+ , [2.25943994522095, 0.772489011287689, -0.349967002868652]+ , [2.25943994522095, 0.772489011287689, 0.349967002868652]+ , [2.26856994628906, 1.39015996456146, -0.250757992267609]+ , [2.26856994628906, 1.39015996456146, 0.250757992267609]+ , [2.28188991546631, 0.696393013000488, -0.20414699614048]+ , [2.28188991546631, 0.696393013000488, 0.20414699614048]+ , [2.29041004180908, 0.667528986930847, 0]+ , [2.29688000679016, 1.0793000459671, -0.446952998638153]+ , [2.29688000679016, 1.0793000459671, 0.446952998638153]+ , [2.29924988746643, 0.874952971935272, -0.384663999080658]+ , [2.29924988746643, 0.874952971935272, 0.384663999080658]+ , [2.30358004570007, 1.31519997119904, -0.356339991092682]+ , [2.30358004570007, 1.31519997119904, 0.356339991092682]+ , [2.30644011497498, 1.50440001487732, 0]+ , [2.31838011741638, 1.48355996608734, -0.17399600148201]+ , [2.31838011741638, 1.48355996608734, 0.17399600148201]+ , [2.33068990707397, 0.784406006336212, -0.271218001842499]+ , [2.33068990707397, 0.784406006336212, 0.271218001842499]+ , [2.33991003036499, 0.966988980770111, -0.419019013643265]+ , [2.33991003036499, 0.966988980770111, 0.419019013643265]+ , [2.34758996963501, 0.734270989894867, -0.0999220013618469]+ , [2.34758996963501, 0.734270989894867, 0.0999220013618469]+ , [2.34758996963501, 1.22096002101898, -0.409130990505219]+ , [2.34758996963501, 1.22096002101898, 0.409130990505219]+ , [2.34983992576599, 1.42864000797272, -0.298278987407684]+ , [2.34983992576599, 1.42864000797272, 0.298278987407684]+ , [2.35317993164062, 1.56816005706787, -0.0808229967951775]+ , [2.35317993164062, 1.56816005706787, 0.0808229967951775]+ , [2.37575006484985, 1.53531002998352, -0.219376996159554]+ , [2.37575006484985, 1.53531002998352, 0.219376996159554]+ , [2.37743997573853, 0.869018971920013, -0.335215002298355]+ , [2.37743997573853, 0.869018971920013, 0.335215002298355]+ , [2.38750004768372, 1.64999997615814, 0]+ , [2.39432001113892, 1.35098004341125, -0.372848987579346]+ , [2.39432001113892, 1.35098004341125, 0.372848987579346]+ , [2.39459991455078, 1.12030005455017, -0.409130990505219]+ , [2.39459991455078, 1.12030005455017, 0.409130990505219]+ , [2.40038990974426, 1.63469004631042, -0.149296998977661]+ , [2.40038990974426, 1.63469004631042, 0.149296998977661]+ , [2.4039900302887, 0.799722015857697, -0.195541992783546]+ , [2.4039900302887, 0.799722015857697, 0.195541992783546]+ , [2.41406011581421, 0.773437976837158, 0]+ , [2.41524004936218, 1.47781002521515, -0.311747014522552]+ , [2.41524004936218, 1.47781002521515, 0.311747014522552]+ , [2.43438005447388, 1.59433996677399, -0.255937993526459]+ , [2.43438005447388, 1.59433996677399, 0.255937993526459]+ , [2.4386100769043, 1.02605998516083, -0.356339991092682]+ , [2.4386100769043, 1.02605998516083, 0.356339991092682]+ , [2.44531011581421, 1.26196002960205, -0.397704988718033]+ , [2.44531011581421, 1.26196002960205, 0.397704988718033]+ , [2.45167994499207, 1.805340051651, -0.0630870014429092]+ , [2.45167994499207, 1.805340051651, 0.0630870014429092]+ , [2.46489000320435, 1.40551996231079, -0.357930988073349]+ , [2.46489000320435, 1.40551996231079, 0.357930988073349]+ , [2.47361993789673, 0.95109897851944, -0.250757992267609]+ , [2.47361993789673, 0.95109897851944, 0.250757992267609]+ , [2.47767996788025, 1.78638005256653, -0.171237006783485]+ , [2.47767996788025, 1.78638005256653, 0.171237006783485]+ , [2.48241996765137, 1.53727996349335, -0.319922000169754]+ , [2.48241996765137, 1.53727996349335, 0.319922000169754]+ , [2.49361991882324, 0.908263981342316, -0.0923840031027794]+ , [2.49361991882324, 0.908263981342316, 0.0923840031027794]+ , [2.49629998207092, 1.17295002937317, -0.372848987579346]+ , [2.49629998207092, 1.17295002937317, 0.372848987579346]+ , [2.50155997276306, 1.97108995914459, 0]+ , [2.5172700881958, 1.9655499458313, -0.103051997721195]+ , [2.5172700881958, 1.9655499458313, 0.103051997721195]+ , [2.51792001724243, 1.32831001281738, -0.357930988073349]+ , [2.51792001724243, 1.32831001281738, 0.357930988073349]+ , [2.52318000793457, 1.75321996212006, -0.243336006999016]+ , [2.52318000793457, 1.75321996212006, 0.243336006999016]+ , [2.53749990463257, 1.47186994552612, -0.341250002384186]+ , [2.53749990463257, 1.47186994552612, 0.341250002384186]+ , [2.54078006744385, 1.09528994560242, -0.298278987407684]+ , [2.54078006744385, 1.09528994560242, 0.298278987407684]+ , [2.5491099357605, 2.0446400642395, -0.047715999186039]+ , [2.5491099357605, 2.0446400642395, 0.047715999186039]+ , [2.55869007110596, 1.95095002651215, -0.176660001277924]+ , [2.55869007110596, 1.95095002651215, 0.176660001277924]+ , [2.56756997108459, 1.25602996349335, -0.311747014522552]+ , [2.56756997108459, 1.25602996349335, 0.311747014522552]+ , [2.57224988937378, 1.04035997390747, -0.17399600148201]+ , [2.57224988937378, 1.04035997390747, 0.17399600148201]+ , [2.57909989356995, 2.1219699382782, 0]+ , [2.58038997650146, 1.71152997016907, -0.279386013746262]+ , [2.58038997650146, 1.71152997016907, 0.279386013746262]+ , [2.58101010322571, 2.0377299785614, -0.129515007138252]+ , [2.58101010322571, 2.0377299785614, 0.129515007138252]+ , [2.58418011665344, 1.0195300579071, 0]+ , [2.59258008003235, 1.40646994113922, -0.319922000169754]+ , [2.59258008003235, 1.40646994113922, 0.319922000169754]+ , [2.59848999977112, 2.11992001533508, -0.0878119990229607]+ , [2.59848999977112, 2.11992001533508, 0.0878119990229607]+ , [2.60177993774414, 1.55472004413605, -0.304019004106522]+ , [2.60177993774414, 1.55472004413605, 0.304019004106522]+ , [2.60706996917725, 1.19852995872498, -0.219376996159554]+ , [2.60706996917725, 1.19852995872498, 0.219376996159554]+ , [2.61161994934082, 1.69128000736237, -0.287907987833023]+ , [2.61161994934082, 1.69128000736237, 0.287907987833023]+ , [2.61724996566772, 1.93031001091003, -0.220825001597404]+ , [2.61724996566772, 1.93031001091003, 0.220825001597404]+ , [2.62963008880615, 1.16568005084991, -0.0808229967951775]+ , [2.62963008880615, 1.16568005084991, 0.0808229967951775]+ , [2.6378800868988, 2.02554988861084, -0.180818006396294]+ , [2.6378800868988, 2.02554988861084, 0.180818006396294]+ , [2.64063000679016, 1.34941005706787, -0.255937993526459]+ , [2.64063000679016, 1.34941005706787, 0.255937993526459]+ , [2.6496000289917, 2.11451005935669, -0.150535002350807]+ , [2.6496000289917, 2.11451005935669, 0.150535002350807]+ , [2.65084004402161, 2.18547010421753, -0.0424610003829002]+ , [2.65084004402161, 2.18547010421753, 0.0424610003829002]+ , [2.65390992164612, 1.50419998168945, -0.264113008975983]+ , [2.65390992164612, 1.50419998168945, 0.264113008975983]+ , [2.6654200553894, 1.64925003051758, -0.266995012760162]+ , [2.6654200553894, 1.64925003051758, 0.266995012760162]+ , [2.67460989952087, 1.30905997753143, -0.149296998977661]+ , [2.67460989952087, 1.30905997753143, 0.149296998977661]+ , [2.67823004722595, 1.78253996372223, -0.252819001674652]+ , [2.67823004722595, 1.78253996372223, 0.252819001674652]+ , [2.68438005447388, 1.90664005279541, -0.235547006130219]+ , [2.68438005447388, 1.90664005279541, 0.235547006130219]+ , [2.6875, 1.29375004768372, 0]+ , [2.69190001487732, 2.18360996246338, -0.115250997245312]+ , [2.69190001487732, 2.18360996246338, 0.115250997245312]+ , [2.69644999504089, 1.46379995346069, -0.185856997966766]+ , [2.69644999504089, 1.46379995346069, 0.185856997966766]+ , [2.70000004768372, 2.25, 0]+ , [2.70808005332947, 2.01037001609802, -0.208084002137184]+ , [2.70808005332947, 2.01037001609802, 0.208084002137184]+ , [2.71703004837036, 1.61167001724243, -0.213596001267433]+ , [2.71703004837036, 1.61167001724243, 0.213596001267433]+ , [2.72076010704041, 1.44071996212006, -0.0684740021824837]+ , [2.72076010704041, 1.44071996212006, 0.0684740021824837]+ , [2.72578001022339, 2.25, -0.0820309966802597]+ , [2.72578001022339, 2.25, 0.0820309966802597]+ , [2.72599005699158, 2.10643005371094, -0.175249993801117]+ , [2.72599005699158, 2.10643005371094, 0.175249993801117]+ , [2.73600006103516, 1.75154995918274, -0.219519004225731]+ , [2.73600006103516, 1.75154995918274, 0.219519004225731]+ , [2.75021004676819, 2.26919007301331, -0.039733998477459]+ , [2.75021004676819, 2.26919007301331, 0.039733998477459]+ , [2.75149989128113, 1.8829699754715, -0.220825001597404]+ , [2.75149989128113, 1.8829699754715, 0.220825001597404]+ , [2.7535400390625, 1.58508002758026, -0.124597996473312]+ , [2.7535400390625, 1.58508002758026, 0.124597996473312]+ , [2.76737999916077, 1.57500004768372, 0]+ , [2.77555990219116, 2.28399991989136, 0]+ , [2.7809898853302, 1.9943699836731, -0.208084002137184]+ , [2.7809898853302, 1.9943699836731, 0.208084002137184]+ , [2.78303003311157, 1.72669994831085, -0.154476001858711]+ , [2.78303003311157, 1.72669994831085, 0.154476001858711]+ , [2.79375004768372, 2.25, -0.140625]+ , [2.79375004768372, 2.25, 0.140625]+ , [2.79782009124756, 2.27174997329712, -0.10784900188446]+ , [2.79782009124756, 2.27174997329712, 0.10784900188446]+ , [2.79948997497559, 2.29274988174438, -0.0769039988517761]+ , [2.79948997497559, 2.29274988174438, 0.0769039988517761]+ , [2.79999995231628, 2.25, 0]+ , [2.80468988418579, 2.09809994697571, -0.200712993741035]+ , [2.80468988418579, 2.09809994697571, 0.200712993741035]+ , [2.8099000453949, 1.71249997615814, -0.0569120012223721]+ , [2.8099000453949, 1.71249997615814, 0.0569120012223721]+ , [2.81006002426147, 1.86232995986938, -0.176660001277924]+ , [2.81006002426147, 1.86232995986938, 0.176660001277924]+ , [2.81201004981995, 2.17814993858337, -0.169843003153801]+ , [2.81201004981995, 2.17814993858337, 0.169843003153801]+ , [2.81274008750916, 2.29753994941711, -0.035631999373436]+ , [2.81274008750916, 2.29753994941711, 0.035631999373436]+ , [2.81718993186951, 2.25, -0.0492190010845661]+ , [2.81718993186951, 2.25, 0.0492190010845661]+ , [2.82500004768372, 2.30625009536743, 0]+ , [2.8301100730896, 2.27129006385803, -0.025891000404954]+ , [2.8301100730896, 2.27129006385803, 0.025891000404954]+ , [2.84063005447388, 2.29219007492065, 0]+ , [2.84478998184204, 2.29963994026184, -0.0299929995089769]+ , [2.84478998184204, 2.29963994026184, 0.0299929995089769]+ , [2.85091996192932, 2.30715990066528, -0.0656249970197678]+ , [2.85091996192932, 2.30715990066528, 0.0656249970197678]+ , [2.85118007659912, 1.97918999195099, -0.180818006396294]+ , [2.85118007659912, 1.97918999195099, 0.180818006396294]+ , [2.85148000717163, 1.84773004055023, -0.103051997721195]+ , [2.85148000717163, 1.84773004055023, 0.103051997721195]+ , [2.86048007011414, 2.30093002319336, -0.0967160016298294]+ , [2.86048007011414, 2.30093002319336, 0.0967160016298294]+ , [2.86249995231628, 2.25, -0.0843750014901161]+ , [2.86249995231628, 2.25, 0.0843750014901161]+ , [2.86262989044189, 2.29297995567322, -0.0543459989130497]+ , [2.86262989044189, 2.29297995567322, 0.0543459989130497]+ , [2.86574006080627, 2.27201008796692, -0.0702759996056557]+ , [2.86574006080627, 2.27201008796692, 0.0702759996056557]+ , [2.86718988418579, 1.84219002723694, 0]+ , [2.87227988243103, 2.29425001144409, -0.131835997104645]+ , [2.87227988243103, 2.29425001144409, 0.131835997104645]+ , [2.88338994979858, 2.08977007865906, -0.175249993801117]+ , [2.88338994979858, 2.08977007865906, 0.175249993801117]+ , [2.88836002349854, 2.30118989944458, -0.0814089998602867]+ , [2.88836002349854, 2.30118989944458, 0.0814089998602867]+ , [2.89826989173889, 2.17088007926941, -0.19438199698925]+ , [2.89826989173889, 2.17088007926941, 0.19438199698925]+ , [2.90805006027222, 1.96700000762939, -0.129515007138252]+ , [2.90805006027222, 1.96700000762939, 0.129515007138252]+ , [2.91923999786377, 2.30955004692078, -0.112499997019768]+ , [2.91923999786377, 2.30955004692078, 0.112499997019768]+ , [2.92063999176025, 2.29506993293762, -0.0931639969348907]+ , [2.92063999176025, 2.29506993293762, 0.0931639969348907]+ , [2.93279004096985, 2.13103008270264, -0.17221100628376]+ , [2.93279004096985, 2.13103008270264, 0.17221100628376]+ , [2.93980002403259, 2.27326011657715, -0.158935993909836]+ , [2.93980002403259, 2.27326011657715, 0.158935993909836]+ , [2.93996000289917, 1.96010005474091, -0.047715999186039]+ , [2.93996000289917, 1.96010005474091, 0.047715999186039]+ , [2.95977997779846, 2.08168005943298, -0.150535002350807]+ , [2.95977997779846, 2.08168005943298, 0.150535002350807]+ , [2.96994996070862, 2.27412009239197, -0.103564001619816]+ , [2.96994996070862, 2.27412009239197, 0.103564001619816]+ , [3, 2.25, -0.1875]+ , [3, 2.25, -0.112499997019768]+ , [3, 2.25, 0.112499997019768]+ , [3, 2.25, 0.1875]+ , [3.00281000137329, 2.30484008789062, -0.142528995871544]+ , [3.00281000137329, 2.30484008789062, 0.142528995871544]+ , [3.01089000701904, 2.07627010345459, -0.0878119990229607]+ , [3.01089000701904, 2.07627010345459, 0.0878119990229607]+ , [3.01577997207642, 2.30571007728577, -0.119970999658108]+ , [3.01577997207642, 2.30571007728577, 0.119970999658108]+ , [3.03027009963989, 2.0742199420929, 0]+ , [3.04150009155273, 2.12566995620728, -0.116276003420353]+ , [3.04150009155273, 2.12566995620728, 0.116276003420353]+ , [3.0432300567627, 2.2110800743103, -0.166430994868279]+ , [3.0432300567627, 2.2110800743103, 0.166430994868279]+ , [3.06841993331909, 2.17344999313354, -0.143215000629425]+ , [3.06841993331909, 2.17344999313354, 0.143215000629425]+ , [3.07928991317749, 2.12305998802185, -0.0428379997611046]+ , [3.07928991317749, 2.12305998802185, 0.0428379997611046]+ , [3.09315991401672, 2.29877996444702, -0.17578099668026]+ , [3.09315991401672, 2.29877996444702, 0.17578099668026]+ , [3.09667992591858, 2.30141997337341, -0.124219000339508]+ , [3.09667992591858, 2.30141997337341, 0.124219000339508]+ , [3.12655997276306, 2.31680011749268, -0.150000005960464]+ , [3.12655997276306, 2.31680011749268, 0.150000005960464]+ , [3.12671995162964, 2.2772901058197, -0.103564001619816]+ , [3.12671995162964, 2.2772901058197, 0.103564001619816]+ , [3.12690997123718, 2.17127990722656, -0.0835419967770576]+ , [3.12690997123718, 2.17127990722656, 0.0835419967770576]+ , [3.13750004768372, 2.25, -0.0843750014901161]+ , [3.13750004768372, 2.25, 0.0843750014901161]+ , [3.14910006523132, 2.17045998573303, 0]+ , [3.15336990356445, 2.27552008628845, -0.158935993909836]+ , [3.15336990356445, 2.27552008628845, 0.158935993909836]+ , [3.16895008087158, 2.21117997169495, -0.11235299706459]+ , [3.16895008087158, 2.21117997169495, 0.11235299706459]+ , [3.18281006813049, 2.25, -0.0492190010845661]+ , [3.18281006813049, 2.25, 0.0492190010845661]+ , [3.20000004768372, 2.25, 0]+ , [3.20624995231628, 2.25, -0.140625]+ , [3.20624995231628, 2.25, 0.140625]+ , [3.20745992660522, 2.31251001358032, -0.119970999658108]+ , [3.20745992660522, 2.31251001358032, 0.119970999658108]+ , [3.21255993843079, 2.21042990684509, -0.0413930006325245]+ , [3.21255993843079, 2.21042990684509, 0.0413930006325245]+ , [3.21691989898682, 2.31072998046875, -0.142528995871544]+ , [3.21691989898682, 2.31072998046875, 0.142528995871544]+ , [3.23094010353088, 2.27940011024475, -0.0702759996056557]+ , [3.23094010353088, 2.27940011024475, 0.0702759996056557]+ , [3.26724004745483, 2.2781400680542, -0.025891000404954]+ , [3.26724004745483, 2.2781400680542, 0.025891000404954]+ , [3.27272009849548, 2.30776000022888, -0.0931639969348907]+ , [3.27272009849548, 2.30776000022888, 0.0931639969348907]+ , [3.27421998977661, 2.25, -0.0820309966802597]+ , [3.27421998977661, 2.25, 0.0820309966802597]+ , [3.29534006118774, 2.2770299911499, -0.10784900188446]+ , [3.29534006118774, 2.2770299911499, 0.10784900188446]+ , [3.29999995231628, 2.25, 0]+ , [3.31404995918274, 2.30330991744995, -0.131835997104645]+ , [3.31404995918274, 2.30330991744995, 0.131835997104645]+ , [3.33072996139526, 2.30984997749329, -0.0543459989130497]+ , [3.33072996139526, 2.30984997749329, 0.0543459989130497]+ , [3.33388996124268, 2.324049949646, -0.112499997019768]+ , [3.33388996124268, 2.324049949646, 0.112499997019768]+ , [3.33488988876343, 2.31701993942261, -0.0814089998602867]+ , [3.33488988876343, 2.31701993942261, 0.0814089998602867]+ , [3.34236001968384, 2.2800600528717, -0.039733998477459]+ , [3.34236001968384, 2.2800600528717, 0.039733998477459]+ , [3.35542988777161, 2.30270004272461, 0]+ , [3.35925006866455, 2.31465005874634, -0.0967160016298294]+ , [3.35925006866455, 2.31465005874634, 0.0967160016298294]+ , [3.37912011146545, 2.31658005714417, -0.0299929995089769]+ , [3.37912011146545, 2.31658005714417, 0.0299929995089769]+ , [3.38684010505676, 2.30481004714966, -0.0769039988517761]+ , [3.38684010505676, 2.30481004714966, 0.0769039988517761]+ , [3.40220999717712, 2.32644009590149, -0.0656249970197678]+ , [3.40220999717712, 2.32644009590149, 0.0656249970197678]+ , [3.40638995170593, 2.31850004196167, -0.035631999373436]+ , [3.40638995170593, 2.31850004196167, 0.035631999373436]+ , [3.40838003158569, 2.31542992591858, 0]+ , [3.42811989784241, 2.32733988761902, 0] ]++voronoiCellTeapot :: [[Double]]+voronoiCellTeapot =+ [0, 1.5, 0] :+ [+ [ -0.18667466541973257+ , 0.5968790484510281+ , 0.5717139272637414+ ]+ , [ -0.15441948465435382+ , 0.7326922941987842+ , 0.7170049842821647+ ]+ , [ -1.7990621802658653e-2+ , 1.9860825589150588e-2+ , 1.7990621802654917e-2+ ]+ , [ -0.29182490099196906+ , 0.7297971305995979+ , 0.6710598534195158+ ]+ , [ -6.448622334243023e-2+ , 0.5851752797903265+ , 0.5851752797903262+ ]+ , [ -0.1624173204710072 , 0.7759039565328439 , 0.7575092969306046 ]+ , [ -2.3194160942496607e-2+ , 2.6785629054845606e-2+ , 2.3194160942459883e-2+ ]+ , [ -0.39216297275000733+ , 0.6074273900130303+ , 0.4720306023200158+ ]+ , [ -2.215103618775975e-2+ , 2.5331770412981847e-2+ , 2.215103618775975e-2+ ]+ , [ -0.4720306023200158+ , 0.6074273900130303+ , 0.39216297275000733+ ]+ , [ -0.1228050514414682 , 0.8753822495491728 , 0.8567258893994795 ]+ , [ -0.23969991808709246+ , 0.8768406254951662+ , 0.8337562524679074+ ]+ , [ 0.7805147235214109+ , 0.8624999821186053+ , 4.365935118304741e-2+ ]+ , [ 0.863072608376728 , 1.4862706190912907 , 0.14576662495052128 ]+ , [ 0.7805147235214106+ , 0.8624999821186055+ , -4.36593511830482e-2+ ]+ , [ 0.7805144198006899+ , 0.8624994903802878+ , 4.366114397309236e-2+ ]+ , [ 0.7740595934432167 , 0.862499490380288 , 9.433094929942933e-2 ]+ , [ 0.8122497597351315 , 1.3634301491185252 , 0.39698702598827496 ]+ , [ 0.7740579104368239 , 0.8624980151653314 , 9.43372496836588e-2 ]+ , [ 2.3194160942461885e-2+ , 2.6785629054845606e-2+ , 2.319416094249719e-2+ ]+ , [ 0.4720306023200158 , 0.6074273900130303 , 0.39216297275000733 ]+ , [ 2.215103618775549e-2+ , 2.5331770412978624e-2+ , 2.2151036187755824e-2+ ]+ , [ 0.39216297275000733 , 0.6074273900130303 , 0.4720306023200158 ]+ , [ -0.18893822974950683+ , 0.9120771417338428+ , 0.8765366554024406+ ]+ , [ -0.15370753430700942+ , 0.9074581172580759+ , 0.8796369493525763+ ]+ , [ -0.19282328992414305+ , 1.0886815800201057+ , 0.8962126634940768+ ]+ , [ -0.21959663365004273 , 0.90801535760842 , 0.8664476852626979 ]+ , [ 0.6553927028305585 , 0.6599214690464071 , 7.68209537002328e-2 ]+ , [ 0.6480770038782534+ , 0.6480770038782535+ , 3.783067537459344e-2+ ]+ , [ 0.6480770038782537+ , 0.6480770038782535+ , -3.7830675374592546e-2+ ]+ , [ 0.5851752797903262+ , 0.5851752797903265+ , 6.448622334243026e-2+ ]+ , [ 0.6500310589666277 , 0.6577486876432921 , 0.10873036208745468 ]+ , [ 0.7710778571614446 , 0.862498015165331 , 0.10798328513535715 ]+ , [ 0.6540866269118542 , 0.6844355992166629 , 0.20191695153447142 ]+ , [ -0.3246126189761304 , 0.9080154562527455 , 0.8335353871385288 ]+ , [ -0.289831893259673 , 0.8768408129798988 , 0.818044872661731 ]+ , [ -9.869506660256815e-2+ , 1.1894267902510014+ , 0.9173511809757207+ ]+ , [ -0.283130810168734 , 1.1814109441614336 , 0.8788805908385666 ]+ , [ -0.28347840420931564+ , 1.2593473684859822+ , 0.8800216675702618+ ]+ , [ -0.36590860085651855+ , 1.0902530151825576+ , 0.8421531448806922+ ]+ , [ 9.87971813131522e-2 , 1.257602275825742 , 0.9183913329068394 ]+ , [ 0.1916029836825906 , 1.29931244670657 , 0.8997455590151622 ]+ , [ 9.86950666025681e-2 , 1.1894267902510014 , 0.9173511809757205 ]+ , [ 0.0 , 1.3011746498142653 , 0.9190561156885906 ]+ , [ -9.879718131315218e-2+ , 1.2576022758257417+ , 0.9183913329068394+ ]+ , [ -0.1916029836825906 , 1.29931244670657 , 0.8997455590151622 ]+ , [ 0.0 , 1.0862351104914478 , 0.9157767877158491 ]+ , [ 0.0 , 1.4739386059327377 , 0.8813815581455101 ]+ , [ -3.497482895265056e-2+ , 0.9074580789365942+ , 0.8918549800719824+ ]+ , [ -6.080525584912345e-2 , 0.8753821429287596 , 0.86310582524099 ]+ , [ 7.812642760551707e-3+ , 7.812642760551707e-3+ , 7.812642760550714e-3+ ]+ , [ 0.6491833829319444 , 0.6692949179238175 , 0.1694761444119623 ]+ , [ -2.7755575615628914e-17+ , 0.7373497776866849+ , 0.7373497776866843+ ]+ , [ -7.812642760551686e-3+ , 7.812642760551715e-3+ , 7.812642760551602e-3+ ]+ , [ -0.3581881925466474 , 0.9124253481133149 , 0.8235344289369642 ]+ , [ -0.4468960748716194 , 1.1765076558719516 , 0.8091216099094026 ]+ , [ -0.4474984305598345 , 1.2598574705147236 , 0.8102438227139427 ]+ , [ -0.36541277208493106+ , 1.2970587028449376+ , 0.8457844093935878+ ]+ , [ -0.5194379814088046 , 1.2957898827631247 , 0.7622224951613341 ]+ , [ -0.518569427022487 , 1.0903982826657406 , 0.7590404339629727 ]+ , [ -0.5851752797903265+ , 0.5851752797903264+ , 6.448622334243026e-2+ ]+ , [ -7.812642760551698e-3+ , 7.81264276055331e-3+ , 7.812642760553379e-3+ ]+ , [ -0.7170049842821663 , 0.732692294198784 , 0.1544194846543815 ]+ , [ -0.7373497776866849+ , 0.7373497776866845+ , -2.7755575615628914e-17+ ]+ , [ -0.5717139272637514+ , 0.5968790484510277+ , 0.18667466541971128+ ]+ , [ -0.6710598534195159+ , 0.7297971305995982+ , 0.29182490099196917+ ]+ , [ -1.7990621802656183e-2+ , 1.9860825589150588e-2+ , 1.7990621802655427e-2+ ]+ , [ -0.7575092969305982+ , 0.7759039565328366+ , 0.16241732047101434+ ]+ , [ -0.532913153617887 , 0.6041132479674722 , 0.2962999185904398 ]+ , [ -0.6045930611639508+ , 0.7294381763744873+ , 0.41317238743168894+ ]+ , [ -0.715603493434698 , 0.780363714438081 , 0.3102929809778049 ]+ , [ 0.6491833829319443 , 0.6692949179238176 , -0.1694761444119623 ]+ , [ 0.6500310589666282 , 0.657748687643292 , -0.10873036208745479 ]+ , [ 0.6540866269118539 , 0.684435599216663 , -0.20191695153447145 ]+ , [ 0.5717139272637514+ , 0.5968790484510275+ , -0.18667466541971123+ ]+ , [ 0.78051441980069 , 0.8624994903802875 , -4.366114397309004e-2 ]+ , [ 0.7740595934432167+ , 0.8624994903802875+ , -9.43309492994287e-2+ ]+ , [ 0.6553927028305583+ , 0.6599214690464065+ , -7.682095370023323e-2+ ]+ , [ 0.8630726083767282 , 1.4862706190912907 , -0.1457666249505203 ]+ , [ 0.8651615019144336+ , 1.5020533690353601+ , -9.103018997631947e-2+ ]+ , [ 0.8545090595639963+ , 1.4798172601240018+ , -0.20522856413423346+ ]+ , [ 0.2834784042093155 , 1.2593473684859822 , 0.8800216675702618 ]+ , [ 0.3654127720849311 , 1.2970587028449378 , 0.8457844093935878 ]+ , [ 0.2831308101687339 , 1.1814109441614336 , 0.8788805908385666 ]+ , [ 0.18424327753183914 , 1.4736973001288365 , 0.8624748737615431 ]+ , [ -0.6846273996141601 , 0.8790068974537645 , 0.5388137355108445 ]+ , [ -0.6567496895904281 , 0.8790071413593897 , 0.5725795213922534 ]+ , [ -0.6464588188586459+ , 0.7823424436625966+ , 0.44119347577210544+ ]+ , [ -0.7213170770599064 , 0.908850927324427 , 0.5321478467750975 ]+ , [ -0.5181196004900488 , 0.7295797909722026 , 0.5181196004900487 ]+ , [ -0.7505675479671516 , 0.8781985739488957 , 0.4400805652422398 ]+ , [ 0.7708735521448062 , 0.8625025153160115 , 0.10873936568443998 ]+ , [ 0.6626047492685796 , 0.9655545808978969 , 0.6132901903841046 ]+ , [ 0.771074187418324 , 0.8625025153160115 , 0.108004379308 ]+ , [ 0.6419080894398502 , 0.7908779235632789 , 0.45940727414369065 ]+ , [ 0.6490953253086557 , 0.7194452653409781 , 0.31163387003160176 ]+ , [ 0.7391783489851426 , 1.1731157788543425 , 0.550164799101456 ]+ , [ -6.169852060031459e-17+ , 0.912056858034264+ , 0.8959767931433186+ ]+ , [ 0.1928232899241431 , 1.0886815800201057 , 0.8962126634940769 ]+ , [ 0.15370753430700868 , 0.907458117258076 , 0.879636949352576 ]+ , [ 0.18893822974950675 , 0.9120771417338429 , 0.8765366554024405 ]+ , [ 0.12280505144146822 , 0.8753822495491728 , 0.8567258893994794 ]+ , [ 3.4974828952650555e-2+ , 0.9074580789365942+ , 0.8918549800719824+ ]+ , [ 0.0 , 0.7712853278687586 , 0.7698054940256471 ]+ , [ 6.080525584912347e-2+ , 0.8753821429287594+ , 0.8631058252409901+ ]+ , [ 0.18667466541971134 , 0.5968790484510275 , 0.5717139272637516 ]+ , [ 0.29182490099196917 , 0.729797130599598 , 0.6710598534195159 ]+ , [ 1.7990621802653095e-2+ , 1.9860825589147996e-2+ , 1.799062180265376e-2+ ]+ , [ 0.15441948465438154 , 0.7326922941987839 , 0.7170049842821663 ]+ , [ 0.23969991808709235 , 0.8768406254951658 , 0.8337562524679074 ]+ , [ 0.289831893259673 , 0.876840812979899 , 0.818044872661731 ]+ , [ 0.21959663365004123 , 0.9080153576084199 , 0.866447685262698 ]+ , [ 0.16241732047101431 , 0.7759039565328366 , 0.757509296930598 ]+ , [ -0.49818544023253314+ , 1.4717742047093716+ , 0.7304675058910846+ ]+ , [ -0.5911999246804508 , 1.2599845756664991 , 0.7130046626566298 ]+ , [ 0.4068358188765504 , 1.74614655744181 , 0.4921333979229422 ]+ , [ 0.4787714299468666 , 1.6423746127113557 , 0.5796425375805216 ]+ , [ 0.4378291627777074 , 1.7613180604656151 , 0.43782916277770734 ]+ , [ 0.34656596334419265 , 1.7617035212112468 , 0.5125010376980281 ]+ , [ 0.35938264373453305 , 1.643431115641555 , 0.6590455307424883 ]+ , [ 0.44011909325789345 , 1.6091658630935881 , 0.6493805393140206 ]+ , [ 0.3077193099076989 , 1.6095465768220536 , 0.7210973557556136 ]+ , [ 0.30618459927572234 , 1.7463793702756347 , 0.5599378049841232 ]+ , [ 0.5851752797903262+ , 0.5851752797903265+ , -6.448622334243026e-2+ ]+ , [ 7.812642760551491e-3+ , 7.812642760551715e-3+ , -7.812642760550492e-3+ ]+ , [ -0.7622224951613341+ , 1.2957898827631245+ , -0.5194379814088045+ ]+ , [ -0.7304675058910846+ , 1.4717742047093714+ , -0.4981854402325332+ ]+ , [ -0.7130046626566298+ , 1.2599845756664991+ , -0.5911999246804508+ ]+ , [ -0.8102438227139428+ , 1.2598574705147236+ , -0.4474984305598346+ ]+ , [ 0.4206163489045973 , 1.4946619231456852 , -0.7670726439144389 ]+ , [ 0.4981854402325333 , 1.4717742047093716 , -0.7304675058910849 ]+ , [ 0.3507821217020296 , 1.4727320843657137 , -0.8105000296381791 ]+ , [ 0.4146046771732798 , 1.5166611367705873 , -0.755872347832471 ]+ , [ -0.5073894465820622 , 0.9128065261969955 , 0.7423359031749056 ]+ , [ -0.4768060384239586 , 0.9085520538404275 , 0.7584440485557873 ]+ , [ -0.5321478467750965 , 0.9088509273244271 , 0.7213170770599069 ]+ , [ -0.5903677161648284 , 1.1741465971067808 , 0.7119923208428119 ]+ , [ -0.5388137355108447 , 0.8790068974537645 , 0.6846273996141601 ]+ , [ -0.6097884029414722 , 0.9088511267237062 , 0.6572150990573598 ]+ , [ -0.29629991859043964+ , 0.6041132479674723+ , 0.5329131536178867+ ]+ , [ -2.2151036187758197e-2+ , 2.5331770412981625e-2+ , 2.215103618775873e-2+ ]+ , [ -0.41317238743168894+ , 0.7294381763744873+ , 0.6045930611639508+ ]+ , [ -0.31029298097780494 , 0.780363714438081 , 0.715603493434698 ]+ , [ -0.810500029638179+ , 1.4727320843657137+ , -0.35078212170202955+ ]+ , [ -0.7670726439144387+ , 1.4946619231456855+ , -0.42061634890459737+ ]+ , [ -0.8260665185299887+ , 1.4902762907980336+ , -0.2884082499027695+ ]+ , [ -0.8457844093935879+ , 1.2970587028449376+ , -0.3654127720849311+ ]+ , [ -0.18424327753183928+ , 1.4736973001288365+ , 0.8624748737615431+ ]+ , [ -0.3507821217020296 , 1.4727320843657137 , 0.8105000296381792 ]+ , [ -0.26432271823494774+ , 1.4970509470350615+ , 0.8320774835437866+ ]+ , [ -0.42061634890459737+ , 1.4946619231456855+ , 0.7670726439144387+ ]+ , [ -9.114226751346854e-2+ , 1.5000742593050618+ , 0.8663032960314866+ ]+ , [ -0.26089628483355987+ , 1.5173670225190703+ , 0.8208298543940763+ ]+ , [ 0.8208298543940764 , 1.5173670225190703 , 0.2608962848335599 ]+ , [ 0.8320774835437869 , 1.4970509470350615 , 0.2643227182349477 ]+ , [ 0.8228126117740736 , 1.5458407114144659 , 0.1707356424814656 ]+ , [ 0.7750736880510629 , 1.543449628093767 , 0.3300998702021285 ]+ , [ 0.8651615019144336+ , 1.5020533690353601+ , 9.103018997631947e-2+ ]+ , [ 0.8545090595639963 , 1.4798172601240018 , 0.2052285641342334 ]+ , [ 0.8562836943885954 , 1.517441568182377 , 9.01587519579244e-2 ]+ , [ 0.7673351760829068 , 1.6096835638600524 , 0.15978142628509895 ]+ , [ 0.8239288970287512 , 1.4058726252835607 , 0.35635039483568703 ]+ , [ 0.7733308622857777 , 1.287477942063759 , 0.502796814195706 ]+ , [ 0.4474984305598345 , 1.2598574705147234 , 0.8102438227139425 ]+ , [ 0.3507821217020293 , 1.4727320843657137 , 0.810500029638179 ]+ , [ 0.32461261897613036 , 0.9080154562527452 , 0.833535387138529 ]+ , [ 0.36590860085651866 , 1.0902530151825576 , 0.8421531448806923 ]+ , [ -0.6045930611639505+ , 0.7294381763744877+ , -0.41317238743168877+ ]+ , [ -0.4720306023200157+ , 0.6074273900130303+ , -0.3921629727500072+ ]+ , [ -0.646458818858646 , 0.7823424436625966 , -0.4411934757721056 ]+ , [ -0.5329131536178867+ , 0.6041132479674725+ , -0.2962999185904397+ ]+ , [ -0.6846273996141597+ , 0.8790068974537646+ , -0.5388137355108444+ ]+ , [ -0.7505675479671517+ , 0.8781985739488954+ , -0.44008056524223993+ ]+ , [ 0.29629991859043964 , 0.6041132479674723 , 0.5329131536178867 ]+ , [ 0.41317238743168894 , 0.7294381763744873 , 0.6045930611639508 ]+ , [ 2.215103618775549e-2+ , 2.533177041297828e-2+ , 2.21510361877556e-2+ ]+ , [ 0.31029298097780494 , 0.780363714438081 , 0.715603493434698 ]+ , [ -0.15441948465438154+ , 0.732692294198784+ , -0.7170049842821664+ ]+ , [ -6.448622334243025e-2+ , 0.5851752797903265+ , -0.5851752797903262+ ]+ , [ -0.16241732047101426+ , 0.7759039565328368+ , -0.7575092969305981+ ]+ , [ -0.18667466541971123+ , 0.5968790484510273+ , -0.5717139272637514+ ]+ , [ -0.5539859487435899+ , 0.7828729928425695+ , -0.5539859487435899+ ]+ , [ -0.5725795213922535+ , 0.8790071413593896+ , -0.6567496895904281+ ]+ , [ -0.5181196004900485+ , 0.7295797909722027+ , -0.5181196004900485+ ]+ , [ -0.6567496895904281+ , 0.8790071413593897+ , -0.5725795213922533+ ]+ , [ 0.8122497597351318 , 1.3634301491185252 , -0.396987025988275 ]+ , [ 0.7740579104368243+ , 0.862498015165331+ , -9.433724968365648e-2+ ]+ , [ 6.44862233424303e-2 , 0.5851752797903262 , 0.5851752797903267 ]+ , [ 7.812642760551684e-3+ , 7.812642760551713e-3+ , 7.812642760551824e-3+ ]+ , [ 0.19212615972377473 , 1.746701943306404 , 0.6080853167177859 ]+ , [ 0.2414067196592598 , 1.7624263437337935 , 0.568522694152947 ]+ , [ 0.22440550617121352 , 1.6454841596605991 , 0.7140470378267627 ]+ , [ 0.12443603223298119 , 1.763801627319081 , 0.6031237664636153 ]+ , [ -0.6254774679047082 , 1.4713967749278507 , 0.6254774679047082 ]+ , [ -0.558287861814229 , 1.4934020335516496 , 0.6747863108748675 ]+ , [ -0.6747863108748675 , 1.4934020335516496 , 0.558287861814229 ]+ , [ -0.6524829343907664 , 1.2954115625390261 , 0.6524829343907664 ]+ , [ 0.2571184603540509 , 1.8714260967581884 , 0.37885544369469604 ]+ , [ 0.28526260180018986+ , 1.8775138180920772+ , 0.34408768727411854+ ]+ , [ 0.21271144734580563 , 1.877827270100134 , 0.3923832499457651 ]+ , [ 0.18017138946943928 , 1.871613285510211 , 0.42041538751051694 ]+ , [ 0.5599378049841232 , 1.7463793702756347 , 0.30618459927572234 ]+ , [ 0.6590455307424883 , 1.643431115641555 , 0.3593826437345331 ]+ , [ 0.568522694152947 , 1.7624263437337935 , 0.2414067196592598 ]+ , [ 0.512501037698028 , 1.7617035212112466 , 0.34656596334419265 ]+ , [ 0.755872347832471 , 1.5166611367705873 , 0.4146046771732797 ]+ , [ 0.7210973557556135 , 1.6095465768220534 , 0.3077193099076989 ]+ , [ 0.7140470378267627 , 1.6454841596605991 , 0.2244055061712136 ]+ , [ 0.6493805393140206 , 1.6091658630935881 , 0.4401190932578934 ]+ , [ -0.8664476852626979+ , 0.9080153576084198+ , -0.21959663365004278+ ]+ , [ -0.8337562524679073+ , 0.8768406254951661+ , -0.2396999180870924+ ]+ , [ -0.8765366554024407+ , 0.9120771417338429+ , -0.18893822974950678+ ]+ , [ -0.833535387138529+ , 0.9080154562527452+ , -0.32461261897613036+ ]+ , [ 0.44689607487161953+ , 1.1765076558719516+ , -0.8091216099094027+ ]+ , [ 0.518569427022487 , 1.0903982826657406 , -0.7590404339629727 ]+ , [ 0.4474984305598345 , 1.2598574705147234 , -0.8102438227139426 ]+ , [ 0.36590860085651866+ , 1.0902530151825576+ , -0.8421531448806926+ ]+ , [ 0.5194379814088046 , 1.2957898827631247 , -0.7622224951613341 ]+ , [ 0.36541277208493106+ , 1.2970587028449376+ , -0.8457844093935878+ ]+ , [ 0.26432271823494774+ , 1.4970509470350615+ , -0.8320774835437867+ ]+ , [ 0.2834784042093156 , 1.2593473684859824 , -0.8800216675702618 ]+ , [ 0.283130810168734 , 1.1814109441614336 , -0.8788805908385665 ]+ , [ 0.19160298368259063 , 1.29931244670657 , -0.8997455590151622 ]+ , [ 1.7990621802653317e-2+ , 1.9860825589147875e-2+ , -1.799062180265354e-2+ ]+ , [ 0.647296121375148 , 0.6980028758147818 , -0.26667276723840716 ]+ , [ -0.86310582524099 , 0.8753821429287596 , 6.080525584912345e-2 ]+ , [ -0.7698054940256472 , 0.7712853278687583 , 0.0 ]+ , [ -0.8567258893994796+ , 0.8753822495491728+ , 0.12280505144146828+ ]+ , [ -0.891854980071982 , 0.9074580789365941 , 3.4974828952651e-2 ]+ , [ -0.8337562524679074+ , 0.8768406254951661+ , 0.23969991808709246+ ]+ , [ -0.8180448726617309 , 0.8768408129798988 , 0.289831893259673 ]+ , [ -0.8664476852626981 , 0.90801535760842 , 0.21959663365004095 ]+ , [ -0.879636949352576 , 0.907458117258076 , 0.15370753430700804 ]+ , [ -0.4400805652422398 , 0.8781985739488954 , 0.7505675479671517 ]+ , [ -0.3856172858664447 , 0.908552060323908 , 0.8081008260732252 ]+ , [ -0.3991852390173557 , 0.8781995871178615 , 0.772837999772326 ]+ , [ -0.44119347577210544+ , 0.7823424436625966+ , 0.6464588188586459+ ]+ , [ -0.5725795213922534 , 0.8790071413593897 , 0.6567496895904281 ]+ , [ -0.5539859487435899 , 0.7828729928425695 , 0.5539859487435899 ]+ , [ -0.6646325123013266 , 1.5161703861218 , 0.5499408317491791 ]+ , [ -0.7304675058910848+ , 1.4717742047093716+ , 0.49818544023253314+ ]+ , [ -0.7584440485557876 , 0.9085520538404273 , 0.4768060384239584 ]+ , [ -0.7728379997723258 , 0.8781995871178616 , 0.3991852390173557 ]+ , [ 0.8562836943885955+ , 1.5174415681823774+ , -9.015875195792439e-2+ ]+ , [ 0.8390008746485779+ , 1.547398454632929+ , 2.7755575615628914e-17+ ]+ , [ 0.16621776154382692+ , 1.9776053853655182+ , 0.16621776154382692+ ]+ , [ 0.34408768727411854+ , 1.8775138180920767+ , 0.28526260180018986+ ]+ , [ 0.15197602557430878+ , 1.9868541707659964+ , 0.15197602557430875+ ]+ , [ 0.3239549649324688 , 1.871257263189551 , 0.3239549649324688 ]+ , [ 0.3923832499457651 , 1.877827270100134 , 0.21271144734580563 ]+ , [ 0.4204153875105169 , 1.8716132855102106 , 0.18017138946943928 ]+ , [ 0.378855443694696 , 1.8714260967581884 , 0.25711846035405095 ]+ , [ 0.21242895637465672+ , 1.9790924197542168+ , 9.226906344799235e-2+ ]+ , [ 0.42437151237708004+ , 1.8784630967211673+ , 0.13411685349983243+ ]+ , [ 0.19692078525583517+ , 1.9867703444520366+ , 8.589118029151296e-2+ ]+ , [ 0.15978142628509898 , 1.6096835638600524 , 0.7673351760829068 ]+ , [ 0.17073564248146567 , 1.545840711414466 , 0.8228126117740738 ]+ , [ 7.668380969626697e-2+ , 1.6481991362041954+ , 0.7417373916617902+ ]+ , [ 0.3300998702021285 , 1.543449628093767 , 0.7750736880510631 ]+ , [ 0.4468960748716194 , 1.1765076558719516 , 0.8091216099094026 ]+ , [ 0.3581881925466474 , 0.9124253481133149 , 0.8235344289369642 ]+ , [ -2.215103618775549e-2+ , 2.533177041297907e-2+ , -2.2151036187755047e-2+ ]+ , [ -0.6710598534195159+ , 0.7297971305995978+ , -0.2918249009919692+ ]+ , [ -0.7584440485557876+ , 0.9085520538404273+ , -0.4768060384239584+ ]+ , [ -0.7728379997723258+ , 0.8781995871178616+ , -0.3991852390173557+ ]+ , [ 0.5717139272637514 , 0.5968790484510275 , 0.18667466541971128 ]+ , [ 1.7990621802653095e-2+ , 1.986082558914795e-2+ , 1.7990621802653206e-2+ ]+ , [ 0.6472961213751481 , 0.6980028758147817 , 0.26667276723840716 ]+ , [ 0.6469996697760713 , 0.7079075212097802 , 0.2926042880159187 ]+ , [ 0.6357399515521689 , 0.8593528035528301 , 0.568778121333005 ]+ , [ 0.6405215643858786 , 0.7748397465927047 , 0.4372196236016949 ]+ , [ 0.6045930611639507 , 0.7294381763744875 , 0.41317238743168894 ]+ , [ 0.5181196004900488 , 0.7295797909722026 , 0.5181196004900487 ]+ , [ 0.5321478467750966 , 0.908850927324427 , 0.7213170770599068 ]+ , [ 0.6097884029414722 , 0.9088511267237062 , 0.6572150990573598 ]+ , [ 0.5073894465820619 , 0.9128065261969955 , 0.7423359031749057 ]+ , [ 0.5388137355108445 , 0.8790068974537643 , 0.6846273996141601 ]+ , [ 0.518569427022487 , 1.0903982826657406 , 0.7590404339629727 ]+ , [ 0.4768060384239586 , 0.9085520538404275 , 0.7584440485557873 ]+ , [ 0.44008056524223993 , 0.8781985739488954 , 0.7505675479671516 ]+ , [ 0.44119347577210555 , 0.7823424436625965 , 0.646458818858646 ]+ , [ 0.3991852390173557 , 0.8781995871178615 , 0.772837999772326 ]+ , [ 0.3856172858664447 , 0.908552060323908 , 0.8081008260732252 ]+ , [ -0.6572150990573598 , 0.9088511267237064 , -0.609788402941472 ]+ , [ -0.6360954419937749 , 0.912960352086011 , -0.6360954419937749 ]+ , [ -0.7213170770599066+ , 0.9088509273244272+ , -0.5321478467750965+ ]+ , [ -0.7423359031749055+ , 0.9128065261969955+ , -0.5073894465820623+ ]+ , [ -0.3921629727500072+ , 0.6074273900130303+ , -0.4720306023200156+ ]+ , [ -2.3194160942469955e-2+ , 2.6785629054825896e-2+ , -2.3194160942469955e-2+ ]+ , [ -0.41317238743168894+ , 0.7294381763744873+ , -0.6045930611639508+ ]+ , [ -2.21510361877556e-2+ , 2.5331770412978235e-2+ , -2.215103618775549e-2+ ]+ , [ -1.7990621802653206e-2+ , 1.986082558914795e-2+ , -1.7990621802653095e-2+ ]+ , [ -0.2918249009919691+ , 0.7297971305995977+ , -0.6710598534195157+ ]+ , [ 9.869506660256812e-2+ , 1.1894267902510014+ , -0.9173511809757207+ ]+ , [ 0.192823289924143 , 1.0886815800201057 , -0.8962126634940769 ]+ , [ 9.87971813131522e-2+ , 1.2576022758257417+ , -0.9183913329068394+ ]+ , [ -2.624567824552988e-17+ , 1.0862351104914478+ , -0.9157767877158495+ ]+ , [ 6.169852060031459e-17+ , 0.912056858034264+ , -0.8959767931433186+ ]+ , [ -9.86950666025681e-2+ , 1.1894267902510014+ , -0.9173511809757204+ ]+ , [ -0.6097884029414723+ , 0.9088511267237063+ , -0.6572150990573598+ ]+ , [ -0.6502787934703502+ , 1.0902677960073714+ , -0.6502787934703501+ ]+ , [ -2.7755575615628914e-17+ , 0.7373497776866849+ , -0.7373497776866843+ ]+ , [ -7.812642760551656e-3+ , 7.812642760551715e-3+ , -7.81264276055138e-3+ ]+ , [ 0.640521564385879 , 0.7748397465927045 , -0.43721962360169503 ]+ , [ 0.6490953253086555 , 0.7194452653409784 , -0.3116338700316015 ]+ , [ 0.6419080894398494 , 0.7908779235632781 , -0.45940727414369 ]+ , [ 0.6045930611639505 , 0.7294381763744877 , -0.4131723874316888 ]+ , [ 0.7708735521448064+ , 0.8625025153160115+ , -0.10873936568444004+ ]+ , [ 0.6357399515521689 , 0.8593528035528301 , -0.568778121333005 ]+ , [ 0.7391783489851428 , 1.1731157788543423 , -0.5501647991014558 ]+ , [ 0.771074187418324 , 0.8625025153160115 , -0.10800437930799994 ]+ , [ 0.7733308622857775 , 1.287477942063759 , -0.5027968141957061 ]+ , [ 0.7271032447312008 , 1.1472482940426965 , -0.5673075249989148 ]+ , [ 0.6626047492685793 , 0.9655545808978973 , -0.6132901903841047 ]+ , [ 0.7710778571614444 , 0.862498015165331 , -0.10798328513535704 ]+ , [ 9.11422675134686e-2 , 1.5000742593050618 , 0.8663032960314866 ]+ , [ 8.706652684736651e-17+ , 1.5473984546329287+ , 0.839000874648578+ ]+ , [ 9.015875195792442e-2+ , 1.5174415681823772+ , 0.8562836943885956+ ]+ , [ 0.0 , 1.6095711978718583 , 0.7838769658257825 ]+ , [ -9.01587519579244e-2 , 1.517441568182377 , 0.8562836943885954 ]+ , [ -0.1707356424814655 , 1.5458407114144659 , 0.8228126117740736 ]+ , [ 9.3613324164243e-2 , 1.871978516403293 , 0.4470200027098403 ]+ , [ 6.613605971990155e-2+ , 1.7466998928928852+ , 0.6342820077896054+ ]+ , [ 0.13411685349983243 , 1.878463096721167 , 0.42437151237708 ]+ , [ 4.4461106873143405e-2+ , 1.8791795052256692+ , 0.4414598402161806+ ]+ , [ -7.668380969626695e-2+ , 1.6481991362041957+ , 0.7417373916617903+ ]+ , [ 0.0 , 1.764488100164783 , 0.6148766884290562 ]+ , [ -0.4146046771732797 , 1.5166611367705873 , 0.755872347832471 ]+ , [ -0.5499408317491791 , 1.5161703861218003 , 0.6646325123013266 ]+ , [ 9.226906344799235e-2+ , 1.979092419754217+ , 0.21242895637465672+ ]+ , [ 8.589118029151292e-2+ , 1.9867703444520366+ , 0.19692078525583512+ ]+ , [ 0.4921333979229422 , 1.74614655744181 , 0.40683581887655035 ]+ , [ 0.5796425375805216 , 1.6423746127113557 , 0.4787714299468666 ]+ , [ 0.2162856218653722+ , 1.986091229359059+ , 1.3877787807814457e-17+ ]+ , [ 0.4414598402161807+ , 1.8791795052256695+ , 4.4461106873143405e-2+ ]+ , [ 0.4414598402161807+ , 1.8791795052256695+ , -4.4461106873143405e-2+ ]+ , [ 0.21397126895807833 , 1.9871467005290857 , 0.0 ]+ , [ -0.7590404339629727+ , 1.0903982826657406+ , -0.5185694270224871+ ]+ , [ -0.7119923208428119+ , 1.1741465971067808+ , -0.5903677161648285+ ]+ , [ -0.8091216099094026+ , 1.1765076558719516+ , -0.4468960748716195+ ]+ , [ -0.8081008260732252+ , 0.9085520603239079+ , -0.3856172858664447+ ]+ , [ 0.3581881925466478 , 0.912425348113315 , -0.823534428936964 ]+ , [ 0.18893822974950705+ , 0.9120771417338429+ , -0.8765366554024404+ ]+ , [ 0.5911999246804508 , 1.2599845756664991 , -0.7130046626566298 ]+ , [ 0.5582878618142291 , 1.4934020335516496 , -0.6747863108748675 ]+ , [ 0.7119923208428119 , 1.1741465971067808 , -0.5903677161648284 ]+ , [ 0.6363011668042756 , 0.9129203244659402 , -0.635839187329811 ]+ , [ 0.4720306023200157 , 0.60742739001303 , -0.3921629727500073 ]+ , [ 2.3194160942469733e-2+ , 2.6785629054825906e-2+ , -2.3194160942469733e-2+ ]+ , [ 0.5181196004900488 , 0.7295797909722026 , -0.5181196004900487 ]+ , [ 0.5329131536178869 , 0.6041132479674722 , -0.2962999185904397 ]+ , [ 0.6469996697760714+ , 0.7079075212097801+ , -0.29260428801591876+ ]+ , [ 2.215103618775549e-2+ , 2.533177041297907e-2+ , -2.2151036187755047e-2+ ]+ , [ 2.2151036187755713e-2+ , 2.5331770412978624e-2+ , -2.215103618775549e-2+ ]+ , [ 0.39216297275000744 , 0.60742739001303 , -0.4720306023200158 ]+ , [ -0.8796369493525764+ , 0.9074581172580761+ , -0.15370753430700962+ ]+ , [ -0.896212663494077+ , 1.0886815800201057+ , -0.19282328992414308+ ]+ , [ -0.6572150990573599 , 0.9088511267237063 , 0.6097884029414724 ]+ , [ -0.6360954419937749 , 0.912960352086011 , 0.6360954419937749 ]+ , [ -0.512501037698028 , 1.7617035212112466 , 0.34656596334419265 ]+ , [ -0.378855443694696 , 1.8714260967581886 , 0.2571184603540509 ]+ , [ -0.4921333979229422 , 1.74614655744181 , 0.4068358188765504 ]+ , [ -0.5599378049841232+ , 1.7463793702756347+ , 0.30618459927572234+ ]+ , [ -0.6747863108748675 , 1.4934020335516496 , -0.558287861814229 ]+ , [ -0.755872347832471 , 1.5166611367705873 , -0.4146046771732797 ]+ , [ -0.8081008260732245+ , 0.9085520603239081+ , 0.38561728586644495+ ]+ , [ -0.8335353871385288+ , 0.9080154562527454+ , 0.32461261897613114+ ]+ , [ 0.7271032447312005 , 1.1472482940426967 , 0.5673075249989148 ]+ , [ 0.6363011668042756 , 0.9129203244659402 , 0.635839187329811 ]+ , [ 0.8105000296381791 , 1.4727320843657137 , 0.3507821217020296 ]+ , [ 0.7670726439144389 , 1.4946619231456855 , 0.4206163489045974 ]+ , [ 0.7838769658257825 , 1.6095711978718585 , 0.0 ]+ , [ 0.7417373916617903+ , 1.6481991362041957+ , 7.668380969626695e-2+ ]+ , [ 0.7417373916617902 , 1.6481991362041954 , -7.6683809696267e-2 ]+ , [ 0.7673351760829068+ , 1.6096835638600524+ , -0.15978142628509898+ ]+ , [ 0.6342820077896054+ , 1.7466998928928852+ , -6.613605971990152e-2+ ]+ , [ 0.8228126117740736 , 1.5458407114144659 , -0.1707356424814655 ]+ , [ 0.42061634890459737 , 1.4946619231456855 , 0.7670726439144389 ]+ , [ 0.26432271823494774 , 1.4970509470350617 , 0.8320774835437867 ]+ , [ 0.4981854402325333 , 1.4717742047093716 , 0.7304675058910848 ]+ , [ 0.41460467717327976 , 1.5166611367705878 , 0.7558723478324711 ]+ , [ 0.4731627371704971 , 1.5418687589859696 , 0.6987516862837263 ]+ , [ 0.2608962848335599 , 1.5173670225190703 , 0.8208298543940764 ]+ , [ -0.5717139272637514+ , 0.5968790484510275+ , -0.18667466541971128+ ]+ , [ -1.7990621802653317e-2+ , 1.9860825589147875e-2+ , -1.799062180265354e-2+ ]+ , [ -0.7170049842821663+ , 0.7326922941987838+ , -0.15441948465438157+ ]+ , [ -0.7575092969305981+ , 0.7759039565328365+ , -0.16241732047101434+ ]+ , [ -0.8180448726617309 , 0.876840812979899 , -0.289831893259673 ]+ , [ -0.715603493434698 , 0.7803637144380808 , -0.310292980977805 ]+ , [ -0.823534428936964 , 0.912425348113315 , -0.3581881925466473 ]+ , [ -0.8421531448806922+ , 1.0902530151825576+ , -0.36590860085651855+ ]+ , [ 0.5329131536178869 , 0.6041132479674723 , 0.2962999185904397 ]+ , [ 0.5539859487435901 , 0.7828729928425694 , 0.55398594874359 ]+ , [ 0.6360954419937748 , 0.9129603520860108 , 0.6360954419937748 ]+ , [ 0.5725795213922534 , 0.8790071413593897 , 0.656749689590428 ]+ , [ 0.6502787934703501 , 1.0902677960073712 , 0.6502787934703502 ]+ , [ 0.7558723478324709+ , 1.5166611367705873+ , -0.41460467717327976+ ]+ , [ 0.7670726439144389 , 1.4946619231456852 , -0.4206163489045974 ]+ , [ 0.7750736880510629 , 1.543449628093767 , -0.3300998702021285 ]+ , [ 0.6987516862837263+ , 1.5418687589859699+ , -0.47316273717049717+ ]+ , [ -0.19282328992414305+ , 1.0886815800201057+ , -0.896212663494077+ ]+ , [ -0.18893822974950705+ , 0.9120771417338428+ , -0.8765366554024404+ ]+ , [ -0.283130810168734 , 1.1814109441614336 , -0.8788805908385667 ]+ , [ -0.28347840420931564+ , 1.2593473684859822+ , -0.8800216675702618+ ]+ , [ -0.36590860085651855+ , 1.0902530151825576+ , -0.8421531448806925+ ]+ , [ -9.879718131315204e-2+ , 1.2576022758257417+ , -0.9183913329068394+ ]+ , [ -0.5388137355108445+ , 0.8790068974537645+ , -0.6846273996141601+ ]+ , [ -0.5321478467750966 , 0.908850927324427 , -0.7213170770599068 ]+ , [ -0.358188192546648 , 0.912425348113315 , -0.8235344289369639 ]+ , [ -0.32461261897613036+ , 0.9080154562527454+ , -0.8335353871385288+ ]+ , [ -0.3856172858664454+ , 0.9085520603239081+ , -0.8081008260732248+ ]+ , [ -0.39918523901735564+ , 0.8781995871178618+ , -0.7728379997723256+ ]+ , [ -0.47680603842395847+ , 0.9085520538404271+ , -0.7584440485557874+ ]+ , [ -0.4468960748716195+ , 1.1765076558719516+ , -0.8091216099094026+ ]+ , [ -0.44749843055983474+ , 1.2598574705147236+ , -0.8102438227139428+ ]+ , [ -0.518569427022487 , 1.0903982826657406 , -0.7590404339629725 ]+ , [ -0.5073894465820619+ , 0.9128065261969955+ , -0.7423359031749057+ ]+ , [ -0.5903677161648284+ , 1.1741465971067808+ , -0.7119923208428119+ ]+ , [ -0.2962999185904398 , 0.6041132479674721 , -0.532913153617887 ]+ , [ -0.44119347577210544+ , 0.7823424436625968+ , -0.6464588188586459+ ]+ , [ -0.31029298097780494+ , 0.7803637144380812+ , -0.7156034934346979+ ]+ , [ -0.44008056524224 , 0.8781985739488956 , -0.7505675479671517 ]+ , [ -0.28983189325967296+ , 0.8768408129798992+ , -0.8180448726617306+ ]+ , [ -0.23969991808709237+ , 0.8768406254951662+ , -0.8337562524679074+ ]+ , [ -0.21959663365004273+ , 0.9080153576084198+ , -0.8664476852626977+ ]+ , [ 0.18424327753183933+ , 1.4736973001288365+ , -0.8624748737615431+ ]+ , [ -2.7755575615628914e-17+ , 1.3011746498142651+ , -0.9190561156885906+ ]+ , [ 6.448622334243023e-2+ , 0.5851752797903262+ , -0.5851752797903267+ ]+ , [ 0.0 , 0.7712853278687584 , -0.7698054940256471 ]+ , [ 0.6646325123013265 , 1.5161703861218 , -0.549940831749179 ]+ , [ 0.6493805393140206 , 1.6091658630935881 , -0.4401190932578934 ]+ , [ 0.6590455307424883 , 1.643431115641555 , -0.3593826437345331 ]+ , [ 0.5796425375805215+ , 1.6423746127113557+ , -0.47877142994686667+ ]+ , [ 0.0 , 1.9860912293590587 , 0.21628562186537217 ]+ , [ 0.0 , 1.8719852675758741 , 0.4566121245194575 ]+ , [ 0.0 , 2.0282480595325483 , 8.294756480297713e-2 ]+ , [ 1.6023498781971006e-2+ , 2.0298344402390818+ , 7.461906659600522e-2+ ]+ , [ -4.163336342344337e-17+ , 1.9871467005290855+ , 0.21397126895807828+ ]+ , [ -1.6023498781971006e-2+ , 2.0298344402390818+ , 7.461906659600522e-2+ ]+ , [ 0.15197602557430875+ , 1.9868541707659964+ , 0.15197602557430875+ ]+ , [ 8.352331765507048e-2+ , 1.9874788959388558+ , 0.19585993121305542+ ]+ , [ 3.459146906505174e-2+ , 2.028192844530145+ , 7.606128724075058e-2+ ]+ , [ 1.3877787807814457e-17+ , 1.9871467005290855+ , 0.2139712689580783+ ]+ , [ -0.5851752797903265+ , 0.5851752797903264+ , -6.448622334243026e-2+ ]+ , [ -7.812642760551698e-3+ , 7.81264276055331e-3+ , -7.812642760553379e-3+ ]+ , [ -0.8631058252409901+ , 0.8753821429287594+ , -6.080525584912344e-2+ ]+ , [ -0.8567258893994794+ , 0.8753822495491729+ , -0.12280505144146825+ ]+ , [ -0.4731627371704971 , 1.5418687589859696 , 0.6987516862837264 ]+ , [ -0.5970610762747673 , 1.5413389851975459 , 0.5970610762747673 ]+ , [ -0.6254774679047082+ , 1.4713967749278507+ , -0.6254774679047082+ ]+ , [ -0.558287861814229 , 1.4934020335516496 , -0.6747863108748675 ]+ , [ -0.6524829343907664+ , 1.2954115625390261+ , -0.6524829343907664+ ]+ , [ -0.6646325123013265 , 1.5161703861218 , -0.549940831749179 ]+ , [ 0.6342820077896054+ , 1.7466998928928852+ , 6.613605971990152e-2+ ]+ , [ 0.6080853167177859 , 1.7467019433064042 , 0.19212615972377467 ]+ , [ 0.5548468717518389 , 1.6090000296403644 , 0.5548468717518389 ]+ , [ 0.6987516862837264 , 1.5418687589859699 , 0.47316273717049717 ]+ , [ 0.7304675058910848 , 1.4717742047093716 , 0.4981854402325333 ]+ , [ 0.6646325123013265 , 1.5161703861218 , 0.5499408317491791 ]+ , [ 0.6747863108748674 , 1.4934020335516496 , 0.5582878618142292 ]+ , [ 0.5970610762747673 , 1.5413389851975459 , 0.5970610762747673 ]+ , [ 0.6524829343907664 , 1.2954115625390261 , -0.6524829343907664 ]+ , [ 0.5903677161648287 , 1.1741465971067808 , -0.7119923208428119 ]+ , [ 0.7130046626566298 , 1.2599845756664991 , -0.5911999246804508 ]+ , [ 0.6254774679047082 , 1.4713967749278507 , -0.6254774679047082 ]+ , [ 0.7622224951613341 , 1.2957898827631247 , -0.5194379814088045 ]+ , [ 0.6502787934703502 , 1.0902677960073714 , -0.6502787934703502 ]+ , [ 0.5499408317491791 , 1.5161703861218003 , 0.6646325123013266 ]+ , [ 0.5582878618142292 , 1.4934020335516496 , 0.6747863108748676 ]+ , [ -0.8306211796074129 , 1.5568496825908318 , 0.0 ]+ , [ -0.7838769658257825 , 1.6095711978718583 , 0.0 ]+ , [ -0.8338378098771851+ , 1.5431253932352957+ , 8.699039391932935e-2+ ]+ , [ -0.8338378098771851+ , 1.5431253932352957+ , -8.699039391932935e-2+ ]+ , [ -0.8788805908385666+ , 1.1814109441614336+ , -0.28313081016873404+ ]+ , [ -0.9170605544812714+ , 1.1880416776333664+ , -9.998920445288839e-2+ ]+ , [ -0.9058201893253578+ , 1.2360005696799088+ , -0.15764435612292219+ ]+ , [ -0.9173135774468576+ , 1.1869621129641121+ , -9.633778848140394e-2+ ]+ , [ -0.7130046626566299 , 1.2599845756664991 , 0.5911999246804511 ]+ , [ -0.6502787934703503 , 1.0902677960073714 , 0.6502787934703503 ]+ , [ -0.7417373916617903+ , 1.6481991362041957+ , 7.668380969626695e-2+ ]+ , [ -0.7417373916617903+ , 1.6481991362041957+ , -7.668380969626695e-2+ ]+ , [ -0.6342820077896054+ , 1.7466998928928854+ , 6.613605971990152e-2+ ]+ , [ -0.7673351760829068+ , 1.6096835638600524+ , 0.15978142628509898+ ]+ , [ -0.6031237664636153 , 1.763801627319081 , 0.12443603223298116 ]+ , [ -0.6148766884290562 , 1.764488100164783 , 0.0 ]+ , [ -0.4566121245194575+ , 1.871985267575874+ , -1.830024316166545e-17+ ]+ , [ -0.6342820077896054+ , 1.7466998928928852+ , -6.613605971990157e-2+ ]+ , [ -0.34408768727411854+ , 1.877513818092077+ , 0.28526260180018986+ ]+ , [ -0.3923832499457651 , 1.877827270100134 , 0.2127114473458056 ]+ , [ -0.19692078525583515+ , 1.9867703444520366+ , 8.589118029151296e-2+ ]+ , [ -0.15197602557430873+ , 1.9868541707659966+ , 0.15197602557430875+ ]+ , [ -0.19585993121305542+ , 1.9874788959388558+ , 8.35233176550705e-2+ ]+ , [ -0.21242895637465672+ , 1.979092419754217+ , 9.226906344799235e-2+ ]+ , [ -0.7140470378267627 , 1.645484159660599 , 0.22440550617121358 ]+ , [ -0.8228126117740738 , 1.545840711414466 , 0.17073564248146567 ]+ , [ -0.7558723478324709+ , 1.5166611367705873+ , 0.41460467717327976+ ]+ , [ -0.6987516862837263+ , 1.5418687589859699+ , 0.47316273717049717+ ]+ , [ -0.7750736880510629 , 1.543449628093767 , 0.3300998702021285 ]+ , [ -0.7670726439144389 , 1.4946619231456852 , 0.4206163489045974 ]+ , [ -0.7210973557556136+ , 1.6095465768220536+ , 0.30771930990769886+ ]+ , [ -0.8208298543940764 , 1.5173670225190703 , 0.2608962848335599 ]+ , [ -0.5911999246804508+ , 1.2599845756664991+ , -0.7130046626566298+ ]+ , [ -0.5194379814088046+ , 1.2957898827631247+ , -0.7622224951613342+ ]+ , [ 0.6254774679047082 , 1.4713967749278507 , 0.625477467904708 ]+ , [ 0.7622224951613341 , 1.2957898827631245 , 0.5194379814088045 ]+ , [ 0.5599378049841232+ , 1.7463793702756347+ , -0.30618459927572234+ ]+ , [ 0.568522694152947 , 1.7624263437337935 , -0.2414067196592598 ]+ , [ 0.512501037698028 , 1.7617035212112468 , -0.34656596334419265 ]+ , [ 0.7210973557556135 , 1.6095465768220536 , -0.3077193099076989 ]+ , [ 0.4204153875105169+ , 1.8716132855102106+ , -0.18017138946943928+ ]+ , [ 0.42437151237708004+ , 1.8784630967211673+ , -0.13411685349983243+ ]+ , [ 0.3923832499457651 , 1.877827270100134 , -0.2127114473458056 ]+ , [ 0.4470200027098403 , 1.871978516403293 , -9.3613324164243e-2 ]+ , [ 0.21242895637465675+ , 1.979092419754217+ , -9.22690634479924e-2+ ]+ , [ 0.6080853167177859 , 1.7467019433064044 , -0.1921261597237747 ]+ , [ 0.7140470378267627+ , 1.6454841596605991+ , -0.22440550617121363+ ]+ , [ 0.6031237664636153+ , 1.7638016273190815+ , -0.12443603223298119+ ]+ , [ 0.6524829343907664 , 1.2954115625390261 , 0.6524829343907664 ]+ , [ 0.5194379814088046 , 1.2957898827631245 , 0.7622224951613343 ]+ , [ -0.7622224951613341 , 1.2957898827631245 , 0.5194379814088045 ]+ , [ -0.8105000296381791 , 1.4727320843657137 , 0.3507821217020296 ]+ , [ -0.1228050514414682+ , 0.8753822495491729+ , -0.8567258893994794+ ]+ , [ -6.080525584912345e-2+ , 0.8753821429287596+ , -0.86310582524099+ ]+ , [ -0.15370753430700937+ , 0.907458117258076+ , -0.8796369493525762+ ]+ , [ -3.497482895265056e-2+ , 0.9074580789365942+ , -0.8918549800719824+ ]+ , [ 9.114226751346854e-2+ , 1.5000742593050618+ , -0.8663032960314866+ ]+ , [ 0.2608962848335599 , 1.5173670225190703 , -0.8208298543940764 ]+ , [ 0.3300998702021285 , 1.5434496280937666 , -0.7750736880510629 ]+ , [ 0.17073564248146547 , 1.545840711414466 , -0.8228126117740737 ]+ , [ -0.1916029836825906 , 1.29931244670657 , -0.8997455590151622 ]+ , [ 5.276171965179255e-17+ , 1.4739386059327377+ , -0.8813815581455101+ ]+ , [ 6.080525584912359e-2+ , 0.8753821429287593+ , -0.8631058252409899+ ]+ , [ 3.497482895265053e-2+ , 0.9074580789365946+ , -0.8918549800719818+ ]+ , [ 0.15441948465438154 , 0.732692294198784 , -0.7170049842821665 ]+ , [ 7.812642760551658e-3+ , 7.8126427605515e-3+ , -7.812642760551602e-3+ ]+ , [ 0.4131723874316888 , 0.7294381763744876 , -0.6045930611639508 ]+ , [ 0.5539859487435899 , 0.7828729928425695 , -0.5539859487435899 ]+ , [ 0.44119347577210555+ , 0.7823424436625966+ , -0.6464588188586459+ ]+ , [ 0.2962999185904397 , 0.6041132479674723 , -0.5329131536178867 ]+ , [ 0.7304675058910848+ , 1.4717742047093716+ , -0.49818544023253314+ ]+ , [ 0.8239288970287512+ , 1.4058726252835607+ , -0.35635039483568703+ ]+ , [ 0.6747863108748675 , 1.4934020335516496 , -0.558287861814229 ]+ , [ 0.8105000296381791 , 1.4727320843657137 , -0.3507821217020296 ]+ , [ -0.8228126117740736 , 1.545840711414466 , -0.1707356424814655 ]+ , [ -0.7673351760829068+ , 1.6096835638600524+ , -0.15978142628509898+ ]+ , [ -0.8208298543940763 , 1.5173670225190703 , -0.26089628483356 ]+ , [ -0.8254897540595187+ , 1.5435692417971658+ , -0.16429080162183143+ ]+ , [ -0.7750736880510629 , 1.543449628093767 , -0.3300998702021285 ]+ , [ -0.8228724369633584+ , 1.5136775995208986+ , -0.26151852913476525+ ]+ , [ -0.2414067196592598 , 1.7624263437337935 , 0.568522694152947 ]+ , [ -0.18017138946943925+ , 1.8716132855102106+ , 0.4204153875105169+ ]+ , [ -0.19212615972377473 , 1.746701943306404 , 0.6080853167177859 ]+ , [ -0.3061845992757223 , 1.7463793702756347 , 0.5599378049841232 ]+ , [ -0.13411685349983243 , 1.878463096721167 , 0.42437151237708 ]+ , [ -0.2127114473458056 , 1.877827270100134 , 0.3923832499457651 ]+ , [ -0.12443603223298119 , 1.763801627319081 , 0.6031237664636153 ]+ , [ -0.22440550617121352+ , 1.6454841596605991+ , 0.7140470378267627+ ]+ , [ -6.613605971990154e-2+ , 1.7466998928928852+ , 0.6342820077896054+ ]+ , [ -0.15978142628509898+ , 1.6096835638600524+ , 0.7673351760829068+ ]+ , [ -4.4461106873143405e-2+ , 1.8791795052256695+ , 0.4414598402161806+ ]+ , [ -9.361332416424303e-2+ , 1.871978516403293+ , 0.4470200027098403+ ]+ , [ -5.957385788325474e-2+ , 2.0280904764199947+ , 5.957385788325473e-2+ ]+ , [ -4.433393286244257e-2+ , 2.0294496432112377+ , 6.476360671202533e-2+ ]+ , [ -6.476360671202533e-2+ , 2.0294496432112377+ , 4.433393286244257e-2+ ]+ , [ -0.15197602557430875+ , 1.9868541707659964+ , 0.15197602557430875+ ]+ , [ -8.352331765507041e-2+ , 1.9874788959388558+ , 0.19585993121305537+ ]+ , [ -3.459146906505173e-2+ , 2.028192844530145+ , 7.606128724075058e-2+ ]+ , [ -8.589118029151296e-2+ , 1.9867703444520366+ , 0.19692078525583517+ ]+ , [ -9.226906344799235e-2+ , 1.9790924197542166+ , 0.21242895637465672+ ]+ , [ 0.19585993121305542+ , 1.9874788959388558+ , 8.352331765507048e-2+ ]+ , [ 7.606128724075059e-2+ , 2.028192844530145+ , 3.459146906505173e-2+ ]+ , [ 1.607119023392417e-3+ , 2.0437414787107526+ , 1.6071190233924448e-3+ ]+ , [ 4.433393286244257e-2+ , 2.0294496432112377+ , 6.476360671202529e-2+ ]+ , [ -0.43782916277770734 , 1.761318060465615 , 0.4378291627777074 ]+ , [ -0.3239549649324688 , 1.871257263189551 , 0.3239549649324688 ]+ , [ -0.4068358188765504 , 1.74614655744181 , 0.4921333979229422 ]+ , [ -0.5796425375805216 , 1.6423746127113557 , 0.4787714299468666 ]+ , [ -0.3300998702021285 , 1.543449628093767 , 0.7750736880510629 ]+ , [ -0.44011909325789345+ , 1.6091658630935883+ , 0.6493805393140207+ ]+ , [ -0.6493805393140206 , 1.6091658630935881 , 0.4401190932578934 ]+ , [ -0.5548468717518389 , 1.6090000296403644 , 0.5548468717518389 ]+ , [ -0.6590455307424883 , 1.643431115641555 , 0.3593826437345331 ]+ , [ -0.47877142994686667+ , 1.6423746127113557+ , 0.5796425375805216+ ]+ , [ -0.5499408317491791+ , 1.5161703861218003+ , -0.6646325123013266+ ]+ , [ -0.49818544023253325+ , 1.4717742047093716+ , -0.7304675058910849+ ]+ , [ -0.36541277208493106+ , 1.2970587028449376+ , -0.8457844093935878+ ]+ , [ -0.4206163489045974+ , 1.4946619231456855+ , -0.7670726439144389+ ]+ , [ 0.6031237664636153 , 1.7638016273190815 , 0.12443603223298119 ]+ , [ 0.4470200027098403 , 1.871978516403293 , 9.3613324164243e-2 ]+ , [ 0.1958599312130554+ , 1.9874788959388558+ , -8.352331765507048e-2+ ]+ , [ 0.2139712689580783 , 1.9871467005290855 , 0.0 ]+ , [ 0.19692078525583515+ , 1.9867703444520366+ , -8.589118029151296e-2+ ]+ , [ 7.606128724075059e-2+ , 2.028192844530145+ , -3.459146906505173e-2+ ]+ , [ 7.46190665960052e-2+ , 2.0298344402390818+ , -1.6023498781971e-2+ ]+ , [ 6.47636067120253e-2+ , 2.0294496432112377+ , -4.433393286244257e-2+ ]+ , [ 0.5548468717518389 , 1.6090000296403646 , -0.5548468717518389 ]+ , [ 0.5970610762747675 , 1.5413389851975459 , -0.5970610762747675 ]+ , [ 0.47877142994686667+ , 1.6423746127113557+ , -0.5796425375805215+ ]+ , [ 0.4921333979229422 , 1.74614655744181 , -0.40683581887655035 ]+ , [ 0.44011909325789345+ , 1.6091658630935881+ , -0.6493805393140206+ ]+ , [ 0.40683581887655035 , 1.74614655744181 , -0.4921333979229422 ]+ , [ -0.41460467717327976+ , 1.5166611367705878+ , -0.7558723478324711+ ]+ , [ -0.3300998702021285 , 1.543449628093767 , -0.7750736880510629 ]+ , [ -0.4731627371704971+ , 1.5418687589859696+ , -0.6987516862837263+ ]+ , [ -0.3507821217020296+ , 1.4727320843657137+ , -0.8105000296381792+ ]+ , [ 0.2244055061712136 , 1.6454841596605991 , -0.7140470378267627 ]+ , [ 0.3077193099076989 , 1.6095465768220536 , -0.7210973557556135 ]+ , [ 0.19212615972377467 , 1.7467019433064042 , -0.608085316717786 ]+ , [ 0.15978142628509895+ , 1.6096835638600524+ , -0.7673351760829068+ ]+ , [ 0.4731627371704971 , 1.5418687589859696 , -0.6987516862837264 ]+ , [ 0.549940831749179 , 1.5161703861218 , -0.6646325123013266 ]+ , [ 0.35938264373453305 , 1.643431115641555 , -0.6590455307424883 ]+ , [ 0.30618459927572234+ , 1.7463793702756347+ , -0.5599378049841232+ ]+ , [ -0.886198300038301+ , 1.2718626269690558+ , -0.25470716973839896+ ]+ , [ -0.880021667570262+ , 1.2593473684859824+ , -0.28347840420931564+ ]+ , [ -0.7140470378267627+ , 1.6454841596605991+ , -0.2244055061712136+ ]+ , [ -0.6080853167177859+ , 1.7467019433064042+ , -0.19212615972377467+ ]+ , [ -0.7210973557556135+ , 1.6095465768220534+ , -0.3077193099076989+ ]+ , [ -0.6031237664636153+ , 1.7638016273190813+ , -0.12443603223298118+ ]+ , [ -0.25711846035405095+ , 1.8714260967581882+ , 0.3788554436946959+ ]+ , [ -0.28526260180018986+ , 1.8775138180920772+ , 0.34408768727411854+ ]+ , [ -0.34656596334419265+ , 1.7617035212112468+ , 0.5125010376980281+ ]+ , [ -0.4204153875105169+ , 1.8716132855102106+ , 0.18017138946943928+ ]+ , [ -0.42437151237708004+ , 1.8784630967211673+ , 0.13411685349983243+ ]+ , [ -0.6080853167177859 , 1.7467019433064044 , 0.1921261597237747 ]+ , [ -0.568522694152947 , 1.7624263437337935 , 0.2414067196592598 ]+ , [ 0.7130046626566298 , 1.2599845756664991 , 0.5911999246804508 ]+ , [ 0.7119923208428119 , 1.1741465971067808 , 0.5903677161648285 ]+ , [ 0.45661212451945754 , 1.8719852675758741 , 0.0 ]+ , [ 0.6148766884290562 , 1.764488100164783 , 0.0 ]+ , [ 0.5911999246804508 , 1.2599845756664991 , 0.7130046626566298 ]+ , [ 0.5903677161648285 , 1.1741465971067808 , 0.7119923208428119 ]+ , [ -0.9058201893253579+ , 1.2360005696799088+ , 0.15764435612292219+ ]+ , [ -0.8861983000383011+ , 1.2718626269690558+ , 0.25470716973839896+ ]+ , [ -0.9170605544812716+ , 1.1880416776333662+ , 9.998920445288842e-2+ ]+ , [ -0.8254897540595187+ , 1.5435692417971658+ , 0.16429080162183185+ ]+ , [ -0.9173135774468574+ , 1.1869621129641121+ , 9.633778848140408e-2+ ]+ , [ -0.9157767877158494 , 1.0862351104914478 , 0.0 ]+ , [ -0.8765366554024407+ , 0.9120771417338429+ , 0.18893822974950678+ ]+ , [ -0.8235344289369638 , 0.9124253481133151 , 0.358188192546648 ]+ , [ -0.3077193099076989 , 1.6095465768220536 , 0.7210973557556136 ]+ , [ -0.35938264373453305 , 1.643431115641555 , 0.6590455307424883 ]+ , [ 0.8208298543940763+ , 1.5173670225190703+ , -0.26089628483355987+ ]+ , [ 0.8320774835437867+ , 1.4970509470350615+ , -0.26432271823494774+ ]+ , [ 0.29182490099196934+ , 0.7297971305995975+ , -0.6710598534195162+ ]+ , [ 0.47680603842395847+ , 0.9085520538404271+ , -0.7584440485557874+ ]+ , [ 0.5073894465820622 , 0.9128065261969955 , -0.7423359031749056 ]+ , [ 0.3856172858664447 , 0.908552060323908 , -0.8081008260732252 ]+ , [ 0.44008056524223993+ , 0.8781985739488957+ , -0.7505675479671516+ ]+ , [ 0.3991852390173557 , 0.8781995871178615 , -0.772837999772326 ]+ , [ 0.5388137355108445 , 0.8790068974537643 , -0.6846273996141601 ]+ , [ 9.01587519579244e-2 , 1.517441568182377 , -0.8562836943885954 ]+ , [ 7.668380969626695e-2+ , 1.6481991362041957+ , -0.7417373916617903+ ]+ , [ 0.6360954419937748 , 0.9129603520860109 , -0.636095441993775 ]+ , [ 0.5321478467750965 , 0.9088509273244271 , -0.7213170770599069 ]+ , [ 5.957385788325474e-2+ , 2.0280904764199947+ , 5.957385788325473e-2+ ]+ , [ 6.476360671202533e-2+ , 2.0294496432112377+ , 4.433393286244257e-2+ ]+ , [ 2.988199908178335e-3+ , 2.0433955508208292+ , 2.988199908178321e-3+ ]+ , [ -0.8918549800719823+ , 0.9074580789365944+ , -3.497482895265047e-2+ ]+ , [ -0.8959767931433185+ , 0.912056858034264+ , -2.7755575615628914e-17+ ]+ , [ -0.4470200027098403+ , 1.871978516403293+ , -9.361332416424299e-2+ ]+ , [ -0.42437151237708 , 1.878463096721167 , -0.13411685349983246 ]+ , [ -0.4414598402161806+ , 1.8791795052256695+ , -4.4461106873143384e-2+ ]+ , [ -0.568522694152947 , 1.7624263437337935 , -0.2414067196592598 ]+ , [ -0.18424327753183928+ , 1.4736973001288365+ , -0.8624748737615431+ ]+ , [ -0.26432271823494774+ , 1.4970509470350615+ , -0.8320774835437866+ ]+ , [ -9.114226751346857e-2+ , 1.5000742593050618+ , -0.8663032960314865+ ]+ , [ -0.26089628483355987+ , 1.5173670225190703+ , -0.8208298543940763+ ]+ , [ 0.34656596334419265 , 1.7617035212112468 , -0.512501037698028 ]+ , [ 0.2414067196592598 , 1.7624263437337935 , -0.568522694152947 ]+ , [ 0.15197602557430875+ , 1.9868541707659966+ , -0.15197602557430875+ ]+ , [ 0.378855443694696 , 1.8714260967581884 , -0.25711846035405095 ]+ , [ -0.3077193099076989+ , 1.6095465768220534+ , -0.7210973557556135+ ]+ , [ -0.17073564248146547+ , 1.545840711414466+ , -0.8228126117740737+ ]+ , [ -0.2244055061712136+ , 1.6454841596605991+ , -0.7140470378267627+ ]+ , [ -0.35938264373453305+ , 1.643431115641555+ , -0.6590455307424883+ ]+ , [ 4.446110687314339e-2+ , 1.8791795052256695+ , -0.4414598402161806+ ]+ , [ 9.361332416424298e-2+ , 1.871978516403293+ , -0.4470200027098403+ ]+ , [ 1.3877787807814457e-17+ , 1.986091229359059+ , -0.2162856218653722+ ]+ , [ 0.0 , 1.8719852675758741 , -0.45661212451945754 ]+ , [ -0.16621776154382692+ , 1.9776053853655182+ , 0.16621776154382692+ ]+ , [ -0.4470200027098403+ , 1.871978516403293+ , 9.361332416424298e-2+ ]+ , [ -0.4414598402161806+ , 1.8791795052256695+ , 4.446110687314339e-2+ ]+ , [ 8.29475648029771e-2 , 2.0282480595325483 , 0.0 ]+ , [ 7.461906659600517e-2+ , 2.0298344402390813+ , 1.6023498781971006e-2+ ]+ , [ 1.6071190233923754e-3+ , 2.0437414787107526+ , -1.607119023392417e-3+ ]+ , [ -2.988199908178335e-3+ , 2.0433955508208292+ , 2.988199908178321e-3+ ]+ , [ -1.607119023392417e-3+ , 2.0437414787107526+ , 1.6071190233924448e-3+ ]+ , [ -0.822872436963358 , 1.5136775995208986 , 0.26151852913476537 ]+ , [ -0.8260665185299888 , 1.4902762907980336 , 0.2884082499027695 ]+ , [ -0.8962126634940768 , 1.0886815800201057 , 0.192823289924143 ]+ , [ -0.8788805908385666 , 1.1814109441614336 , 0.2831308101687341 ]+ , [ -0.8800216675702618 , 1.2593473684859822 , 0.2834784042093155 ]+ , [ -0.8421531448806925+ , 1.0902530151825576+ , 0.36590860085651855+ ]+ , [ 2.7755575615628914e-17+ , 1.547398454632929+ , -0.8390008746485779+ ]+ , [ -9.015875195792439e-2+ , 1.5174415681823774+ , -0.8562836943885955+ ]+ , [ 0.310292980977805 , 0.7803637144380807 , -0.7156034934346982 ]+ , [ 0.3246126189761304 , 0.9080154562527455 , -0.8335353871385288 ]+ , [ 0.2898318932596732 , 0.8768408129798986 , -0.8180448726617308 ]+ , [ 0.21959663365004273+ , 0.9080153576084198+ , -0.8664476852626977+ ]+ , [ 0.15370753430700937 , 0.907458117258076 , -0.8796369493525762 ]+ , [ 0.1228050514414682 , 0.8753822495491729 , -0.8567258893994794 ]+ , [ 0.5725795213922535 , 0.8790071413593896 , -0.6567496895904281 ]+ , [ 0.6097884029414725 , 0.9088511267237063 , -0.65721509905736 ]+ , [ -0.4401190932578934+ , 1.6091658630935881+ , -0.6493805393140207+ ]+ , [ -0.5970610762747673+ , 1.5413389851975459+ , -0.5970610762747673+ ]+ , [ -0.30618459927572234+ , 1.7463793702756347+ , -0.5599378049841232+ ]+ , [ -0.47877142994686667+ , 1.6423746127113557+ , -0.5796425375805215+ ]+ , [ -0.40683581887655035 , 1.74614655744181 , -0.4921333979229422 ]+ , [ -0.5548468717518389+ , 1.6090000296403644+ , -0.5548468717518389+ ]+ , [ -0.34656596334419265+ , 1.7617035212112468+ , -0.512501037698028+ ]+ , [ -0.4378291627777074+ , 1.7613180604656151+ , -0.43782916277770734+ ]+ , [ -0.21242895637465672+ , 1.9790924197542168+ , -9.226906344799235e-2+ ]+ , [ -0.39238324994576507+ , 1.8778272701001337+ , -0.21271144734580563+ ]+ , [ -0.19692078525583517+ , 1.9867703444520366+ , -8.589118029151296e-2+ ]+ , [ -0.4204153875105169+ , 1.8716132855102106+ , -0.18017138946943928+ ]+ , [ 0.12443603223298116 , 1.763801627319081 , -0.6031237664636153 ]+ , [ 0.18017138946943925+ , 1.8716132855102106+ , -0.42041538751051694+ ]+ , [ 6.613605971990155e-2+ , 1.7466998928928852+ , -0.6342820077896054+ ]+ , [ 0.13411685349983243 , 1.878463096721167 , -0.42437151237708 ]+ , [ 0.0 , 1.6095711978718583 , -0.7838769658257825 ]+ , [ -0.15978142628509895+ , 1.6096835638600524+ , -0.7673351760829068+ ]+ , [ 0.0 , 1.764488100164783 , -0.6148766884290562 ]+ , [ -4.4461106873143405e-2+ , 1.8791795052256695+ , -0.4414598402161806+ ]+ , [ -7.606128724075058e-2+ , 2.028192844530145+ , 3.459146906505172e-2+ ]+ , [ -0.21397126895807833 , 1.9871467005290857 , 0.0 ]+ , [ -8.29475648029771e-2 , 2.0282480595325483 , 0.0 ]+ , [ -7.461906659600517e-2+ , 2.0298344402390813+ , 1.6023498781971006e-2+ ]+ , [ -7.461906659600517e-2+ , 2.0298344402390813+ , -1.6023498781971006e-2+ ]+ , [ -0.21397126895807833 , 1.987146700529086 , 0.0 ]+ , [ -0.2162856218653722+ , 1.9860912293590587+ , -4.293150814092422e-18+ ]+ , [ -1.6023498781971048e-2+ , 2.0298344402390818+ , -7.461906659600523e-2+ ]+ , [ 0.0 , 2.0282480595325483 , -8.294756480297713e-2 ]+ , [ -1.607119023392417e-3+ , 2.0437414787107526+ , -1.6071190233923754e-3+ ]+ , [ -3.459146906505177e-2+ , 2.028192844530145+ , -7.606128724075062e-2+ ]+ , [ -8.352331765507041e-2+ , 1.9874788959388558+ , -0.19585993121305537+ ]+ , [ -4.433393286244257e-2+ , 2.0294496432112377+ , -6.47636067120253e-2+ ]+ , [ 1.6071190233924448e-3+ , 2.0437414787107526+ , 1.6071190233924448e-3+ ]+ , [ 2.988199908178335e-3+ , 2.0433955508208292+ , -2.988199908178363e-3+ ]+ , [ 4.4333932862442554e-2+ , 2.029449643211237+ , -6.476360671202536e-2+ ]+ , [ 5.957385788325471e-2+ , 2.0280904764199947+ , -5.95738578832547e-2+ ]+ , [ -0.8457844093935878+ , 1.2970587028449376+ , 0.36541277208493106+ ]+ , [ -0.8102438227139426 , 1.2598574705147234 , 0.4474984305598345 ]+ , [ -0.8091216099094026 , 1.1765076558719514 , 0.4468960748716195 ]+ , [ -0.7119923208428118 , 1.1741465971067808 , 0.5903677161648285 ]+ , [ 0.18667466541971134+ , 0.5968790484510273+ , -0.5717139272637516+ ]+ , [ 1.7990621802653206e-2+ , 1.986082558914795e-2+ , -1.7990621802653095e-2+ ]+ , [ 0.2396999180870924 , 0.8768406254951663 , -0.8337562524679073 ]+ , [ 0.16241732047101426+ , 0.7759039565328367+ , -0.7575092969305981+ ]+ , [ -0.6987516862837264+ , 1.5418687589859696+ , -0.4731627371704971+ ]+ , [ -0.5796425375805216+ , 1.6423746127113557+ , -0.4787714299468666+ ]+ , [ -0.4921333979229422 , 1.74614655744181 , -0.40683581887655035 ]+ , [ -0.6493805393140207+ , 1.6091658630935883+ , -0.44011909325789345+ ]+ , [ -0.16621776154382692+ , 1.9776053853655182+ , -0.16621776154382692+ ]+ , [ -0.28526260180018986+ , 1.877513818092077+ , -0.34408768727411854+ ]+ , [ -0.15197602557430878+ , 1.9868541707659964+ , -0.15197602557430875+ ]+ , [ -0.34408768727411854+ , 1.8775138180920767+ , -0.28526260180018986+ ]+ , [ -0.2571184603540509+ , 1.8714260967581884+ , -0.3788554436946959+ ]+ , [ -0.3239549649324688 , 1.871257263189551 , -0.3239549649324688 ]+ , [ -0.378855443694696+ , 1.8714260967581884+ , -0.25711846035405095+ ]+ , [ -0.5599378049841232+ , 1.746379370275635+ , -0.30618459927572234+ ]+ , [ -0.512501037698028+ , 1.7617035212112468+ , -0.34656596334419265+ ]+ , [ -0.6590455307424883+ , 1.643431115641555+ , -0.35938264373453305+ ]+ , [ -0.18017138946943928+ , 1.8716132855102106+ , -0.4204153875105169+ ]+ , [ -0.13411685349983246 , 1.878463096721167 , -0.42437151237708 ]+ , [ -0.2127114473458056+ , 1.8778272701001337+ , -0.39238324994576507+ ]+ , [ -0.2414067196592598 , 1.7624263437337935 , -0.568522694152947 ]+ , [ -0.1921261597237747+ , 1.7467019433064042+ , -0.6080853167177859+ ]+ , [ -9.226906344799232e-2+ , 1.979092419754217+ , -0.21242895637465659+ ]+ , [ -0.19585993121305542+ , 1.9874788959388558+ , -8.352331765507048e-2+ ]+ , [ -7.606128724075059e-2+ , 2.028192844530145+ , -3.459146906505173e-2+ ]+ , [ 9.226906344799235e-2+ , 1.979092419754217+ , -0.21242895637465672+ ]+ , [ 0.2127114473458056 , 1.877827270100134 , -0.3923832499457651 ]+ , [ 8.589118029151292e-2+ , 1.9867703444520366+ , -0.19692078525583512+ ]+ , [ 0.25711846035405095 , 1.8714260967581884 , -0.378855443694696 ]+ , [ 0.15197602557430875+ , 1.9868541707659966+ , -0.15197602557430878+ ]+ , [ 8.35233176550705e-2+ , 1.9874788959388558+ , -0.19585993121305542+ ]+ , [ 3.459146906505177e-2+ , 2.028192844530145+ , -7.606128724075062e-2+ ]+ , [ 0.34408768727411854+ , 1.877513818092077+ , -0.28526260180018986+ ]+ , [ 0.43782916277770734+ , 1.7613180604656151+ , -0.4378291627777074+ ]+ , [ -7.668380969626697e-2+ , 1.6481991362041954+ , -0.7417373916617902+ ]+ , [ -0.1244360322329812 , 1.763801627319081 , -0.6031237664636153 ]+ , [ -6.613605971990157e-2+ , 1.746699892892885+ , -0.6342820077896053+ ]+ , [ -9.361332416424303e-2+ , 1.871978516403293+ , -0.4470200027098403+ ]+ , [ -1.6071190233924448e-3+ , 2.0437414787107526+ , 1.6071190233924448e-3+ ]+ , [ 1.602349878197104e-2+ , 2.0298344402390813+ , -7.461906659600523e-2+ ]+ , [ 1.6071190233924448e-3+ , 2.0437414787107526+ , -1.6071190233924448e-3+ ]+ , [ 4.163336342344337e-17+ , 1.9871467005290857+ , -0.2139712689580784+ ]+ , [ 1.3877787807814457e-17+ , 1.9871467005290855+ , -0.2139712689580783+ ]+ , [ -8.589118029151295e-2+ , 1.9867703444520366+ , -0.19692078525583515+ ]+ , [ -0.15197602557430875+ , 1.9868541707659964+ , -0.15197602557430875+ ]+ , [ -0.7590404339629725 , 1.0903982826657406 , 0.518569427022487 ]+ , [ -0.7423359031749055 , 0.9128065261969956 , 0.5073894465820623 ]+ , [ -5.957385788325474e-2+ , 2.0280904764199947+ , -5.957385788325473e-2+ ]+ , [ -6.476360671202533e-2+ , 2.0294496432112377+ , -4.433393286244257e-2+ ]+ , [ -2.988199908178335e-3+ , 2.0433955508208292+ , -2.988199908178321e-3+ ]+ , [ -1.6071190233924448e-3+ , 2.0437414787107526+ , -1.6071190233924448e-3+ ]+ , [ 0.16621776154382692+ , 1.9776053853655182+ , -0.16621776154382692+ ]+ , [ 0.2852626018001899 , 1.8775138180920772 , -0.3440876872741186 ]+ , [ 0.3239549649324688 , 1.871257263189551 , -0.3239549649324688 ]+ , [ 0.0 , 0.0 , 0.0 ]+ , [ 2.7755575615628914e-17 , 2.0440475966384333 , 0.0 ] ]
+ src/ConvexHull/OmniTruncated120Cell.hs view
@@ -0,0 +1,108 @@+module ConvexHull.OmniTruncated120Cell (vs120omnitrunc)+ where+import Data.List+import Math.Combinat.Permutations as P+-- http://eusebeia.dyndns.org/4d/trunc120cell+-- http://mathworld.wolfram.com/120-Cell.html++vertices :: ([Double], Bool) -> [[Double]]+vertices (coords, allperms) =+ map (map (/ sqrt 6376.10896088)) $ signsAll $+ nub $ zipWith permuteList perms (replicate 24 coords)+ where+ perms = filter (if allperms then const True else isEvenPermutation) (P.permutations 4)+ signsAll :: (Eq a, Num a) => [[a]] -> [[a]]+ signsAll = concatMap signs+ where+ signs :: (Eq a, Num a) => [a] -> [[a]]+ signs = mapM (\x -> nub [x,-x])++vs120omnitrunc :: [[Double]]+vs120omnitrunc = concatMap vertices [+ ([1, 1, 1+6*phi, 7+10*phi], True),+ ([1, 1, 3+8*phi, 7+8*phi], True),+ ([1, 1, 1+4*phi, 5+12*phi], True),+ ([1, 3, phi6, phi6], True),+ ([2, 2, 4*phi3, 6+8*phi], True),+ ([2*phi2, 4+2*phi, 4*phi3, 4*phi3], True),+ ([3+2*phi, 3+2*phi, 3+8*phi, phi6], True),+ ([1+4*phi, 3+4*phi, 3+8*phi, 3+8*phi], True),+ ([2*phi3, 2*phi3, 2+8*phi, 4*phi3], True),+ ([1, 5*phi2, 4+7*phi, 6*phi2], False),+ ([1, 2*phi4, 5+7*phi, 6+5*phi], False),+ ([1, 2, 1+5*phi, 6+11*phi], False),+ ([1, phi2, 6+9*phi, 2+8*phi], False),+ ([1, phi2, 8+9*phi, 2+6*phi], False),+ ([1, 2*phi, 7+9*phi, 2+7*phi], False),+ ([1, phi3, 5+12*phi, 3+2*phi], False),+ ([1, 3+phi, 4+9*phi, 4*phi3], False),+ ([1, 1+3*phi, 8+9*phi, 4*phi2], False),+ ([1, 1+3*phi, 6+11*phi, 4+2*phi], False),+ ([1, 4*phi, 7+9*phi, 4+5*phi], False),+ ([1, 3*phi2, 4+9*phi, 6*phi2], False),+ ([1, 2*phi3, 5+9*phi, 6+5*phi], False),+ ([2, phi2, 5+12*phi, phi4], False),+ ([2, 2+phi, 5+9*phi, 3+8*phi], False),+ ([2, phi3, 8+9*phi, phi5], False),+ ([2, 3*phi, 7+9*phi, 3*phi3], False),+ ([2, 1+3*phi, 7+10*phi, 4+3*phi], False),+ ([2, 3+2*phi, 4+9*phi, 5+7*phi], False),+ ([2, 1+4*phi, 6+9*phi, 5*phi2], False),+ ([phi2, 4+5*phi, 3+8*phi, 6*phi2], False),+ ([phi2, 3*phi3, 4*phi3, 6+5*phi], False),+ ([phi2, 3, 2*phi3, 6+11*phi], False),+ ([phi2, 3*phi, 5+12*phi, 2*phi2], False),+ ([phi2, 3+2*phi, 4*phi, 6+11*phi], False),+ ([phi2, 4+3*phi, phi6, 6*phi2], False),+ ([phi2, 3+4*phi, 6+8*phi, 6+5*phi], False),+ ([3, phi3, 7+10*phi, 3+4*phi], False),+ ([3, 2*phi2, 5+9*phi, 4+7*phi], False),+ ([3, 1+3*phi, 6+9*phi, 2*phi4], False),+ ([2*phi, 4*phi2, 4*phi3, 6*phi2], False),+ ([2*phi, phi5, phi6, 6+5*phi], False),+ ([2*phi, 2+phi, 1+3*phi, 5+12*phi], False),+ ([2*phi, 3+phi, 1+4*phi, 6+11*phi], False),+ ([2+phi, 4*phi, 7+10*phi, 3*phi2], False),+ ([2+phi, 4+2*phi, phi6, 5+7*phi], False),+ ([2+phi, 2*phi3, 7+8*phi, 5*phi2], False),+ ([phi3, 4+5*phi, 2+8*phi, 5+7*phi], False),+ ([phi3, 5*phi2, 2+7*phi, 4*phi3], False),+ ([3+phi, 3*phi, 7+10*phi, 2*phi3], False),+ ([3+phi, 3+2*phi, 6+8*phi, 4+7*phi], False),+ ([3+phi, phi4, 7+8*phi, 2*phi4], False),+ ([3*phi, 4*phi2, 3+8*phi, 5+7*phi], False),+ ([3*phi, 2+6*phi, phi6, 5*phi2], False),+ ([2*phi2, 1+4*phi, 8+9*phi, 3*phi2], False),+ ([2*phi2, 1+5*phi, 7+8*phi, 4+5*phi], False),+ ([1+3*phi, 1+6*phi, 6+8*phi, 4+5*phi], False),+ ([1+3*phi, 3*phi3, 2+8*phi, 4+7*phi], False),+ ([1+3*phi, 2+7*phi, 3+8*phi, 2*phi4], False),+ ([1+3*phi, 3+2*phi, 2*phi3, 8+9*phi], False),+ ([1+3*phi, 4+3*phi, 3+8*phi, 4*phi3], False),+ ([3+2*phi, 1+4*phi, 7+8*phi, 3*phi3], False),+ ([3+2*phi, 2*phi3, 7+9*phi, 4+3*phi], False),+ ([3+2*phi, 1+5*phi, 6+9*phi, 4*phi2], False),+ ([4*phi, phi5, 3+8*phi, 4+7*phi], False),+ ([4*phi, 2+6*phi, 4*phi3, 2*phi4], False),+ ([phi4, 4*phi2, 1+6*phi, 5+9*phi], False),+ ([phi4, 4+2*phi, 3+4*phi, 7+9*phi], False),+ ([phi4, 3*phi2, 2+8*phi, phi6], False),+ ([4+2*phi, 1+4*phi, 6+9*phi, phi5], False),+ ([1+4*phi, 1+6*phi, phi6, 3*phi3], False),+ ([1+4*phi, 3*phi2, 2+7*phi, 6+8*phi], False),+ ([1+4*phi, 4+3*phi, 2+6*phi, 5+9*phi], False),+ ([2*phi3, 1+6*phi, 4+9*phi, phi5], False),+ ([2*phi3, 1+5*phi, phi6, 2+7*phi], False),+ ([1+5*phi, 3+4*phi, 2+6*phi, 4+9*phi], False)+ ]+ where+ phi = (1+sqrt 5) / 2+ phi2 = phi*phi+ phi3 = phi2*phi+ phi4 = phi3*phi+ phi5 = phi4*phi+ phi6 = phi5*phi++++
+ src/ConvexHull/R.hs view
@@ -0,0 +1,55 @@+module ConvexHull.R+ where+import Control.Monad (when)+import ConvexHull+import qualified Data.HashMap.Strict.InsOrd as H+import qualified Data.IntMap.Strict as IM+import Data.List.Index (iconcatMap)+import Data.Maybe+import Data.Tuple.Extra (snd3)+-- import System.IO (writeFile)++convexHull3DrglCode :: [[Double]] -> Bool -> Maybe FilePath -> IO String+convexHull3DrglCode points rainbow file = do+ -- get edges --+ hull1 <- convexHull points False False Nothing+ let edges = H.elems (_hedges hull1)+ -- get triangles --+ hull2 <- convexHull points True False Nothing+ let grpFaces = groupedFacets hull2+ let triangles = map (map IM.elems . snd3) grpFaces+ -- code for edges --+ let code1 = concatMap rglSegment edges+ -- color palette --+ let code_colors = if rainbow+ then "colors <- rainbow(" ++ show (length grpFaces + 1) +++ ", alpha=0.5)\n"+ else "colors <- rep(\"blue\", " +++ show (length grpFaces + 1) ++ ")\n"+ -- code for triangles --+ let code2 = iconcatMap (\i x -> concatMap (rglTriangle i) x) triangles+ -- whole code --+ let code = "library(rgl)\n" ++ code_colors ++ code1 ++ code2+ -- write file --+ when (isJust file) $+ writeFile (fromJust file) code+ -- --+ return code+ -- auxiliary functions --+ where+ asTriplet p = (p!!0, p!!1, p!!2)+ rglSegment :: ([Double], [Double]) -> String+ rglSegment (p1', p2') =+ let p1 = asTriplet p1' in+ let p2 = asTriplet p2' in+ "segments3d(rbind(c" ++ show p1 ++ ", c" ++ show p2 +++ "), color=\"black\")\n"+ rglTriangle :: Int -> [[Double]] -> String+ rglTriangle i threepoints =+ "triangles3d(rbind(c" ++ show p1 ++ ", c" ++ show p2 +++ ", c" ++ show p3 ++ "), color=colors[" ++ show (i+1) ++ "]" +++ ", alpha=0.75)\n"+ where+ p1 = asTriplet $ threepoints!!0+ p2 = asTriplet $ threepoints!!1+ p3 = asTriplet $ threepoints!!2
+ src/ConvexHull/SnubDodecahedron/SnubDodecahedron.hs view
@@ -0,0 +1,31 @@+module ConvexHull.SnubDodecahedron.SnubDodecahedron where+import Data.List hiding (permutations)+import Math.Combinat.Permutations++signsAll2 :: (Eq a, Num a) => [[a]] -> [[a]]+signsAll2 = concatMap signs+ where+ signs :: (Eq a, Num a) => [a] -> [[a]]+ signs [x,y,z] = nub [[x,y,-z], [x,-y,z], [-x,y,z], [-x,-y,-z]]+++vertices :: ([Double], Bool) -> [[Double]]+vertices (coords, allperms) =+ -- map (map (/ sqrt 128.9619)) $+ signsAll2 $+ nub $ zipWith permuteList perms (replicate 12 coords)+ where perms = filter (if allperms then const True else isEvenPermutation) (permutations 3)++snubDodecahedron :: [[Double]]+snubDodecahedron = concatMap vertices+ [ ([2*alpha, 2, 2*beta], False)+ , ([alpha + beta/phi + phi, -alpha*phi + beta + 1/phi, alpha/phi + beta*phi-1], False)+ , ([alpha + beta/phi - phi, alpha*phi - beta + 1/phi, alpha/phi + beta*phi+1], False)+ , ([-alpha/phi + beta*phi+1, -alpha + beta/phi - phi, alpha*phi + beta - 1/phi], False)+ , ([-alpha/phi + beta*phi-1, alpha - beta/phi - phi, alpha*phi + beta + 1/phi], False) ]+ where+ alpha = xi - 1/xi+ beta = xi*phi + phi*phi + phi/xi+ xi = 1.7155615+ phi = (1 + sqrt 5)/2+
+ src/ConvexHull/Truncated120Cell3.hs view
@@ -0,0 +1,43 @@+module ConvexHull.Truncated120Cell3+ where+import Data.List+import Math.Combinat.Permutations as P+-- http://eusebeia.dyndns.org/4d/trunc120cell+-- http://mathworld.wolfram.com/120-Cell.html++signs :: (Eq a, Num a) => [a] -> [[a]]+signs = mapM (\x -> nub [x,-x])++signsAll :: (Eq a, Num a) => [[a]] -> [[a]]+signsAll = concatMap signs++vertices :: ([Double], Bool) -> [[Double]]+vertices (coords, allperms) =+ map (map (/ sqrt 270.1640786)) $ signsAll $+ nub $ zipWith permuteList perms (replicate 24 coords)+ where perms = filter (if allperms then const True else isEvenPermutation) (P.permutations 4)++vs120trunc = concatMap vertices [([1, 3+4*phi, 3+4*phi, 3+4*phi], True)+ , ([phi3, phi3, phi3, 5+6*phi], True)+ , ([2*phi2, 2*phi2, 2*phi2, 2*phi4], True)+ , ([0, 1, 4+5*phi, phi5] ,False)+ , ([0, 1, 4+7*phi, 1+3*phi], False)+ , ([0, phi2, 3*phi3, 2+5*phi], False)+ , ([0, phi2, 5+6*phi, phi4], False)+ , ([0, 2*phi, 2*phi4, 2*phi3], False)+ , ([1, phi2, 4+7*phi, 2*phi2], False)+ , ([1, phi3, 3*phi2, 3+4*phi], False)+ , ([phi2, 2*phi, 4+7*phi, phi3], False)+ , ([phi2, 2*phi2, 4+5*phi, 3+4*phi], False)+ , ([phi2, 2*phi3, 2+5*phi, 3+4*phi], False)+ , ([2*phi, phi4, phi5, 3+4*phi], False)+ , ([phi3, 2*phi2, phi5, 2+5*phi], False)+ , ([phi3, 1+3*phi, 4+5*phi, 2*phi3], False)+ , ([2*phi2, 1+3*phi, 3*phi3, phi4], False)+ ]+ where+ phi = (1+sqrt 5) / 2+ phi3 = phi*phi*phi+ phi2 = phi*phi+ phi4 = phi*phi*phi*phi+ phi5 = phi*phi*phi*phi*phi
+ src/ConvexHull/Types.hs view
@@ -0,0 +1,69 @@+module ConvexHull.Types+ where+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IM+import Data.IntSet (IntSet)+import Qhull.Types++data Vertex = Vertex {+ _point :: [Double]+ , _neighfacets :: IntSet+ , _neighvertices :: IndexSet+ , _neighridges :: IntSet+} deriving Show++data Ridge = Ridge {+ _rvertices :: IndexMap [Double]+ , _ridgeOf :: IntSet+ , _redges :: EdgeMap+} deriving Show++instance HasVertices Ridge where+ _vertices = _rvertices++instance HasEdges Ridge where+ _edges = _redges++data Facet = Facet {+ _fvertices :: IndexMap [Double]+ , _fridges :: IntSet+ , _centroid :: [Double]+ , _normal' :: [Double]+ , _offset' :: Double+ , _area :: Double+ , _neighbors :: IntSet+ , _family' :: Family+ , _fedges :: EdgeMap+} deriving Show++instance HasCenter Facet where+ _center = _centroid++instance HasEdges Facet where+ _edges = _fedges++instance HasVertices Facet where+ _vertices = _fvertices++instance HasNormal Facet where+ _normal = _normal'+ _offset = _offset'++instance HasVolume Facet where+ _volume = _area++instance HasFamily Facet where+ _family = _family'++data ConvexHull = ConvexHull {+ _hvertices :: IndexMap Vertex+ , _hfacets :: IntMap Facet+ , _hridges :: IntMap Ridge+ , _hedges :: EdgeMap+} deriving Show++instance HasEdges ConvexHull where+ _edges = _hedges++instance HasVertices ConvexHull where+ _vertices hull = IM.map _point (_hvertices hull)
+ src/Delaunay.hs view
@@ -0,0 +1,7 @@+module Delaunay+ (module X)+ where+import Delaunay.Delaunay as X+import Delaunay.Types as X+import Qhull.Shared as X+import Qhull.Types as X
+ src/Delaunay/Adjacency.hs view
@@ -0,0 +1,25 @@+module Delaunay.Adjacency+ where+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import Delaunay.Types+++vertices :: [[Double]]+vertices = [+ [ -5, -5, 16 ] -- 0+ , [ -5, 8, 3 ] -- 1+ , [ 4, -1, 3 ] -- 2+ , [ 4, -5, 7 ] -- 3+ , [ 4, -1, -10 ] -- 4+ , [ 4, -5, -10 ] -- 5+ , [ -5, 8, -10 ] -- 6+ , [ -5, -5, -10 ] -- 7+ ]+++adjMatrix :: Tesselation -> [[Int]]+adjMatrix tess = map (adjacency tess) (IM.keys ( _sites tess ))+ where+ adjacency tess i =+ map (fromEnum . ((i `IS.member`) . _neighsitesIds) . snd) $ IM.toList $ _sites tess
+ src/Delaunay/CDelaunay.hs view
@@ -0,0 +1,390 @@+{-# LINE 1 "delaunay.hsc" #-}+{-# LANGUAGE ForeignFunctionInterface #-}+module Delaunay.CDelaunay+ ( cTesselationToTesselation+ , c_tesselation )+ where+import Control.Monad ((<$!>))+import qualified Data.HashMap.Strict.InsOrd as H+import Data.IntMap.Strict (fromAscList, (!))+import qualified Data.IntSet as IS+import Data.List+import Data.Maybe+import Data.Tuple.Extra (both, fst3, snd3, thd3, (&&&))+import Delaunay.Types+import Foreign+import Foreign.C.Types+import Qhull.Types++data CSite = CSite {+ __id :: CUInt+ , __neighsites :: Ptr CUInt+ , __nneighsites :: CUInt+ , __neighridgesids :: Ptr CUInt+ , __nneighridges :: CUInt+ , __neightiles :: Ptr CUInt+ , __nneightiles :: CUInt+}++instance Storable CSite where+ sizeOf __ = (56)+{-# LINE 29 "delaunay.hsc" #-}+ alignment __ = 8+{-# LINE 30 "delaunay.hsc" #-}+ peek ptr = do+ id' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 32 "delaunay.hsc" #-}+ neighsites' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 33 "delaunay.hsc" #-}+ nneighsites' <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr+{-# LINE 34 "delaunay.hsc" #-}+ neighridgesids' <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr+{-# LINE 35 "delaunay.hsc" #-}+ nneighridges' <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr+{-# LINE 36 "delaunay.hsc" #-}+ neightiles' <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr+{-# LINE 37 "delaunay.hsc" #-}+ nneightiles' <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr+{-# LINE 38 "delaunay.hsc" #-}+ return CSite { __id = id'+ , __neighsites = neighsites'+ , __nneighsites = nneighsites'+ , __neighridgesids = neighridgesids'+ , __nneighridges = nneighridges'+ , __neightiles = neightiles'+ , __nneightiles = nneightiles'+ }+ poke ptr (CSite r1 r2 r3 r4 r5 r6 r7)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 49 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 50 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3+{-# LINE 51 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4+{-# LINE 52 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5+{-# LINE 53 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6+{-# LINE 54 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r7+{-# LINE 55 "delaunay.hsc" #-}++cSiteToSite :: [[Double]] -> CSite -> IO (Int, Site, [(Int,Int)])+cSiteToSite sites csite = do+ let id' = fromIntegral $ __id csite+ nneighsites = fromIntegral $ __nneighsites csite+ nneighridges = fromIntegral $ __nneighridges csite+ nneightiles = fromIntegral $ __nneightiles csite+ point = sites !! id'+ neighsites <- (<$!>) (map fromIntegral)+ (peekArray nneighsites (__neighsites csite))+ neighridges <- (<$!>) (map fromIntegral)+ (peekArray nneighridges (__neighridgesids csite))+ neightiles <- (<$!>) (map fromIntegral)+ (peekArray nneightiles (__neightiles csite))+ return ( id'+ , Site {+ _point = point+ , _neighsitesIds = IS.fromAscList neighsites+ , _neighfacetsIds = IS.fromAscList neighridges+ , _neightilesIds = IS.fromAscList neightiles+ }+ , map (\j -> (id', j)) (filterAscList id' neighsites) )+ where+ filterAscList :: Int -> [Int] -> [Int]+ filterAscList n list =+ let i = findIndex (>n) list in+ if isJust i+ then drop (fromJust i) list+ else []++data CSimplex = CSimplex {+ __sitesids :: Ptr CUInt+ , __center :: Ptr CDouble+ , __radius :: CDouble+ , __volume :: CDouble+}++instance Storable CSimplex where+ sizeOf __ = (32)+{-# LINE 85 "delaunay.hsc" #-}+ alignment __ = 8+{-# LINE 86 "delaunay.hsc" #-}+ peek ptr = do+ sitesids' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 88 "delaunay.hsc" #-}+ center' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 89 "delaunay.hsc" #-}+ radius' <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr+{-# LINE 90 "delaunay.hsc" #-}+ volume' <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr+{-# LINE 91 "delaunay.hsc" #-}+ return CSimplex { __sitesids = sitesids'+ , __center = center'+ , __radius = radius'+ , __volume = volume'+ }+ poke ptr (CSimplex r1 r2 r3 r4)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 99 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 100 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3+{-# LINE 101 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4+{-# LINE 102 "delaunay.hsc" #-}++cSimplexToSimplex :: [[Double]] -> Int -> CSimplex -> IO Simplex+cSimplexToSimplex sites simplexdim csimplex = do+ let radius = cdbl2dbl $ __radius csimplex+ volume = cdbl2dbl $ __volume csimplex+ dim = length (head sites)+ sitesids <- (<$!>) (map fromIntegral)+ (peekArray simplexdim (__sitesids csimplex))+ -- putStrLn "cSimplexToSimplex - peek sitesids"+ let points = fromAscList+ (zip sitesids (map ((!!) sites) sitesids))+ center <- (<$!>) (map cdbl2dbl) (peekArray dim (__center csimplex))+ -- putStrLn "cSimplexToSimplex - peek center"+ return Simplex { _vertices' = points+ , _circumcenter = center+ , _circumradius = radius+ , _volume' = volume }+ where+ cdbl2dbl :: CDouble -> Double+ cdbl2dbl x = if isNaN x then 0/0 else realToFrac x++data CSubTile = CSubTile {+ __id' :: CUInt+ , __subsimplex :: CSimplex+ , __ridgeOf1 :: CUInt+ , __ridgeOf2 :: CInt+ , __normal :: Ptr CDouble+ , __offset :: CDouble+}++instance Storable CSubTile where+ sizeOf __ = (72)+{-# LINE 132 "delaunay.hsc" #-}+ alignment __ = 8+{-# LINE 133 "delaunay.hsc" #-}+ peek ptr = do+ id' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 135 "delaunay.hsc" #-}+ simplex' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 136 "delaunay.hsc" #-}+ ridgeOf1' <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr+{-# LINE 137 "delaunay.hsc" #-}+ ridgeOf2' <- (\hsc_ptr -> peekByteOff hsc_ptr 44) ptr+{-# LINE 138 "delaunay.hsc" #-}+ normal' <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr+{-# LINE 139 "delaunay.hsc" #-}+ offset' <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr+{-# LINE 140 "delaunay.hsc" #-}+ return CSubTile { __id' = id'+ , __subsimplex = simplex'+ , __ridgeOf1 = ridgeOf1'+ , __ridgeOf2 = ridgeOf2'+ , __normal = normal'+ , __offset = offset' }+ poke ptr (CSubTile r1 r2 r3 r4 r5 r6)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 149 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 150 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r3+{-# LINE 151 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 44) ptr r4+{-# LINE 152 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r5+{-# LINE 153 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r6+{-# LINE 154 "delaunay.hsc" #-}++cSubTiletoTileFacet :: [[Double]] -> CSubTile -> IO (Int, TileFacet)+cSubTiletoTileFacet points csubtile = do+ let dim = length (head points)+ ridgeOf1 = fromIntegral $ __ridgeOf1 csubtile+ ridgeOf2 = fromIntegral $ __ridgeOf2 csubtile+ ridgeOf = if ridgeOf2 == -1 then [ridgeOf1] else [ridgeOf1, ridgeOf2]+ id' = fromIntegral $ __id' csubtile+ subsimplex = __subsimplex csubtile+ offset = realToFrac $ __offset csubtile+ simplex <- cSimplexToSimplex points dim subsimplex+ normal <- (<$!>) (map realToFrac) (peekArray dim (__normal csubtile))+ -- putStrLn "cSubTiletoTileFacet - peek normal"+ return (id', TileFacet { _subsimplex = simplex+ , _facetOf = IS.fromAscList ridgeOf+ , _normal' = normal+ , _offset' = offset })++data CTile = CTile {+ __id'' :: CUInt+ , __simplex :: CSimplex+ , __neighbors :: Ptr CUInt+ , __nneighbors :: CUInt+ , __ridgesids :: Ptr CUInt+ , __nridges :: CUInt+ , __family :: CInt+ , __orientation :: CInt+}++instance Storable CTile where+ sizeOf __ = (80)+{-# LINE 184 "delaunay.hsc" #-}+ alignment __ = 8+{-# LINE 185 "delaunay.hsc" #-}+ peek ptr = do+ id' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 187 "delaunay.hsc" #-}+ simplex' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 188 "delaunay.hsc" #-}+ neighbors' <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr+{-# LINE 189 "delaunay.hsc" #-}+ nneighbors' <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr+{-# LINE 190 "delaunay.hsc" #-}+ ridgesids' <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr+{-# LINE 191 "delaunay.hsc" #-}+ nridges' <- (\hsc_ptr -> peekByteOff hsc_ptr 64) ptr+{-# LINE 192 "delaunay.hsc" #-}+ family' <- (\hsc_ptr -> peekByteOff hsc_ptr 68) ptr+{-# LINE 193 "delaunay.hsc" #-}+ orient <- (\hsc_ptr -> peekByteOff hsc_ptr 72) ptr+{-# LINE 194 "delaunay.hsc" #-}+ return CTile { __id'' = id'+ , __simplex = simplex'+ , __neighbors = neighbors'+ , __nneighbors = nneighbors'+ , __ridgesids = ridgesids'+ , __nridges = nridges'+ , __family = family'+ , __orientation = orient+ }+ poke ptr (CTile r1 r2 r3 r4 r5 r6 r7 r8)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 206 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 207 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r3+{-# LINE 208 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r4+{-# LINE 209 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r5+{-# LINE 210 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 64) ptr r6+{-# LINE 211 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 68) ptr r7+{-# LINE 212 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 72) ptr r8+{-# LINE 213 "delaunay.hsc" #-}++cTileToTile :: [[Double]] -> CTile -> IO (Int, Tile)+cTileToTile points ctile = do+ let id' = fromIntegral $ __id'' ctile+ csimplex = __simplex ctile+ nneighbors = fromIntegral $ __nneighbors ctile+ nridges = fromIntegral $ __nridges ctile+ family = __family ctile+ orient = __orientation ctile+ dim = length (head points)+ -- putStrLn $ "tile id: " ++ show id'+ simplex <- cSimplexToSimplex points (dim+1) csimplex+ neighbors <- (<$!>) (map fromIntegral)+ (peekArray nneighbors (__neighbors ctile))+ -- putStrLn "cTileToTile - peek neighbors"+ ridgesids <- (<$!>) (map fromIntegral)+ (peekArray nridges (__ridgesids ctile))+ -- putStrLn "cTileToTile - peek ridges"+ return (id', Tile { _simplex = simplex+ , _neighborsIds = IS.fromAscList neighbors+ , _facetsIds = IS.fromAscList ridgesids+ , _family' = if family == -1+ then None+ else Family (fromIntegral family)+ , _toporiented = orient == 1 })++data CTesselation = CTesselation {+ __sites :: Ptr CSite+ , __tiles :: Ptr CTile+ , __ntiles :: CUInt+ , __subtiles :: Ptr CSubTile+ , __nsubtiles :: CUInt+}++instance Storable CTesselation where+ sizeOf __ = (40)+{-# LINE 246 "delaunay.hsc" #-}+ alignment __ = 8+{-# LINE 247 "delaunay.hsc" #-}+ peek ptr = do+ sites' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr+{-# LINE 249 "delaunay.hsc" #-}+ tiles' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr+{-# LINE 250 "delaunay.hsc" #-}+ ntiles' <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr+{-# LINE 251 "delaunay.hsc" #-}+ subtiles' <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr+{-# LINE 252 "delaunay.hsc" #-}+ nsubtiles' <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr+{-# LINE 253 "delaunay.hsc" #-}+ return CTesselation {+ __sites = sites'+ , __tiles = tiles'+ , __ntiles = ntiles'+ , __subtiles = subtiles'+ , __nsubtiles = nsubtiles'+ }+ poke ptr (CTesselation r1 r2 r3 r4 r5)+ = do+ (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1+{-# LINE 263 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2+{-# LINE 264 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3+{-# LINE 265 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4+{-# LINE 266 "delaunay.hsc" #-}+ (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5+{-# LINE 267 "delaunay.hsc" #-}++foreign import ccall unsafe "tesselation" c_tesselation+ :: Ptr CDouble -- sites+ -> CUInt -- dim+ -> CUInt -- nsites+ -> CUInt -- 0/1, point at infinity+ -> CUInt -- 0/1, include degenerate+ -> CDouble -- volume threshold+ -> Ptr CUInt -- exitcode+ -> IO (Ptr CTesselation)++cTesselationToTesselation :: [[Double]] -> CTesselation -> IO Tesselation+cTesselationToTesselation vertices ctess = do+ let ntiles = fromIntegral $ __ntiles ctess+ nsubtiles = fromIntegral $ __nsubtiles ctess+ nsites = length vertices+ sites'' <- peekArray nsites (__sites ctess)+ -- putStrLn "peek sites"+ tiles'' <- peekArray ntiles (__tiles ctess)+ -- putStrLn "peek tiles"+ subtiles'' <- peekArray nsubtiles (__subtiles ctess)+ -- putStrLn "peek ridges"+ sites' <- mapM (cSiteToSite vertices) sites''+ let sites = fromAscList (map (fst3 &&& snd3) sites')+ edgesIndices = concatMap thd3 sites'+ edges = map (toPair &&& both (_point . ((!) sites))) edgesIndices+ tiles' <- mapM (cTileToTile vertices) tiles''+ -- putStrLn "mapped cTileToTile"+ subtiles' <- mapM (cSubTiletoTileFacet vertices) subtiles''+ -- putStrLn "mapped cSubTiletoTileFacet"+ return Tesselation+ { _sites = sites+ , _tiles = fromAscList tiles'+ , _tilefacets = fromAscList subtiles'+ , _edges' = H.fromList edges }+ where+ toPair (i,j) = Pair i j
+ src/Delaunay/Delaunay.hs view
@@ -0,0 +1,94 @@+module Delaunay.Delaunay+ where+import Control.Monad (unless, when)+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import Data.List.Unique (allUnique)+import Data.Maybe+import Delaunay.CDelaunay+import Delaunay.Types+import Foreign.C.Types+import Foreign.Marshal.Alloc (free, mallocBytes)+import Foreign.Marshal.Array (pokeArray)+import Foreign.Storable (peek, sizeOf)+import Qhull.Types++delaunay :: [[Double]] -- sites (vertices)+ -> Bool -- add a point at infinity+ -> Bool -- include degenerate tiles+ -> Maybe Double -- volume threshold+ -> IO Tesselation+delaunay sites atinfinity degenerate vthreshold = do+ let n = length sites+ dim = length (head sites)+ when (dim < 2) $+ error "dimension must be at least 2"+ when (n <= dim+1) $+ error "insufficient number of points"+ unless (all (== dim) (map length (tail sites))) $+ error "the points must have the same dimension"+ unless (allUnique sites) $+ error "some points are duplicated"+ let vthreshold' = fromMaybe 0 vthreshold + sitesPtr <- mallocBytes (n * dim * sizeOf (undefined :: CDouble))+ pokeArray sitesPtr (concatMap (map realToFrac) sites)+ exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt))+ resultPtr <- c_tesselation sitesPtr+ (fromIntegral dim) (fromIntegral n)+ (fromIntegral $ fromEnum atinfinity)+ (fromIntegral $ fromEnum degenerate)+ (realToFrac vthreshold') exitcodePtr+ exitcode <- peek exitcodePtr+ free exitcodePtr+ free sitesPtr+ if exitcode /= 0+ then do+ free resultPtr+ error $ "qhull returned an error (code " ++ show exitcode ++ ")"+ else do+ result <- peek resultPtr+ out <- cTesselationToTesselation sites result+ free resultPtr+ return out++-- | tile facets a vertex belongs to, vertex given by its index;+-- the output is the empty map if the index is not valid+vertexNeighborFacets :: Tesselation -> Index -> IntMap TileFacet+vertexNeighborFacets tess i = IM.restrictKeys (_tilefacets tess) ids+ where+ ids = maybe IS.empty _neighfacetsIds (IM.lookup i (_sites tess))++-- | whether a tile facet is sandwiched between two tiles+sandwichedFacet :: TileFacet -> Bool+sandwichedFacet tilefacet = IS.size (_facetOf tilefacet) == 2++-- | the tiles a facet belongs to+facetOf :: Tesselation -> TileFacet -> IntMap Tile+facetOf tess tilefacet = IM.restrictKeys (_tiles tess) (_facetOf tilefacet)++-- | the families of the tiles a facet belongs to+facetFamilies :: Tesselation -> TileFacet -> IntMap Family+facetFamilies tess tilefacet = IM.map _family (facetOf tess tilefacet)++-- | the circumcenters of the tiles a facet belongs to+facetCenters :: Tesselation -> TileFacet -> IntMap [Double]+facetCenters tess tilefacet =+ IM.map _center (facetOf tess tilefacet)++funofFacetToFunofInt :: (Tesselation -> TileFacet -> IntMap a)+ -> (Tesselation -> Int -> IntMap a)+funofFacetToFunofInt f tess i =+ maybe IM.empty (f tess) (IM.lookup i (_tilefacets tess))++-- | the tiles a facet belongs to, facet given by its id+facetOf' :: Tesselation -> Int -> IntMap Tile+facetOf' = funofFacetToFunofInt facetOf++-- | the families of the tiles a facet belongs to, facet given by its id+facetFamilies' :: Tesselation -> Int -> IntMap Family+facetFamilies' = funofFacetToFunofInt facetFamilies++-- | the circumcenters of the tiles a facet belongs to, facet given by its id+facetCenters' :: Tesselation -> Int -> IntMap [Double]+facetCenters' = funofFacetToFunofInt facetCenters
+ src/Delaunay/Examples.hs view
@@ -0,0 +1,910 @@+module Delaunay.Examples+ ( module X+ , duoCylinder)+ where+import ConvexHull.Examples as X++duoCylinder :: [[Double]]+duoCylinder =+ [+ [1, 0, 1, 0],+ [1, 0, 0.978147600733806, 0.207911690817759],+ [1, 0, 0.913545457642601, 0.4067366430758],+ [1, 0, 0.809016994374947, 0.587785252292473],+ [1, 0, 0.669130606358858, 0.743144825477394],+ [1, 0, 0.5, 0.866025403784439],+ [1, 0, 0.309016994374947, 0.951056516295154],+ [1, 0, 0.104528463267653, 0.994521895368273],+ [1, 0, -0.104528463267653, 0.994521895368273],+ [1, 0, -0.309016994374947, 0.951056516295154],+ [1, 0, -0.5, 0.866025403784439],+ [1, 0, -0.669130606358858, 0.743144825477394],+ [1, 0, -0.809016994374947, 0.587785252292473],+ [1, 0, -0.913545457642601, 0.4067366430758],+ [1, 0, -0.978147600733806, 0.207911690817759],+ [1, 0, -1, 5.665498452323e-16],+ [1, 0, -0.978147600733806, -0.207911690817759],+ [1, 0, -0.913545457642601, -0.4067366430758],+ [1, 0, -0.809016994374948, -0.587785252292473],+ [1, 0, -0.669130606358858, -0.743144825477394],+ [1, 0, -0.5, -0.866025403784438],+ [1, 0, -0.309016994374948, -0.951056516295154],+ [1, 0, -0.104528463267654, -0.994521895368273],+ [1, 0, 0.104528463267653, -0.994521895368273],+ [1, 0, 0.309016994374947, -0.951056516295154],+ [1, 0, 0.5, -0.866025403784439],+ [1, 0, 0.669130606358858, -0.743144825477394],+ [1, 0, 0.809016994374947, -0.587785252292473],+ [1, 0, 0.913545457642601, -0.4067366430758],+ [1, 0, 0.978147600733806, -0.207911690817759],+ [0.978147600733806, 0.207911690817759, 1, 0],+ [0.978147600733806, 0.207911690817759, 0.978147600733806, 0.207911690817759],+ [0.978147600733806, 0.207911690817759, 0.913545457642601, 0.4067366430758],+ [0.978147600733806, 0.207911690817759, 0.809016994374947, 0.587785252292473],+ [0.978147600733806, 0.207911690817759, 0.669130606358858, 0.743144825477394],+ [0.978147600733806, 0.207911690817759, 0.5, 0.866025403784439],+ [0.978147600733806, 0.207911690817759, 0.309016994374947, 0.951056516295154],+ [0.978147600733806, 0.207911690817759, 0.104528463267653, 0.994521895368273],+ [0.978147600733806, 0.207911690817759, -0.104528463267653, 0.994521895368273],+ [0.978147600733806, 0.207911690817759, -0.309016994374947, 0.951056516295154],+ [0.978147600733806, 0.207911690817759, -0.5, 0.866025403784439],+ [0.978147600733806, 0.207911690817759, -0.669130606358858, 0.743144825477394],+ [0.978147600733806, 0.207911690817759, -0.809016994374947, 0.587785252292473],+ [0.978147600733806, 0.207911690817759, -0.913545457642601, 0.4067366430758],+ [0.978147600733806, 0.207911690817759, -0.978147600733806, 0.207911690817759],+ [0.978147600733806, 0.207911690817759, -1, 5.665498452323e-16],+ [0.978147600733806, 0.207911690817759, -0.978147600733806, -0.207911690817759],+ [0.978147600733806, 0.207911690817759, -0.913545457642601, -0.4067366430758],+ [0.978147600733806, 0.207911690817759, -0.809016994374948, -0.587785252292473],+ [0.978147600733806, 0.207911690817759, -0.669130606358858, -0.743144825477394],+ [0.978147600733806, 0.207911690817759, -0.5, -0.866025403784438],+ [0.978147600733806, 0.207911690817759, -0.309016994374948, -0.951056516295154],+ [0.978147600733806, 0.207911690817759, -0.104528463267654, -0.994521895368273],+ [0.978147600733806, 0.207911690817759, 0.104528463267653, -0.994521895368273],+ [0.978147600733806, 0.207911690817759, 0.309016994374947, -0.951056516295154],+ [0.978147600733806, 0.207911690817759, 0.5, -0.866025403784439],+ [0.978147600733806, 0.207911690817759, 0.669130606358858, -0.743144825477394],+ [0.978147600733806, 0.207911690817759, 0.809016994374947, -0.587785252292473],+ [0.978147600733806, 0.207911690817759, 0.913545457642601, -0.4067366430758],+ [0.978147600733806, 0.207911690817759, 0.978147600733806, -0.207911690817759],+ [0.913545457642601, 0.4067366430758, 1, 0],+ [0.913545457642601, 0.4067366430758, 0.978147600733806, 0.207911690817759],+ [0.913545457642601, 0.4067366430758, 0.913545457642601, 0.4067366430758],+ [0.913545457642601, 0.4067366430758, 0.809016994374947, 0.587785252292473],+ [0.913545457642601, 0.4067366430758, 0.669130606358858, 0.743144825477394],+ [0.913545457642601, 0.4067366430758, 0.5, 0.866025403784439],+ [0.913545457642601, 0.4067366430758, 0.309016994374947, 0.951056516295154],+ [0.913545457642601, 0.4067366430758, 0.104528463267653, 0.994521895368273],+ [0.913545457642601, 0.4067366430758, -0.104528463267653, 0.994521895368273],+ [0.913545457642601, 0.4067366430758, -0.309016994374947, 0.951056516295154],+ [0.913545457642601, 0.4067366430758, -0.5, 0.866025403784439],+ [0.913545457642601, 0.4067366430758, -0.669130606358858, 0.743144825477394],+ [0.913545457642601, 0.4067366430758, -0.809016994374947, 0.587785252292473],+ [0.913545457642601, 0.4067366430758, -0.913545457642601, 0.4067366430758],+ [0.913545457642601, 0.4067366430758, -0.978147600733806, 0.207911690817759],+ [0.913545457642601, 0.4067366430758, -1, 5.665498452323e-16],+ [0.913545457642601, 0.4067366430758, -0.978147600733806, -0.207911690817759],+ [0.913545457642601, 0.4067366430758, -0.913545457642601, -0.4067366430758],+ [0.913545457642601, 0.4067366430758, -0.809016994374948, -0.587785252292473],+ [0.913545457642601, 0.4067366430758, -0.669130606358858, -0.743144825477394],+ [0.913545457642601, 0.4067366430758, -0.5, -0.866025403784438],+ [0.913545457642601, 0.4067366430758, -0.309016994374948, -0.951056516295154],+ [0.913545457642601, 0.4067366430758, -0.104528463267654, -0.994521895368273],+ [0.913545457642601, 0.4067366430758, 0.104528463267653, -0.994521895368273],+ [0.913545457642601, 0.4067366430758, 0.309016994374947, -0.951056516295154],+ [0.913545457642601, 0.4067366430758, 0.5, -0.866025403784439],+ [0.913545457642601, 0.4067366430758, 0.669130606358858, -0.743144825477394],+ [0.913545457642601, 0.4067366430758, 0.809016994374947, -0.587785252292473],+ [0.913545457642601, 0.4067366430758, 0.913545457642601, -0.4067366430758],+ [0.913545457642601, 0.4067366430758, 0.978147600733806, -0.207911690817759],+ [0.809016994374947, 0.587785252292473, 1, 0],+ [0.809016994374947, 0.587785252292473, 0.978147600733806, 0.207911690817759],+ [0.809016994374947, 0.587785252292473, 0.913545457642601, 0.4067366430758],+ [0.809016994374947, 0.587785252292473, 0.809016994374947, 0.587785252292473],+ [0.809016994374947, 0.587785252292473, 0.669130606358858, 0.743144825477394],+ [0.809016994374947, 0.587785252292473, 0.5, 0.866025403784439],+ [0.809016994374947, 0.587785252292473, 0.309016994374947, 0.951056516295154],+ [0.809016994374947, 0.587785252292473, 0.104528463267653, 0.994521895368273],+ [0.809016994374947, 0.587785252292473, -0.104528463267653, 0.994521895368273],+ [0.809016994374947, 0.587785252292473, -0.309016994374947, 0.951056516295154],+ [0.809016994374947, 0.587785252292473, -0.5, 0.866025403784439],+ [0.809016994374947, 0.587785252292473, -0.669130606358858, 0.743144825477394],+ [0.809016994374947, 0.587785252292473, -0.809016994374947, 0.587785252292473],+ [0.809016994374947, 0.587785252292473, -0.913545457642601, 0.4067366430758],+ [0.809016994374947, 0.587785252292473, -0.978147600733806, 0.207911690817759],+ [0.809016994374947, 0.587785252292473, -1, 5.665498452323e-16],+ [0.809016994374947, 0.587785252292473, -0.978147600733806, -0.207911690817759],+ [0.809016994374947, 0.587785252292473, -0.913545457642601, -0.4067366430758],+ [0.809016994374947, 0.587785252292473, -0.809016994374948, -0.587785252292473],+ [0.809016994374947, 0.587785252292473, -0.669130606358858, -0.743144825477394],+ [0.809016994374947, 0.587785252292473, -0.5, -0.866025403784438],+ [0.809016994374947, 0.587785252292473, -0.309016994374948, -0.951056516295154],+ [0.809016994374947, 0.587785252292473, -0.104528463267654, -0.994521895368273],+ [0.809016994374947, 0.587785252292473, 0.104528463267653, -0.994521895368273],+ [0.809016994374947, 0.587785252292473, 0.309016994374947, -0.951056516295154],+ [0.809016994374947, 0.587785252292473, 0.5, -0.866025403784439],+ [0.809016994374947, 0.587785252292473, 0.669130606358858, -0.743144825477394],+ [0.809016994374947, 0.587785252292473, 0.809016994374947, -0.587785252292473],+ [0.809016994374947, 0.587785252292473, 0.913545457642601, -0.4067366430758],+ [0.809016994374947, 0.587785252292473, 0.978147600733806, -0.207911690817759],+ [0.669130606358858, 0.743144825477394, 1, 0],+ [0.669130606358858, 0.743144825477394, 0.978147600733806, 0.207911690817759],+ [0.669130606358858, 0.743144825477394, 0.913545457642601, 0.4067366430758],+ [0.669130606358858, 0.743144825477394, 0.809016994374947, 0.587785252292473],+ [0.669130606358858, 0.743144825477394, 0.669130606358858, 0.743144825477394],+ [0.669130606358858, 0.743144825477394, 0.5, 0.866025403784439],+ [0.669130606358858, 0.743144825477394, 0.309016994374947, 0.951056516295154],+ [0.669130606358858, 0.743144825477394, 0.104528463267653, 0.994521895368273],+ [0.669130606358858, 0.743144825477394, -0.104528463267653, 0.994521895368273],+ [0.669130606358858, 0.743144825477394, -0.309016994374947, 0.951056516295154],+ [0.669130606358858, 0.743144825477394, -0.5, 0.866025403784439],+ [0.669130606358858, 0.743144825477394, -0.669130606358858, 0.743144825477394],+ [0.669130606358858, 0.743144825477394, -0.809016994374947, 0.587785252292473],+ [0.669130606358858, 0.743144825477394, -0.913545457642601, 0.4067366430758],+ [0.669130606358858, 0.743144825477394, -0.978147600733806, 0.207911690817759],+ [0.669130606358858, 0.743144825477394, -1, 5.665498452323e-16],+ [0.669130606358858, 0.743144825477394, -0.978147600733806, -0.207911690817759],+ [0.669130606358858, 0.743144825477394, -0.913545457642601, -0.4067366430758],+ [0.669130606358858, 0.743144825477394, -0.809016994374948, -0.587785252292473],+ [0.669130606358858, 0.743144825477394, -0.669130606358858, -0.743144825477394],+ [0.669130606358858, 0.743144825477394, -0.5, -0.866025403784438],+ [0.669130606358858, 0.743144825477394, -0.309016994374948, -0.951056516295154],+ [0.669130606358858, 0.743144825477394, -0.104528463267654, -0.994521895368273],+ [0.669130606358858, 0.743144825477394, 0.104528463267653, -0.994521895368273],+ [0.669130606358858, 0.743144825477394, 0.309016994374947, -0.951056516295154],+ [0.669130606358858, 0.743144825477394, 0.5, -0.866025403784439],+ [0.669130606358858, 0.743144825477394, 0.669130606358858, -0.743144825477394],+ [0.669130606358858, 0.743144825477394, 0.809016994374947, -0.587785252292473],+ [0.669130606358858, 0.743144825477394, 0.913545457642601, -0.4067366430758],+ [0.669130606358858, 0.743144825477394, 0.978147600733806, -0.207911690817759],+ [0.5, 0.866025403784439, 1, 0],+ [0.5, 0.866025403784439, 0.978147600733806, 0.207911690817759],+ [0.5, 0.866025403784439, 0.913545457642601, 0.4067366430758],+ [0.5, 0.866025403784439, 0.809016994374947, 0.587785252292473],+ [0.5, 0.866025403784439, 0.669130606358858, 0.743144825477394],+ [0.5, 0.866025403784439, 0.5, 0.866025403784439],+ [0.5, 0.866025403784439, 0.309016994374947, 0.951056516295154],+ [0.5, 0.866025403784439, 0.104528463267653, 0.994521895368273],+ [0.5, 0.866025403784439, -0.104528463267653, 0.994521895368273],+ [0.5, 0.866025403784439, -0.309016994374947, 0.951056516295154],+ [0.5, 0.866025403784439, -0.5, 0.866025403784439],+ [0.5, 0.866025403784439, -0.669130606358858, 0.743144825477394],+ [0.5, 0.866025403784439, -0.809016994374947, 0.587785252292473],+ [0.5, 0.866025403784439, -0.913545457642601, 0.4067366430758],+ [0.5, 0.866025403784439, -0.978147600733806, 0.207911690817759],+ [0.5, 0.866025403784439, -1, 5.665498452323e-16],+ [0.5, 0.866025403784439, -0.978147600733806, -0.207911690817759],+ [0.5, 0.866025403784439, -0.913545457642601, -0.4067366430758],+ [0.5, 0.866025403784439, -0.809016994374948, -0.587785252292473],+ [0.5, 0.866025403784439, -0.669130606358858, -0.743144825477394],+ [0.5, 0.866025403784439, -0.5, -0.866025403784438],+ [0.5, 0.866025403784439, -0.309016994374948, -0.951056516295154],+ [0.5, 0.866025403784439, -0.104528463267654, -0.994521895368273],+ [0.5, 0.866025403784439, 0.104528463267653, -0.994521895368273],+ [0.5, 0.866025403784439, 0.309016994374947, -0.951056516295154],+ [0.5, 0.866025403784439, 0.5, -0.866025403784439],+ [0.5, 0.866025403784439, 0.669130606358858, -0.743144825477394],+ [0.5, 0.866025403784439, 0.809016994374947, -0.587785252292473],+ [0.5, 0.866025403784439, 0.913545457642601, -0.4067366430758],+ [0.5, 0.866025403784439, 0.978147600733806, -0.207911690817759],+ [0.309016994374947, 0.951056516295154, 1, 0],+ [0.309016994374947, 0.951056516295154, 0.978147600733806, 0.207911690817759],+ [0.309016994374947, 0.951056516295154, 0.913545457642601, 0.4067366430758],+ [0.309016994374947, 0.951056516295154, 0.809016994374947, 0.587785252292473],+ [0.309016994374947, 0.951056516295154, 0.669130606358858, 0.743144825477394],+ [0.309016994374947, 0.951056516295154, 0.5, 0.866025403784439],+ [0.309016994374947, 0.951056516295154, 0.309016994374947, 0.951056516295154],+ [0.309016994374947, 0.951056516295154, 0.104528463267653, 0.994521895368273],+ [0.309016994374947, 0.951056516295154, -0.104528463267653, 0.994521895368273],+ [0.309016994374947, 0.951056516295154, -0.309016994374947, 0.951056516295154],+ [0.309016994374947, 0.951056516295154, -0.5, 0.866025403784439],+ [0.309016994374947, 0.951056516295154, -0.669130606358858, 0.743144825477394],+ [0.309016994374947, 0.951056516295154, -0.809016994374947, 0.587785252292473],+ [0.309016994374947, 0.951056516295154, -0.913545457642601, 0.4067366430758],+ [0.309016994374947, 0.951056516295154, -0.978147600733806, 0.207911690817759],+ [0.309016994374947, 0.951056516295154, -1, 5.665498452323e-16],+ [0.309016994374947, 0.951056516295154, -0.978147600733806, -0.207911690817759],+ [0.309016994374947, 0.951056516295154, -0.913545457642601, -0.4067366430758],+ [0.309016994374947, 0.951056516295154, -0.809016994374948, -0.587785252292473],+ [0.309016994374947, 0.951056516295154, -0.669130606358858, -0.743144825477394],+ [0.309016994374947, 0.951056516295154, -0.5, -0.866025403784438],+ [0.309016994374947, 0.951056516295154, -0.309016994374948, -0.951056516295154],+ [0.309016994374947, 0.951056516295154, -0.104528463267654, -0.994521895368273],+ [0.309016994374947, 0.951056516295154, 0.104528463267653, -0.994521895368273],+ [0.309016994374947, 0.951056516295154, 0.309016994374947, -0.951056516295154],+ [0.309016994374947, 0.951056516295154, 0.5, -0.866025403784439],+ [0.309016994374947, 0.951056516295154, 0.669130606358858, -0.743144825477394],+ [0.309016994374947, 0.951056516295154, 0.809016994374947, -0.587785252292473],+ [0.309016994374947, 0.951056516295154, 0.913545457642601, -0.4067366430758],+ [0.309016994374947, 0.951056516295154, 0.978147600733806, -0.207911690817759],+ [0.104528463267653, 0.994521895368273, 1, 0],+ [0.104528463267653, 0.994521895368273, 0.978147600733806, 0.207911690817759],+ [0.104528463267653, 0.994521895368273, 0.913545457642601, 0.4067366430758],+ [0.104528463267653, 0.994521895368273, 0.809016994374947, 0.587785252292473],+ [0.104528463267653, 0.994521895368273, 0.669130606358858, 0.743144825477394],+ [0.104528463267653, 0.994521895368273, 0.5, 0.866025403784439],+ [0.104528463267653, 0.994521895368273, 0.309016994374947, 0.951056516295154],+ [0.104528463267653, 0.994521895368273, 0.104528463267653, 0.994521895368273],+ [0.104528463267653, 0.994521895368273, -0.104528463267653, 0.994521895368273],+ [0.104528463267653, 0.994521895368273, -0.309016994374947, 0.951056516295154],+ [0.104528463267653, 0.994521895368273, -0.5, 0.866025403784439],+ [0.104528463267653, 0.994521895368273, -0.669130606358858, 0.743144825477394],+ [0.104528463267653, 0.994521895368273, -0.809016994374947, 0.587785252292473],+ [0.104528463267653, 0.994521895368273, -0.913545457642601, 0.4067366430758],+ [0.104528463267653, 0.994521895368273, -0.978147600733806, 0.207911690817759],+ [0.104528463267653, 0.994521895368273, -1, 5.665498452323e-16],+ [0.104528463267653, 0.994521895368273, -0.978147600733806, -0.207911690817759],+ [0.104528463267653, 0.994521895368273, -0.913545457642601, -0.4067366430758],+ [0.104528463267653, 0.994521895368273, -0.809016994374948, -0.587785252292473],+ [0.104528463267653, 0.994521895368273, -0.669130606358858, -0.743144825477394],+ [0.104528463267653, 0.994521895368273, -0.5, -0.866025403784438],+ [0.104528463267653, 0.994521895368273, -0.309016994374948, -0.951056516295154],+ [0.104528463267653, 0.994521895368273, -0.104528463267654, -0.994521895368273],+ [0.104528463267653, 0.994521895368273, 0.104528463267653, -0.994521895368273],+ [0.104528463267653, 0.994521895368273, 0.309016994374947, -0.951056516295154],+ [0.104528463267653, 0.994521895368273, 0.5, -0.866025403784439],+ [0.104528463267653, 0.994521895368273, 0.669130606358858, -0.743144825477394],+ [0.104528463267653, 0.994521895368273, 0.809016994374947, -0.587785252292473],+ [0.104528463267653, 0.994521895368273, 0.913545457642601, -0.4067366430758],+ [0.104528463267653, 0.994521895368273, 0.978147600733806, -0.207911690817759],+ [-0.104528463267653, 0.994521895368273, 1, 0],+ [-0.104528463267653, 0.994521895368273, 0.978147600733806, 0.207911690817759],+ [-0.104528463267653, 0.994521895368273, 0.913545457642601, 0.4067366430758],+ [-0.104528463267653, 0.994521895368273, 0.809016994374947, 0.587785252292473],+ [-0.104528463267653, 0.994521895368273, 0.669130606358858, 0.743144825477394],+ [-0.104528463267653, 0.994521895368273, 0.5, 0.866025403784439],+ [-0.104528463267653, 0.994521895368273, 0.309016994374947, 0.951056516295154],+ [-0.104528463267653, 0.994521895368273, 0.104528463267653, 0.994521895368273],+ [-0.104528463267653, 0.994521895368273, -0.104528463267653, 0.994521895368273],+ [-0.104528463267653, 0.994521895368273, -0.309016994374947, 0.951056516295154],+ [-0.104528463267653, 0.994521895368273, -0.5, 0.866025403784439],+ [-0.104528463267653, 0.994521895368273, -0.669130606358858, 0.743144825477394],+ [-0.104528463267653, 0.994521895368273, -0.809016994374947, 0.587785252292473],+ [-0.104528463267653, 0.994521895368273, -0.913545457642601, 0.4067366430758],+ [-0.104528463267653, 0.994521895368273, -0.978147600733806, 0.207911690817759],+ [-0.104528463267653, 0.994521895368273, -1, 5.665498452323e-16],+ [-0.104528463267653, 0.994521895368273, -0.978147600733806, -0.207911690817759],+ [-0.104528463267653, 0.994521895368273, -0.913545457642601, -0.4067366430758],+ [-0.104528463267653, 0.994521895368273, -0.809016994374948, -0.587785252292473],+ [-0.104528463267653, 0.994521895368273, -0.669130606358858, -0.743144825477394],+ [-0.104528463267653, 0.994521895368273, -0.5, -0.866025403784438],+ [-0.104528463267653, 0.994521895368273, -0.309016994374948, -0.951056516295154],+ [-0.104528463267653, 0.994521895368273, -0.104528463267654, -0.994521895368273],+ [-0.104528463267653, 0.994521895368273, 0.104528463267653, -0.994521895368273],+ [-0.104528463267653, 0.994521895368273, 0.309016994374947, -0.951056516295154],+ [-0.104528463267653, 0.994521895368273, 0.5, -0.866025403784439],+ [-0.104528463267653, 0.994521895368273, 0.669130606358858, -0.743144825477394],+ [-0.104528463267653, 0.994521895368273, 0.809016994374947, -0.587785252292473],+ [-0.104528463267653, 0.994521895368273, 0.913545457642601, -0.4067366430758],+ [-0.104528463267653, 0.994521895368273, 0.978147600733806, -0.207911690817759],+ [-0.309016994374947, 0.951056516295154, 1, 0],+ [-0.309016994374947, 0.951056516295154, 0.978147600733806, 0.207911690817759],+ [-0.309016994374947, 0.951056516295154, 0.913545457642601, 0.4067366430758],+ [-0.309016994374947, 0.951056516295154, 0.809016994374947, 0.587785252292473],+ [-0.309016994374947, 0.951056516295154, 0.669130606358858, 0.743144825477394],+ [-0.309016994374947, 0.951056516295154, 0.5, 0.866025403784439],+ [-0.309016994374947, 0.951056516295154, 0.309016994374947, 0.951056516295154],+ [-0.309016994374947, 0.951056516295154, 0.104528463267653, 0.994521895368273],+ [-0.309016994374947, 0.951056516295154, -0.104528463267653, 0.994521895368273],+ [-0.309016994374947, 0.951056516295154, -0.309016994374947, 0.951056516295154],+ [-0.309016994374947, 0.951056516295154, -0.5, 0.866025403784439],+ [-0.309016994374947, 0.951056516295154, -0.669130606358858, 0.743144825477394],+ [-0.309016994374947, 0.951056516295154, -0.809016994374947, 0.587785252292473],+ [-0.309016994374947, 0.951056516295154, -0.913545457642601, 0.4067366430758],+ [-0.309016994374947, 0.951056516295154, -0.978147600733806, 0.207911690817759],+ [-0.309016994374947, 0.951056516295154, -1, 5.665498452323e-16],+ [-0.309016994374947, 0.951056516295154, -0.978147600733806, -0.207911690817759],+ [-0.309016994374947, 0.951056516295154, -0.913545457642601, -0.4067366430758],+ [-0.309016994374947, 0.951056516295154, -0.809016994374948, -0.587785252292473],+ [-0.309016994374947, 0.951056516295154, -0.669130606358858, -0.743144825477394],+ [-0.309016994374947, 0.951056516295154, -0.5, -0.866025403784438],+ [-0.309016994374947, 0.951056516295154, -0.309016994374948, -0.951056516295154],+ [-0.309016994374947, 0.951056516295154, -0.104528463267654, -0.994521895368273],+ [-0.309016994374947, 0.951056516295154, 0.104528463267653, -0.994521895368273],+ [-0.309016994374947, 0.951056516295154, 0.309016994374947, -0.951056516295154],+ [-0.309016994374947, 0.951056516295154, 0.5, -0.866025403784439],+ [-0.309016994374947, 0.951056516295154, 0.669130606358858, -0.743144825477394],+ [-0.309016994374947, 0.951056516295154, 0.809016994374947, -0.587785252292473],+ [-0.309016994374947, 0.951056516295154, 0.913545457642601, -0.4067366430758],+ [-0.309016994374947, 0.951056516295154, 0.978147600733806, -0.207911690817759],+ [-0.5, 0.866025403784439, 1, 0],+ [-0.5, 0.866025403784439, 0.978147600733806, 0.207911690817759],+ [-0.5, 0.866025403784439, 0.913545457642601, 0.4067366430758],+ [-0.5, 0.866025403784439, 0.809016994374947, 0.587785252292473],+ [-0.5, 0.866025403784439, 0.669130606358858, 0.743144825477394],+ [-0.5, 0.866025403784439, 0.5, 0.866025403784439],+ [-0.5, 0.866025403784439, 0.309016994374947, 0.951056516295154],+ [-0.5, 0.866025403784439, 0.104528463267653, 0.994521895368273],+ [-0.5, 0.866025403784439, -0.104528463267653, 0.994521895368273],+ [-0.5, 0.866025403784439, -0.309016994374947, 0.951056516295154],+ [-0.5, 0.866025403784439, -0.5, 0.866025403784439],+ [-0.5, 0.866025403784439, -0.669130606358858, 0.743144825477394],+ [-0.5, 0.866025403784439, -0.809016994374947, 0.587785252292473],+ [-0.5, 0.866025403784439, -0.913545457642601, 0.4067366430758],+ [-0.5, 0.866025403784439, -0.978147600733806, 0.207911690817759],+ [-0.5, 0.866025403784439, -1, 5.665498452323e-16],+ [-0.5, 0.866025403784439, -0.978147600733806, -0.207911690817759],+ [-0.5, 0.866025403784439, -0.913545457642601, -0.4067366430758],+ [-0.5, 0.866025403784439, -0.809016994374948, -0.587785252292473],+ [-0.5, 0.866025403784439, -0.669130606358858, -0.743144825477394],+ [-0.5, 0.866025403784439, -0.5, -0.866025403784438],+ [-0.5, 0.866025403784439, -0.309016994374948, -0.951056516295154],+ [-0.5, 0.866025403784439, -0.104528463267654, -0.994521895368273],+ [-0.5, 0.866025403784439, 0.104528463267653, -0.994521895368273],+ [-0.5, 0.866025403784439, 0.309016994374947, -0.951056516295154],+ [-0.5, 0.866025403784439, 0.5, -0.866025403784439],+ [-0.5, 0.866025403784439, 0.669130606358858, -0.743144825477394],+ [-0.5, 0.866025403784439, 0.809016994374947, -0.587785252292473],+ [-0.5, 0.866025403784439, 0.913545457642601, -0.4067366430758],+ [-0.5, 0.866025403784439, 0.978147600733806, -0.207911690817759],+ [-0.669130606358858, 0.743144825477394, 1, 0],+ [-0.669130606358858, 0.743144825477394, 0.978147600733806, 0.207911690817759],+ [-0.669130606358858, 0.743144825477394, 0.913545457642601, 0.4067366430758],+ [-0.669130606358858, 0.743144825477394, 0.809016994374947, 0.587785252292473],+ [-0.669130606358858, 0.743144825477394, 0.669130606358858, 0.743144825477394],+ [-0.669130606358858, 0.743144825477394, 0.5, 0.866025403784439],+ [-0.669130606358858, 0.743144825477394, 0.309016994374947, 0.951056516295154],+ [-0.669130606358858, 0.743144825477394, 0.104528463267653, 0.994521895368273],+ [-0.669130606358858, 0.743144825477394, -0.104528463267653, 0.994521895368273],+ [-0.669130606358858, 0.743144825477394, -0.309016994374947, 0.951056516295154],+ [-0.669130606358858, 0.743144825477394, -0.5, 0.866025403784439],+ [-0.669130606358858, 0.743144825477394, -0.669130606358858, 0.743144825477394],+ [-0.669130606358858, 0.743144825477394, -0.809016994374947, 0.587785252292473],+ [-0.669130606358858, 0.743144825477394, -0.913545457642601, 0.4067366430758],+ [-0.669130606358858, 0.743144825477394, -0.978147600733806, 0.207911690817759],+ [-0.669130606358858, 0.743144825477394, -1, 5.665498452323e-16],+ [-0.669130606358858, 0.743144825477394, -0.978147600733806, -0.207911690817759],+ [-0.669130606358858, 0.743144825477394, -0.913545457642601, -0.4067366430758],+ [-0.669130606358858, 0.743144825477394, -0.809016994374948, -0.587785252292473],+ [-0.669130606358858, 0.743144825477394, -0.669130606358858, -0.743144825477394],+ [-0.669130606358858, 0.743144825477394, -0.5, -0.866025403784438],+ [-0.669130606358858, 0.743144825477394, -0.309016994374948, -0.951056516295154],+ [-0.669130606358858, 0.743144825477394, -0.104528463267654, -0.994521895368273],+ [-0.669130606358858, 0.743144825477394, 0.104528463267653, -0.994521895368273],+ [-0.669130606358858, 0.743144825477394, 0.309016994374947, -0.951056516295154],+ [-0.669130606358858, 0.743144825477394, 0.5, -0.866025403784439],+ [-0.669130606358858, 0.743144825477394, 0.669130606358858, -0.743144825477394],+ [-0.669130606358858, 0.743144825477394, 0.809016994374947, -0.587785252292473],+ [-0.669130606358858, 0.743144825477394, 0.913545457642601, -0.4067366430758],+ [-0.669130606358858, 0.743144825477394, 0.978147600733806, -0.207911690817759],+ [-0.809016994374947, 0.587785252292473, 1, 0],+ [-0.809016994374947, 0.587785252292473, 0.978147600733806, 0.207911690817759],+ [-0.809016994374947, 0.587785252292473, 0.913545457642601, 0.4067366430758],+ [-0.809016994374947, 0.587785252292473, 0.809016994374947, 0.587785252292473],+ [-0.809016994374947, 0.587785252292473, 0.669130606358858, 0.743144825477394],+ [-0.809016994374947, 0.587785252292473, 0.5, 0.866025403784439],+ [-0.809016994374947, 0.587785252292473, 0.309016994374947, 0.951056516295154],+ [-0.809016994374947, 0.587785252292473, 0.104528463267653, 0.994521895368273],+ [-0.809016994374947, 0.587785252292473, -0.104528463267653, 0.994521895368273],+ [-0.809016994374947, 0.587785252292473, -0.309016994374947, 0.951056516295154],+ [-0.809016994374947, 0.587785252292473, -0.5, 0.866025403784439],+ [-0.809016994374947, 0.587785252292473, -0.669130606358858, 0.743144825477394],+ [-0.809016994374947, 0.587785252292473, -0.809016994374947, 0.587785252292473],+ [-0.809016994374947, 0.587785252292473, -0.913545457642601, 0.4067366430758],+ [-0.809016994374947, 0.587785252292473, -0.978147600733806, 0.207911690817759],+ [-0.809016994374947, 0.587785252292473, -1, 5.665498452323e-16],+ [-0.809016994374947, 0.587785252292473, -0.978147600733806, -0.207911690817759],+ [-0.809016994374947, 0.587785252292473, -0.913545457642601, -0.4067366430758],+ [-0.809016994374947, 0.587785252292473, -0.809016994374948, -0.587785252292473],+ [-0.809016994374947, 0.587785252292473, -0.669130606358858, -0.743144825477394],+ [-0.809016994374947, 0.587785252292473, -0.5, -0.866025403784438],+ [-0.809016994374947, 0.587785252292473, -0.309016994374948, -0.951056516295154],+ [-0.809016994374947, 0.587785252292473, -0.104528463267654, -0.994521895368273],+ [-0.809016994374947, 0.587785252292473, 0.104528463267653, -0.994521895368273],+ [-0.809016994374947, 0.587785252292473, 0.309016994374947, -0.951056516295154],+ [-0.809016994374947, 0.587785252292473, 0.5, -0.866025403784439],+ [-0.809016994374947, 0.587785252292473, 0.669130606358858, -0.743144825477394],+ [-0.809016994374947, 0.587785252292473, 0.809016994374947, -0.587785252292473],+ [-0.809016994374947, 0.587785252292473, 0.913545457642601, -0.4067366430758],+ [-0.809016994374947, 0.587785252292473, 0.978147600733806, -0.207911690817759],+ [-0.913545457642601, 0.4067366430758, 1, 0],+ [-0.913545457642601, 0.4067366430758, 0.978147600733806, 0.207911690817759],+ [-0.913545457642601, 0.4067366430758, 0.913545457642601, 0.4067366430758],+ [-0.913545457642601, 0.4067366430758, 0.809016994374947, 0.587785252292473],+ [-0.913545457642601, 0.4067366430758, 0.669130606358858, 0.743144825477394],+ [-0.913545457642601, 0.4067366430758, 0.5, 0.866025403784439],+ [-0.913545457642601, 0.4067366430758, 0.309016994374947, 0.951056516295154],+ [-0.913545457642601, 0.4067366430758, 0.104528463267653, 0.994521895368273],+ [-0.913545457642601, 0.4067366430758, -0.104528463267653, 0.994521895368273],+ [-0.913545457642601, 0.4067366430758, -0.309016994374947, 0.951056516295154],+ [-0.913545457642601, 0.4067366430758, -0.5, 0.866025403784439],+ [-0.913545457642601, 0.4067366430758, -0.669130606358858, 0.743144825477394],+ [-0.913545457642601, 0.4067366430758, -0.809016994374947, 0.587785252292473],+ [-0.913545457642601, 0.4067366430758, -0.913545457642601, 0.4067366430758],+ [-0.913545457642601, 0.4067366430758, -0.978147600733806, 0.207911690817759],+ [-0.913545457642601, 0.4067366430758, -1, 5.665498452323e-16],+ [-0.913545457642601, 0.4067366430758, -0.978147600733806, -0.207911690817759],+ [-0.913545457642601, 0.4067366430758, -0.913545457642601, -0.4067366430758],+ [-0.913545457642601, 0.4067366430758, -0.809016994374948, -0.587785252292473],+ [-0.913545457642601, 0.4067366430758, -0.669130606358858, -0.743144825477394],+ [-0.913545457642601, 0.4067366430758, -0.5, -0.866025403784438],+ [-0.913545457642601, 0.4067366430758, -0.309016994374948, -0.951056516295154],+ [-0.913545457642601, 0.4067366430758, -0.104528463267654, -0.994521895368273],+ [-0.913545457642601, 0.4067366430758, 0.104528463267653, -0.994521895368273],+ [-0.913545457642601, 0.4067366430758, 0.309016994374947, -0.951056516295154],+ [-0.913545457642601, 0.4067366430758, 0.5, -0.866025403784439],+ [-0.913545457642601, 0.4067366430758, 0.669130606358858, -0.743144825477394],+ [-0.913545457642601, 0.4067366430758, 0.809016994374947, -0.587785252292473],+ [-0.913545457642601, 0.4067366430758, 0.913545457642601, -0.4067366430758],+ [-0.913545457642601, 0.4067366430758, 0.978147600733806, -0.207911690817759],+ [-0.978147600733806, 0.207911690817759, 1, 0],+ [-0.978147600733806, 0.207911690817759, 0.978147600733806, 0.207911690817759],+ [-0.978147600733806, 0.207911690817759, 0.913545457642601, 0.4067366430758],+ [-0.978147600733806, 0.207911690817759, 0.809016994374947, 0.587785252292473],+ [-0.978147600733806, 0.207911690817759, 0.669130606358858, 0.743144825477394],+ [-0.978147600733806, 0.207911690817759, 0.5, 0.866025403784439],+ [-0.978147600733806, 0.207911690817759, 0.309016994374947, 0.951056516295154],+ [-0.978147600733806, 0.207911690817759, 0.104528463267653, 0.994521895368273],+ [-0.978147600733806, 0.207911690817759, -0.104528463267653, 0.994521895368273],+ [-0.978147600733806, 0.207911690817759, -0.309016994374947, 0.951056516295154],+ [-0.978147600733806, 0.207911690817759, -0.5, 0.866025403784439],+ [-0.978147600733806, 0.207911690817759, -0.669130606358858, 0.743144825477394],+ [-0.978147600733806, 0.207911690817759, -0.809016994374947, 0.587785252292473],+ [-0.978147600733806, 0.207911690817759, -0.913545457642601, 0.4067366430758],+ [-0.978147600733806, 0.207911690817759, -0.978147600733806, 0.207911690817759],+ [-0.978147600733806, 0.207911690817759, -1, 5.665498452323e-16],+ [-0.978147600733806, 0.207911690817759, -0.978147600733806, -0.207911690817759],+ [-0.978147600733806, 0.207911690817759, -0.913545457642601, -0.4067366430758],+ [-0.978147600733806, 0.207911690817759, -0.809016994374948, -0.587785252292473],+ [-0.978147600733806, 0.207911690817759, -0.669130606358858, -0.743144825477394],+ [-0.978147600733806, 0.207911690817759, -0.5, -0.866025403784438],+ [-0.978147600733806, 0.207911690817759, -0.309016994374948, -0.951056516295154],+ [-0.978147600733806, 0.207911690817759, -0.104528463267654, -0.994521895368273],+ [-0.978147600733806, 0.207911690817759, 0.104528463267653, -0.994521895368273],+ [-0.978147600733806, 0.207911690817759, 0.309016994374947, -0.951056516295154],+ [-0.978147600733806, 0.207911690817759, 0.5, -0.866025403784439],+ [-0.978147600733806, 0.207911690817759, 0.669130606358858, -0.743144825477394],+ [-0.978147600733806, 0.207911690817759, 0.809016994374947, -0.587785252292473],+ [-0.978147600733806, 0.207911690817759, 0.913545457642601, -0.4067366430758],+ [-0.978147600733806, 0.207911690817759, 0.978147600733806, -0.207911690817759],+ [-1, 5.665498452323e-16, 1, 0],+ [-1, 5.665498452323e-16, 0.978147600733806, 0.207911690817759],+ [-1, 5.665498452323e-16, 0.913545457642601, 0.4067366430758],+ [-1, 5.665498452323e-16, 0.809016994374947, 0.587785252292473],+ [-1, 5.665498452323e-16, 0.669130606358858, 0.743144825477394],+ [-1, 5.665498452323e-16, 0.5, 0.866025403784439],+ [-1, 5.665498452323e-16, 0.309016994374947, 0.951056516295154],+ [-1, 5.665498452323e-16, 0.104528463267653, 0.994521895368273],+ [-1, 5.665498452323e-16, -0.104528463267653, 0.994521895368273],+ [-1, 5.665498452323e-16, -0.309016994374947, 0.951056516295154],+ [-1, 5.665498452323e-16, -0.5, 0.866025403784439],+ [-1, 5.665498452323e-16, -0.669130606358858, 0.743144825477394],+ [-1, 5.665498452323e-16, -0.809016994374947, 0.587785252292473],+ [-1, 5.665498452323e-16, -0.913545457642601, 0.4067366430758],+ [-1, 5.665498452323e-16, -0.978147600733806, 0.207911690817759],+ [-1, 5.665498452323e-16, -1, 5.665498452323e-16],+ [-1, 5.665498452323e-16, -0.978147600733806, -0.207911690817759],+ [-1, 5.665498452323e-16, -0.913545457642601, -0.4067366430758],+ [-1, 5.665498452323e-16, -0.809016994374948, -0.587785252292473],+ [-1, 5.665498452323e-16, -0.669130606358858, -0.743144825477394],+ [-1, 5.665498452323e-16, -0.5, -0.866025403784438],+ [-1, 5.665498452323e-16, -0.309016994374948, -0.951056516295154],+ [-1, 5.665498452323e-16, -0.104528463267654, -0.994521895368273],+ [-1, 5.665498452323e-16, 0.104528463267653, -0.994521895368273],+ [-1, 5.665498452323e-16, 0.309016994374947, -0.951056516295154],+ [-1, 5.665498452323e-16, 0.5, -0.866025403784439],+ [-1, 5.665498452323e-16, 0.669130606358858, -0.743144825477394],+ [-1, 5.665498452323e-16, 0.809016994374947, -0.587785252292473],+ [-1, 5.665498452323e-16, 0.913545457642601, -0.4067366430758],+ [-1, 5.665498452323e-16, 0.978147600733806, -0.207911690817759],+ [-0.978147600733806, -0.207911690817759, 1, 0],+ [-0.978147600733806, -0.207911690817759, 0.978147600733806, 0.207911690817759],+ [-0.978147600733806, -0.207911690817759, 0.913545457642601, 0.4067366430758],+ [-0.978147600733806, -0.207911690817759, 0.809016994374947, 0.587785252292473],+ [-0.978147600733806, -0.207911690817759, 0.669130606358858, 0.743144825477394],+ [-0.978147600733806, -0.207911690817759, 0.5, 0.866025403784439],+ [-0.978147600733806, -0.207911690817759, 0.309016994374947, 0.951056516295154],+ [-0.978147600733806, -0.207911690817759, 0.104528463267653, 0.994521895368273],+ [-0.978147600733806, -0.207911690817759, -0.104528463267653, 0.994521895368273],+ [-0.978147600733806, -0.207911690817759, -0.309016994374947, 0.951056516295154],+ [-0.978147600733806, -0.207911690817759, -0.5, 0.866025403784439],+ [-0.978147600733806, -0.207911690817759, -0.669130606358858, 0.743144825477394],+ [-0.978147600733806, -0.207911690817759, -0.809016994374947, 0.587785252292473],+ [-0.978147600733806, -0.207911690817759, -0.913545457642601, 0.4067366430758],+ [-0.978147600733806, -0.207911690817759, -0.978147600733806, 0.207911690817759],+ [-0.978147600733806, -0.207911690817759, -1, 5.665498452323e-16],+ [-0.978147600733806, -0.207911690817759, -0.978147600733806, -0.207911690817759],+ [-0.978147600733806, -0.207911690817759, -0.913545457642601, -0.4067366430758],+ [-0.978147600733806, -0.207911690817759, -0.809016994374948, -0.587785252292473],+ [-0.978147600733806, -0.207911690817759, -0.669130606358858, -0.743144825477394],+ [-0.978147600733806, -0.207911690817759, -0.5, -0.866025403784438],+ [-0.978147600733806, -0.207911690817759, -0.309016994374948, -0.951056516295154],+ [-0.978147600733806, -0.207911690817759, -0.104528463267654, -0.994521895368273],+ [-0.978147600733806, -0.207911690817759, 0.104528463267653, -0.994521895368273],+ [-0.978147600733806, -0.207911690817759, 0.309016994374947, -0.951056516295154],+ [-0.978147600733806, -0.207911690817759, 0.5, -0.866025403784439],+ [-0.978147600733806, -0.207911690817759, 0.669130606358858, -0.743144825477394],+ [-0.978147600733806, -0.207911690817759, 0.809016994374947, -0.587785252292473],+ [-0.978147600733806, -0.207911690817759, 0.913545457642601, -0.4067366430758],+ [-0.978147600733806, -0.207911690817759, 0.978147600733806, -0.207911690817759],+ [-0.913545457642601, -0.4067366430758, 1, 0],+ [-0.913545457642601, -0.4067366430758, 0.978147600733806, 0.207911690817759],+ [-0.913545457642601, -0.4067366430758, 0.913545457642601, 0.4067366430758],+ [-0.913545457642601, -0.4067366430758, 0.809016994374947, 0.587785252292473],+ [-0.913545457642601, -0.4067366430758, 0.669130606358858, 0.743144825477394],+ [-0.913545457642601, -0.4067366430758, 0.5, 0.866025403784439],+ [-0.913545457642601, -0.4067366430758, 0.309016994374947, 0.951056516295154],+ [-0.913545457642601, -0.4067366430758, 0.104528463267653, 0.994521895368273],+ [-0.913545457642601, -0.4067366430758, -0.104528463267653, 0.994521895368273],+ [-0.913545457642601, -0.4067366430758, -0.309016994374947, 0.951056516295154],+ [-0.913545457642601, -0.4067366430758, -0.5, 0.866025403784439],+ [-0.913545457642601, -0.4067366430758, -0.669130606358858, 0.743144825477394],+ [-0.913545457642601, -0.4067366430758, -0.809016994374947, 0.587785252292473],+ [-0.913545457642601, -0.4067366430758, -0.913545457642601, 0.4067366430758],+ [-0.913545457642601, -0.4067366430758, -0.978147600733806, 0.207911690817759],+ [-0.913545457642601, -0.4067366430758, -1, 5.665498452323e-16],+ [-0.913545457642601, -0.4067366430758, -0.978147600733806, -0.207911690817759],+ [-0.913545457642601, -0.4067366430758, -0.913545457642601, -0.4067366430758],+ [-0.913545457642601, -0.4067366430758, -0.809016994374948, -0.587785252292473],+ [-0.913545457642601, -0.4067366430758, -0.669130606358858, -0.743144825477394],+ [-0.913545457642601, -0.4067366430758, -0.5, -0.866025403784438],+ [-0.913545457642601, -0.4067366430758, -0.309016994374948, -0.951056516295154],+ [-0.913545457642601, -0.4067366430758, -0.104528463267654, -0.994521895368273],+ [-0.913545457642601, -0.4067366430758, 0.104528463267653, -0.994521895368273],+ [-0.913545457642601, -0.4067366430758, 0.309016994374947, -0.951056516295154],+ [-0.913545457642601, -0.4067366430758, 0.5, -0.866025403784439],+ [-0.913545457642601, -0.4067366430758, 0.669130606358858, -0.743144825477394],+ [-0.913545457642601, -0.4067366430758, 0.809016994374947, -0.587785252292473],+ [-0.913545457642601, -0.4067366430758, 0.913545457642601, -0.4067366430758],+ [-0.913545457642601, -0.4067366430758, 0.978147600733806, -0.207911690817759],+ [-0.809016994374948, -0.587785252292473, 1, 0],+ [-0.809016994374948, -0.587785252292473, 0.978147600733806, 0.207911690817759],+ [-0.809016994374948, -0.587785252292473, 0.913545457642601, 0.4067366430758],+ [-0.809016994374948, -0.587785252292473, 0.809016994374947, 0.587785252292473],+ [-0.809016994374948, -0.587785252292473, 0.669130606358858, 0.743144825477394],+ [-0.809016994374948, -0.587785252292473, 0.5, 0.866025403784439],+ [-0.809016994374948, -0.587785252292473, 0.309016994374947, 0.951056516295154],+ [-0.809016994374948, -0.587785252292473, 0.104528463267653, 0.994521895368273],+ [-0.809016994374948, -0.587785252292473, -0.104528463267653, 0.994521895368273],+ [-0.809016994374948, -0.587785252292473, -0.309016994374947, 0.951056516295154],+ [-0.809016994374948, -0.587785252292473, -0.5, 0.866025403784439],+ [-0.809016994374948, -0.587785252292473, -0.669130606358858, 0.743144825477394],+ [-0.809016994374948, -0.587785252292473, -0.809016994374947, 0.587785252292473],+ [-0.809016994374948, -0.587785252292473, -0.913545457642601, 0.4067366430758],+ [-0.809016994374948, -0.587785252292473, -0.978147600733806, 0.207911690817759],+ [-0.809016994374948, -0.587785252292473, -1, 5.665498452323e-16],+ [-0.809016994374948, -0.587785252292473, -0.978147600733806, -0.207911690817759],+ [-0.809016994374948, -0.587785252292473, -0.913545457642601, -0.4067366430758],+ [-0.809016994374948, -0.587785252292473, -0.809016994374948, -0.587785252292473],+ [-0.809016994374948, -0.587785252292473, -0.669130606358858, -0.743144825477394],+ [-0.809016994374948, -0.587785252292473, -0.5, -0.866025403784438],+ [-0.809016994374948, -0.587785252292473, -0.309016994374948, -0.951056516295154],+ [-0.809016994374948, -0.587785252292473, -0.104528463267654, -0.994521895368273],+ [-0.809016994374948, -0.587785252292473, 0.104528463267653, -0.994521895368273],+ [-0.809016994374948, -0.587785252292473, 0.309016994374947, -0.951056516295154],+ [-0.809016994374948, -0.587785252292473, 0.5, -0.866025403784439],+ [-0.809016994374948, -0.587785252292473, 0.669130606358858, -0.743144825477394],+ [-0.809016994374948, -0.587785252292473, 0.809016994374947, -0.587785252292473],+ [-0.809016994374948, -0.587785252292473, 0.913545457642601, -0.4067366430758],+ [-0.809016994374948, -0.587785252292473, 0.978147600733806, -0.207911690817759],+ [-0.669130606358858, -0.743144825477394, 1, 0],+ [-0.669130606358858, -0.743144825477394, 0.978147600733806, 0.207911690817759],+ [-0.669130606358858, -0.743144825477394, 0.913545457642601, 0.4067366430758],+ [-0.669130606358858, -0.743144825477394, 0.809016994374947, 0.587785252292473],+ [-0.669130606358858, -0.743144825477394, 0.669130606358858, 0.743144825477394],+ [-0.669130606358858, -0.743144825477394, 0.5, 0.866025403784439],+ [-0.669130606358858, -0.743144825477394, 0.309016994374947, 0.951056516295154],+ [-0.669130606358858, -0.743144825477394, 0.104528463267653, 0.994521895368273],+ [-0.669130606358858, -0.743144825477394, -0.104528463267653, 0.994521895368273],+ [-0.669130606358858, -0.743144825477394, -0.309016994374947, 0.951056516295154],+ [-0.669130606358858, -0.743144825477394, -0.5, 0.866025403784439],+ [-0.669130606358858, -0.743144825477394, -0.669130606358858, 0.743144825477394],+ [-0.669130606358858, -0.743144825477394, -0.809016994374947, 0.587785252292473],+ [-0.669130606358858, -0.743144825477394, -0.913545457642601, 0.4067366430758],+ [-0.669130606358858, -0.743144825477394, -0.978147600733806, 0.207911690817759],+ [-0.669130606358858, -0.743144825477394, -1, 5.665498452323e-16],+ [-0.669130606358858, -0.743144825477394, -0.978147600733806, -0.207911690817759],+ [-0.669130606358858, -0.743144825477394, -0.913545457642601, -0.4067366430758],+ [-0.669130606358858, -0.743144825477394, -0.809016994374948, -0.587785252292473],+ [-0.669130606358858, -0.743144825477394, -0.669130606358858, -0.743144825477394],+ [-0.669130606358858, -0.743144825477394, -0.5, -0.866025403784438],+ [-0.669130606358858, -0.743144825477394, -0.309016994374948, -0.951056516295154],+ [-0.669130606358858, -0.743144825477394, -0.104528463267654, -0.994521895368273],+ [-0.669130606358858, -0.743144825477394, 0.104528463267653, -0.994521895368273],+ [-0.669130606358858, -0.743144825477394, 0.309016994374947, -0.951056516295154],+ [-0.669130606358858, -0.743144825477394, 0.5, -0.866025403784439],+ [-0.669130606358858, -0.743144825477394, 0.669130606358858, -0.743144825477394],+ [-0.669130606358858, -0.743144825477394, 0.809016994374947, -0.587785252292473],+ [-0.669130606358858, -0.743144825477394, 0.913545457642601, -0.4067366430758],+ [-0.669130606358858, -0.743144825477394, 0.978147600733806, -0.207911690817759],+ [-0.5, -0.866025403784438, 1, 0],+ [-0.5, -0.866025403784438, 0.978147600733806, 0.207911690817759],+ [-0.5, -0.866025403784438, 0.913545457642601, 0.4067366430758],+ [-0.5, -0.866025403784438, 0.809016994374947, 0.587785252292473],+ [-0.5, -0.866025403784438, 0.669130606358858, 0.743144825477394],+ [-0.5, -0.866025403784438, 0.5, 0.866025403784439],+ [-0.5, -0.866025403784438, 0.309016994374947, 0.951056516295154],+ [-0.5, -0.866025403784438, 0.104528463267653, 0.994521895368273],+ [-0.5, -0.866025403784438, -0.104528463267653, 0.994521895368273],+ [-0.5, -0.866025403784438, -0.309016994374947, 0.951056516295154],+ [-0.5, -0.866025403784438, -0.5, 0.866025403784439],+ [-0.5, -0.866025403784438, -0.669130606358858, 0.743144825477394],+ [-0.5, -0.866025403784438, -0.809016994374947, 0.587785252292473],+ [-0.5, -0.866025403784438, -0.913545457642601, 0.4067366430758],+ [-0.5, -0.866025403784438, -0.978147600733806, 0.207911690817759],+ [-0.5, -0.866025403784438, -1, 5.665498452323e-16],+ [-0.5, -0.866025403784438, -0.978147600733806, -0.207911690817759],+ [-0.5, -0.866025403784438, -0.913545457642601, -0.4067366430758],+ [-0.5, -0.866025403784438, -0.809016994374948, -0.587785252292473],+ [-0.5, -0.866025403784438, -0.669130606358858, -0.743144825477394],+ [-0.5, -0.866025403784438, -0.5, -0.866025403784438],+ [-0.5, -0.866025403784438, -0.309016994374948, -0.951056516295154],+ [-0.5, -0.866025403784438, -0.104528463267654, -0.994521895368273],+ [-0.5, -0.866025403784438, 0.104528463267653, -0.994521895368273],+ [-0.5, -0.866025403784438, 0.309016994374947, -0.951056516295154],+ [-0.5, -0.866025403784438, 0.5, -0.866025403784439],+ [-0.5, -0.866025403784438, 0.669130606358858, -0.743144825477394],+ [-0.5, -0.866025403784438, 0.809016994374947, -0.587785252292473],+ [-0.5, -0.866025403784438, 0.913545457642601, -0.4067366430758],+ [-0.5, -0.866025403784438, 0.978147600733806, -0.207911690817759],+ [-0.309016994374948, -0.951056516295154, 1, 0],+ [-0.309016994374948, -0.951056516295154, 0.978147600733806, 0.207911690817759],+ [-0.309016994374948, -0.951056516295154, 0.913545457642601, 0.4067366430758],+ [-0.309016994374948, -0.951056516295154, 0.809016994374947, 0.587785252292473],+ [-0.309016994374948, -0.951056516295154, 0.669130606358858, 0.743144825477394],+ [-0.309016994374948, -0.951056516295154, 0.5, 0.866025403784439],+ [-0.309016994374948, -0.951056516295154, 0.309016994374947, 0.951056516295154],+ [-0.309016994374948, -0.951056516295154, 0.104528463267653, 0.994521895368273],+ [-0.309016994374948, -0.951056516295154, -0.104528463267653, 0.994521895368273],+ [-0.309016994374948, -0.951056516295154, -0.309016994374947, 0.951056516295154],+ [-0.309016994374948, -0.951056516295154, -0.5, 0.866025403784439],+ [-0.309016994374948, -0.951056516295154, -0.669130606358858, 0.743144825477394],+ [-0.309016994374948, -0.951056516295154, -0.809016994374947, 0.587785252292473],+ [-0.309016994374948, -0.951056516295154, -0.913545457642601, 0.4067366430758],+ [-0.309016994374948, -0.951056516295154, -0.978147600733806, 0.207911690817759],+ [-0.309016994374948, -0.951056516295154, -1, 5.665498452323e-16],+ [-0.309016994374948, -0.951056516295154, -0.978147600733806, -0.207911690817759],+ [-0.309016994374948, -0.951056516295154, -0.913545457642601, -0.4067366430758],+ [-0.309016994374948, -0.951056516295154, -0.809016994374948, -0.587785252292473],+ [-0.309016994374948, -0.951056516295154, -0.669130606358858, -0.743144825477394],+ [-0.309016994374948, -0.951056516295154, -0.5, -0.866025403784438],+ [-0.309016994374948, -0.951056516295154, -0.309016994374948, -0.951056516295154],+ [-0.309016994374948, -0.951056516295154, -0.104528463267654, -0.994521895368273],+ [-0.309016994374948, -0.951056516295154, 0.104528463267653, -0.994521895368273],+ [-0.309016994374948, -0.951056516295154, 0.309016994374947, -0.951056516295154],+ [-0.309016994374948, -0.951056516295154, 0.5, -0.866025403784439],+ [-0.309016994374948, -0.951056516295154, 0.669130606358858, -0.743144825477394],+ [-0.309016994374948, -0.951056516295154, 0.809016994374947, -0.587785252292473],+ [-0.309016994374948, -0.951056516295154, 0.913545457642601, -0.4067366430758],+ [-0.309016994374948, -0.951056516295154, 0.978147600733806, -0.207911690817759],+ [-0.104528463267654, -0.994521895368273, 1, 0],+ [-0.104528463267654, -0.994521895368273, 0.978147600733806, 0.207911690817759],+ [-0.104528463267654, -0.994521895368273, 0.913545457642601, 0.4067366430758],+ [-0.104528463267654, -0.994521895368273, 0.809016994374947, 0.587785252292473],+ [-0.104528463267654, -0.994521895368273, 0.669130606358858, 0.743144825477394],+ [-0.104528463267654, -0.994521895368273, 0.5, 0.866025403784439],+ [-0.104528463267654, -0.994521895368273, 0.309016994374947, 0.951056516295154],+ [-0.104528463267654, -0.994521895368273, 0.104528463267653, 0.994521895368273],+ [-0.104528463267654, -0.994521895368273, -0.104528463267653, 0.994521895368273],+ [-0.104528463267654, -0.994521895368273, -0.309016994374947, 0.951056516295154],+ [-0.104528463267654, -0.994521895368273, -0.5, 0.866025403784439],+ [-0.104528463267654, -0.994521895368273, -0.669130606358858, 0.743144825477394],+ [-0.104528463267654, -0.994521895368273, -0.809016994374947, 0.587785252292473],+ [-0.104528463267654, -0.994521895368273, -0.913545457642601, 0.4067366430758],+ [-0.104528463267654, -0.994521895368273, -0.978147600733806, 0.207911690817759],+ [-0.104528463267654, -0.994521895368273, -1, 5.665498452323e-16],+ [-0.104528463267654, -0.994521895368273, -0.978147600733806, -0.207911690817759],+ [-0.104528463267654, -0.994521895368273, -0.913545457642601, -0.4067366430758],+ [-0.104528463267654, -0.994521895368273, -0.809016994374948, -0.587785252292473],+ [-0.104528463267654, -0.994521895368273, -0.669130606358858, -0.743144825477394],+ [-0.104528463267654, -0.994521895368273, -0.5, -0.866025403784438],+ [-0.104528463267654, -0.994521895368273, -0.309016994374948, -0.951056516295154],+ [-0.104528463267654, -0.994521895368273, -0.104528463267654, -0.994521895368273],+ [-0.104528463267654, -0.994521895368273, 0.104528463267653, -0.994521895368273],+ [-0.104528463267654, -0.994521895368273, 0.309016994374947, -0.951056516295154],+ [-0.104528463267654, -0.994521895368273, 0.5, -0.866025403784439],+ [-0.104528463267654, -0.994521895368273, 0.669130606358858, -0.743144825477394],+ [-0.104528463267654, -0.994521895368273, 0.809016994374947, -0.587785252292473],+ [-0.104528463267654, -0.994521895368273, 0.913545457642601, -0.4067366430758],+ [-0.104528463267654, -0.994521895368273, 0.978147600733806, -0.207911690817759],+ [0.104528463267653, -0.994521895368273, 1, 0],+ [0.104528463267653, -0.994521895368273, 0.978147600733806, 0.207911690817759],+ [0.104528463267653, -0.994521895368273, 0.913545457642601, 0.4067366430758],+ [0.104528463267653, -0.994521895368273, 0.809016994374947, 0.587785252292473],+ [0.104528463267653, -0.994521895368273, 0.669130606358858, 0.743144825477394],+ [0.104528463267653, -0.994521895368273, 0.5, 0.866025403784439],+ [0.104528463267653, -0.994521895368273, 0.309016994374947, 0.951056516295154],+ [0.104528463267653, -0.994521895368273, 0.104528463267653, 0.994521895368273],+ [0.104528463267653, -0.994521895368273, -0.104528463267653, 0.994521895368273],+ [0.104528463267653, -0.994521895368273, -0.309016994374947, 0.951056516295154],+ [0.104528463267653, -0.994521895368273, -0.5, 0.866025403784439],+ [0.104528463267653, -0.994521895368273, -0.669130606358858, 0.743144825477394],+ [0.104528463267653, -0.994521895368273, -0.809016994374947, 0.587785252292473],+ [0.104528463267653, -0.994521895368273, -0.913545457642601, 0.4067366430758],+ [0.104528463267653, -0.994521895368273, -0.978147600733806, 0.207911690817759],+ [0.104528463267653, -0.994521895368273, -1, 5.665498452323e-16],+ [0.104528463267653, -0.994521895368273, -0.978147600733806, -0.207911690817759],+ [0.104528463267653, -0.994521895368273, -0.913545457642601, -0.4067366430758],+ [0.104528463267653, -0.994521895368273, -0.809016994374948, -0.587785252292473],+ [0.104528463267653, -0.994521895368273, -0.669130606358858, -0.743144825477394],+ [0.104528463267653, -0.994521895368273, -0.5, -0.866025403784438],+ [0.104528463267653, -0.994521895368273, -0.309016994374948, -0.951056516295154],+ [0.104528463267653, -0.994521895368273, -0.104528463267654, -0.994521895368273],+ [0.104528463267653, -0.994521895368273, 0.104528463267653, -0.994521895368273],+ [0.104528463267653, -0.994521895368273, 0.309016994374947, -0.951056516295154],+ [0.104528463267653, -0.994521895368273, 0.5, -0.866025403784439],+ [0.104528463267653, -0.994521895368273, 0.669130606358858, -0.743144825477394],+ [0.104528463267653, -0.994521895368273, 0.809016994374947, -0.587785252292473],+ [0.104528463267653, -0.994521895368273, 0.913545457642601, -0.4067366430758],+ [0.104528463267653, -0.994521895368273, 0.978147600733806, -0.207911690817759],+ [0.309016994374947, -0.951056516295154, 1, 0],+ [0.309016994374947, -0.951056516295154, 0.978147600733806, 0.207911690817759],+ [0.309016994374947, -0.951056516295154, 0.913545457642601, 0.4067366430758],+ [0.309016994374947, -0.951056516295154, 0.809016994374947, 0.587785252292473],+ [0.309016994374947, -0.951056516295154, 0.669130606358858, 0.743144825477394],+ [0.309016994374947, -0.951056516295154, 0.5, 0.866025403784439],+ [0.309016994374947, -0.951056516295154, 0.309016994374947, 0.951056516295154],+ [0.309016994374947, -0.951056516295154, 0.104528463267653, 0.994521895368273],+ [0.309016994374947, -0.951056516295154, -0.104528463267653, 0.994521895368273],+ [0.309016994374947, -0.951056516295154, -0.309016994374947, 0.951056516295154],+ [0.309016994374947, -0.951056516295154, -0.5, 0.866025403784439],+ [0.309016994374947, -0.951056516295154, -0.669130606358858, 0.743144825477394],+ [0.309016994374947, -0.951056516295154, -0.809016994374947, 0.587785252292473],+ [0.309016994374947, -0.951056516295154, -0.913545457642601, 0.4067366430758],+ [0.309016994374947, -0.951056516295154, -0.978147600733806, 0.207911690817759],+ [0.309016994374947, -0.951056516295154, -1, 5.665498452323e-16],+ [0.309016994374947, -0.951056516295154, -0.978147600733806, -0.207911690817759],+ [0.309016994374947, -0.951056516295154, -0.913545457642601, -0.4067366430758],+ [0.309016994374947, -0.951056516295154, -0.809016994374948, -0.587785252292473],+ [0.309016994374947, -0.951056516295154, -0.669130606358858, -0.743144825477394],+ [0.309016994374947, -0.951056516295154, -0.5, -0.866025403784438],+ [0.309016994374947, -0.951056516295154, -0.309016994374948, -0.951056516295154],+ [0.309016994374947, -0.951056516295154, -0.104528463267654, -0.994521895368273],+ [0.309016994374947, -0.951056516295154, 0.104528463267653, -0.994521895368273],+ [0.309016994374947, -0.951056516295154, 0.309016994374947, -0.951056516295154],+ [0.309016994374947, -0.951056516295154, 0.5, -0.866025403784439],+ [0.309016994374947, -0.951056516295154, 0.669130606358858, -0.743144825477394],+ [0.309016994374947, -0.951056516295154, 0.809016994374947, -0.587785252292473],+ [0.309016994374947, -0.951056516295154, 0.913545457642601, -0.4067366430758],+ [0.309016994374947, -0.951056516295154, 0.978147600733806, -0.207911690817759],+ [0.5, -0.866025403784439, 1, 0],+ [0.5, -0.866025403784439, 0.978147600733806, 0.207911690817759],+ [0.5, -0.866025403784439, 0.913545457642601, 0.4067366430758],+ [0.5, -0.866025403784439, 0.809016994374947, 0.587785252292473],+ [0.5, -0.866025403784439, 0.669130606358858, 0.743144825477394],+ [0.5, -0.866025403784439, 0.5, 0.866025403784439],+ [0.5, -0.866025403784439, 0.309016994374947, 0.951056516295154],+ [0.5, -0.866025403784439, 0.104528463267653, 0.994521895368273],+ [0.5, -0.866025403784439, -0.104528463267653, 0.994521895368273],+ [0.5, -0.866025403784439, -0.309016994374947, 0.951056516295154],+ [0.5, -0.866025403784439, -0.5, 0.866025403784439],+ [0.5, -0.866025403784439, -0.669130606358858, 0.743144825477394],+ [0.5, -0.866025403784439, -0.809016994374947, 0.587785252292473],+ [0.5, -0.866025403784439, -0.913545457642601, 0.4067366430758],+ [0.5, -0.866025403784439, -0.978147600733806, 0.207911690817759],+ [0.5, -0.866025403784439, -1, 5.665498452323e-16],+ [0.5, -0.866025403784439, -0.978147600733806, -0.207911690817759],+ [0.5, -0.866025403784439, -0.913545457642601, -0.4067366430758],+ [0.5, -0.866025403784439, -0.809016994374948, -0.587785252292473],+ [0.5, -0.866025403784439, -0.669130606358858, -0.743144825477394],+ [0.5, -0.866025403784439, -0.5, -0.866025403784438],+ [0.5, -0.866025403784439, -0.309016994374948, -0.951056516295154],+ [0.5, -0.866025403784439, -0.104528463267654, -0.994521895368273],+ [0.5, -0.866025403784439, 0.104528463267653, -0.994521895368273],+ [0.5, -0.866025403784439, 0.309016994374947, -0.951056516295154],+ [0.5, -0.866025403784439, 0.5, -0.866025403784439],+ [0.5, -0.866025403784439, 0.669130606358858, -0.743144825477394],+ [0.5, -0.866025403784439, 0.809016994374947, -0.587785252292473],+ [0.5, -0.866025403784439, 0.913545457642601, -0.4067366430758],+ [0.5, -0.866025403784439, 0.978147600733806, -0.207911690817759],+ [0.669130606358858, -0.743144825477394, 1, 0],+ [0.669130606358858, -0.743144825477394, 0.978147600733806, 0.207911690817759],+ [0.669130606358858, -0.743144825477394, 0.913545457642601, 0.4067366430758],+ [0.669130606358858, -0.743144825477394, 0.809016994374947, 0.587785252292473],+ [0.669130606358858, -0.743144825477394, 0.669130606358858, 0.743144825477394],+ [0.669130606358858, -0.743144825477394, 0.5, 0.866025403784439],+ [0.669130606358858, -0.743144825477394, 0.309016994374947, 0.951056516295154],+ [0.669130606358858, -0.743144825477394, 0.104528463267653, 0.994521895368273],+ [0.669130606358858, -0.743144825477394, -0.104528463267653, 0.994521895368273],+ [0.669130606358858, -0.743144825477394, -0.309016994374947, 0.951056516295154],+ [0.669130606358858, -0.743144825477394, -0.5, 0.866025403784439],+ [0.669130606358858, -0.743144825477394, -0.669130606358858, 0.743144825477394],+ [0.669130606358858, -0.743144825477394, -0.809016994374947, 0.587785252292473],+ [0.669130606358858, -0.743144825477394, -0.913545457642601, 0.4067366430758],+ [0.669130606358858, -0.743144825477394, -0.978147600733806, 0.207911690817759],+ [0.669130606358858, -0.743144825477394, -1, 5.665498452323e-16],+ [0.669130606358858, -0.743144825477394, -0.978147600733806, -0.207911690817759],+ [0.669130606358858, -0.743144825477394, -0.913545457642601, -0.4067366430758],+ [0.669130606358858, -0.743144825477394, -0.809016994374948, -0.587785252292473],+ [0.669130606358858, -0.743144825477394, -0.669130606358858, -0.743144825477394],+ [0.669130606358858, -0.743144825477394, -0.5, -0.866025403784438],+ [0.669130606358858, -0.743144825477394, -0.309016994374948, -0.951056516295154],+ [0.669130606358858, -0.743144825477394, -0.104528463267654, -0.994521895368273],+ [0.669130606358858, -0.743144825477394, 0.104528463267653, -0.994521895368273],+ [0.669130606358858, -0.743144825477394, 0.309016994374947, -0.951056516295154],+ [0.669130606358858, -0.743144825477394, 0.5, -0.866025403784439],+ [0.669130606358858, -0.743144825477394, 0.669130606358858, -0.743144825477394],+ [0.669130606358858, -0.743144825477394, 0.809016994374947, -0.587785252292473],+ [0.669130606358858, -0.743144825477394, 0.913545457642601, -0.4067366430758],+ [0.669130606358858, -0.743144825477394, 0.978147600733806, -0.207911690817759],+ [0.809016994374947, -0.587785252292473, 1, 0],+ [0.809016994374947, -0.587785252292473, 0.978147600733806, 0.207911690817759],+ [0.809016994374947, -0.587785252292473, 0.913545457642601, 0.4067366430758],+ [0.809016994374947, -0.587785252292473, 0.809016994374947, 0.587785252292473],+ [0.809016994374947, -0.587785252292473, 0.669130606358858, 0.743144825477394],+ [0.809016994374947, -0.587785252292473, 0.5, 0.866025403784439],+ [0.809016994374947, -0.587785252292473, 0.309016994374947, 0.951056516295154],+ [0.809016994374947, -0.587785252292473, 0.104528463267653, 0.994521895368273],+ [0.809016994374947, -0.587785252292473, -0.104528463267653, 0.994521895368273],+ [0.809016994374947, -0.587785252292473, -0.309016994374947, 0.951056516295154],+ [0.809016994374947, -0.587785252292473, -0.5, 0.866025403784439],+ [0.809016994374947, -0.587785252292473, -0.669130606358858, 0.743144825477394],+ [0.809016994374947, -0.587785252292473, -0.809016994374947, 0.587785252292473],+ [0.809016994374947, -0.587785252292473, -0.913545457642601, 0.4067366430758],+ [0.809016994374947, -0.587785252292473, -0.978147600733806, 0.207911690817759],+ [0.809016994374947, -0.587785252292473, -1, 5.665498452323e-16],+ [0.809016994374947, -0.587785252292473, -0.978147600733806, -0.207911690817759],+ [0.809016994374947, -0.587785252292473, -0.913545457642601, -0.4067366430758],+ [0.809016994374947, -0.587785252292473, -0.809016994374948, -0.587785252292473],+ [0.809016994374947, -0.587785252292473, -0.669130606358858, -0.743144825477394],+ [0.809016994374947, -0.587785252292473, -0.5, -0.866025403784438],+ [0.809016994374947, -0.587785252292473, -0.309016994374948, -0.951056516295154],+ [0.809016994374947, -0.587785252292473, -0.104528463267654, -0.994521895368273],+ [0.809016994374947, -0.587785252292473, 0.104528463267653, -0.994521895368273],+ [0.809016994374947, -0.587785252292473, 0.309016994374947, -0.951056516295154],+ [0.809016994374947, -0.587785252292473, 0.5, -0.866025403784439],+ [0.809016994374947, -0.587785252292473, 0.669130606358858, -0.743144825477394],+ [0.809016994374947, -0.587785252292473, 0.809016994374947, -0.587785252292473],+ [0.809016994374947, -0.587785252292473, 0.913545457642601, -0.4067366430758],+ [0.809016994374947, -0.587785252292473, 0.978147600733806, -0.207911690817759],+ [0.913545457642601, -0.4067366430758, 1, 0],+ [0.913545457642601, -0.4067366430758, 0.978147600733806, 0.207911690817759],+ [0.913545457642601, -0.4067366430758, 0.913545457642601, 0.4067366430758],+ [0.913545457642601, -0.4067366430758, 0.809016994374947, 0.587785252292473],+ [0.913545457642601, -0.4067366430758, 0.669130606358858, 0.743144825477394],+ [0.913545457642601, -0.4067366430758, 0.5, 0.866025403784439],+ [0.913545457642601, -0.4067366430758, 0.309016994374947, 0.951056516295154],+ [0.913545457642601, -0.4067366430758, 0.104528463267653, 0.994521895368273],+ [0.913545457642601, -0.4067366430758, -0.104528463267653, 0.994521895368273],+ [0.913545457642601, -0.4067366430758, -0.309016994374947, 0.951056516295154],+ [0.913545457642601, -0.4067366430758, -0.5, 0.866025403784439],+ [0.913545457642601, -0.4067366430758, -0.669130606358858, 0.743144825477394],+ [0.913545457642601, -0.4067366430758, -0.809016994374947, 0.587785252292473],+ [0.913545457642601, -0.4067366430758, -0.913545457642601, 0.4067366430758],+ [0.913545457642601, -0.4067366430758, -0.978147600733806, 0.207911690817759],+ [0.913545457642601, -0.4067366430758, -1, 5.665498452323e-16],+ [0.913545457642601, -0.4067366430758, -0.978147600733806, -0.207911690817759],+ [0.913545457642601, -0.4067366430758, -0.913545457642601, -0.4067366430758],+ [0.913545457642601, -0.4067366430758, -0.809016994374948, -0.587785252292473],+ [0.913545457642601, -0.4067366430758, -0.669130606358858, -0.743144825477394],+ [0.913545457642601, -0.4067366430758, -0.5, -0.866025403784438],+ [0.913545457642601, -0.4067366430758, -0.309016994374948, -0.951056516295154],+ [0.913545457642601, -0.4067366430758, -0.104528463267654, -0.994521895368273],+ [0.913545457642601, -0.4067366430758, 0.104528463267653, -0.994521895368273],+ [0.913545457642601, -0.4067366430758, 0.309016994374947, -0.951056516295154],+ [0.913545457642601, -0.4067366430758, 0.5, -0.866025403784439],+ [0.913545457642601, -0.4067366430758, 0.669130606358858, -0.743144825477394],+ [0.913545457642601, -0.4067366430758, 0.809016994374947, -0.587785252292473],+ [0.913545457642601, -0.4067366430758, 0.913545457642601, -0.4067366430758],+ [0.913545457642601, -0.4067366430758, 0.978147600733806, -0.207911690817759],+ [0.978147600733806, -0.207911690817759, 1, 0],+ [0.978147600733806, -0.207911690817759, 0.978147600733806, 0.207911690817759],+ [0.978147600733806, -0.207911690817759, 0.913545457642601, 0.4067366430758],+ [0.978147600733806, -0.207911690817759, 0.809016994374947, 0.587785252292473],+ [0.978147600733806, -0.207911690817759, 0.669130606358858, 0.743144825477394],+ [0.978147600733806, -0.207911690817759, 0.5, 0.866025403784439],+ [0.978147600733806, -0.207911690817759, 0.309016994374947, 0.951056516295154],+ [0.978147600733806, -0.207911690817759, 0.104528463267653, 0.994521895368273],+ [0.978147600733806, -0.207911690817759, -0.104528463267653, 0.994521895368273],+ [0.978147600733806, -0.207911690817759, -0.309016994374947, 0.951056516295154],+ [0.978147600733806, -0.207911690817759, -0.5, 0.866025403784439],+ [0.978147600733806, -0.207911690817759, -0.669130606358858, 0.743144825477394],+ [0.978147600733806, -0.207911690817759, -0.809016994374947, 0.587785252292473],+ [0.978147600733806, -0.207911690817759, -0.913545457642601, 0.4067366430758],+ [0.978147600733806, -0.207911690817759, -0.978147600733806, 0.207911690817759],+ [0.978147600733806, -0.207911690817759, -1, 5.665498452323e-16],+ [0.978147600733806, -0.207911690817759, -0.978147600733806, -0.207911690817759],+ [0.978147600733806, -0.207911690817759, -0.913545457642601, -0.4067366430758],+ [0.978147600733806, -0.207911690817759, -0.809016994374948, -0.587785252292473],+ [0.978147600733806, -0.207911690817759, -0.669130606358858, -0.743144825477394],+ [0.978147600733806, -0.207911690817759, -0.5, -0.866025403784438],+ [0.978147600733806, -0.207911690817759, -0.309016994374948, -0.951056516295154],+ [0.978147600733806, -0.207911690817759, -0.104528463267654, -0.994521895368273],+ [0.978147600733806, -0.207911690817759, 0.104528463267653, -0.994521895368273],+ [0.978147600733806, -0.207911690817759, 0.309016994374947, -0.951056516295154],+ [0.978147600733806, -0.207911690817759, 0.5, -0.866025403784439],+ [0.978147600733806, -0.207911690817759, 0.669130606358858, -0.743144825477394],+ [0.978147600733806, -0.207911690817759, 0.809016994374947, -0.587785252292473],+ [0.978147600733806, -0.207911690817759, 0.913545457642601, -0.4067366430758],+ [0.978147600733806, -0.207911690817759, 0.978147600733806, -0.207911690817759]+ ]
+ src/Delaunay/R.hs view
@@ -0,0 +1,98 @@+module Delaunay.R+ where+-- import qualified Data.HashMap.Strict.InsOrd as H+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import Data.List+import Data.List.Index (iconcatMap)+-- import Data.Maybe+import Delaunay+import Text.Printf++-- | R code to plot a 2D Delaunay tesselation+delaunay2ForR :: Tesselation -> Bool -> String+delaunay2ForR tess colors =+ let tiles = IM.elems (_tiles tess) in+ "plot(0, 0, type=\"n\", xlim=c(0,5), ylim=c(0,5)) # please set the limits\n" +++ (if colors+ then printf "colors <- heat.colors(%d, alpha=0.5)\n" (length tiles + 1)+ else "\n") +++ concatMap triangle (zip [1 .. length tiles] tiles)+ where+ triangle :: (Int, Tile) -> String+ triangle (i, tile) =+ let pts = map (\p -> [p!!0,p!!1,p!!2]) (verticesCoordinates tile)+ in+ printf "polygon(c(%f,%f,%f), c(%f,%f,%f), border=\"black\", "+ (pts!!0!!0) (pts!!1!!0) (pts!!2!!0)+ (pts!!0!!1) (pts!!1!!1) (pts!!2!!1) +++ -- "polygon(c(" ++ show (pts!!0!!0) ++ ", " ++ show (pts!!1!!0) +++ -- ", " ++ show (pts!!2!!0) ++ "), "+ -- ++ "c(" ++ show (pts!!0!!1) ++ ", " ++ show (pts!!1!!1) +++ -- ", " ++ show (pts!!2!!1) +++ -- "), border=\"black\", " +++ (if colors+ then printf "col=colors[%d])\n" i+ else "col=\"lightblue\")\n")++-- | R code to plot a 3D Delaunay tesselation+delaunay3rgl :: Tesselation -> Bool -> Bool -> Bool -> Bool -> Maybe Double -> String+delaunay3rgl tess interior exterior segments colors alpha =+ let allridges = IM.elems (_tilefacets tess) in+ let ridges | exterior && interior = allridges+ | interior = filter sandwichedFacet allridges+ | exterior = filter (not . sandwichedFacet) allridges+ in+ "library(rgl)\n" +++ (if colors+ then printf "colors <- topo.colors(%d, alpha=0.5)\n" (length ridges + 1)+ else "\n") +++ concatMap rglRidge ridges +++ (if segments+ then if exterior && interior+ then concatMap rglSegment (edgesCoordinates tess)+ else concatMap rglSegment (tilefacetsEdges ridges)+ else "")+ where+ rglRidge :: TileFacet -> String+ rglRidge ridge =+ let i = 1 + head (IS.elems $ _facetOf ridge) in+ printf "\ntriangles3d(rbind(c%s,c%s,c%s"+ (show (pts!!0)) (show (pts!!1)) (show (pts!!2)) +++ (if colors+ then+ printf "), color=colors[%d]" i+ else+ "), color=\"blue\"") +++ maybe ")\n" (printf "alpha=%f)\n") alpha+ where+ pts = map (\p -> (p!!0,p!!1,p!!2)) (verticesCoordinates ridge)+ rglSegment :: ([Double], [Double]) -> String+ rglSegment (p1, p2) =+ printf "segments3d(rbind(c%s,c%s), color=\"black\")\n"+ (show p1') (show p2')+ where+ p1' = (p1!!0, p1!!1, p1!!2)+ p2' = (p2!!0, p2!!1, p2!!2)+ tilefacetsEdges :: [TileFacet] -> [([Double], [Double])]+ tilefacetsEdges tilefacets = foldl' union [] (map tilefacetEdges tilefacets)+ where+ tilefacetEdges :: TileFacet -> [([Double], [Double])]+ tilefacetEdges tilefacet = [(v!!0,v!!1),(v!!1,v!!2),(v!!2,v!!0)]+ where+ v = verticesCoordinates tilefacet++delaunaySpheres :: Tesselation -> String+delaunaySpheres tess =+ let tiles = IM.elems (_tiles tess) in+ "library(rgl)\n" +++ printf "colors <- rainbow(%d)\n" (length tiles) +++ iconcatMap rglSphere tiles+ where+ rglSphere :: Int -> Tile -> String+ rglSphere i tile =+ printf "spheres3d(%f, %f, %f, radius=%f, color=colors[%d], alpha=0.75)\n"+ (c!!0) (c!!1) (c!!2) r (i+1)+ where+ c = _center tile+ r = _circumradius (_simplex tile)
+ src/Delaunay/Types.hs view
@@ -0,0 +1,85 @@+module Delaunay.Types+ where+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IM+import Data.IntSet (IntSet)+import Qhull.Types++data Site = Site {+ _point :: [Double]+ , _neighsitesIds :: IndexSet+ , _neighfacetsIds :: IntSet+ , _neightilesIds :: IntSet+} deriving Show++data Simplex = Simplex {+ _vertices' :: IndexMap [Double]+ , _circumcenter :: [Double]+ , _circumradius :: Double+ , _volume' :: Double+} deriving Show++instance HasCenter Simplex where+ _center = _circumcenter++instance HasVertices Simplex where+ _vertices = _vertices'++instance HasVolume Simplex where+ _volume = _volume'++data TileFacet = TileFacet {+ _subsimplex :: Simplex+ , _facetOf :: IntSet+ , _normal' :: [Double]+ , _offset' :: Double+} deriving Show++instance HasNormal TileFacet where+ _normal = _normal'+ _offset = _offset'++instance HasVertices TileFacet where+ _vertices = _vertices' . _subsimplex++instance HasVolume TileFacet where+ _volume = _volume' . _subsimplex++instance HasCenter TileFacet where+ _center = _circumcenter . _subsimplex++data Tile = Tile {+ _simplex :: Simplex+ , _neighborsIds :: IntSet+ , _facetsIds :: IntSet+ , _family' :: Family+ , _toporiented :: Bool+} deriving Show++instance HasFamily Tile where+ _family = _family'++instance HasVertices Tile where+ _vertices = _vertices' . _simplex++instance HasVolume Tile where+ _volume = _volume' . _simplex++instance HasCenter Tile where+ _center = _circumcenter . _simplex++data Tesselation = Tesselation {+ _sites :: IndexMap Site+ , _tiles :: IntMap Tile+ , _tilefacets :: IntMap TileFacet+ , _edges' :: EdgeMap+} deriving Show++instance HasEdges Tesselation where+ _edges = _edges'++instance HasVertices Tesselation where+ _vertices tess = IM.map _point (_sites tess)++instance HasVolume Tesselation where+ _volume tess = sum (IM.elems $ IM.map (_volume' . _simplex) (_tiles tess))
+ src/HalfSpaces.hs view
@@ -0,0 +1,7 @@+module HalfSpaces+ (module X)+ where+import HalfSpaces.Constraint as X+import HalfSpaces.Examples as X+import HalfSpaces.HalfSpaces as X+import HalfSpaces.LinearCombination as X
+ src/HalfSpaces/CHalfSpaces.hs view
@@ -0,0 +1,15 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module HalfSpaces.CHalfSpaces+ where+import Foreign+import Foreign.C.Types++foreign import ccall unsafe "intersections" c_intersections+ :: Ptr CDouble -- halfspaces+ -> Ptr CDouble -- interior point+ -> CUInt -- dim+ -> CUInt -- n halfspaces+ -> Ptr CUInt -- n intersections+ -> Ptr CUInt -- exitcode+ -> CUInt -- 0/1 print to stdout+ -> IO (Ptr (Ptr CDouble))
+ src/HalfSpaces/Constraint.hs view
@@ -0,0 +1,28 @@+module HalfSpaces.Constraint+ where+-- import Data.Ratio (Rational)#+import HalfSpaces.LinearCombination (LinearCombination, constant)++data Sense = Gt | Lt+ deriving Eq++instance Show Sense where+ show Gt = ">="+ show Lt = "<="++data Constraint = Constraint LinearCombination Sense LinearCombination+ deriving (Eq, Show)++(.>=.) :: LinearCombination -> LinearCombination -> Constraint+(.>=.) lhs rhs = Constraint lhs Gt rhs++(.<=.) :: LinearCombination -> LinearCombination -> Constraint+(.<=.) lhs rhs = Constraint lhs Lt rhs++(.>=) :: LinearCombination -> Rational -> Constraint+(.>=) lhs x = (.>=.) lhs (constant x)++(.<=) :: LinearCombination -> Rational -> Constraint+(.<=) lhs x = (.<=.) lhs (constant x)++infix 4 .<=., .>=.
+ src/HalfSpaces/Examples.hs view
@@ -0,0 +1,53 @@+module HalfSpaces.Examples+ where+import Data.Ratio ((%))+import Data.VectorSpace+import HalfSpaces.Constraint (Constraint (..), (.<=), (.<=.),+ (.>=), (.>=.))+import HalfSpaces.LinearCombination (constant, cst, linearCombination,+ newVar)++testSmall :: [Constraint]+testSmall = [ x .<= 1, x .>= 0, y .<= 1]+ where+ x = newVar 1+ y = newVar 2++rggConstraints :: [Constraint]+rggConstraints =+ [ x .>= (-5)+ , x .<= 4+ , y .>= (-5)+ , y .<=. cst 3 ^-^ x+ , z .>= (-10)+ , z .<=. cst 6 ^-^ x ^-^ y ]+ where+ x = newVar 1+ y = newVar 2+ z = newVar 3++region3D :: [Constraint]+region3D =+ [ x .>= 0 -- shortcut for x .>=. cst 0+ , x .<= 3+ , y .>= 0+ , y .<=. cst 2 ^-^ (2%3)*^x+ , z .>= 0+ , z .<=. cst 6 ^-^ 2*^x ^-^ 3*^y ]+ where+ x = newVar 1+ y = newVar 2+ z = newVar 3++cubeConstraints :: [Constraint]+cubeConstraints =+ [ x .<= 1+ , x .>= (-1)+ , y .<= 1+ , y .>= (-1)+ , z .<= 1+ , z .>= (-1) ]+ where+ x = newVar 1+ y = newVar 2+ z = newVar 3
+ src/HalfSpaces/HalfSpaces.hs view
@@ -0,0 +1,64 @@+module HalfSpaces.HalfSpaces+ where+import Control.Monad (unless, when, (<$!>), (=<<))+import Foreign.C.Types+import Foreign.Marshal.Alloc (free, mallocBytes)+import Foreign.Marshal.Array (peekArray, pokeArray)+import Foreign.Storable (peek, sizeOf)+import HalfSpaces.CHalfSpaces+import HalfSpaces.Constraint (Constraint)+import HalfSpaces.Internal (normalizeConstraints)+import HalfSpaces.ToySolver++hsintersections' :: [[Double]] -- halfspaces+ -> [Double] -- interior point+ -> Bool -- print to stdout+ -> IO [[Double]]+hsintersections' halfspaces ipoint stdout = do+ let n = length halfspaces+ dim = length ipoint+ unless (all (== dim+1) (map length halfspaces)) $+ error "the points must have the same dimension"+ when (dim < 2) $+ error "dimension must be at least 2"+ when (n <= dim) $+ error "insufficient number of halfspaces"+ hsPtr <- mallocBytes (n * (dim+1) * sizeOf (undefined :: CDouble))+ pokeArray hsPtr (concatMap (map realToFrac) halfspaces)+ ipointPtr <- mallocBytes (dim * sizeOf (undefined :: CDouble))+ pokeArray ipointPtr (map realToFrac ipoint)+ exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt))+ nintersectionsPtr <- mallocBytes (sizeOf (undefined :: CUInt))+ resultPtr <- c_intersections hsPtr ipointPtr+ (fromIntegral dim) (fromIntegral n)+ nintersectionsPtr exitcodePtr (fromIntegral $ fromEnum stdout)+ exitcode <- peek exitcodePtr+ free exitcodePtr+ free hsPtr+ if exitcode /= 0+ then do+ free resultPtr+ free nintersectionsPtr+ error $ "qhull returned an error (code " ++ show exitcode ++ ")"+ else do+ nintersections <- (<$!>) fromIntegral (peek nintersectionsPtr)+ result <- (<$!>) (map (map realToFrac))+ ((=<<) (mapM (peekArray dim))+ (peekArray nintersections resultPtr))+ free resultPtr+ free nintersectionsPtr+ return result++hsintersections :: [Constraint] -> Bool -> IO [[Double]]+hsintersections constraints stdout = do+ let halfspacesMatrix = normalizeConstraints constraints+ ipoint <- (<$!>) (map realToFrac) (interiorPoint constraints)+ hsintersections' halfspacesMatrix ipoint stdout++cubeConstraints' :: [[Double]]+cubeConstraints' = [[ 1, 0, 0,-1]+ ,[-1, 0, 0,-1]+ ,[ 0, 1, 0,-1]+ ,[ 0,-1, 0,-1]+ ,[ 0, 0, 1,-1]+ ,[ 0, 0,-1,-1]]
+ src/HalfSpaces/Internal.hs view
@@ -0,0 +1,46 @@+module HalfSpaces.Internal+ (normalizeConstraints, varsOfConstraint)+ where+import Data.IntMap.Strict (IntMap, mergeWithKey)+import qualified Data.IntMap.Strict as IM+import Data.List (nub, union)+import Data.Ratio+import HalfSpaces.Constraint (Constraint (..), Sense (..))+import HalfSpaces.LinearCombination (LinearCombination (..), VarIndex)++normalizeLinearCombination :: [VarIndex] -> LinearCombination -> IntMap Rational+normalizeLinearCombination vars (LinearCombination lc) =+ IM.union lc (IM.fromList [(i,0) | i <- vars `union` [0]])++varsOfLinearCombo :: LinearCombination -> [VarIndex]+varsOfLinearCombo (LinearCombination imap) = IM.keys imap++varsOfConstraint :: Constraint -> [VarIndex]+varsOfConstraint (Constraint lhs _ rhs) =+ varsOfLinearCombo lhs `union` varsOfLinearCombo rhs++normalizeConstraint :: [VarIndex] -> Constraint -> [Double]+normalizeConstraint vars (Constraint lhs sense rhs) =+ if sense == Lt+ then xs ++ [x]+ else map negate xs ++ [-x]+ where+ lhs' = normalizeLinearCombination vars lhs+ rhs' = normalizeLinearCombination vars rhs+ coefs = IM.elems $ mergeWithKey (\_ a b -> Just (a-b)) id id lhs' rhs'+ denominators = map denominator coefs+ ppcm = foldr lcm 1 denominators % 1+ x:xs = map (realToFrac . numerator . (*ppcm)) coefs+ -- let (x:xs) = map realToFrac $+ -- IM.elems $ mergeWithKey (\_ a b -> Just (a-b)) id id lhs' rhs'+ -- in+ -- if sense == Lt+ -- then xs ++ [x]+ -- else map negate xs ++ [-x]+ -- where lhs' = normalizeLinearCombination vars lhs+ -- rhs' = normalizeLinearCombination vars rhs++normalizeConstraints :: [Constraint] -> [[Double]] -- for qhalf+normalizeConstraints constraints = map (normalizeConstraint vars) constraints+ where+ vars = nub $ concatMap varsOfConstraint constraints
+ src/HalfSpaces/LinearCombination.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE TypeFamilies #-}+module HalfSpaces.LinearCombination+ where+import Data.AdditiveGroup+import Data.IntMap.Strict (IntMap, mergeWithKey)+import qualified Data.IntMap.Strict as IM+import Data.List+import Data.Ratio+import Data.Tuple (swap)+import Data.VectorSpace++newtype LinearCombination = LinearCombination (IntMap Rational)+ deriving Eq++instance Show LinearCombination where+ show (LinearCombination x) =+ intercalate " + " $+ map (\(i,r) -> if i==0+ then showRational r+ else if r == 1+ then "x" ++ show i+ else showRational r ++ "*x" ++ show i)+ (IM.toAscList x)+ where+ showRational :: Rational -> String+ showRational r = if q==1 then show p else show p ++ "/" ++ show q+ where+ p = numerator r+ q = denominator r++instance AdditiveGroup LinearCombination where+ zeroV = LinearCombination (IM.singleton 0 0)+ (^+^) (LinearCombination imap1) (LinearCombination imap2) =+ LinearCombination+ (mergeWithKey (\_ x y -> Just (x+y)) id id imap1 imap2)+ negateV (LinearCombination imap) = LinearCombination (IM.map negate imap)++instance VectorSpace LinearCombination where+ type Scalar LinearCombination = Rational+ (*^) lambda (LinearCombination imap) =+ LinearCombination (IM.map (*lambda) imap)++type Var = LinearCombination+type VarIndex = Int++-- | new variable+newVar :: VarIndex -> Var+newVar i = if i >= 0+ then LinearCombination (IM.singleton i 1)+ else error "negative index"++-- | linear combination from list of terms+linearCombination :: [(Rational,Var)] -> LinearCombination+linearCombination terms = linearCombo (map swap terms)+-- LinearCombination (IM.fromListWith (+) (map swap terms))++-- | constant linear combination+constant :: Rational -> LinearCombination+constant x = LinearCombination (IM.singleton 0 x)++-- | alias for `constant`+cst :: Rational -> LinearCombination+cst = constant
+ src/HalfSpaces/ToySolver.hs view
@@ -0,0 +1,44 @@+module HalfSpaces.ToySolver+ (interiorPoint)+ where+import Control.Monad (replicateM_)+import Data.Default.Class+import Data.IntMap.Strict (IntMap, mapKeys, mergeWithKey)+import qualified Data.IntMap.Strict as IM+import Data.List (nub)+-- import Data.Ratio (Rational)+import Data.VectorSpace+import HalfSpaces.Constraint (Constraint (..), Sense (..))+import HalfSpaces.Internal (varsOfConstraint)+import HalfSpaces.LinearCombination (LinearCombination (..))+import ToySolver.Arith.Simplex hiding (Lt)+import qualified ToySolver.Data.LA as LA++constraintToCoeffMap :: Int -> Constraint -> IntMap Rational+constraintToCoeffMap+ newvar (Constraint (LinearCombination lhs) sense (LinearCombination rhs)) =+ let terms = mapKeys (subtract 1)+ (mergeWithKey (\_ x y -> Just (x-y)) id (IM.map negate) lhs rhs)+ in+ if sense == Lt+ then IM.union terms (IM.singleton newvar 1)+ else IM.union (IM.map negate terms) (IM.singleton newvar 1)++constraintToAtom :: Int -> Constraint -> Atom Rational+constraintToAtom newvar constraint =+ let coeffmap = constraintToCoeffMap newvar constraint+ in+ LA.fromCoeffMap coeffmap .<=. LA.constant 0++interiorPoint :: [Constraint] -> IO [Rational]+interiorPoint constraints = do+ let vars = nub (concatMap varsOfConstraint constraints)+ dim = length (filter (/= 0) vars)+ atoms = map (constraintToAtom dim) constraints+ solver <- newSolver+ replicateM_ (dim+1) (newVar solver)+ mapM_ (assertAtom solver) atoms+ setObj solver (negateV $ LA.var dim)+ o <- optimize solver def+ print o+ mapM (getValue solver) [0 .. dim-1]
@@ -0,0 +1,54 @@+module Qhull.Shared+ where+import qualified Data.HashMap.Strict.InsOrd as H+import qualified Data.IntMap.Strict as IM+import Data.Maybe+import Qhull.Types++-- | whether two families are the same+sameFamily :: Family -> Family -> Bool+sameFamily (Family i) (Family j) = i == j+sameFamily _ _ = False++-- | vertices ids+verticesIds :: HasVertices a => a -> [Index]+verticesIds = IM.keys . _vertices++-- | vertices coordinates+verticesCoordinates :: HasVertices a => a -> [[Double]]+verticesCoordinates = IM.elems . _vertices++-- | number of vertices+nVertices :: HasVertices a => a -> Int+nVertices = IM.size . _vertices++-- | edges ids+edgesIds :: HasEdges a => a -> [IndexPair]+edgesIds = H.keys . _edges++-- | edges ids as pairs of integers+edgesIds' :: HasEdges a => a -> [(Index,Index)]+edgesIds' x = map fromPair (edgesIds x)+ where+ fromPair (Pair i j) = (i,j)++-- | edges coordinates+edgesCoordinates :: HasEdges a => a -> [([Double],[Double])]+edgesCoordinates = H.elems . _edges++-- | number of edges+nEdges :: HasEdges a => a -> Int+nEdges = H.size . _edges++-- | whether a pair of vertices indices form an edge;+-- the order of the indices has no importance+isEdge :: HasEdges a => a -> (Index, Index) -> Bool+isEdge x (i,j) = Pair i j `H.member` _edges x++-- | edge as pair of points; the order of the vertices has no importance+toPoints :: HasEdges a => a -> (Index, Index) -> Maybe ([Double], [Double])+toPoints x (i,j) = H.lookup (Pair i j) (_edges x)++-- | edge as pair of points, without checking the edge exists+toPoints' :: HasEdges a => a -> (Index, Index) -> ([Double], [Double])+toPoints' x (i,j) = fromJust $ toPoints x (i,j)
+ src/Qhull/Types.hs view
@@ -0,0 +1,42 @@+module Qhull.Types+ where+import Data.Hashable+import Data.HashMap.Strict.InsOrd (InsOrdHashMap)+import Data.IntMap.Strict (IntMap)+import Data.IntSet (IntSet)++type Index = Int+type IndexMap = IntMap+type IndexSet = IntSet++data IndexPair = Pair Index Index+ deriving (Show, Read)+instance Eq IndexPair where+ Pair i j == Pair i' j' = (i == i' && j == j') || (i == j' && j == i')++instance Hashable IndexPair where+ hashWithSalt _ (Pair i j) = (i+j)*(i+j+1) + 2 * min i j++type EdgeMap = InsOrdHashMap IndexPair ([Double],[Double])++data Family = Family Int | None+ deriving (Show, Read, Eq)++class HasFamily m where+ _family :: m -> Family++class HasNormal m where+ _normal :: m -> [Double]+ _offset :: m -> Double++class HasVertices m where+ _vertices :: m -> IndexMap [Double]++class HasEdges m where+ _edges :: m -> EdgeMap++class HasVolume m where+ _volume :: m -> Double++class HasCenter m where+ _center :: m -> [Double]
+ src/Voronoi/R.hs view
@@ -0,0 +1,98 @@+module Voronoi.R+ where+import ConvexHull (convexHull, ConvexHull (..))+import qualified Data.IntMap.Strict as IM+import Data.List (intercalate, transpose)+import Data.List.Index (imap, iconcatMap)+import Data.Maybe+import Delaunay.R+import Delaunay.Types (Tesselation)+import Qhull.Shared+import Voronoi2D+import Voronoi3D++voronoi2ForR :: Voronoi2 -> Maybe Tesselation -> String+voronoi2ForR v d =+ (if isJust d then dcode else "")+ ++ "colors <- rainbow(" ++ show (length boundedCells +1) ++ ")\n"+ ++ unlines (imap boundedCellForR boundedCells)+ ++ unlines (map cellForR v)+ where+ dcode = delaunay2ForR (fromJust d) True+ cellForR :: ([Double], Cell2) -> String+ cellForR (site, edges) =+ point ++ "\n" ++ unlines (map f edges)+ where+ point =+ "points(" ++ show (site!!0) ++ ", " ++ show (site!!1) +++ ", pch=19, col=\"blue\")"+ f :: Edge2 -> String+ f edge = case edge of+ Edge2 ((x0,y0),(x1,y1)) ->+ "segments(" ++ intercalate "," (map show [x0,y0,x1,y1]) +++ ", col=\"black\", lty=1, lwd=1)"+ IEdge2 ((x0,y0),(x1,y1)) ->+ "segments(" ++ intercalate "," (map show [x0,y0,x0+x1,y0+y1]) +++ ", col=\"red\", lty=1, lwd=1)"+ TIEdge2 ((x0,y0),(x1,y1)) ->+ "segments(" ++ intercalate "," (map show [x0,y0,x1,y1]) +++ ", col=\"red\", lty=1, lwd=1)"+ boundedCells = map snd (restrictVoronoi2 v)+ boundedCellForR :: Int -> Cell2 -> String+ boundedCellForR i cell =+ "polygon(c(" ++ intercalate "," (map show x) ++ "), c(" +++ intercalate "," (map show y) ++ "), col=colors[" ++ show (i+1) ++ "])\n"+ where+ [x, y] = transpose (cell2Vertices' cell)+++voronoi3ForRgl :: Voronoi3 -> Maybe Int -> Maybe Tesselation -> String+voronoi3ForRgl v n d =+ let v' = if isJust n then roundVoronoi3 (fromJust n) v else v in+ let code = unlines $ map cellForRgl v' in+ "library(rgl)\n" +++ if isJust d+ then code ++ "\n" ++ "# Delaunay:\n" +++ delaunay3rgl (fromJust d) True True True True (Just 0.9)+ else code+ where+ cellForRgl :: ([Double], Cell3) -> String+ cellForRgl (site, cell) = plotpoint ++ unlines (map f cell)+ where+ plotpoint = "spheres3d(" ++ intercalate "," (map show site) ++ ", radius=0.1, color=\"red\")\n"+ f :: Edge3 -> String+ f edge = case edge of+ Edge3 (x,y) ->+ "segments3d(rbind(c" ++ show x ++ ", \n\tc" ++ show y ++ "))"+ TIEdge3 (x,y) ->+ "segments3d(rbind(c" ++ show x ++ ", \n\tc" ++ show y ++ "), col=c(\"red\",\"red\"))"+ IEdge3 (x,y) ->+ "segments3d(rbind(c" ++ show x ++ ", \n\tc" ++ show (sumTriplet x y) ++ "), col=c(\"red\",\"red\"))"+ sumTriplet (a,b,c) (a',b',c') = (a+a',b+b',c+c')++-- | plot with facets+voronoi3ForRgl' :: Voronoi3 -> Maybe Int -> Maybe Tesselation -> IO String+voronoi3ForRgl' v n d = do -- faudrait un argument approx+ let code1 = voronoi3ForRgl v n d+ v' = restrictVoronoi3' v+ v'' = if isJust n then roundVoronoi3 (fromJust n) v' else v'+ boundedCells = map (cell3Vertices . snd) (restrictVoronoi3' v'')+-- boundedCells' = map (nub . map (map (approx 13))) boundedCells+ hulls <- mapM (\cell -> convexHull cell True False Nothing) boundedCells+ let triangles = map (map verticesCoordinates . IM.elems . _hfacets) hulls+ code_colors = "colors <- rainbow(" ++ show (length triangles +1) ++ ")\n"+ code2 = iconcatMap (\i x -> concatMap (rglTriangle i) x ++ "\n") triangles+ return $ code_colors ++ code1 ++ code2+ where+ -- approx :: RealFrac a => Int -> a -> a+ -- approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)+ asTriplet p = (p!!0, p!!1, p!!2)+ rglTriangle :: Int -> [[Double]] -> String+ rglTriangle i threepoints =+ "triangles3d(rbind(c" ++ show p1 ++ ", \n\tc" ++ show p2 +++ ", \n\tc" ++ show p3 ++ "), color=colors[" ++ show (i+1) ++ "]" +++ ", alpha=0.75)\n"+ where+ p1 = asTriplet $ threepoints!!0+ p2 = asTriplet $ threepoints!!1+ p3 = asTriplet $ threepoints!!2
@@ -0,0 +1,8 @@+module Voronoi.Shared+ where++filterVoronoi :: ([a] -> Bool) -> [([Double], [a])] -> [([Double], [a])]+filterVoronoi cellTester = filter (\(_, cell) -> cellTester cell)++removeDegenerateCells :: [([Double], [a])] -> [([Double], [a])]+removeDegenerateCells = filterVoronoi (not . null)
+ src/Voronoi/Voronoi.hs view
@@ -0,0 +1,63 @@+module Voronoi.Voronoi+ where+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import Data.Maybe+import Delaunay.Delaunay (vertexNeighborFacets)+import Delaunay.Types+import Qhull.Shared+import Qhull.Types++type Point = [Double]+type Vector = [Double]+data Edge = Edge (Point, Point) | IEdge (Point, Vector)+ deriving Show+type Cell = [Edge]++-- factor2 :: (Double,Double,Double,Double) -> (Double,Double) -> (Double,Double) -> Double+-- factor2 box@(xmin, xmax, ymin, ymax) p@(p1,p2) (v1,v2)+-- | v1==0 = if v2>0 then (ymax-p2)/v2 else (ymin-p2)/v2+-- | v2==0 = if v1>0 then (xmax-p1)/v1 else (xmin-p1)/v1+-- | otherwise = min (factor2 box p (v1,0)) (factor2 box p (0,v2))+-- -- | v1>0 && v2>0 = min ((r-p1)/v1) ((t-p2)/v2)+-- -- | v1>0 && v2<0 = min ((r-p1)/v1) ((b-p2)/v2)+-- -- | v1<0 && v2>0 = min ((l-p1)/v1) ((t-p2)/v2)+-- -- | v1<0 && v2<0 = min ((l-p1)/v1) ((b-p2)/v2)++-- approx :: RealFrac a => Int -> a -> a+-- approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)++edgesFromTileFacet :: Tesselation -> TileFacet -> Maybe Edge+edgesFromTileFacet tess tilefacet+ | length tileindices == 1 = Just $ IEdge (c1, _normal tilefacet)+ | sameFamily (_family tile1) (_family tile2) || c1 == c2 = Nothing+ | otherwise = Just $ Edge (c1, c2)+ where+ tileindices = (IS.toList . _facetOf) tilefacet+ tiles = _tiles tess+ tile1 = tiles IM.! head tileindices+ tile2 = tiles IM.! last tileindices+ c1 = _center tile1+ c2 = _center tile2++voronoiCell :: ([TileFacet] -> [TileFacet]) -> (Edge -> a) -> Tesselation+ -> Index -> [a]+voronoiCell facetsQuotienter edgeTransformer tess i =+ let tilefacets = facetsQuotienter $ IM.elems (vertexNeighborFacets tess i) in+ map (edgeTransformer . fromJust) $+ filter isJust $ map (edgesFromTileFacet tess) tilefacets++voronoi :: (Tesselation -> Index -> a) -> Tesselation -> [([Double], a)]+voronoi cellGetter tess =+ let sites = IM.elems $ _vertices tess in+ zip sites (map (cellGetter tess) [0 .. length sites -1])++voronoi' :: Tesselation -> [([Double], Cell)]+voronoi' = voronoi (voronoiCell id id)++-- | whether a Voronoi cell is bounded+boundedCell :: Cell -> Bool+boundedCell = all isFiniteEdge+ where+ isFiniteEdge (Edge _) = True+ isFiniteEdge _ = False
+ src/Voronoi2D.hs view
@@ -0,0 +1,122 @@+module Voronoi2D+ (Edge2(..)+ , Cell2+ , Voronoi2+ , prettyShowVoronoi2+ , voronoiCell2+ , voronoi2+ , clipVoronoi2+ , boundedCell2+ , restrictVoronoi2+ , cell2Vertices+ , cell2Vertices'+ , module Voronoi.Shared)+ where+import Control.Arrow (second)+import Data.Graph (flattenSCCs, stronglyConnComp)+import Data.List+import Data.List.Index (imap)+import Data.Tuple.Extra (both)+import Delaunay.Types+import Qhull.Types+import Text.Show.Pretty (ppShow)+import Voronoi.Shared+import Voronoi.Voronoi++type Point2 = (Double, Double)+type Vector2 = (Double, Double)+data Edge2 = Edge2 (Point2, Point2) | IEdge2 (Point2, Vector2)+ | TIEdge2 (Point2, Point2)+ deriving (Show, Eq)+type Cell2 = [Edge2]+type Voronoi2 = [([Double], Cell2)]+type Box2 = ((Double, Double), (Double, Double))++-- | pretty print a Voronoi 2D diagram+prettyShowVoronoi2 :: Voronoi2 -> Maybe Int -> IO ()+prettyShowVoronoi2 v m = do+ let string = intercalate "\n---\n" (map (prettyShowCell2 m) v)+ putStrLn string+ where+ approx :: RealFrac a => Int -> a -> a+ approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)+ roundPairPoint2 :: (Point2, Point2) -> Int -> (Point2, Point2)+ roundPairPoint2 ((x1,x2), (y1,y2)) n =+ (asPair $ map (approx n) [x1,x2], asPair $ map (approx n) [y1,y2])+ prettyShowEdge2 :: Maybe Int -> Edge2 -> String+ prettyShowEdge2 n edge = case edge of+ Edge2 x -> " Edge " ++ string x+ IEdge2 x -> " IEdge " ++ string x+ TIEdge2 x -> " TIEdge " ++ string x+ where+ string x = ppShow $ maybe x (roundPairPoint2 x) n+ prettyShowEdges2 :: Maybe Int -> [Edge2] -> String+ prettyShowEdges2 n edges = intercalate "\n" (map (prettyShowEdge2 n) edges)+ prettyShowCell2 :: Maybe Int -> ([Double], Cell2) -> String+ prettyShowCell2 n (site, edges) =+ "Site " ++ ppShow site ++ " :\n" ++ prettyShowEdges2 n edges++asPair :: [Double] -> (Double, Double)+asPair [a,b] = (a,b)+asPair _ = (undefined, undefined)++edgeToEdge2 :: Edge -> Edge2+edgeToEdge2 (Edge (x, y)) = Edge2 (both asPair (x, y))+edgeToEdge2 (IEdge (x, v)) = IEdge2 (both asPair (x, v))++-- | Voronoi cell of a vertex given by its index+voronoiCell2 :: Tesselation -> Index -> Cell2+voronoiCell2 = voronoiCell id edgeToEdge2++-- | 2D Voronoi Diagram+voronoi2 :: Tesselation -> Voronoi2+voronoi2 = voronoi voronoiCell2++-- | whether a 2D Voronoi cell is bounded+boundedCell2 :: Cell2 -> Bool+boundedCell2 = all isFiniteEdge+ where+ isFiniteEdge (Edge2 _) = True+ isFiniteEdge _ = False++-- | restrict a 2D Voronoi diagram to its bounded cells+restrictVoronoi2 :: Voronoi2 -> Voronoi2+restrictVoronoi2 = filterVoronoi boundedCell2++-- | vertices of a bounded 2D cell+cell2Vertices :: Cell2 -> [[Double]]+cell2Vertices cell = nub $ concatMap extractVertices cell+ where+ extractVertices :: Edge2 -> [[Double]]+ extractVertices (Edge2 ((x1,x2),(y1,y2))) = [[x1,x2],[y1,y2]]+ extractVertices _ = []++-- | ordered vertices of a bounded 2D cell+cell2Vertices' :: Cell2 -> [[Double]]+cell2Vertices' cell = flattenSCCs (stronglyConnComp x)+ where+ vs = cell2Vertices cell+ x = imap (\i v -> (v, i, findIndices (connectedVertices v) vs)) vs+ connectedVertices :: [Double] -> [Double] -> Bool+ connectedVertices [x1,x2] [y1,y2] =+ (Edge2 ((x1,x2),(y1,y2)) `elem` cell) ||+ (Edge2 ((y1,y2),(x1,x2)) `elem` cell)+ connectedVertices _ _ = False++truncEdge2 :: Box2 -> Edge2 -> Edge2+truncEdge2 ((xmin, xmax), (ymin, ymax)) edge =+ if isIEdge edge+ then TIEdge2 (p, (p1 + factor v1 v2 * v1, p2 + factor v1 v2 * v2))+ else edge+ where+ isIEdge (IEdge2 _) = True+ isIEdge _ = False+ IEdge2 (p@(p1,p2), (v1,v2)) = edge+ factor w1 w2 | w1==0 = if w2>0 then (ymax-p2)/w2 else (ymin-p2)/w2+ | w2==0 = if w1>0 then (xmax-p1)/w1 else (xmin-p1)/w1+ | otherwise = min (factor w1 0) (factor 0 w2)+ -- factor = factor2 box p v++-- | clip a 2D Voronoi diagram in a bounding box+clipVoronoi2 :: Box2 -> Voronoi2 -> Voronoi2+clipVoronoi2 box = map (second (map (truncEdge2 box)))
+ src/Voronoi3D.hs view
@@ -0,0 +1,196 @@+module Voronoi3D+ (Edge3(..)+ , Cell3+ , Voronoi3+ , prettyShowVoronoi3+ , clipVoronoi3+ , voronoiCell3+ , voronoi3+ , cell3Vertices+ , voronoi3vertices+ , boundedCell3+ , restrictVoronoi3+ , restrictVoronoi3'+ , restrictVoronoi3box+ , restrictVoronoi3box'+ , roundVoronoi3+ , summaryVoronoi3+ , module Voronoi.Shared)+ where+import Control.Arrow (second)+import Control.Monad (liftM2)+import qualified Data.IntSet as IS+import Data.List+import Data.List.Unique (count_)+import Data.Tuple.Extra (both)+import Delaunay.Types+import Qhull.Types+import Text.Show.Pretty (ppShow)+import Voronoi.Shared+import Voronoi.Voronoi++type Point3 = (Double, Double, Double)+type Vector3 = (Double, Double, Double)++data Edge3 = Edge3 (Point3, Point3) | IEdge3 (Point3, Vector3)+ | TIEdge3 (Point3, Point3)+ deriving (Show)+instance Eq Edge3 where+ Edge3 (x,y) == Edge3 (x',y') = (x == x' && y == y') || (x == y') && (y == x')+ IEdge3 (x,v) == IEdge3 (x',v') = x == x' && v == v'+ TIEdge3 (x,y) == TIEdge3 (x',y') = x == x' && y == y'+ _ == _ = False++type Cell3 = [Edge3]+type Voronoi3 = [([Double], Cell3)]+type Box3 = ((Double, Double), (Double, Double), (Double, Double))++-- | summary of a 3D Voronoi diagram+summaryVoronoi3 :: Voronoi3 -> IO ()+summaryVoronoi3 v = do+ let ntotal = show $ length v+ let boundedDiagram = restrictVoronoi3 v+ let nbounded = show $ length boundedDiagram+ let ndegenerate = show $ length $ filterVoronoi null boundedDiagram+ let lengths = map (\(_,cell) -> length cell) boundedDiagram+ putStrLn $ "Voronoi diagram with " ++ ntotal ++ " cells, including " +++ nbounded ++ " bounded and " ++ ndegenerate ++ " degenerate.\n" +++ "Number of edges for bounded cells: " ++ show (count_ lengths)++-- | pretty print a 3D Voronoi diagram+prettyShowVoronoi3 :: Voronoi3 -> Maybe Int -> IO ()+prettyShowVoronoi3 v m = do+ let string = intercalate "\n---\n" (map (prettyShowCell3 m) v)+ putStrLn $ string ++ "\n------\n"+ where+ approx :: RealFrac a => Int -> a -> a+ approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)+ roundPairPoint3 :: (Point3, Point3) -> Int -> (Point3, Point3)+ roundPairPoint3 ((x1,x2,x3), (y1,y2,y3)) n =+ (asTriplet $ map (approx n) [x1,x2,x3],+ asTriplet $ map (approx n) [y1,y2,y3])+ prettyShowEdge3 :: Maybe Int -> Edge3 -> String+ prettyShowEdge3 n edge = case edge of+ Edge3 x -> " Edge " ++ string x+ IEdge3 x -> " IEdge " ++ string x+ TIEdge3 x -> " TIEdge " ++ string x+ where+ string x = ppShow $ maybe x (roundPairPoint3 x) n+ prettyShowEdges3 :: Maybe Int -> [Edge3] -> String+ prettyShowEdges3 n edges = intercalate "\n" (map (prettyShowEdge3 n) edges)+ prettyShowCell3 :: Maybe Int -> ([Double], Cell3) -> String+ prettyShowCell3 n (site, edges) =+ "Site " ++ ppShow site ++ " :\n" ++ prettyShowEdges3 n edges+++asTriplet :: [a] -> (a, a, a)+asTriplet [x,y,z] = (x,y,z)+asTriplet _ = (undefined, undefined, undefined)++edgeToEdge3 :: Edge -> Edge3+edgeToEdge3 (Edge (x, y)) = Edge3 (both asTriplet (x, y))+edgeToEdge3 (IEdge (x, v)) = IEdge3 (both asTriplet (x, v))++equalFacets :: TileFacet -> TileFacet -> Bool+equalFacets tfacet1 tfacet2 =+ IS.size f1 == 1 && IS.size f2 == 1 &&+ _center tfacet1 == _center tfacet2 &&+ _normal tfacet1 == _normal tfacet2+ where+ f1 = _facetOf tfacet1+ f2 = _facetOf tfacet2++-- | Voronoi cell of a vertex given by its index+voronoiCell3 :: Tesselation -> Index -> Cell3+voronoiCell3 = voronoiCell (nubBy equalFacets) edgeToEdge3++-- | 3D Voronoi diagram+voronoi3 :: Tesselation -> Voronoi3+voronoi3 = voronoi voronoiCell3++-- |+roundVoronoi3 :: Int -> Voronoi3 -> Voronoi3+roundVoronoi3 n = map (second roundCell3)+ where+ roundCell3 :: Cell3 -> Cell3+ roundCell3 cell = nub $ map roundEdge3 cell+ roundEdge3 :: Edge3 -> Edge3+ roundEdge3 (Edge3 (x,y)) = Edge3 (approx n x, approx n y)+ roundEdge3 (IEdge3 (x,v)) = IEdge3 (approx n x, approx n v)+ roundEdge3 (TIEdge3 (x,y)) = TIEdge3 (approx n x, y)+ approx :: Int -> (Double, Double, Double) -> (Double, Double, Double)+ approx m (a,b,c) =+ asTriplet $ map (\x -> fromInteger (round $ x*(10^m)) / (10.0^^m)) [a,b,c]+++-- | whether a 3D Voronoi cell is bounded+boundedCell3 :: Cell3 -> Bool+boundedCell3 = all isFiniteEdge+ where+ isFiniteEdge (Edge3 _) = True+ isFiniteEdge _ = False++-- | whether a 3D Voronoi cell is inside a given box+cell3inBox :: Box3 -> Cell3 -> Bool+cell3inBox ((xmin,xmax), (ymin, ymax), (zmin,zmax)) cell =+ boundedCell3 cell && all edgeInBox cell+ where+ tripletInBox (x,y,z) =+ x > xmin && y > ymin && z > zmin && x < xmax && y < ymax && z < zmax+ edgeInBox (Edge3 (p1,p2)) = tripletInBox p1 && tripletInBox p2+ edgeInBox _ = False++-- | restrict a 3D Voronoi diagram to its bounded cells+restrictVoronoi3 :: Voronoi3 -> Voronoi3+restrictVoronoi3 = filterVoronoi boundedCell3++-- | restrict a 3D Voronoi diagram to its nondegenerate bounded cells+restrictVoronoi3' :: Voronoi3 -> Voronoi3+restrictVoronoi3' = filterVoronoi (liftM2 (&&) boundedCell3 (not . null))+-- (\cell -> boundedCell3 cell && not (null cell))++-- | restrict a 3D Voronoi diagram to the cells contained in a box+restrictVoronoi3box :: Box3 -> Voronoi3 -> Voronoi3+restrictVoronoi3box box = filterVoronoi (cell3inBox box)++-- | restrict a 3D Voronoi diagram to the nondegenerate cells contained in a box+restrictVoronoi3box' :: Box3 -> Voronoi3 -> Voronoi3+restrictVoronoi3box' box =+ filterVoronoi (not . null) . restrictVoronoi3box box++-- | vertices of a bounded 3D cell+cell3Vertices :: Cell3 -> [[Double]]+cell3Vertices cell = nub $ concatMap extractVertices cell+ where+ extractVertices :: Edge3 -> [[Double]]+ extractVertices (Edge3 ((x1,y1,z1),(x2,y2,z2))) = [[x1,y1,z1],[x2,y2,z2]]+ extractVertices _ = []++-- | vertices of a 3D Voronoi diagram+voronoi3vertices :: Voronoi3 -> [[Double]]+voronoi3vertices = concatMap (\(_,cell) -> cell3Vertices cell)++truncEdge3 :: Box3 -> Edge3 -> Edge3+truncEdge3 ((xmin, xmax), (ymin, ymax), (zmin, zmax)) edge =+ if isIEdge edge+ then TIEdge3 ((p1,p2,p3), (p1 + factor v1 v2 v3 * v1,+ p2 + factor v1 v2 v3 * v2, p3 + factor v1 v2 v3 * v3))+ else edge+ where+ isIEdge (IEdge3 _) = True+ isIEdge _ = False+ IEdge3 ((p1,p2,p3), (v1,v2,v3)) = edge+ factor u1 u2 u3 | u1==0 && u2==0 = (if u3>0 then zmax-p3 else zmin-p3)/u3+ | u1==0 && u3==0 = (if u2>0 then ymax-p2 else ymin-p2)/u2+ | u2==0 && u3==0 = (if u1>0 then xmax-p1 else xmin-p1)/u1+ | otherwise = min (min (factor u1 0 0) (factor 0 u2 0))+ (factor 0 0 u3)+ -- factor u1 u2 u3 | u3==0 = factor2 (xmin,xmax,ymin,ymax) (p1,p2) (u1,u2)+ -- | u2==0 = factor2 (zmin,zmax,xmin,xmax) (p3,p1) (u3,u1)+ -- | u1==0 = factor2 (ymin,ymax,zmin,zmax) (p2,p3) (u2,u3)+ -- | otherwise = min (min (factor u1 u2 0) (factor 0 u2 u3))+ -- (factor u1 0 u3)++-- | clip 3D Voronoi diagram in a bounding box+clipVoronoi3 :: Box3 -> Voronoi3 -> Voronoi3+clipVoronoi3 box = map (second (map (truncEdge3 box)))