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

/* PROC AXIS - Draw an X or Y axis.  astart parameter allows areadef xaxis.* or yaxis.* parameters */

/* Coded for Y axis.. axes are logically flipped when drawing an X axis */

#include "pl.h"

#define INCREMENTAL 1
#define HERE 2
#define FROMFILE 3
#define FROMDATA 4
#define FROMCATS 5
#define MONTHS 100

int
PLP_axis( xory, astart )
char xory; /* either 'x' or 'y' */
int astart;
{
int i;
char attr[NAMEMAXLEN], val[256];
char *line, *lineval;
int nt, lvp;
int first;

int stat;
int align;
double adjx, adjy;

char buf[256];
int j;
char opax;
double y;
double f;
double pos; /* distance of axis in absolute units from scale-0 */
double stubstart, stubstop; /* start and stop of stubs and tics in data units */
double inc;
char tics[256]; /* details of tic lines */
double ticin, ticout; /* length of tic into plot area and outward */
char mtics[256]; /* details of minor tic lines */
double minorticinc;	/* inc for minor tics */
double mticin, mticout; /* length of minor tic into plot area and outward */
char axisline[256]; /* axis line details */
char stubdetails[256];    /* stub text details and on/off */
char txt[256]; /* general */
char stubformat[80];	
int isrc;
FILE *stubfp;
double axlinestart, axlinestop;
double max, min;
char axislabel[300];
char axislabeldet[256];
double axislabelofs;
int stubreverse;
char grid[256];
int vertstub;
int irow;
double ofsx, ofsy;
char stubomit[256];
int mon, day, yr;

int stubdf1, stubdf2, nstubdf;
int selfloc;
char filename[256];
double incamount, ticincamount;
char incunits[50], ticincunits[50], minorticunits[50];
int ibb;
double overrun;
int bigbuflen;
int doingtics;
int stubrangegiven;
int stubreverse_given;
int specialunits;
int doinggrid;
double stubslide;
char scaleunits[30];
char gridskip[20];
char scalesubtype[20];
char autoyears[20];
int firsttime;
double ticslide;
int revsign;
char glemins[40], glemaxs[40];
double glemin, glemax;
char gbcolor1[COLORLEN], gbcolor2[COLORLEN];
double gbylast;
int gbstate;
double stubcull, prevstub;
int stubevery;
int forlen;
char clickmap;
double cmylast, cmemin, cmemax;
char cmemins[40], cmemaxs[40];
char cmvalfmt[80], cmtxt[100];
int logx, logy, stubexp;
int stublen;
char autodays[40], automonths[40];
char labelurl[256], labelinfo[MAXTT];
double stubmult;
double stubmininc;
char firststub[80], laststub[80];
char stubsubpat[80], stubsubnew[80];
int sanecount, stubhide;
int curyr, curday, curmon;
int circuit_breaker_disable;
int axis_arrow;
double arrowheadsize;
char nearest[30];
char stubround[30];
int prec;

TDH_errprog( "pl proc axis" );

if( xory == 'x' ) opax = 'y';
else if( xory == 'y' ) opax = 'x';
else { Eerr( 301, "axis: bad xory", "" ); xory = 'x'; opax = 'y'; }



/* initialize */
min = Elimit( xory, 'l', 's' );
max = Elimit( xory, 'h', 's' );
pos = Elimit( opax, 'l', 'a' ); /* location will be at minima of other axis */
stubstart = axlinestart = min; 
stubstop = axlinestop = max; 
ticin = 0;
ticout = 0.07;
strcpy( axisline, "" );
strcpy( tics, "" );
strcpy( mtics, "none" );
minorticinc = 0.0;
mticin = 0;
mticout = 0.03;
strcpy( stubformat, "" );
strcpy( PL_bigbuf, "" );
strcpy( stubdetails, "" );
strcpy( axislabel, "" );
strcpy( axislabeldet, "" );
axislabelofs = 0.4;
stubreverse = 0;
strcpy( grid, "none" );
/* isrc = INCREMENTAL; */
isrc = 0;
incamount = 0.0;
ticincamount = 0.0;
vertstub = 0;
nstubdf = 0;
strcpy( stubomit, "" );
doingtics = 1;
selfloc = 0;
stubrangegiven = 0;
stubreverse_given = 0;
doinggrid = 0;
stubslide = 0.0;
ticslide = 0.0;
strcpy( gridskip, "" );
strcpy( autoyears, "" );
revsign = 0;
strcpy( glemins, "min" ); strcpy( glemaxs, "max" );
strcpy( gbcolor1, "" );
strcpy( gbcolor2, "white" );
stubcull = 0.0;
stubevery = 1;
clickmap = 0;
strcpy( cmemins, "min" ); strcpy( cmemaxs, "max" );
strcpy( cmvalfmt, "" );
stubexp = 0; stublen = 0;
strcpy( autodays, "" ); strcpy( automonths, "" );
strcpy( labelurl, "" ); strcpy( labelinfo, "" );
stubmult = 1.0; stubmininc = 0.0;
strcpy( firststub, "" ); strcpy( laststub, "" );
strcpy( stubsubpat, "" ); strcpy( stubsubnew, "" );
stubhide = 0;
curyr = curmon = curday = 0;
circuit_breaker_disable = 0;
axis_arrow = 0;
arrowheadsize = 0.15;
strcpy( nearest, "" );
strcpy( stubround, "" );

Egetunits( xory, scaleunits );  /* moved from below - scg 1/27/05 */

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

	 /* screen out areadef attributes for the other axis.. */
	if( GL_slmember( attr, "axisline tic* minortic*" )) ;
	else if( astart > 0 && strnicmp( &attr[1], "axis.", 5 )!=0 ) continue;
	else if( astart > 0 && tolower(attr[0]) != xory ) continue; 

	if( stricmp( &attr[astart], "label" )==0 ) {
		strcpy( axislabel, lineval );
		convertnl( axislabel );
		}
	else if( stricmp( &attr[astart], "labelurl" )==0 ) strcpy( labelurl, val );
	else if( stricmp( &attr[astart], "labelinfo" )==0 ) strcpy( labelinfo, lineval );
        else if( stricmp( attr, "labelinfotext" )==0 ) {
                if( PLS.clickmap ) { getmultiline( "labelinfotext", lineval, MAXTT, labelinfo ); }
                }


	else if( stricmp( &attr[astart], "stubs" )==0 || stricmp( &attr[astart], "selflocatingstubs" )==0 ) {

		if( stricmp( &attr[astart], "selflocatingstubs" )==0 ) selfloc = 1;	
		else selfloc = 0;

		if( strnicmp( val, "inc", 3 )==0 || GL_slmember( val, "dat*matic" )) {
			isrc = INCREMENTAL;
			strcpy( incunits, "" );
			nt = sscanf( lineval, "%*s %lf %s", &incamount, incunits );
			if( nt < 1 || incamount == 0.0 ) {
				if( strncmp( scaleunits, "date", 4 )== 0 || strcmp( scaleunits, "time" )==0 ) {
					DT_reasonable( scaleunits, min, max, &incamount, incunits, stubformat, automonths, 
						autoyears, autodays, &minorticinc, minorticunits, nearest );
					}
				else incamount = 0.0;
				}
			}
		else if( stricmp( val, "minmaxonly" )==0 ) {
			isrc = INCREMENTAL;
			incamount = max - min;
			}
		else if( stricmp( val, "minonly" )==0 ) {
			isrc = INCREMENTAL;
			incamount = (max - min) * 2.0;
			}
		else if( stricmp( val, "maxonly" )==0 ) {
			isrc = INCREMENTAL;
			stubstart = max;
			incamount = (max - min) * 2.0;
			}
		else if( stricmp( val, "file" )==0 ) {
			isrc = FROMFILE;
			strcpy( filename, "" );
			nt = sscanf( lineval, "%*s %s", filename );
			if( nt < 1 ) {
				Eerr( 2796, "usage:   stub: file filename", "" );
				isrc = INCREMENTAL;
				}
			}
		else if( strnicmp( val, "datafield", 9 ) ==0 )  {
			char fnames[2][50];
			isrc = FROMDATA;
			for( j = 0, forlen = strlen( lineval ); j < forlen; j++ ) /* allow = and , */
				if( GL_member( lineval[j], "=," )) lineval[j] = ' ';
			nstubdf = sscanf( lineval, "%*s %s %s", fnames[0], fnames[1] );
			if( nstubdf < 1 ) { 
				Eerr( 2795, "usage:   stub: datafields=a,[b] where a and b are dfields", "" );
				isrc = INCREMENTAL;
				}
			if( nstubdf > 0 ) stubdf1 = fref( fnames[0] );
			if( nstubdf == 2 ) stubdf2 = fref( fnames[1] );
			}

		else if( stricmp( val, "categories" )==0 || stricmp( val, "usecategories" )==0 ) isrc = FROMCATS;
			
		else if( stricmp( val, "list" )==0 ) {
			isrc = HERE;
			i = 0;
			GL_getchunk( buf, lineval, &i, " \t" );
			while( GL_member( lineval[i], " \t" ) ) i++;
			strcpy( PL_bigbuf, &lineval[i] );
			convertnl( PL_bigbuf );
			}
		else if( stricmp( val, "none" )==0 ) isrc = 0;
		else 	{
			isrc = HERE;
			if( stricmp( val, "text" ) == 0 ) {
				getmultiline( "stubs", "", MAXBIGBUF, PL_bigbuf );
				}
			else 	{
				fprintf( PLS.errfp, 
				  "warning: proc axis assuming multiline stub text even though 'text' keyword not given\n" );
				getmultiline( "stubs", lineval, MAXBIGBUF, PL_bigbuf );
				}
			}
		}
			

	else if( stricmp( &attr[astart], "stubformat" )==0 ) strcpy( stubformat, lineval );

	else if( stricmp( &attr[astart], "stubdetails" )==0 ) strcpy( stubdetails, lineval );

	else if( stricmp( &attr[astart], "stubrange" )==0 ) {
		getrange( lineval, &stubstart, &stubstop, xory, min, max );
		stubrangegiven = 1;
		}
	else if( stricmp( &attr[astart], "stubround" )==0 ) strcpy( stubround, val );  /* added 5/29/06 scg */

	else if( stricmp( &attr[astart], "stubreverse" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 )stubreverse = 1;
		else stubreverse = 0;
		stubreverse_given = 1;
		}

	else if( stricmp( &attr[astart], "stubvert" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) vertstub = 1;
		else vertstub = 0;
		}

	else if( stricmp( &attr[astart], "stubomit" )==0 ) strcpy( stubomit, lineval );

	else if( stricmp( &attr[astart], "stubslide" )==0 ) Elenex( val, xory, &stubslide );

	else if( stricmp( &attr[astart], "stubexp" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) stubexp = 1;
		else if( stricmp( val, "exp-1" )==0 ) stubexp = 2;
                else stubexp = 0;
		}

	else if( stricmp( &attr[astart], "stublen" )==0 ) stublen = atoi( val );

	else if( stricmp( &attr[astart], "stubmult" )==0 ) stubmult = atof( val );

	else if( stricmp( &attr[astart], "stubmininc" )==0 ) stubmininc = atof( val );

	else if( stricmp( &attr[astart], "stubhide" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) stubhide = 1;
		else stubhide = 0;
		}

	else if( stricmp( &attr[astart], "ticslide" )==0 )  Elenex( val, xory, &ticslide );

	else if( stricmp( &attr[astart], "labeldetails" )==0 ) strcpy( axislabeldet, lineval );

	else if( stricmp( &attr[astart], "labeldistance" )==0 ) {
		axislabelofs = atof( val );
		if( PLS.usingcm ) axislabelofs /= 2.54;
		}

	else if( stricmp( &attr[astart], "location" )==0 ) {
		Eposex( val, opax, &f );
		pos = f;
		}


	else if( stricmp( &attr[astart], "axisline" )==0  ) {
		if( strnicmp( val, "no", 2 )==0 ) strcpy( axisline, "none" );
		else strcpy( axisline, lineval );
		}

	else if( stricmp( &attr[astart], "axislinerange" )==0 ) {
		getrange( lineval, &axlinestart, &axlinestop, xory, min, max );
		}

	else if( stricmp( &attr[astart], "tics" )==0  ) {
		strcpy( tics, lineval );
		if( strnicmp( tics, "no", 2 ) != 0 ) doingtics = 1;
		else doingtics = 0;
		}

	else if( stricmp( &attr[astart], "ticincrement" )==0  ) {
		strcpy( ticincunits, "" );
		sscanf( lineval, "%lf %s", &ticincamount, ticincunits );
		}

	else if( stricmp( &attr[astart], "ticlen" )==0  ) {
		sscanf( lineval, "%lf %lf", &ticout, &ticin );
		if( PLS.usingcm ) { ticout /= 2.54; ticin /= 2.54; }
		}

	else if( stricmp( &attr[astart], "minortics" )==0 ) {
		if( strnicmp( val, "no", 2 )==0 ) { strcpy( mtics, "none" ); minorticinc = 0.0; }   /* set ticinc = 0 scg 1/28/05 */
		else strcpy( mtics, lineval );
		}
	else if( stricmp( &attr[astart], "minorticinc" )==0 ) {
		strcpy( minorticunits, "" );
		nt = sscanf( lineval, "%lf %s", &minorticinc, minorticunits );
		}

	else if( stricmp( &attr[astart], "minorticlen" )==0 ||
		stricmp( attr, "minorticlen" )==0 ) {
		sscanf( lineval, "%lf %lf", &mticout, &mticin );
		if( PLS.usingcm ) { mticout /= 2.54; mticin /= 2.54; }
		}

	else if( stricmp( &attr[astart], "grid" )==0 ) {
		strcpy( grid, lineval );
		if( strnicmp( grid, "no", 2 )!= 0 ) doinggrid = 1;
		}
	else if( stricmp( &attr[astart], "gridskip" )==0 ) strcpy( gridskip, val );
	else if( stricmp( &attr[astart], "gridlineextent" )==0 ) {
		nt = sscanf( lineval, "%s %s", glemins, glemaxs );
		}
	else if( stricmp( &attr[astart], "gridblocks" )==0 ) {
		nt = sscanf( lineval, "%s %s", gbcolor1, gbcolor2 );
		if( stricmp( gbcolor1, "no" )==0 || stricmp( gbcolor1, "none" )==0 ) strcpy( gbcolor1, "" );
		else doinggrid = 1;
		}

	else if( stricmp( &attr[astart], "stubcull" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) stubcull = 0.1;
		else if( atof( val ) > 0.0 ) stubcull = atof( val );
                else stubcull = 0.0;
		}

	else if( stricmp( &attr[astart], "autoyears" )==0 ) {
		strcpy( autoyears, val );
		if( stricmp( autoyears, "yes" )==0 || stricmp( autoyears, "y" )==0 ) strcpy( autoyears, "'yy" );
		else if( stricmp( autoyears, "no" )==0 ) strcpy( autoyears, "" );
		}

	else if( stricmp( &attr[astart], "autodays" )==0 ) {
		strcpy( autodays, val );
		if( stricmp( autodays, "yes" )==0 || stricmp( autodays, "y" )==0 ) strcpy( autodays, "Mmmdd" );
		else if( stricmp( autodays, "no" )==0 ) strcpy( autodays, "" );
		}

	else if( stricmp( &attr[astart], "automonths" )==0 ) {
		strcpy( automonths, val );
		if( stricmp( automonths, "yes" )==0 || stricmp( automonths, "y" )==0 ) strcpy( automonths, "Mmm" );
		else if( stricmp( automonths, "no" )==0 ) strcpy( automonths, "" );
		}


	else if( stricmp( &attr[astart], "signreverse" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 )revsign = 1;
                else revsign = 0;
		}

	else if( stricmp( &attr[astart], "stubevery" )==0 ) stubevery = atoi( val );

	else if( stricmp( &attr[astart], "firststub" )==0 ) {
		strcpy( firststub, lineval );
		if( strcmp( firststub, "\"\"" )==0 ) strcpy( firststub, " " );
		}
	else if( stricmp( &attr[astart], "laststub" )==0 ) {
		strcpy( laststub, lineval );
		if( strcmp( laststub, "\"\"" )==0 ) strcpy( laststub, " " );
		}
	else if( stricmp( &attr[astart], "stubsubpat" )==0 ) strcpy( stubsubpat, lineval );
	else if( stricmp( &attr[astart], "stubsubnew" )==0 ) {
		strcpy( stubsubnew, lineval );
		if( strcmp( stubsubnew, "\"\"" )==0 ) strcpy( stubsubnew, " " );
		}

	else if( stricmp( &attr[astart], "clickmap" )==0 ) {
		if( strnicmp( val, "xy", 2 )==0 ) {
			if( xory == 'x' ) clickmap = 3;
			else clickmap = 4;
			}
		else 	{
			if( xory == 'x' ) clickmap = 1;
			else clickmap = 2;
			}
		}
	else if( stricmp( &attr[astart], "clickmapextent" )==0 ) nt = sscanf( lineval, "%s %s", cmemins, cmemaxs );
	else if( stricmp( &attr[astart], "clickmapvalformat" )==0 ) strcpy( cmvalfmt, lineval );
	else if( stricmp( &attr[astart], "nolimit" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) circuit_breaker_disable = 1;
		}
	else if( stricmp( &attr[astart], "arrow" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) axis_arrow = 1;
		}
	else if( stricmp( &attr[astart], "arrowheadsize" )==0 ) {
		arrowheadsize = atof( val );
		if( PLS.usingcm ) arrowheadsize /= 2.54;
		}
	else if( astart == 0 || strnicmp( attr, "xaxis.", 6 )==0 || 
	       strnicmp( attr, "yaxis.", 6 )==0 )
			Eerr( 301, "axis attribute not recognized", attr );
	}



