plt - Software for 2D Plots 2.5

File: <base>/plt/classic/plot.c (11,944 bytes)
/*	plt/plot.c		Paul Albrecht		Sept 1984

	Copyright (C) Paul Albrecht 1988.  All rights reserved.

	Last Update:	13 June 1995 (GBM)
	EMACS_MODES:	tabstop=4
*/

#include	"plt.h"
#include	"plot.h"
#include	"axis.h"
#include	"text.h"

#define		GETPM(DFLT)	( *cp ? *cp++ : DFLT )
#define		UNGETPM(CHR) {	if( *(--cp) != CHR )			\
								ASSERT( NO, "Bug in UNGETPM" ); }
#define		PMBLANK(C)	( (C) == ' ' || (C) == ',' || (C) == '/' )



static void	PROTO( GetScatInfo, (PltPtr) );
static void	PROTO( GetCols, (PltPtr,int) );

static Boolean	PROTO( pen, (Boolean,Ptype,Ptype) );
static void	PROTO( spen, (PltPtr,double,double,double) );
static Boolean	PROTO( OutOfRange, (Ptype,Ptype) );

static void PlotText();
static char *FontInfoString();

static	short	lastuse, nextuse, use[20], useall, nextcol;
static	char	*cp;



void	PlotInit( mode )
Mode	mode;
{
PltPtr	plt;
short	n;
	
	for( n=0;  n < Plot.nPlts;  n++ ) {
		plt = &Plot.plts[n];
		if( plt->fgName != 0 )
			FREE( plt->fgName );
		if( plt->name != 0 )
			FREE( plt->name );
	}

	if( Plot.plts != 0 )
		FREE( Plot.plts );
	Plot.nPlts = 0;

	Plot.supress = (char *)calloc(20);
 	Plot.defaultPMode = NORMAL;
	Plot.exclude = YES;
	Plot.xFrom = FDEFAULT;
	Plot.xIncr = FDEFAULT;
}

/******* Match up plot specification (-p option) with columns *******/

void	PlotDef( pspec )
char	*pspec;
{
PltPtr	plt;
short	n;
char	c, cf, str[100];

	nextcol = nextuse = lastuse = 0;
	useall = YES;

	cp = pspec;
	while( *cp || (useall && nextcol < Data.nCols) ) {
		if( *cp == 0 ) {
			if( !useall || nextcol == Data.nCols )
				break;
		}
		if( Plot.nPlts == Plot.maxPlts )
			Plot.plts = (PltPtr)azmem(Plot.plts,
						&Plot.maxPlts,2,sizeof(*Plot.plts));
		plt = &Plot.plts[Plot.nPlts++];
		plt->c2 = NO_COL;
		plt->c3 = NO_COL;

		while( YES ) {
			while( PMBLANK(*cp) )
				cp++;
			if( *cp < '0' || *cp > '9' )
				break;
			n = 0;
			while( *cp >= '0' && *cp <= '9' )
				n = 10*n + ((*cp++ -'0')&0177);
			if( n > Data.nCols )
				err( YES, "Bad column: %d", n );
			use[lastuse++] = n;
			useall = NO;
		}

		while( PMBLANK(*cp) )
			cp++;

		if( *cp == '(' || *cp == '[' )
			plt->pm = Plot.defaultPMode;
		else
			plt->pm = GETPM(Plot.defaultPMode);
		Plot.defaultPMode = NORMAL;

		switch( plt->pm ) {
			case LABEL_N:
			case OUTLINEFILL:
			case FILLBETWEEN:
			case OUTLINE:
			case CDRIVEN:
					GetCols( plt, 3 );
					break;
			case IMPULSE:	ymin = ymax = 0; /* Flow through to NORMAL */
			case DARKNORMAL:
			case FILLED:
			case NORMAL:
					GetCols( plt, 2 );
					break;
			case NCOLZERO:	GetCols( plt, 1 );
					plt->c1 = plt->c0;
					plt->c0 = 0;
					break;
			case LINES:
					GetCols( plt, 4 );
					break;
			case SYMBOL:
			case SCATTER:
			case SYMBOL_STD:
			case SCATTER_STD:
					GetScatInfo( plt );
					break;
			default:
				err( YES, "Bad plot mode spec `%c'", cp[-1] );
		}
		if( nextuse != lastuse )
			err( YES, "Too many columns for `%c'", plt->pm );
		lastuse = nextuse = 0;

		while( PMBLANK(*cp) )
			cp++;
		if( *cp == '[' || *cp == '(' ) {
			n = 0;
			cf = *cp++;
			do { str[n++] = c = *cp++; } while( c && c != ']' && c != ')' );

			if( c != ']' && c != ')' )
				err( YES, "Unterminated %c in `%s'", cf, Plot.pModes );
			str[n-1] = 0;
			plt->fgName = StringSave( str );
		}
	}
	if( *cp )
		err( YES, "Not enough columns for `%s'", cp );
}


