/*~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~
 *		
 * NAME:	chull
 *	
 * USAGE:	chull [-c] < points > vertices
 *
 * DESCRIPTION: This program computes and prints out the vertices of the 
 *		convex hull of a set of planar points specified in the 
 *		input stream.
 * 
 * ARGUMENTS:	<input stream>	A list of points in the form 
 *				<numPoints>
 *				<point1.x> <point1.y>
 *				<point2.x> <point2.y>
 *					 .
 *					 .
 *
 *		<output stream> A list of the convex hull vertices sorted
 *				in clockwise order. The output has the form
 *
 *				<numPoints>
 *				<point1.x> <point1.y>
 *				<point2.x> <point2.y>
 *					 .
 *					 .
 *
 *		-c		Debug option by which the program will
 *				verify that the output points form a convex
 *				hull of the input points.
 *
 * AUTHOR/DATE:	John Lloyd (7510163)		    Mon Oct 17 20:09:15 1988
 *
 *~=~=~=~=~=~=~=~=~=~=~=~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~=~~=~~=~=*/

#define NULL	0

#include "chull.h"

/* Macro to insert an new element (n) into a double circular linked list
   so that it is directly previous to element (p) */

insertList (p, n)
register POINT2 *p;
register POINT2 *n;
{
	/* inserts a new point 'n' into a circular linked list at the
	   point immediately next to 'p' */

	n->next = p->next;
	n->prev = p;
	p->next = n;
	n->next->prev = n;
}

deleteList (p1,p2)
register POINT2 *p1;
register POINT2 *p2;
{
	/* deletes points p1 through p2 (clockwise order) from a circular
	   linked list */

	p1->prev->next = p2->next;
	p2->next->prev = p1->prev;
}

double sign (p1, p2, p3)
VECT2 *p1, *p2, *p3;
{
	double det;

	/* returns the determinant 

	       |  1    1    1   |
	       | p1_x p2_x p3_x |
	       | p1_y p2_y p3_y |
       
	  which will be positive if the points form a left turn,
	  negative if they form a right turn, and 0 if they are
	  colinear.
	*/

	det = ((p2->x - p1->x) * (p3->y - p1->y) -
	       (p3->x - p1->x) * (p2->y - p1->y));
	return (det);
}

/* Macros using 'sign()' which detect left turns, right turns, and
   colinear points */

#define RIGHT_TURN(p1,p2,p3)	(sign(p1,p2,p3) < 0.0)
#define LEFT_TURN(p1,p2,p3)	(sign(p1,p2,p3) > 0.0)
#define COLINEAR(p1,p2,p3)	(sign(p1,p2,p3) == 0.0)
	
comparePoints (p1, p2)
POINT2 *p1, *p2;
{
	/* Comparison function for sorting points into a lexical order */

	if (p1->p.x > p2->p.x)
	 { return (1);
	 }
	if (p1->p.x < p2->p.x)
	 { return (-1);
	 }
	if (p1->p.y > p2->p.y)
	 { return (1);
	 }
	if (p1->p.y < p2->p.y)
	 { return (-1);
	 }
	return (0);
}

quickSortPoints (points, first, last)
POINT2 *points;
int first, last;
{
	/* Implementation of QUICK SORT for the data type POINT2.
	   This algorithm was taken pretty much directly from
	   Aho, Hopcropt and Ullman, page 96.

	   Points 	-- the base address of the array being sorted.
			   (sorting is done in place).

	   first	-- index of the first point in the current partition.

	   last 	-- index of the last point in the current partition.
	*/

	POINT2 temp;
	POINT2 aval;
	POINT2 *a;
	int i, j;
	int k;

	if (first == last)
	 { return;
	 }
	else
	 { 
	   a = &points[(first+last)/2];
	   aval = *a;
	   i = first;
	   j = last;

	   while (i <= j)
	    { while (comparePoints (&points[j], &aval) >= 0 &&
		     j >= first)
	       { j--;
	       }
	      while (comparePoints (&points[i], &aval) < 0 &&
		     i <= last)
	       { i++;
	       }
	      if (i < j)
	       { temp = points[j];
		 points[j] = points[i];
		 points[i] = temp;
		 i++;
		 j--;
	       }
	    }
	   if (i == first)
	    { /* remove element from second partition be interchanging a
		 with the first element */
	      temp = *a;
	      *a = points[first];
	      points[first] = temp;

	      quickSortPoints (points, first+1, last);
	    }
	   else
	    { 
	      quickSortPoints (points, first, i-1);
	      quickSortPoints (points, j+1, last);
	    }
	 }
}