/* check for degenerate cases and control overrides.. */
if( !scalebeenset() )
         return( Eerr( 51, "No scaled plotting area has been defined yet w/ proc areadef", "" ) );

if( stubstart < min || stubstart > max ) stubstart = min;
if( stubstop > max || stubstop < min ) stubstop = max;
if( axlinestart < min || axlinestart > max ) axlinestart = min;
if( axlinestop > max || axlinestop < min ) axlinestop = max;
if( stubevery == 0 ) stubevery = 1;




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

/* Egetunits( xory, scaleunits ); */ /* moved up - scg 1/27/05 */


/* do the label.. easier if we do it before the Eflip below.. */
if( axislabel[0] != '\0' ) {
	double xpos, ypos, txtsize;
	textdet( "labeldetails", axislabeldet, &align, &adjx, &adjy, 0, "R", 1.0 );
	txtsize = (double)(strlen( axislabel )) * Ecurtextwidth;

	if( xory == 'x' ) {
		xpos =  ( EXlo+ (( EXhi - EXlo ) / 2.0 )) + adjx;
		ypos = (pos - axislabelofs) + adjy;
		Emov( xpos, ypos );
		Ecentext( axislabel );
		if( PLS.clickmap && ( labelurl[0] != '\0' || labelinfo[0] != '\0' ) )
			clickmap_entry( 'r', labelurl, 0, xpos - (txtsize/2.0), ypos, 
				xpos + (txtsize/2.0), ypos+Ecurtextheight, 1, 0, labelinfo );
		}
	else if( xory == 'y' ) {
		xpos = (pos - axislabelofs) + adjx;
		ypos = (EYlo + (( EYhi-EYlo ) / 2.0 )) + adjy;
		Emov( xpos, ypos );
		Etextdir( 90 );
		Ecentext( axislabel );
		Etextdir( 0 );
		if( PLS.clickmap && ( labelurl[0] != '\0' || labelinfo[0] != '\0' ) )
			clickmap_entry( 'r', labelurl, 0, xpos-Ecurtextheight, ypos - (txtsize*0.6), 
				xpos, ypos + (txtsize*0.6), 0, 0, labelinfo );
		}
	}