static void	GetScatInfo( plt )
PltPtr	plt;
{
static	struct	{
	char	*name;
	short	n;
	}	*s, sym[] = {	/* The first 10 entries must not be altered !!! ... */
	"0", 0,	"1", 1,	"2", 2,	"3", 3,	"4", 4,
	"5", 5,	"6", 6,	"7", 7,	"8", 8,	"9", 9,
	"circle", 0, "utriangle", 1, "diamond", 2, "square", 3, "triangle", 4,
	"fcircle", 5, "futriangle", 6, "fdiamond", 7, "fsquare", 8, "ftriangle", 9,
	 };
short	nchrs;
char	c, symname[20];

	if( plt->pm == SCATTER_STD || plt->pm == SYMBOL_STD ) {
		GetCols( plt, 3 );
		c = GETPM(SYMMETRIC_STD);
		if( c==SYMMETRIC_STD || c==UP_STD || c==DOWN_STD ) {
			plt->subpm = c | 040;
			c = GETPM('*');
		}
		else
			plt->subpm = SYMMETRIC_STD;
	}
	else {
		GetCols( plt, 2 );
		plt->subpm = 0;
		c = GETPM('.');
	}

	if( plt->pm == SYMBOL || plt->pm == SYMBOL_STD ) {
		while( PMBLANK(c) )
			c = GETPM(0);
		for( nchrs=0;  nchrs <  sizeof(symname)-1; nchrs++ ) {
			symname[nchrs] = c;
			if( (c < 'a' || c > 'z') && (c < '0' || c > '9') )
				break;
			c = GETPM(0);
		}
		symname[nchrs] = 0;
		if( c != 0 )
			UNGETPM(c);

		for( s=sym; YES;  s++ ) {
			if( strcmp(s->name,symname) == 0 || strncmp(s->name,symname,nchrs) == 0 ) 
				break;
			if( s+1 == ENDP(sym) )
				err( YES, "No symbol type `%s'", symname );
		}		
		plt->pc = *sym[s->n].name;	/* ... and this is why !!!!!! */
		plt->symbol = YES;
	}
	else
		plt->pc = c;
}


static	void	GetCols( plt, n )
PltPtr	plt;
int		n;
{
short	*col;

	col = &plt->c0;
	while( n-- > 0 ) {
		if( lastuse > 0 ) {
			if( nextuse < lastuse )
				*col++ = use[nextuse++];
			else
				err( YES, "Too few columns for `%c'", plt->pm );
		}
		else {
			if( nextcol < Data.nCols )
				*col++ = nextcol++;
			else
				err( YES, "Ran out of columns for `%c'", plt->pm );
		}
	}
}


PlotNameDef( n, name )
long	n;
char	*name;
{
	while( n >= Plot.maxPlts )
		Plot.plts = (PltPtr)azmem(Plot.plts,
				&Plot.maxPlts,5,sizeof(*Plot.plts));
	Plot.plts[n].name = StringSave( name );
}

/************ Output the plot specified by structure *plt ************/