convexHull (chull, points, numPoints)
POINT2 **chull;
POINT2 *points;
int numPoints;
{
	/* Computes the convex hull of a set of points.

	   INPUT:

	   points	-- input array of points. This array is expendable
			   in that the values will be changed by the routine.

	   num		-- number of points in the original set.
	   
	   OUTPUT:

	   chull 	-- pointer to returned linked list of convex hull 
			   vertices sorted in clockwise order. The memory for
			   these vertices is allocated by the routine using 
			   malloc().

	   <return val> -- the number of vertices on the convex hull.

	*/

	POINT2 *minPoint;	/* first point in the lexical order */
	POINT2 *lastPoint;	/* more recently added convex hull vertex */
	POINT2 *nextPoint;	/* next point to be added to convex hull */
	int numVertices;	/* number of convex hull vertices */
	POINT2 *p, *q;		/* scratch points */
	int i, k;		/* scratch integers */

	if (numPoints == 0)
	 { *chull = NULL;
	   return (0);
	 }
	
	/* first sort the points, and then go through an remove all
	   redudant points */

	quickSortPoints (points, 0, numPoints-1);

	for (k=0, i=1; i<numPoints; )
	 { if (points[k].p.x == points[i].p.x &&
	       points[k].p.y == points[i].p.y)
	    { i++;
	    }
	   else
	    { points[++k] = points[i++];
	    }
	 }
	numPoints = k+1;

	/* if we have fewer than three points, then we have trivial
	   cases */

	if (numPoints < 3)
	 { p = (POINT2*)malloc(numPoints*sizeof(POINT2));
	   if (numPoints == 1)
	    { p[0] = points[0];
	      p[0].next = p[0].prev = p;
	    }
	   else
	    { p[0] = points[0];
	      p[1] = points[1];
	      p[0].next = p[0].prev = &p[1];
	      p[1].next = p[1].prev = &p[0];
	    }
	   *chull = p;
	   return (numPoints);
	 }

	/* seed the algorithm: grab the first three non-colinear points.
	   We start with the first two points. For succesively colinear
	   points, we throw out the middle point. */

	for (i=2; i<numPoints && 
		  COLINEAR (&points[0].p, &points[1].p, &points[i].p); i++)
	   ;

	if (i == numPoints)
	 { /* trivial colinear case */

	   p = (POINT2*)malloc(2*sizeof(POINT2));
	   p[0] = points[0];
	   p[1] = points[i-1];
	   p[0].next = p[0].prev = &p[1];
	   p[1].next = p[1].prev = &p[0];
	   *chull = p;
	   return (2);
	 }

	/* Form circular linked list from points 0, i-1, and i */

	if (RIGHT_TURN (&points[0].p, &points[i-1].p, &points[i].p))
	 { 
	   points[0].next   = points[i].prev   = &points[i-1];
	   points[i-1].next = points[0].prev   = &points[i];
	   points[i].next   = points[i-1].prev = &points[0];
	 }
	else
	 {
	   points[0].prev   = points[i].next   = &points[i-1];
	   points[i-1].prev = points[0].next   = &points[i];
	   points[i].prev   = points[i-1].next = &points[0];
	 }

	minPoint = &points[0];
	lastPoint = &points[i];
	
	for (i++; i<numPoints; i++)
	 { POINT2 *upper, *lower;

	   tangent (&upper, &lower, &points[i], lastPoint);
	   if (upper->next != lower)
	    { deleteList (upper->next, lower->prev);
	    }
	   lastPoint = &points[i];
	   insertList (upper, lastPoint);
	 }

	/* Build the returned vertex list. Count the number of vertices,
	   allocate memory for them, and copy the circular linked list */

	numVertices = 1;
	for (p = minPoint->next; p!= minPoint; p = p->next)
	 { numVertices++;
	 }
	q = (POINT2*)malloc(numVertices*sizeof(POINT2));

	p = minPoint;
	k = 0;
	do
	 { q[k] = *p;
	   if (k == 0)
	    { q[k].prev = &q[numVertices-1];
	    }
	   else
	    { q[k].prev = &q[k-1];
	    }
	   if (k == numVertices-1)
	    { q[k].next = &q[0];
	    }
	   else
	    { q[k].next = &q[k+1];
	    }
	   k++;
	   p = p->next;
	 }
	while (p != minPoint);

	*chull = q;
	return (numVertices);
}

