Math-ConvexHull-MonotoneChain

 view release on metacpan or  search on metacpan

MonotoneChain.xs  view on Meta::CPAN


typedef struct {
  double x;
  double y;
} point_t;

typedef point_t* point_ptr_t;

/* Three points are a counter-clockwise turn if ccw > 0, clockwise if
 * ccw < 0, and collinear if ccw = 0 because ccw is a determinant that
 * gives the signed area of the triangle formed by p1, p2 and p3.
 */
STATIC double
ccw(point_t* p1, point_t* p2, point_t* p3)
{
  return (p2->x - p1->x)*(p3->y - p1->y) - (p2->y - p1->y)*(p3->x - p1->x);
}

/* Returns a list of points on the convex hull in counter-clockwise order.
 * Note: the last point in the returned list is the same as the first one.
 */
STATIC void
convex_hull(point_t* points, SSize_t npoints, point_ptr_t** out_hull, SSize_t* out_hullsize)
{
  point_ptr_t* hull;
  SSize_t i, t, k = 0;

  hull = *out_hull;

  /* lower hull */
  for (i = 0; i < npoints; ++i) {
    while (k >= 2 && ccw(hull[k-2], hull[k-1], &points[i]) <= 0) --k;
    hull[k++] = &points[i];
  }

  /* upper hull */
  for (i = npoints-2, t = k+1; i >= 0; --i) {
    while (k >= t && ccw(hull[k-2], hull[k-1], &points[i]) <= 0) --k;
    hull[k++] = &points[i];
  }

  *out_hull = hull;
  *out_hullsize = k;
}

MODULE = Math::ConvexHull::MonotoneChain	PACKAGE = Math::ConvexHull::MonotoneChain
PROTOTYPES: DISABLE

void
convex_hull_sorted(points)
    AV* points
  PREINIT:
    AV* hull_points;
    SSize_t out_hullsize, npoints, arylen, i;
    point_t* cpoints;
    point_ptr_t* out_hull;
    AV* inner_av;
    AV* out_av;
    SV** elemptr;
    SV* elem;
  PPCODE:
    npoints = av_len(points)+1;
    if (npoints <= 2) {
      out_av = newAV();
      av_fill(out_av, npoints-1);
      for (i = 0; i < npoints; ++i) {
        elemptr = av_fetch(points, i, 0);
        av_store(out_av, i, newSVsv(*elemptr));
      }
      XPUSHs(sv_2mortal(newRV_noinc((SV*)out_av)));
      XSRETURN(1);
    }
    else {
      cpoints = (point_t*)malloc(npoints * sizeof(point_t));
      for (i = 0; i < npoints; ++i) {
        if (NULL == (elemptr = av_fetch(points, i, 0))) {
          free(cpoints);
          croak("Could not fetch element from array");
        }

        elem = *elemptr;
        if (SvROK(elem) && SvTYPE(SvRV(elem)) == SVt_PVAV) {
          inner_av = (AV*)SvRV(elem);
          arylen = av_len(inner_av)+1;
          if (arylen < 2) {
            free(cpoints);
            croak("Input array does not only contain point-like structures with at least two coordinates? At point %i.", i);
          }
          else if (NULL == (elemptr = av_fetch(inner_av, 0, 0))) {
            free(cpoints);
            croak("Could not fetch element from array");
          }
          cpoints[i].x = SvNV(*elemptr);

          if (NULL == (elemptr = av_fetch(inner_av, 1, 0))) {
            free(cpoints);
            croak("Could not fetch element from array");
          }
          cpoints[i].y = SvNV(*elemptr);

        }
        else {
          free(cpoints);
          croak("Input array does not only contain point-like structures?");
        }
      }
    } /* end for i in 0..npoints */

    out_hull = malloc(npoints*2 * sizeof(point_ptr_t));
    convex_hull(cpoints, npoints, &out_hull, &out_hullsize);
    out_av = newAV();
    av_fill(out_av, out_hullsize-2);
    for (i = 0; i < out_hullsize-1; ++i) {
      inner_av = newAV();
      av_fill(inner_av, 1);
      av_store(inner_av, 0, newSVnv(out_hull[i]->x));
      av_store(inner_av, 1, newSVnv(out_hull[i]->y));
      elem = newRV_noinc((SV*)inner_av);
      av_store(out_av, i, elem);
    }
    free(out_hull);



( run in 1.097 second using v1.01-cache-2.11-cpan-5511b514fd6 )