The Tiny HTM Library
src/geometry.c
Go to the documentation of this file.
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(&center, 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], &center);
01442             if (sep > maxsep) {
01443                 extremum = i;
01444                 maxsep = sep;
01445             }
01446         }
01447         *anchor = points[extremum];
01448         htm_v3_rcross(&refplane, &center, 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, &center, &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, &center) < 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(&center, &center, &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