if( xory == 'x' ) Eflip = 1; /* reverse sense of x and y for draw operations */



/* avoid "circuit breaker" message when just doing label and/or line..  added scg 5/24/06 */
if( isrc == 0 && !doingtics && !doinggrid ) goto SKIPLOOP;   


/* --------------------- */
/* tics and axis preliminaries.. */
/* --------------------- */

inc = 1.0;
overrun = inc / 10.0; 
specialunits = 0;



/* ----------------- */
/* helpful overrides */
/* ----------------- */


/* if user didn't specify stub range, and 
   if isrc indicates text stubs, and not doing stubreverse, start at 1.0 */
if( !stubrangegiven && !stubreverse && !selfloc &&
	( isrc == HERE || isrc == FROMFILE || isrc == FROMDATA ) ) stubstart = 1.0;


/* if doing Y axis with text stubs, and user didn't specify stubreverse: no, 
	reverse the stubs */
if( xory == 'y' && !stubreverse_given && stubreverse == 0 &&
	( isrc == HERE || isrc == FROMFILE || isrc == FROMDATA ) && !selfloc ) {  /* && !selfloc added scg 3/19/03 */
	stubreverse = 1;
	if( !stubrangegiven ) stubstop = max - 1.0;
	}

if( stubformat[0] != '\0' && isrc == 0 )
	Eerr( 2749, "warning, stubformat but no stubs specification", "" );

