/*
 *      AxisCore.c
 *
 *      The AthenaTools Plotter Widget Set - Version 6.0
 *
 *      klin, Tue Jul  7 13:59:47 1992
 *      klin, Wed Jul 22 09:29:41 1992, patchlevel 1
 *                                      Bug in SetValues() fixed (reported by
 *                                      Ken Rempe 92/07/21, ken@caesar.uucp)
 *      klin, Mon Jul 27 14:16:10 1992, patchlevel 2
 *                                      Added new resource XtNnumberWidth
 *                                      and function AtAxisGetNumberWidth().
 *                                      Draw() changed for drawing
 *                                      to a pixmap instead of a window.
 *                                      Shorter procedure names.
 *      klin, Fri Aug 14 15:45:52 1992, patchlevel 4
 *                                      Minor changes in PS output.
 *                                      Changed <At/..> to <X11/At/..>.
 */
static char SCCSid[] = "@(#) Plotter V6.0  92/08/15  AxisCore.c";

/*

Copyright 1992 by University of Paderborn
Copyright 1991 by Burdett, Buckeridge & Young Ltd.

All rights reserved.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of the firms, institutes
or employers of the authors not be used in advertising or publicity
pertaining to distribution of the software without specific, written
prior permission.

THE AUTHORS AND THEIR FIRMS, INSTITUTES OR EMPLOYERS DISCLAIM ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHORS AND THEIR FIRMS,
INSTITUTES OR EMPLOYERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

*/

/*
 *   This contains the heart of the pixel fiddling code for the axes.
 *   It is a meta class that relies on subclass methods to decide where
 *   and what to label the axes, and then handles all the calculations,
 *   redisplays etc internally.
 *
 *   Should not be instansiated, but will sort-of work if required.
 */

#include <X11/At/AxisCoreP.h>
#include <X11/At/AtConverters.h>

#define MARGIN      2    /* Pixels between elemnts - will be a resource */
#define PS_MARGIN   6    /* Same for PS */

#define SubticOnTic(ac, i, j) (ac->subtic_pos[i] > (ac->tic_pos[j] - 2) && \
			       ac->subtic_pos[i] < (ac->tic_pos[j] + 2))

static void ClassPartInitialize P((WidgetClass));
static void ClassInitialize P((WidgetClass));
static void Initialize P((AtAxisCoreWidget, AtAxisCoreWidget));
static void Destroy P((AtAxisCoreWidget));
static Boolean SetValues P((AtAxisCoreWidget, AtAxisCoreWidget, AtAxisCoreWidget));
static void Draw P((AtPlotWidget, Display *, Drawable, Region, int));
static void Recalc P((AtPlotWidget, AtScale *, AtScale *, int, int));
static void RangeProc P((AtAxisCoreWidget, double *, double *, double *, int *));
static void CalcProc P((AtAxisCoreWidget));

/* A helper routine for handling labels */

static void ReformatLabels P((AtAxisCoreWidget, int));
static void CalcAxisWidth P((AtAxisCoreWidget));

/* The resources */

static double dflt_min = LIN_MIN;
static double dflt_max = LIN_MAX;
static double dflt_tic = TIC_INT;
#define off(field) XtOffsetOf(AtAxisCoreRec, axiscore.field)
static XtResource resources[] = {
  {
     XtNmax, XtCMax,
     XtRDouble, sizeof(double),
     off(max), XtRDouble, (XtPointer) &dflt_max
  },
  {
     XtNmin, XtCMin,
     XtRDouble, sizeof(double),
     off(min), XtRDouble, (XtPointer) &dflt_min
  },
  {
     XtNticInterval, XtCTicInterval,
     XtRDouble, sizeof(double),
     off(tic_interval), XtRImmediate, (XtPointer) &dflt_tic
  },
  {
     XtNvertical, XtCVertical,
     XtRBoolean, sizeof(Boolean),
     off(vertical), XtRImmediate, (XtPointer) False
  },
  {
     XtNmirror, XtCMirror,
     XtRBoolean, sizeof(Boolean),
     off(mirror), XtRImmediate, (XtPointer) False
  },
  {
     XtNrangeCallback, XtCCallback,
     XtRCallback, sizeof(XtCallbackList),
     off(range_callback), XtRImmediate, (XtPointer) NULL
  },
  {
     XtNlabel, XtCLabel,
     XtRString, sizeof(String),
     off(label), XtRImmediate, (XtPointer) NULL
  },
  {
     XtNfontFamily, XtCFontFamily,
     XtRString, sizeof(String),
     off(font_family), XtRImmediate, (XtPointer) NULL
		    /* XtRCallProc, (XtPointer) defFF */
  },
  {
     XtNlabelSize, XtCFontSize,
     XtRFontSize, sizeof(int),
     off(label_size), XtRImmediate, (XtPointer) DFLT_FONTNORMAL
  },
  {
     XtNlabelStyle, XtCFontStyle,
     XtRFontStyle, sizeof(int),
     off(label_style), XtRImmediate, (XtPointer) AtFontPLAIN
  },
  {
     XtNlabelColor, XtCForeground,
     XtRPixel, sizeof(Pixel),
     off(label_color), XtRString, XtDefaultForeground
  },
  {
     XtNnumberWidth, XtCNumberWidth,
     XtRDimension, sizeof(Dimension),
     off(default_number_width), XtRImmediate, (XtPointer) 0
  },
  {
     XtNnumberSize, XtCFontSize,
     XtRFontSize, sizeof(int),
     off(number_size), XtRImmediate, (XtPointer) DFLT_FONTSMALL
  },
  {
     XtNnumberStyle, XtCFontStyle,
     XtRFontStyle, sizeof(int),
     off(number_style), XtRImmediate, (XtPointer) AtFontPLAIN
  },
  {
     XtNnumberColor, XtCForeground,
     XtRPixel, sizeof(Pixel),
     off(number_color), XtRString, XtDefaultForeground
  },
  {
     XtNticsInside, XtCTicsInside,
     XtRBoolean, sizeof(Boolean),
     off(tics_inside), XtRImmediate, (XtPointer) False
  },
  {
     XtNticsOutside, XtCTicsOutside,
     XtRBoolean, sizeof(Boolean),
     off(tics_outside), XtRImmediate, (XtPointer) True
  },
  {
     XtNticLength, XtCTicLength,
     XtRDimension, sizeof(Dimension),
     off(tic_length), XtRImmediate, (XtPointer) 5
  },
  {
     XtNsubticLength, XtCTicLength,
     XtRDimension, sizeof(Dimension),
     off(subtic_length), XtRImmediate, (XtPointer) 2
  },
  {
     XtNdrawNumbers, XtCDrawNumbers,
     XtRBoolean, sizeof(Boolean),
     off(draw_numbers), XtRImmediate, (XtPointer) True
  },
  {
     XtNnumbersOutside, XtCNumbersOutside,
     XtRBoolean, sizeof(Boolean),
     off(numbers_outside), XtRImmediate, (XtPointer) True
  },
  {
     XtNdrawGrid, XtCDrawGrid,
     XtRBoolean, sizeof(Boolean),
     off(draw_grid), XtRImmediate, (XtPointer) True
  },
  {
     XtNdrawSubgrid, XtCDrawSubgrid,
     XtRBoolean, sizeof(Boolean),
     off(draw_subgrid), XtRImmediate, (XtPointer) False
  },
  {
     XtNdrawOrigin, XtCDrawOrigin,
     XtRBoolean, sizeof(Boolean),
     off(draw_origin), XtRImmediate, (XtPointer) True
  },
  {
     XtNdrawFrame, XtCDrawFrame,
     XtRBoolean, sizeof(Boolean),
     off(draw_frame), XtRImmediate, (XtPointer) True
  },
  {
     XtNaxisWidth, XtCAxisWidth,
     XtRDimension, sizeof(Dimension),
     off(axis_linewidth), XtRImmediate, (XtPointer) 1
  },
  {
     XtNaxisColor, XtCForeground,
     XtRPixel, sizeof(Pixel),
     off(axis_color), XtRString, (XtPointer) XtDefaultForeground
  }
};
#undef off

