#include <math.h>
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <Scale.h>
#include <double.h>
#include "Axis3P.h"
#include "ThreedeeP.h"
#include <Text.h>
#include <At/FontFamily.h>

#define DEBUGAX

/************************************************************/
/*  Some of the code used in this file is taken from       **/
/*  gnuplot, especially the code for calculating number    **/
/*  and locations of log ticks. Also, some is taken from   **/
/*  the 2d plotter widget now under development at Athena  **/
/************************************************************/


#define pTOP 0.0
#define pCENTER 0.5
#define pBOTTOM 1.0
#define pLEFT 0.0
#define pRIGHT 1.0

static double top = TOP;
static double bottom = BOTTOM;
static double left = LEFT;
static double right = RIGHT;
static double center = CENTER;

/*
static double subl = SUBTIC_LINE_LENGTH;
static double ticl = TIC_LINE_LENGTH;
*/

static double defaultInter = 0.2;

static double zero = 0.0;
static double one = 1.0;

#define AtFontHeight(f) (f->ascent + f->descent)
#define AtWarning(w, e) XtWarning(e)

#define ALMOSTZERO 1.0e-12
#define fequal(x,y) (fabs((x)-(y)) < ALMOSTZERO)
#define fzero(x) (fabs(x) < ALMOSTZERO)

#define offset(fld) XtOffset(AtAxis3Object, fld)
static XtResource axis3_resources[] = {
{XtNaxisLabel, XtCAxisLabel, XtRString,
     sizeof(String), offset(axis3.axisLabel), XtRString, NULL},
{XtNlabelFontFamily, XtCFontFamily, XtRString,
     sizeof(String), offset(axis3.labelFontFamily),
     XtRString, "new century schoolbook"},
{XtNdrawLabel, XtCDrawLabel, XtRBoolean,
     sizeof(Boolean), offset(axis3.drawLabel),
     XtRImmediate, (caddr_t)True},
{XtNdrawNumbers, XtCDrawNumbers, XtRBoolean,
     sizeof(Boolean), offset(axis3.drawNumbers),
     XtRImmediate, (caddr_t)True},
{XtNautoNumber, XtCAutoNumber, XtRBoolean,
     sizeof(Boolean), offset(axis3.autoNumber),
    XtRImmediate, (caddr_t)True},
{XtNnumberFontFamily, XtCFontFamily, XtRString,
     sizeof(String), offset(axis3.numberFontFamily),
     XtRString, "new century schoolbook"},
{XtNlabelSize, XtCFontSize, XtRFontSize,
     sizeof(int), offset(axis3.labelSize),
     XtRImmediate, (caddr_t) AtFontNORMAL},
{XtNnumberSize, XtCFontSize, XtRFontSize,
     sizeof(int), offset(axis3.numberSize),
     XtRImmediate, (caddr_t) AtFontMEDIUM},
{XtNaxisColor, XtCForeground, XtRPixel,
     sizeof(Pixel), offset(axis3.axisColor),
     XtRString, (caddr_t) XtDefaultForeground},
{XtNnumberColor, XtCForeground, XtRPixel,
     sizeof(Pixel), offset(axis3.numberColor),
     XtRString, (caddr_t) XtDefaultForeground},
{XtNlabelColor, XtCForeground, XtRPixel,
     sizeof(Pixel), offset(axis3.labelColor),
     XtRString, (caddr_t) XtDefaultForeground},
{XtNmin, XtCMin, XtRDouble,
     sizeof(double), offset(axis3.min), XtRDouble, (caddr_t)&zero},
{XtNmax, XtCMax, XtRDouble,
     sizeof(double), offset(axis3.max), XtRDouble, (caddr_t)&one},
{XtNtransform, XtCTransform, XtRTransform,
     sizeof(short), offset(axis3.transform),
     XtRImmediate, (caddr_t)AtTransformLINEAR},
{XtNticSide, XtCTicSide, XtRDouble,
   sizeof(double), offset(axis3.ticSide), XtRDouble,
   (caddr_t)&center},
{XtNticInterval, XtCTicInterval, XtRDouble,
   sizeof(double), offset(axis3.ticInterval), XtRDouble,
   (caddr_t)&defaultInter},
{XtNticsPerAxis, XtCTicsPerAxis, XtRInt, 
   sizeof(int), offset(axis3.ticsPerAxis), XtRImmediate, (caddr_t)6},
{XtNsubsPerTic, XtCSubsPerTic, XtRInt, 
   sizeof(int), offset(axis3.subsPerTic), XtRImmediate, (caddr_t)1 },
{XtNaxisType, XtCAxisType, XtRShort, 
   sizeof(short), offset(axis3.axisType), XtRImmediate, (caddr_t)0},
{XtNlabelSide, XtCLabelSide, XtRDouble, sizeof(double),
   offset(axis3.labelSide), XtRDouble, (caddr_t)&bottom },
{XtNticLength, XtCTicLength, XtRShort, 
   sizeof(short), offset(axis3.ticLength), XtRImmediate, 
   (caddr_t)5},
{XtNsubticLength, XtCSubticLength, XtRShort, 
   sizeof(short), offset(axis3.subticLength), XtRImmediate, 
   (caddr_t)3},
};
#undef offset