/* if( strncmp( scaleunits, "date", 4 )==0 && stubformat[0] == '\0' ) 
 *	Eerr( 319, "warning, no stubformat specified.. using current notation.. for better results try 'stubs: datematic'", "" );
 */
	

if( minorticinc > 0.0 && strnicmp( mtics, "no", 2 ) == 0 ) strcpy( mtics, "yes" );


/* compute abs grid line extent - added scg 11/22/00 */
if( doinggrid ) { 
	Eposex( glemins, opax, &glemin );
	Eposex( glemaxs, opax, &glemax );
	/* Eposex( "min", Y, &gbylast ); changed to below 5/17/01 */
	Eposex( "min", xory, &gbylast );
	gbstate = 0;
 	} 

if( PLS.clickmap && clickmap ) {
	Eposex( cmemins, opax, &cmemin ); 
	Eposex( cmemaxs, opax, &cmemax );
	Eposex( "min", xory, &cmylast );
	}


/* stubround stuff..   added scg 5/29/06 */
if( stubround[0] != '\0' ) {    /* stubround is useful when user requires min and max to be at exact oddball locations, 
				 * but want the stubs to fall on round locations.  Shouldn't come into play with 'datematic' 
				 * since dm sets min and max to round (nearest) locations. */
	char minval[40], maxval[40];
	double ninc;
	if( GL_smember( scaleunits, "date datetime time" )) {
		Euprint( buf, xory, stubstart, "" );
		PLP_findnearest( buf, buf, xory, stubround, minval, maxval );  
		stubstart = Econv( xory, maxval );
		}
	else if( stricmp( stubround, "useinc" )==0 ) {   /* base it on whatever the axis increment value is/will be.. */
		if( incamount > 0.0 ) stubstart = GL_numgroup( stubstart, incamount, "high" );
		else	{
			PL_defaultinc( min, max, &ninc );
			stubstart = GL_numgroup( stubstart, ninc, "high" );
			}
		}
	else if( GL_goodnum( stubround, &prec )) stubstart = GL_numgroup( stubstart, atof( stubround ), "high" );
	}

	

