plt - Software for 2D Plots 2.5

File: <base>/plt/classic/axis.c (10,829 bytes)
/*	plt/axis.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	"axis.h"
#include	"figs.h"

static void MinmaxSetup();
static void AxisSetup();
static void LinearTicSetup();
static void GridSetup();
static Ptype TicDrawAxis();
static void TicLabel();

#define		NO_TIC		0
#define		IN_OUT_TIC	01
#define		IN_TIC		02
#define		OUT_TIC		03
#define		IN_OUT_MASK	03
#define		GRID		04
#define		SYMMETRIC	010
#define		SUB_GRID	020
#define		XGRID		040
#define		YGRID		0100

static	int	gtype = OUT_TIC;


static void	PROTO( AxisInitOne, (AxisPtr,Mode) );


void	AxisInit( mode )
Mode	mode;
{
	xa.name = 'x';
	AxisInitOne( &xa );
	ya.name = 'y';
	AxisInitOne( &ya );

	xmin = ymin = FHUGE;
	xmax = ymax = -FHUGE;
	xwmins = xwmaxs = ywmins = ywmaxs = FDEFAULT;
	gridfg  = "P12,W.1,Ldot";
	gridtype = "out";
}

static void	AxisInitOne( a, mode )
AxisPtr	a;
Mode	mode;
{
	a->min = a->max = a->cr = FDEFAULT;
	a->aoff = a->mlt = a->tic = a->tmark = FDEFAULT;
	a->tscl = 1;
	a->skp = DEFAULT;
	a->pfm = a->lbl = a->base = 0;	
	a->mode = 077777;
	a->logflg = a->rev = NO;
	a->scl = a->off = 0;
	a->acchi = a->acclo = 0.2;
	a->numfg = a->lblfg = a->extra = 0;
	a->lo = a->hi = 0;
	a->this = (a->name == 'x') ? X : Y;
	a->other = (a->name == 'x') ? Y : X;
}


/*********************************************************************/


SetupAxes()
{
char	xtitle[200];

	xwmin = p->xfull * ((xwmins == DEFAULT) ? p->xwmins : xwmins);
	xwmax = p->xfull * ((xwmaxs == DEFAULT) ? p->xwmaxs : xwmaxs);
	ywmin = p->yfull * ((ywmins == DEFAULT) ? p->ywmins : ywmins);
	ywmax = p->yfull * ((ywmaxs == DEFAULT) ? p->ywmaxs : ywmaxs);

	xfract = (double)(xwmax - xwmin)/p->xfull/(p->xwmaxs-p->xwmins);
	yfract = (double)(ywmax - ywmin)/p->yfull/(p->ywmaxs-p->ywmins);
	if( xfract > yfract )
		yfract = 0.1*xfract + 0.9*yfract;
	else	xfract = 0.9*xfract + 0.1*yfract;

	MinmaxSetup( &xa );
	MinmaxSetup( &ya );

	xa.scl = (xwmax-xwmin)/(xa.max-xa.min);
	xa.off = xwmin - xa.min*xa.scl + 0.5;
	ya.scl = (ywmax-ywmin)/(ya.max-ya.min);
	ya.off = ywmin - ya.min*ya.scl + 0.5;

	AxisSetup( &xa, &ya );
	AxisSetup( &ya, &xa );
	GridSetup();

	if( xa.lbl == 0 ) {
		sprintf( xtitle, "%sX is %.5g to %.5g,  %sY is %.5g to %.5g",
		xa.logflg ? "Log " : "", xmin, xmax,
		ya.logflg ? "Log " : "", ymin, ymax );
		xa.lbl = StringSave( xtitle );
	}
}


static void MinmaxSetup( a )
AxisPtr	a;
{
	if( a->min != DEFAULT && a->max != DEFAULT && a->min > a->max )
		estop( "%c axis reversed", a->name );

	if( a->min != DEFAULT )
		a->acchi = 0;
	if( a->max != DEFAULT )
		a->acclo = 0;

	if( a->logflg )
		LogTicSetup( a );
	else {
		LinearTicLogic( a );
		LinearTicSetup( a );
	}
}


