The Tiny HTM Library
|
00001 00009 #include "tinyhtm/geometry.h" 00010 00011 #include <float.h> 00012 #include <stdlib.h> 00013 #include <string.h> 00014 00015 #include "tinyhtm/select.h" 00016 00017 #ifdef __cplusplus 00018 extern "C" { 00019 #endif 00020 00021 00022 /* ---- Spherical coordinates and 3-vectors ---- */ 00023 00024 enum htm_errcode htm_v3_ne(struct htm_v3 *north, 00025 struct htm_v3 *east, 00026 const struct htm_v3 *v) 00027 { 00028 if (north == NULL || east == NULL || v == NULL) { 00029 return HTM_ENULLPTR; 00030 } else if (htm_v3_norm2(v) == 0.0) { 00031 return HTM_EZERONORM; 00032 } 00033 north->x = - v->x * v->z; 00034 north->y = - v->y * v->z; 00035 north->z = v->x * v->x + v->y * v->y; 00036 if (north->x == 0.0 && north->y == 0.0 && north->z == 0.0) { 00037 /* pick an arbitrary orthogonal basis with z = 0 */ 00038 north->x = -1.0; 00039 east->x = 0.0; 00040 east->y = 1.0; 00041 east->z = 0.0; 00042 } else { 00043 htm_v3_normalize(north, north); 00044 htm_v3_rcross(east, north, v); 00045 htm_v3_normalize(east, east); 00046 } 00047 return HTM_OK; 00048 } 00049 00050 00051 static const double HTM_NAN = 0.0 / 0.0; 00052 static const double HTM_RMAX = 90.0 - 0.001/3600.0; 00053 00054 enum htm_errcode htm_v3_tanrot(double *angle, 00055 const struct htm_v3 *v1, 00056 const struct htm_v3 *v2, 00057 double r) 00058 { 00059 double a, s; 00060 if (angle == NULL || v1 == NULL || v2 == NULL) { 00061 return HTM_ENULLPTR; 00062 } 00063 if (r <= 0.0) { 00064 return HTM_EANG; 00065 } 00066 a = htm_v3_angsep(v1, v2); 00067 if (a == 0.0) { 00068 return HTM_EDEGEN; 00069 } 00070 if (a + 2.0*r > 2.0*HTM_RMAX) { 00071 return HTM_EANG; 00072 } 00073 r *= HTM_RAD_PER_DEG; 00074 a *= HTM_RAD_PER_DEG; 00075 s = 2.0 * sin(r) * sin(0.5*a) / sin(a); 00076 if (s >= 1.0) { 00077 *angle = 90.0; 00078 } else { 00079 *angle = asin(s) * HTM_DEG_PER_RAD; 00080 } 00081 return HTM_OK; 00082 } 00083 00084 00085 enum htm_errcode htm_v3_rot(struct htm_v3 *out, 00086 const struct htm_v3 *v, 00087 const struct htm_v3 *k, 00088 double angle) 00089 { 00090 struct htm_v3 kxv, tmp; 00091 double sina, cosa, nk, kdotv; 00092 if (out == NULL || v == NULL || k == NULL) { 00093 return HTM_ENULLPTR; 00094 } 00095 /* Rodrigues' rotation formula: 00096 v_rot = v cos(a) + (k ^ v) sin(a) + k (k . v) (1 - cos(a)) 00097 */ 00098 nk = htm_v3_norm(k); 00099 if (nk == 0.0) { 00100 return HTM_EZERONORM; 00101 } 00102 sina = sin(angle * HTM_RAD_PER_DEG); 00103 cosa = cos(angle * HTM_RAD_PER_DEG); 00104 kdotv = htm_v3_dot(k, v) / nk; 00105 htm_v3_rcross(&kxv, k, v); 00106 htm_v3_mul(&kxv, &kxv, 0.5*sina / nk); 00107 htm_v3_mul(&tmp, v, cosa); 00108 htm_v3_add(out, &kxv, &tmp); 00109 htm_v3_mul(&tmp, k, kdotv*(1.0 - cosa)); 00110 htm_v3_add(out, out, &tmp); 00111 return HTM_OK; 00112 } 00113 00114 00115 enum htm_errcode htm_v3_centroid(struct htm_v3 *cen, 00116 const struct htm_v3 *points, 00117 size_t n) 00118 { 00119 size_t i; 00120 if (cen == NULL || points == NULL) { 00121 return HTM_ENULLPTR; 00122 } else if (n == 0) { 00123 return HTM_ELEN; 00124 } 00125 cen->x = cen->y = cen->z = 0.0; 00126 for (i = 0; i < n; ++i) { 00127 cen->x += points[i].x; 00128 cen->y += points[i].y; 00129 cen->z += points[i].z; 00130 } 00131 htm_v3_normalize(cen, cen); 00132 return HTM_OK; 00133 } 00134 00135 00136 enum htm_errcode htm_sc_tov3(struct htm_v3 *out, const struct htm_sc *p) 00137 { 00138 double lon, lat, cos_lat; 00139 if (out == NULL || p == NULL) { 00140 return HTM_ENULLPTR; 00141 } 00142 lon = p->lon * HTM_RAD_PER_DEG; 00143 lat = p->lat * HTM_RAD_PER_DEG; 00144 cos_lat = cos(lat); 00145 out->x = cos(lon) * cos_lat; 00146 out->y = sin(lon) * cos_lat; 00147 out->z = sin(lat); 00148 return HTM_OK; 00149 } 00150 00151 00152 enum htm_errcode htm_v3_tosc(struct htm_sc *out, const struct htm_v3 *v) 00153 { 00154 double d2; 00155 if (out == NULL || v == NULL) { 00156 return HTM_ENULLPTR; 00157 } 00158 d2 = v->x*v->x + v->y*v->y; 00159 if (d2 == 0.0) { 00160 out->lon = 0.0; 00161 } else { 00162 double lon = atan2(v->y, v->x) * HTM_DEG_PER_RAD; 00163 if (lon < 0.0) { 00164 lon += 360.0; 00165 if (lon == 360.0) { 00166 lon = 0.0; 00167 } 00168 } 00169 out->lon = lon; 00170 } 00171 if (v->z == 0.0) { 00172 out->lat = 0.0; 00173 } else { 00174 out->lat = htm_clamp(atan2(v->z, sqrt(d2)) * HTM_DEG_PER_RAD, 00175 -90.0, 90.0); 00176 } 00177 return HTM_OK; 00178 } 00179 00180 00181 /* ---- Angular separation and distance ---- */ 00182 00183 double htm_sc_dist2(const struct htm_sc *p1, const struct htm_sc *p2) 00184 { 00185 double x, y, z, d2; 00186 x = sin((p1->lon - p2->lon) * HTM_RAD_PER_DEG * 0.5); 00187 x *= x; 00188 y = sin((p1->lat - p2->lat) * HTM_RAD_PER_DEG * 0.5); 00189 y *= y; 00190 z = cos((p1->lat + p2->lat) * HTM_RAD_PER_DEG * 0.5); 00191 z *= z; 00192 d2 = 4.0 * (x * (z - y) + y); 00193 return d2 < 0.0 ? 0.0 : (d2 > 4.0 ? 4.0 : d2); 00194 } 00195 00196 00197 double htm_sc_angsep(const struct htm_sc *p1, const struct htm_sc *p2) 00198 { 00199 double angsep, x; 00200 x = htm_sc_dist2(p1, p2) * 0.25; 00201 angsep = 2.0 * HTM_DEG_PER_RAD * asin(sqrt(x)); 00202 return angsep > 180.0 ? 180.0 : angsep; 00203 } 00204 00205 00206 double htm_v3_angsepu(const struct htm_v3 *unit_v1, 00207 const struct htm_v3 *unit_v2) 00208 { 00209 double angsep, x; 00210 x = htm_v3_dist2(unit_v1, unit_v2) * 0.25; 00211 angsep = 2.0 * HTM_DEG_PER_RAD * asin(sqrt(x > 1.0 ? 1.0 : x)); 00212 return angsep > 180.0 ? 180.0 : angsep; 00213 } 00214 00215 00216 double htm_v3_angsep(const struct htm_v3 *v1, const struct htm_v3 *v2) 00217 { 00218 struct htm_v3 n; 00219 double ss, cs, angsep; 00220 htm_v3_cross(&n, v1, v2); 00221 ss = htm_v3_norm(&n); 00222 cs = htm_v3_dot(v1, v2); 00223 if (cs == 0.0 && ss == 0.0) { 00224 return 0.0; 00225 } 00226 angsep = atan2(ss, cs) * HTM_DEG_PER_RAD; 00227 return (angsep > 180.0) ? 180.0 : angsep; 00228 } 00229 00230 00231 double htm_v3_edgedist2(const struct htm_v3 *v, 00232 const struct htm_v3 *v1, 00233 const struct htm_v3 *v2, 00234 const struct htm_v3 *e) 00235 { 00236 struct htm_v3 c; 00237 htm_v3_cross(&c, v, e); 00238 if (htm_v3_dot(&c, v1) > 0.0 && htm_v3_dot(&c, v2) < 0.0) { 00239 double d = htm_v3_dot(v, e); 00240 double x = d * d / htm_v3_norm2(e); 00241 double y; 00242 /* x is the square of the sin of the minimum angle between v and the 00243 edge. To map to a square secant distance, compute 00244 2.0*(1 - sqrt(1 - x)) */ 00245 if (x > 1.0) { 00246 return 2.0; 00247 } else if (x < 1.0e-7) { 00248 /* for small x, use taylor series to compute a result accurate to 00249 about 1 ulp */ 00250 y = x * x; 00251 return x + (0.25*y + 0.125*x*y); 00252 } 00253 y = 1.0 - sqrt(1.0 - x); 00254 /* 1 newton-raphson iteration to improve accuracy. */ 00255 return (x - y*y)/(1 - y); 00256 } else { 00257 double d1, d2; 00258 d1 = htm_v3_dist2(v, v1); 00259 d2 = htm_v3_dist2(v, v2); 00260 return d1 < d2 ? d1 : d2; 00261 } 00262 } 00263 00264 00265 /* ---- Spherical Ellipses ---- */ 00266 00267 enum htm_errcode htm_s2ellipse_init(struct htm_s2ellipse * const ell, 00268 const struct htm_v3 * const f1, 00269 const struct htm_v3 * const f2, 00270 const double a) 00271 { 00272 double e, ss, c; 00273 struct htm_v3 f11, f22, f12; 00274 00275 e = 0.5 * htm_v3_angsepu(f1, f2); 00276 if (e > 90.0 - 2.777777777777777778e-6 || a <= e || a >= 180.0 - e) { 00277 return HTM_EANG; 00278 } 00279 htm_v3_add(&ell->cen, f1, f2); 00280 htm_v3_normalize(&ell->cen, &ell->cen); 00281 ss = sin(2.0 * HTM_RAD_PER_DEG * a); 00282 c = cos(2.0 * HTM_RAD_PER_DEG * a); 00283 ss *= ss; 00284 htm_v3_cwise_mul(&f11, f1, f1); 00285 htm_v3_cwise_mul(&f22, f2, f2); 00286 htm_v3_cwise_mul(&f12, f1, f2); 00287 ell->xx = ss - f11.x - f22.x + 2.0*c*f12.x; 00288 ell->yy = ss - f11.y - f22.y + 2.0*c*f12.y; 00289 ell->zz = ss - f11.z - f22.z + 2.0*c*f12.z; 00290 ell->xy = c*(f1->x*f2->y + f1->y*f2->x) - f1->x*f1->y - f2->x*f2->y; 00291 ell->xz = c*(f1->x*f2->z + f1->z*f2->x) - f1->x*f1->z - f2->x*f2->z; 00292 ell->yz = c*(f1->y*f2->z + f1->z*f2->y) - f1->y*f1->z - f2->y*f2->z; 00293 ell->a = a; 00294 return HTM_OK; 00295 } 00296 00297 00298 enum htm_errcode htm_s2ellipse_init2(struct htm_s2ellipse *ellipse, 00299 const struct htm_v3 *cen, 00300 double a, 00301 double b, 00302 double angle) 00303 { 00304 struct htm_v3 N, E, n, e; 00305 double s, c; 00306 00307 if (ellipse == NULL || cen == NULL) { 00308 return HTM_ENULLPTR; 00309 } 00310 if (a <= 0.0 || b <= 0.0 || 00311 a > 90.0 - 2.777777777777777778e-6 || 00312 b > 90.0 - 2.777777777777777778e-6) { 00313 return HTM_EANG; 00314 } 00315 /* elliptical cone is given by: 00316 00317 x^2/tan(a)^2 + y^2/tan(b)^2 - z^2 = 0 00318 00319 in the basis [n, e, cen], where n and e are the north/east vectors 00320 at cen rotated clockwise by posang. 00321 00322 Let M be the 3x3 orthogonal matrix with rows n, e and cen, and let M' 00323 be its tranpose. Then the matrix Q of the elliptical cone is: 00324 00325 Q = M' D M 00326 00327 where D is the diagonal matrix: 00328 00329 D = [ 1/tan(a)^2 0 0 00330 0 1/tan(b)^2 0 00331 0 0 -1 ] 00332 */ 00333 ellipse->cen = *cen; 00334 ellipse->a = a; 00335 a = tan(HTM_RAD_PER_DEG * a); 00336 b = tan(HTM_RAD_PER_DEG * b); 00337 a = 1.0 / (a*a); 00338 b = 1.0 / (b*b); 00339 htm_v3_ne(&N, &E, cen); 00340 /* N, E is the north, east basis at cen */ 00341 s = sin(HTM_RAD_PER_DEG * angle); 00342 c = cos(HTM_RAD_PER_DEG * angle); 00343 htm_v3_mul(&n, &N, c); 00344 htm_v3_mul(&e, &E, s); 00345 htm_v3_sub(&n, &n, &e); /* n = cos(angle)*N - sin(angle)*E */ 00346 htm_v3_mul(&N, &N, s); 00347 htm_v3_mul(&E, &E, c); 00348 htm_v3_add(&e, &N, &E); /* e = sin(angle)*N + cos(angle)*E */ 00349 /* have the elements of M and D, compute and store Q */ 00350 ellipse->xx = a * n.x*n.x + b * e.x*e.x - cen->x*cen->x; 00351 ellipse->yy = a * n.y*n.y + b * e.y*e.y - cen->y*cen->y; 00352 ellipse->zz = a * n.z*n.z + b * e.z*e.z - cen->z*cen->z; 00353 ellipse->xy = a * n.x*n.y + b * e.x*e.y - cen->x*cen->y; 00354 ellipse->xz = a * n.x*n.z + b * e.x*e.z - cen->x*cen->z; 00355 ellipse->yz = a * n.y*n.z + b * e.y*e.z - cen->y*cen->z; 00356 return HTM_OK; 00357 } 00358 00359 00360 /* ---- Convex Spherical Polygons ---- */ 00361 00362 HTM_INLINE int _htm_nv_valid(size_t n) 00363 { 00364 return (n >= 3 && 00365 n < (SIZE_MAX - sizeof(struct htm_s2cpoly)) / 00366 (2 * sizeof(struct htm_v3))); 00367 } 00368 00369 struct htm_s2cpoly * htm_s2cpoly_init(const struct htm_v3 *verts, 00370 size_t n, 00371 enum htm_errcode *err) 00372 { 00373 struct htm_s2cpoly *out; 00374 size_t i; 00375 if (verts == NULL) { 00376 if (err != NULL) { 00377 *err = HTM_ENULLPTR; 00378 } 00379 return NULL; 00380 } 00381 if (!_htm_nv_valid(n)) { 00382 if (err != NULL) { 00383 *err = HTM_ELEN; 00384 } 00385 return NULL; 00386 } 00387 out = (struct htm_s2cpoly *) malloc( 00388 sizeof(struct htm_s2cpoly) + 2 * n * sizeof(struct htm_v3)); 00389 if (out == NULL) { 00390 if (err != NULL) { 00391 *err = HTM_ENOMEM; 00392 } 00393 return NULL; 00394 } 00395 out->n = n; 00396 out->vsum = verts[n - 1]; 00397 for (i = 0; i < n - 1; ++i) { 00398 /* the cross product of two consecutive vertices gives a vector 00399 parallel to the edge plane normal. */ 00400 htm_v3_rcross(&out->ve[i + n], &verts[i], &verts[i + 1]); 00401 htm_v3_add(&out->vsum, &out->vsum, &verts[i]); 00402 } 00403 /* compute last edge plane */ 00404 htm_v3_rcross(&out->ve[2*n - 1], &verts[n - 1], &verts[0]); 00405 /* if vertices are clockwise, then the dot-product of vsum with 00406 any edge plane is negative. */ 00407 if (htm_v3_dot(&out->vsum, &out->ve[n]) < 0.0) { 00408 struct htm_v3 tmp; 00409 /* reorder and invert edge plane normals */ 00410 for (i = 0; i < n/2; ++i) { 00411 tmp = out->ve[i + n]; 00412 htm_v3_neg(&out->ve[i + n], &out->ve[2*n - i - 2]); 00413 htm_v3_neg(&out->ve[2*n - i - 2], &tmp); 00414 } 00415 htm_v3_neg(&out->ve[2*n - 1], &out->ve[2*n - 1]); 00416 for (i = 0; i < n; ++i) { 00417 out->ve[i] = verts[n - i - 1]; 00418 } 00419 } else { 00420 memcpy(out->ve, verts, n * sizeof(struct htm_v3)); 00421 } 00422 if (err != NULL) { 00423 *err = HTM_OK; 00424 } 00425 return out; 00426 } 00427 00428 00429 struct htm_s2cpoly * htm_s2cpoly_box(const struct htm_v3 *cen, 00430 double width, 00431 double height, 00432 double angle, 00433 enum htm_errcode *err) 00434 { 00435 struct htm_v3 verts[4]; 00436 struct htm_v3 edges[4]; 00437 struct htm_v3 north, east; 00438 00439 if (cen == NULL) { 00440 if (err != NULL) { 00441 *err = HTM_ENULLPTR; 00442 } 00443 return NULL; 00444 } 00445 if (width <= 0.0 || height <= 0.0 || 00446 width >= HTM_RMAX || height >= HTM_RMAX) { 00447 if (err != NULL) { 00448 *err = HTM_EANG; 00449 } 00450 return NULL; 00451 } 00452 if (htm_v3_norm2(cen) == 0.0) { 00453 if (err != NULL) { 00454 *err = HTM_EZERONORM; 00455 } 00456 return NULL; 00457 } 00458 /* compute N,E at cen */ 00459 htm_v3_ne(&north, &east, cen); 00460 /* rotate N by +- height/2 around E, and E by +- width/2 around N 00461 to obtain edge plane normals */ 00462 htm_v3_rot(&edges[0], &east, &north, 0.5*width); 00463 htm_v3_rot(&edges[2], &east, &north, -0.5*width); 00464 htm_v3_rot(&edges[1], &north, &east, -0.5*height); 00465 htm_v3_rot(&edges[3], &north, &east, 0.5*height); 00466 /* cross products of edge plane normals yields vertices */ 00467 htm_v3_rcross(&verts[0], &edges[0], &edges[1]); 00468 htm_v3_normalize(&verts[0], &verts[0]); 00469 htm_v3_rcross(&verts[1], &edges[2], &edges[1]); 00470 htm_v3_normalize(&verts[1], &verts[1]); 00471 htm_v3_rcross(&verts[2], &edges[2], &edges[3]); 00472 htm_v3_normalize(&verts[2], &verts[2]); 00473 htm_v3_rcross(&verts[3], &edges[0], &edges[3]); 00474 htm_v3_normalize(&verts[3], &verts[3]); 00475 /* rotate to desired orientation ... */ 00476 if (angle != 0.0) { 00477 htm_v3_rot(&verts[0], &verts[0], cen, angle); 00478 htm_v3_rot(&verts[1], &verts[1], cen, angle); 00479 htm_v3_rot(&verts[2], &verts[2], cen, angle); 00480 htm_v3_rot(&verts[3], &verts[3], cen, angle); 00481 } 00482 /* ... and build polygon from vertices */ 00483 return htm_s2cpoly_init(verts, 4, err); 00484 } 00485 00486 00487 /* Utility function to build an N-gon inscribed in the given circle. 00488 */ 00489 struct htm_s2cpoly * htm_s2cpoly_ngon(const struct htm_v3 *cen, 00490 double r, 00491 size_t n, 00492 enum htm_errcode *err) 00493 { 00494 struct htm_s2cpoly *out; 00495 struct htm_v3 *verts; 00496 struct htm_v3 north, east, v; 00497 double sr, cr; 00498 size_t i; 00499 00500 if (cen == NULL) { 00501 if (err != NULL) { 00502 *err = HTM_ENULLPTR; 00503 } 00504 return NULL; 00505 } 00506 if (r <= 0.0 || r >= HTM_RMAX) { 00507 if (err != NULL) { 00508 *err = HTM_EANG; 00509 } 00510 return NULL; 00511 } 00512 if (!_htm_nv_valid(n)) { 00513 if (err != NULL) { 00514 *err = HTM_ELEN; 00515 } 00516 return NULL; 00517 } 00518 if (htm_v3_norm2(cen) == 0.0) { 00519 if (err != NULL) { 00520 *err = HTM_EZERONORM; 00521 } 00522 return NULL; 00523 } 00524 verts = (struct htm_v3 *) malloc(n * sizeof(struct htm_v3)); 00525 if (verts == NULL) { 00526 if (err != NULL) { 00527 *err = HTM_ENOMEM; 00528 } 00529 return NULL; 00530 } 00531 htm_v3_ne(&north, &east, cen); 00532 sr = sin(r * HTM_RAD_PER_DEG); 00533 cr = cos(r * HTM_RAD_PER_DEG); 00534 for (i = 0; i < n; ++i) { 00535 double ang, sa, ca; 00536 ang = (HTM_RAD_PER_DEG * 360.0 * i) / n; 00537 sa = sin(ang); 00538 ca = cos(ang); 00539 v.x = ca * north.x + sa * east.x; 00540 v.y = ca * north.y + sa * east.y; 00541 v.z = ca * north.z + sa * east.z; 00542 verts[i].x = cr * cen->x + sr * v.x; 00543 verts[i].y = cr * cen->y + sr * v.y; 00544 verts[i].z = cr * cen->z + sr * v.z; 00545 htm_v3_normalize(&verts[i], &verts[i]); 00546 } 00547 out = htm_s2cpoly_init(verts, n, err); 00548 free(verts); 00549 return out; 00550 } 00551 00552 00553 struct htm_s2cpoly * htm_s2cpoly_line(const struct htm_v3 *v1, 00554 const struct htm_v3 *v2, 00555 double r, 00556 enum htm_errcode *err) 00557 { 00558 struct htm_v3 verts[4]; 00559 struct htm_v3 edges[4]; 00560 struct htm_v3 axis1, axis2; 00561 double a; 00562 enum htm_errcode e = htm_v3_tanrot(&a, v1, v2, r); 00563 if (e != HTM_OK) { 00564 if (err != NULL) { 00565 *err = e; 00566 } 00567 return NULL; 00568 } 00569 /* compute rotation axes */ 00570 htm_v3_sub(&axis1, v1, v2); 00571 htm_v3_rcross(&axis2, v1, v2); 00572 /* compute edge plane normals */ 00573 htm_v3_rot(&edges[0], &axis2, &axis1, a); 00574 htm_v3_rcross(&edges[1], v1, &axis2); 00575 htm_v3_rot(&edges[1], &edges[1], &axis2, -r); 00576 htm_v3_rot(&edges[2], &axis2, &axis1, -a); 00577 htm_v3_rcross(&edges[3], v2, &axis2); 00578 htm_v3_rot(&edges[3], &edges[3], &axis2, r); 00579 /* cross products of edge plane normals yields vertices */ 00580 htm_v3_rcross(&verts[0], &edges[0], &edges[1]); 00581 htm_v3_normalize(&verts[0], &verts[0]); 00582 htm_v3_rcross(&verts[1], &edges[2], &edges[1]); 00583 htm_v3_normalize(&verts[1], &verts[1]); 00584 htm_v3_rcross(&verts[2], &edges[2], &edges[3]); 00585 htm_v3_normalize(&verts[2], &verts[2]); 00586 htm_v3_rcross(&verts[3], &edges[0], &edges[3]); 00587 htm_v3_normalize(&verts[3], &verts[3]); 00588 /* ... and build polygon from vertices */ 00589 return htm_s2cpoly_init(verts, 4, err); 00590 } 00591 00592 00593 int htm_s2cpoly_cv3(const struct htm_s2cpoly *cp, const struct htm_v3 *v) 00594 { 00595 size_t i; 00596 const size_t n = cp->n; 00597 for (i = 0; i < n; ++i) { 00598 if (htm_v3_dot(v, &cp->ve[n + i]) < 0.0) { 00599 return 0; 00600 } 00601 } 00602 return 1; 00603 } 00604 00605 00606 double htm_s2cpoly_area(const struct htm_s2cpoly *poly) 00607 { 00608 double asum; 00609 size_t i, n; 00610 00611 if (poly == NULL) { 00612 return 0.0; 00613 } 00614 /* see Girard's theorem */ 00615 for (i = 0, n = poly->n, asum = 0.0; i < n; ++i) { 00616 struct htm_v3 v; 00617 double sina, cosa; 00618 size_t j = (i == 0) ? n - 1 : i - 1; 00619 htm_v3_rcross(&v, &poly->ve[n + j], &poly->ve[n + i]); 00620 sina = 0.5 * htm_v3_norm(&v); 00621 cosa = - htm_v3_dot(&poly->ve[n + j], &poly->ve[n + i]); 00622 asum += atan2(sina, cosa); 00623 } 00624 return asum - (poly->n - 2) * M_PI; 00625 } 00626 00627 00628 struct htm_s2cpoly * htm_s2cpoly_clone(const struct htm_s2cpoly *poly) 00629 { 00630 struct htm_s2cpoly *clone; 00631 size_t n; 00632 00633 if (poly == NULL) { 00634 return NULL; 00635 } 00636 n = sizeof(struct htm_s2cpoly) + 2 * poly->n * sizeof(struct htm_v3); 00637 clone = (struct htm_s2cpoly *) malloc(n); 00638 if (clone != NULL) { 00639 memcpy(clone, poly, n); 00640 } 00641 return clone; 00642 } 00643 00644 00645 enum htm_errcode htm_s2cpoly_pad(struct htm_s2cpoly *poly, double r) 00646 { 00647 struct htm_v3 stackbuf[128]; 00648 struct htm_v3 *vecs; 00649 struct htm_v3 tmp; 00650 double angle; 00651 size_t i, n; 00652 int hemis; 00653 enum htm_errcode err = HTM_OK; 00654 00655 if (poly == NULL) { 00656 return HTM_ENULLPTR; 00657 } else if (r < 0.0) { 00658 return HTM_EANG; 00659 } else if (r == 0.0) { 00660 return HTM_OK; 00661 } 00662 n = poly->n; 00663 if (n > sizeof(stackbuf) / (2*sizeof(struct htm_v3))) { 00664 /* allocate scratch space */ 00665 vecs = (struct htm_v3 *) malloc(n * 2 * sizeof(struct htm_v3)); 00666 if (vecs == NULL) { 00667 return HTM_ENOMEM; 00668 } 00669 } else { 00670 vecs = stackbuf; 00671 } 00672 00673 /* rotate edge plane normals outward */ 00674 for (i = 0; i < n; ++i) { 00675 size_t j = (i == 0 ? n - 1: i - 1); 00676 err = htm_v3_tanrot(&angle, &poly->ve[j], &poly->ve[i], r); 00677 if (err != HTM_OK) { 00678 goto cleanup; 00679 } 00680 htm_v3_sub(&tmp, &poly->ve[i], &poly->ve[j]); 00681 htm_v3_rot(&vecs[j], &poly->ve[n + j], &tmp, angle); 00682 } 00683 00684 /* compute new vertices */ 00685 for (i = 0; i < n; ++i) { 00686 size_t j = (i == 0 ? n - 1: i - 1); 00687 htm_v3_rcross(&vecs[n + i], &vecs[j], &vecs[i]); 00688 htm_v3_normalize(&vecs[n + i], &vecs[n + i]); 00689 if (htm_v3_dot(&vecs[n + i], &poly->ve[i]) < 0.0) { 00690 htm_v3_neg(&vecs[n + i], &vecs[n + i]); 00691 } 00692 } 00693 00694 /* checks that the union of old and new vertices is hemispherical */ 00695 memcpy(vecs, poly->ve, n * sizeof(struct htm_v3)); 00696 hemis = htm_v3_hemispherical(vecs, 2*n, &err); 00697 if (err != HTM_OK) { 00698 goto cleanup; 00699 } else if (!hemis) { 00700 err = HTM_EANG; 00701 goto cleanup; 00702 } 00703 /* copy in new vertices and compute edge plane normals */ 00704 memcpy(poly->ve, vecs + n, n * sizeof(struct htm_v3)); 00705 poly->vsum = vecs[2*n - 1]; 00706 for (i = 0; i < n - 1; ++i) { 00707 htm_v3_rcross(&poly->ve[i + n], &poly->ve[i], &poly->ve[i + 1]); 00708 htm_v3_add(&poly->vsum, &poly->vsum, &poly->ve[i]); 00709 } 00710 /* compute last edge plane */ 00711 htm_v3_rcross(&poly->ve[2*n - 1], &poly->ve[n - 1], &poly->ve[0]); 00712 00713 cleanup: 00714 if (vecs != stackbuf) { 00715 free(vecs); 00716 } 00717 return err; 00718 } 00719 00720 00721 /* ---- Testing whether a set of points is hemispherical ---- */ 00722 00723 /* This test is used by the convexity test and convex hull algorithm. It is 00724 implemented using Megiddo's algorithm for linear programming in R2, see: 00725 00726 Megiddo, N. 1982. Linear-time algorithms for linear programming in R3 and related problems. 00727 In Proceedings of the 23rd Annual Symposium on Foundations of Computer Science (November 03 - 05, 1982). 00728 SFCS. IEEE Computer Society, Washington, DC, 329-338. DOI= http://dx.doi.org/10.1109/SFCS.1982.74 00729 */ 00730 00734 struct _htm_pair { 00735 double first; 00736 double second; 00737 }; 00738 00739 00742 struct _htm_pairs { 00743 size_t n; 00744 struct _htm_pair pairs[]; 00745 }; 00746 00747 00748 HTM_INLINE void _htm_pairs_append(struct _htm_pairs *pairs, 00749 const struct _htm_pair *p) 00750 { 00751 size_t n = pairs->n; 00752 pairs->pairs[n] = *p; 00753 pairs->n = n + 1; 00754 } 00755 00756 00757 static const double HTM_INF = 1.0 / 0.0; 00758 00759 00760 static void _htm_g(double *out, 00761 const struct _htm_pairs *constraints, 00762 double x) 00763 { 00764 size_t i; 00765 double ai = constraints->pairs[0].first; 00766 double v = ai * x + constraints->pairs[0].second; 00767 double amin = ai; 00768 double amax = ai; 00769 for (i = 1; i < constraints->n; ++i) { 00770 double ai = constraints->pairs[i].first; 00771 double vi = ai * x + constraints->pairs[i].second; 00772 if (vi == v) { 00773 if (ai < amin) { 00774 amin = ai; 00775 } 00776 if (ai > amax) { 00777 amax = ai; 00778 } 00779 } else if (vi > v) { 00780 v = vi; 00781 amin = ai; 00782 amax = ai; 00783 } 00784 } 00785 out[0] = v; 00786 out[1] = amin; 00787 out[2] = amax; 00788 } 00789 00790 00791 static void _htm_h(double *out, 00792 const struct _htm_pairs *constraints, 00793 double x) 00794 { 00795 size_t i; 00796 double ai = constraints->pairs[0].first; 00797 double v = ai * x + constraints->pairs[0].second; 00798 double amin = ai; 00799 double amax = ai; 00800 for (i = 1; i < constraints->n; ++i) { 00801 double ai = constraints->pairs[i].first; 00802 double vi = ai * x + constraints->pairs[i].second; 00803 if (vi == v) { 00804 if (ai < amin) { 00805 amin = ai; 00806 } 00807 if (ai > amax) { 00808 amax = ai; 00809 } 00810 } else if (vi < v) { 00811 v = vi; 00812 amin = ai; 00813 amax = ai; 00814 } 00815 } 00816 out[0] = v; 00817 out[1] = amin; 00818 out[2] = amax; 00819 } 00820 00821 00822 static size_t _htm_prune_g(double *intersections, 00823 size_t ni, 00824 struct _htm_pairs *constraints, 00825 const struct _htm_pair *x) 00826 { 00827 size_t i = 0; 00828 size_t n = constraints->n - 1; 00829 while (i < n) { 00830 double xx; 00831 double a1 = constraints->pairs[i].first; 00832 double b1 = constraints->pairs[i].second; 00833 double a2 = constraints->pairs[i + 1].first; 00834 double b2 = constraints->pairs[i + 1].second; 00835 double da = a1 - a2; 00836 if (fabs(da) < DBL_MIN / DBL_EPSILON) { 00837 xx = HTM_INF; 00838 } else { 00839 xx = (b2 - b1) / da; 00840 } 00841 if (HTM_ISSPECIAL(xx)) { 00842 if (b1 > b2) { 00843 constraints->pairs[i + 1] = constraints->pairs[n]; 00844 } else { 00845 constraints->pairs[i] = constraints->pairs[n]; 00846 } 00847 --n; 00848 } else { 00849 if (xx <= x->first) { 00850 if (a1 > a2) { 00851 constraints->pairs[i + 1] = constraints->pairs[n]; 00852 } else { 00853 constraints->pairs[i] = constraints->pairs[n]; 00854 } 00855 --n; 00856 } else if (xx >= x->second) { 00857 if (a1 > a2) { 00858 constraints->pairs[i] = constraints->pairs[n]; 00859 } else { 00860 constraints->pairs[i + 1] = constraints->pairs[n]; 00861 } 00862 --n; 00863 } else { 00864 /* save intersection */ 00865 intersections[ni] = xx; 00866 ++ni; 00867 i += 2; 00868 } 00869 } 00870 } 00871 constraints->n = n + 1; 00872 return ni; 00873 } 00874 00875 00876 static size_t _htm_prune_h(double *intersections, 00877 size_t ni, 00878 struct _htm_pairs *constraints, 00879 const struct _htm_pair *x) 00880 { 00881 size_t i = 0; 00882 size_t n = constraints->n - 1; 00883 while (i < n) { 00884 double xx; 00885 double a1 = constraints->pairs[i].first; 00886 double b1 = constraints->pairs[i].second; 00887 double a2 = constraints->pairs[i + 1].first; 00888 double b2 = constraints->pairs[i + 1].second; 00889 double da = a1 - a2; 00890 if (fabs(da) < DBL_MIN / DBL_EPSILON) { 00891 xx = HTM_INF; 00892 } else { 00893 xx = (b2 - b1) / da; 00894 } 00895 if (HTM_ISSPECIAL(xx)) { 00896 if (b1 < b2) { 00897 constraints->pairs[i + 1] = constraints->pairs[n]; 00898 } else { 00899 constraints->pairs[i] = constraints->pairs[n]; 00900 } 00901 --n; 00902 } else { 00903 if (xx <= x->first) { 00904 if (a1 < a2) { 00905 constraints->pairs[i + 1] = constraints->pairs[n]; 00906 } else { 00907 constraints->pairs[i] = constraints->pairs[n]; 00908 } 00909 --n; 00910 } else if (xx >= x->second) { 00911 if (a1 < a2) { 00912 constraints->pairs[i] = constraints->pairs[n]; 00913 } else { 00914 constraints->pairs[i + 1] = constraints->pairs[n]; 00915 } 00916 --n; 00917 } else { 00918 /* save intersection */ 00919 intersections[ni] = xx; 00920 ++ni; 00921 i += 2; 00922 } 00923 } 00924 } 00925 constraints->n = n + 1; 00926 return ni; 00927 } 00928 00929 00930 static int _htm_feasible_2d(struct _htm_pairs *I1, 00931 struct _htm_pairs *I2, 00932 double *intersections, 00933 const struct htm_v3 *points, 00934 size_t n, 00935 double z) 00936 { 00937 struct _htm_pair xr; 00938 size_t i, ni; 00939 double g[3]; 00940 double h[3]; 00941 00942 xr.first = -HTM_INF; 00943 xr.second = HTM_INF; 00944 I1->n = 0; 00945 I2->n = 0; 00946 /* transform each constraint of the form x*v.x + y*v.y + z*v.z > 0 00947 into y op a*x + b or x op c, where op is < or > */ 00948 for (i = 0; i < n; ++i) { 00949 if (fabs(points[i].y) <= DBL_MIN) { 00950 if (fabs(points[i].x) <= DBL_MIN) { 00951 if (z * points[i].z <= 0.0) { 00952 /* inequalities trivially lack a solution */ 00953 return 0; 00954 } 00955 /* current inequality is trivially true, skip it */ 00956 } else { 00957 double xlim = - z * points[i].z / points[i].x; 00958 if (points[i].x > 0.0) { 00959 if (xlim > xr.first) { 00960 xr.first = xlim; 00961 } 00962 } else { 00963 if (xlim < xr.second) { 00964 xr.second = xlim; 00965 } 00966 } 00967 if (xr.second <= xr.first) { 00968 /* inequalities trivially lack a solution */ 00969 return 0; 00970 } 00971 } 00972 } else { 00973 /* finite since |z|, |points[i].xyz| <= 1.0 and 1/DBL_MIN 00974 are finite */ 00975 struct _htm_pair coeffs; 00976 coeffs.first = - points[i].x / points[i].y; 00977 coeffs.second = - z * points[i].z / points[i].y; 00978 if (points[i].y > 0.0) { 00979 _htm_pairs_append(I1, &coeffs); 00980 } else { 00981 _htm_pairs_append(I2, &coeffs); 00982 } 00983 } 00984 } 00985 /* At this point (xmin, xmax) is non-empty - if either I1 or I2 is empty 00986 then a solution trivially exists. */ 00987 if (I1->n == 0 || I2->n == 0) { 00988 return 1; 00989 } 00990 00991 /* Check for a feasible solution to the inequalities I1 of the form 00992 y > a*x + b, I2 of the form y < a*x + b, x > xr.min and x < xr.max */ 00993 while (1) { 00994 double med; 00995 ni = _htm_prune_g(intersections, 0, I1, &xr); 00996 ni = _htm_prune_h(intersections, ni, I2, &xr); 00997 if (ni == 0) { 00998 /* I1 and I2 each contain exactly one constraint */ 00999 double a1 = I1->pairs[0].first; 01000 double b1 = I1->pairs[0].second; 01001 double a2 = I2->pairs[0].first; 01002 double b2 = I2->pairs[0].second; 01003 double xi; 01004 xi = (b2 - b1) / (a1 - a2); 01005 if (HTM_ISSPECIAL(xi)) { 01006 return b1 < b2; 01007 } 01008 return (xi > xr.first || a1 < a2) && (xi < xr.second || a1 > a2); 01009 } 01010 med = htm_select(intersections, ni, ni >> 1); 01011 /* If g(x) < h(x), x is a feasible solution. Otherwise, refine the 01012 search interval by examining the one-sided derivates of g/h. */ 01013 _htm_g(g, I1, med); 01014 _htm_h(h, I2, med); 01015 if (g[0] <= h[0]) { 01016 return 1; 01017 } else if (g[1] > h[2]) { 01018 xr.second = med; 01019 } else if (g[2] < h[1]) { 01020 xr.first = med; 01021 } else { 01022 return 0; 01023 } 01024 } 01025 /* never reached */ 01026 return 0; 01027 } 01028 01029 01030 static int _htm_feasible_1d(const struct htm_v3 *points, size_t n, double y) 01031 { 01032 double xmin = - HTM_INF; 01033 double xmax = HTM_INF; 01034 size_t i; 01035 for (i = 0; i < n; ++i) { 01036 double xp = points[i].x; 01037 double yp = points[i].y; 01038 if (fabs(xp) <= DBL_MIN) { 01039 if (y * yp <= 0.0) { 01040 return 0; 01041 } 01042 /* inequality is trivially true, skip it */ 01043 } else { 01044 double xlim = - y * yp / xp; 01045 if (xp > 0.0) { 01046 if (xlim > xmin) { 01047 xmin = xlim; 01048 } 01049 } else if (xlim < xmax) { 01050 xmax = xlim; 01051 } 01052 if (xmax <= xmin) { 01053 return 0; 01054 } 01055 } 01056 } 01057 return 1; 01058 } 01059 01060 01061 int htm_v3_hemispherical(const struct htm_v3 *points, 01062 size_t n, 01063 enum htm_errcode *err) 01064 { 01065 unsigned char stackbuf[4096]; 01066 unsigned char *buf; 01067 struct _htm_pairs *I1; 01068 struct _htm_pairs *I2; 01069 double *intersections; 01070 size_t i, b; 01071 int pos, neg; 01072 01073 if (points == NULL) { 01074 if (err != NULL) { 01075 *err = HTM_ENULLPTR; 01076 } 01077 return 0; 01078 } 01079 if (n == 0) { 01080 if (err != NULL) { 01081 *err = HTM_ELEN; 01082 } 01083 return 0; 01084 } 01085 /* setup pointers to 2 _htm_pairs structures, each containing at most 01086 n pairs, and an array of n doubles. Use stack for small inputs, 01087 perform a single allocation otherwise. */ 01088 i = sizeof(struct _htm_pairs) + n*sizeof(struct _htm_pair); 01089 b = 2*i + 3*sizeof(struct _htm_pairs) + (n + 2) * sizeof(double); 01090 if (b <= sizeof(stackbuf)) { 01091 buf = stackbuf; 01092 } else { 01093 buf = (unsigned char *) malloc(b); 01094 if (buf == NULL) { 01095 if (err != NULL) { 01096 *err = HTM_ENOMEM; 01097 } 01098 return 0; 01099 } 01100 } 01101 /* compute b s.t. buf + b is a multiple of sizeof(struct _htm_pairs) */ 01102 b = sizeof(struct _htm_pairs) - ((size_t) buf) % sizeof(struct _htm_pairs); 01103 /* compute i, a multiple of sizeof(struct _htm_pairs) bytes s.t. an 01104 _htm_pairs followed by n _htm_pair structures fits in i */ 01105 i += sizeof(struct _htm_pairs) - i % sizeof(struct _htm_pairs); 01106 /* setup scratch space pointers */ 01107 I1 = (struct _htm_pairs *) (buf + b); 01108 I2 = (struct _htm_pairs *) (buf + (i + b)); 01109 i = 2 * i + b; 01110 i += sizeof(double) - ((size_t) (buf + i)) % sizeof(double); 01111 intersections = (double *) (buf + i); 01112 01113 /* Check whether the set of linear equations 01114 x*v[0] + y*v[1] + z*v[2] > 0.0 (for v in points) 01115 has a solution (x, y, z). If (x,y,z) is a solution (is feasible), 01116 so is C*(x,y,z), C > 0. Therefore we can fix z to 1, -1 and 01117 perform 2D feasibility tests. */ 01118 if (_htm_feasible_2d(I1, I2, intersections, points, n, 1.0) != 0) { 01119 goto feasible; 01120 } 01121 if (_htm_feasible_2d(I1, I2, intersections, points, n, -1.0) != 0) { 01122 goto feasible; 01123 } 01124 /* At this point a feasible solution must have z = 0. Fix y to 1, -1 and 01125 perform 1D feasibility tests. */ 01126 if (_htm_feasible_1d(points, n, 1.0) != 0) { 01127 goto feasible; 01128 } 01129 if (_htm_feasible_1d(points, n, -1.0) != 0) { 01130 goto feasible; 01131 } 01132 /* At this point a feasible solution must have y = z = 0. If all x 01133 coordinates are non-zero and of the same sign, then there is a 01134 feasible solution. */ 01135 for (i = 0, pos = 0, neg = 0; i < n; ++i) { 01136 if (points[i].x > 0.0) { 01137 if (neg != 0) { 01138 goto not_feasible; 01139 } 01140 pos = 1; 01141 } else if (points[i].x < 0.0) { 01142 if (pos != 0) { 01143 goto not_feasible; 01144 } 01145 neg = 1; 01146 } else { 01147 goto not_feasible; 01148 } 01149 } 01150 01151 feasible: 01152 if (buf != stackbuf) { 01153 free(buf); 01154 } 01155 if (err != NULL) { 01156 *err = HTM_OK; 01157 } 01158 return 1; 01159 01160 not_feasible: 01161 if (buf != stackbuf) { 01162 free(buf); 01163 } 01164 if (err != NULL) { 01165 *err = HTM_OK; 01166 } 01167 return 0; 01168 } 01169 01170 01171 /* ---- Convexity test ---- */ 01172 01173 /* Square norm of the robust cross product of 2 cartesian unit vectors must be 01174 >= HTM_RCROSS_N2MIN, or the edge joining them is considered degenerate. 01175 */ 01176 static const double HTM_RCROSS_N2MIN = 4.0e-16; 01177 01178 /* Dot product of a unit plane normal and a cartesian unit vector must be 01179 > HTM_SIN_MIN, or the vector is considered to be on the plane. 01180 */ 01181 static const double HTM_SIN_MIN = 1.0e-10; 01182 01183 /* Dot product of 2 cartesian unit vectors must be < HTM_COS_MAX 01184 or they are considered degenerate. 01185 */ 01186 static const double HTM_COS_MAX = 0.999999999999999; 01187 01188 01189 int htm_v3_convex(const struct htm_v3 *points, 01190 size_t n, 01191 enum htm_errcode *err) 01192 { 01193 struct htm_v3 cen = { 0.0, 0.0, 0.0 }; 01194 struct htm_v3 plane, p1, p2; 01195 double d, n2, wind = 0.0; 01196 size_t end; 01197 int cw = 0, ccw = 0; 01198 enum htm_errcode e; 01199 01200 if (n < 3) { 01201 if (err != NULL) { 01202 *err = HTM_ELEN; 01203 } 01204 return 0; 01205 } 01206 if (!htm_v3_hemispherical(points, n, &e)) { 01207 if (err != NULL) { 01208 *err = e; 01209 } 01210 return 0; 01211 } 01212 htm_v3_centroid(&cen, points, n); 01213 htm_v3_rcross(&p1, &cen, &points[n - 1]); 01214 n2 = htm_v3_norm2(&p1); 01215 if (fabs(n2) < HTM_RCROSS_N2MIN) { 01216 if (err != NULL) { 01217 *err = HTM_EDEGEN; 01218 } 01219 return 0; 01220 } 01221 for (end = 0; end < n; ++end) { 01222 size_t beg = (end < 2 ? (n - 2) + end : end - 2); 01223 size_t mid = (end == 0 ? n - 1 : end - 1); 01224 htm_v3_rcross(&plane, &points[mid], &points[end]); 01225 n2 = htm_v3_norm2(&plane); 01226 if (htm_v3_dot(&points[mid], &points[end]) >= HTM_COS_MAX || 01227 n2 < HTM_RCROSS_N2MIN) { 01228 if (err != NULL) { 01229 *err = HTM_EDEGEN; 01230 } 01231 return 0; 01232 } 01233 htm_v3_div(&plane, &plane, sqrt(n2)); 01234 d = htm_v3_dot(&plane, &points[beg]); 01235 if (d > HTM_SIN_MIN) { 01236 if (cw) { 01237 if (err != NULL) { 01238 *err = HTM_OK; 01239 } 01240 return 0; 01241 } 01242 ccw = 1; 01243 } else if (d < - HTM_SIN_MIN) { 01244 if (ccw) { 01245 if (err != NULL) { 01246 *err = HTM_OK; 01247 } 01248 return 0; 01249 } 01250 cw = 1; 01251 } 01252 /* check that vertices always wind around cen in the same direction */ 01253 d = htm_v3_dot(&plane, &cen); 01254 if ((d < HTM_SIN_MIN && ccw) || (d > -HTM_SIN_MIN && cw)) { 01255 if (err != NULL) { 01256 *err = HTM_OK; 01257 } 01258 return 0; 01259 } 01260 /* sum up winding angle for edge (mid, end) */ 01261 htm_v3_rcross(&p2, &cen, &points[end]); 01262 n2 = htm_v3_norm2(&p2); 01263 if (fabs(n2) < HTM_RCROSS_N2MIN) { 01264 if (err != NULL) { 01265 *err = HTM_EDEGEN; 01266 } 01267 return 0; 01268 } 01269 wind += htm_v3_angsep(&p1, &p2); 01270 p1 = p2; 01271 } 01272 if (err != NULL) { 01273 *err = HTM_OK; 01274 } 01275 /* for convex polygons, the closest multiple of 360 to the 01276 total winding angle is 1 */ 01277 if (wind > 180.0 && wind < 540.0) { 01278 return ccw ? 1 : -1; 01279 } 01280 return 0; 01281 } 01282 01283 01284 /* ---- Convex hull algorithm ---- */ 01285 01288 struct _htm_av3 { 01289 double angle; 01290 struct htm_v3 v; 01291 } HTM_ALIGNED(16); 01292 01293 01294 static void _htm_av3_insertsort(struct _htm_av3 *elts, size_t n) 01295 { 01296 struct _htm_av3 k; 01297 size_t i, j; 01298 01299 for (j = 1; j < n; ++j) { 01300 k = elts[j]; 01301 for (i = j; i > 0 && elts[i - 1].angle > k.angle; --i) { 01302 elts[i] = elts[i - 1]; 01303 } 01304 elts[i] = k; 01305 } 01306 } 01307 01308 01309 static void _htm_av3_merge(struct _htm_av3 *dst, 01310 const struct _htm_av3 *left, 01311 size_t nleft, 01312 const struct _htm_av3 *right, 01313 size_t nright) 01314 { 01315 while (nleft > 0 && nright > 0) { 01316 if (left->angle <= right->angle) { 01317 *dst = *left; 01318 ++left; 01319 --nleft; 01320 } else { 01321 *dst = *right; 01322 ++right; 01323 --nright; 01324 } 01325 ++dst; 01326 } 01327 if (nleft > 0) { 01328 memcpy(dst, left, nleft * sizeof(struct _htm_av3)); 01329 } else if (nright > 0) { 01330 memcpy(dst, right, nright * sizeof(struct _htm_av3)); 01331 } 01332 } 01333 01334 01335 static void _htm_av3_mergesort(struct _htm_av3 *elts, size_t n) 01336 { 01337 size_t i, ns; 01338 uint64_t clg2n; 01339 if (n <= 8) { 01340 _htm_av3_insertsort(elts, n); 01341 return; 01342 } 01343 /* bottom up merge sort: in-place insertion sort, followed by a tree 01344 of merges. 01345 01346 The merge tree is evaluated breadth-first, allowing a simple, 01347 non-recursive loop-based implementation. Assume that elts has 01348 space for 2*n entries, providing the scratch space required by the 01349 mergesort algorithm. Each level of the merge tree m moves the 01350 first/last n entries to the last/first n entries of elts. In order 01351 to end up with the sorted array in the first n entries, arrange 01352 to always perform an even number of merges by adjusting the size 01353 of the insertion sort performed at the merge tree leaves. */ 01354 clg2n = (uint64_t) (n - 1); 01355 clg2n |= (clg2n >> 1); 01356 clg2n |= (clg2n >> 2); 01357 clg2n |= (clg2n >> 4); 01358 clg2n |= (clg2n >> 8); 01359 clg2n |= (clg2n >> 16); 01360 clg2n |= (clg2n >> 32); 01361 clg2n = htm_popcount(clg2n); 01362 if ((clg2n & 1) == 0) { 01363 ns = 4; 01364 clg2n -= 2; 01365 } else { 01366 ns = 8; 01367 clg2n -= 3; 01368 } 01369 /* insertion sort pass */ 01370 for (i = 0; i < n; i += ns) { 01371 _htm_av3_insertsort(elts + i, (n - i >= ns) ? ns : n - i); 01372 } 01373 /* evaluate merge-tree breadth first */ 01374 for (; clg2n > 0; --clg2n, ns *= 2) { 01375 struct _htm_av3 *src = elts + ((clg2n & 1) == 0 ? 0 : n); 01376 struct _htm_av3 *scratch = elts + ((clg2n & 1) == 0 ? n : 0); 01377 for (i = 0; i + 2*ns < n; i += 2*ns) { 01378 _htm_av3_merge(&scratch[i], &src[i], ns, &src[i + ns], ns); 01379 } 01380 if (n - i > ns) { 01381 _htm_av3_merge(&scratch[i], &src[i], ns, &src[i + ns], n - i - ns); 01382 } else { 01383 memcpy(&scratch[i], &src[i], (n - i) * sizeof(struct _htm_av3)); 01384 } 01385 } 01386 } 01387 01388 01389 struct htm_s2cpoly * htm_s2cpoly_hull(const struct htm_v3 *points, 01390 size_t n, 01391 enum htm_errcode *err) 01392 { 01393 struct _htm_av3 stackbuf[128]; 01394 struct _htm_av3 *av; 01395 struct htm_s2cpoly *poly = NULL; 01396 struct htm_v3 *anchor; 01397 struct htm_v3 *edge = NULL; 01398 struct htm_v3 center, refplane; 01399 size_t i, extremum, nav, ncv; 01400 enum htm_errcode e = HTM_OK; 01401 01402 if (points == NULL) { 01403 if (err != NULL) { 01404 *err = HTM_ENULLPTR; 01405 } 01406 return NULL; 01407 } 01408 if (!_htm_nv_valid(n)) { 01409 if (err != NULL) { 01410 *err = HTM_ELEN; 01411 } 01412 return NULL; 01413 } 01414 if (htm_v3_hemispherical(points, n, &e) != 1) { 01415 if (err != NULL) { 01416 *err = (e == HTM_OK) ? HTM_EHEMIS : e; 01417 } 01418 return NULL; 01419 } 01420 /* allocate space for 2n _htm_av3 structs. We need n vertices, n 01421 scratch entries for merge sort, and space for n edge plane normals. */ 01422 if (n <= sizeof(stackbuf)/(2 * sizeof(struct _htm_av3))) { 01423 av = stackbuf; 01424 } else { 01425 av = (struct _htm_av3 *) malloc(2 * sizeof(struct _htm_av3) * n); 01426 if (av == NULL) { 01427 if (err != NULL) { 01428 *err = HTM_ENOMEM; 01429 } 01430 return NULL; 01431 } 01432 } 01433 anchor = &av[0].v; 01434 01435 /* point furthest from the center is on the hull. */ 01436 htm_v3_centroid(¢er, points, n); 01437 { 01438 double n2, maxsep = 0.0; 01439 extremum = 0; 01440 for (i = 0, maxsep = 0.0; i < n; ++i) { 01441 double sep = htm_v3_dist2(&points[i], ¢er); 01442 if (sep > maxsep) { 01443 extremum = i; 01444 maxsep = sep; 01445 } 01446 } 01447 *anchor = points[extremum]; 01448 htm_v3_rcross(&refplane, ¢er, anchor); 01449 n2 = htm_v3_dot(&refplane, &refplane); 01450 if (n2 < HTM_RCROSS_N2MIN) { 01451 /* vertex and center are too close */ 01452 e = HTM_EDEGEN; 01453 goto cleanup; 01454 } 01455 htm_v3_div(&refplane, &refplane, sqrt(n2)); 01456 } 01457 01458 /* build (angle,vertex) list */ 01459 av[0].angle = 0.0; 01460 nav = 1; 01461 for (i = 0; i < n; ++i) { 01462 struct htm_v3 plane; 01463 double n2; 01464 if (extremum == i) { 01465 continue; 01466 } 01467 htm_v3_rcross(&plane, ¢er, &points[i]); 01468 n2 = htm_v3_norm2(&plane); 01469 /* skip points too close to center */ 01470 if (n2 >= HTM_RCROSS_N2MIN) { 01471 struct htm_v3 p; 01472 double sa, angle; 01473 01474 htm_v3_div(&plane, &plane, sqrt(n2)); 01475 htm_v3_rcross(&p, &refplane, &plane); 01476 sa = htm_v3_norm(&p); 01477 if (htm_v3_dot(&p, ¢er) < 0.0) { 01478 sa = -sa; 01479 } 01480 angle = atan2(sa, htm_v3_dot(&refplane, &plane)); 01481 if (angle < 0.0) { 01482 angle += 2.0*M_PI; 01483 } 01484 av[nav].angle = angle; 01485 av[nav].v = points[i]; 01486 ++nav; 01487 } 01488 } 01489 if (nav < 3) { 01490 e = HTM_EDEGEN; 01491 goto cleanup; 01492 } 01493 01494 /* order points by winding angle from the first (extreme) vertex */ 01495 _htm_av3_mergesort(av, nav); 01496 /* stable: av[0].v == anchor */ 01497 01498 /* Loop over vertices using a Graham scan adapted for spherical geometry. 01499 Store verticess in 01500 01501 av[0].v, av[1].v, ... 01502 01503 and edges in 01504 01505 av[nav].v av[nav + 1].v ... 01506 */ 01507 edge = NULL; 01508 ncv = 1; 01509 for (i = 1; i < nav;) { 01510 struct htm_v3 p; 01511 struct htm_v3 *v = &av[i].v; 01512 double n2; 01513 01514 htm_v3_rcross(&p, anchor, v); 01515 n2 = htm_v3_norm2(&p); 01516 01517 if (htm_v3_dot(anchor, v) < HTM_COS_MAX && n2 >= HTM_RCROSS_N2MIN) { 01518 if (ncv == 1) { 01519 /* compute first edge */ 01520 edge = &av[nav].v; 01521 htm_v3_div(edge, &p, sqrt(n2)); 01522 anchor = &av[1].v; 01523 *anchor = *v; 01524 ++ncv; 01525 } else { 01526 double d = htm_v3_dot(v, edge); 01527 if (d > HTM_SIN_MIN) { 01528 /* v is inside the edge defined by the last 01529 2 vertices on the hull */ 01530 edge = &av[nav + ncv - 1].v; 01531 htm_v3_div(edge, &p, sqrt(n2)); 01532 anchor = &av[ncv].v; 01533 *anchor = *v; 01534 ++ncv; 01535 } else if (d < - HTM_SIN_MIN) { 01536 /* backtrack - the most recently added hull vertex 01537 is not actually on the hull. */ 01538 --ncv; 01539 anchor = &av[ncv - 1].v; 01540 edge = &av[nav + ncv - 2].v; 01541 /* reprocess v to decide whether to add it to the hull 01542 or whether further backtracking is necessary. */ 01543 continue; 01544 } 01545 /* v is coplanar with edge, skip it */ 01546 } 01547 } 01548 ++i; 01549 } 01550 01551 /* handle backtracking necessary for last edge */ 01552 while (1) { 01553 struct htm_v3 p; 01554 struct htm_v3 *v = &av[0].v; 01555 double n2; 01556 01557 if (ncv < 3) { 01558 e = HTM_EDEGEN; 01559 goto cleanup; 01560 } 01561 htm_v3_rcross(&p, anchor, v); 01562 n2 = htm_v3_norm2(&p); 01563 if (htm_v3_dot(anchor, v) < HTM_COS_MAX && n2 >= HTM_RCROSS_N2MIN) { 01564 if (htm_v3_dot(v, edge) > HTM_SIN_MIN) { 01565 htm_v3_div(&av[nav + ncv - 1].v, &p, sqrt(n2)); 01566 break; 01567 } 01568 } 01569 --ncv; 01570 anchor = &av[ncv - 1].v; 01571 edge = &av[nav + ncv - 1].v; 01572 } 01573 01574 /* allocate and populate convex polygon */ 01575 poly = (struct htm_s2cpoly *) malloc( 01576 sizeof(struct htm_s2cpoly) + 2 * ncv * sizeof(struct htm_v3)); 01577 if (poly == NULL) { 01578 e = HTM_ENOMEM; 01579 goto cleanup; 01580 } 01581 center.x = center.y = center.z = 0.0; 01582 for (i = 0; i < ncv; ++i) { 01583 htm_v3_add(¢er, ¢er, &av[i].v); 01584 poly->ve[i] = av[i].v; 01585 poly->ve[i + ncv] = av[i + nav].v; 01586 } 01587 poly->n = ncv; 01588 poly->vsum = center; 01589 01590 cleanup: 01591 if (av != stackbuf) { 01592 free(av); 01593 } 01594 if (err != NULL) { 01595 *err = e; 01596 } 01597 return poly; 01598 } 01599 01600 01601 #ifdef __cplusplus 01602 } 01603 #endif 01604