/* ======================================================= *
 * Copyright 1998-2005 Stephen C. Grubb                    *
 * http://ploticus.sourceforge.net                         *
 * Covered by GPL; see the file ./Copyright for details.   *
 * ======================================================= */

/* PROC LINEPLOT - draw a lineplot */

/* Jan 15 01 - went from dat2d to dat3d so that original data row is preserved */

#include "pl.h"
#define MAXALT 200
#define MOVE 0
#define LINE 1
#define PATH 2


static int dblcompare( const void *a, const void *b );
/* static int dblcompare( double *f, double *g ); */
static int placenum();

int
PLP_lineplot()
{
int i;
char attr[NAMEMAXLEN], val[256];
char *line, *lineval;
int nt, lvp;
int first;

char buf[256];
int stat;
int align;
double adjx, adjy;

int yfield;
int xfield;
char linedetails[256];
double linestart, linestop; 
int j, k;
int accum;
double x, y;
int stairstep;
double lastseglen;
char label[200];
char labeldetails[256];
double lastx, lasty;
char numstr[40];
char shownums[80];
char numstrfmt[20];
int donumbers;
char pointsym[256];
int dopoints;
char symcode[80];
double radius;
int ptlabelfield;
char ptlabeldetails[256];
double ptlblstart, ptlblstop;
double sob;
double linxstart;
char fillcolor[COLORLEN];
char legendlabel[256]; /* raised (can contain urls for clickmap) scg 4/22/04 */
int npoints;
double f, sum, cr;
int instancemode;
int groupmode;
char selectex[256];
int result;
char forcelastx[50];
char legsamptyp[20];
char altsym[120];
char altwhen[256];
int nalt;
int altlist[MAXALT+2];
int anyvalid;
int realrow;
int gapmissing, ingap;
int clipping;
int firstpt;
double firstx, firsty;
int sortopt;
int relax_xrange;
int fillmode;
/* char oldcolor[COLORLEN]; */
double typical_interval;

TDH_errprog( "pl proc lineplot" );

/* initialize */
yfield = -1;
xfield = -1;
linestart = EDXlo; linestop = EDXhi;
ptlblstart = EDXlo; ptlblstop = EDXhi;
accum = 0;
stairstep = 0;
lastseglen = 0.0;
strcpy( label, "" );
strcpy( labeldetails, "" );
strcpy( shownums, "" );
strcpy( numstrfmt, "%g" );
strcpy( pointsym, "" );
ptlabelfield = 0;
strcpy( ptlabeldetails, "" );
strcpy( linedetails, "" );
sob = 0.0;
linxstart = EDXlo;
fillmode = 0;
strcpy( fillcolor, "gray(0.8)" );
strcpy( legendlabel, "" );
instancemode = 0;
groupmode = 0; /* 1? */
strcpy( selectex, "" );
strcpy( forcelastx, "" );
strcpy( legsamptyp, "symbol" );
strcpy( altsym, "" );
strcpy( altwhen, "" );
gapmissing = 0;
clipping = 0;
firstpt = 0;
sortopt = 0;
relax_xrange = 0;
typical_interval = -99.0;


/* get attributes.. */
first = 1;
while( 1 ) {
	line = getnextattr( first, attr, val, &lvp, &nt );
	if( line == NULL ) break;
	first = 0;
	lineval = &line[lvp];

	if( stricmp( attr, "yfield" )==0 ) yfield = fref( val ) - 1;
	else if( stricmp( attr, "xfield" )==0 ) xfield = fref( val ) - 1;
	else if( stricmp( attr, "linedetails" )==0 ) {
		strcpy( linedetails, lineval );
		}
	else if( stricmp( attr, "label" )==0 ) {
		strcpy( label, lineval );
		convertnl( label );
		}
	else if( stricmp( attr, "labeldetails" )==0 ) strcpy( labeldetails, lineval );
	else if( stricmp( attr, "legendlabel" )==0 ) strcpy( legendlabel, lineval );
	else if( stricmp( attr, "linerange" )==0 ) {
		getrange( lineval, &linestart, &linestop, 'x', EDXlo, EDXhi );
		}
	else if( stricmp( attr, "xstart" )==0 ) {
		linxstart = Econv( X, val );
		if( Econv_error() ) linxstart = EDXlo;
		}
	else if( stricmp( attr, "firstpoint" )==0 ) {
		int ix;
		ix = 0;
		firstpt = 1;
		strcpy( buf, GL_getok( lineval, &ix ));
		firstx = Econv( X, buf );
		if( Econv_error() ) firstpt = 0;
		strcpy( buf, GL_getok( lineval, &ix ));
		firsty = Econv( X, buf );
		if( Econv_error() ) firstpt = 0;
		}
	else if( stricmp( attr, "accum" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) accum = 1;
		else accum = 0;
		}
	else if( stricmp( attr, "stairstep" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) stairstep = 1;
		else stairstep = 0;
		}
	else if( stricmp( attr, "instancemode" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) instancemode = 1;
		else instancemode = 0;
		}
	else if( stricmp( attr, "groupmode" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) groupmode = 1;
		else groupmode = 0;
		}
	else if( stricmp( attr, "gapmissing" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) gapmissing = 1;
		else if( stricmp( val, "small" )==0 ) gapmissing = 2;
		else if( stricmp( val, "auto" )==0 ) gapmissing = 3;
		else if( stricmp( val, "autosmall" )==0 ) gapmissing = 4;
		else if( stricmp( val, "autozero" )==0 ) gapmissing = 5;
		else gapmissing = 0;
		}
	else if( stricmp( attr, "clip" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) clipping = 1;
		else clipping = 0;
		}
	else if( stricmp( attr, "sort" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) sortopt = 1;
		else sortopt = 0;
		}
	else if( stricmp( attr, "relax_xrange" ) == 0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) relax_xrange = 1;
		else relax_xrange = 0;
		}

	else if( stricmp( attr, "lastseglen" )==0 ) Elenex( val, X, &lastseglen );

	else if( stricmp( attr, "numbers" )==0 ) strcpy( shownums, lineval );
	else if( stricmp( attr, "numbersformat" )==0 ) strcpy( numstrfmt, val );
	else if( stricmp( attr, "select" )==0 ) strcpy( selectex, lineval );
	else if( stricmp( attr, "altsymbol" )==0 ) strcpy( altsym, lineval );
	else if( stricmp( attr, "altwhen" )==0 ) strcpy( altwhen, lineval );
	else if( stricmp( attr, "lastx" )==0 ) strcpy( forcelastx, val );
	else if( stricmp( attr, "pointsymbol" )==0 ) strcpy( pointsym, lineval );
	/* else if( stricmp( attr, "ptlabelfield" )==0 ) ptlabelfield = atoi( val ) - 1; */
	else if( stricmp( attr, "ptlabelfield" )==0 ) ptlabelfield = fref( val ) - 1; 
	else if( stricmp( attr, "ptlabeldetails" )==0 ) strcpy( ptlabeldetails, lineval );
	else if( stricmp( attr, "ptlabelrange" )==0 ) {
		getrange( lineval, &ptlblstart, &ptlblstop, 'x', EDXlo, EDXhi );
		}
	else if( stricmp( attr, "stairoverbars" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) {
			sob = 0.5;
			stairstep = 1; /* implied */
			}
		else sob = 0.0;
		}
	else if( stricmp( attr, "fill" )==0 ) { 
		strcpy( fillcolor, val );
		if( fillcolor[0] != '\0' ) fillmode = 1;
		else fillmode = 0;
		}
	else if( stricmp( attr, "legendsampletype" )==0 ) strcpy( legsamptyp, val );

	else Eerr( 1, "lineplot attribute not recognized", attr );
	}