static void AxisSetup( a, oa )
AxisPtr	a, oa;
{
	if( a->cr == DEFAULT ) {
		a->cr = a->rev ? oa->max : oa->min;
		if( a->aoff != DEFAULT )
			a->cr -= (a->rev ? -1 : 1)*a->aoff*(oa->max-oa->min);
	}
	a->lo = (*a->other)(a->cr);
	a->hi = (*a->other)(oa->max+oa->min-a->cr);
}


static void LinearTicSetup( a )
AxisPtr	a;
{
double	tic, tend, tscl;
short	k, n;
char	*tlbl;

	tlbl = 0;
	tscl = a->tscl;	
	switch( a->mode & (TICMARKS|TICNUMS) ) {
		case NOTHING:
			return;
		case TICMARKS:
			tlbl = "";
			break;
		case TICNUMS:
			tscl = 0;
		case TICMARKS+TICNUMS:
			break;
	}

	n = ceil( (a->min - a->tmark)/a->tic - 0.0001 );
	tic  = a->tmark + n*a->tic;
	tend = a->max + 0.001*a->tic;

	for( k=0;  tic < tend;  k++ ) {
		TicDef( a, tic, ((k+n)%a->skp) ? "" : tlbl, tscl, NO );
		tic += a->tic;
	}
}

LogTicSetup( a )
AxisPtr	a;
{
double	tic, tbeg, tend, tscl, sfract, ftmp, subtic[20];
int		k, n, nsubtic;
char	*tlbl;

	if( a->logflg && a->base == 0  )
		a->base = "10";
	if( a->min == DEFAULT )
		a->min = floor((a->name == 'x' ? xmin : ymin) + 0.001 );
	if( a->max == DEFAULT )
		a->max = ceil( (a->name == 'x' ? xmax : ymax) - 0.001 );
	if( a->tmark == DEFAULT )
		a->tmark = 0;
	if( a->pfm == 0 )
		a->pfm = "%g";

	tlbl = 0;
	tscl = a->tscl;	
	switch( a->mode & (TICMARKS|TICNUMS) ) {
		case NOTHING:
			return;
		case TICMARKS:
			tlbl = "";
			break;
		case TICNUMS:
			tscl = 0;
		case TICMARKS+TICNUMS:
			break;
	}

	n  = ceil( (a->min - a->tmark) - 0.0001 );
	tbeg = a->tmark + n;
	tend = a->max;
	sfract = ((a->name == 'x') ? (double)(xwmax-xwmin)/p->xfull :
		(double)(ywmax-ywmin)/p->yfull)/(tend-tbeg);

	if( ticlogic )
		eprintf( "tbeg, tend, sfract: %g %g %g\n", tbeg, tend, sfract );


	if( a->extra == NULLS )
		a->extra = (sfract < 0.07) ? "n" : "y";

	nsubtic = 0;
	if( a->extra[0] == 'y' && (n=atoi(a->base)) > 2 ) {
		if( a->skp == DEFAULT )
			a->skp = 1 + 0.05/sfract;
		ftmp = 1/log( (double)n );
		for( k=2;  k < n;  k++ )
			subtic[nsubtic++] = log((double)k)*ftmp;
	}
	else {
		if( a->skp == DEFAULT )
			a->skp = 1 + 0.05/sfract;
	}

	for( tic=tbeg;  tic <= tend;  tic += a->skp ) {
		TicDef( a, tic, tlbl, tscl, NO );
		for( n=0; n < a->skp;  n++ ) {
			if( n > 0 && tic+n-.01 < tend )
				TicDef( a, tic+n, "", tscl, NO );
			if( a->extra[0] == 'y' && tic+n+0.99 < tend ) {
				for( k=0;  k < nsubtic;  k++ )
					TicDef( a, tic+n+subtic[k], "", tscl, NO );
			}
		}
	}
}


