/*
 *
 * $Header: /usr/u/wjr/src/support/RCS/readpts.c,v 1.5 1993/07/26 22:16:16 wjr Exp $
 *
 * Copyright (c) 1990, 1991, 1992, 1993 Cornell University.  All Rights
 * Reserved.
 *
 * Copyright (c) 1991, 1992 Xerox Corporation.  All Rights Reserved.
 *  
 * Use, reproduction, preparation of derivative works, and distribution
 * of this software is permitted.  Any copy of this software or of any
 * derivative work must include both the above copyright notices of
 * Cornell University and Xerox Corporation and this paragraph.  Any
 * distribution of this software or derivative works must comply with all
 * applicable United States export control laws.  This software is made
 * available AS IS, and XEROX CORPORATION DISCLAIMS ALL WARRANTIES,
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
 * AND NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY
 * LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS
 * EXPRESSLY DISCLAIMED, WHETHER ARISING IN CONTRACT, TORT (INCLUDING
 * NEGLIGENCE) OR STRICT LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGES.
 */

static char rcsid[] = "@(#)$Header: /usr/u/wjr/src/support/RCS/readpts.c,v 1.5 1993/07/26 22:16:16 wjr Exp $";

#include <stdio.h>
#include "misc.h"
#include "image.h"
#include "readpts.h"

/*
 * This routine reads a list of points from the given stream. It expects
 * the format
 *	xsize ysize
 *	npts
 *	p1.x p1.y
 *	p2.x p2.y
 *	...
 *
 * Alternately, it can be fed a pbm file and will generate the list itself.
 * This may take longer. It might also be faster because it doesn't have
 * to parse all those decimal numbers; it depends on how sparse the image
 * is. For regular text images, reading the pbm file is probably faster.
 *
 * It returns the appropriate values in the given variables, as you would
 * expect.
 *
 * If pointim is NULL, it is ignored.
 * If it is non-NULL, it is taken to be a pointer to a BinaryImage variable,
 * and on return the variable will contain a BinaryImage which represents
 * the points read from the image.
 *
 * It returns NULL if something goes wrong. In this case, the values of the
 * parameters passed are undefined (including the file position).
 *
 */
point *
read_pts(FILE *file, unsigned *xsize, unsigned *ysize, unsigned *npts,
	 BinaryImage *pointim)
{
    point *data;
    int i;
    char c;
    BinaryImage im;
    int x, y;

    assert(file != (FILE *)NULL);
    assert(xsize != (unsigned *)NULL);
    assert(ysize != (unsigned *)NULL);
    assert(npts != (unsigned *)NULL);

    /*
     * We have to figure out if we're reading a pointlist or a pbm
     * file. Unfortunately, the file we're reading from might be a pipe -
     * we can't read too much to decide. In fact, one character...
     */
    c = getc(file);
    if (c == EOF) {
	return((point *)NULL);
	}
    /* One character of pushback is always OK */
    ungetc(c, file);

    if (c == 'P') {
	/* We've got a pbm file */
	im = imLoadF(IMAGE_BINARY, file);
	if (im == (BinaryImage)NULL) {
	    return((point *)NULL);
	    }

	*npts = 0;
	for (y = imGetYBase(im);
	     y < imGetYBase(im) + (int)imGetHeight(im);
	     y++) {
	    for (x = imGetXBase(im);
		 x < imGetXBase(im) + (int)imGetWidth(im);
		 x++) {
		if (imRef(im, x, y) == 1) {
		    (*npts)++;
		    }
		}
	    }

	data = (point *)malloc(*npts * sizeof(point));
	if (data == (point *)NULL) {
	    imFree(im);
	    return((point *)NULL);
	    }

	i = 0;
	for (y = imGetYBase(im);
	     y < imGetYBase(im) + (int)imGetHeight(im);
	     y++) {
	    for (x = imGetXBase(im);
		 x < imGetXBase(im) + (int)imGetWidth(im);
		 x++) {
		if (imRef(im, x, y) == 1) {
		    data[i].x = x;
		    data[i].y = y;
		    i++;
		    }
		}
	    }

	*xsize = imGetWidth(im);
	*ysize = imGetHeight(im);

	if (pointim != (BinaryImage *)NULL) {
	    *pointim = im;
	    }
	else {
	    imFree(im);
	    }
	}
    else {	    
	/* A point list file */
	if (fscanf(file, "%d %d", xsize, ysize) != 2) {
	    return((point *)NULL);
	    }

	if (fscanf(file, "%d", npts) != 1) {
	    return((point *)NULL);
	    }

	data = malloc(*npts * sizeof(point));
	if (data == (point *)NULL) {
	    return((point *)NULL);
	    }

	for (i = 0; i < *npts; i++) {
	    if (fscanf(file, "%ld %ld", &(data[i].x), &(data[i].y)) != 2) {
		free((void *)data);
		return((point *)NULL);
		}
	    if ((data[i].x < 0) || (data[i].x >= *xsize) ||
		(data[i].y < 0) || (data[i].y >= *ysize)) {
		/* Bad point */
		free((void *)data);
		return((point *)NULL);
		}
	    }
    
	if (pointim != (BinaryImage *)NULL) {
	    /* Gotta convert it into a BinaryImage */
	    im = pts_to_bim(*xsize, *ysize, 0, 0, *npts, data);
	    if (im == (BinaryImage)NULL) {
		/* I bet they regret they ever asked... */
		free((void *)data);
		return((point *)NULL);
		}

	    *pointim = im;
	    }
	}

    return(data);
    }

/*
 * Convert the point list to a binary image of the given shape.
 * Points must be in range. If anything goes wrong, (BinaryImage)NULL
 * is returned.
 */
BinaryImage
pts_to_bim(unsigned width, unsigned height,
	   int xbase, int ybase,
	   unsigned npts, point *pts)
{
    int i;
    BinaryImage im;

    assert(pts != (point *)NULL);

    im = (BinaryImage)imNewOffset(IMAGE_BINARY, width, height, xbase, ybase);
    if (im == (BinaryImage)NULL) {
	return((BinaryImage)NULL);
	}

    for (i = 0; i < npts; i++) {
	assert(((pts[i].x >= xbase) && (pts[i].x < xbase + (int)width)) &&
	       ((pts[i].y >= ybase) && (pts[i].y < ybase + (int)height)));
	imRef(im, pts[i].x, pts[i].y) = 1;
	}

    return(im);
    }

/*
 * This routine takes the array of points it is given and randomises them.
 * This is useful for many applications.
 */
void
shuffle_pts(point *pts, unsigned npts)
{
    int i;
    int j;
    point p;

    /* Beware of npts == (unsigned)0 */
    for (i = 0; i + 1 < npts; i++) {
	j = (random() % (npts - i)) + i;
	p = pts[i];
	pts[i] = pts[j];
	pts[j] = p;
	}
    }