static Boolean AtAxis3SetValues(AtAxis3Object, AtAxis3Object, AtAxis3Object);
static void Axis3Initialize(Widget, Widget);
static void Axis3Destroy(Widget);
static void RecalcNumTics(AtAxis3Object);
static void AutoNumber(AtAxis3Object, int);

AtAxis3ClassRec atAxis3ClassRec = {
  {
    /* superclass         */    (WidgetClass) &objectClassRec,
    /* class_name         */    "AtAxis3",
    /* widget_size        */    sizeof(AtAxis3Rec),
    /* class_initialize   */    NULL,
    /* class_part_initialize*/  NULL,
    /* class_inited       */    FALSE,
    /* initialize         */    (XtInitProc) Axis3Initialize,
    /* initialize_hook    */    NULL,
    /* pad                */    NULL,
    /* pad                */    NULL,
    /* pad                */    0,
    /* resources          */    axis3_resources,
    /* num_resources      */    XtNumber(axis3_resources),
    /* xrm_class          */    NULLQUARK,
    /* pad                */    FALSE,
    /* pad                */    FALSE,
    /* pad                */    FALSE,
    /* pad                */    FALSE,
    /* destroy            */    (XtWidgetProc) Axis3Destroy,
    /* pad                */    NULL,
    /* pad                */    NULL,
    /* set_values         */    (XtSetValuesFunc) AtAxis3SetValues,
    /* set_values_hook    */    NULL,
    /* pad                */    NULL,
    /* get_values_hook    */    NULL,
    /* pad                */    NULL,
    /* version            */    XtVersion,
    /* callback_offsets   */    NULL,
    /* pad                */    NULL,
    /* pad                */    NULL,
    /* pad                */    NULL,
    /* extension            */  NULL
},
/* AtAxis3ClassPart initialization */
{
    0
}
};


WidgetClass atAxis3ObjectClass = (WidgetClass)&atAxis3ClassRec;

#define CopyLabel(a) a->axis3.axisLabel = XtNewString(a->axis3.axisLabel);
#define FreeLabel(a) XtFree(a->axis3.axisLabel)

static void GetLabelText(AtAxis3Object a)
{
    if (a->axis3.axisLabel != NULL)  {
        a->axis3.labelText = AtTextCreate(a->axis3.axisLabel,
					      a->axis3.labelFF,
                                         a->axis3.labelSize);
        if (a->axis3.angle > PI/3) AtTextRotate(a->axis3.labelText);
    }
    else
        a->axis3.labelText = NULL;
}

static void FreeLabelText(AtAxis3Object a)
{
    if (a->axis3.labelText)
        AtTextDestroy(a->axis3.labelText);
}

static void GetAxisGC(AtAxis3Object a)
{
    XGCValues gcv;
    gcv.foreground = a->axis3.axisColor;
    a->axis3.axisGC = XtGetGC(XtParent((Widget)a), GCForeground, &gcv);
}


static void GetLabelGC(AtAxis3Object a)
{
    XGCValues gcv;

    gcv.foreground = a->axis3.labelColor;
    a->axis3.labelGC = XtGetGC(XtParent((Widget)a), GCForeground, &gcv);
}

static void GetNumberGC(AtAxis3Object a)
{
    XGCValues gcv;

    gcv.foreground = a->axis3.numberColor;
    gcv.font = a->axis3.numberFont->fid;
    a->axis3.numberGC = XtGetGC(XtParent((Widget)a),
                               GCForeground | GCFont, &gcv);
}

#define FreeLabelGC(a)     XtReleaseGC(a, a->axis3.labelGC);
#define FreeNumberGC(a)    XtReleaseGC(a, a->axis3.numberGC);
#define FreeAxisGC(a)      XtReleaseGC(a, a->axis3.axisGC);
#define GetLabelFF(a)  a->axis3.labelFF = AtFontFamilyGet(XtDisplay(XtParent((Widget)a)), a->axis3.labelFontFamily);
#define GetNumberFF(a) a->axis3.numberFF = AtFontFamilyGet(XtDisplay(XtParent((Widget)a)), a->axis3.numberFontFamily);
#define GetNumberFont(a)  a->axis3.numberFont = AtFontFetch(a->axis3.numberFF, a->axis3.numberStyle, a->axis3.numberSize);
#define FreeLabelFF(a)  AtFontFamilyRelease(a->axis3.labelFF);
#define FreeNumberFF(a)  AtFontFamilyRelease(a->axis3.numberFF);