TicDef( a, tic, lbl, scl, override )
AxisPtr	a;
char	*lbl;
double	tic, scl;
Boolean	override;
{
TicPtr	t;
double	delta, tmp;
short	n;
	
	if( a->min != DEFAULT && a->max != DEFAULT )
		delta = (a->max - a->min)/1e3;
	else {
		tmp = fabs(tic);
		delta = (tmp < 1e-3) ? 1e-3 : tmp/1e3;
	}

	for( n=0;  n < ntics;  n++ ) {
		t = &tics[n];
		tmp = fabs(tic - t->tic);
		if( a->name == (t->a)->name && tmp < delta )
			break;
	}

	if( n == ntics ) {
		if( ntics == maxtics )
			tics = (TicPtr)azmem(tics,&maxtics,12,sizeof(*tics));
		t = &tics[ntics++];
	}
	else {
		if( !override )
			return;
	}

	t->a = a;
	t->tic = tic;
	t->lbl = (lbl == 0) ? lbl : StringSave(lbl);
	t->scl = (scl == DEFAULT) ? 1.0 : scl;
	if( a->rev )
		t->scl = -(t->scl);
}


static void GridSetup()
{
char	*spec;

	if( *gridtype >= '0' && *gridtype <= '9' ) {
		gtype = LongNum(gridtype);	/* old-time compatibility */
		return;
	}

	while( *gridtype ) {
		if( !getstr(&gridtype, &spec) )
			continue;			

		if( strcmp(spec,"out") == 0 )
			gtype = (gtype & ~IN_OUT_MASK) + OUT_TIC;
		else	if( strcmp(spec,"in") == 0 )
				gtype = (gtype & ~IN_OUT_MASK) + IN_TIC;
		else	if( strcmp(spec,"out") == 0 )
				gtype = (gtype & ~IN_OUT_MASK) + OUT_TIC;
		else	if( strcmp(spec,"both") == 0 )
				gtype = (gtype & ~IN_OUT_MASK) + IN_OUT_TIC;
		else	if( strncmp(spec,"no",2) == 0 )
				gtype = (gtype & ~IN_OUT_MASK);
		else	if( strncmp(spec,"sym",3) == 0 )
				gtype |= SYMMETRIC;
		else	if( strcmp(spec,"grid") == 0 )
				gtype |= GRID;
		else	if( strcmp(spec,"xgrid") == 0 )
				gtype |= XGRID;
		else	if( strcmp(spec,"ygrid") == 0 )
				gtype |= YGRID;
		else	if( strncmp(spec,"sub",3) == 0 )
				gtype |= SUB_GRID;
		else	err( YES, "Unrecognized tic spec `%s'", spec );
		while( *gridtype == ',' )
			gridtype++;
	}
}


/************************************************************************/

XAxisDraw()
{
Ptype	min, max, off, off2, xsize, ysize;

	if( (gtype & (GRID|XGRID)) && (xa.mode & GRIDMARKS) )
		GridDraw( &xa, &ya );

	FontGroupSelect( "a", xa.numfg );
	if( xa.mode & AXIS ) {
		min = X(xa.min);
		max = X(xa.max);
		line( min, xa.lo, max, xa.lo );
		if( gtype & SYMMETRIC )
			line( min, xa.hi, max, xa.hi );
	}

	off = TicDrawAxis( &xa );
	if( xa.lbl && (xa.mode & TITLES) ) {
		strsize( xa.lbl, &xsize, &ysize, 0.0 );
		off2 = yfract*(yinch/4 + ysize/2) - ysize/3;
		off -= xa.rev ? -off2 : off2;
		FontGroupSelect( "t", xa.lblfg );
		alabel( xa.rev ? "XTR" : "XT", xa.lbl, (xwmin+xwmax)/2, off );
	}
}


YAxisDraw()
{
Ptype	min, max, off, off2, xsize, ysize;

	if( (gtype & (GRID|YGRID)) && (xa.mode & GRIDMARKS) )
		GridDraw( &ya, &xa );

	FontGroupSelect( "a", ya.numfg );
	if( ya.mode & AXIS ) {
		min = Y(ya.min);
		max = Y(ya.max);
		line( ya.lo, min, ya.lo, max );
		if( gtype & SYMMETRIC )
			line(  ya.hi, min, ya.hi, max );
	}

	off = TicDrawAxis( &ya );
	if( ya.lbl && (ya.mode & TITLES) ) {
		strsize( ya.lbl, &xsize, &ysize, 90.0 );
		off2 = xfract*(xinch/4 + xsize/2);
		off -= ya.rev ? -off2 : off2;
		FontGroupSelect( "t", ya.lblfg );
		alabel( ya.rev ? "YTR" : "YT", ya.lbl, off, (ywmin+ywmax)/2 );
	}
}