AtAxisCoreClassRec atAxisCoreClassRec = {
  { /* core fields */
     /* superclass              */      (WidgetClass) &atPlotClassRec,
     /* class_name              */      "AtAxisCore",
     /* widget_size             */      sizeof(AtAxisCoreRec),
     /* class_initialize        */      (XtProc) ClassInitialize,
     /* class_part_initialize   */      (XtWidgetClassProc) ClassPartInitialize,
     /* class_inited            */      FALSE,
     /* initialize              */      (XtInitProc) Initialize,
     /* initialize_hook         */      NULL,
     /* pad                     */      NULL,
     /* pad                     */      NULL,
     /* pad                     */      0,
     /* resources               */      resources,
     /* num_resources           */      XtNumber(resources),
     /* xrm_class               */      NULLQUARK,
     /* pad                     */      FALSE,
     /* pad                     */      FALSE,
     /* pad                     */      FALSE,
     /* pad                     */      FALSE,
     /* destroy                 */      (XtWidgetProc) Destroy,
     /* pad                     */      NULL,
     /* pad                     */      NULL,
     /* set_values              */      (XtSetValuesFunc) SetValues,
     /* set_values_hook         */      NULL,
     /* pad                     */      NULL,
     /* get_values_hook         */      NULL,
     /* pad                     */      NULL,
     /* version                 */      XtVersion,
     /* callback_private        */      NULL,
     /* pad                     */      NULL,
     /* pad                     */      NULL,
     /* pad                     */      NULL,
     /* pad                     */      NULL
     },
  { /* atPlot fields */
     /* draw                    */      Draw,
     /* draw_icon               */      XtInheritDrawIcon,
     /* drawPS                  */      XtInheritDrawPS,
     /* draw_iconPS             */      XtInheritDrawIconPS,
     /* recalc                  */      Recalc
  },
  { /* axisCore fields */
     /* range_proc              */      (AtAxisRangeProc) RangeProc,
     /* calc_proc               */      (AtAxisCalcProc) CalcProc
  }
};

WidgetClass atAxisCoreWidgetClass = (WidgetClass)&atAxisCoreClassRec;

/*
 *   The core member procs
 */

static void ClassPartInitialize(wc)
WidgetClass wc;
{
     AtAxisCoreWidgetClass super =
	  (AtAxisCoreWidgetClass) wc->core_class.superclass;
     AtAxisCoreWidgetClass spc = (AtAxisCoreWidgetClass) wc;

#define CheckInherit(fld, inherit) \
     if (spc->axiscore_class.fld == inherit) \
	  spc->axiscore_class.fld = super->axiscore_class.fld;

     CheckInherit(range_proc, XtInheritRangeProc);
     CheckInherit(calc_proc, XtInheritCalcProc);
#undef CheckInherit
     *SCCSid = *SCCSid;       /* Keeps gcc quiet */
}

static void ClassInitialize(wc)
WidgetClass wc;
{
     AtRegisterFontSizeConverter();
     AtRegisterFontStyleConverter();
}

/*
 *   Some helper functions for GC management
 */

static void GetLabelGC P((AtAxisCoreWidget ac));
static void GetLabelGC(ac)
AtAxisCoreWidget ac;
{
     XGCValues v;

     v.foreground = ac->axiscore.label_color;
     v.background = ac->plot.background;
     ac->axiscore.label_gc = XtGetGC(XtParent((Widget) ac),
				     GCForeground | GCBackground, &v);
}

static void GetAxisGC P((AtAxisCoreWidget ac));
static void GetAxisGC(ac)
AtAxisCoreWidget ac;
{
     XGCValues v;
     XtGCMask mask;

     v.foreground = ac->axiscore.axis_color;
     v.line_width = ac->axiscore.axis_linewidth;
     v.line_style = LineSolid;
     mask = GCForeground | GCLineWidth | GCLineStyle;
     ac->axiscore.axis_gc =
	  XtGetGC(XtParent((Widget) ac), mask, &v);
}

static void GetTicGC P((AtAxisCoreWidget ac));
static void GetTicGC(ac)
AtAxisCoreWidget ac;
{
     XGCValues v;

     v.foreground = ac->axiscore.axis_color;
     v.line_style = LineSolid;
     ac->axiscore.tic_gc =
	  XtGetGC(XtParent((Widget) ac), GCForeground | GCLineStyle, &v);
}

static void GetNumberGC P((AtAxisCoreWidget ac));
static void GetNumberGC(ac)
AtAxisCoreWidget ac;
{
     XGCValues v;

     v.foreground = ac->axiscore.number_color;
     v.background = ac->plot.background;
     ac->axiscore.number_gc = XtGetGC(XtParent((Widget) ac),
				      GCForeground | GCBackground, &v);
}

static void GetGridGC P((AtAxisCoreWidget ac));
static void GetGridGC(ac)
AtAxisCoreWidget ac;
{
     XGCValues v;

     v.foreground = ac->axiscore.axis_color;
     v.line_style = LineOnOffDash;
     v.dashes = (char)1;

     ac->axiscore.grid_gc =
	  XtGetGC(XtParent((Widget) ac),
		  GCForeground | GCLineStyle | GCDashList, &v);
}

static void GetSubgridGC P((AtAxisCoreWidget ac));
static void GetSubgridGC(ac)
AtAxisCoreWidget ac;
{
     static char dashes[] = { 1, 3 };
     XGCValues v;

     v.foreground = ac->axiscore.axis_color;
     v.line_style = LineOnOffDash;
     v.dashes = (char) 222;

     ac->axiscore.subgrid_gc =
	  XtGetGC(XtParent((Widget) ac),
		  GCForeground | GCLineStyle | GCDashList, &v);
     XSetDashes(XtDisplay(XtParent((Widget) ac)), ac->axiscore.subgrid_gc,
			  0, dashes, 2);

}

#define FreeLabelGC(w)   XtReleaseGC((Widget) w, w->axiscore.label_gc)
#define FreeAxisGC(w)    XtReleaseGC((Widget) w, w->axiscore.axis_gc)
#define FreeTicGC(w)     XtReleaseGC((Widget) w, w->axiscore.tic_gc);
#define FreeNumberGC(w)  XtReleaseGC((Widget) w, w->axiscore.number_gc)
#define FreeGridGC(w)    XtReleaseGC((Widget) w, w->axiscore.grid_gc)
#define FreeSubgridGC(w) XtReleaseGC((Widget) w, w->axiscore.subgrid_gc)