static void Axis3Initialize(request, new)
     Widget request, new;
{
  AtAxis3Object a = (AtAxis3Object)new;
  AtAxis3Part *ax = &a->axis3;
  XGCValues gcv;

#ifdef DEBUGAX
  printf("Axis3Initialize called\n");
  printf("In Axis3.c:Axis3Initialize ax->min = %f, ax->max =  %f \n", 
	 a->axis3.min, a->axis3.max);
#endif DEBUGAX
  
  ax->endpoints = (XPoint *)XtMalloc(sizeof(XPoint) * 2);
  ax->xscale = AtScaleCreate(ax->min, ax->max, 0, 1, ax->transform);
  ax->yscale = AtScaleCreate(ax->min, ax->max, 0, 1, ax->transform);
  
  GetLabelFF(a);
  GetNumberFF(a);
  GetNumberFont(a);
  
  CopyLabel(a);
  GetLabelText(a);
  
  GetAxisGC(a);
  GetNumberGC(a);
  GetLabelGC(a);
  
  ax->maxticwidth = 0;
  ax->nlines = 0;
  ax->lines = NULL;
  a->axis3.ticvalues = (double*) XtMalloc(0);

  a->axis3.ticXCoords = (short *) XtMalloc(0);
  a->axis3.ticYCoords = (short *) XtMalloc(0);
  a->axis3.subXCoords = (short *) XtMalloc(0);
  a->axis3.subYCoords = (short *) XtMalloc(0);

  a->axis3.ticlabels = (char **) XtMalloc(0);

  if(a->axis3.ticsPerAxis < 0) {
    AtWarning(XtParent(new), "Negative number of tick marks not allowed");
    a->axis3.ticsPerAxis = 0;
  }
  if(a->axis3.ticsPerAxis == 1){
    AtWarning(XtParent(new), "Cannot have just one tick. Must be 0 or >= 2");
    a->axis3.ticsPerAxis = 2;
  }

  a->axis3.ntics = a->axis3.ticsPerAxis;
  a->axis3.nsubs = a->axis3.subsPerTic;
  ax->angle = (ax->axisType == AxisTypeX)?(0.0):
    (ax->axisType == AxisTypeZ)?(PI/2):
      ((AtThreedeeWidget)XtParent((Widget)new))->threedee.yAngle;
  
  ax->ticsVertical = (ax->angle < PI/12);
  if (!a->axis3.autoNumber) RecalcNumTics(a);
  ax->valid = False;

  ax->labelX = ax->labelY = 0;

  ax->subticspacing = 0;

#ifdef DEBUGAX
  printf("In Axis3.c:Axis3Initialize done. ax->min = %f, ax->max =  %f \n", 
	 ax->min, ax->max);
#endif DEBUGAX
  
}

static void Axis3Destroy(Widget w)
{
    AtAxis3Object a = (AtAxis3Object)w;
    AtAxis3Part *ax = &a->axis3;
    int i;

#ifdef DEBUGAX
  printf("Axis3Destroy called\n");
#endif DEBUGAX

    AtScaleDestroy(a->axis3.xscale);
    AtScaleDestroy(a->axis3.yscale);
    FreeLabel(a);
    FreeLabelText(a);
    FreeLabelFF(a);
    FreeNumberFF(a);
    FreeAxisGC(a);
    FreeNumberGC(a);
    FreeLabelGC(a);
    XtFree(a->axis3.ticvalues);
    XtFree(a->axis3.ticXCoords);
    XtFree(a->axis3.subXCoords);
    XtFree(a->axis3.ticYCoords);
    XtFree(a->axis3.subYCoords);
    for(i=0; i<a->axis3.ntics; i++) XtFree(a->axis3.ticlabels[i]);
    XtFree(a->axis3.ticlabels);

#ifdef DEBUGAX
  printf("Axis3Destroy finishes\n");
#endif DEBUGAX

}