/* render minor tics.. */
if( isrc == INCREMENTAL && strnicmp( mtics, "no", 2 )!= 0 ) {
	/* unit conversions.. */
	if( stricmp( scaleunits, "time" )==0 && strnicmp( minorticunits, "hour", 4 ) ==0 ) {
		strcpy( minorticunits, "" );
		minorticinc = minorticinc * 60;
		}
	else if( stricmp( scaleunits, "datetime" )==0 && 
		(strnicmp( minorticunits, "hour", 4 ) ==0 || strnicmp( minorticunits, "minute", 3 )==0 )) {
		double winsize, mm; /* window size in hours */
		DT_getwin( &winsize );
		mm = ((24.0/winsize) / 24.0);
		if( strnicmp( minorticunits, "minute", 3 )==0 ) mm /= 60.0;
		minorticinc *= mm;
		/* minorticinc = minorticinc / winsize;
		 * if( strnicmp( minorticunits, "minute", 3 )==0 ) minorticinc = minorticinc / 60.0;
		 * strcpy( minorticunits, "" );
		 */
		}
	else if( stricmp( scaleunits, "time" )==0 && strnicmp( minorticunits, "second", 3 ) ==0 ) {
		strcpy( minorticunits, "" );
		minorticinc = minorticinc / 60.0;
		}
	else if( stricmp( scaleunits, "date" )==0 && strnicmp( minorticunits, "year", 4 ) ==0 ) {
		strcpy( minorticunits, "month" );
		minorticinc = minorticinc * 12;
		}
	else if( stricmp( scaleunits, "date" )==0 && strnicmp( minorticunits, "month", 5 ) ==0 ) {
		strcpy( minorticunits, "month" );
		minorticinc = minorticinc * (365.25/12.0);
		}

	linedet( "minortics", mtics, 0.5 );
	if( minorticinc <= 0.0 ) {
		Eerr( 2340, "warning, minorticinc must be specified if doing minor tics", "" );
		minorticinc = 1.0;
		}
	y = stubstart;
	while( 1 ) {
		Emov( pos-mticout, Ea( Y, y ) ); 
		Elin( pos+mticin, Ea( Y, y ) );
		y += minorticinc;
		if( y >= stubstop ) break;
		}
	} 




/* --------------------------------- */
/* preliminaries based on stubs type */
/* --------------------------------- */

if( isrc == HERE ) bigbuflen = strlen( PL_bigbuf );

if( isrc == FROMFILE ) {  /* if taking from file, read the file into PL_bigbuf */
	stubfp = fopen( filename, "r" );
	if( stubfp == NULL ) {
		Eerr( 303, "warning, cannot open specified stub file.. using incremental stubs.", filename );
		isrc = INCREMENTAL; /* fallback */
		}
	else	{
		i = 0;
		while( fgets( buf, 128, stubfp ) != NULL ) {
			strcpy( &PL_bigbuf[i], buf );
			i += strlen( buf );
			}
		fclose( stubfp );
		bigbuflen = i;
		}
	}

if( isrc == FROMDATA ) {
	if( nstubdf >= 1 ) stubdf1--;  /* off-by-one */

	if( nstubdf >= 2 ) { 
		stubdf2--;
		nstubdf = 2;
		}
	}

if( isrc == FROMCATS ) selfloc = 0; 	/* for rendering purposes don't treat category stubs as self locating */


if( isrc == 0 && (doingtics || doinggrid) ) {   /* no stubs but doing tics or doing grid */
	strcpy( incunits, ticincunits );
	incamount = ticincamount;
	} 

/* incunit conversions.. */
if( stricmp( scaleunits, "time" )==0 && strnicmp( incunits, "hour", 4 ) ==0 ) {
	strcpy( incunits, "" );
	incamount = incamount * 60.0;
	}
else if( stricmp( scaleunits, "datetime" )==0 && 
	( strnicmp( incunits, "hour", 4 ) ==0 || strnicmp( incunits, "minute", 3 )==0 )) {
	double winsize; /* window size in hours */
	/* must be relative to window size.. */
	DT_getwin( &winsize );
	incamount = incamount / winsize;
	if( strnicmp( incunits, "minute", 3 )==0 ) incamount = incamount / 60.0;
	strcpy( incunits, "" );
	/* incamount = incamount / 24.0; */
	}
else if( stricmp( scaleunits, "datetime" )==0 && strnicmp( incunits, "minute", 3 ) ==0 ) {
	strcpy( incunits, "" );
	incamount = (incamount / 24.0) / 60.0;
	}
