/*
 * C code from the article
 * "Testing the Convexity of a Polygon"
 * by Peter Schorn and Frederick Fisher,
 *	(schorn@inf.ethz.ch, fred@kpc.com)
 * in "Graphics Gems IV", Academic Press, 1994
 */

/* Reasonably Optimized Routine to Classify a Polygon's Shape */

/*

.. code omitted which reads polygon, stores in an array, and calls
	classifyPolygon2()
*/

typedef double	Number;		/* float or double */

#define ConvexCompare(delta)						\
    ( (delta[0] > 0) ? -1 :	/* x coord diff, second pt > first pt */\
      (delta[0] < 0) ?	1 :	/* x coord diff, second pt < first pt */\
      (delta[1] > 0) ? -1 :	/* x coord same, second pt > first pt */\
      (delta[1] < 0) ?	1 :	/* x coord same, second pt > first pt */\
      0 )			/* second pt equals first point */

#define ConvexGetPointDelta(delta, pprev, pcur )			\
    /* Given a previous point 'pprev', read a new point into 'pcur' */	\
    /* and return delta in 'delta'.				    */	\
    pcur = pVert[iread++];						\
    delta[0] = pcur[0] - pprev[0];					\
    delta[1] = pcur[1] - pprev[1];					\

#define ConvexCross(p, q) p[0] * q[1] - p[1] * q[0];

#define ConvexCheckTriple						\
    if ( (thisDir = ConvexCompare(dcur)) == -curDir ) {			\
	  ++dirChanges;							\
	  /* The following line will optimize for polygons that are  */ \
	  /* not convex because of classification condition 4,	     */ \
	  /* otherwise, this will only slow down the classification. */ \
	  /* if ( dirChanges > 2 ) return NotConvex;		     */ \
    }									\
    curDir = thisDir;							\
    cross = ConvexCross(dprev, dcur);					\
    if ( cross > 0 ) { if ( angleSign == -1 ) return NotConvex;		\
		       angleSign = 1;					\
		     }							\
    else if (cross < 0) { if (angleSign == 1) return NotConvex;		\
			    angleSign = -1;				\
			  }						\
    pSecond = pThird;		/* Remember ptr to current point. */	\
    dprev[0] = dcur[0];		/* Remember current delta.	  */	\
    dprev[1] = dcur[1];							\

classifyPolygon2( nvert, pVert )
int	nvert;
Number	pVert[][2];
/* Determine polygon type. return one of:
 *	NotConvex, NotConvexDegenerate,
 *	ConvexCCW, ConvexCW, ConvexDegenerate
 */
{
    int	     curDir, thisDir, dirChanges = 0,
	     angleSign = 0, iread, endOfData;
    Number   *pSecond, *pThird, *pSaveSecond, dprev[2], dcur[2], cross;

    /* if ( nvert <= 0 ) return error;	     if you care */

    /* Get different point, return if less than 3 diff points. */
    if ( nvert < 3 ) return ConvexDegenerate;
    iread = 1;
    while ( 1 ) {
	ConvexGetPointDelta( dprev, pVert[0], pSecond );
	if ( dprev[0] || dprev[1] ) break;
	/* Check if out of points. Check here to avoid slowing down cases
	 * without repeated points.
	 */
	if ( iread >= nvert ) return ConvexDegenerate;
    }

    pSaveSecond = pSecond;

    curDir = ConvexCompare(dprev);	/* Find initial direction */

    while ( iread < nvert ) {
	/* Get different point, break if no more points */
	ConvexGetPointDelta(dcur, pSecond, pThird );
	if ( dcur[0] == 0.0  &&	 dcur[1] == 0.0 ) continue;

	ConvexCheckTriple;		/* Check current three points */
    }

    /* Must check for direction changes from last vertex back to first */
    pThird = pVert[0];			/* Prepare for 'ConvexCheckTriple' */
    dcur[0] = pThird[0] - pSecond[0];
    dcur[1] = pThird[1] - pSecond[1];
    if ( ConvexCompare(dcur) ) {
	ConvexCheckTriple;
    }

    /* and check for direction changes back to second vertex */
    dcur[0] = pSaveSecond[0] - pSecond[0];
    dcur[1] = pSaveSecond[1] - pSecond[1];
    ConvexCheckTriple;			/* Don't care about 'pThird' now */

    /* Decide on polygon type given accumulated status */
    if ( dirChanges > 2 )
	return angleSign ? NotConvex : NotConvexDegenerate;

    if ( angleSign > 0 ) return ConvexCCW;
    if ( angleSign < 0 ) return ConvexCW;
    return ConvexDegenerate;
}