static Boolean AtAxis3SetValues(AtAxis3Object current,
                             AtAxis3Object request,
                             AtAxis3Object new)
{
#define Changed(field) (current->axis3.field != new->axis3.field)
  Boolean redraw = False;
  Boolean layout = False;
  Boolean ticsChanged = False;
  int axis_length;
#ifdef DEBUGAX
  printf("AtAxis3SetValues called\n");
#endif DEBUGAX

    /* free/reallocate AtText, FontFamilies, GCs, etc */
    if (Changed(axisLabel)) {
        FreeLabel(current);
        CopyLabel(new);
        FreeLabelText(new);
        GetLabelText(new);
	redraw = layout = True;
    }
  
    if (Changed(labelFontFamily)) {
        FreeLabelFF(new);
        GetLabelFF(new);
        FreeLabelText(new);
        GetLabelText(new);
	redraw = True;
    }
    if (Changed(axisColor)) {
        FreeAxisGC(new);
        GetAxisGC(new);
	redraw = True;
    }
    if (Changed(labelColor)) {
        FreeLabelGC(new);
        GetLabelGC(new);
	redraw = True;
    }
  
  if(Changed(drawLabel) || Changed(drawNumbers)) {
    redraw = True;
  }

  if(Changed(min) || Changed(max)) {
    AtScaleRescale(new->axis3.xscale, new->axis3.min, new->axis3.max);
    AtScaleRescale(new->axis3.yscale, new->axis3.min, new->axis3.max);
  }
  if(Changed(transform))
    AtScaleChangeTransform(new->axis3.xscale, new->axis3.transform);
  if (Changed(min) || Changed(max) || Changed(transform) ||
      Changed(ticsPerAxis) ||Changed(subsPerTic) || Changed(ticInterval) ||
      Changed(autoNumber))
    {
      AtAxis3ComputeTicSpacing(new, AtAxis3Length(new));
      ticsChanged = True;
      redraw = True;
    }

  if (Changed(numberFontFamily)) {
    FreeNumberFF(new);
    GetNumberFF(new);
  }
  if (Changed(numberFontFamily) || Changed(numberSize)) {
    GetNumberFont(new); 
    layout = redraw = True;
  }
  if (Changed(numberColor) || Changed(numberFontFamily) ||
      Changed(numberSize)) {
    FreeNumberGC(new);
    GetNumberGC(new);
    redraw = True;
  }
  
  if(Changed(ticsPerAxis)) {
    if(new->axis3.ticsPerAxis < 0) {
      AtWarning(XtParent(new), "Negative number of tick marks not allowed");
      new->axis3.ticsPerAxis = 0;
    }
    if(new->axis3.ticsPerAxis == 1){
      AtWarning(XtParent(new), "Cannot have just one tick. Must be 0 or >= 2");
      new->axis3.ticsPerAxis = 2;
    }

    axis_length = AtAxis3Length(new);
    if((axis_length / new->axis3.ticsPerAxis) < 
       2 * AtFontHeight(new->axis3.numberFont)) 
      new->axis3.ntics = new->axis3.ticsPerAxis = 
	axis_length / (2 * AtFontHeight(new->axis3.numberFont));
    ticsChanged = True;
    redraw = True;
  }
    
  if(Changed(subsPerTic)) {
    axis_length = AtAxis3Length(new);
      if((axis_length / ((new->axis3.ticsPerAxis - 1)*
			 new->axis3.subsPerTic)) < 5)
	new->axis3.nsubs = 
	  new->axis3.subsPerTic = 
	    axis_length / (5 * (new->axis3.ticsPerAxis - 1));
      ticsChanged = True;
      redraw = True;
  }
  
  if ((!new->axis3.autoNumber) &&
      (Changed(ticInterval) || Changed(nsubs) ||
       Changed(min) || Changed(max)))
    RecalcNumTics(new);
  

  if((ticsChanged)  || Changed(axisLabel) || Changed(labelFontFamily) ||
     Changed(numberFontFamily) || Changed(labelSize) ||
     Changed(ticSide) || Changed(labelSide) || Changed(ticLength) ||
     Changed(subticLength) ||Changed(numberSize) || Changed(numberStyle)) {

    layout = redraw = True;
    }  

  if(Changed(axisType)) {
    AtWarning(new, "AtAxis3SetValues: must not change axis type. Restoring.");
    new->axis3.axisType = current->axis3.axisType;
  }
    
  if(layout) At3dComputeLayout((AtThreedeeWidget)XtParent((Widget)new));
#ifdef DEBUGAX
  printf("Axis3SetValues finishes\n");
#endif DEBUGAX

  return(redraw);

}


/* -------------------------------------------------------------*/


/****************** ffix *********************
 used to round a number up or down to the nearest
 integer if that number of extremely close to that
 integer. This prevents seeing numbers like:
 1.99999999999 or 1.000000001
 *********************************************/
static double ffix(double x)
{
    if (fequal(x,ceil(x))) return ceil(x);
    else if (fequal(x,floor(x))) return floor(x);
    else return x;
}

/********************************************************
 **********              Round             **************
 ********************************************************/
static int Round(double x)
     /* uses floor() or ceil() to round towards zero */
{
    if (x >= 0) return floor(x);
    else return ceil(x);
  }

void AtAxis3SetBounds(AtAxis3Object axis, double min, double max)
{
#ifdef DEBUGAX
  printf("AtAxis3SetBounds called to set bounds to min %f and max %f\n",
	 min, max);
#endif DEBUGAX

  axis->axis3.min = min;
  axis->axis3.max = max;
  AtScaleRescale(axis->axis3.xscale,min, max);
  AtScaleRescale(axis->axis3.yscale,min, max);
}