GridDraw( a, oa )
AxisPtr	a, oa;
{
TicPtr	t;
Ptype	n, itic, min, max;

	FontGroupSelect( "a", gridfg );
	min = (*oa->this)(oa->min);
	max = (*oa->this)(oa->max);

	for( n=0;  n < ntics;  n++ ) {
		t = &tics[n];
		if( (t->a)->name == a->name && (t->lbl == 0 ||
			*t->lbl != 0 || (gtype & SUB_GRID)) ) {
			itic = (*a->this)(t->tic);
			if( a->name == 'x' )
				line( itic, min, itic, max );
			else
				line( min, itic, max, itic );
		}
	}
}


static Ptype TicDrawAxis( a )
AxisPtr	a;
{
TicPtr	t;
double	fudge;
Ptype	off, toff, ticlen, ticsize;
short	n;
Boolean lbl;

	fudge = 0.3 * (double)chht/yinch * (3+xfract+yfract); 
	ticsize = fudge * ((a->name == 'x') ? xinch : yinch)/5.0;
	toff = a->lo;

	for( n=0, t=tics;  n < ntics;  n++, t++ ) {
		if( (t->a)->name == a->name ) {
			lbl = !(t->lbl && t->lbl[0] == 0);
			ticlen = (lbl ? (3*ticsize+1)/2 : ticsize) * t->scl;
			off = TicDraw( t, ticlen );
			if( lbl )
				TicLabel( t, off );
			if( a->rev ? (toff < off) : (toff > off) )
				toff = off;
	 	}
	}
	return( toff );
}

/***********************************************************************/

#define		XYTIC(A,B) 	( isx ? line(itic, A, itic, off=B)	\
					: line(A, itic, off=B, itic) )

TicDraw( t, ticlen )
TicPtr	t;
Ptype	ticlen;
{
AxisPtr	a;
int	isx, itic, off;

	a = t->a;
	itic = (*a->this)(t->tic);
	isx  = (a->name == 'x');

	if( gtype & SYMMETRIC )
	   switch( gtype & IN_OUT_MASK ) {
		case NO_TIC:
			break;
		case IN_TIC:
			XYTIC(a->hi, a->hi-ticlen);		break;
		case OUT_TIC:
			XYTIC(a->hi+ticlen, a->hi);		break;
		case IN_OUT_TIC:
			XYTIC(a->hi+ticlen, a->hi-ticlen);	break;
	}

	switch( gtype & IN_OUT_MASK ) {
		case NO_TIC:
			off = a->lo;				break;
		case IN_TIC:
			XYTIC(a->lo+ticlen, a->lo);		break;
		case OUT_TIC:
			XYTIC(a->lo, a->lo-ticlen);		break;
		case IN_OUT_TIC:
			XYTIC(a->lo+ticlen, a->lo-ticlen);	break;
	}
	return( off );
}


static void TicLabel( t, off )
TicPtr	t;
{
AxisPtr	a;
Boolean	rev;
double	tmp;		/* Done this way to overcome a 386 compiler bug */
char	str[60];

	a = t->a;
	if( t->lbl == 0 ) {
		tmp = fabs(t->tic);
		if( (a->max-a->min)*1e-6 > tmp )
			t->tic = 0;
		sprintf( str, a->pfm, t->tic );
		t->lbl = StringSave(str);
	}

	rev = (t->scl < 0);
	if( a->logflg ) {
		if( a->name == 'x' )
			elabel( rev ? "XER" : "XE", a->base, t->lbl, X(t->tic), off );
		else
			elabel( rev ? "YER" : "YE", a->base, t->lbl, off, Y(t->tic) );
	}
	else {
		if( a->name == 'x' )
			alabel( rev ? "XNR" : "XN", t->lbl, X(t->tic), off );
		else
			alabel( rev ? "YNR" : "YN", t->lbl, off, Y(t->tic) );
	}
}