plt - Software for 2D Plots 2.5
(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 );
}