/*
 *   Helper routines for the AtText management
 */

static void GetLabelText P((AtAxisCoreWidget acw));
static void GetLabelText(acw)
AtAxisCoreWidget acw;
{
     AtAxisCorePart *ac = &acw->axiscore;

     if (ac->label && *ac->label) {
	  ac->label_text =
	       AtTextCreate(ac->label, ac->ff, ac->label_size,
			    ac->label_style);
	  if (ac->vertical)
	       AtTextRotate(ac->label_text);
     } else
	  ac->label_text = NULL;
}

#define FreeLabelText(a) AtTextDestroy(a->axiscore.label_text)

#define GetFF(a) \
     a->axiscore.ff = \
     AtFontFamilyGet(XtDisplay(XtParent((Widget) a)), \
		     a->axiscore.font_family);
#define FreeFF(a)  AtFontFamilyRelease(a->axiscore.ff);

#define CopyString(str) str = XtNewString(str)

/*
 *   The initialize/destroy/set values procs
 */

static void Initialize(req, new)
AtAxisCoreWidget req, new;
{
     if ( !new->axiscore.label)
	  new->axiscore.label = "";
     CopyString(new->axiscore.label);
     if ( !new->axiscore.font_family)
	  XtVaGetValues(XtParent((Widget) new), XtNfontFamily,
			&new->axiscore.font_family, NULL);
     CopyString(new->axiscore.font_family);
     GetFF(new);
     GetLabelText(new);

     GetLabelGC(new);
     GetAxisGC(new);
     GetTicGC(new);
     GetNumberGC(new);
     GetGridGC(new);
     GetSubgridGC(new);

     new->axiscore.scale =
	  AtScaleCreate(new->axiscore.min, new->axiscore.max, 0, 1,
			AtTransformLINEAR);
     if (new->axiscore.min >= new->axiscore.max) {
	  XtAppWarning(XtWidgetToApplicationContext(XtParent((Widget) new)),
		       "Min is >= Max in AtAxisCore");
	  new->axiscore.min = 0.0;
	  new->axiscore.max = 1.0;
     }

     new->axiscore.num_ticsegments = new->axiscore.num_tics =
	  new->axiscore.num_subtics = 0;
     new->axiscore.x1 = new->axiscore.y1 = 0;
     new->axiscore.x2 = new->axiscore.x2 = 10;
     new->axiscore.grid_length = 10;
     new->axiscore.max_num_width = new->axiscore.actual_num_width =
	  new->axiscore.axis_width = new->axiscore.label_line =
	  new->axiscore.tic_label_line = 0;
     new->axiscore.tic_values = NULL;
     new->axiscore.tic_label_string = NULL;
     new->axiscore.subtic_values = NULL;
     new->axiscore.tic_segments = NULL;
     new->axiscore.tic_pos = NULL;
     new->axiscore.subtic_pos = NULL;
     new->axiscore.grid_segments = NULL;
     new->axiscore.subgrid_segments = NULL;
     new->axiscore.tic_label_text = NULL;
     new->axiscore.numbers_changed = new->axiscore.position_changed = True;
}

static void Destroy(ac)
AtAxisCoreWidget ac;
{
     XtFree(ac->axiscore.label);
     XtFree(ac->axiscore.font_family);
     XtFree((char *) ac->axiscore.tic_segments);
     XtFree((char *) ac->axiscore.grid_segments);
     XtFree((char *) ac->axiscore.subgrid_segments);
     FreeFF(ac);
     FreeLabelText(ac);
     FreeLabelGC(ac);
     FreeAxisGC(ac);
     FreeTicGC(ac);
     FreeNumberGC(ac);
     FreeGridGC(ac);
     FreeSubgridGC(ac);
     AtScaleDestroy(ac->axiscore.scale);
}

static Boolean SetValues(old, req, new)
AtAxisCoreWidget old, req, new;
{
#define Changed(fld)      (old->axiscore.fld != new->axiscore.fld)
     Boolean refresh = False;
     Boolean redraw = False;
     Boolean recalc = False;
     Boolean relayout = False;
     int old_w, new_w;
     AtAxisCorePart *ac = &new->axiscore;

     if (Changed(min) || Changed(max)) {
	  recalc = redraw = True;
	  ac->numbers_changed = True;
     }
     if (new->axiscore.min >= new->axiscore.max) {
	  XtAppWarning(XtWidgetToApplicationContext(XtParent((Widget) new)),
		       "Min is >= Max in AtAxisCore");
	  new->axiscore.min = old->axiscore.min;
	  new->axiscore.max = old->axiscore.max;
     }

     if (Changed(tic_interval)) {
	  recalc = True;
	  ac->numbers_changed = True;
     }

     if (Changed(vertical)) {
	  XtAppWarning(XtWidgetToApplicationContext(XtParent((Widget) new)),
		       "Can't change XtNvertical for an axis");
	  ac->vertical = old->axiscore.vertical;
     }
     if (Changed(mirror)) {
	  XtAppWarning(XtWidgetToApplicationContext(XtParent((Widget) new)),
		       "Can't change XtNmirror for an axis");
	  ac->vertical = old->axiscore.vertical;
     }
     if (Changed(label) || Changed(font_family) ||
	 Changed(label_style) || Changed(label_size)) {
	  old_w = (ac->vertical ? AtTextWidth(ac->label_text) :
		   AtTextHeight(ac->label_text));
	  FreeLabelText(new);
	  GetLabelText(new);
	  new_w = (ac->vertical ? AtTextWidth(ac->label_text) :
		   AtTextHeight(ac->label_text));
	  redraw = True;
	  if (old_w != new_w) {
	       relayout = True;
	  }
     }
     if (Changed(label_color)) {
	  FreeLabelGC(new);
	  GetLabelGC(new);
	  refresh = True;
     }
     if (Changed(font_family) || Changed(number_size) || Changed(number_style)) {
	  redraw = True;
	  ReformatLabels(new, True);
     }
     if (Changed(default_number_width)) {
	  relayout = True;
     }
     if (Changed(label)) {
	  XtFree(old->axiscore.label);
	  CopyString(ac->label);
	  redraw = True;
     }
     if (Changed(font_family)) {
	  XtFree(old->axiscore.font_family);
	  CopyString(ac->font_family);
	  FreeFF(new);
	  GetFF(new);
     }
     if (Changed(number_color)) {
	  FreeNumberGC(new);
	  GetNumberGC(new);
	  refresh = True;
     }
     if (Changed(axis_color)) {
	  FreeAxisGC(new);
	  FreeTicGC(new);
	  FreeGridGC(new);
	  FreeSubgridGC(new);
	  GetAxisGC(new);
	  GetTicGC(new);
	  GetGridGC(new);
	  GetSubgridGC(new);
	  redraw = True;
     }
     else if (Changed(axis_linewidth)) {
	  FreeAxisGC(new);
	  GetAxisGC(new);
	  redraw = True;
     }

     if (Changed(draw_grid) || Changed(draw_subgrid)) {
	  redraw = True;
     }
     if (Changed(draw_origin) || Changed(draw_frame)) {
	  redraw = True;
     }

     if (Changed(tics_inside) || Changed(tics_outside) ||
	 Changed(subtic_length) || Changed(tic_length) ||
	 Changed(numbers_outside)) {
	  ac->position_changed = True;
	  relayout = True;
	  redraw = True;
     }

     if (ac->tics_outside && (Changed(subtic_length) || Changed(tic_length))) {
	  relayout = True;
     }

     if (recalc)
	  AtPlotterRecalcThisPlot((AtPlotWidget)new);
     if (relayout) {
	  AtPlotterLayoutRequired((AtPlotWidget)new);
	  CalcAxisWidth(new);
     }
     if (redraw)
	  AtPlotterRedrawRequired((AtPlotWidget)new);
     else if (refresh)
	  AtPlotterRefreshRequired((AtPlotWidget)new);
     return False;
#undef Changed
}