/* overrides and degenerate cases */
/* -------------------------- */
if( Nrecords < 1 ) return( Eerr( 17, "No data has been read yet w/ proc getdata", "" ) );
if( !scalebeenset() ) 
         return( Eerr( 51, "No scaled plotting area has been defined yet w/ proc areadef", "" ) );


if( (yfield < 0 || yfield >= Nfields ) && !instancemode ) return( Eerr( 601, "yfield out of range", "" ) );
if( xfield >= Nfields ) return( Eerr( 601, "xfield out of range", "" ) );
if( yfield >= 0 && instancemode ) {
	Eerr( 4729, "warning, turning instancemode off since yfield specified", "" );
	instancemode = 0;
	}
 
if( groupmode && ptlabelfield ) {
	Eerr( 4729, "warning, turning ptlabelfield off since groupmode specified", "" );
	ptlabelfield = 0;
	}
	
if( strnicmp( legendlabel, "#usefname", 9 )==0 ) {
	if( instancemode ) getfname( xfield+1, legendlabel );
	else getfname( yfield+1, legendlabel );
	}



/* now do the plotting work.. */
/* -------------------------- */

donumbers = 1;
if( GL_slmember( shownums, "no*" ) || shownums[0] == '\0' ) donumbers = 0;

dopoints = 1;
if( GL_slmember( pointsym, "no*" ) || pointsym[0] == '\0' ) dopoints = 0;