void AtAxis3SetLocation(AtAxis3Object axis, 
			int x1, int y1, int x2, int y2)
{
  int i, j, width, height;
  AtAxis3Part *a = &axis->axis3;
  Arg wargs[2];
  
  /* assumes know ntics, nsubs, ticvalues. */
  /* computes ticXCoords, ticYCoords, scales */
#ifdef DEBUGAX
  printf("AtAxis3SetLocation begins w. location arguments %d %d %d %d\n",
	 x1, y1, x2, y2);

#endif DEBUGAX
  
  XtSetArg(wargs[0], XtNwidth, width);
  XtSetArg(wargs[1], XtNheight, height);
  XtGetValues(((Widget)(axis->object.parent)), wargs, 2);

  exit(0);

  if((x2 < x1) || (y2 < y1) || (x1 < 0) || (x2 < 0) || (y1 < 0) || (y2 < 0) || 
     (x1 > width) || (x2 > width) || (y1 > height) || (y2 > height)) {
    AtWarning(((Widget)axis->object.parent), 
	      "Axis3SetLocation: Cannot set axis at these coords\n");
    x1 = 0;
    x2 = 1;
    y1 = 0;
    y2 = 1;
  }
  
  a->endpoints[0].x = x1;
  a->endpoints[0].y = y1;
  a->endpoints[1].x = x2;
  a->endpoints[1].y = y2;
 
#ifdef DEBUGAX
  printf("AtAxis3SetLocation begins w. location arguments %d %d %d %d\n",
	 x1, y1, x2, y2);

  printf("now resize scales\n");
#endif DEBUGAX
 
  AtScaleResize(a->xscale, x2,x1);
  AtScaleResize(a->yscale, y2,y1);
  
#ifdef DEBUGAX
    printf("done resizing scales, now for point coords\n");
#endif DEBUGAX  

  for(i = 0; i < a->ntics; i++) {
#ifdef DEBUGAX
    printf("tic value =  %f\n", a->ticvalues[i]);    
#endif DEBUGAX
    a->ticXCoords[i] = AtScaleUserToPixel(a->xscale, 
					  a->ticvalues[i]);
    a->ticYCoords[i] = AtScaleUserToPixel(a->yscale, 
					  a->ticvalues[i]);
    
#ifdef DEBUGAX
    printf("tic coords (%d, %d)\n", a->ticXCoords[i], a->ticYCoords[i]);
#endif DEBUGAX
    if(a->transform == AtTransformLINEAR) {
      for(j = 0; j < a->nsubs; j++) {
	a->subXCoords[i * a->nsubs + j] =
	  AtScaleUserToPixel(a->xscale,
			     a->ticvalues[i] +
			     ((a->ticInterval / (a->nsubs + 1)) * (j + 1)));
	a->subYCoords[i * a->nsubs + j] =
	  AtScaleUserToPixel(a->yscale,
			     a->ticvalues[i] +
			     ((a->ticInterval / (a->nsubs + 1)) * (j + 1)));
      } /* for j */
    } /* if AtTransformLINEAR */
    
    else {
      int decade;
      double ticval, logticval;
      ticval = a->ticvalues[i];
      logticval = log10(ticval);
      decade = (int)floor(logticval);
      if(logticval == (double)decade)
	ticval = 0.0;
      
      for(j = 0; j < a->nsubs; j++) {
	a->subXCoords[i * (a->nsubs) + j] =
	  AtScaleUserToPixel(a->xscale,
			     ticval +  
			     (a->ticInterval/(a->nsubs+1)*(j+1)) *
			     pow(10.0, (double)decade));
	a->subYCoords[i * (a->nsubs) + j] =
	  AtScaleUserToPixel(a->yscale,
			     ticval +  
			     (a->ticInterval/(a->nsubs+1)*(j+1)) *
			     pow(10.0, (double)decade));
      } /*for j */
    }/* else */
    
  } /* for i */
  a->ticXCoords[i] = AtScaleUserToPixel(a->xscale, 
					a->ticvalues[i]);
  a->ticYCoords[i] = AtScaleUserToPixel(a->yscale, 
					a->ticvalues[i]);
  
#ifdef DEBUGAX
  printf("AtAxis3SetLocation ends\n");
#endif DEBUGAX
}