static void CalcAxisWidth(acw)
AtAxisCoreWidget acw;
{
     AtAxisCorePart *ac = &acw->axiscore;
     int tl, mw;

     tl =  ac->tics_outside ? Max(0, Max(ac->tic_length,
					 ac->subtic_length)) : 0;
     mw = ac->default_number_width > 0 ? ac->default_number_width :
					 ac->max_num_width;

     if (ac->vertical) {
	  ac->axis_width = MARGIN + tl +
	       (ac->label_text ? AtTextWidth(ac->label_text) + MARGIN : 0)
		    + (ac->draw_numbers && ac->numbers_outside ?
		       mw + MARGIN : 0);
     } else {
	  ac->axis_width = MARGIN + tl +
	       (ac->label_text ? AtTextHeight(ac->label_text) + MARGIN : 0)
		    + (ac->draw_numbers && ac->numbers_outside ?
		       mw + MARGIN : 0);
     }
}

/*
 *   The AtPlot member proc
 */

static void Draw(pw, dpy, drw, region, refresh)
AtPlotWidget pw;
Display *dpy;
Drawable drw;
Region region;
int refresh;
{
     AtAxisCorePart *ac = &(((AtAxisCoreWidget)pw)->axiscore);
     Window win = XtWindow(XtParent((Widget) pw));
     int i;

     /* Draw axis/origin/frame */
     if (region)
	   XSetRegion(dpy, ac->axis_gc, region);
     XDrawSegments(dpy, drw, ac->axis_gc, &ac->axis_segment, 1);
     if (ac->draw_origin && ac->origin_segment.x1 > 0)
	  XDrawSegments(dpy, drw, ac->axis_gc, &ac->origin_segment, 1);
     if (ac->draw_frame)
	  XDrawSegments(dpy, drw, ac->axis_gc, &ac->frame_segment, 1);
     if (region)
	  XSetClipMask(dpy, ac->axis_gc, None);

     /* Now the tics and subtics */
     if (ac->num_ticsegments) {
	  if (region)
	       XSetRegion(dpy, ac->tic_gc, region);
	  XDrawSegments(dpy, drw, ac->tic_gc, ac->tic_segments,
			ac->num_ticsegments);
	  if (region)
	       XSetClipMask(dpy, ac->tic_gc, None);
     }

     /* Now the label */
     if (ac->label_text) {
	  if (ac->vertical)
	       AtTextDrawJustified(dpy, win, drw,
				   ac->label_gc, ac->label_text,
				   AtTextJUSTIFY_CENTER,
				   AtTextJUSTIFY_CENTER,
				   ac->label_line, ac->y2,
				   AtTextWidth(ac->label_text),
				   ac->y1 - ac->y2);
	  else
	       AtTextDrawJustified(dpy, win, drw,
				   ac->label_gc, ac->label_text,
				   AtTextJUSTIFY_CENTER,
				   AtTextJUSTIFY_CENTER,
				   ac->x1, ac->label_line,
				   ac->x2 - ac->x1,
				   -AtTextHeight(ac->label_text));
     }

     /* Now the numbers */
     if (ac->draw_numbers) {
	  if (region)
	       XSetRegion(dpy, ac->number_gc, region);
	  if (ac->vertical) {
	       for (i = 0; i < ac->num_tics; i++) {
		    int ypos;

		    if (!i) {
			 /* First is flush to Bottom */
			 ypos = ac->tic_pos[i];
		    } else if (i == ac->num_tics - 1) {
			 /* Last is flush to Top - which is low pixel no! */
			 ypos = ac->tic_pos[i] +
			      AtTextAscent(ac->tic_label_text[i]);
		    } else {
			 /* Rest centered on tic */
			 ypos = ac->tic_pos[i] +
			      AtTextHeight(ac->tic_label_text[i]) / 2;
		    }
		    AtTextDraw(dpy, win, drw, ac->number_gc,
			       ac->tic_label_text[i], ac->tic_label_line, ypos);
	       }
	  } else {
	       for (i = 0; i < ac->num_tics; i++) {
		    int xpos;

		    if (!i) {
			 /* First is flush to left */
			 xpos = ac->tic_pos[i];
		    } else if (i == ac->num_tics - 1) {
			 /* Last is flush to right */
			 xpos = ac->tic_pos[i] -
			      AtTextWidth(ac->tic_label_text[i]);
		    } else {
			 /* Rest centered on tic */
			 xpos = ac->tic_pos[i] -
			      AtTextWidth(ac->tic_label_text[i]) / 2;
		    }
		    AtTextDraw(dpy, win, drw, ac->number_gc,
			       ac->tic_label_text[i], xpos, ac->tic_label_line);
	       }
	  }
	  if (region)
	       XSetClipMask(dpy, ac->number_gc, None);
     }

     /* Now the grid and subgrid */
     if (ac->draw_grid) {
	  if (ac->draw_subgrid && ac->subgrid_segments) {
	       if (region)
		    XSetRegion(dpy, ac->subgrid_gc, region);
	       XDrawSegments(dpy, drw, ac->subgrid_gc, ac->subgrid_segments,
			     ac->num_subtics);
	       if (region)
		    XSetClipMask(dpy, ac->subgrid_gc, None);
	  }
	  if (region)
	       XSetRegion(dpy, ac->grid_gc, region);
	  XDrawSegments(dpy, drw, ac->grid_gc, ac->grid_segments, ac->num_tics);
	  if (region)
	       XSetClipMask(dpy, ac->grid_gc, None);
     }
}

/*
 *   The recalc routine.
 *
 *   The recalculation happens in two parts.
 *
 *   This routine is the plot.recalc routine and is called by the
 *   Plotter parent when recalc is desired.  It calls the subclass
 *   recalc routine stored in axiscore.recalc_proc to set up the tics
 *   and subtics array, then sets up the pixel values here.  Subclasses
 *   should inherit this routine (from the AtPlot class) unless special
 *   pixel calculation is required.
 */