/* put all values into PLV vector, doing accumulation if required.. */
j = 0;
f = linxstart;
nalt = 0;

for( i = 0; i < Nrecords; i++ ) {

	if( selectex[0] != '\0' ) { /* process against selection condition if any.. */
                stat = do_select( selectex, i, &result );
                if( stat != 0 ) { Eerr( stat, "Select error", selectex ); continue; }
                if( result == 0 ) continue; /* reject */
                }
	if( altwhen[0] != '\0' ) { /* check altwhen condition.. */
                stat = do_select( altwhen, i, &result );
                if( stat != 0 ) { Eerr( stat, "Select error", altwhen ); continue; }
                if( result == 1 && nalt < MAXALT ) {
			/* altlist[nalt] = j/2; */
			altlist[nalt] = j/3;
			nalt++;
			}
                }
		


	/* X */
	if( xfield < 0 ) {
		PLV[j] = f + sob;
		f += 1.0;
		}
	else 	{
		PLV[j] = fda( i, xfield, X ) + sob;
		if( Econv_error() ) { 
			conv_msg( i, xfield, "xfield" ); 
			continue;
			}
		}

	j++; 

	/* Y */
	if( instancemode ) PLV[j] = 1.0;
	else 	{
		PLV[j] = fda( i, yfield, Y );
		if( Econv_error() ) { 
			conv_msg( i, yfield, "yfield" ); 
			PLV[j] = NEGHUGE;
			/* continue; removed scg 5/19/99 */
			}
		}
	j++;

	PLV[j] = (double)i;
	j++;

	if( j >= PLVsize-1 ) {
		Eerr( 3579, "Sorry, too many curve points, curve truncated (raise using -maxvector)", "" );
		break;
		}
	}

npoints = j / 3;


/* sort if required.. */  /* added 4/22/02 */
if( sortopt ) {
        if( PLS.debug ) fprintf( PLS.diagfp, "sorting points for line\n" );
        qsort( PLV, npoints, sizeof(double)*3, dblcompare );
        }

/* fprintf( stderr, "after sort\n" );
 * for( i = 0; i < npoints; i++ ) fprintf( stderr, "%g %g %g\n", dat3d(i,0), dat3d(i,1), dat3d(i,2 ) );
 */