PlotDraw( plt )
PltPtr	plt;
{
PtrUnion	arg0, arg1;
double		*c0, *c1, *c2, *c3, gray;
Ptype		n, x, y, xFirst, yFirst;
Boolean		dflag, pflag;

	c0  = &Data.row[plt->c0];
	c1  = &Data.row[plt->c1];
	c2 = (plt->c2 == NO_COL) ? 0 : &Data.row[plt->c2];
	c3 = (plt->c3 == NO_COL) ? 0 : &Data.row[plt->c3];

	FontGroupSelect( "p", plt->fgName );

	dflag = pflag = NO;
	DataRead( YES );
	xFirst = DEFAULT;

	while( DataRead(NO) ) {
		x = X(*c0);
		y = Y(*c1);
		if( xFirst == DEFAULT ) {
			xFirst = x;
			yFirst = y;
		}
		switch( plt->pm ) {
		case LABEL_N:
			if( !OutOfRange(x,y) )
				PlotText( x, y, *c2 );
			break;
		case CDRIVEN:	/* kludgy mode, but flexibile */
			n = *c2;
			if( n == CSTROKE || n == CFILL || n == CBBFILL ) {
				if( dflag )
					PolyDef( x, y, n );
				dflag = NO;
			}
			else	if( n >= CTEXT )
					n = CTEXT;
			else	if( n >= CSYMBOL && n <= CSYMBOL+14 )
					n = CSYMBOL;
			else	if( n == CFILLI || n == CFILLI+1 )
					n = CFILLI;
			else	if( n == CBBFILLI || n == CBBFILLI+1 )
					n = CBBFILLI;
			else	if( dflag && n != CCONT ) {
					PolyDef( 0, 0, COSTROKE );
					dflag = NO;
				}

			switch( n ) {
				case CCONT:	PolyDef( x, y, dflag ? CCONT : CMOVE );
					dflag = YES;
					break;
				case CMOVE:	PolyDef( x, y, CMOVE );
					dflag = YES;
					break;
				case CDOT:	plt->pc = '.';
					spen( plt, x, y, 0.0 );
					break;
				case CBOX:	SmallBox( x, y );
					break;
				case CBBFILLI:
				case CFILLI:	gray = *c2 - n;
					arg0.d = &gray;
					special( SETGRAYD, arg0, arg1 );
					PolyDef( x, y, n );
					arg0.d = &oldGrayLevel;
					special( SETGRAY, arg0, arg1 );
					dflag = NO;
					break;
				case CSYMBOL: plt->pc = '0'+ (short)(*c2-n);
					plt->symbol = YES;
					scatterplot( plt, x, y, y, y );
					dflag = plt->symbol = NO;
					break;
				case CTEXT:	PlotText( x, y, *c2-n );
					dflag = NO;
					break;
				case CHANGEFNT:
					old_font = FontInfoString(c0);
					if( *c1 > 0 )
						old_ps = *c1;
					arg0.c = old_font;
					arg1.d = &old_ps;
					special( SETFONT, arg0, arg1 );
					break;
				case CHANGEPS:
					old_ps = *c0;
					arg0.c = old_font;
					arg1.d = &old_ps;
					special( SETFONT, arg0, arg1 );
					break;
				case CHANGELW:
					old_lw = *c0;
					arg0.d = &old_lw;
					special( SETLINEWIDTH, arg0, arg1 );
					break;
				case CHANGEGRAY:
					oldGrayLevel = *c0;
					arg0.d = &oldGrayLevel;
					special( SETGRAY, arg0, arg1 );
					break;
				case CHANGECOLOR:
					arg0.c = FontInfoString(c0);
					special( SETCOLOR, arg0, arg1 );
					break;
				case CHANGELM:
					old_lm = FontInfoString(c0);
					arg0.c = old_lm;
					special( SETLINEMODE, arg0, arg1 );
					break;
			}
			break;
		case DARKNORMAL:
		case OUTLINEFILL:
			if( !dflag )
				PolyDef( x, y, CMOVE );
			else 
				PolyDef( x, y, CBBCONT );
			dflag = YES;
			break;
		case FILLED:
		case FILLBETWEEN:
		case OUTLINE:
			if( !dflag )
				PolyDef( x, y, CMOVE );
			else 
				PolyDef( x, y, CCONT );
			dflag = YES;
			break;
		case NCOLZERO:
		case NORMAL:
			dflag = pen( dflag, x, y );
			pflag |= dflag;
			break;
		case IMPULSE:
			pen( NO, x, Y(0.0) );
			pen( YES, x, y );
			SmallBox( x, y );
			break;
		case SYMBOL:
		case SCATTER:
			spen( plt, *c0, *c1, 0.0 );
			break;
		case SYMBOL_STD:
		case SCATTER_STD:
			spen( plt, *c0, *c1, *c2 );
			break;
		case LINES:
			pen( NO,  x, y );
			pen( YES, X(*c2), Y(*c3) );
			break;
		}
	}

	switch( plt->pm ) {
		case CDRIVEN:
			if( dflag )
				PolyDef( x, y, COSTROKE );
			break;
		case DARKNORMAL:
			y = Y(ya.min);
			PolyDef( x, y, CBBCONT );
			PolyDef( xFirst, y, CBBFILL );
			break;
		case FILLBETWEEN:
			PolyDef( xFirst, yFirst, CFILL );
			break;
		case FILLED:
			ReverseDataRead( YES );
			while( ReverseDataRead(NO) )
				PolyDef( X(*c0), Y(*c2), CCONT );
			PolyDef( xFirst, yFirst, CFILL );
			break;
		case OUTLINEFILL:
			ReverseDataRead( YES );
			while( ReverseDataRead(NO) )
				PolyDef( X(*c0), Y(*c2), CBBCONT );
			PolyDef( xFirst, yFirst, CBBFILL );
			break;
		case OUTLINE:
			ReverseDataRead( YES );
			while( ReverseDataRead(NO) )
				PolyDef( X(*c0), Y(*c2), CCONT );
			PolyDef( xFirst, yFirst, CSTROKE );
			break;
	}

	if( plt->name && pflag )
		label( plt->name );
	else
		pen( NO, xwmin, ywmin );
}