static void Recalc(pw, xs, ys, from, to)
AtPlotWidget pw;
AtScale *xs, *ys;
int from, to;
{
     AtAxisCalcProc fn;
     AtAxisCorePart *ac = &((AtAxisCoreWidget)pw)->axiscore;
     int pos, i, j, tp, tm, stp, stm, old_num_tics = ac->num_tics;
     XSegment *sp;

     if (ac->numbers_changed) {
	  XtFree((char *) ac->tic_values);
	  XtFree((char *) ac->subtic_values);
	  for (i = 0; ac->tic_label_string && i < ac->num_tics; i++)
	       XtFree(ac->tic_label_string[i]);
	  XtFree((char *) ac->tic_label_string);

	  ac->tic_label_string = NULL;
	  ac->tic_values = NULL;
	  ac->subtic_values = NULL;
	  ac->num_tics = ac->num_subtics = 0;

	  /*
	   * Call the subclass calc function
	   */
	  fn = ((AtAxisCoreWidgetClass)
		XtClass((Widget) pw))->axiscore_class.calc_proc;
	  if (fn) {
	       fn((AtAxisCoreWidget) pw);
	  }
     }

     if ( !(ac->numbers_changed || ac->position_changed)) {
#ifdef DEBUG
	  /* Hmm. how come we got called if there is nothing to change?? */
	  XtAppWarning(XtWidgetToApplicationContext(XtParent((Widget) pw)),
		       "AtAxis Recalc called without a recalc pending?");
#endif
	  return;
     }

     /*
      * Make the pixel arrays from the stores position arrays
      *
      * First, the segments
      */
     XtFree((char *) ac->tic_segments);
     XtFree((char *) ac->grid_segments);
     if(ac->subgrid_segments)
	  XtFree((char *) ac->subgrid_segments);
     XtFree((char *) ac->tic_pos);
     if(ac->subtic_pos)
	  XtFree((char *) ac->subtic_pos);
     for (i = 0; ac->tic_label_text && i < old_num_tics; i++) {
	  AtTextDestroy(ac->tic_label_text[i]);
     }
     XtFree((char *) ac->tic_label_text);

     /* First the axis */
     ac->axis_segment.x1 = ac->x1;
     ac->axis_segment.y1 = ac->y1;
     ac->axis_segment.x2 = ac->x2;
     ac->axis_segment.y2 = ac->y2;

     /* Now the origin */
     if (ac->draw_origin) {
	  pos = AtScaleUserToPixel(ac->scale, 0.0);
	  if(ac->vertical) {
	       if (pos > ac->y2 && pos < ac->y1) {
		    ac->origin_segment.x1 = ac->x1;
		    ac->origin_segment.x2 = ac->x1 +
			 (ac->mirror ? -ac->grid_length : ac->grid_length);
		    ac->origin_segment.y1 = ac->origin_segment.y2 = pos;
	       }
	       else
		    ac->origin_segment.x1 = 0;
	  }
	  else {
	       if (pos > ac->x1 && pos < ac->x2) {
		    ac->origin_segment.x1 = ac->origin_segment.x2 = pos;
		    ac->origin_segment.y1 = ac->y1;
		    ac->origin_segment.y2 = ac->y1 -
			 (ac->mirror ? -ac->grid_length : ac->grid_length);
	       }
	       else
		    ac->origin_segment.x1 = 0;
	  }
     }
     else
	  ac->origin_segment.x1 = 0;

     /* Now the frame */
     if (ac->vertical) {
	  ac->frame_segment.x1 = ac->frame_segment.x2 =
	       ac->x1 + (ac->mirror ? -ac->grid_length : ac->grid_length);
	  ac->frame_segment.y1 = ac->y1;
	  ac->frame_segment.y2 = ac->y2;
     }
     else {
	  ac->frame_segment.x1 = ac->x1;
	  ac->frame_segment.x2 = ac->x2;
	  ac->frame_segment.y1 = ac->frame_segment.y2 =
	       ac->y1 - (ac->mirror ? -ac->grid_length : ac->grid_length);
     }

     /* Now the tics */
     ac->num_ticsegments = ac->num_tics + ac->num_subtics;
     ac->tic_segments = sp =
	  (XSegment *) XtMalloc(sizeof(XSegment) * ac->num_ticsegments);
     ac->tic_pos = (Dimension *) XtMalloc(ac->num_tics * sizeof(Dimension));
     if(ac->num_subtics > 0)
	  ac->subtic_pos = (Dimension *) XtMalloc(ac->num_subtics * sizeof(Dimension));
     ac->tic_label_text = (AtText **) XtMalloc(sizeof(AtText *) * ac->num_tics);

     tp = tm = stp = stm = 0;
     if (ac->tics_inside && !ac->mirror || ac->tics_outside && ac->mirror) {
	  tp = ac->tic_length;
	  stp = ac->subtic_length;
     }
     if (ac->tics_inside && ac->mirror || ac->tics_outside && !ac->mirror) {
	  tm = ac->tic_length;
	  stm = ac->subtic_length;
     }
     for (i = 0; i < ac->num_tics; i++, sp++) {
	  pos = AtScaleUserToPixel(ac->scale, ac->tic_values[i]);
	  ac->tic_pos[i] = pos;
	  assert((sp - ac->tic_segments) < ac->num_ticsegments);
	  if (ac->vertical) {
	       sp->y1 = sp->y2 = pos;
	       sp->x1 = ac->x1 - tm;
	       sp->x2 = ac->x1 + tp;
	  } else {
	       sp->x1 = sp->x2 = pos;
	       /* remember +ve pixel is downward */
	       sp->y1 = ac->y1 + tm;
	       sp->y2 = ac->y1 - tp;
	  }
     }
     /* Now the subtics */
     for (i = 0; i < ac->num_subtics; i++, sp++) {
	  pos = AtScaleUserToPixel(ac->scale, ac->subtic_values[i]);
	  ac->subtic_pos[i] = pos;
	  assert((sp - ac->tic_segments) < ac->num_ticsegments);
	  if (ac->vertical) {
	       sp->y1 = sp->y2 = pos;
	       sp->x1 = ac->x1 - stm;
	       sp->x2 = ac->x1 + stp;
	  } else {
	       sp->x1 = sp->x2 = pos;
	       /* remember +ve pixel is downward */
	       sp->y1 = ac->y1 + stm;
	       sp->y2 = ac->y1 - stp;
	  }
     }

     /* Now define the line against which tic labels are displayed */
     tp = ac->tics_outside && ac->numbers_outside ||
	  ac->tics_inside && !ac->numbers_outside ?
	       Max(0, Max(ac->tic_length, ac->subtic_length)) : 0;
     if (ac->vertical) {
	  ac->tic_label_line =
	       (!ac->mirror ^ !!ac->numbers_outside) ?
		    ac->x2 + tp + MARGIN :
			 ac->x1 - tp - MARGIN - ac->max_num_width;
     } else {
	  /* Remember, towards bottom is DECREASING pixel address!! */
	  ac->tic_label_line =
	       (!ac->mirror ^ !!ac->numbers_outside) ?
		    ac->y2 - MARGIN - tp :
			 ac->y1 + MARGIN + tp + ac->max_num_width;
     }

     /*
      * Now the grid segments
      */
     ac->grid_segments = sp =
	  (XSegment *) XtMalloc(sizeof(XSegment) * ac->num_tics);
     if (ac->vertical) {
	  for (i = 0; i < ac->num_tics; i++, sp++) {
	       sp->y1 = sp->y2 = ac->tic_pos[i];
	       sp->x1 = ac->x1;
	       sp->x2 = ac->x1 +
		    (ac->mirror ? -ac->grid_length : ac->grid_length);
	  }

     } else {
	  for (i = 0; i < ac->num_tics; i++, sp++) {
	       sp->x1 = sp->x2 = ac->tic_pos[i];
	       sp->y1 = ac->y1;
	       sp->y2 = ac->y1 -
		    (ac->mirror ? -ac->grid_length : ac->grid_length);
	  }
     }

     /*
      * Now the subgrid segments
      */
     if(ac->num_subtics > 0) {
	  ac->subgrid_segments = sp =
	       (XSegment *) XtMalloc(sizeof(XSegment) * ac->num_subtics);
	  if (ac->vertical) {
	       for (i = j = 0; i < ac->num_subtics && j < ac->num_tics; i++, sp++) {
		    sp->y1 = sp->y2 = ac->subtic_pos[i];
		    sp->x1 = ac->x1;
		    if (SubticOnTic(ac, i, j)) {
			 sp->x2 = ac->x1;
			 ++j;
		    }
		    else
			 sp->x2 = ac->x1 +
			      (ac->mirror ? -ac->grid_length : ac->grid_length);
	       }
	  } else {
	       for (i = j = 0; i < ac->num_subtics && j < ac->num_tics; i++, sp++) {
		    sp->x1 = sp->x2 = ac->subtic_pos[i];
		    sp->y1 = ac->y1;
		    if (SubticOnTic(ac, i, j)) {
			 sp->y2 = ac->y1;
			 ++j;
		    }
		    else
			 sp->y2 = ac->y1 -
			      (ac->mirror ? -ac->grid_length : ac->grid_length);
	       }
	  }
     }
     else
	  ac->subgrid_segments = NULL;

     /*
      * Now convert the tic labels to AtText format
      */
     ReformatLabels((AtAxisCoreWidget) pw, False);

     /*
      * Now, layout the label etc
      */
     if (ac->label_text) {
	  tp = ac->tics_outside ?
	       Max(0, Max(ac->tic_length, ac->subtic_length)) : 0;
	  tp += MARGIN +
	       (ac->draw_numbers && ac->numbers_outside ?
		ac->max_num_width + MARGIN : 0);
	  if (ac->vertical) {
	       ac->label_line = ac->mirror ? ac->x1 + tp :
		    ac->x1 - tp - AtTextWidth(ac->label_text);
	  } else {
	       /* +y is towards bottom! */
	       ac->label_line = ac->mirror ? ac->y1 - tp :
		    ac->y1 + tp + AtTextHeight(ac->label_text);
	  }
     }

     ac->numbers_changed = ac->position_changed = False;
}

