www.pudn.com > 2D.rar > graham.c


/*
This code is described in "Computational Geometry in C" (Second Edition),
Chapter 3.  It is not written to be comprehensible without the
explanation in that book.

Input: 2n integer coordinates of points in the plane. 
Output: the convex hull, cw, in PostScript; other output precedes the PS.

NB: The original array storing the points is overwritten.

Compile: gcc -o graham graham.c  (or simply: make)

Written by Joseph O'Rourke.
Last modified: October 1997
Questions to orourke@cs.smith.edu.
--------------------------------------------------------------------
This code is Copyright 1998 by Joseph O'Rourke.  It may be freely
redistributed in its entirety provided that this copyright notice is
not removed.
--------------------------------------------------------------------
*/

#include   
#include   
#include   
#include   "macros.h"

#define EXIT_FAILURE   1
#define X   0
#define Y   1
typedef enum { FALSE, TRUE }   bool;

#define DIM 2               /* Dimension of points */
typedef int tPointi[DIM];   /* Type integer point */


/*----------Point(s) Structure-------------*/
typedef struct tPointStructure tsPoint;
typedef tsPoint *tPoint;
struct tPointStructure {
   int     vnum;
   tPointi v;
   bool    delete;
};
/* Global variables */
#define PMAX    1000               /* Max # of points */
typedef tsPoint tPointArray[PMAX];
static tPointArray P;
int n = 0;                         /* Actual # of points */
int ndelete = 0;                   /* Number deleted */

/*----------Stack Structure-------------*/
typedef struct tStackCell tsStack; /* Used on in NEW() */
typedef tsStack *tStack;
struct tStackCell {
   tPoint   p;
   tStack   next;
};


/*----------Function Prototypes-------------*/
tStack	Pop( tStack s );
void    PrintStack( tStack t );
tStack  Push( tPoint p, tStack top );
tStack  Graham( void );
void    Squash( void );
void	Copy( int i, int j );
void    PrintPostscript( tStack t );
int     Compare( const void *tp1, const void *tp2 );
void    FindLowest( void );
void	Swap( int i, int j );

int     AreaSign( tPointi a, tPointi b, tPointi c );
bool    Left( tPointi a, tPointi b, tPointi c );
int     ReadPoints( void );
void    PrintPoints( void );


main()
{
   tStack   top;

   n = ReadPoints();
   FindLowest();
   PrintPoints();
   qsort(
      &P[1],             /* pointer to 1st elem */
      n-1,               /* number of elems */
      sizeof( tsPoint ), /* size of each elem */
      Compare            /* -1,0,+1 compare function */
   );
   printf("After sorting, ndelete = %d:\n", ndelete);
   PrintPoints();
   if (ndelete > 0) {
      Squash();
      printf("After squashing:\n");
   }

   top = Graham();
   printf("Hull:\n");
   PrintStack( top );
   PrintPostscript( top );
}

/*---------------------------------------------------------------------
FindLowest finds the rightmost lowest point and swaps with 0-th.
The lowest point has the min y-coord, and amongst those, the
max x-coord: so it is rightmost among the lowest.
---------------------------------------------------------------------*/
void   FindLowest( void )
{
   int i;
   int m = 0;   /* Index of lowest so far. */

   for ( i = 1; i < n; i++ )
      if ( (P[i].v[Y] <  P[m].v[Y]) ||
          ((P[i].v[Y] == P[m].v[Y]) && (P[i].v[X] > P[m].v[X])) ) 
         m = i;
   printf("Swapping %d with 0\n", m);
   Swap(0,m); /* Swap P[0] and P[m] */
}