/* process for groupmode.. */
if( groupmode && xfield >= 0 ) for( i = 0; i < npoints; i++ ) {

	for( k = i+1; k < npoints; k++ ) {
                		
		if( dat3d(i,0) == dat3d(k,0) ) {
			if( instancemode ) y = 1.0;
			else y = dat3d( k, 1 );
			dat3d( k, 1 ) = NEGHUGE; /* rub out the additional instance.. */
			if( y > NEGHUGE+1 ) (dat3d( i, 1 )) += (y);
			}
		else 	{
			i = k-1;     /* back off.. */
			break;
			}
		}
	}
/* fprintf( stderr, "after grouping\n" );
 * for( i = 0; i < npoints; i++ ) fprintf( stderr, "%g %g %g\n", dat3d(i,0), dat3d(i,1), dat3d(i,2 ) );
 */

/* process for accum.. */
if( accum ) {
	sum = 0.0;
	for( i = 0; i < npoints; i++ ) {

		if( dat3d( i, 1 ) > (NEGHUGE+1) ) {
			sum += (dat3d( i, 1 )); 
			(dat3d( i, 1 )) = sum;
			}
		}
   	}

/* fprintf( stderr, "after accum\n" );
 * for( i = 0; i < npoints; i++ ) fprintf( stderr, "%g %g %g\n", dat3d(i,0), dat3d(i,1), dat3d(i,2 ) );
 */


/* draw the curve.. */
/* ---------------- */

/* set line parameters */
linedet( "linedetails", linedetails, 1.0 );

if( fillmode ) {
	/* strcpy( oldcolor, Ecurcolor ); */
	Ecolor( fillcolor ); /* scg 6/18/04 */
	}
	

first = 1;
lasty = 0.0;
lastx = 0.0;
anyvalid = 0;
ingap = 0;
cr = Elimit( Y, 'l', 's' );
for( i = 0; i < npoints; i++ ) {
	if( !first && (y > (NEGHUGE+1) && x > (NEGHUGE+1) ) ) { lasty = y; lastx = x; }

	if( first && firstpt ) { 
		x = firstx; 
		y = firsty; 
		}
	else	{
		x = dat3d(i,0);
		y = dat3d(i,1);
		}
	
	if( x < (NEGHUGE+1) || y < (NEGHUGE+1) ) {
		if( gapmissing ) ingap = 1;
		continue; /* skip bad values */
		}
	
	if( x < linestart && !relax_xrange ) continue; /* X out of range - lo */ 
	if( x > linestop && !relax_xrange ) {          /* X out of range - hi */ 
		x = lastx; /* back up to last in-range point so last stairtstep is correct*/
		y = lasty;
		break; 
		}
	if( !first && ( gapmissing == 3 || gapmissing == 4 || gapmissing == 5 )) {
		if( typical_interval < 0.0 ) typical_interval = (x - lastx)*1.01;
		else if( x - lastx > typical_interval ) {
			if( gapmissing == 5 ) { Elinu( lastx+typical_interval, 0.0 ); Elinu( x-typical_interval, 0.0 ); }
			else ingap = 1;
			}
		}

	if( !anyvalid && !Ef_inr( Y, y ) ) continue; /* 1/9/03 scg - anyvalid should not become 1 if out of range in Y */

	anyvalid = 1;
	if( first ) {
		Emovu( x, y );
		setfloatvar( "XSTART", x );
		setfloatvar( "YSTART", y );
		first = 0;
		ingap = 0;
		continue;
		}
	if( !first && fillmode && !ingap ) {
		Emovu( x, cr ); Epathu( lastx, cr ); 
		/* if( stairstep ) Epathu( lastx, y ); 
		 * else Epathu( lastx, lasty ); 
		 */
		Epathu( lastx, lasty );
		if( stairstep ) Epathu( x, lasty );
		else Epathu( x, y );
		/* Ecolorfill( fillcolor ); */ /* using Efill .. scg 6/18/04 */
		Efill();
		}
	if( ( gapmissing == 2 || gapmissing ==4 ) && ingap ) {        /* do a quarter-length nib at previous location */
		double nib;
		Emovu( lastx, lasty );
		nib = (x-lastx) * 0.25;
		if( fillmode ) Ecblock( Eax(lastx), Eay(cr), Eax(lastx+nib), Eay(lasty), fillcolor, 0 ); 
		else Elinu( lastx+nib, lasty );
		}
	if( ! fillmode ) { 
		if( ingap ) Emovu( x, y ); 	
		if( stairstep && x > linestart && !ingap ) Elinu( x, lasty ); 
		if( stairstep && x == linestart ) Emovu( x, y ); 
		if( clipping && !ingap && !stairstep ) {
			double cx1, cy1, cx2, cy2;
			cx1 = lastx; cy1 = lasty; cx2 = x; cy2 = y;
			stat = Elineclip( &cx1, &cy1, &cx2, &cy2, EDXlo, EDYlo, EDXhi, EDYhi );
			if( !stat ) { Emovu( cx1, cy1 ); Elinu( cx2, cy2 ); }
			}
		else Elinu( x, y );
		}
	ingap = 0;
	}

