# plt - Software for 2D Plots 2.5

File: <base>/plt-2.5a/classic/tic.c (7,928 bytes)
``````/*	plt/tic.c		Paul Albrecht		March 1988

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

Attempt to make a smart choice for the tic marks.  To avoid worries
about roundoff errors, all computation is done with long integers.

*/

#include	"plt.h"
#include	"axis.h"

static void TicSetup();
static void TicRound();
static void TicConfigCost();
static short DecimalPlaces();
static void MakeLongs();
static void GetString();
static void GetLong();

#define		WORKDIG		7		/* don't go over 8 	*/
#define		SIGDIG		5		/* max sig dig for tics	*/
#define		WORKLONG	(10000000L)	/* 10**WORKDIG		*/
#define		PLT_MINLONG	(100L)		/* 10**(WORKDIG-SIGDIG)	*/

typedef	struct	{
double	dbl;
long	l;		/* the shifted WORKDIG digit representation dbl	*/
short	exp;	/* the exponent of dbl	*/
char	str[20];/* string representation of dbl	*/
}	NumInfo, *NumPtr;

typedef	struct	{
short	mult;	/* must divide WORKLONG without remainder! */
float	icost;
float	cost;
long	tic;
long	min;
long	max;
long	tmark;
short	ntic;
short	ticdp;
short	mindp;
short	maxdp;
short	minfit;
short	maxfit;
}	TLWInfo, *TLWPtr;		/* Tic mark logic workspace	*/

static	NumInfo	lo,		/* minimum axis value	*/
hi,			/* maximum axis value	*/
diff,		/* length of the axis	*/
tmark;		/* where a labeled tic mark should fall */

static	TLWInfo	tcs[]= { {10}, {50}, {25}, {20}, {30,1}, {40}, {60,1}, {75} };
static	double	tenf;
static	int	havetmark;

void	TicInit( mode )
Mode	mode;
{
}

LinearTicLogic( a )
AxisPtr	a;
{
TLWPtr	tc, tcbest;

if( a->min == DEFAULT )
lo.dbl = (a->name == 'x') ? xmin : ymin;
else	lo.dbl = a->min;

if( a->max == DEFAULT )
hi.dbl = (a->name == 'x') ? xmax : ymax;
else	hi.dbl = a->max;

if( lo.dbl == hi.dbl ) {
err( NO, "All %c values are %lg", a->name, lo.dbl );
if( lo.dbl == 0 )
hi.dbl = 1;
else {	lo.dbl = floor( lo.dbl );
hi.dbl = lo.dbl + 1;
}
a->acclo = a->acchi = 0;
}

if( a->skp == DEFAULT || a->skp < 1 )
a->skp = 2;

diff.dbl  = hi.dbl - lo.dbl;
tmark.dbl = a->tmark;
havetmark = !(tmark.dbl == DEFAULT || tmark.dbl < lo.dbl || tmark.dbl > hi.dbl);

MakeLongs();

if( ticlogic ) {
eprintf( "\nGIVEN: %g  %g  (%g)\n", lo.dbl, hi.dbl, tmark.dbl );
eprintf( "    MIN     MAX     TIC   NTIC    DEC PLACES    COST   TMARK\n" );
}

tcbest = tcs;
for( tc=tcs; tc < ENDP(tcs);  tc++ ) {
TicSetup( a, tc );
TicRound( a, tc );

tc->ticdp = DecimalPlaces( tc->tic );
tc->mindp = DecimalPlaces( tc->min );
tc->maxdp = DecimalPlaces( tc->max );

TicConfigCost( a, tc );
if( tcbest->cost > tc->cost )
tcbest = tc;

if( ticlogic )
eprintf( "%8.3f%8.3f%8.3f%6d%5d%3d/%d%3d/%d%8.3f%8.3f\n",
tc->min*tenf, tc->max*tenf, tc->tic*tenf,
tc->ntic, tc->ticdp, tc->mindp, tc->minfit,
tc->maxdp, tc->maxfit, tc->cost, tc->tmark*tenf );
if( a->tic != DEFAULT || a->mlt != DEFAULT )
break;
}

a->min = tcbest->min * tenf;
a->max = tcbest->max * tenf;
a->tic = tcbest->tic/a->skp * tenf;
if( !havetmark )
a->tmark = tcbest->tmark * tenf;

if( a->pfm == NULLS ) {
if( tcbest->ticdp <=  0 || tcbest->ticdp > SIGDIG )
a->pfm = "%lg";
else {	char fmt[30];
sprintf( fmt, "%%.%dlf", (int)tcbest->ticdp );
a->pfm = StringSave( fmt );
}
}

if( a->tmark == DEFAULT ) {
a->tmark = (a->name == 'x') ? ya.cr : xa.cr;
if( a->tmark < a->min )
a->tmark = a->min;
if( a->tmark > a->max )
a->tmark = a->max;
}

if( ticlogic )
eprintf( "CHOOSE: %g  %g  %g using %s  (%g)\n",
a->min, a->max, a->tic, a->pfm, a->tmark );
}

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