/***** Output a string provided by the -tf or -ts option ******/

static void PlotText( x, y, dbl )
Ptype	x, y;
double	dbl;
{
short	n;
char	lbl[30];

	if( pstr.n > 0 ) {
		n = dbl;
		if( n >= 0 && n < pstr.n )
			plabel( pstr.list[n], x, y, pstr.just, 0.0 );
		else	err( NO, "Plot string %g undefined", dbl );
	}
	else {
		sprintf( lbl, "%g", dbl );
		plabel( lbl, x, y, "CC", 0.0 );
	}
}


/**** Match the index *dptr with a string provided by the -fs option ****/

static	char	*FontInfoString( dptr )
double	*dptr;
{
short	n;

	n = *dptr;
	if( fgstr.list == 0 || n < 0 || n >= fgstr.n )
		err( YES, "No string for font index %lg", *dptr );
	return( fgstr.list[n] );
}


SmallBox( x, y )
Ptype	x, y;
{
static	Ptype	xd, yd;

	if( xd == 0 ) {
		xd = (p->xfull+256)/512;
		yd = (p->yfull+195)/390;
	}
	move( x-xd, y-yd );
	cont( x+xd, y-yd );
	cont( x+xd, y+yd );
	cont( x-xd, y+yd );
	cont( x-xd, y-yd);
}


static	void	spen( plt, dx, dy, yerr )
PltPtr	plt;
double	dx, dy, yerr;
{
Ptype	x, y, yneg, ypos;

	x = X(dx);
	y = Y(dy);

	if( OutOfRange(x,y) ) {
		Plot.excluded++;
		return;
	}

	switch( plt->pm  ) {
		case SYMBOL:
		case SCATTER:
			if( plt->pc == ' ' )
				SmallBox( x, y );
			else
				scatterplot( plt, x, y, y, y );
			break;
		case SYMBOL_STD:
		case SCATTER_STD:
			if( plt->pc == ' ' ) {
				SmallBox( x, y );
				break;
			}
			yneg = Y(dy-yerr);
			ypos = Y(dy+yerr);
			switch( plt->subpm) {
				case SYMMETRIC_STD:	break;
				case UP_STD:		yneg = y;	break;
				case DOWN_STD:		ypos = y;	break;
			}
			scatterplot( plt, x, y, yneg, ypos );
			break;
	}
}


static	Boolean	pen( draw, x, y )
Boolean	draw;
Ptype	x, y;
{
static	Boolean	oldOff = YES, off;

	off = OutOfRange( x, y );

	if( off ) {
		Plot.excluded++;
		return( NO );
	}
	if( !draw || oldOff )
		move( x, y );
	else	cont( x, y );

	oldOff = off;
	return( YES );
}


static	Boolean	OutOfRange( x, y )
{
	if( Plot.exclude )
		return( (x < xwmin) || (x > xwmax) || (y < ywmin) || ( y > ywmax) );
	else
		return( (x < 0) || (x > p->xfull) || (y < 0) || (y > p->yfull) );
}


Ptype	X( xpt )
double	xpt;
{
Ptype	x;

	x = xpt * xa.scl + xa.off;
	if( x < 0 )
		x = 0;
	else	if( x > p->xfull )
			x = p->xfull;
	return( x );
}


Ptype	Y( ypt )
double	ypt;
{
Ptype	y;

	y = ypt * ya.scl + ya.off;
	if( y < 0 )
		y = 0;
	else	if( y > p->yfull )
			y = p->yfull;
	return( y );
}