static void ReformatLabels(acw, free_them)
AtAxisCoreWidget acw;
int free_them;
{
     AtAxisCorePart *ac = &acw->axiscore;
     int wid, maxwid, i;

     maxwid = 0;
     for (i = 0; i < ac->num_tics; i++) {
	  if (free_them) AtTextDestroy(ac->tic_label_text[i]);
	  ac->tic_label_text[i] =
	       AtTextCreate(ac->tic_label_string[i], ac->ff,
			    ac->number_size, ac->number_style);

	  wid = ac->vertical ? AtTextWidth(ac->tic_label_text[i]) :
			       AtTextHeight(ac->tic_label_text[i]);
	  maxwid = Max(maxwid, wid);
     }
     if (maxwid != ac->actual_num_width && ac->draw_numbers &&
	 ac->numbers_outside && ac->max_num_width < maxwid) {
	  /*
	   * The width of the numbers has changed, so request a rescale
	   * (so the max_num_width can be calculated). Its a pity,
	   * relayout is pretty much all that changes.
	   */
	  ac->numbers_changed = True;
	  AtPlotterRescaleRequired((AtPlotWidget) acw);
     }
     ac->actual_num_width = maxwid;
}

/*
 *   The AxisCore member functions
 *
 *   The default range proc just accepts the answers, and calculates the
 *   number_width based on the stored actual_number_width or (for
 *   startup) by formatting max as a guess, and makes tic_interval
 *   equal to the range.
 */

static void RangeProc(w, minp, maxp, tip, nwp)
AtAxisCoreWidget w;
double *minp, *maxp, *tip;
int *nwp;
{
     AtAxisCorePart *ac = &w->axiscore;
     AtText *tmp;
     char lbl[20];

     if (ac->actual_num_width) {
	  *nwp = ac->actual_num_width;
     } else {
	  sprintf(lbl, "%.0g", *maxp);
	  tmp = AtTextCreate(lbl, ac->ff, ac->number_size, ac->number_style);
	  /* Default Number width is with of max */
	  if (ac->vertical) {
	       *nwp = AtTextWidth(tmp);
	  } else
	       *nwp = AtTextHeight(tmp);
	  AtTextDestroy(tmp);
     }
     /* Default tic_interval is (max - min)! */
     *tip = *maxp - *minp;
}

/*
 *   The default Calc proc just has two tics (at min and max) and three subtics.
 */
static void CalcProc(w)
AtAxisCoreWidget w;
{
     AtAxisCorePart *ac = &w->axiscore;
     char lbl[20];

     ac->tic_values = (double *) XtMalloc(sizeof(double) * 2);
     ac->tic_label_string = (String *) XtMalloc(sizeof (String) * 2);
     ac->num_tics = 2;

     ac->tic_values[0] = ac->min;
     ac->tic_values[1] = ac->max;

     sprintf(lbl, "%.0f", ac->min);
     ac->tic_label_string[0] = XtNewString(lbl);
     sprintf(lbl, "%.0f", ac->max);
     ac->tic_label_string[1] = XtNewString(lbl);

     ac->num_subtics = 3;
     ac->subtic_values = (double *) XtMalloc(sizeof(double) * 3);
     ac->subtic_values[0] = (ac->max + ac->min) / 2;
     ac->subtic_values[1] = (ac->max + ac->subtic_values[0]) / 2;
     ac->subtic_values[2] = (ac->min + ac->subtic_values[0]) / 2;
}

/*
 *
 *   The wrappers for the member functions that get called by the parent
 */

void AtAxisAskRange(acw, minp, maxp)
AtAxisCoreWidget acw;
double *minp, *maxp;
{
     AtAxisRangeProc fn;
     AtAxisRangeArgs ra;
     AtAxisCorePart *ac = &acw->axiscore;
     double new_ti, *tip = &new_ti;
     int old_width = ac->axis_width;
     int new_num_width = ac->actual_num_width;
     int *widp = &new_num_width;
     Boolean rescale = False;

     ra.minp = minp;
     ra.maxp = maxp;
     ra.max_widthp = widp;
     ra.tic_intervalp = tip;

     /*
      * First, ask the class method to suggest
      * min/max/tic interval/max label width
      */
     if(fn = ((AtAxisCoreWidgetClass) XtClass((Widget) acw))->axiscore_class.range_proc)
	  fn(acw, minp, maxp, tip, widp);

     /*
      * Then call the callbacks so they can suggest same
      */
     XtCallCallbackList((Widget) acw, ac->range_callback, (XtPointer) &ra);

     /*
      * Decide if we need to recalculate our tics and subtics,
      * depending on whether or not min/max/ticinterval changed
      */
     if (ac->min != *minp || ac->max != *maxp) {
#ifdef TRACE
	  fprintf(stderr, "Axis (label %s) changed endpoints to %.1f,%.1f\n",
		  ac->label, *minp, *maxp);
#endif
	  AtScaleRescale(ac->scale, *minp, *maxp);
	  rescale = True;
     }
     if (rescale || ac->tic_interval != *tip) {
	  ac->numbers_changed = True;
	  AtPlotterRecalcThisPlot((AtPlotWidget)acw);
     }

     /* Accept the values for min, max and ticinterval */
     ac->min = *minp;
     ac->max = *maxp;
     ac->tic_interval = *tip;
     ac->max_num_width = *widp;
     /*
      * Now calculate the actual axis width based on the max number
      * width and things like labels etc.
      */
     CalcAxisWidth(acw);
     if (ac->axis_width != old_width) {
#ifdef TRACE
	  fprintf(stderr,
		  "AtAxisRange (label %s) changed max_width from %d to %d\n",
		  ac->label, old_width, ac->axis_width);
#endif
	  ac->position_changed = True;
	  AtPlotterLayoutRequired((AtPlotWidget)acw);
     }
}