void AtAxis3SetAngle(AtAxis3Object axis, double angle)
{
  AtAxis3Part *a = &axis->axis3;
  double c;
  int x1, y1;
  /* assume know old axis endpoints, ntics, nsubs, ticInterval, ticvalues */
  /* computes ticsVertical, new endpoint, scales, ticXCoords, ticYCoords */  

#ifdef DEBUGAX
  printf("AtAxis3SetAngle begins\n");
#endif DEBUGAX
  if((angle < 0) || (angle > PI/2))
    {
      AtWarning((Widget)axis->object.parent, 
		"Axis angle must be between 0 and PI/2");
      return;
    }
  
  if(angle < PI/12) a->ticsVertical = True;
  else a->ticsVertical = False;

  a->angle = angle;
  
  c = sqrt(((a->endpoints[1].x - a->endpoints[0].x) *
	    (a->endpoints[1].x - a->endpoints[0].x)) +
	   ((a->endpoints[1].y - a->endpoints[0].y) *	
	     (a->endpoints[1].y - a->endpoints[0].y)));

  /* axis rotated around (x0, y0) origin */

  x1 = a->endpoints[0].x + (int)(c * cos(angle));
  y1 = a->endpoints[0].y + (int)(c * sin(angle));
  AtAxis3SetLocation(axis,
		     a->endpoints[0].x,
  		     a->endpoints[0].y,
		     x1, y1);
#ifdef DEBUGAX
  printf("AtAxis3SetAngle ends\n");
#endif DEBUGAX
}


void AtAxis3Draw(Display *dpy, Drawable win, AtAxis3Object axis)
{
  /* assumes ntics, nsubs, ticsVertical, texts and values of labels,
     ticX/YCoords, subX/YCoords, endpoints */
  
  AtAxis3Part *a = &axis->axis3;
  int tx1, tx2, tx3, tx4, ty1, ty2;
  int stx1, stx2, stx3, stx4, sty1, sty2;
  int i, j, k;

#ifdef DEBUGAX
  printf("AtAxis3Draw begins \n");
#endif DEBUGAX
  /* draw axis spine */
  XDrawLine(dpy, win, a->axisGC, a->endpoints[0].x,
	    a->endpoints[0].y,
	    a->endpoints[1].x,
	    a->endpoints[1].y);
  tx1 = tx2 = tx3 = tx4 = ty1 = ty2 = 0;
  stx1 = stx2 = stx3 = stx4 = sty1 = sty2 = 0; 
  
  /* draw tics, subtics */
  if(a->ticsVertical) {
    ty1 = ((pBOTTOM - a->ticSide) * a->ticLength);
    ty2 = a->ticSide * a->ticLength;
    sty1 = ((pBOTTOM - a->ticSide) * a->subticLength);
    sty2 = a->ticSide * a->subticLength;
  }

  else {
    tx1 = ((pRIGHT - a->ticSide) * a->ticLength);
    tx2 = a->ticSide * a->ticLength;
    stx1 = ((pRIGHT - a->ticSide) * a->ticLength);
    stx2 = a->ticSide  * a->subticLength;
  }
  k = 0;
  for(i = 0; i < a->ntics - 1; i++) {
    XDrawLine(dpy, win, a->axisGC,
	      a->ticXCoords[i] - tx1,
	      a->ticYCoords[i] - ty1,
	      a->ticXCoords[i] - tx2,
	      a->ticYCoords[i] - ty2);
    for (j = 0; i < a->nsubs; j++) {
      XDrawLine(dpy, win, a->axisGC,
		a->subXCoords[k] - stx1,
		a->subYCoords[k] - sty1,
		a->subXCoords[k] - stx2,
		a->subYCoords[k] - sty2);
      k++;
    }
  } 
  XDrawLine(dpy, win, a->axisGC,
	    a->ticXCoords[i] - tx1,
	    a->ticYCoords[i] - ty1,
	    a->ticXCoords[i] - tx2,
	    a->ticYCoords[i] - ty2);

  /* draw numbers */
  a->maxticwidth = 0;
  if(a->drawNumbers) {
    for(i =1; i < a->ntics; i++) { /* does not actually draw 1st number */
      /* this keeps 1st num of y1 axis from overlapping last num of x1 axis,
	 etc. */
      short x, y, w, l;
      l = strlen(a->ticlabels[i]);
      w = XTextWidth(a->numberFont, a->ticlabels[i], l);
      
      if(w > a->maxticwidth) a->maxticwidth = w;
      x = a->ticXCoords[i];
      y = a->ticYCoords[i];

      if(a->ticsVertical) {
	x -= w/2;
	if(a->labelSide == pTOP)
	  y -= (2 + ty1 + a->numberFont->descent);
	else 
	  y += (2 + ty1 + a->numberFont->descent);
      }
      else {
	y += a->numberFont->ascent/2 - a->numberFont->descent/2;
	if(a->labelSide == pLEFT)
	  x -= (2 + tx1);
	else 
	  x += (2 + tx2);
      }
      XDrawString(dpy, win, a->numberGC, x, y, a->ticlabels[i], l);
    }
  }

  /* draw label */
  if((a->labelText) && (a->drawLabel)) {
    int x, y;
    
    x = (a->endpoints[0].x + a->endpoints[1].x)/2;
    y = (a->endpoints[0].y + a->endpoints[1].y)/2;
    
    if(a->ticsVertical) {
      if(a->labelSide == pTOP) {
	y -= (ty1 + 2 + AtTextDescent(a->labelText));
	if(a->drawNumbers)
	  y -= (a-> numberFont->ascent + a->numberFont->descent);
      } /* pTOP */
      else {
	y += 2 + ty2 + AtTextAscent(a->labelText);
	if(a->drawNumbers)
	  y += (a->numberFont->ascent + a->numberFont->descent);
      }
      x -= (AtTextWidth(a->labelText)/2);
    }
    else {
      if(a->labelSide == pLEFT) 
	x -= (tx1 + 2 + AtTextWidth(a->labelText) + a->maxticwidth);
      else
	x += (tx2 + 2 + a->maxticwidth);
      y += (AtTextAscent(a->labelText)/2 - AtTextDescent(a->labelText)/2);
    }
    
    AtTextDraw(dpy, win, a->labelGC, a->labelText, x, y);
  }
}
   

   


