www.pudn.com > ilib > IFillPol.c


/*
 * IFillPol.c
 *
 * Image library
 *
 * Description:
 *	Portable routines to manipulate raster images.
 *
 * Limitations:
 *	This function only handles CONVEX non-intersecting polygons.
 *	That means if you scan from left to right, you will pass through
 *	the polygon once.  This make this algorith (1) faster and
 *	(2) easier.  We also assume this is _closed_ polygon.
 *
 * History:
 *	22-Nov-99	Craig Knudsen	cknudsen@radix.net
 *			Created
 *
 ****************************************************************************/

#include 
#include 
#include 
#include 
#include 

#include "Ilib.h"
#include "IlibP.h"


typedef struct {
  int x1, y1, x2, y2;
  double slope;
} linetype;


#undef max
#define max(a,b)	( a > b ) ? a : b
#undef min
#define min(a,b)	( a < b ) ? a : b

/*
** Calculate slope.  Keep in mind that our y coordinate is reverse
** compared to the standard math coordinate system.
*/
static void set_line_slope ( line )
linetype *line;
{
  if ( abs ( line->x2 - line->x1 ) < 0.01 )
    line->slope = 0;
  else
    line->slope = ( (double)line->y2 - (double)line->y1 ) /
      ( (double) line->x2 - (double) line->x1 );
}



/*
** Does a line include the specified y value?
*/
static int line_includes_y_value ( line, yval )
linetype line;
int yval;
{
  if ( line.y1 <= yval && line.y2 >= yval )
    return 1;
  if ( line.y2 <= yval && line.y1 >= yval )
    return 1;
  return 0;
}

static int get_intersection_x_value ( line, yval )
linetype line;
int yval;
{
  double b, ret;

  if ( line.x1 == line.x2 )
    ret = (double) line.x1;

  else if ( line.y1 == line.y2 )
    ret = (double) line.x1;

  else {
    /* calc b now
    ** y = mx + b
    ** b = y - mx
    */
    b = line.y2 - ( line.slope * (double) line.x2 );

    /*
    ** now determine x value
    ** x = (y - b) / m)
    */
    ret = ( ( (double)yval - b ) / line.slope );
  }

  return (int) ret;
}



IError IFillPolygon ( image, gc, points, npoints )
IImage image;
IGC gc;
IPoint *points;
int npoints;
{
  IGCP *gcp = (IGCP *)gc;
  IImageP *imagep = (IImageP *)image;
  int loop;
  int maxY, minY;
  IPoint *pts;
  int left, right, xval;
  int npts, yloop;
  linetype *lines;
  int nlines;
  int found;
  int save_line_width;

  if ( ! gcp )
    return ( IInvalidGC );
  if ( gcp->magic != IMAGIC_GC )
    return ( IInvalidGC );
  if ( ! imagep )
    return ( IInvalidImage );
  if ( imagep->magic != IMAGIC_IMAGE )
    return ( IInvalidImage );
  if ( npoints < 2 )
    return ( INoError );

  save_line_width = gcp->line_width;
  gcp->line_width = 1;

  /* create an array of lines */
  nlines = npoints;
  lines = (linetype *) malloc ( sizeof ( linetype ) * nlines );
  for ( loop = 1; loop < npoints; loop++ ) {
    lines[loop].x1 = points[loop-1].x;
    lines[loop].y1 = points[loop-1].y;
    lines[loop].x2 = points[loop].x;
    lines[loop].y2 = points[loop].y;
    set_line_slope ( &lines[loop] );
  }
  /* last line connects first and last points */
  lines[0].x1 = points[npoints-1].x;
  lines[0].y1 = points[npoints-1].y;
  lines[0].x2 = points[0].x;
  lines[0].y2 = points[0].y;
  set_line_slope ( &lines[0] );

/* debugging code
  for ( loop = 0; loop < nlines; loop++ ) {
    printf ( "Line %d: (%d,%d) to (%d,%d) with slope = %.2f\n",
      loop, lines[loop].x1, lines[loop].y1, lines[loop].x2, lines[loop].y2,
      (float)lines[loop].slope );
  }
*/

  /* calculate the min and max y values */
  minY = maxY = points[0].y;
  for ( loop = 1; loop < npoints; loop++ ) {
    minY = points[loop].y < minY ? points[loop].y : minY;
    maxY = points[loop].y > maxY ? points[loop].y : maxY;
  }
  npts = maxY - minY + 1;
  pts = (IPoint *) malloc ( sizeof ( IPoint ) * npts );
  memset ( pts, '\0', sizeof ( IPoint ) * npts );

  /* now loop through from lowest y to top y */
  for ( yloop = minY; yloop <= maxY; yloop++ ) {
    /* now determine which lines of this polygon intersect this y value */
    found = 0;
    for ( loop = 0; loop < nlines; loop++ ) {
      if ( line_includes_y_value ( lines[loop], yloop ) ) {
        /* don't know if this is the left-most or right-most if this is
        ** first point found...
        */
        if ( ! found ) {
          /* get intersection of this line and y */
          if ( lines[loop].y1 == lines[loop].y2 ) {
            left = min ( lines[loop].x1, lines[loop].x2 );
            right = max ( lines[loop].x1, lines[loop].x2 );
          } else {
            left = right = get_intersection_x_value ( lines[loop], yloop );
          }
          found++;
        } else {
          if ( lines[loop].y1 == lines[loop].y2 ) {
            left = min ( left, lines[loop].x1 );
            left = min ( left, lines[loop].x2 );
            right = max ( right, lines[loop].x1 );
            right = max ( right, lines[loop].x2 );
          } else {
            xval = get_intersection_x_value ( lines[loop], yloop );
            left = min ( left, xval );
            right = max ( right, xval );
          }
          found++;
          /* printf ( "left = %d, right = %d\n", left, right ); */
        }
      }
    }

    if ( found >= 2 ) {
      IDrawLine ( image, gc, left, yloop, right, yloop );
    } else if ( found == 1 && left == right ) {
      IDrawLine ( image, gc, left, yloop, right, yloop );
    } else if ( found == 1 && left != right ) {
      /* Eek.  This really shouldn't happen */
      fprintf ( stderr, "Ilib bug no. 34534345\n" );
    }
  }

  free ( lines );
  free ( pts );

  gcp->line_width = save_line_width;
 
  return ( INoError );
}