void	Swap( int i, int j )
{
   int temp;
   /* Uses swap macro. */

   SWAP( temp, P[i].vnum,   P[j].vnum );
   SWAP( temp, P[i].v[X],   P[j].v[X] );
   SWAP( temp, P[i].v[Y],   P[j].v[Y] );
   SWAP( temp, P[i].delete, P[j].delete );

}
/*---------------------------------------------------------------------
Compare: returns -1,0,+1 if p1 < p2, =, or > respectively;
here "<" means smaller angle.  Follows the conventions of qsort.
---------------------------------------------------------------------*/
int   Compare( const void *tpi, const void *tpj )
{
   int a;             /* area */
   int x, y;          /* projections of ri & rj in 1st quadrant */
   tPoint pi, pj;
   pi = (tPoint)tpi;
   pj = (tPoint)tpj;

   a = AreaSign( P[0].v, pi->v, pj->v );
   if (a > 0)
      return -1;
   else if (a < 0)
      return 1;
   else { /* Collinear with P[0] */
      x =  abs( pi->v[X] -  P[0].v[X] ) - abs( pj->v[X] -  P[0].v[X] );
      y =  abs( pi->v[Y] -  P[0].v[Y] ) - abs( pj->v[Y] -  P[0].v[Y] );

      ndelete++;
      if ( (x < 0) || (y < 0) ) {
         pi->delete = TRUE;
         return -1;
      }
      else if ( (x > 0) || (y > 0) ) {
         pj->delete = TRUE;
         return 1;
      }
      else { /* points are coincident */
         if (pi->vnum > pj->vnum)
             pj->delete = TRUE;
         else 
             pi->delete = TRUE;
         return 0;
      }
   }
}


/*---------------------------------------------------------------------
Pops off top elment of stack s, frees up the cell, and returns new top.
---------------------------------------------------------------------*/
tStack   Pop( tStack s )
{
   tStack top;

   top = s->next;
   FREE( s );
   return top;
}

/*---------------------------------------------------------------------
Get a new cell, fill it with p, and push it onto the stack.
Return pointer to new stack top.
---------------------------------------------------------------------*/
tStack   Push( tPoint p, tStack top )
{
   tStack   s;

   /* Get new cell and fill it with point. */
   NEW( s, tsStack );
   s->p = p;
   s->next = top;
   return s;
}
/*---------------------------------------------------------------------
---------------------------------------------------------------------*/
void   PrintStack( tStack t )
{
   if (!t) printf("Empty stack\n");
   while (t) { 
      printf("vnum=%d\tx=%d\ty=%d\n", 
             t->p->vnum,t->p->v[X],t->p->v[Y]); 
      t = t->next;
   }
}

/*---------------------------------------------------------------------
Performs the Graham scan on an array of angularly sorted points P.
---------------------------------------------------------------------*/
tStack   Graham()
{
   tStack   top;
   int i;
   tPoint p1, p2;  /* Top two points on stack. */

   /* Initialize stack. */
   top = NULL;
   top = Push ( &P[0], top );
   top = Push ( &P[1], top );

   /* Bottom two elements will never be removed. */
   i = 2;

   while ( i < n ) {
      printf("Stack at top of while loop, i=%d, vnum=%d:\n", i, P[i].vnum);
      PrintStack( top );
      if( !top->next) printf("Error\n"),exit(EXIT_FAILURE);
      p1 = top->next->p;
      p2 = top->p;
      if ( Left( p1->v , p2->v, P[i].v ) ) {
         top = Push ( &P[i], top );
         i++;
      } else    
         top = Pop( top );
      printf("Stack at bot of while loop, i=%d, vnum=%d:\n", i, P[i].vnum);
      PrintStack( top );
      putchar('\n');
   }

   return top;

}

/*---------------------------------------------------------------------
Squash removes all elements from P marked delete.
---------------------------------------------------------------------*/
void   Squash( void )
{
   int i, j;
   i = 0; j = 0;
   /*printf("Squash: n=%d\n",n);*/
   while ( i < n ) {
      /*printf("i=%d,j=%d\n",i,j);*/
      if ( !P[i].delete ) { /* if not marked for deletion */
         Copy( i, j ); /* Copy P[i] to P[j]. */
         j++;
      }
      /* else do nothing: delete by skipping. */
      i++;
   }
   n = j;
   printf("After Squash: n=%d\n",n);
   PrintPoints();
}

void	Copy( int i, int j )
{
   P[j].v[X] = P[i].v[X];
   P[j].v[Y] = P[i].v[Y];
   P[j].vnum = P[i].vnum;
   P[j].delete = P[i].delete;
}