else if( stricmp( scaleunits, "time" )==0 && strnicmp( incunits, "second", 3 ) ==0 ) {
	strcpy( incunits, "" );
	incamount = incamount / 60.0;
	}
else if( stricmp( scaleunits, "date" )==0 && strnicmp( incunits, "year", 4 ) ==0 ) {
	strcpy( incunits, "month" );
	incamount = incamount * 12;
	}
else if( strnicmp( incunits, "month", 5 )!=0 && 
	atof( incunits ) == 0.0 ) strcpy( incunits, "" ); /* prevent racecon */


Egetunitsubtype( xory, scalesubtype );

/* yymm (etc) implies inc unit of "months" (yy implies years) */
if( scalesubtype[0] != '\0' ) {
	if( GL_slmember( scalesubtype, "yymm yy?mm yyyy?mm mm?yy mm?yyyy" ) && incunits[0] == '\0'  ) 
			strcpy( incunits, "month" );
	if( stricmp( scalesubtype, "yy" )==0 && incunits[0] == '\0' ) 
			strcpy( incunits, "year" );
	}


if( isrc == INCREMENTAL || ( isrc == 0 && (doingtics || doinggrid )) ) {

	/* for special units, initialize */
	if( strnicmp( incunits, "month", 5 )==0 ) {
		long l;
		if( ! GL_smember( scaleunits, "date datetime" ))  /* changed to include datetime as well as date - scg 8/11/05 */
			return( Eerr( 2476, "month increment only valid with date or datetime scale type", "" ) );
		specialunits = MONTHS;
		selfloc = 0; /* for rendering purposes don't treat month stubs as self locating.. */
		/* do the following to get starting m, d, y.. */
		Euprint( buf, xory, stubstart, "" );
		stat = DT_jdate( buf, &l );
		DT_getmdy( &mon, &day, &yr ); 
		if( incamount > 0.0 ) inc = incamount;
		else inc = 1; 	/* added scg 3/29 */
		}

	else if( stricmp( scaleunits, "datetime" )==0 && 
			strnicmp( incunits, "hour", 4 ) ==0 ) {
		inc = incamount / 24.0;
		}

		
		
	else	{
		if( incamount > 0.0 ) {
			if( incunits[0] == '\0' ) inc = incamount;
			else {
				double ftest;
				sscanf( incunits, "%lf", &ftest );
				inc = incamount * ftest;
				/* inc = incamount * atof( incunits ); repl scg 5/23/00 */
				}
			}
		else	{
			/* try to be smart about choosing default inc */
			PL_defaultinc( min, max, &inc );
			if( stubevery > 1 ) inc = inc * (double)stubevery;
			if( inc < stubmininc ) inc = stubmininc;
			}
		overrun = inc / 10.0;
		}
	}

if( xory == 'x' ) setfloatvar( "XINC", inc );
else if( xory == 'y' ) setfloatvar( "YINC", inc );

/* log mode */
logx = logy = 0;
if( Escaletype_x == E_LOG || Escaletype_x == E_LOGPLUS1 ) logx = 1;
if( Escaletype_y == E_LOG || Escaletype_y == E_LOGPLUS1 ) logy = 1;

/* sanity check.. added scg 8/7/00 */
if( inc <= 0.0 ) return( Eerr( 2705, "axis increment is zero or negative", "" ));





/* ---------------------------------------------- */
/* render the stubs and/or tics and/or grid lines.. */
/* ---------------------------------------------- */


if( stubreverse ) y = stubstop;
else y = stubstart;
ibb = 0;
irow = 0;
if( vertstub ) Etextdir(90);
prevstub = NEGHUGE;