tangent (upper, lower, next, last)
POINT2 **upper, **lower;
POINT2 *next, *last;
{
	/* Routine to add a new point 'p' to the convex hull. Computes
	   the tangent points 'upper' and 'lower' such that the line segments
	   |p upper| and |p lower| are edges of the new convex hull. Points
	   in between 'upper' and 'lower' can then be eliminated.
	
	   INPUT:

	   next		-- new point to be added to convex hull.
	   last		-- last point most recently added to the convex hull.

	   OUTPUT:

	   upper	-- tangent point between p and upper convex hull 
			   vertices.
	   lower 	-- tangent point between p and lower convex hull
			   vertices.
	*/

	POINT2 *v;

	v = last;
	while (!LEFT_TURN(&next->p, &v->p, &v->prev->p))
	 { v = v->prev;
	 }
	*upper = v;
	while (!RIGHT_TURN(&next->p, &v->p, &v->next->p))
	 { v = v->next;
	 }
	*lower = v;
}

#if PROG

#include <stdio.h>

main(argc, argv)
int argc;
char **argv;
{
	POINT2 *pointBuffer;	/* input points to convexHull() */
	POINT2 *checkBuffer;	/* copy of input points for checking */
	POINT2 *chull;		/* pointer to returned convex hull */
	int numPoints;		/* number of input points */
	int numVertices;	/* number of convex hull vertices */
	int i;
	int doCheck = 0;	/* check data */

	argc--; argv++;
	while (argc > 0)
	 { if (strcmp (*argv, "-c") == 0)
	    { doCheck = 1;
	    }
	   else
	    { argc = -1;
	      break;
	    }
	   argc--; argv++;
	 }
	if (argc == -1)
	 { fprintf (stderr, "Usage: chull <-c>\n");
	   exit (-1);
	 }

	/* first read the total number of points */

	if (scanf ("%d", &numPoints) != 1)
	 { fprintf (stderr, "Error reading number of points\n");
	   exit (-1);
	 }
	if (numPoints < 0)
	 { fprintf (stderr, "Error -- number of points given as negative\n");
	   exit (-1);
	 }
	
	/* allocate memory and read in all the points */

	pointBuffer = (POINT2*)malloc (numPoints * sizeof(POINT2));
	for (i=0; i<numPoints; i++)
	 { if (scanf ("%lf%lf", &pointBuffer[i].p.x, 
				&pointBuffer[i].p.y) != 2)
	    { fprintf (stderr, "Error reading point %d\n", i+1);
	      exit (-1);
	    }
	 }

	/* if checking, make a copy of the points */

	if (doCheck)
	 { checkBuffer = (POINT2*)malloc (numPoints * sizeof(POINT2));
	   bcopy ((char*)pointBuffer, (char*)checkBuffer, 
		   		      numPoints*sizeof(POINT2));
	 }

	/* compute the convex hull and print it out */

	numVertices = convexHull (&chull, pointBuffer, numPoints);

	if (numPoints > 0)
	 { POINT2 *p;

	   printf ("%d vertices:\n", numVertices);
	   p = chull;
	   do 
	    { printf ("%21.14e %21.14e\n", p->p.x, p->p.y);
	      p = p->next;
	    }
	   while (p != chull);
	 }

	if (doCheck)
	 { POINT2 *p;
	   int testOK = 1;

	   printf ("Checking results ...\n");

	   /* for each point in the set, make sure that it does not form
	      a left turn with any pair of adjacent convex hull vertices */

	   for (i=0; i<numPoints; i++)
	    { p = chull;
	      do
	       { if (!checkPoint (&pointBuffer[i], p, p->next))
		  { testOK = 0;
		  }
		 p = p->next;
	       }
	      while (p != chull);
	    }
	
	   /* for each point on the convex hull, make sure that it forms
	      a right turn with the next 2 vertices */

	   if (numVertices > 2)
	    { p = chull;
	      do
	       { if (!RIGHT_TURN (&p->p, &p->next->p, &p->next->next->p))
		  { printf ("Bad vertices (%g %g) (%g %g) (%g %g)\n", 
			    p->p.x, p->p.y,
			    p->next->p.x, p->next->p.y,
			    p->next->next->p.x, p->next->next->p.y);
		    testOK = 0;
		  }
		 p = p->next;
	       }
	      while (p != chull);
	    }

	   if (testOK)
	    { printf ("OK\n");
	    }
	 }
}

checkPoint (p, v1, v2)
POINT2 *p, *v1, *v2;
{
	/* make sure point p does not form a left turn with a pair of
	   convex hull vertices */

       	if (LEFT_TURN (&p->p, &v1->p, &v2->p))
	 { printf (
"Bad point (%10g %10g):\n	 vertices  (%10g %10g) (%10g %10g)\n",
	   p->p.x, p->p.y, v1->p.x, v1->p.y, v2->p.x, v2->p.y);
	   return (0);
	 }
	else
	 { return (1);
	 }
}

#endif