/*---------------------------------------------------------------------
Returns twice the signed area of the triangle determined by a,b,c.
The area is positive if a,b,c are oriented ccw, negative if cw,
and zero if the points are collinear.
---------------------------------------------------------------------*/
int     Area2( tPointi a, tPointi b, tPointi c )
{
   return
          (b[X] - a[X]) * (c[Y] - a[Y]) -
          (c[X] - a[X]) * (b[Y] - a[Y]);
}

/*---------------------------------------------------------------------
Returns true iff c is strictly to the left of the directed
line through a to b.
---------------------------------------------------------------------*/
bool   Left( tPointi a, tPointi b, tPointi c )
{ 
   return  Area2( a, b, c ) > 0;
}

/*---------------------------------------------------------------------
Reads in the coordinates of the points from stdin,
puts them into P, and returns n, the number of vertices.
Initializes other fields of point structure.
---------------------------------------------------------------------*/
int    ReadPoints( void )
{
   int n = 0;

   while ( (n < PMAX) && (scanf("%d %d",&P[n].v[0],&P[n].v[1]) != EOF) ) {
      P[n].vnum = n;
      P[n].delete = FALSE;
      /*printf("vnum=%3d, x=%4d, y=%4d, delete=%d\n", 
	 P[n].vnum, P[n].v[X], P[n].v[Y], P[n].delete);*/
      ++n;
   }
   if (n < PMAX)
      printf("n = %3d vertices read\n",n);
   else {
      printf("Error in ReadPoints:  too many points; max is %d\n", PMAX);
      exit(EXIT_FAILURE);
   }

   return n;
}

void   PrintPoints( void )
{
   int   i;

   printf("Points:\n");
   for( i = 0; i < n; i++ )
      printf("vnum=%3d, x=%4d, y=%4d, delete=%d\n", 
	     P[i].vnum, P[i].v[X], P[i].v[Y], P[i].delete);
}

void   PrintPostscript( tStack t)
{
   int   i;
   int xmin, ymin, xmax, ymax;

   xmin = xmax = P[0].v[X];
   ymin = ymax = P[0].v[Y];
   for (i = 1; i < n; i++) {
      if      ( P[i].v[X] > xmax ) xmax = P[i].v[X];
      else if ( P[i].v[X] < xmin ) xmin = P[i].v[X];
      if      ( P[i].v[Y] > ymax ) ymax = P[i].v[Y];
      else if ( P[i].v[Y] < ymin ) ymin = P[i].v[Y];
   }

   /* PostScript header */
   printf("%%!PS\n");
   printf("%%%%Creator: graham.c (Joseph O'Rourke)\n");
   printf("%%%%BoundingBox: %d %d %d %d\n", xmin, ymin, xmax, ymax);
   printf("%%%%EndComments\n");
   printf(".00 .00 setlinewidth\n");
   printf("%d %d translate\n", -xmin+72, -ymin+72 );
   /* The +72 shifts the figure one inch from the lower left corner */

   /* Draw the points as little circles. */
   printf("newpath\n");
   printf("\n%%Points:\n");
   for (i = 0; i < n; i++)
      printf("%d\t%d\t1  0  360\tarc\tstroke\n", P[i].v[X], P[i].v[Y]);
   printf("closepath\n");

   /* Draw the polygon. */
   printf("\n%%Hull:\n");
   printf("newpath\n");
   printf("%d\t%d\tmoveto\n", t->p->v[X], t->p->v[Y]);
   while (t) {
      printf("%d\t%d\tlineto\n", t->p->v[X], t->p->v[Y]);
      t = t->next;
   }
   printf("closepath stroke\n");
   printf("showpage\n%%%%EOF\n");
}
int     AreaSign( tPointi a, tPointi b, tPointi c )
{
    double area2;

    area2 = ( b[0] - a[0] ) * (double)( c[1] - a[1] ) -
            ( c[0] - a[0] ) * (double)( b[1] - a[1] );

    /* The area should be an integer. */
    if      ( area2 >  0.5 ) return  1;
    else if ( area2 < -0.5 ) return -1;
    else                     return  0;
}