static void TicSetup( a, tc )
AxisPtr	a;
TLWPtr	tc;
{
double	dbl;

if( a->tic == DEFAULT ) {
if( a->mlt != DEFAULT ) {
dbl = a->skp*a->mlt/tenf;
while( dbl < PLT_MINLONG )
dbl *= 10;
tc->tic = dbl + 0.5;
}
else	tc->tic = tc->mult;
while( tc->tic*20 < diff.l )
tc->tic *= 10;
}
else	tc->tic = a->skp*a->tic/tenf + 0.5;

tc->min = lo.l;
tc->max = hi.l;
tc->tmark = havetmark ? tmark.l : (lo.l+tc->tic)/tc->tic*tc->tic;
tc->cost = tc->icost;
}

/* Extend the axes to a multiple of the tic spacing */
static void TicRound(a, tc)
AxisPtr	a;
TLWPtr	tc;
{
long	test, stic;
int	n;

tc->minfit = tc->maxfit = 0;
for( n=0;  n < a->skp;  n++ ) {
stic = tc->tic*(n+1)/a->skp;
test = tc->tmark - (tc->tmark-lo.l+stic-1)/stic*stic;
if( (test == 0 || tc->minfit == 0) && (double)(lo.l-test)/diff.l < a->acclo ) {
tc->min = test;
tc->minfit = (test/tc->tic*tc->tic == test);
}

test = tc->tmark + (hi.l-tc->tmark+stic-1)/stic*stic;
if( (test == 0 || tc->maxfit == 0) && (double)(test-hi.l)/diff.l < a->acchi ) {
tc->max = test;
tc->maxfit = (test/tc->tic*tc->tic == test);
}
}

if( tc->max > 0 )
tc->ntic = tc->max/tc->tic;
else	tc->ntic = (tc->max - (tc->tic-1))/tc->tic;
if( tc->min > 0 )
tc->ntic -= (tc->min + (tc->tic-1))/tc->tic;
else	tc->ntic -= tc->min/tc->tic;
tc->ntic += 1;
}

/* Compute undesirability of tic configuration */
static void TicConfigCost(a, tc)
AxisPtr	a;
TLWPtr	tc;
{
AxisPtr	oa;
double	dbl;
int	n;

n = (tc->ticdp > 0) ? tc->ticdp : 0;
tc->cost += n;
tc->cost += tc->minfit ? ((tc->mindp > 0) ? tc->mindp : 0) : n;
tc->cost += tc->maxfit ? ((tc->maxdp > 0) ? tc->maxdp : 0) : n;

n = tc->maxfit+tc->minfit;
dbl = 3 + n/2.0;
tc->cost += (tc->ntic < dbl) ?  2*(dbl-tc->ntic) : 0;;
dbl = 6 + n/2.0;
tc->cost += (tc->ntic > dbl) ?  2*(tc->ntic-dbl) : 0;;

oa = (a->name == 'x' ? &ya : &xa);
if( oa->cr == DEFAULT || oa->cr == a->min )
tc->cost -= tc->minfit/2.0;
else	if( oa->cr == a->max )
tc->cost -= tc->maxfit/2.0;
}

/* How many decimal places are needed to print lnum */
static short DecimalPlaces( lnum )
long	lnum;
{
short	dp;

dp = -(WORKDIG + diff.exp);
while( YES ) {
lnum = 10*(lnum - lnum/WORKLONG*WORKLONG);
if( lnum == 0 )
break;
dp++;
}
return( dp );
}

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

Subroutine GetString(num) takes fabs(num->dbl) and puts the first
WORKDIG significant digits into the string num->str.  The base 10
exponent is stored in num->exp;

The variable eref is the largest base 10 exponent value of the
following three numbers: the axis min, axis max, and axis max-min.

Subroutine GetLong(num,eref) produces a long integer num->l which
is equal to num->dbl*WORKLONG/10**eref.  The exponent num->exp becomes
eref.  The actual value of num->l is always less than WORKLONG.
This provides the first WORKDIG working digits for the three relevant
numbers (min, max, max-min).  The numbers are scaled so that the
is greater than or equal to WORKLONG/10.
*/

static void MakeLongs()
{
int	eref;

GetString( &lo );
GetString( &hi );
GetString( &diff );

eref = (lo.exp > hi.exp) ? lo.exp : hi.exp;
if( diff.exp > eref )
eref = diff.exp;

GetLong( &lo, eref );
GetLong( &hi, eref );
GetLong( &diff, eref );

if( diff.l < PLT_MINLONG ) {
diff.l = PLT_MINLONG;
}
if( havetmark ) {
GetString( &tmark );
GetLong( &tmark, eref );
}

tenf = pow( 10.0, (double)eref );
}

static void GetString( num )
NumPtr	num;
{
int		n;		/* must be int */
short	ndig;
char	*str;

str = num->str;
sprintf( str, "%.8le", num->dbl );
while( *str && (*str == '0' || *str == '-' || *str == ' ') )
str++;

ndig = 0;
while( *str && *str != 'e' && *str != 'E' ) {
if( *str == '.' )
num->exp = ndig;
else {	if( ndig < WORKDIG )
num->str[ndig++] = *str;
}
str++;
}
while( ndig < WORKDIG )
num->str[ndig++] = '0';
num->str[ndig] = 0;

sscanf( str+1, "%d", &n );
num->exp += n - WORKDIG;
}

static void GetLong( num, eref )
NumPtr	num;
{
long	atol();
short	ndigs;

ndigs = WORKDIG - (eref - num->exp);
if( ndigs > 0 ) {
num->str[ndigs] = 0;
num->l = atol(num->str);
}
else	num->l = 0;

num->exp = eref;
if( num->dbl < 0 )
num->l = -num->l;
}``````