/*
 *   This routine is called by the parent to set the location of the axis.
 *
 */

Boolean AtAxisSetPosition(acw, x1, y1, x2, y2, grid_length)
AtAxisCoreWidget acw;
int x1, y1, x2, y2, grid_length;
{
     AtAxisCorePart *ac = &acw->axiscore;
     int old_len, len;
     Boolean len_changed = False;

     XtCheckSubclass((Widget) acw, atAxisCoreWidgetClass,
		     "AtAxisSetPosition needs an AtAxisCoreWidget");

#define dif(var)     (ac->var != var)
     if (dif(x1) || dif(y1) || dif(x2) || dif(y2) || dif(grid_length)) {
	  AtPlotterRecalcThisPlot((AtPlotWidget)acw);
	  ac->position_changed = True;
#ifdef TRACE
	  fprintf(stderr, "Axis (label %s) changed position\n",
		  ac->label);
#endif
     }
#undef dif

     if (ac->vertical) {
	  old_len = ac->y1 - ac->y2;
	  len = y1 - y2;
     } else {
	  old_len = ac->x2 - ac->x1;
	  len = x2 - x1;
     }
     if (old_len < (len - (len >> 2)) || old_len > (len + (len >> 2))) {
	  /* Has changed length by 25%, so need to rethink tic_interval! */
	  len_changed = ac->numbers_changed = True;
#ifdef TRACE
	  fprintf(stderr, "Axis (label %s) changed in length\n",
		  ac->label);
#endif
     }

     ac->x1 = x1;
     ac->x2 = x2;
     ac->y1 = y1;
     ac->y2 = y2;
     ac->grid_length = grid_length;

     if (ac->vertical && x1 != x2) {
	  XtAppError(XtWidgetToApplicationContext((Widget) acw),
		     "Vertical axis given non-vertical position");
     }
     if (!ac->vertical && y1 != y2) {
	  XtAppError(XtWidgetToApplicationContext((Widget) acw),
		     "Horizontal axis given non-horizontal position");
     }

     /* Now change the scale */
     if (ac->vertical) {
	  AtScaleResize(ac->scale, y1, y2);
     } else {
	  AtScaleResize(ac->scale, x1, x2);
     }

     return len_changed;
}

/*
 *   The simple member functions that have no virtual functions
 */

AtScale *AtAxisGetScale(acw)
AtAxisCoreWidget acw;
{
     if (!acw) return NULL;     /* Can happen with textplot classes */

     XtCheckSubclass((Widget) acw, atAxisCoreWidgetClass,
		  "AtAxisGetScale needs an AtAxisCoreWidget");

     return acw->axiscore.scale;
}

int AtAxisWidth(acw)
AtAxisCoreWidget acw;
{
     XtCheckSubclass((Widget) acw, atAxisCoreWidgetClass,
		  "AtAxisWidth needs an AtAxisCoreWidget");
     return acw->axiscore.axis_width;
}

void AtAxisGetBounds(acw, minp, maxp)
AtAxisCoreWidget acw;
double *minp, *maxp;
{
     *minp = acw->axiscore.min;
     *maxp = acw->axiscore.max;
}

AtTransform AtAxisGetTransform(acw)
AtAxisCoreWidget acw;
{
     return AtScaleGetTransform(acw->axiscore.scale);
}


/*
 *   Return the maximal number width.
 *   This may be called from applications to get the maximal
 *   number width and then to set a default number width.
 */

int AtAxisGetNumberWidth(acw)
AtAxisCoreWidget acw;
{
     AtAxisCorePart *ac;
     int w, mw, i;

     if ( !XtIsRealized((Widget) acw) || acw->axiscore.tic_label_string == NULL)
	  return 0;

     ac = &acw->axiscore;
     mw = 0;
     for (i = 0; i < ac->num_tics; i++) {
	  if (ac->tic_label_string[i]) {
	       w = ac->vertical ? AtTextWidth(ac->tic_label_text[i]) :
				  AtTextHeight(ac->tic_label_text[i]);
	       mw = Max(mw, w);
	  }
     }
     return mw;
}

/*
 *   PostScript stuff
 */

#define AtTextPSHeight(a) (AtTextPSAscent((a)) + AtTextPSDescent(a))

int AtAxisWidthPS(acw)
AtAxisCoreWidget acw;
{
     AtAxisCorePart *acp = &acw->axiscore;
     int i, max_num_width;

     if (!acp->vertical) {
	  return PS_MARGIN + (acp->tics_outside ? acp->tic_length : 0) +
	       (acp->draw_numbers && acp->numbers_outside ?
		AtTextPSHeight(acp->tic_label_text[0]) + PS_MARGIN : 0) +
		     (acp->label_text ?
		      AtTextPSHeight(acp->label_text) + PS_MARGIN : 0);
     }
     max_num_width = 0;
     if (acp->draw_numbers && acp->numbers_outside) {
	  for (i = 0; i < acp->num_tics; i++) {
	       int wid = AtTextPSWidth(acp->tic_label_text[i]);
	       max_num_width = Max(max_num_width, wid);
	  }
	  max_num_width += PS_MARGIN;
     }

     return PS_MARGIN + (acp->tics_outside ? acp->tic_length : 0) +
	  max_num_width +
	       (acp->label_text ?
		AtTextPSWidth(acp->label_text) + PS_MARGIN : 0);
}

