/* $Id: pldtik.c,v 1.7 1994/06/30 18:22:06 mjl Exp $
 * $Log: pldtik.c,v $
 * Revision 1.7  1994/06/30  18:22:06  mjl
 * All core source files: made another pass to eliminate warnings when using
 * gcc -Wall.  Lots of cleaning up: got rid of includes of math.h or string.h
 * (now included by plplot.h), and other minor changes.  Now each file has
 * global access to the plstream pointer via extern; many accessor functions
 * eliminated as a result.
 *
*/

/*	pldtik.c

	Determines tick spacing and mode (fixed or floating) of
	numeric axis labels.
*/

#include "plplotP.h"

#define MIN_FLTDIG	3	/* disregarded if fractional part is 0 */
#define MAX_FIXDIG_POS	6
#define MAX_FIXDIG_NEG	4
#define DIGMAX_DEF	5

/*----------------------------------------------------------------------*\
 * void pldtik()
 *
 * If tick == 0, this works out a "nice" interval, so that there are
 * between 3 and 7.5 major tick intervals in the input range "vmin" to
 * "vmax". Using this value for the tick interval or supplied value, it
 * also computes "prec" which specifies the number of places that should
 * be written after the decimal point. The recommended number of subticks
 * is returned in "nsubt" unless the routine is entered with a non-zero
 * value of "nsubt".  The output variable "mode" is set to 0 if labels are
 * to be written in floating-point format, or to 1 if they are to be
 * written in scientific format.  For mode = 1, the exponent will be
 * placed at:
 *
 * 	top left	for vertical axis on left
 * 	top right	for vertical axis on right
 * 	bottom right	for horizontal axis
 *
 * The digmax flag can be set by the user, and represents the maximum
 * number of digits a label may occupy.  If digmax<0, it is disregarded,
 * and if digmax=0 the default value is used.  For digmax>0, mode=1 is
 * chosen if there is insufficient room for the label within the specified
 * # of digits.
 *
 * In the case of mode=0, the actual # of digits will become too large
 * when the magnitude of the labels become too large.  The mode=1 case
 * offers the greatest precision for the smallest field length.
 *
 * The determination of maximum length for fixed point quantities is
 * complicated by the fact that very long fixed point representations look
 * much worse than the same sized floating point representation.  Further,
 * a fixed point number with a large negative exponent will actually gain
 * in precision when written as floating point.  Thus we use certain fuzz
 * factors to get 'digfix' from 'digmax', however it will always be true
 * that digfix<=digmax.
 *
 * Finally, if 'digmax' is set, 'prec' is reduced in size if necessary so
 * that the labels fit the requested field length.
\*----------------------------------------------------------------------*/

void
pldtik(PLFLT vmin, PLFLT vmax, PLFLT *tick, PLINT *nsubt,
       PLINT *mode, PLINT *prec, PLINT digmax, PLINT *scale)
{
    PLFLT t0, t1, t2, vmod;
    PLINT msd, np, ns, digmin, digfix, autoscale = 0;

    *mode = 0;
    *scale = 0;
    if (*tick == 0.0)
	autoscale = 1;
    if (digmax == 0)
	digmax = DIGMAX_DEF;

/* Magnitute of min/max to get number of significant digits */

    vmod = MAX(ABS(vmin), ABS(vmax));
    t0 = (PLFLT) log10(vmod);
    msd = (PLINT) floor(t0);

/* Autoselect the mode flag */
/* 'digmin' is the minimum number of places taken up by the label */

    if (msd >= 0) {
	digmin = msd + 1;
	digfix = MAX_FIXDIG_POS;
	if (digmax > 0)
	    digfix = MIN(digmax, MAX_FIXDIG_POS);
    }
    else {
	digmin = -msd + 2;
	digfix = MAX_FIXDIG_NEG;
	if (digmax > 0)
	    digfix = MIN(digmax, MAX_FIXDIG_NEG);
    }

    if (digmin > digfix) {
	*mode = 1;
	*scale = msd;
    }

/* Magnitude of min/max difference to get tick spacing */

    t1 = (PLFLT) log10(ABS(vmax - vmin));
    np = (PLINT) floor(t1);
    t1 = t1 - np;

/* Get tick spacing. */

    if (t1 > 0.7781512503) {
	t2 = 2.0;
	ns = 4;
    }
    else if (t1 > 0.4771212549) {
	t2 = 1.0;
	ns = 5;
    }
    else if (t1 > 0.1760912591) {
	t2 = 5.0;
	ns = 5;
	np = np - 1;
    }
    else {
	t2 = 2.0;
	ns = 4;
	np = np - 1;
    }

/* Now compute tick spacing */

    if (autoscale) {
	*tick = t2 * pow(10.0, (double) np);
	if (vmin > vmax)
	    *tick = -*tick;
    }
    else {
	t1 = log10(ABS(*tick));
	np = (PLINT) floor(t1);
    }
    if (*nsubt == 0)
	*nsubt = ns;

/* Establish precision.  */

    if (*mode != 0)
	*prec = msd - np;
    else
	*prec = MAX(-np, 0);

/* One last hack required here: if exponent < 0, i.e. number has leading
   '0.', it is better to change to floating point form if the number of
    digits is insufficient to represent the tick spacing. */

    if (autoscale) {
	if (*mode == 0 && digmax > 0) {
	    if (t0 < 0.0) {
		if (digmax - 2 - *prec < 0) {
		    *mode = 1;
		    *scale = msd;
		}
	    }
	    else
		*prec = MAX(MIN(*prec, digmax - msd - 1), 0);
	}
	if (*mode != 0) {
	    *prec = msd - np;
	    *prec = MAX(MIN(*prec, MAX(digmax-1, MIN_FLTDIG)), 0);
	}
    }
}