int AtAxis3Length(AtAxis3Object axis)
{
#define EP(ax, i) ax->axis3.endpoints[i]
#define Sq(n) n * n
  return(sqrt(Sq(EP(axis, 1).x - EP(axis, 0).x) + 
	      Sq(EP(axis, 1).y - EP(axis, 0).y)));
#undef EP
#undef Sq
}


int AtAxis3Width(AtAxis3Object axis)
{
  AtAxis3Part *a = &axis->axis3;
  
  if(a->ticsVertical) 
    return(((a->labelText)?AtTextWidth(a->labelText):0) +
      MIN_MARGIN + a->maxticwidth + a->ticLength + 1);
  else 
    return(((a->labelText)?AtTextHeight(a->labelText):0) + 
	   MIN_MARGIN + a->numberFont->ascent
	   + a->numberFont->descent + a->ticLength + 1);
}


static char *NewTicLabel(double value)
{
  char *result;
  static char labstr[20];
  sprintf(labstr, "%d", value);
  result = (char *)malloc(sizeof(char) * strlen(labstr));
  strcpy(result, labstr);
  return(result);
}


void AtAxis3ComputeTicSpacing(AtAxis3Object w, int length)
{
  double min, max;
  int i, width;
  AtAxis3Part *a = &w->axis3;
  double ti;
  int d, firstd, lastd;
  int n;
	  
#ifdef DEBUGAX
  printf("AtAxis3ComputeTicSpacing begins. Here a->min = %f, a->max = %f\n",
	 a->min, a->max);
#endif DEBUGAX
  if(length <= 0) length = 1;
  if(a->autoNumber) AutoNumber(w, length);
  
  /* tic vals */
#ifdef DEBUGAX
  printf("Axis3.c: AtAxis3ComputeTicSpacing: computes tic values.\n");
  printf("     (Here, min = a->min = %f and a->max = %f)\n", 
	 a->min,  a->max);
#endif DEBUGAX
  if(a->transform == AtTransformLINEAR) {
    double min = a->min;

    min = ceil(min/a->ticInterval) * a->ticInterval;
    printf("     (Now min = %d)\n", min);
    for(i = 0; i < a->ntics; i++) {
      a->ticvalues[i] = ffix(min + i * a->ticInterval);
#ifdef DEBUGAX
      printf("Axis3.c:     (a->ticvalues[%d] = %f, min = %f)\n",
	     i, a->ticvalues[i], min);
#endif DEBUGAX
    }
  }
  else {
    double tic;
    firstd = (int)floor(log10(a->min));
    lastd = (int)floor(log10(a->max));
    n = 0;
    for(d = firstd; d <= lastd; d++) {
      /* for each decade */
      a->ticvalues[n++] = pow(10.0, (double)d);
      for(tic = a->ticInterval; tic < 10.0; tic += a->ticInterval){
	a->ticvalues[n++] = pow(10.0, (double)d + log10(tic));
#ifdef DEBUGAX
	printf("Axis3.c:     (a->ticvalues[%d] = %f, min = %f)\n",
	       i, a->ticvalues[n], min);
#endif DEBUGAX
      }
    }
  }
  a->maxticwidth = 0;
  for(i = 0; i < a->ntics; i++) {
    a->ticlabels[i] = NewTicLabel(a->ticvalues[i]);
    width = XTextWidth(a->numberFont, a->ticlabels[i],
		       strlen(a->ticlabels[i]));
    if(width > a->maxticwidth)
      a->maxticwidth = width;
  }
#ifdef DEBUGAX
  printf("AtAxis3ComputeTicSpacing ends with a->min %f and a->max %f\n",
	 a->min, a->max);
#endif DEBUGAX


}