firsttime = 1;


    
for( sanecount = 0; sanecount < 2000; sanecount++ ) {

	if( circuit_breaker_disable ) sanecount = 0;

	strcpy( txt, "" );

	/* if( isrc == 0 ) goto DOTIC;  */ /* no stubs */

	if( isrc == INCREMENTAL ) {

 		/* exception for log 0.0 */
                if( y <= 0.0 && ((xory == 'x' && logx ) || (xory == 'y' && logy ) ) ) goto NEXTSTUB;
                 
		}

	if( isrc == INCREMENTAL ) {
		double yy, fabs(), ftest, exp();
		if( revsign && fabs(y) > 0.0001 ) yy = y * -1;
		else yy = y;
		if( stubexp == 1 ) yy = exp( yy );
		else if( stubexp == 2 ) yy = exp( yy ) - 1;

		/* when generating incremental axes moving from negative to positive, for zero sprintf sometimes
           	gives -0.00 or very tiny values like -5.5579e-17.  The following is a workaround.. scg 7/5/01 */
		/* if( stubstart < 0.0 && stubstop > 0.0 && yy < 0.0000000000001 && yy > -0.0000000000001 ) yy = 0.0;  */
		/* 5/18/06 scg - problem still occurring.. adjusting to: */
		if( stubstart < 0.00001 && stubstop >= 0.0 && yy < 0.000000001 && yy > -0.000000001 ) yy = 0.0; 

		if( stubmult != 1.0 ) yy = yy * stubmult; /* added scg 10/2/03 */

		nt = sscanf( incunits, "%lf", &ftest );
		if( nt > 0 ) Euprint( txt, xory, yy/ftest, stubformat );
		else Euprint( txt, xory, yy, stubformat );
		if( PLS.clickmap && clickmap ) Euprint( cmtxt, xory, yy, cmvalfmt );
		}

	if( isrc == HERE || isrc == FROMFILE ) {
		if( ibb >= bigbuflen ) break;
		GL_getseg( txt, PL_bigbuf, &ibb, "\n" );
		if( PLS.clickmap && clickmap ) strcpy( cmtxt, txt );
		}
		
	if( isrc == FROMDATA ) {
		if( irow >= Nrecords ) break;
		if( ( irow % stubevery ) != 0 ) { irow++; goto NEXTSTUB; }
		if( nstubdf == 1 )
			sprintf( txt, "%s", da( irow, stubdf1 ) );
		else if( nstubdf == 2 ) 
			sprintf( txt, "%s %s", da( irow, stubdf1 ), da( irow, stubdf2 ) );
		if( PLS.clickmap && clickmap ) strcpy( cmtxt, txt );
		irow++;
		}

	if( isrc == FROMCATS ) {
		stat = PL_getcat( xory, irow, txt, 255 );
		if( stat ) break;  /* reached end of cat list */
		if( ( irow % stubevery ) != 0 ) { irow++; goto NEXTSTUB; }
		y = Econv( xory, txt ); /* error checking not needed */
		if( PLS.clickmap && clickmap ) strcpy( cmtxt, txt );
		irow++;
		}

	/* special units.. */
	if( specialunits == MONTHS ) {
		/* put a stub at current mon/year */
		DT_makedate( yr, mon, day, "", buf );
		y = Econv( xory, buf );
		if( Econv_error() ) { 
			Eerr( 9675, "warning, error on date conversion", buf );
			break; /* goto NEXTSTUB; changed to avoid inf loop - scg 2/12/03 */
			}
		if( day != 1 ) goto NEXTSTUB; /* added scg 9/12/01 */
		if( ( y - stubstop ) > overrun ) break;
		DT_formatdate( buf, stubformat, txt ); /* buf holds date string made above */
		if( PLS.clickmap && clickmap ) DT_formatdate( buf, cmvalfmt, cmtxt ); /* buf holds date string made above */
		}

	/* autoyears */
	if( autoyears[0] != '\0' ) {
		int doit;
		doit = 0;
		if( specialunits == MONTHS ) {
			if( (firsttime && mon < 11) || yr != curyr ) doit = 1; /* mon<11 added scg 9/12/01 */
			}
		else 	{
			DT_getmdy( &mon, &day, &yr );
			if( (firsttime && (mon < 11 || day < 18 )) || yr != curyr ) doit = 1;
			}
		curyr = yr;
		if( doit ) { 
			if( strlen( autoyears ) == 2 ) sprintf( buf, "%s\n%02d", txt, yr % 100 );
			else if( strlen( autoyears ) == 3 ) sprintf( buf, "%s\n'%02d", txt, yr % 100 );
			else	{
				if( yr >= 100 ) sprintf( buf, "%s\n%d", txt, yr );
				else if( yr >= PIVOTYEAR ) sprintf( buf, "%s\n%d", txt, 1900+yr );
				else if( yr < PIVOTYEAR ) sprintf( buf, "%s\n%d", txt, 2000+yr );
				}
			strcpy( txt, buf );
			}
		}

	/* autodays */
	if( autodays[0] != '\0' ) {
		if( stricmp( scaleunits, "datetime" )!=0 ) Eerr( 9677, "warning, autodays is only valid with datetime scaling", "" );
		else	{
			double datepart, timepart;
			char dt[40];
			datepart = floor( y );
			timepart = y - datepart;
			if( firsttime || datepart != curday ) { /* render date  */
				DT_fromjul( (long)datepart, dt );
				DT_formatdate( dt, autodays, buf );
				strcat( txt, "\n" );
				strcat( txt, buf );
				}
			curday = datepart;
			}
		}

	/* automonths - can be used w/ date or datetime.. */
	if( automonths[0] != '\0' ) {
		char dt[40];
		long foo;
		int imon, iday, iyr;
		DT_fromjul( (long) y, dt );
		DT_jdate( dt, &foo ); /* to get m d y */
		DT_getmdy( &imon, &iday, &iyr );
		if( firsttime || imon != curmon ) {
			DT_formatdate( dt, automonths, buf );
			strcat( txt, "\n" );
			strcat( txt, buf );
			}
		curmon = imon;
		}
	

	/* last minute stub content overrides.. */
	if( stubsubpat[0] != '\0' && GL_wildcmp( txt, stubsubpat, strlen(stubsubpat), 0 )==0 ) strcpy( txt, stubsubnew );
	if( firststub[0] != '\0' && firsttime ) strcpy( txt, firststub );
	if( laststub[0] != '\0' && y+inc > stubstop ) strcpy( txt, laststub );

	firsttime = 0;
		




	/* by this point stub text (including any selfloc field) 
	   should be in txt and location in y.. */


	/* if selflocating, get embedded location */
	if( selfloc ) {
		i = 0;
		GL_getchunk( buf, txt, &i, " \t" );
		if( buf[0] == '\0' ) goto NEXTSTUB;
		y = Econv( xory, buf );
		if( Econv_error() ) { 
			Eerr( 9676, "warning, error on value conversion", buf );
			goto NEXTSTUB;
			}
		if( y < stubstart || y > stubstop ) continue;
		while( GL_member( txt[i], " \t" ) ) i++;
		strcpy( txt, &txt[i] ); /* now obliterate the location field */
		}


	/* out of plotting area.. */
	if( min - y > overrun ) goto NEXTSTUB;
	if( y - max > overrun ) goto NEXTSTUB;

	/* too close to previous stub.. supress.. */
	if( stubcull > 0.0 ) {
		double fabs();
		if( fabs( Ea( Y, y ) - Ea( Y, prevstub )) < stubcull && prevstub != NEGHUGE ) goto NEXTSTUB; 
		/* fixed bug.. in log space, NEGHUGE becomes 1, sometimes causing 1st stub to be culled ... scg 9/21/04 */
		else prevstub = y;
		}


	/* render grid line or block - done first so other content can be "on top" of it.. */
	/* moved here from below, grid line extent and gridblocks added - scg 11/22/00 */
	if( doinggrid ) {
		if( gridskip[0] != '\0' && y <= min && GL_smember( gridskip, "min minmax both" ) );
		else if( gridskip[0] != '\0' && y >= max && 
			GL_smember( gridskip, "max minmax both" ) );
		else if( gbcolor1[0] != '\0' ) {   /* grid blocks */
			if( gbstate == 0 ) {
				Ecblock( glemin, gbylast, glemax, Ea( Y, y ), gbcolor1, 0 );
				gbstate = 1;
				}
			else 	{
				Ecblock( glemin, gbylast, glemax, Ea( Y, y ), gbcolor2, 0 );
				gbstate = 0;
				}
			gbylast = Ea( Y, y );
			}
		else	{
			linedet( "grid", grid, 0.5 );
			Emov( glemin, Ea( Y, y ) );
			Elin( glemax, Ea( Y, y ) );
			}
		}

	if( PLS.clickmap && clickmap ) {    /* save region.. */
		double halfdown, halfup;
		if( specialunits == MONTHS ) { halfdown = halfup = ( Ea( Y, y ) - cmylast ) / 2.0; }
		else 	{
			/* handle linear & log.. (doesn't work quite right for months).. */
			halfup =  ( Ea( Y, y+inc ) - Ea( Y, y ) ) / 2.0;  
			halfdown =  ( Ea( Y, y ) - Ea( Y, y-inc ) ) / 2.0;
			/* halfdist = ( Ea( Y, y+inc ) - Ea( Y, y ) ) / 2.0; */
			}

		/* urlencode the value now.. scg 5/29/06 */
		GL_urlencode( cmtxt, buf );

		if( Eflip ) {
			clickmap_entry( 'r', buf, clickmap, (Ea(Y,y)-halfdown)+stubslide, cmemin, 
				Ea(Y,y)+halfup+stubslide, cmemax, 0, 1, "" );
			}
		else 	{
			clickmap_entry( 'r', buf, clickmap, cmemin, (Ea(Y,y)-halfdown)+stubslide, 
				cmemax, Ea(Y,y)+halfup+stubslide, 0, 2, "" );
			}
		cmylast = Ea( Y, y );
		}

	/* convert any embedded "\n" to newline.. */
	convertnl( txt );

	/* determine exact position and render stub text.. */ 
	if( isrc > 0 && !stubhide && ! GL_slmember( txt, stubomit ) ) {   /* stub can't be in omit list.. */ 
		textdet( "stubdetails", stubdetails, &align, &adjx, &adjy, -2, "R", 1.0 );
		if( vertstub && xory == 'x' ) { 
			if( align == '?' ) align = 'R';
			ofsx = adjx + (ticout * -2.0) ;
			ofsy = (adjy + 0.04) + stubslide;
			}
		else if( xory == 'y' ) {
			if( align == '?' ) align = 'R';
			ofsx = (adjx + -0.15) /* + stubslide*/;
			/* ofsy = adjy + ( Ecurtextheight * -0.4 ); */
			ofsy = adjy + ( Ecurtextheight * -0.3 ) + stubslide; 
			}
		else if( xory == 'x' ) {
			if( align == '?' ) align = 'C';
			ofsx = adjx + (Ecurtextheight + ticout)*-1.0 ;
			ofsy = adjy + stubslide;
			}
		
		/* render text..   but don't do last stub if it is past range.. */
		if( ( (Ea(Y, y)+ofsy)  - Ea(Y, stubstop ) <= overrun ) || stubreverse ) {  
			Emov( pos+ofsx, Ea( Y, y)+ofsy ); 
			if( stublen && txt[stublen] != '\0' ) { txt[stublen] = '.'; txt[stublen+1] = '.'; txt[stublen+2] = '\0'; }
			Edotext( txt, align );
			}
		}



	/* render major tic mark  */
	if( doingtics ) {
		linedet( "tics", tics, 0.5 );
		Emov( pos-ticout, Ea( Y, y) + ticslide );  
		Elin( pos+ticin, Ea( Y, y ) + ticslide );
		}

	/* render grid line - was here */


	NEXTSTUB:

	/* increment stub location.. */

	if( specialunits == MONTHS ) {    /* increment to next */
		int newmon;

		mon += inc;

		/* wrap around year.. */
		newmon = ((mon-1) % 12 ) +1;
		yr += ((mon-1) / 12);
		mon = newmon;
		
		/* fprintf( stderr, "[mon=%d yr=%d]", mon, yr ); */

		day = 1;
		}

	else if( ! selfloc ) {
		if( stubreverse ) {
			y -= inc;
			if( stubstart - y > overrun ) break;
			}
		else	{
			y += inc;
			if( ( y - stubstop ) > overrun ) break;
			}
		}
	}

if( vertstub ) Etextdir(0);


SKIPLOOP:

/* make the line..  do it last of all for appearance sake.. */
if( stricmp( axisline, "none" )!= 0 ) {
	linedet( "axisline", axisline, 0.5 );
	Emov( pos, Ea( Y, axlinestart ) ); Elin( pos, Ea( Y, axlinestop ) );
	if( axis_arrow ) PLG_arrow( pos, Ea( Y, axlinestop), pos, Ea( Y, axlinestop)+(arrowheadsize*2.0), arrowheadsize, 0.3, Ecurcolor );
	}


Eflip = 0;

if( sanecount >= 2000 ) return( Eerr( 5729, "warning, too many stubs/major tics, circuit breaker tripped", "" ));

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