void AtAxisDrawPS(acw, fp, sp, x1, y1, x2, y2, grid_len)
AtAxisCoreWidget acw;
FILE *fp;
AtScale *sp;
int x1, y1, x2, y2, grid_len;
{
     AtAxisCorePart *acp = &acw->axiscore;
     int tp, tm, stp, stm;
     int pos, len, i, j;
     int tll, max_num_width;
     int x, y;

     fprintf(fp, "%%%%BeginObject: AtAxisCore\nGS\n");

     /* Draw the line */
     fprintf(fp, "0.75 setlinewidth [ ] 0 setdash\n");
     fprintf(fp, "%d %d M %d %d L\n", x1, y1, x2, y2);

     /* Now the frame */
     if (acp->draw_frame) {
	  len = (acp->mirror) ? -grid_len : grid_len;
	  if (acp->vertical) {
	       fprintf(fp, "%d %d M %d %d L\n", x1+len, y1, x2+len, y2);
	  } else {
	       fprintf(fp, "%d %d M %d %d L\n", x1, y1+len, x2, y2+len);
	  }
     }
     /* Now the origin */
     if (acp->draw_origin) {
	  for (i = 0; i < acp->num_tics; i++) {
	       if (acp->tic_values[i] == 0.0) {
		    pos = AtScaleUserToPixel(sp, acp->tic_values[i]);
		    len = (acp->mirror) ? -grid_len : grid_len;
		    if (acp->vertical)
			 fprintf(fp, "%d %d M %d %d L\n", x1, pos, x1+len, pos);
		    else
			 fprintf(fp, "%d %d M %d %d L\n", pos, y1, pos, y1+len);
		    break;
	       }
	  }
     }

     /* Now the tics */
     tp = tm = stp = stm = 0;
     if (acp->tics_inside && !acp->mirror || acp->tics_outside && acp->mirror) {
	  tp = acp->tic_length;
	  stp = acp->subtic_length;
     }
     if (acp->tics_inside && acp->mirror || acp->tics_outside && !acp->mirror) {
	  tm = acp->tic_length;
	  stm = acp->subtic_length;
     }
     for (i = 0; i < acp->num_tics; i++) {
	  pos = AtScaleUserToPixel(sp, acp->tic_values[i]);
	  if (acp->vertical) {
	       fprintf(fp, "%d %d M %d %d L\n", x1 - tm, pos, x1 + tp, pos);
	  } else {
	       fprintf(fp, "%d %d M %d %d L\n", pos, y1 - tm, pos, y1 + tp);
	  }
     }
     for (i = 0; i < acp->num_subtics; i++) {
	  pos = AtScaleUserToPixel(sp, acp->subtic_values[i]);
	  if (acp->vertical) {
	       fprintf(fp, "%d %d M %d %d L\n", x1 - stm, pos, x1 + stp, pos);
	  } else {
	       fprintf(fp, "%d %d M %d %d L\n", pos, y1 - stm, pos, y1 + stp);
	  }
     }
     fprintf(fp, "ST GR\n");

     /* Now the grid */
     if (acp->draw_grid) {
	  if (acp->mirror) grid_len = -grid_len;
	  fprintf(fp, "GS 0.3 setlinewidth [ ] 0 setdash\n");
	  for(i = 0; i<acp->num_tics; i++) {
	       pos = AtScaleUserToPixel(sp, acp->tic_values[i]);
	       if (acp->vertical)
		    fprintf(fp, "%d %d M %d %d L\n", x1, pos,
			    x1 + grid_len, pos);
	       else
		    fprintf(fp, "%d %d M %d %d L\n", pos, y1, pos,
			    y1 + grid_len);
	  }
	  fprintf(fp, "ST GR\n");

	  /* Now the subgrid */
	  if (acp->draw_subgrid) {
	       fprintf(fp, "GS 0.15 setlinewidth [ 1 ] 0 setdash\n");
	       for(i = j = 0; i < acp->num_subtics; i++) {
		    if (SubticOnTic(acp, i, j)) {
			 ++j;
		    }
		    else {
			 pos = AtScaleUserToPixel(sp, acp->subtic_values[i]);
			 if (acp->vertical)
			      fprintf(fp, "%d %d M %d %d L\n", x1, pos,
				      x1 + grid_len, pos);
			 else
			      fprintf(fp, "%d %d M %d %d L\n", pos, y1, pos,
				      y1 + grid_len);
		    }
	       }
	       fprintf(fp, "ST GR\n");
	  }
     }

     /*
      * Now the labels, if required
      */
     max_num_width = 0;
     if (acp->draw_numbers) {
	  int tl;

	  if (acp->vertical) {
	       for (i = 0; i < acp->num_tics; i++) {
		    int wid = AtTextPSWidth(acp->tic_label_text[i]);
		    max_num_width = Max(wid, max_num_width);
	       }
	  } else
	       max_num_width = AtTextPSHeight(acp->tic_label_text[0]);
	  tl = (acp->tics_outside && acp->numbers_outside ||
		acp->tics_inside && !acp->numbers_outside) ?
		     acp->tic_length : 0;
	  if (acp->vertical) {
	       tll = (!acp->mirror ^ !!acp->numbers_outside) ?
		    x2 + tl + PS_MARGIN : x1 - tl - PS_MARGIN - max_num_width;
	  } else {
	       tll = (!acp->mirror ^ !!acp->numbers_outside) ?
		    y1 + PS_MARGIN + tl :
			 y2 - PS_MARGIN - tl - max_num_width ;
	  }
	  for (i = 0; i < acp->num_tics; i++) {
	       pos = AtScaleUserToPixel(sp, acp->tic_values[i]);
	       if (acp->vertical) {
		    x = tll;
		    if (!i) {
			 /* First is flush to bottom */
			 y = pos;
		    } else if (i == acp->num_tics - 1) {
			 /* Last is flush to Top - which is low pixel no! */
			 y = pos - AtTextPSAscent(acp->tic_label_text[i]);
		    } else  {
			 /* Rest centered on tic */
			 y = pos - AtTextPSHeight(acp->tic_label_text[i]) / 2;
		    }
	       } else {
		    y = tll;
		    if (!i) {
			 /* First is flush to left */
			 x = pos;
		    } else if (i == acp->num_tics - 1) {
			 /* Last is flush to right */
			 x = pos - AtTextPSWidth(acp->tic_label_text[i]);
		    } else {
			 /* Rest centered on tic */
			 x = pos - AtTextPSWidth(acp->tic_label_text[i]) / 2;
		    }
	       }
	       AtTextPSDraw(fp, acp->tic_label_text[i], x, y);
	  }
     }
     /* Lastly, the label */
     if (acp->label_text) {
	  x = (x1 + x2) / 2;
	  y = (y1 + y2) / 2;
	  if (acp->vertical) {
	       if (acp->mirror)
		    x += tp + PS_MARGIN + (acp->draw_numbers ?
			       max_num_width + PS_MARGIN : 0);
	       else
		    x -= tm + PS_MARGIN +
			 (acp->draw_numbers ? max_num_width + PS_MARGIN : 0) +
			      AtTextPSWidth(acp->label_text);
	       y -= AtTextPSAscent(acp->label_text) / 2 -
		    AtTextPSDescent(acp->label_text) / 2;
	  } else {
	       if (acp->mirror) {
		    y += tp + PS_MARGIN + AtTextPSDescent(acp->label_text);
		    if (acp->draw_numbers)
			 y += PS_MARGIN +
			      AtTextPSHeight(acp->tic_label_text[0]);
	       }
	       else {
		    y -= tm + PS_MARGIN + AtTextPSAscent(acp->label_text);
		    if (acp->draw_numbers)
			 y -=  PS_MARGIN +
			      AtTextPSHeight(acp->tic_label_text[0]);
	       }
	       x -= AtTextPSWidth(acp->label_text)/2;
	  }
	  AtTextPSDraw(fp, acp->label_text, x, y);
     }
     fprintf(fp, "\n%%%%EndObject: AtAxisCore\n");
}
