[10207] | 1 | /*<html><pre> -<a href="qh-geom.htm"
|
---|
| 2 | >-------------------------------</a><a name="TOP">-</a>
|
---|
| 3 |
|
---|
| 4 |
|
---|
| 5 | geom2.c
|
---|
| 6 | infrequently used geometric routines of qhull
|
---|
| 7 |
|
---|
| 8 | see qh-geom.htm and geom.h
|
---|
| 9 |
|
---|
| 10 | Copyright (c) 1993-2012 The Geometry Center.
|
---|
| 11 | $Id: //main/2011/qhull/src/libqhull/geom2.c#3 $$Change: 1464 $
|
---|
| 12 | $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
|
---|
| 13 |
|
---|
| 14 | frequently used code goes into geom.c
|
---|
| 15 | */
|
---|
| 16 |
|
---|
| 17 | #include "qhull_a.h"
|
---|
| 18 |
|
---|
| 19 | /*================== functions in alphabetic order ============*/
|
---|
| 20 |
|
---|
| 21 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 22 | >-------------------------------</a><a name="copypoints">-</a>
|
---|
| 23 |
|
---|
| 24 | qh_copypoints( points, numpoints, dimension)
|
---|
| 25 | return qh_malloc'd copy of points
|
---|
| 26 | */
|
---|
| 27 | coordT *qh_copypoints(coordT *points, int numpoints, int dimension) {
|
---|
| 28 | int size;
|
---|
| 29 | coordT *newpoints;
|
---|
| 30 |
|
---|
| 31 | size= numpoints * dimension * (int)sizeof(coordT);
|
---|
| 32 | if (!(newpoints=(coordT*)qh_malloc((size_t)size))) {
|
---|
| 33 | qh_fprintf(qh ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
|
---|
| 34 | numpoints);
|
---|
| 35 | qh_errexit(qh_ERRmem, NULL, NULL);
|
---|
| 36 | }
|
---|
| 37 | memcpy((char *)newpoints, (char *)points, (size_t)size);
|
---|
| 38 | return newpoints;
|
---|
| 39 | } /* copypoints */
|
---|
| 40 |
|
---|
| 41 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 42 | >-------------------------------</a><a name="crossproduct">-</a>
|
---|
| 43 |
|
---|
| 44 | qh_crossproduct( dim, vecA, vecB, vecC )
|
---|
| 45 | crossproduct of 2 dim vectors
|
---|
| 46 | C= A x B
|
---|
| 47 |
|
---|
| 48 | notes:
|
---|
| 49 | from Glasner, Graphics Gems I, p. 639
|
---|
| 50 | only defined for dim==3
|
---|
| 51 | */
|
---|
| 52 | void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){
|
---|
| 53 |
|
---|
| 54 | if (dim == 3) {
|
---|
| 55 | vecC[0]= det2_(vecA[1], vecA[2],
|
---|
| 56 | vecB[1], vecB[2]);
|
---|
| 57 | vecC[1]= - det2_(vecA[0], vecA[2],
|
---|
| 58 | vecB[0], vecB[2]);
|
---|
| 59 | vecC[2]= det2_(vecA[0], vecA[1],
|
---|
| 60 | vecB[0], vecB[1]);
|
---|
| 61 | }
|
---|
| 62 | } /* vcross */
|
---|
| 63 |
|
---|
| 64 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 65 | >-------------------------------</a><a name="determinant">-</a>
|
---|
| 66 |
|
---|
| 67 | qh_determinant( rows, dim, nearzero )
|
---|
| 68 | compute signed determinant of a square matrix
|
---|
| 69 | uses qh.NEARzero to test for degenerate matrices
|
---|
| 70 |
|
---|
| 71 | returns:
|
---|
| 72 | determinant
|
---|
| 73 | overwrites rows and the matrix
|
---|
| 74 | if dim == 2 or 3
|
---|
| 75 | nearzero iff determinant < qh NEARzero[dim-1]
|
---|
| 76 | (!quite correct, not critical)
|
---|
| 77 | if dim >= 4
|
---|
| 78 | nearzero iff diagonal[k] < qh NEARzero[k]
|
---|
| 79 | */
|
---|
| 80 | realT qh_determinant(realT **rows, int dim, boolT *nearzero) {
|
---|
| 81 | realT det=0;
|
---|
| 82 | int i;
|
---|
| 83 | boolT sign= False;
|
---|
| 84 |
|
---|
| 85 | *nearzero= False;
|
---|
| 86 | if (dim < 2) {
|
---|
| 87 | qh_fprintf(qh ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
|
---|
| 88 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 89 | }else if (dim == 2) {
|
---|
| 90 | det= det2_(rows[0][0], rows[0][1],
|
---|
| 91 | rows[1][0], rows[1][1]);
|
---|
| 92 | if (fabs_(det) < qh NEARzero[1]) /* not really correct, what should this be? */
|
---|
| 93 | *nearzero= True;
|
---|
| 94 | }else if (dim == 3) {
|
---|
| 95 | det= det3_(rows[0][0], rows[0][1], rows[0][2],
|
---|
| 96 | rows[1][0], rows[1][1], rows[1][2],
|
---|
| 97 | rows[2][0], rows[2][1], rows[2][2]);
|
---|
| 98 | if (fabs_(det) < qh NEARzero[2]) /* not really correct, what should this be? */
|
---|
| 99 | *nearzero= True;
|
---|
| 100 | }else {
|
---|
| 101 | qh_gausselim(rows, dim, dim, &sign, nearzero); /* if nearzero, diagonal still ok*/
|
---|
| 102 | det= 1.0;
|
---|
| 103 | for (i=dim; i--; )
|
---|
| 104 | det *= (rows[i])[i];
|
---|
| 105 | if (sign)
|
---|
| 106 | det= -det;
|
---|
| 107 | }
|
---|
| 108 | return det;
|
---|
| 109 | } /* determinant */
|
---|
| 110 |
|
---|
| 111 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 112 | >-------------------------------</a><a name="detjoggle">-</a>
|
---|
| 113 |
|
---|
| 114 | qh_detjoggle( points, numpoints, dimension )
|
---|
| 115 | determine default max joggle for point array
|
---|
| 116 | as qh_distround * qh_JOGGLEdefault
|
---|
| 117 |
|
---|
| 118 | returns:
|
---|
| 119 | initial value for JOGGLEmax from points and REALepsilon
|
---|
| 120 |
|
---|
| 121 | notes:
|
---|
| 122 | computes DISTround since qh_maxmin not called yet
|
---|
| 123 | if qh SCALElast, last dimension will be scaled later to MAXwidth
|
---|
| 124 |
|
---|
| 125 | loop duplicated from qh_maxmin
|
---|
| 126 | */
|
---|
| 127 | realT qh_detjoggle(pointT *points, int numpoints, int dimension) {
|
---|
| 128 | realT abscoord, distround, joggle, maxcoord, mincoord;
|
---|
| 129 | pointT *point, *pointtemp;
|
---|
| 130 | realT maxabs= -REALmax;
|
---|
| 131 | realT sumabs= 0;
|
---|
| 132 | realT maxwidth= 0;
|
---|
| 133 | int k;
|
---|
| 134 |
|
---|
| 135 | for (k=0; k < dimension; k++) {
|
---|
| 136 | if (qh SCALElast && k == dimension-1)
|
---|
| 137 | abscoord= maxwidth;
|
---|
| 138 | else if (qh DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */
|
---|
| 139 | abscoord= 2 * maxabs * maxabs; /* may be low by qh hull_dim/2 */
|
---|
| 140 | else {
|
---|
| 141 | maxcoord= -REALmax;
|
---|
| 142 | mincoord= REALmax;
|
---|
| 143 | FORALLpoint_(points, numpoints) {
|
---|
| 144 | maximize_(maxcoord, point[k]);
|
---|
| 145 | minimize_(mincoord, point[k]);
|
---|
| 146 | }
|
---|
| 147 | maximize_(maxwidth, maxcoord-mincoord);
|
---|
| 148 | abscoord= fmax_(maxcoord, -mincoord);
|
---|
| 149 | }
|
---|
| 150 | sumabs += abscoord;
|
---|
| 151 | maximize_(maxabs, abscoord);
|
---|
| 152 | } /* for k */
|
---|
| 153 | distround= qh_distround(qh hull_dim, maxabs, sumabs);
|
---|
| 154 | joggle= distround * qh_JOGGLEdefault;
|
---|
| 155 | maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
|
---|
| 156 | trace2((qh ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
|
---|
| 157 | return joggle;
|
---|
| 158 | } /* detjoggle */
|
---|
| 159 |
|
---|
| 160 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 161 | >-------------------------------</a><a name="detroundoff">-</a>
|
---|
| 162 |
|
---|
| 163 | qh_detroundoff()
|
---|
| 164 | determine maximum roundoff errors from
|
---|
| 165 | REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,
|
---|
| 166 | qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1
|
---|
| 167 |
|
---|
| 168 | accounts for qh.SETroundoff, qh.RANDOMdist, qh MERGEexact
|
---|
| 169 | qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,
|
---|
| 170 | qh.postmerge_centrum, qh.MINoutside,
|
---|
| 171 | qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar
|
---|
| 172 |
|
---|
| 173 | returns:
|
---|
| 174 | sets qh.DISTround, etc. (see below)
|
---|
| 175 | appends precision constants to qh.qhull_options
|
---|
| 176 |
|
---|
| 177 | see:
|
---|
| 178 | qh_maxmin() for qh.NEARzero
|
---|
| 179 |
|
---|
| 180 | design:
|
---|
| 181 | determine qh.DISTround for distance computations
|
---|
| 182 | determine minimum denominators for qh_divzero
|
---|
| 183 | determine qh.ANGLEround for angle computations
|
---|
| 184 | adjust qh.premerge_cos,... for roundoff error
|
---|
| 185 | determine qh.ONEmerge for maximum error due to a single merge
|
---|
| 186 | determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,
|
---|
| 187 | qh.MINoutside, qh.WIDEfacet
|
---|
| 188 | initialize qh.max_vertex and qh.minvertex
|
---|
| 189 | */
|
---|
| 190 | void qh_detroundoff(void) {
|
---|
| 191 |
|
---|
| 192 | qh_option("_max-width", NULL, &qh MAXwidth);
|
---|
| 193 | if (!qh SETroundoff) {
|
---|
| 194 | qh DISTround= qh_distround(qh hull_dim, qh MAXabs_coord, qh MAXsumcoord);
|
---|
| 195 | if (qh RANDOMdist)
|
---|
| 196 | qh DISTround += qh RANDOMfactor * qh MAXabs_coord;
|
---|
| 197 | qh_option("Error-roundoff", NULL, &qh DISTround);
|
---|
| 198 | }
|
---|
| 199 | qh MINdenom= qh MINdenom_1 * qh MAXabs_coord;
|
---|
| 200 | qh MINdenom_1_2= sqrt(qh MINdenom_1 * qh hull_dim) ; /* if will be normalized */
|
---|
| 201 | qh MINdenom_2= qh MINdenom_1_2 * qh MAXabs_coord;
|
---|
| 202 | /* for inner product */
|
---|
| 203 | qh ANGLEround= 1.01 * qh hull_dim * REALepsilon;
|
---|
| 204 | if (qh RANDOMdist)
|
---|
| 205 | qh ANGLEround += qh RANDOMfactor;
|
---|
| 206 | if (qh premerge_cos < REALmax/2) {
|
---|
| 207 | qh premerge_cos -= qh ANGLEround;
|
---|
| 208 | if (qh RANDOMdist)
|
---|
| 209 | qh_option("Angle-premerge-with-random", NULL, &qh premerge_cos);
|
---|
| 210 | }
|
---|
| 211 | if (qh postmerge_cos < REALmax/2) {
|
---|
| 212 | qh postmerge_cos -= qh ANGLEround;
|
---|
| 213 | if (qh RANDOMdist)
|
---|
| 214 | qh_option("Angle-postmerge-with-random", NULL, &qh postmerge_cos);
|
---|
| 215 | }
|
---|
| 216 | qh premerge_centrum += 2 * qh DISTround; /*2 for centrum and distplane()*/
|
---|
| 217 | qh postmerge_centrum += 2 * qh DISTround;
|
---|
| 218 | if (qh RANDOMdist && (qh MERGEexact || qh PREmerge))
|
---|
| 219 | qh_option("Centrum-premerge-with-random", NULL, &qh premerge_centrum);
|
---|
| 220 | if (qh RANDOMdist && qh POSTmerge)
|
---|
| 221 | qh_option("Centrum-postmerge-with-random", NULL, &qh postmerge_centrum);
|
---|
| 222 | { /* compute ONEmerge, max vertex offset for merging simplicial facets */
|
---|
| 223 | realT maxangle= 1.0, maxrho;
|
---|
| 224 |
|
---|
| 225 | minimize_(maxangle, qh premerge_cos);
|
---|
| 226 | minimize_(maxangle, qh postmerge_cos);
|
---|
| 227 | /* max diameter * sin theta + DISTround for vertex to its hyperplane */
|
---|
| 228 | qh ONEmerge= sqrt((realT)qh hull_dim) * qh MAXwidth *
|
---|
| 229 | sqrt(1.0 - maxangle * maxangle) + qh DISTround;
|
---|
| 230 | maxrho= qh hull_dim * qh premerge_centrum + qh DISTround;
|
---|
| 231 | maximize_(qh ONEmerge, maxrho);
|
---|
| 232 | maxrho= qh hull_dim * qh postmerge_centrum + qh DISTround;
|
---|
| 233 | maximize_(qh ONEmerge, maxrho);
|
---|
| 234 | if (qh MERGING)
|
---|
| 235 | qh_option("_one-merge", NULL, &qh ONEmerge);
|
---|
| 236 | }
|
---|
| 237 | qh NEARinside= qh ONEmerge * qh_RATIOnearinside; /* only used if qh KEEPnearinside */
|
---|
| 238 | if (qh JOGGLEmax < REALmax/2 && (qh KEEPcoplanar || qh KEEPinside)) {
|
---|
| 239 | realT maxdist; /* adjust qh.NEARinside for joggle */
|
---|
| 240 | qh KEEPnearinside= True;
|
---|
| 241 | maxdist= sqrt((realT)qh hull_dim) * qh JOGGLEmax + qh DISTround;
|
---|
| 242 | maxdist= 2*maxdist; /* vertex and coplanar point can joggle in opposite directions */
|
---|
| 243 | maximize_(qh NEARinside, maxdist); /* must agree with qh_nearcoplanar() */
|
---|
| 244 | }
|
---|
| 245 | if (qh KEEPnearinside)
|
---|
| 246 | qh_option("_near-inside", NULL, &qh NEARinside);
|
---|
| 247 | if (qh JOGGLEmax < qh DISTround) {
|
---|
| 248 | qh_fprintf(qh ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n",
|
---|
| 249 | qh JOGGLEmax, qh DISTround);
|
---|
| 250 | qh_errexit(qh_ERRinput, NULL, NULL);
|
---|
| 251 | }
|
---|
| 252 | if (qh MINvisible > REALmax/2) {
|
---|
| 253 | if (!qh MERGING)
|
---|
| 254 | qh MINvisible= qh DISTround;
|
---|
| 255 | else if (qh hull_dim <= 3)
|
---|
| 256 | qh MINvisible= qh premerge_centrum;
|
---|
| 257 | else
|
---|
| 258 | qh MINvisible= qh_COPLANARratio * qh premerge_centrum;
|
---|
| 259 | if (qh APPROXhull && qh MINvisible > qh MINoutside)
|
---|
| 260 | qh MINvisible= qh MINoutside;
|
---|
| 261 | qh_option("Visible-distance", NULL, &qh MINvisible);
|
---|
| 262 | }
|
---|
| 263 | if (qh MAXcoplanar > REALmax/2) {
|
---|
| 264 | qh MAXcoplanar= qh MINvisible;
|
---|
| 265 | qh_option("U-coplanar-distance", NULL, &qh MAXcoplanar);
|
---|
| 266 | }
|
---|
| 267 | if (!qh APPROXhull) { /* user may specify qh MINoutside */
|
---|
| 268 | qh MINoutside= 2 * qh MINvisible;
|
---|
| 269 | if (qh premerge_cos < REALmax/2)
|
---|
| 270 | maximize_(qh MINoutside, (1- qh premerge_cos) * qh MAXabs_coord);
|
---|
| 271 | qh_option("Width-outside", NULL, &qh MINoutside);
|
---|
| 272 | }
|
---|
| 273 | qh WIDEfacet= qh MINoutside;
|
---|
| 274 | maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MAXcoplanar);
|
---|
| 275 | maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MINvisible);
|
---|
| 276 | qh_option("_wide-facet", NULL, &qh WIDEfacet);
|
---|
| 277 | if (qh MINvisible > qh MINoutside + 3 * REALepsilon
|
---|
| 278 | && !qh BESToutside && !qh FORCEoutput)
|
---|
| 279 | qh_fprintf(qh ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g. Flipped facets are likely.\n",
|
---|
| 280 | qh MINvisible, qh MINoutside);
|
---|
| 281 | qh max_vertex= qh DISTround;
|
---|
| 282 | qh min_vertex= -qh DISTround;
|
---|
| 283 | /* numeric constants reported in printsummary */
|
---|
| 284 | } /* detroundoff */
|
---|
| 285 |
|
---|
| 286 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 287 | >-------------------------------</a><a name="detsimplex">-</a>
|
---|
| 288 |
|
---|
| 289 | qh_detsimplex( apex, points, dim, nearzero )
|
---|
| 290 | compute determinant of a simplex with point apex and base points
|
---|
| 291 |
|
---|
| 292 | returns:
|
---|
| 293 | signed determinant and nearzero from qh_determinant
|
---|
| 294 |
|
---|
| 295 | notes:
|
---|
| 296 | uses qh.gm_matrix/qh.gm_row (assumes they're big enough)
|
---|
| 297 |
|
---|
| 298 | design:
|
---|
| 299 | construct qm_matrix by subtracting apex from points
|
---|
| 300 | compute determinate
|
---|
| 301 | */
|
---|
| 302 | realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero) {
|
---|
| 303 | pointT *coorda, *coordp, *gmcoord, *point, **pointp;
|
---|
| 304 | coordT **rows;
|
---|
| 305 | int k, i=0;
|
---|
| 306 | realT det;
|
---|
| 307 |
|
---|
| 308 | zinc_(Zdetsimplex);
|
---|
| 309 | gmcoord= qh gm_matrix;
|
---|
| 310 | rows= qh gm_row;
|
---|
| 311 | FOREACHpoint_(points) {
|
---|
| 312 | if (i == dim)
|
---|
| 313 | break;
|
---|
| 314 | rows[i++]= gmcoord;
|
---|
| 315 | coordp= point;
|
---|
| 316 | coorda= apex;
|
---|
| 317 | for (k=dim; k--; )
|
---|
| 318 | *(gmcoord++)= *coordp++ - *coorda++;
|
---|
| 319 | }
|
---|
| 320 | if (i < dim) {
|
---|
| 321 | qh_fprintf(qh ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
|
---|
| 322 | i, dim);
|
---|
| 323 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 324 | }
|
---|
| 325 | det= qh_determinant(rows, dim, nearzero);
|
---|
| 326 | trace2((qh ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
|
---|
| 327 | det, qh_pointid(apex), dim, *nearzero));
|
---|
| 328 | return det;
|
---|
| 329 | } /* detsimplex */
|
---|
| 330 |
|
---|
| 331 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 332 | >-------------------------------</a><a name="distnorm">-</a>
|
---|
| 333 |
|
---|
| 334 | qh_distnorm( dim, point, normal, offset )
|
---|
| 335 | return distance from point to hyperplane at normal/offset
|
---|
| 336 |
|
---|
| 337 | returns:
|
---|
| 338 | dist
|
---|
| 339 |
|
---|
| 340 | notes:
|
---|
| 341 | dist > 0 if point is outside of hyperplane
|
---|
| 342 |
|
---|
| 343 | see:
|
---|
| 344 | qh_distplane in geom.c
|
---|
| 345 | */
|
---|
| 346 | realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {
|
---|
| 347 | coordT *normalp= normal, *coordp= point;
|
---|
| 348 | realT dist;
|
---|
| 349 | int k;
|
---|
| 350 |
|
---|
| 351 | dist= *offsetp;
|
---|
| 352 | for (k=dim; k--; )
|
---|
| 353 | dist += *(coordp++) * *(normalp++);
|
---|
| 354 | return dist;
|
---|
| 355 | } /* distnorm */
|
---|
| 356 |
|
---|
| 357 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 358 | >-------------------------------</a><a name="distround">-</a>
|
---|
| 359 |
|
---|
| 360 | qh_distround(dimension, maxabs, maxsumabs )
|
---|
| 361 | compute maximum round-off error for a distance computation
|
---|
| 362 | to a normalized hyperplane
|
---|
| 363 | maxabs is the maximum absolute value of a coordinate
|
---|
| 364 | maxsumabs is the maximum possible sum of absolute coordinate values
|
---|
| 365 |
|
---|
| 366 | returns:
|
---|
| 367 | max dist round for REALepsilon
|
---|
| 368 |
|
---|
| 369 | notes:
|
---|
| 370 | calculate roundoff error according to
|
---|
| 371 | Lemma 3.2-1 of Golub and van Loan "Matrix Computation"
|
---|
| 372 | use sqrt(dim) since one vector is normalized
|
---|
| 373 | or use maxsumabs since one vector is < 1
|
---|
| 374 | */
|
---|
| 375 | realT qh_distround(int dimension, realT maxabs, realT maxsumabs) {
|
---|
| 376 | realT maxdistsum, maxround;
|
---|
| 377 |
|
---|
| 378 | maxdistsum= sqrt((realT)dimension) * maxabs;
|
---|
| 379 | minimize_( maxdistsum, maxsumabs);
|
---|
| 380 | maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs);
|
---|
| 381 | /* adds maxabs for offset */
|
---|
| 382 | trace4((qh ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",
|
---|
| 383 | maxround, maxabs, maxsumabs, maxdistsum));
|
---|
| 384 | return maxround;
|
---|
| 385 | } /* distround */
|
---|
| 386 |
|
---|
| 387 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 388 | >-------------------------------</a><a name="divzero">-</a>
|
---|
| 389 |
|
---|
| 390 | qh_divzero( numer, denom, mindenom1, zerodiv )
|
---|
| 391 | divide by a number that's nearly zero
|
---|
| 392 | mindenom1= minimum denominator for dividing into 1.0
|
---|
| 393 |
|
---|
| 394 | returns:
|
---|
| 395 | quotient
|
---|
| 396 | sets zerodiv and returns 0.0 if it would overflow
|
---|
| 397 |
|
---|
| 398 | design:
|
---|
| 399 | if numer is nearly zero and abs(numer) < abs(denom)
|
---|
| 400 | return numer/denom
|
---|
| 401 | else if numer is nearly zero
|
---|
| 402 | return 0 and zerodiv
|
---|
| 403 | else if denom/numer non-zero
|
---|
| 404 | return numer/denom
|
---|
| 405 | else
|
---|
| 406 | return 0 and zerodiv
|
---|
| 407 | */
|
---|
| 408 | realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {
|
---|
| 409 | realT temp, numerx, denomx;
|
---|
| 410 |
|
---|
| 411 |
|
---|
| 412 | if (numer < mindenom1 && numer > -mindenom1) {
|
---|
| 413 | numerx= fabs_(numer);
|
---|
| 414 | denomx= fabs_(denom);
|
---|
| 415 | if (numerx < denomx) {
|
---|
| 416 | *zerodiv= False;
|
---|
| 417 | return numer/denom;
|
---|
| 418 | }else {
|
---|
| 419 | *zerodiv= True;
|
---|
| 420 | return 0.0;
|
---|
| 421 | }
|
---|
| 422 | }
|
---|
| 423 | temp= denom/numer;
|
---|
| 424 | if (temp > mindenom1 || temp < -mindenom1) {
|
---|
| 425 | *zerodiv= False;
|
---|
| 426 | return numer/denom;
|
---|
| 427 | }else {
|
---|
| 428 | *zerodiv= True;
|
---|
| 429 | return 0.0;
|
---|
| 430 | }
|
---|
| 431 | } /* divzero */
|
---|
| 432 |
|
---|
| 433 |
|
---|
| 434 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 435 | >-------------------------------</a><a name="facetarea">-</a>
|
---|
| 436 |
|
---|
| 437 | qh_facetarea( facet )
|
---|
| 438 | return area for a facet
|
---|
| 439 |
|
---|
| 440 | notes:
|
---|
| 441 | if non-simplicial,
|
---|
| 442 | uses centrum to triangulate facet and sums the projected areas.
|
---|
| 443 | if (qh DELAUNAY),
|
---|
| 444 | computes projected area instead for last coordinate
|
---|
| 445 | assumes facet->normal exists
|
---|
| 446 | projecting tricoplanar facets to the hyperplane does not appear to make a difference
|
---|
| 447 |
|
---|
| 448 | design:
|
---|
| 449 | if simplicial
|
---|
| 450 | compute area
|
---|
| 451 | else
|
---|
| 452 | for each ridge
|
---|
| 453 | compute area from centrum to ridge
|
---|
| 454 | negate area if upper Delaunay facet
|
---|
| 455 | */
|
---|
| 456 | realT qh_facetarea(facetT *facet) {
|
---|
| 457 | vertexT *apex;
|
---|
| 458 | pointT *centrum;
|
---|
| 459 | realT area= 0.0;
|
---|
| 460 | ridgeT *ridge, **ridgep;
|
---|
| 461 |
|
---|
| 462 | if (facet->simplicial) {
|
---|
| 463 | apex= SETfirstt_(facet->vertices, vertexT);
|
---|
| 464 | area= qh_facetarea_simplex(qh hull_dim, apex->point, facet->vertices,
|
---|
| 465 | apex, facet->toporient, facet->normal, &facet->offset);
|
---|
| 466 | }else {
|
---|
| 467 | if (qh CENTERtype == qh_AScentrum)
|
---|
| 468 | centrum= facet->center;
|
---|
| 469 | else
|
---|
| 470 | centrum= qh_getcentrum(facet);
|
---|
| 471 | FOREACHridge_(facet->ridges)
|
---|
| 472 | area += qh_facetarea_simplex(qh hull_dim, centrum, ridge->vertices,
|
---|
| 473 | NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset);
|
---|
| 474 | if (qh CENTERtype != qh_AScentrum)
|
---|
| 475 | qh_memfree(centrum, qh normal_size);
|
---|
| 476 | }
|
---|
| 477 | if (facet->upperdelaunay && qh DELAUNAY)
|
---|
| 478 | area= -area; /* the normal should be [0,...,1] */
|
---|
| 479 | trace4((qh ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
|
---|
| 480 | return area;
|
---|
| 481 | } /* facetarea */
|
---|
| 482 |
|
---|
| 483 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 484 | >-------------------------------</a><a name="facetarea_simplex">-</a>
|
---|
| 485 |
|
---|
| 486 | qh_facetarea_simplex( dim, apex, vertices, notvertex, toporient, normal, offset )
|
---|
| 487 | return area for a simplex defined by
|
---|
| 488 | an apex, a base of vertices, an orientation, and a unit normal
|
---|
| 489 | if simplicial or tricoplanar facet,
|
---|
| 490 | notvertex is defined and it is skipped in vertices
|
---|
| 491 |
|
---|
| 492 | returns:
|
---|
| 493 | computes area of simplex projected to plane [normal,offset]
|
---|
| 494 | returns 0 if vertex too far below plane (qh WIDEfacet)
|
---|
| 495 | vertex can't be apex of tricoplanar facet
|
---|
| 496 |
|
---|
| 497 | notes:
|
---|
| 498 | if (qh DELAUNAY),
|
---|
| 499 | computes projected area instead for last coordinate
|
---|
| 500 | uses qh gm_matrix/gm_row and qh hull_dim
|
---|
| 501 | helper function for qh_facetarea
|
---|
| 502 |
|
---|
| 503 | design:
|
---|
| 504 | if Notvertex
|
---|
| 505 | translate simplex to apex
|
---|
| 506 | else
|
---|
| 507 | project simplex to normal/offset
|
---|
| 508 | translate simplex to apex
|
---|
| 509 | if Delaunay
|
---|
| 510 | set last row/column to 0 with -1 on diagonal
|
---|
| 511 | else
|
---|
| 512 | set last row to Normal
|
---|
| 513 | compute determinate
|
---|
| 514 | scale and flip sign for area
|
---|
| 515 | */
|
---|
| 516 | realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices,
|
---|
| 517 | vertexT *notvertex, boolT toporient, coordT *normal, realT *offset) {
|
---|
| 518 | pointT *coorda, *coordp, *gmcoord;
|
---|
| 519 | coordT **rows, *normalp;
|
---|
| 520 | int k, i=0;
|
---|
| 521 | realT area, dist;
|
---|
| 522 | vertexT *vertex, **vertexp;
|
---|
| 523 | boolT nearzero;
|
---|
| 524 |
|
---|
| 525 | gmcoord= qh gm_matrix;
|
---|
| 526 | rows= qh gm_row;
|
---|
| 527 | FOREACHvertex_(vertices) {
|
---|
| 528 | if (vertex == notvertex)
|
---|
| 529 | continue;
|
---|
| 530 | rows[i++]= gmcoord;
|
---|
| 531 | coorda= apex;
|
---|
| 532 | coordp= vertex->point;
|
---|
| 533 | normalp= normal;
|
---|
| 534 | if (notvertex) {
|
---|
| 535 | for (k=dim; k--; )
|
---|
| 536 | *(gmcoord++)= *coordp++ - *coorda++;
|
---|
| 537 | }else {
|
---|
| 538 | dist= *offset;
|
---|
| 539 | for (k=dim; k--; )
|
---|
| 540 | dist += *coordp++ * *normalp++;
|
---|
| 541 | if (dist < -qh WIDEfacet) {
|
---|
| 542 | zinc_(Znoarea);
|
---|
| 543 | return 0.0;
|
---|
| 544 | }
|
---|
| 545 | coordp= vertex->point;
|
---|
| 546 | normalp= normal;
|
---|
| 547 | for (k=dim; k--; )
|
---|
| 548 | *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;
|
---|
| 549 | }
|
---|
| 550 | }
|
---|
| 551 | if (i != dim-1) {
|
---|
| 552 | qh_fprintf(qh ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
|
---|
| 553 | i, dim);
|
---|
| 554 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 555 | }
|
---|
| 556 | rows[i]= gmcoord;
|
---|
| 557 | if (qh DELAUNAY) {
|
---|
| 558 | for (i=0; i < dim-1; i++)
|
---|
| 559 | rows[i][dim-1]= 0.0;
|
---|
| 560 | for (k=dim; k--; )
|
---|
| 561 | *(gmcoord++)= 0.0;
|
---|
| 562 | rows[dim-1][dim-1]= -1.0;
|
---|
| 563 | }else {
|
---|
| 564 | normalp= normal;
|
---|
| 565 | for (k=dim; k--; )
|
---|
| 566 | *(gmcoord++)= *normalp++;
|
---|
| 567 | }
|
---|
| 568 | zinc_(Zdetsimplex);
|
---|
| 569 | area= qh_determinant(rows, dim, &nearzero);
|
---|
| 570 | if (toporient)
|
---|
| 571 | area= -area;
|
---|
| 572 | area *= qh AREAfactor;
|
---|
| 573 | trace4((qh ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
|
---|
| 574 | area, qh_pointid(apex), toporient, nearzero));
|
---|
| 575 | return area;
|
---|
| 576 | } /* facetarea_simplex */
|
---|
| 577 |
|
---|
| 578 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 579 | >-------------------------------</a><a name="facetcenter">-</a>
|
---|
| 580 |
|
---|
| 581 | qh_facetcenter( vertices )
|
---|
| 582 | return Voronoi center (Voronoi vertex) for a facet's vertices
|
---|
| 583 |
|
---|
| 584 | returns:
|
---|
| 585 | return temporary point equal to the center
|
---|
| 586 |
|
---|
| 587 | see:
|
---|
| 588 | qh_voronoi_center()
|
---|
| 589 | */
|
---|
| 590 | pointT *qh_facetcenter(setT *vertices) {
|
---|
| 591 | setT *points= qh_settemp(qh_setsize(vertices));
|
---|
| 592 | vertexT *vertex, **vertexp;
|
---|
| 593 | pointT *center;
|
---|
| 594 |
|
---|
| 595 | FOREACHvertex_(vertices)
|
---|
| 596 | qh_setappend(&points, vertex->point);
|
---|
| 597 | center= qh_voronoi_center(qh hull_dim-1, points);
|
---|
| 598 | qh_settempfree(&points);
|
---|
| 599 | return center;
|
---|
| 600 | } /* facetcenter */
|
---|
| 601 |
|
---|
| 602 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 603 | >-------------------------------</a><a name="findgooddist">-</a>
|
---|
| 604 |
|
---|
| 605 | qh_findgooddist( point, facetA, dist, facetlist )
|
---|
| 606 | find best good facet visible for point from facetA
|
---|
| 607 | assumes facetA is visible from point
|
---|
| 608 |
|
---|
| 609 | returns:
|
---|
| 610 | best facet, i.e., good facet that is furthest from point
|
---|
| 611 | distance to best facet
|
---|
| 612 | NULL if none
|
---|
| 613 |
|
---|
| 614 | moves good, visible facets (and some other visible facets)
|
---|
| 615 | to end of qh facet_list
|
---|
| 616 |
|
---|
| 617 | notes:
|
---|
| 618 | uses qh visit_id
|
---|
| 619 |
|
---|
| 620 | design:
|
---|
| 621 | initialize bestfacet if facetA is good
|
---|
| 622 | move facetA to end of facetlist
|
---|
| 623 | for each facet on facetlist
|
---|
| 624 | for each unvisited neighbor of facet
|
---|
| 625 | move visible neighbors to end of facetlist
|
---|
| 626 | update best good neighbor
|
---|
| 627 | if no good neighbors, update best facet
|
---|
| 628 | */
|
---|
| 629 | facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp,
|
---|
| 630 | facetT **facetlist) {
|
---|
| 631 | realT bestdist= -REALmax, dist;
|
---|
| 632 | facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;
|
---|
| 633 | boolT goodseen= False;
|
---|
| 634 |
|
---|
| 635 | if (facetA->good) {
|
---|
| 636 | zzinc_(Zcheckpart); /* calls from check_bestdist occur after print stats */
|
---|
| 637 | qh_distplane(point, facetA, &bestdist);
|
---|
| 638 | bestfacet= facetA;
|
---|
| 639 | goodseen= True;
|
---|
| 640 | }
|
---|
| 641 | qh_removefacet(facetA);
|
---|
| 642 | qh_appendfacet(facetA);
|
---|
| 643 | *facetlist= facetA;
|
---|
| 644 | facetA->visitid= ++qh visit_id;
|
---|
| 645 | FORALLfacet_(*facetlist) {
|
---|
| 646 | FOREACHneighbor_(facet) {
|
---|
| 647 | if (neighbor->visitid == qh visit_id)
|
---|
| 648 | continue;
|
---|
| 649 | neighbor->visitid= qh visit_id;
|
---|
| 650 | if (goodseen && !neighbor->good)
|
---|
| 651 | continue;
|
---|
| 652 | zzinc_(Zcheckpart);
|
---|
| 653 | qh_distplane(point, neighbor, &dist);
|
---|
| 654 | if (dist > 0) {
|
---|
| 655 | qh_removefacet(neighbor);
|
---|
| 656 | qh_appendfacet(neighbor);
|
---|
| 657 | if (neighbor->good) {
|
---|
| 658 | goodseen= True;
|
---|
| 659 | if (dist > bestdist) {
|
---|
| 660 | bestdist= dist;
|
---|
| 661 | bestfacet= neighbor;
|
---|
| 662 | }
|
---|
| 663 | }
|
---|
| 664 | }
|
---|
| 665 | }
|
---|
| 666 | }
|
---|
| 667 | if (bestfacet) {
|
---|
| 668 | *distp= bestdist;
|
---|
| 669 | trace2((qh ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
|
---|
| 670 | qh_pointid(point), bestdist, bestfacet->id));
|
---|
| 671 | return bestfacet;
|
---|
| 672 | }
|
---|
| 673 | trace4((qh ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
|
---|
| 674 | qh_pointid(point), facetA->id));
|
---|
| 675 | return NULL;
|
---|
| 676 | } /* findgooddist */
|
---|
| 677 |
|
---|
| 678 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 679 | >-------------------------------</a><a name="getarea">-</a>
|
---|
| 680 |
|
---|
| 681 | qh_getarea( facetlist )
|
---|
| 682 | set area of all facets in facetlist
|
---|
| 683 | collect statistics
|
---|
| 684 | nop if hasAreaVolume
|
---|
| 685 |
|
---|
| 686 | returns:
|
---|
| 687 | sets qh totarea/totvol to total area and volume of convex hull
|
---|
| 688 | for Delaunay triangulation, computes projected area of the lower or upper hull
|
---|
| 689 | ignores upper hull if qh ATinfinity
|
---|
| 690 |
|
---|
| 691 | notes:
|
---|
| 692 | could compute outer volume by expanding facet area by rays from interior
|
---|
| 693 | the following attempt at perpendicular projection underestimated badly:
|
---|
| 694 | qh.totoutvol += (-dist + facet->maxoutside + qh DISTround)
|
---|
| 695 | * area/ qh hull_dim;
|
---|
| 696 | design:
|
---|
| 697 | for each facet on facetlist
|
---|
| 698 | compute facet->area
|
---|
| 699 | update qh.totarea and qh.totvol
|
---|
| 700 | */
|
---|
| 701 | void qh_getarea(facetT *facetlist) {
|
---|
| 702 | realT area;
|
---|
| 703 | realT dist;
|
---|
| 704 | facetT *facet;
|
---|
| 705 |
|
---|
| 706 | if (qh hasAreaVolume)
|
---|
| 707 | return;
|
---|
| 708 | if (qh REPORTfreq)
|
---|
| 709 | qh_fprintf(qh ferr, 8020, "computing area of each facet and volume of the convex hull\n");
|
---|
| 710 | else
|
---|
| 711 | trace1((qh ferr, 1001, "qh_getarea: computing volume and area for each facet\n"));
|
---|
| 712 | qh totarea= qh totvol= 0.0;
|
---|
| 713 | FORALLfacet_(facetlist) {
|
---|
| 714 | if (!facet->normal)
|
---|
| 715 | continue;
|
---|
| 716 | if (facet->upperdelaunay && qh ATinfinity)
|
---|
| 717 | continue;
|
---|
| 718 | if (!facet->isarea) {
|
---|
| 719 | facet->f.area= qh_facetarea(facet);
|
---|
| 720 | facet->isarea= True;
|
---|
| 721 | }
|
---|
| 722 | area= facet->f.area;
|
---|
| 723 | if (qh DELAUNAY) {
|
---|
| 724 | if (facet->upperdelaunay == qh UPPERdelaunay)
|
---|
| 725 | qh totarea += area;
|
---|
| 726 | }else {
|
---|
| 727 | qh totarea += area;
|
---|
| 728 | qh_distplane(qh interior_point, facet, &dist);
|
---|
| 729 | qh totvol += -dist * area/ qh hull_dim;
|
---|
| 730 | }
|
---|
| 731 | if (qh PRINTstatistics) {
|
---|
| 732 | wadd_(Wareatot, area);
|
---|
| 733 | wmax_(Wareamax, area);
|
---|
| 734 | wmin_(Wareamin, area);
|
---|
| 735 | }
|
---|
| 736 | }
|
---|
| 737 | qh hasAreaVolume= True;
|
---|
| 738 | } /* getarea */
|
---|
| 739 |
|
---|
| 740 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 741 | >-------------------------------</a><a name="gram_schmidt">-</a>
|
---|
| 742 |
|
---|
| 743 | qh_gram_schmidt( dim, row )
|
---|
| 744 | implements Gram-Schmidt orthogonalization by rows
|
---|
| 745 |
|
---|
| 746 | returns:
|
---|
| 747 | false if zero norm
|
---|
| 748 | overwrites rows[dim][dim]
|
---|
| 749 |
|
---|
| 750 | notes:
|
---|
| 751 | see Golub & van Loan Algorithm 6.2-2
|
---|
| 752 | overflow due to small divisors not handled
|
---|
| 753 |
|
---|
| 754 | design:
|
---|
| 755 | for each row
|
---|
| 756 | compute norm for row
|
---|
| 757 | if non-zero, normalize row
|
---|
| 758 | for each remaining rowA
|
---|
| 759 | compute inner product of row and rowA
|
---|
| 760 | reduce rowA by row * inner product
|
---|
| 761 | */
|
---|
| 762 | boolT qh_gram_schmidt(int dim, realT **row) {
|
---|
| 763 | realT *rowi, *rowj, norm;
|
---|
| 764 | int i, j, k;
|
---|
| 765 |
|
---|
| 766 | for (i=0; i < dim; i++) {
|
---|
| 767 | rowi= row[i];
|
---|
| 768 | for (norm= 0.0, k= dim; k--; rowi++)
|
---|
| 769 | norm += *rowi * *rowi;
|
---|
| 770 | norm= sqrt(norm);
|
---|
| 771 | wmin_(Wmindenom, norm);
|
---|
| 772 | if (norm == 0.0) /* either 0 or overflow due to sqrt */
|
---|
| 773 | return False;
|
---|
| 774 | for (k=dim; k--; )
|
---|
| 775 | *(--rowi) /= norm;
|
---|
| 776 | for (j=i+1; j < dim; j++) {
|
---|
| 777 | rowj= row[j];
|
---|
| 778 | for (norm= 0.0, k=dim; k--; )
|
---|
| 779 | norm += *rowi++ * *rowj++;
|
---|
| 780 | for (k=dim; k--; )
|
---|
| 781 | *(--rowj) -= *(--rowi) * norm;
|
---|
| 782 | }
|
---|
| 783 | }
|
---|
| 784 | return True;
|
---|
| 785 | } /* gram_schmidt */
|
---|
| 786 |
|
---|
| 787 |
|
---|
| 788 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 789 | >-------------------------------</a><a name="inthresholds">-</a>
|
---|
| 790 |
|
---|
| 791 | qh_inthresholds( normal, angle )
|
---|
| 792 | return True if normal within qh.lower_/upper_threshold
|
---|
| 793 |
|
---|
| 794 | returns:
|
---|
| 795 | estimate of angle by summing of threshold diffs
|
---|
| 796 | angle may be NULL
|
---|
| 797 | smaller "angle" is better
|
---|
| 798 |
|
---|
| 799 | notes:
|
---|
| 800 | invalid if qh.SPLITthresholds
|
---|
| 801 |
|
---|
| 802 | see:
|
---|
| 803 | qh.lower_threshold in qh_initbuild()
|
---|
| 804 | qh_initthresholds()
|
---|
| 805 |
|
---|
| 806 | design:
|
---|
| 807 | for each dimension
|
---|
| 808 | test threshold
|
---|
| 809 | */
|
---|
| 810 | boolT qh_inthresholds(coordT *normal, realT *angle) {
|
---|
| 811 | boolT within= True;
|
---|
| 812 | int k;
|
---|
| 813 | realT threshold;
|
---|
| 814 |
|
---|
| 815 | if (angle)
|
---|
| 816 | *angle= 0.0;
|
---|
| 817 | for (k=0; k < qh hull_dim; k++) {
|
---|
| 818 | threshold= qh lower_threshold[k];
|
---|
| 819 | if (threshold > -REALmax/2) {
|
---|
| 820 | if (normal[k] < threshold)
|
---|
| 821 | within= False;
|
---|
| 822 | if (angle) {
|
---|
| 823 | threshold -= normal[k];
|
---|
| 824 | *angle += fabs_(threshold);
|
---|
| 825 | }
|
---|
| 826 | }
|
---|
| 827 | if (qh upper_threshold[k] < REALmax/2) {
|
---|
| 828 | threshold= qh upper_threshold[k];
|
---|
| 829 | if (normal[k] > threshold)
|
---|
| 830 | within= False;
|
---|
| 831 | if (angle) {
|
---|
| 832 | threshold -= normal[k];
|
---|
| 833 | *angle += fabs_(threshold);
|
---|
| 834 | }
|
---|
| 835 | }
|
---|
| 836 | }
|
---|
| 837 | return within;
|
---|
| 838 | } /* inthresholds */
|
---|
| 839 |
|
---|
| 840 |
|
---|
| 841 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 842 | >-------------------------------</a><a name="joggleinput">-</a>
|
---|
| 843 |
|
---|
| 844 | qh_joggleinput()
|
---|
| 845 | randomly joggle input to Qhull by qh.JOGGLEmax
|
---|
| 846 | initial input is qh.first_point/qh.num_points of qh.hull_dim
|
---|
| 847 | repeated calls use qh.input_points/qh.num_points
|
---|
| 848 |
|
---|
| 849 | returns:
|
---|
| 850 | joggles points at qh.first_point/qh.num_points
|
---|
| 851 | copies data to qh.input_points/qh.input_malloc if first time
|
---|
| 852 | determines qh.JOGGLEmax if it was zero
|
---|
| 853 | if qh.DELAUNAY
|
---|
| 854 | computes the Delaunay projection of the joggled points
|
---|
| 855 |
|
---|
| 856 | notes:
|
---|
| 857 | if qh.DELAUNAY, unnecessarily joggles the last coordinate
|
---|
| 858 | the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease
|
---|
| 859 |
|
---|
| 860 | design:
|
---|
| 861 | if qh.DELAUNAY
|
---|
| 862 | set qh.SCALElast for reduced precision errors
|
---|
| 863 | if first call
|
---|
| 864 | initialize qh.input_points to the original input points
|
---|
| 865 | if qh.JOGGLEmax == 0
|
---|
| 866 | determine default qh.JOGGLEmax
|
---|
| 867 | else
|
---|
| 868 | increase qh.JOGGLEmax according to qh.build_cnt
|
---|
| 869 | joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]
|
---|
| 870 | if qh.DELAUNAY
|
---|
| 871 | sets the Delaunay projection
|
---|
| 872 | */
|
---|
| 873 | void qh_joggleinput(void) {
|
---|
| 874 | int i, seed, size;
|
---|
| 875 | coordT *coordp, *inputp;
|
---|
| 876 | realT randr, randa, randb;
|
---|
| 877 |
|
---|
| 878 | if (!qh input_points) { /* first call */
|
---|
| 879 | qh input_points= qh first_point;
|
---|
| 880 | qh input_malloc= qh POINTSmalloc;
|
---|
| 881 | size= qh num_points * qh hull_dim * sizeof(coordT);
|
---|
| 882 | if (!(qh first_point=(coordT*)qh_malloc((size_t)size))) {
|
---|
| 883 | qh_fprintf(qh ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",
|
---|
| 884 | qh num_points);
|
---|
| 885 | qh_errexit(qh_ERRmem, NULL, NULL);
|
---|
| 886 | }
|
---|
| 887 | qh POINTSmalloc= True;
|
---|
| 888 | if (qh JOGGLEmax == 0.0) {
|
---|
| 889 | qh JOGGLEmax= qh_detjoggle(qh input_points, qh num_points, qh hull_dim);
|
---|
| 890 | qh_option("QJoggle", NULL, &qh JOGGLEmax);
|
---|
| 891 | }
|
---|
| 892 | }else { /* repeated call */
|
---|
| 893 | if (!qh RERUN && qh build_cnt > qh_JOGGLEretry) {
|
---|
| 894 | if (((qh build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) {
|
---|
| 895 | realT maxjoggle= qh MAXwidth * qh_JOGGLEmaxincrease;
|
---|
| 896 | if (qh JOGGLEmax < maxjoggle) {
|
---|
| 897 | qh JOGGLEmax *= qh_JOGGLEincrease;
|
---|
| 898 | minimize_(qh JOGGLEmax, maxjoggle);
|
---|
| 899 | }
|
---|
| 900 | }
|
---|
| 901 | }
|
---|
| 902 | qh_option("QJoggle", NULL, &qh JOGGLEmax);
|
---|
| 903 | }
|
---|
| 904 | if (qh build_cnt > 1 && qh JOGGLEmax > fmax_(qh MAXwidth/4, 0.1)) {
|
---|
| 905 | qh_fprintf(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",
|
---|
| 906 | qh JOGGLEmax);
|
---|
| 907 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 908 | }
|
---|
| 909 | /* for some reason, using qh ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */
|
---|
| 910 | seed= qh_RANDOMint;
|
---|
| 911 | qh_option("_joggle-seed", &seed, NULL);
|
---|
| 912 | trace0((qh ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n",
|
---|
| 913 | qh JOGGLEmax, seed));
|
---|
| 914 | inputp= qh input_points;
|
---|
| 915 | coordp= qh first_point;
|
---|
| 916 | randa= 2.0 * qh JOGGLEmax/qh_RANDOMmax;
|
---|
| 917 | randb= -qh JOGGLEmax;
|
---|
| 918 | size= qh num_points * qh hull_dim;
|
---|
| 919 | for (i=size; i--; ) {
|
---|
| 920 | randr= qh_RANDOMint;
|
---|
| 921 | *(coordp++)= *(inputp++) + (randr * randa + randb);
|
---|
| 922 | }
|
---|
| 923 | if (qh DELAUNAY) {
|
---|
| 924 | qh last_low= qh last_high= qh last_newhigh= REALmax;
|
---|
| 925 | qh_setdelaunay(qh hull_dim, qh num_points, qh first_point);
|
---|
| 926 | }
|
---|
| 927 | } /* joggleinput */
|
---|
| 928 |
|
---|
| 929 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 930 | >-------------------------------</a><a name="maxabsval">-</a>
|
---|
| 931 |
|
---|
| 932 | qh_maxabsval( normal, dim )
|
---|
| 933 | return pointer to maximum absolute value of a dim vector
|
---|
| 934 | returns NULL if dim=0
|
---|
| 935 | */
|
---|
| 936 | realT *qh_maxabsval(realT *normal, int dim) {
|
---|
| 937 | realT maxval= -REALmax;
|
---|
| 938 | realT *maxp= NULL, *colp, absval;
|
---|
| 939 | int k;
|
---|
| 940 |
|
---|
| 941 | for (k=dim, colp= normal; k--; colp++) {
|
---|
| 942 | absval= fabs_(*colp);
|
---|
| 943 | if (absval > maxval) {
|
---|
| 944 | maxval= absval;
|
---|
| 945 | maxp= colp;
|
---|
| 946 | }
|
---|
| 947 | }
|
---|
| 948 | return maxp;
|
---|
| 949 | } /* maxabsval */
|
---|
| 950 |
|
---|
| 951 |
|
---|
| 952 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 953 | >-------------------------------</a><a name="maxmin">-</a>
|
---|
| 954 |
|
---|
| 955 | qh_maxmin( points, numpoints, dimension )
|
---|
| 956 | return max/min points for each dimension
|
---|
| 957 | determine max and min coordinates
|
---|
| 958 |
|
---|
| 959 | returns:
|
---|
| 960 | returns a temporary set of max and min points
|
---|
| 961 | may include duplicate points. Does not include qh.GOODpoint
|
---|
| 962 | sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth
|
---|
| 963 | qh.MAXlastcoord, qh.MINlastcoord
|
---|
| 964 | initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok
|
---|
| 965 |
|
---|
| 966 | notes:
|
---|
| 967 | loop duplicated in qh_detjoggle()
|
---|
| 968 |
|
---|
| 969 | design:
|
---|
| 970 | initialize global precision variables
|
---|
| 971 | checks definition of REAL...
|
---|
| 972 | for each dimension
|
---|
| 973 | for each point
|
---|
| 974 | collect maximum and minimum point
|
---|
| 975 | collect maximum of maximums and minimum of minimums
|
---|
| 976 | determine qh.NEARzero for Gaussian Elimination
|
---|
| 977 | */
|
---|
| 978 | setT *qh_maxmin(pointT *points, int numpoints, int dimension) {
|
---|
| 979 | int k;
|
---|
| 980 | realT maxcoord, temp;
|
---|
| 981 | pointT *minimum, *maximum, *point, *pointtemp;
|
---|
| 982 | setT *set;
|
---|
| 983 |
|
---|
| 984 | qh max_outside= 0.0;
|
---|
| 985 | qh MAXabs_coord= 0.0;
|
---|
| 986 | qh MAXwidth= -REALmax;
|
---|
| 987 | qh MAXsumcoord= 0.0;
|
---|
| 988 | qh min_vertex= 0.0;
|
---|
| 989 | qh WAScoplanar= False;
|
---|
| 990 | if (qh ZEROcentrum)
|
---|
| 991 | qh ZEROall_ok= True;
|
---|
| 992 | if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax
|
---|
| 993 | && REALmax > 0.0 && -REALmax < 0.0)
|
---|
| 994 | ; /* all ok */
|
---|
| 995 | else {
|
---|
| 996 | qh_fprintf(qh ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\
|
---|
| 997 | REALepsilon %g REALmin %g REALmax %g -REALmax %g\n",
|
---|
| 998 | REALepsilon, REALmin, REALmax, -REALmax);
|
---|
| 999 | qh_errexit(qh_ERRinput, NULL, NULL);
|
---|
| 1000 | }
|
---|
| 1001 | set= qh_settemp(2*dimension);
|
---|
| 1002 | for (k=0; k < dimension; k++) {
|
---|
| 1003 | if (points == qh GOODpointp)
|
---|
| 1004 | minimum= maximum= points + dimension;
|
---|
| 1005 | else
|
---|
| 1006 | minimum= maximum= points;
|
---|
| 1007 | FORALLpoint_(points, numpoints) {
|
---|
| 1008 | if (point == qh GOODpointp)
|
---|
| 1009 | continue;
|
---|
| 1010 | if (maximum[k] < point[k])
|
---|
| 1011 | maximum= point;
|
---|
| 1012 | else if (minimum[k] > point[k])
|
---|
| 1013 | minimum= point;
|
---|
| 1014 | }
|
---|
| 1015 | if (k == dimension-1) {
|
---|
| 1016 | qh MINlastcoord= minimum[k];
|
---|
| 1017 | qh MAXlastcoord= maximum[k];
|
---|
| 1018 | }
|
---|
| 1019 | if (qh SCALElast && k == dimension-1)
|
---|
| 1020 | maxcoord= qh MAXwidth;
|
---|
| 1021 | else {
|
---|
| 1022 | maxcoord= fmax_(maximum[k], -minimum[k]);
|
---|
| 1023 | if (qh GOODpointp) {
|
---|
| 1024 | temp= fmax_(qh GOODpointp[k], -qh GOODpointp[k]);
|
---|
| 1025 | maximize_(maxcoord, temp);
|
---|
| 1026 | }
|
---|
| 1027 | temp= maximum[k] - minimum[k];
|
---|
| 1028 | maximize_(qh MAXwidth, temp);
|
---|
| 1029 | }
|
---|
| 1030 | maximize_(qh MAXabs_coord, maxcoord);
|
---|
| 1031 | qh MAXsumcoord += maxcoord;
|
---|
| 1032 | qh_setappend(&set, maximum);
|
---|
| 1033 | qh_setappend(&set, minimum);
|
---|
| 1034 | /* calculation of qh NEARzero is based on error formula 4.4-13 of
|
---|
| 1035 | Golub & van Loan, authors say n^3 can be ignored and 10 be used in
|
---|
| 1036 | place of rho */
|
---|
| 1037 | qh NEARzero[k]= 80 * qh MAXsumcoord * REALepsilon;
|
---|
| 1038 | }
|
---|
| 1039 | if (qh IStracing >=1)
|
---|
| 1040 | qh_printpoints(qh ferr, "qh_maxmin: found the max and min points(by dim):", set);
|
---|
| 1041 | return(set);
|
---|
| 1042 | } /* maxmin */
|
---|
| 1043 |
|
---|
| 1044 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1045 | >-------------------------------</a><a name="maxouter">-</a>
|
---|
| 1046 |
|
---|
| 1047 | qh_maxouter()
|
---|
| 1048 | return maximum distance from facet to outer plane
|
---|
| 1049 | normally this is qh.max_outside+qh.DISTround
|
---|
| 1050 | does not include qh.JOGGLEmax
|
---|
| 1051 |
|
---|
| 1052 | see:
|
---|
| 1053 | qh_outerinner()
|
---|
| 1054 |
|
---|
| 1055 | notes:
|
---|
| 1056 | need to add another qh.DISTround if testing actual point with computation
|
---|
| 1057 |
|
---|
| 1058 | for joggle:
|
---|
| 1059 | qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex)
|
---|
| 1060 | need to use Wnewvertexmax since could have a coplanar point for a high
|
---|
| 1061 | facet that is replaced by a low facet
|
---|
| 1062 | need to add qh.JOGGLEmax if testing input points
|
---|
| 1063 | */
|
---|
| 1064 | realT qh_maxouter(void) {
|
---|
| 1065 | realT dist;
|
---|
| 1066 |
|
---|
| 1067 | dist= fmax_(qh max_outside, qh DISTround);
|
---|
| 1068 | dist += qh DISTround;
|
---|
| 1069 | trace4((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));
|
---|
| 1070 | return dist;
|
---|
| 1071 | } /* maxouter */
|
---|
| 1072 |
|
---|
| 1073 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1074 | >-------------------------------</a><a name="maxsimplex">-</a>
|
---|
| 1075 |
|
---|
| 1076 | qh_maxsimplex( dim, maxpoints, points, numpoints, simplex )
|
---|
| 1077 | determines maximum simplex for a set of points
|
---|
| 1078 | starts from points already in simplex
|
---|
| 1079 | skips qh.GOODpointp (assumes that it isn't in maxpoints)
|
---|
| 1080 |
|
---|
| 1081 | returns:
|
---|
| 1082 | simplex with dim+1 points
|
---|
| 1083 |
|
---|
| 1084 | notes:
|
---|
| 1085 | assumes at least pointsneeded points in points
|
---|
| 1086 | maximizes determinate for x,y,z,w, etc.
|
---|
| 1087 | uses maxpoints as long as determinate is clearly non-zero
|
---|
| 1088 |
|
---|
| 1089 | design:
|
---|
| 1090 | initialize simplex with at least two points
|
---|
| 1091 | (find points with max or min x coordinate)
|
---|
| 1092 | for each remaining dimension
|
---|
| 1093 | add point that maximizes the determinate
|
---|
| 1094 | (use points from maxpoints first)
|
---|
| 1095 | */
|
---|
| 1096 | void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) {
|
---|
| 1097 | pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL;
|
---|
| 1098 | boolT nearzero, maxnearzero= False;
|
---|
| 1099 | int k, sizinit;
|
---|
| 1100 | realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax;
|
---|
| 1101 |
|
---|
| 1102 | sizinit= qh_setsize(*simplex);
|
---|
| 1103 | if (sizinit < 2) {
|
---|
| 1104 | if (qh_setsize(maxpoints) >= 2) {
|
---|
| 1105 | FOREACHpoint_(maxpoints) {
|
---|
| 1106 | if (maxcoord < point[0]) {
|
---|
| 1107 | maxcoord= point[0];
|
---|
| 1108 | maxx= point;
|
---|
| 1109 | }
|
---|
| 1110 | if (mincoord > point[0]) {
|
---|
| 1111 | mincoord= point[0];
|
---|
| 1112 | minx= point;
|
---|
| 1113 | }
|
---|
| 1114 | }
|
---|
| 1115 | }else {
|
---|
| 1116 | FORALLpoint_(points, numpoints) {
|
---|
| 1117 | if (point == qh GOODpointp)
|
---|
| 1118 | continue;
|
---|
| 1119 | if (maxcoord < point[0]) {
|
---|
| 1120 | maxcoord= point[0];
|
---|
| 1121 | maxx= point;
|
---|
| 1122 | }
|
---|
| 1123 | if (mincoord > point[0]) {
|
---|
| 1124 | mincoord= point[0];
|
---|
| 1125 | minx= point;
|
---|
| 1126 | }
|
---|
| 1127 | }
|
---|
| 1128 | }
|
---|
| 1129 | qh_setunique(simplex, minx);
|
---|
| 1130 | if (qh_setsize(*simplex) < 2)
|
---|
| 1131 | qh_setunique(simplex, maxx);
|
---|
| 1132 | sizinit= qh_setsize(*simplex);
|
---|
| 1133 | if (sizinit < 2) {
|
---|
| 1134 | qh_precision("input has same x coordinate");
|
---|
| 1135 | if (zzval_(Zsetplane) > qh hull_dim+1) {
|
---|
| 1136 | qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",
|
---|
| 1137 | qh_setsize(maxpoints)+numpoints);
|
---|
| 1138 | qh_errexit(qh_ERRprec, NULL, NULL);
|
---|
| 1139 | }else {
|
---|
| 1140 | qh_fprintf(qh ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh hull_dim);
|
---|
| 1141 | qh_errexit(qh_ERRinput, NULL, NULL);
|
---|
| 1142 | }
|
---|
| 1143 | }
|
---|
| 1144 | }
|
---|
| 1145 | for (k=sizinit; k < dim+1; k++) {
|
---|
| 1146 | maxpoint= NULL;
|
---|
| 1147 | maxdet= -REALmax;
|
---|
| 1148 | FOREACHpoint_(maxpoints) {
|
---|
| 1149 | if (!qh_setin(*simplex, point)) {
|
---|
| 1150 | det= qh_detsimplex(point, *simplex, k, &nearzero);
|
---|
| 1151 | if ((det= fabs_(det)) > maxdet) {
|
---|
| 1152 | maxdet= det;
|
---|
| 1153 | maxpoint= point;
|
---|
| 1154 | maxnearzero= nearzero;
|
---|
| 1155 | }
|
---|
| 1156 | }
|
---|
| 1157 | }
|
---|
| 1158 | if (!maxpoint || maxnearzero) {
|
---|
| 1159 | zinc_(Zsearchpoints);
|
---|
| 1160 | if (!maxpoint) {
|
---|
| 1161 | trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));
|
---|
| 1162 | }else {
|
---|
| 1163 | trace0((qh ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n",
|
---|
| 1164 | k+1, qh_pointid(maxpoint), maxdet));
|
---|
| 1165 | }
|
---|
| 1166 | FORALLpoint_(points, numpoints) {
|
---|
| 1167 | if (point == qh GOODpointp)
|
---|
| 1168 | continue;
|
---|
| 1169 | if (!qh_setin(*simplex, point)) {
|
---|
| 1170 | det= qh_detsimplex(point, *simplex, k, &nearzero);
|
---|
| 1171 | if ((det= fabs_(det)) > maxdet) {
|
---|
| 1172 | maxdet= det;
|
---|
| 1173 | maxpoint= point;
|
---|
| 1174 | maxnearzero= nearzero;
|
---|
| 1175 | }
|
---|
| 1176 | }
|
---|
| 1177 | }
|
---|
| 1178 | } /* !maxpoint */
|
---|
| 1179 | if (!maxpoint) {
|
---|
| 1180 | qh_fprintf(qh ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");
|
---|
| 1181 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 1182 | }
|
---|
| 1183 | qh_setappend(simplex, maxpoint);
|
---|
| 1184 | trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",
|
---|
| 1185 | qh_pointid(maxpoint), k+1, maxdet));
|
---|
| 1186 | } /* k */
|
---|
| 1187 | } /* maxsimplex */
|
---|
| 1188 |
|
---|
| 1189 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1190 | >-------------------------------</a><a name="minabsval">-</a>
|
---|
| 1191 |
|
---|
| 1192 | qh_minabsval( normal, dim )
|
---|
| 1193 | return minimum absolute value of a dim vector
|
---|
| 1194 | */
|
---|
| 1195 | realT qh_minabsval(realT *normal, int dim) {
|
---|
| 1196 | realT minval= 0;
|
---|
| 1197 | realT maxval= 0;
|
---|
| 1198 | realT *colp;
|
---|
| 1199 | int k;
|
---|
| 1200 |
|
---|
| 1201 | for (k=dim, colp=normal; k--; colp++) {
|
---|
| 1202 | maximize_(maxval, *colp);
|
---|
| 1203 | minimize_(minval, *colp);
|
---|
| 1204 | }
|
---|
| 1205 | return fmax_(maxval, -minval);
|
---|
| 1206 | } /* minabsval */
|
---|
| 1207 |
|
---|
| 1208 |
|
---|
| 1209 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1210 | >-------------------------------</a><a name="mindiff">-</a>
|
---|
| 1211 |
|
---|
| 1212 | qh_mindif ( vecA, vecB, dim )
|
---|
| 1213 | return index of min abs. difference of two vectors
|
---|
| 1214 | */
|
---|
| 1215 | int qh_mindiff(realT *vecA, realT *vecB, int dim) {
|
---|
| 1216 | realT mindiff= REALmax, diff;
|
---|
| 1217 | realT *vecAp= vecA, *vecBp= vecB;
|
---|
| 1218 | int k, mink= 0;
|
---|
| 1219 |
|
---|
| 1220 | for (k=0; k < dim; k++) {
|
---|
| 1221 | diff= *vecAp++ - *vecBp++;
|
---|
| 1222 | diff= fabs_(diff);
|
---|
| 1223 | if (diff < mindiff) {
|
---|
| 1224 | mindiff= diff;
|
---|
| 1225 | mink= k;
|
---|
| 1226 | }
|
---|
| 1227 | }
|
---|
| 1228 | return mink;
|
---|
| 1229 | } /* mindiff */
|
---|
| 1230 |
|
---|
| 1231 |
|
---|
| 1232 |
|
---|
| 1233 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1234 | >-------------------------------</a><a name="orientoutside">-</a>
|
---|
| 1235 |
|
---|
| 1236 | qh_orientoutside( facet )
|
---|
| 1237 | make facet outside oriented via qh.interior_point
|
---|
| 1238 |
|
---|
| 1239 | returns:
|
---|
| 1240 | True if facet reversed orientation.
|
---|
| 1241 | */
|
---|
| 1242 | boolT qh_orientoutside(facetT *facet) {
|
---|
| 1243 | int k;
|
---|
| 1244 | realT dist;
|
---|
| 1245 |
|
---|
| 1246 | qh_distplane(qh interior_point, facet, &dist);
|
---|
| 1247 | if (dist > 0) {
|
---|
| 1248 | for (k=qh hull_dim; k--; )
|
---|
| 1249 | facet->normal[k]= -facet->normal[k];
|
---|
| 1250 | facet->offset= -facet->offset;
|
---|
| 1251 | return True;
|
---|
| 1252 | }
|
---|
| 1253 | return False;
|
---|
| 1254 | } /* orientoutside */
|
---|
| 1255 |
|
---|
| 1256 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1257 | >-------------------------------</a><a name="outerinner">-</a>
|
---|
| 1258 |
|
---|
| 1259 | qh_outerinner( facet, outerplane, innerplane )
|
---|
| 1260 | if facet and qh.maxoutdone (i.e., qh_check_maxout)
|
---|
| 1261 | returns outer and inner plane for facet
|
---|
| 1262 | else
|
---|
| 1263 | returns maximum outer and inner plane
|
---|
| 1264 | accounts for qh.JOGGLEmax
|
---|
| 1265 |
|
---|
| 1266 | see:
|
---|
| 1267 | qh_maxouter(), qh_check_bestdist(), qh_check_points()
|
---|
| 1268 |
|
---|
| 1269 | notes:
|
---|
| 1270 | outerplaner or innerplane may be NULL
|
---|
| 1271 | facet is const
|
---|
| 1272 | Does not error (QhullFacet)
|
---|
| 1273 |
|
---|
| 1274 | includes qh.DISTround for actual points
|
---|
| 1275 | adds another qh.DISTround if testing with floating point arithmetic
|
---|
| 1276 | */
|
---|
| 1277 | void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane) {
|
---|
| 1278 | realT dist, mindist;
|
---|
| 1279 | vertexT *vertex, **vertexp;
|
---|
| 1280 |
|
---|
| 1281 | if (outerplane) {
|
---|
| 1282 | if (!qh_MAXoutside || !facet || !qh maxoutdone) {
|
---|
| 1283 | *outerplane= qh_maxouter(); /* includes qh.DISTround */
|
---|
| 1284 | }else { /* qh_MAXoutside ... */
|
---|
| 1285 | #if qh_MAXoutside
|
---|
| 1286 | *outerplane= facet->maxoutside + qh DISTround;
|
---|
| 1287 | #endif
|
---|
| 1288 |
|
---|
| 1289 | }
|
---|
| 1290 | if (qh JOGGLEmax < REALmax/2)
|
---|
| 1291 | *outerplane += qh JOGGLEmax * sqrt((realT)qh hull_dim);
|
---|
| 1292 | }
|
---|
| 1293 | if (innerplane) {
|
---|
| 1294 | if (facet) {
|
---|
| 1295 | mindist= REALmax;
|
---|
| 1296 | FOREACHvertex_(facet->vertices) {
|
---|
| 1297 | zinc_(Zdistio);
|
---|
| 1298 | qh_distplane(vertex->point, facet, &dist);
|
---|
| 1299 | minimize_(mindist, dist);
|
---|
| 1300 | }
|
---|
| 1301 | *innerplane= mindist - qh DISTround;
|
---|
| 1302 | }else
|
---|
| 1303 | *innerplane= qh min_vertex - qh DISTround;
|
---|
| 1304 | if (qh JOGGLEmax < REALmax/2)
|
---|
| 1305 | *innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim);
|
---|
| 1306 | }
|
---|
| 1307 | } /* outerinner */
|
---|
| 1308 |
|
---|
| 1309 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1310 | >-------------------------------</a><a name="pointdist">-</a>
|
---|
| 1311 |
|
---|
| 1312 | qh_pointdist( point1, point2, dim )
|
---|
| 1313 | return distance between two points
|
---|
| 1314 |
|
---|
| 1315 | notes:
|
---|
| 1316 | returns distance squared if 'dim' is negative
|
---|
| 1317 | */
|
---|
| 1318 | coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {
|
---|
| 1319 | coordT dist, diff;
|
---|
| 1320 | int k;
|
---|
| 1321 |
|
---|
| 1322 | dist= 0.0;
|
---|
| 1323 | for (k= (dim > 0 ? dim : -dim); k--; ) {
|
---|
| 1324 | diff= *point1++ - *point2++;
|
---|
| 1325 | dist += diff * diff;
|
---|
| 1326 | }
|
---|
| 1327 | if (dim > 0)
|
---|
| 1328 | return(sqrt(dist));
|
---|
| 1329 | return dist;
|
---|
| 1330 | } /* pointdist */
|
---|
| 1331 |
|
---|
| 1332 |
|
---|
| 1333 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1334 | >-------------------------------</a><a name="printmatrix">-</a>
|
---|
| 1335 |
|
---|
| 1336 | qh_printmatrix( fp, string, rows, numrow, numcol )
|
---|
| 1337 | print matrix to fp given by row vectors
|
---|
| 1338 | print string as header
|
---|
| 1339 |
|
---|
| 1340 | notes:
|
---|
| 1341 | print a vector by qh_printmatrix(fp, "", &vect, 1, len)
|
---|
| 1342 | */
|
---|
| 1343 | void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
|
---|
| 1344 | realT *rowp;
|
---|
| 1345 | realT r; /*bug fix*/
|
---|
| 1346 | int i,k;
|
---|
| 1347 |
|
---|
| 1348 | qh_fprintf(fp, 9001, "%s\n", string);
|
---|
| 1349 | for (i=0; i < numrow; i++) {
|
---|
| 1350 | rowp= rows[i];
|
---|
| 1351 | for (k=0; k < numcol; k++) {
|
---|
| 1352 | r= *rowp++;
|
---|
| 1353 | qh_fprintf(fp, 9002, "%6.3g ", r);
|
---|
| 1354 | }
|
---|
| 1355 | qh_fprintf(fp, 9003, "\n");
|
---|
| 1356 | }
|
---|
| 1357 | } /* printmatrix */
|
---|
| 1358 |
|
---|
| 1359 |
|
---|
| 1360 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1361 | >-------------------------------</a><a name="printpoints">-</a>
|
---|
| 1362 |
|
---|
| 1363 | qh_printpoints( fp, string, points )
|
---|
| 1364 | print pointids to fp for a set of points
|
---|
| 1365 | if string, prints string and 'p' point ids
|
---|
| 1366 | */
|
---|
| 1367 | void qh_printpoints(FILE *fp, const char *string, setT *points) {
|
---|
| 1368 | pointT *point, **pointp;
|
---|
| 1369 |
|
---|
| 1370 | if (string) {
|
---|
| 1371 | qh_fprintf(fp, 9004, "%s", string);
|
---|
| 1372 | FOREACHpoint_(points)
|
---|
| 1373 | qh_fprintf(fp, 9005, " p%d", qh_pointid(point));
|
---|
| 1374 | qh_fprintf(fp, 9006, "\n");
|
---|
| 1375 | }else {
|
---|
| 1376 | FOREACHpoint_(points)
|
---|
| 1377 | qh_fprintf(fp, 9007, " %d", qh_pointid(point));
|
---|
| 1378 | qh_fprintf(fp, 9008, "\n");
|
---|
| 1379 | }
|
---|
| 1380 | } /* printpoints */
|
---|
| 1381 |
|
---|
| 1382 |
|
---|
| 1383 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1384 | >-------------------------------</a><a name="projectinput">-</a>
|
---|
| 1385 |
|
---|
| 1386 | qh_projectinput()
|
---|
| 1387 | project input points using qh.lower_bound/upper_bound and qh DELAUNAY
|
---|
| 1388 | if qh.lower_bound[k]=qh.upper_bound[k]= 0,
|
---|
| 1389 | removes dimension k
|
---|
| 1390 | if halfspace intersection
|
---|
| 1391 | removes dimension k from qh.feasible_point
|
---|
| 1392 | input points in qh first_point, num_points, input_dim
|
---|
| 1393 |
|
---|
| 1394 | returns:
|
---|
| 1395 | new point array in qh first_point of qh hull_dim coordinates
|
---|
| 1396 | sets qh POINTSmalloc
|
---|
| 1397 | if qh DELAUNAY
|
---|
| 1398 | projects points to paraboloid
|
---|
| 1399 | lowbound/highbound is also projected
|
---|
| 1400 | if qh ATinfinity
|
---|
| 1401 | adds point "at-infinity"
|
---|
| 1402 | if qh POINTSmalloc
|
---|
| 1403 | frees old point array
|
---|
| 1404 |
|
---|
| 1405 | notes:
|
---|
| 1406 | checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY
|
---|
| 1407 |
|
---|
| 1408 |
|
---|
| 1409 | design:
|
---|
| 1410 | sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)
|
---|
| 1411 | determines newdim and newnum for qh hull_dim and qh num_points
|
---|
| 1412 | projects points to newpoints
|
---|
| 1413 | projects qh.lower_bound to itself
|
---|
| 1414 | projects qh.upper_bound to itself
|
---|
| 1415 | if qh DELAUNAY
|
---|
| 1416 | if qh ATINFINITY
|
---|
| 1417 | projects points to paraboloid
|
---|
| 1418 | computes "infinity" point as vertex average and 10% above all points
|
---|
| 1419 | else
|
---|
| 1420 | uses qh_setdelaunay to project points to paraboloid
|
---|
| 1421 | */
|
---|
| 1422 | void qh_projectinput(void) {
|
---|
| 1423 | int k,i;
|
---|
| 1424 | int newdim= qh input_dim, newnum= qh num_points;
|
---|
| 1425 | signed char *project;
|
---|
| 1426 | int size= (qh input_dim+1)*sizeof(*project);
|
---|
| 1427 | pointT *newpoints, *coord, *infinity;
|
---|
| 1428 | realT paraboloid, maxboloid= 0;
|
---|
| 1429 |
|
---|
| 1430 | project= (signed char*)qh_memalloc(size);
|
---|
| 1431 | memset((char*)project, 0, (size_t)size);
|
---|
| 1432 | for (k=0; k < qh input_dim; k++) { /* skip Delaunay bound */
|
---|
| 1433 | if (qh lower_bound[k] == 0 && qh upper_bound[k] == 0) {
|
---|
| 1434 | project[k]= -1;
|
---|
| 1435 | newdim--;
|
---|
| 1436 | }
|
---|
| 1437 | }
|
---|
| 1438 | if (qh DELAUNAY) {
|
---|
| 1439 | project[k]= 1;
|
---|
| 1440 | newdim++;
|
---|
| 1441 | if (qh ATinfinity)
|
---|
| 1442 | newnum++;
|
---|
| 1443 | }
|
---|
| 1444 | if (newdim != qh hull_dim) {
|
---|
| 1445 | qh_fprintf(qh ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh hull_dim);
|
---|
| 1446 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 1447 | }
|
---|
| 1448 | if (!(newpoints=(coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){
|
---|
| 1449 | qh_fprintf(qh ferr, 6016, "qhull error: insufficient memory to project %d points\n",
|
---|
| 1450 | qh num_points);
|
---|
| 1451 | qh_errexit(qh_ERRmem, NULL, NULL);
|
---|
| 1452 | }
|
---|
| 1453 | qh_projectpoints(project, qh input_dim+1, qh first_point,
|
---|
| 1454 | qh num_points, qh input_dim, newpoints, newdim);
|
---|
| 1455 | trace1((qh ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
|
---|
| 1456 | qh_projectpoints(project, qh input_dim+1, qh lower_bound,
|
---|
| 1457 | 1, qh input_dim+1, qh lower_bound, newdim+1);
|
---|
| 1458 | qh_projectpoints(project, qh input_dim+1, qh upper_bound,
|
---|
| 1459 | 1, qh input_dim+1, qh upper_bound, newdim+1);
|
---|
| 1460 | if (qh HALFspace) {
|
---|
| 1461 | if (!qh feasible_point) {
|
---|
| 1462 | qh_fprintf(qh ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
|
---|
| 1463 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 1464 | }
|
---|
| 1465 | qh_projectpoints(project, qh input_dim, qh feasible_point,
|
---|
| 1466 | 1, qh input_dim, qh feasible_point, newdim);
|
---|
| 1467 | }
|
---|
| 1468 | qh_memfree(project, (qh input_dim+1)*sizeof(*project));
|
---|
| 1469 | if (qh POINTSmalloc)
|
---|
| 1470 | qh_free(qh first_point);
|
---|
| 1471 | qh first_point= newpoints;
|
---|
| 1472 | qh POINTSmalloc= True;
|
---|
| 1473 | if (qh DELAUNAY && qh ATinfinity) {
|
---|
| 1474 | coord= qh first_point;
|
---|
| 1475 | infinity= qh first_point + qh hull_dim * qh num_points;
|
---|
| 1476 | for (k=qh hull_dim-1; k--; )
|
---|
| 1477 | infinity[k]= 0.0;
|
---|
| 1478 | for (i=qh num_points; i--; ) {
|
---|
| 1479 | paraboloid= 0.0;
|
---|
| 1480 | for (k=0; k < qh hull_dim-1; k++) {
|
---|
| 1481 | paraboloid += *coord * *coord;
|
---|
| 1482 | infinity[k] += *coord;
|
---|
| 1483 | coord++;
|
---|
| 1484 | }
|
---|
| 1485 | *(coord++)= paraboloid;
|
---|
| 1486 | maximize_(maxboloid, paraboloid);
|
---|
| 1487 | }
|
---|
| 1488 | /* coord == infinity */
|
---|
| 1489 | for (k=qh hull_dim-1; k--; )
|
---|
| 1490 | *(coord++) /= qh num_points;
|
---|
| 1491 | *(coord++)= maxboloid * 1.1;
|
---|
| 1492 | qh num_points++;
|
---|
| 1493 | trace0((qh ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
|
---|
| 1494 | }else if (qh DELAUNAY) /* !qh ATinfinity */
|
---|
| 1495 | qh_setdelaunay( qh hull_dim, qh num_points, qh first_point);
|
---|
| 1496 | } /* projectinput */
|
---|
| 1497 |
|
---|
| 1498 |
|
---|
| 1499 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1500 | >-------------------------------</a><a name="projectpoints">-</a>
|
---|
| 1501 |
|
---|
| 1502 | qh_projectpoints( project, n, points, numpoints, dim, newpoints, newdim )
|
---|
| 1503 | project points/numpoints/dim to newpoints/newdim
|
---|
| 1504 | if project[k] == -1
|
---|
| 1505 | delete dimension k
|
---|
| 1506 | if project[k] == 1
|
---|
| 1507 | add dimension k by duplicating previous column
|
---|
| 1508 | n is size of project
|
---|
| 1509 |
|
---|
| 1510 | notes:
|
---|
| 1511 | newpoints may be points if only adding dimension at end
|
---|
| 1512 |
|
---|
| 1513 | design:
|
---|
| 1514 | check that 'project' and 'newdim' agree
|
---|
| 1515 | for each dimension
|
---|
| 1516 | if project == -1
|
---|
| 1517 | skip dimension
|
---|
| 1518 | else
|
---|
| 1519 | determine start of column in newpoints
|
---|
| 1520 | determine start of column in points
|
---|
| 1521 | if project == +1, duplicate previous column
|
---|
| 1522 | copy dimension (column) from points to newpoints
|
---|
| 1523 | */
|
---|
| 1524 | void qh_projectpoints(signed char *project, int n, realT *points,
|
---|
| 1525 | int numpoints, int dim, realT *newpoints, int newdim) {
|
---|
| 1526 | int testdim= dim, oldk=0, newk=0, i,j=0,k;
|
---|
| 1527 | realT *newp, *oldp;
|
---|
| 1528 |
|
---|
| 1529 | for (k=0; k < n; k++)
|
---|
| 1530 | testdim += project[k];
|
---|
| 1531 | if (testdim != newdim) {
|
---|
| 1532 | qh_fprintf(qh ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
|
---|
| 1533 | newdim, testdim);
|
---|
| 1534 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 1535 | }
|
---|
| 1536 | for (j=0; j<n; j++) {
|
---|
| 1537 | if (project[j] == -1)
|
---|
| 1538 | oldk++;
|
---|
| 1539 | else {
|
---|
| 1540 | newp= newpoints+newk++;
|
---|
| 1541 | if (project[j] == +1) {
|
---|
| 1542 | if (oldk >= dim)
|
---|
| 1543 | continue;
|
---|
| 1544 | oldp= points+oldk;
|
---|
| 1545 | }else
|
---|
| 1546 | oldp= points+oldk++;
|
---|
| 1547 | for (i=numpoints; i--; ) {
|
---|
| 1548 | *newp= *oldp;
|
---|
| 1549 | newp += newdim;
|
---|
| 1550 | oldp += dim;
|
---|
| 1551 | }
|
---|
| 1552 | }
|
---|
| 1553 | if (oldk >= dim)
|
---|
| 1554 | break;
|
---|
| 1555 | }
|
---|
| 1556 | trace1((qh ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",
|
---|
| 1557 | numpoints, dim, newdim));
|
---|
| 1558 | } /* projectpoints */
|
---|
| 1559 |
|
---|
| 1560 |
|
---|
| 1561 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1562 | >-------------------------------</a><a name="rotateinput">-</a>
|
---|
| 1563 |
|
---|
| 1564 | qh_rotateinput( rows )
|
---|
| 1565 | rotate input using row matrix
|
---|
| 1566 | input points given by qh first_point, num_points, hull_dim
|
---|
| 1567 | assumes rows[dim] is a scratch buffer
|
---|
| 1568 | if qh POINTSmalloc, overwrites input points, else mallocs a new array
|
---|
| 1569 |
|
---|
| 1570 | returns:
|
---|
| 1571 | rotated input
|
---|
| 1572 | sets qh POINTSmalloc
|
---|
| 1573 |
|
---|
| 1574 | design:
|
---|
| 1575 | see qh_rotatepoints
|
---|
| 1576 | */
|
---|
| 1577 | void qh_rotateinput(realT **rows) {
|
---|
| 1578 |
|
---|
| 1579 | if (!qh POINTSmalloc) {
|
---|
| 1580 | qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim);
|
---|
| 1581 | qh POINTSmalloc= True;
|
---|
| 1582 | }
|
---|
| 1583 | qh_rotatepoints(qh first_point, qh num_points, qh hull_dim, rows);
|
---|
| 1584 | } /* rotateinput */
|
---|
| 1585 |
|
---|
| 1586 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1587 | >-------------------------------</a><a name="rotatepoints">-</a>
|
---|
| 1588 |
|
---|
| 1589 | qh_rotatepoints( points, numpoints, dim, row )
|
---|
| 1590 | rotate numpoints points by a d-dim row matrix
|
---|
| 1591 | assumes rows[dim] is a scratch buffer
|
---|
| 1592 |
|
---|
| 1593 | returns:
|
---|
| 1594 | rotated points in place
|
---|
| 1595 |
|
---|
| 1596 | design:
|
---|
| 1597 | for each point
|
---|
| 1598 | for each coordinate
|
---|
| 1599 | use row[dim] to compute partial inner product
|
---|
| 1600 | for each coordinate
|
---|
| 1601 | rotate by partial inner product
|
---|
| 1602 | */
|
---|
| 1603 | void qh_rotatepoints(realT *points, int numpoints, int dim, realT **row) {
|
---|
| 1604 | realT *point, *rowi, *coord= NULL, sum, *newval;
|
---|
| 1605 | int i,j,k;
|
---|
| 1606 |
|
---|
| 1607 | if (qh IStracing >= 1)
|
---|
| 1608 | qh_printmatrix(qh ferr, "qh_rotatepoints: rotate points by", row, dim, dim);
|
---|
| 1609 | for (point= points, j= numpoints; j--; point += dim) {
|
---|
| 1610 | newval= row[dim];
|
---|
| 1611 | for (i=0; i < dim; i++) {
|
---|
| 1612 | rowi= row[i];
|
---|
| 1613 | coord= point;
|
---|
| 1614 | for (sum= 0.0, k= dim; k--; )
|
---|
| 1615 | sum += *rowi++ * *coord++;
|
---|
| 1616 | *(newval++)= sum;
|
---|
| 1617 | }
|
---|
| 1618 | for (k=dim; k--; )
|
---|
| 1619 | *(--coord)= *(--newval);
|
---|
| 1620 | }
|
---|
| 1621 | } /* rotatepoints */
|
---|
| 1622 |
|
---|
| 1623 |
|
---|
| 1624 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1625 | >-------------------------------</a><a name="scaleinput">-</a>
|
---|
| 1626 |
|
---|
| 1627 | qh_scaleinput()
|
---|
| 1628 | scale input points using qh low_bound/high_bound
|
---|
| 1629 | input points given by qh first_point, num_points, hull_dim
|
---|
| 1630 | if qh POINTSmalloc, overwrites input points, else mallocs a new array
|
---|
| 1631 |
|
---|
| 1632 | returns:
|
---|
| 1633 | scales coordinates of points to low_bound[k], high_bound[k]
|
---|
| 1634 | sets qh POINTSmalloc
|
---|
| 1635 |
|
---|
| 1636 | design:
|
---|
| 1637 | see qh_scalepoints
|
---|
| 1638 | */
|
---|
| 1639 | void qh_scaleinput(void) {
|
---|
| 1640 |
|
---|
| 1641 | if (!qh POINTSmalloc) {
|
---|
| 1642 | qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim);
|
---|
| 1643 | qh POINTSmalloc= True;
|
---|
| 1644 | }
|
---|
| 1645 | qh_scalepoints(qh first_point, qh num_points, qh hull_dim,
|
---|
| 1646 | qh lower_bound, qh upper_bound);
|
---|
| 1647 | } /* scaleinput */
|
---|
| 1648 |
|
---|
| 1649 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1650 | >-------------------------------</a><a name="scalelast">-</a>
|
---|
| 1651 |
|
---|
| 1652 | qh_scalelast( points, numpoints, dim, low, high, newhigh )
|
---|
| 1653 | scale last coordinate to [0,m] for Delaunay triangulations
|
---|
| 1654 | input points given by points, numpoints, dim
|
---|
| 1655 |
|
---|
| 1656 | returns:
|
---|
| 1657 | changes scale of last coordinate from [low, high] to [0, newhigh]
|
---|
| 1658 | overwrites last coordinate of each point
|
---|
| 1659 | saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay()
|
---|
| 1660 |
|
---|
| 1661 | notes:
|
---|
| 1662 | when called by qh_setdelaunay, low/high may not match actual data
|
---|
| 1663 |
|
---|
| 1664 | design:
|
---|
| 1665 | compute scale and shift factors
|
---|
| 1666 | apply to last coordinate of each point
|
---|
| 1667 | */
|
---|
| 1668 | void qh_scalelast(coordT *points, int numpoints, int dim, coordT low,
|
---|
| 1669 | coordT high, coordT newhigh) {
|
---|
| 1670 | realT scale, shift;
|
---|
| 1671 | coordT *coord;
|
---|
| 1672 | int i;
|
---|
| 1673 | boolT nearzero= False;
|
---|
| 1674 |
|
---|
| 1675 | trace4((qh ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n",
|
---|
| 1676 | low, high, newhigh));
|
---|
| 1677 | qh last_low= low;
|
---|
| 1678 | qh last_high= high;
|
---|
| 1679 | qh last_newhigh= newhigh;
|
---|
| 1680 | scale= qh_divzero(newhigh, high - low,
|
---|
| 1681 | qh MINdenom_1, &nearzero);
|
---|
| 1682 | if (nearzero) {
|
---|
| 1683 | if (qh DELAUNAY)
|
---|
| 1684 | qh_fprintf(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");
|
---|
| 1685 | else
|
---|
| 1686 | qh_fprintf(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",
|
---|
| 1687 | newhigh, low, high, high-low);
|
---|
| 1688 | qh_errexit(qh_ERRinput, NULL, NULL);
|
---|
| 1689 | }
|
---|
| 1690 | shift= - low * newhigh / (high-low);
|
---|
| 1691 | coord= points + dim - 1;
|
---|
| 1692 | for (i=numpoints; i--; coord += dim)
|
---|
| 1693 | *coord= *coord * scale + shift;
|
---|
| 1694 | } /* scalelast */
|
---|
| 1695 |
|
---|
| 1696 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1697 | >-------------------------------</a><a name="scalepoints">-</a>
|
---|
| 1698 |
|
---|
| 1699 | qh_scalepoints( points, numpoints, dim, newlows, newhighs )
|
---|
| 1700 | scale points to new lowbound and highbound
|
---|
| 1701 | retains old bound when newlow= -REALmax or newhigh= +REALmax
|
---|
| 1702 |
|
---|
| 1703 | returns:
|
---|
| 1704 | scaled points
|
---|
| 1705 | overwrites old points
|
---|
| 1706 |
|
---|
| 1707 | design:
|
---|
| 1708 | for each coordinate
|
---|
| 1709 | compute current low and high bound
|
---|
| 1710 | compute scale and shift factors
|
---|
| 1711 | scale all points
|
---|
| 1712 | enforce new low and high bound for all points
|
---|
| 1713 | */
|
---|
| 1714 | void qh_scalepoints(pointT *points, int numpoints, int dim,
|
---|
| 1715 | realT *newlows, realT *newhighs) {
|
---|
| 1716 | int i,k;
|
---|
| 1717 | realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord;
|
---|
| 1718 | boolT nearzero= False;
|
---|
| 1719 |
|
---|
| 1720 | for (k=0; k < dim; k++) {
|
---|
| 1721 | newhigh= newhighs[k];
|
---|
| 1722 | newlow= newlows[k];
|
---|
| 1723 | if (newhigh > REALmax/2 && newlow < -REALmax/2)
|
---|
| 1724 | continue;
|
---|
| 1725 | low= REALmax;
|
---|
| 1726 | high= -REALmax;
|
---|
| 1727 | for (i=numpoints, coord=points+k; i--; coord += dim) {
|
---|
| 1728 | minimize_(low, *coord);
|
---|
| 1729 | maximize_(high, *coord);
|
---|
| 1730 | }
|
---|
| 1731 | if (newhigh > REALmax/2)
|
---|
| 1732 | newhigh= high;
|
---|
| 1733 | if (newlow < -REALmax/2)
|
---|
| 1734 | newlow= low;
|
---|
| 1735 | if (qh DELAUNAY && k == dim-1 && newhigh < newlow) {
|
---|
| 1736 | qh_fprintf(qh ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n",
|
---|
| 1737 | k, k, newhigh, newlow);
|
---|
| 1738 | qh_errexit(qh_ERRinput, NULL, NULL);
|
---|
| 1739 | }
|
---|
| 1740 | scale= qh_divzero(newhigh - newlow, high - low,
|
---|
| 1741 | qh MINdenom_1, &nearzero);
|
---|
| 1742 | if (nearzero) {
|
---|
| 1743 | qh_fprintf(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",
|
---|
| 1744 | k, newlow, newhigh, low, high);
|
---|
| 1745 | qh_errexit(qh_ERRinput, NULL, NULL);
|
---|
| 1746 | }
|
---|
| 1747 | shift= (newlow * high - low * newhigh)/(high-low);
|
---|
| 1748 | coord= points+k;
|
---|
| 1749 | for (i=numpoints; i--; coord += dim)
|
---|
| 1750 | *coord= *coord * scale + shift;
|
---|
| 1751 | coord= points+k;
|
---|
| 1752 | if (newlow < newhigh) {
|
---|
| 1753 | mincoord= newlow;
|
---|
| 1754 | maxcoord= newhigh;
|
---|
| 1755 | }else {
|
---|
| 1756 | mincoord= newhigh;
|
---|
| 1757 | maxcoord= newlow;
|
---|
| 1758 | }
|
---|
| 1759 | for (i=numpoints; i--; coord += dim) {
|
---|
| 1760 | minimize_(*coord, maxcoord); /* because of roundoff error */
|
---|
| 1761 | maximize_(*coord, mincoord);
|
---|
| 1762 | }
|
---|
| 1763 | trace0((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",
|
---|
| 1764 | k, low, high, newlow, newhigh, numpoints, scale, shift));
|
---|
| 1765 | }
|
---|
| 1766 | } /* scalepoints */
|
---|
| 1767 |
|
---|
| 1768 |
|
---|
| 1769 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1770 | >-------------------------------</a><a name="setdelaunay">-</a>
|
---|
| 1771 |
|
---|
| 1772 | qh_setdelaunay( dim, count, points )
|
---|
| 1773 | project count points to dim-d paraboloid for Delaunay triangulation
|
---|
| 1774 |
|
---|
| 1775 | dim is one more than the dimension of the input set
|
---|
| 1776 | assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation)
|
---|
| 1777 |
|
---|
| 1778 | points is a dim*count realT array. The first dim-1 coordinates
|
---|
| 1779 | are the coordinates of the first input point. array[dim] is
|
---|
| 1780 | the first coordinate of the second input point. array[2*dim] is
|
---|
| 1781 | the first coordinate of the third input point.
|
---|
| 1782 |
|
---|
| 1783 | if qh.last_low defined (i.e., 'Qbb' called qh_scalelast)
|
---|
| 1784 | calls qh_scalelast to scale the last coordinate the same as the other points
|
---|
| 1785 |
|
---|
| 1786 | returns:
|
---|
| 1787 | for each point
|
---|
| 1788 | sets point[dim-1] to sum of squares of coordinates
|
---|
| 1789 | scale points to 'Qbb' if needed
|
---|
| 1790 |
|
---|
| 1791 | notes:
|
---|
| 1792 | to project one point, use
|
---|
| 1793 | qh_setdelaunay(qh hull_dim, 1, point)
|
---|
| 1794 |
|
---|
| 1795 | Do not use options 'Qbk', 'QBk', or 'QbB' since they scale
|
---|
| 1796 | the coordinates after the original projection.
|
---|
| 1797 |
|
---|
| 1798 | */
|
---|
| 1799 | void qh_setdelaunay(int dim, int count, pointT *points) {
|
---|
| 1800 | int i, k;
|
---|
| 1801 | coordT *coordp, coord;
|
---|
| 1802 | realT paraboloid;
|
---|
| 1803 |
|
---|
| 1804 | trace0((qh ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count));
|
---|
| 1805 | coordp= points;
|
---|
| 1806 | for (i=0; i < count; i++) {
|
---|
| 1807 | coord= *coordp++;
|
---|
| 1808 | paraboloid= coord*coord;
|
---|
| 1809 | for (k=dim-2; k--; ) {
|
---|
| 1810 | coord= *coordp++;
|
---|
| 1811 | paraboloid += coord*coord;
|
---|
| 1812 | }
|
---|
| 1813 | *coordp++ = paraboloid;
|
---|
| 1814 | }
|
---|
| 1815 | if (qh last_low < REALmax/2)
|
---|
| 1816 | qh_scalelast(points, count, dim, qh last_low, qh last_high, qh last_newhigh);
|
---|
| 1817 | } /* setdelaunay */
|
---|
| 1818 |
|
---|
| 1819 |
|
---|
| 1820 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1821 | >-------------------------------</a><a name="sethalfspace">-</a>
|
---|
| 1822 |
|
---|
| 1823 | qh_sethalfspace( dim, coords, nextp, normal, offset, feasible )
|
---|
| 1824 | set point to dual of halfspace relative to feasible point
|
---|
| 1825 | halfspace is normal coefficients and offset.
|
---|
| 1826 |
|
---|
| 1827 | returns:
|
---|
| 1828 | false if feasible point is outside of hull (error message already reported)
|
---|
| 1829 | overwrites coordinates for point at dim coords
|
---|
| 1830 | nextp= next point (coords)
|
---|
| 1831 |
|
---|
| 1832 | design:
|
---|
| 1833 | compute distance from feasible point to halfspace
|
---|
| 1834 | divide each normal coefficient by -dist
|
---|
| 1835 | */
|
---|
| 1836 | boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp,
|
---|
| 1837 | coordT *normal, coordT *offset, coordT *feasible) {
|
---|
| 1838 | coordT *normp= normal, *feasiblep= feasible, *coordp= coords;
|
---|
| 1839 | realT dist;
|
---|
| 1840 | realT r; /*bug fix*/
|
---|
| 1841 | int k;
|
---|
| 1842 | boolT zerodiv;
|
---|
| 1843 |
|
---|
| 1844 | dist= *offset;
|
---|
| 1845 | for (k=dim; k--; )
|
---|
| 1846 | dist += *(normp++) * *(feasiblep++);
|
---|
| 1847 | if (dist > 0)
|
---|
| 1848 | goto LABELerroroutside;
|
---|
| 1849 | normp= normal;
|
---|
| 1850 | if (dist < -qh MINdenom) {
|
---|
| 1851 | for (k=dim; k--; )
|
---|
| 1852 | *(coordp++)= *(normp++) / -dist;
|
---|
| 1853 | }else {
|
---|
| 1854 | for (k=dim; k--; ) {
|
---|
| 1855 | *(coordp++)= qh_divzero(*(normp++), -dist, qh MINdenom_1, &zerodiv);
|
---|
| 1856 | if (zerodiv)
|
---|
| 1857 | goto LABELerroroutside;
|
---|
| 1858 | }
|
---|
| 1859 | }
|
---|
| 1860 | *nextp= coordp;
|
---|
| 1861 | if (qh IStracing >= 4) {
|
---|
| 1862 | qh_fprintf(qh ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset);
|
---|
| 1863 | for (k=dim, coordp=coords; k--; ) {
|
---|
| 1864 | r= *coordp++;
|
---|
| 1865 | qh_fprintf(qh ferr, 8022, " %6.2g", r);
|
---|
| 1866 | }
|
---|
| 1867 | qh_fprintf(qh ferr, 8023, "\n");
|
---|
| 1868 | }
|
---|
| 1869 | return True;
|
---|
| 1870 | LABELerroroutside:
|
---|
| 1871 | feasiblep= feasible;
|
---|
| 1872 | normp= normal;
|
---|
| 1873 | qh_fprintf(qh ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");
|
---|
| 1874 | for (k=dim; k--; )
|
---|
| 1875 | qh_fprintf(qh ferr, 8024, qh_REAL_1, r=*(feasiblep++));
|
---|
| 1876 | qh_fprintf(qh ferr, 8025, "\n halfspace: ");
|
---|
| 1877 | for (k=dim; k--; )
|
---|
| 1878 | qh_fprintf(qh ferr, 8026, qh_REAL_1, r=*(normp++));
|
---|
| 1879 | qh_fprintf(qh ferr, 8027, "\n at offset: ");
|
---|
| 1880 | qh_fprintf(qh ferr, 8028, qh_REAL_1, *offset);
|
---|
| 1881 | qh_fprintf(qh ferr, 8029, " and distance: ");
|
---|
| 1882 | qh_fprintf(qh ferr, 8030, qh_REAL_1, dist);
|
---|
| 1883 | qh_fprintf(qh ferr, 8031, "\n");
|
---|
| 1884 | return False;
|
---|
| 1885 | } /* sethalfspace */
|
---|
| 1886 |
|
---|
| 1887 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1888 | >-------------------------------</a><a name="sethalfspace_all">-</a>
|
---|
| 1889 |
|
---|
| 1890 | qh_sethalfspace_all( dim, count, halfspaces, feasible )
|
---|
| 1891 | generate dual for halfspace intersection with feasible point
|
---|
| 1892 | array of count halfspaces
|
---|
| 1893 | each halfspace is normal coefficients followed by offset
|
---|
| 1894 | the origin is inside the halfspace if the offset is negative
|
---|
| 1895 |
|
---|
| 1896 | returns:
|
---|
| 1897 | malloc'd array of count X dim-1 points
|
---|
| 1898 |
|
---|
| 1899 | notes:
|
---|
| 1900 | call before qh_init_B or qh_initqhull_globals
|
---|
| 1901 | unused/untested code: please email bradb@shore.net if this works ok for you
|
---|
| 1902 | If using option 'Fp', also set qh feasible_point. It is a malloc'd array
|
---|
| 1903 | that is freed by qh_freebuffers.
|
---|
| 1904 |
|
---|
| 1905 | design:
|
---|
| 1906 | see qh_sethalfspace
|
---|
| 1907 | */
|
---|
| 1908 | coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible) {
|
---|
| 1909 | int i, newdim;
|
---|
| 1910 | pointT *newpoints;
|
---|
| 1911 | coordT *coordp, *normalp, *offsetp;
|
---|
| 1912 |
|
---|
| 1913 | trace0((qh ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n"));
|
---|
| 1914 | newdim= dim - 1;
|
---|
| 1915 | if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){
|
---|
| 1916 | qh_fprintf(qh ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",
|
---|
| 1917 | count);
|
---|
| 1918 | qh_errexit(qh_ERRmem, NULL, NULL);
|
---|
| 1919 | }
|
---|
| 1920 | coordp= newpoints;
|
---|
| 1921 | normalp= halfspaces;
|
---|
| 1922 | for (i=0; i < count; i++) {
|
---|
| 1923 | offsetp= normalp + newdim;
|
---|
| 1924 | if (!qh_sethalfspace(newdim, coordp, &coordp, normalp, offsetp, feasible)) {
|
---|
| 1925 | qh_fprintf(qh ferr, 8032, "The halfspace was at index %d\n", i);
|
---|
| 1926 | qh_errexit(qh_ERRinput, NULL, NULL);
|
---|
| 1927 | }
|
---|
| 1928 | normalp= offsetp + 1;
|
---|
| 1929 | }
|
---|
| 1930 | return newpoints;
|
---|
| 1931 | } /* sethalfspace_all */
|
---|
| 1932 |
|
---|
| 1933 |
|
---|
| 1934 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1935 | >-------------------------------</a><a name="sharpnewfacets">-</a>
|
---|
| 1936 |
|
---|
| 1937 | qh_sharpnewfacets()
|
---|
| 1938 |
|
---|
| 1939 | returns:
|
---|
| 1940 | true if could be an acute angle (facets in different quadrants)
|
---|
| 1941 |
|
---|
| 1942 | notes:
|
---|
| 1943 | for qh_findbest
|
---|
| 1944 |
|
---|
| 1945 | design:
|
---|
| 1946 | for all facets on qh.newfacet_list
|
---|
| 1947 | if two facets are in different quadrants
|
---|
| 1948 | set issharp
|
---|
| 1949 | */
|
---|
| 1950 | boolT qh_sharpnewfacets() {
|
---|
| 1951 | facetT *facet;
|
---|
| 1952 | boolT issharp = False;
|
---|
| 1953 | int *quadrant, k;
|
---|
| 1954 |
|
---|
| 1955 | quadrant= (int*)qh_memalloc(qh hull_dim * sizeof(int));
|
---|
| 1956 | FORALLfacet_(qh newfacet_list) {
|
---|
| 1957 | if (facet == qh newfacet_list) {
|
---|
| 1958 | for (k=qh hull_dim; k--; )
|
---|
| 1959 | quadrant[ k]= (facet->normal[ k] > 0);
|
---|
| 1960 | }else {
|
---|
| 1961 | for (k=qh hull_dim; k--; ) {
|
---|
| 1962 | if (quadrant[ k] != (facet->normal[ k] > 0)) {
|
---|
| 1963 | issharp= True;
|
---|
| 1964 | break;
|
---|
| 1965 | }
|
---|
| 1966 | }
|
---|
| 1967 | }
|
---|
| 1968 | if (issharp)
|
---|
| 1969 | break;
|
---|
| 1970 | }
|
---|
| 1971 | qh_memfree( quadrant, qh hull_dim * sizeof(int));
|
---|
| 1972 | trace3((qh ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));
|
---|
| 1973 | return issharp;
|
---|
| 1974 | } /* sharpnewfacets */
|
---|
| 1975 |
|
---|
| 1976 | /*-<a href="qh-geom.htm#TOC"
|
---|
| 1977 | >-------------------------------</a><a name="voronoi_center">-</a>
|
---|
| 1978 |
|
---|
| 1979 | qh_voronoi_center( dim, points )
|
---|
| 1980 | return Voronoi center for a set of points
|
---|
| 1981 | dim is the orginal dimension of the points
|
---|
| 1982 | gh.gm_matrix/qh.gm_row are scratch buffers
|
---|
| 1983 |
|
---|
| 1984 | returns:
|
---|
| 1985 | center as a temporary point
|
---|
| 1986 | if non-simplicial,
|
---|
| 1987 | returns center for max simplex of points
|
---|
| 1988 |
|
---|
| 1989 | notes:
|
---|
| 1990 | from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65
|
---|
| 1991 |
|
---|
| 1992 | design:
|
---|
| 1993 | if non-simplicial
|
---|
| 1994 | determine max simplex for points
|
---|
| 1995 | translate point0 of simplex to origin
|
---|
| 1996 | compute sum of squares of diagonal
|
---|
| 1997 | compute determinate
|
---|
| 1998 | compute Voronoi center (see Bowyer & Woodwark)
|
---|
| 1999 | */
|
---|
| 2000 | pointT *qh_voronoi_center(int dim, setT *points) {
|
---|
| 2001 | pointT *point, **pointp, *point0;
|
---|
| 2002 | pointT *center= (pointT*)qh_memalloc(qh center_size);
|
---|
| 2003 | setT *simplex;
|
---|
| 2004 | int i, j, k, size= qh_setsize(points);
|
---|
| 2005 | coordT *gmcoord;
|
---|
| 2006 | realT *diffp, sum2, *sum2row, *sum2p, det, factor;
|
---|
| 2007 | boolT nearzero, infinite;
|
---|
| 2008 |
|
---|
| 2009 | if (size == dim+1)
|
---|
| 2010 | simplex= points;
|
---|
| 2011 | else if (size < dim+1) {
|
---|
| 2012 | qh_fprintf(qh ferr, 6025, "qhull internal error (qh_voronoi_center):\n need at least %d points to construct a Voronoi center\n",
|
---|
| 2013 | dim+1);
|
---|
| 2014 | qh_errexit(qh_ERRqhull, NULL, NULL);
|
---|
| 2015 | simplex= points; /* never executed -- avoids warning */
|
---|
| 2016 | }else {
|
---|
| 2017 | simplex= qh_settemp(dim+1);
|
---|
| 2018 | qh_maxsimplex(dim, points, NULL, 0, &simplex);
|
---|
| 2019 | }
|
---|
| 2020 | point0= SETfirstt_(simplex, pointT);
|
---|
| 2021 | gmcoord= qh gm_matrix;
|
---|
| 2022 | for (k=0; k < dim; k++) {
|
---|
| 2023 | qh gm_row[k]= gmcoord;
|
---|
| 2024 | FOREACHpoint_(simplex) {
|
---|
| 2025 | if (point != point0)
|
---|
| 2026 | *(gmcoord++)= point[k] - point0[k];
|
---|
| 2027 | }
|
---|
| 2028 | }
|
---|
| 2029 | sum2row= gmcoord;
|
---|
| 2030 | for (i=0; i < dim; i++) {
|
---|
| 2031 | sum2= 0.0;
|
---|
| 2032 | for (k=0; k < dim; k++) {
|
---|
| 2033 | diffp= qh gm_row[k] + i;
|
---|
| 2034 | sum2 += *diffp * *diffp;
|
---|
| 2035 | }
|
---|
| 2036 | *(gmcoord++)= sum2;
|
---|
| 2037 | }
|
---|
| 2038 | det= qh_determinant(qh gm_row, dim, &nearzero);
|
---|
| 2039 | factor= qh_divzero(0.5, det, qh MINdenom, &infinite);
|
---|
| 2040 | if (infinite) {
|
---|
| 2041 | for (k=dim; k--; )
|
---|
| 2042 | center[k]= qh_INFINITE;
|
---|
| 2043 | if (qh IStracing)
|
---|
| 2044 | qh_printpoints(qh ferr, "qh_voronoi_center: at infinity for ", simplex);
|
---|
| 2045 | }else {
|
---|
| 2046 | for (i=0; i < dim; i++) {
|
---|
| 2047 | gmcoord= qh gm_matrix;
|
---|
| 2048 | sum2p= sum2row;
|
---|
| 2049 | for (k=0; k < dim; k++) {
|
---|
| 2050 | qh gm_row[k]= gmcoord;
|
---|
| 2051 | if (k == i) {
|
---|
| 2052 | for (j=dim; j--; )
|
---|
| 2053 | *(gmcoord++)= *sum2p++;
|
---|
| 2054 | }else {
|
---|
| 2055 | FOREACHpoint_(simplex) {
|
---|
| 2056 | if (point != point0)
|
---|
| 2057 | *(gmcoord++)= point[k] - point0[k];
|
---|
| 2058 | }
|
---|
| 2059 | }
|
---|
| 2060 | }
|
---|
| 2061 | center[i]= qh_determinant(qh gm_row, dim, &nearzero)*factor + point0[i];
|
---|
| 2062 | }
|
---|
| 2063 | #ifndef qh_NOtrace
|
---|
| 2064 | if (qh IStracing >= 3) {
|
---|
| 2065 | qh_fprintf(qh ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);
|
---|
| 2066 | qh_printmatrix(qh ferr, "center:", ¢er, 1, dim);
|
---|
| 2067 | if (qh IStracing >= 5) {
|
---|
| 2068 | qh_printpoints(qh ferr, "points", simplex);
|
---|
| 2069 | FOREACHpoint_(simplex)
|
---|
| 2070 | qh_fprintf(qh ferr, 8034, "p%d dist %.2g, ", qh_pointid(point),
|
---|
| 2071 | qh_pointdist(point, center, dim));
|
---|
| 2072 | qh_fprintf(qh ferr, 8035, "\n");
|
---|
| 2073 | }
|
---|
| 2074 | }
|
---|
| 2075 | #endif
|
---|
| 2076 | }
|
---|
| 2077 | if (simplex != points)
|
---|
| 2078 | qh_settempfree(&simplex);
|
---|
| 2079 | return center;
|
---|
| 2080 | } /* voronoi_center */
|
---|
| 2081 |
|
---|