if( !anyvalid ) { /* no plottable data points.. exit */  
	/* Ecolor( oldcolor ); */ /* don't do color chg - scg 5/10/05 */
	return( 0 ); 
	} 

/* if last point was invalid, back up to most recent valid point.. */
if( x < (NEGHUGE+1) || y < (NEGHUGE+1) ) { x = lastx; y = lasty; }


/* handle last segment of stairstep.. */
/* if( stairstep ) { */ /* } changed to allow lastseglen to be used anytime, scg 12/13/01 */
if( lastseglen > 0.0 ) {
	if( x < (NEGHUGE+1) || y < (NEGHUGE+1) ) { x = lastx; y = lasty; }
	lastx = Eax( x ) + lastseglen;
	if( fillmode ) {
		Emov( Eax(x), Eay(cr) ); Epath( lastx, Eay(cr) ); 
		Epath( lastx, Eay(y) ); Epath( Eax(x), Eay(y) );
		/* Ecolorfill( fillcolor ); */ /* using Efill .. scg 6/18/04 */
		Efill();
		}
	else 	Elin( lastx, Eay( y ) );
	}
else lastx = Eax(x);

if( forcelastx[0] != '\0' ) {
	lastx = Eax( Econv( X, forcelastx ) );
	if( !Econv_error() && anyvalid ) Elin( lastx, Eay( y ) );
	}


/* set YFINAL and Xfinal */
/* sprintf( numstr, numstrfmt, y ); */
Euprint( numstr, Y, y, numstrfmt );
setcharvar( "YFINAL", numstr );
Euprint( buf, X, Edx(lastx), "" );
setcharvar( "XFINAL", buf );
	





/* do points, labels, etc. */
/* ----------------------- */
for( i = 0; i < npoints; i++ ) {
	x = dat3d(i,0);
	y = dat3d(i,1);
	realrow = (int) dat3d(i,2);


	if( x < (NEGHUGE+1) || y < (NEGHUGE+1) ) continue; /* skip bad values */

	if( x < linestart && !relax_xrange ) continue; /* out of range - lo */
	if( x > linestop && !relax_xrange ) {          /* out of range - hi */
		break; 
		}

	if( clipping && !stairstep && !fillmode ) {
		/* if clipping, suppress points or labels that are outside the plotting area */
		if( x < EDXlo || x > EDXhi || y < EDYlo || y > EDYhi ) continue;
		}

	lasty = y;

	if( x >= ptlblstart && x <= ptlblstop ) {
		if( donumbers && stairstep ) {
			if( i == npoints-1 || GL_close_to( x, linestop, 0.01 ) ) {  
				double xls;
				xls = Edx(lastseglen) - Edx(0.0);
				placenum( shownums, x, x + xls, y, numstrfmt, linedetails );
				}
			else placenum( shownums, x, dat3d(i+1,0), y, numstrfmt, linedetails );
			}
		else if( donumbers && !stairstep ) 
			  placenum( shownums, x, x, y, numstrfmt, linedetails );
		if( donumbers && fillmode ) Ecolor( fillcolor ); /* scg 6/18/04 */
		}

	if( dopoints ) {
		int jj;
		/* see if this is one of the alternates.. if so use altsym rather
			than the regular symbol */
		for( jj = 0; jj < nalt; jj++ ) if( i == altlist[jj] ) break;
		if( jj != nalt ) symdet( "altsym", altsym, symcode, &radius );
		else symdet( "pointsymbol", pointsym, symcode, &radius );
		Emark( Eax(x), Eay(y), symcode, radius );
		}
	if( ptlabelfield && x >= ptlblstart && x <= ptlblstop ) {
		textdet( "ptlabeldetails", ptlabeldetails, &align, &adjx, &adjy, -4, "R", 1.0 );
		if( align == '?' ) align = 'C';
		Emov( Eax( x ) + adjx, Eay( y ) + adjy );
		Edotext( da( realrow, ptlabelfield ), align );
		}
	}