static void AutoNumber(AtAxis3Object w, int length)
{
  AtAxis3Part *a = &w->axis3;
  int fontheight, ntics, nsubs;
  
#ifdef DEBUGAX
  printf("AutoNumber begins\n");
#endif DEBUGAX
  fontheight = a->numberFont->ascent + a->numberFont->descent;

#ifdef DEBUGAX
  printf("AutoNumber: fontheight = %d\n",fontheight);
#endif DEBUGAX

  ntics = a->ticsPerAxis;
#ifdef DEBUGAX
  printf("AutoNumber: ntics = %d\n", ntics);
#endif DEBUGAX
  
  if(length/(ntics - 1) < 2 * fontheight)
    ntics = 1 + (int)(length / (2 * fontheight));
  else ntics = ((ntics > 1)?
		(ntics): 1);

#ifdef DEBUGAX
  printf("AutoNumber: new value for ntics is %d\n", ntics);
#endif DEBUGAX


  if(ntics < 2) nsubs = 0;
  else {
#ifdef DEBUGAX
    printf("AutoNumber: ntics is not < 2 so compute nsubs\n");
#endif DEBUGAX
    nsubs = a->subsPerTic;
#ifdef DEBUGAX
    printf("AutoNumber: initially, nsubs = %d\n", nsubs);
#endif DEBUGAX

    if (length/(nsubs * (ntics - 1)) < 5)
      nsubs = length / (5 * (ntics - 1));
    a->nsubs = nsubs;

#ifdef DEBUGAX
    printf("AutoNumber: new value for nsubs is %d\n", nsubs);
#endif DEBUGAX
    
    if(a->transform == AtTransformLINEAR) {
      double mag, flr, d, sizeticratio;
      int mult;
      d = 1.0;
      mag = log10(fabs(a->max - a->min));
      flr = floor(mag);
      sizeticratio = pow(10.0, mag - flr)/ntics;
      
      while(1) {
#ifdef DEBUGAX
	printf("AutoNumber: sizeticratio = %f\n");
#endif DEBUGAX
	
	if(sizeticratio  >2.857){
	  mult = 5; 
	  break;}
	if(sizeticratio  >1.333) {
	  mult = 2;
	  break;}
	if(sizeticratio  >0.6666) {
	  mult = 1;
	  break;}
	d /= 10.;
      }
      
      a->ticInterval = mult * d * pow(10.0, flr);
#ifdef DEBUGAX
    printf("AutoNumber: new value for a->ticInterval is %f\n", a->ticInterval);
#endif DEBUGAX
      
    }

    else {
      double tpd = ntics / (log10(a->max) - log10(a->min));

      if(tpd > 7.5)
	a->ticInterval = 1.0;
      if(tpd > 3.5)
	a->ticInterval = 2.0;
      if(tpd >1.5)
	a->ticInterval = 5.0;
      else a->ticInterval = 10.0;
    } /* if !LINEAR */

    RecalcNumTics(w);
  } /* if ntics != 0 */

#ifdef DEBUGAX
  printf("AutoNumber ends. Here, a->min = %f, a->max = %f\n",
	 a->min, a->max);
#endif DEBUGAX

}
	

static void RecalcNumTics(AtAxis3Object w)
{
  AtAxis3Part *a = &w->axis3;
  short oldtics = a->ntics;
  int i;

#ifdef DEBUGAX
  printf("RecalcNumTics begins\n");
#endif DEBUGAX
  if(a->transform == AtTransformLINEAR)
    {
      double min = a->min;
      double max = a->max;
      min = ceil(min/a->ticInterval) * a->ticInterval;
      max = floor(max/a->ticInterval) * a->ticInterval;
      a->ntics = 1 + (int)((max - min) / a->ticInterval);
      a->nsubs = a->subsPerTic;
    }
  else  {
    a->ntics = 1 + (int)((log10(a->max) - log10(a->min)) * 
			 10.0 / a->ticInterval);
    a->nsubs = a->subsPerTic;
  }
  if(a->ntics != oldtics) {
    /* reallocate storage for arrays */
    a->ticvalues = (double *)XtRealloc(a->ticvalues, a->ntics *
				       sizeof(double));
    a->ticXCoords = (short *)XtRealloc(a->ticXCoords,
				       a->ntics * 
				       sizeof(short));
    a->ticYCoords = (short *)XtRealloc(a->ticYCoords,
				       a->ntics * 
				       sizeof(short));
    for(i = 0; i < oldtics; i++)
      if(a->ticlabels[i] != NULL) XtFree(a->ticlabels);
    a->ticlabels = (char **)XtRealloc(a->ticlabels,
					a->ntics * 
				      sizeof(char *));
    for (i = 0; i < a->ntics; i++) a->ticlabels[i] = NULL;
  }
  a->subXCoords = (short *)XtRealloc(a->subXCoords,
				     sizeof(short) * 
				     (a->ntics - 1) *
				     a->nsubs);
  a->subYCoords = (short *)XtRealloc(a->subYCoords,
				     sizeof(short) * 
				     (a->ntics - 1) *
				     a->nsubs);
#ifdef DEBUGAX
  printf("RecalcNumTics ends\n");
#endif DEBUGAX
}


      


      
    

  
  
	    
   
    