if( label[0] != '\0' ) {
        GL_varsub( label, "@YFINAL", numstr );
        textdet( "labeldetails", labeldetails, &align, &adjx, &adjy, -2, "R", 1.0 );
        if( align == '?' ) align = 'L';
        Emov( lastx+0.05+adjx, (Eay( lasty )-(Ecurtextheight*.35))+adjy );
        Edotext( label, align );
        }
 
if( legendlabel[0] != '\0' ) {
	if( fillmode ) PL_add_legent( LEGEND_COLOR, legendlabel, "", fillcolor, "", "" );
	else if( pointsym[0] != '\0' && stricmp( pointsym, "none" )!= 0 ) {
		if( tolower(legsamptyp[0]) == 's' && strlen( legsamptyp ) <= 6 )
			PL_add_legent( LEGEND_SYMBOL, legendlabel, "", pointsym, "", "" );
		else
			PL_add_legent( LEGEND_LINE+LEGEND_SYMBOL, legendlabel, "", linedetails, pointsym, "" );
		}
	else PL_add_legent( LEGEND_LINE, legendlabel, "", linedetails, "", "" );
	}

/* if( fillmode ) Ecolor( oldcolor ); */ /* restore */
return( 0 );
}

/* ----------------------- */
/* place one line plot number  */
static int
placenum( shownums, x1, x2, y, numstrfmt, linedetails )
char *shownums;
double x1, x2, y;
char *numstrfmt, *linedetails;
{
char numstr[20];
int align;
double adjx, adjy;

/* change to text color, size, etc.. */
textdet( "numbers", shownums, &align, &adjx, &adjy, -4, "R", 1.0 );
if( align == '?' ) align = 'C';
Emov( Eax( (x1+x2)/2.0 ) + adjx, Eay(y)+0.02+adjy );
/* sprintf( numstr, numstrfmt, y ); */
Euprint( numstr, 'y', y, numstrfmt );
Edotext( numstr, align );
linedet( "linedetails", linedetails, 1.0 );
Emovu( x1, y ); /* restore old position for drawing the curve.. */
return( 0 );
}

/* ------------------------- */
static int
dblcompare( a, b )
const void *a, *b;

/* dblcompare( f, g )
 * double *f, *g;
 */  /* changed to eliminate gcc warnings  scg 5/18/06 */

{
double *f, *g;
f = (double *)a;
g = (double *)b;

if( *f > *g ) return( 1 );
if( *f < *g ) return( -1 );
return( 0 );
}


/* ======================================================= *
 * Copyright 1998-2005 Stephen C. Grubb                    *
 * http://ploticus.sourceforge.net                         *
 * Covered by GPL; see the file ./Copyright for details.   *
 * ======================================================= */


syntax highlighted by Code2HTML, v. 0.9.1