/* $Header: /afs/athena.mit.edu/astaff/project/atdev/src/plotter/RCS/XYPlot.c,v 3.8 95/05/18 12:53:03 dot Exp Locker: dot $ */

/*******************************************************************
  Copyright (C) 1990 by the Massachusetts Institute of Technology

   Export of this software from the United States of America is assumed
   to require a specific license from the United States Government.
   It is the responsibility of any person or organization contemplating
   export to obtain such a license before exporting.

WITHIN THAT CONSTRAINT, 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 M.I.T. not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.  M.I.T. makes no representations about the suitability of
this software for any purpose.  It is provided "as is" without express
or implied warranty.

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

#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#ifdef _AtDevelopment_
#include "Scale.h"    
#include "XYPlotP.h"
#include "AtConverters.h"
#else
#include <At/Scale.h>
#include <At/XYPlotP.h>
#include <At/AtConverters.h>
#endif

#define offset(field) XtOffset(AtXYPlotWidget, field)
static XtResource resources[] = {
{XtNconnectPoints, XtCConnectPoints, XtRBoolean,
     sizeof(Boolean), offset(xyplot.connectPoints),
     XtRImmediate, (caddr_t) False},
{XtNmarkPoints, XtCMarkPoints, XtRBoolean,
     sizeof(Boolean), offset(xyplot.markPoints),
     XtRImmediate, (caddr_t) True},
{XtNhorizontalImpulses, XtCHorizontalImpulses, XtRBoolean,
     sizeof(Boolean), offset(xyplot.horizontalImpulses),
     XtRImmediate, (caddr_t) False},
{XtNverticalImpulses, XtCVerticalImpulses, XtRBoolean,
     sizeof(Boolean), offset(xyplot.verticalImpulses),
     XtRImmediate, (caddr_t) False},
{XtNhorizontalImpulseInterval, XtCImpulseInterval, XtRInt,
   sizeof(int), offset(xyplot.horzImpulseInterval),
   XtRImmediate, (caddr_t) 1},
{XtNverticalImpulseInterval, XtCImpulseInterval, XtRInt,
   sizeof(int), offset(xyplot.vertImpulseInterval),
   XtRImmediate, (caddr_t) 1},
{XtNmarker, XtCMarker, XtRAtMarker,
     sizeof(int), offset(xyplot.marker),
     XtRImmediate, (caddr_t)AtMarkerCIRCLE},
{XtNxPoints, XtCXPoints, XtRDoubleArray,
     sizeof(double *), offset(xyplot.xPoints),
     XtRImmediate, NULL},
{XtNyPoints, XtCYPoints, XtRDoubleArray,
     sizeof(double *), offset(xyplot.yPoints),
     XtRImmediate, NULL},
{XtNnumPoints, XtCNumPoints, XtRInt,
     sizeof(int), offset(xyplot.numPoints),
     XtRImmediate, (caddr_t)0}
};
#undef offset

static void Initialize(AtXYPlotWidget, AtXYPlotWidget);
static void Destroy(AtXYPlotWidget);
static Boolean SetValues(AtXYPlotWidget, AtXYPlotWidget, AtXYPlotWidget);
static void DrawSyms(Display *, Window, AtXYPlotWidget, XPoint *, int);
static void Draw(Display *, Window, AtPlotWidget, AtScale *, AtScale *,
                 Region);
static void DrawSymsInc(Display *, Window, AtXYPlotWidget, XPoint *, int);
static void DrawInc(Display *, Window, AtPlotWidget, AtScale *, AtScale *);
static void Recompute(AtPlotWidget, AtScale *, AtScale *);
static void DrawPS(FILE *, AtPlotWidget, AtScale * , AtScale *);
static void DrawIcon(Display *, Window, AtPlotWidget, int, int, int, int);
static void DrawIconPS(FILE *, AtPlotWidget, int, int, int, int);

AtXYPlotClassRec atXYPlotClassRec = {
{ /* core part */
    /* superclass         */    (WidgetClass)&atPlotClassRec,
    /* class_name         */    "AtXYPlot",
    /* widget_size        */    sizeof(AtXYPlotRec),
    /* class_initialize   */    NULL,
    /* class_part_initialize*/  NULL,
    /* 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_offsets   */    NULL,
    /* pad                */    NULL,
    /* pad                */    NULL,
    /* pad                */    NULL,
    /* extension            */  NULL
},
/* AtPlotClassPart initialization */
{
    /* draw()       */   Draw,
    /* drawIcon()   */   DrawIcon,
    /* resize()     */   Recompute,
    /* rescale()    */   Recompute,
    /* drawPS()     */	 DrawPS,
    /* drawIconPS() */   DrawIconPS,
    /* checkhit()   */   NULL,
}
};

WidgetClass atXYPlotWidgetClass = (WidgetClass) &atXYPlotClassRec;

#define AtWarning(w,msg) XtWarning(msg)

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

static void GetGC(AtXYPlotWidget w)
{
  XGCValues gcv;
  XtGCMask gcmask;

  if (w->plot.fastUpdate)  {
    gcv.foreground = w->plot.background ^ w->plot.foreground;
    gcv.function = GXxor;
    gcmask = GCForeground | GCFunction;
  }
  else  {
    gcv.foreground = w->plot.foreground;
    gcv.background = w->plot.background;
    gcmask = GCForeground | GCBackground;
  }
  w->xyplot.solidGc = XtGetGC(XtParent((Widget)w), gcmask, &gcv);
}

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

static void GetBoundingBox(AtXYPlotWidget w, BoundingBox *b) {
  double xmin, ymin, xmax, ymax;
  int i;

  if (w->xyplot.numPoints == 0) {
    xmin = ymin = xmax = ymax = 0.0;
  }
  else {
    xmin = xmax = w->xyplot.xpts[0];
    for(i = 1; i < w->xyplot.numPoints; i++) {
      if (w->xyplot.xpts[i] < xmin)
	xmin = w->xyplot.xpts[i];
      if (w->xyplot.xpts[i] > xmax)
	xmax = w->xyplot.xpts[i];
    }

    ymin = ymax = w->xyplot.ypts[0];
    for(i = 1; i < w->xyplot.numPoints; i++) {
      if (w->xyplot.ypts[i] < ymin)
	ymin = w->xyplot.ypts[i];
      if (w->xyplot.ypts[i] > ymax)
	ymax = w->xyplot.ypts[i];
    }
  }

  b->xmin = xmin;
  b->ymin = ymin;
  b->xmax = xmax;
  b->ymax = ymax;
}

/**********************************************************************/
static void SetBoundingBox(AtXYPlotWidget w)
{
  double xmin, ymin, xmax, ymax;
  int i;
  Arg arg;
  BoundingBox b;

  if (w->xyplot.numPoints == 0) {
    xmin = ymin = xmax = ymax = 0.0;
  }
  else {
    xmin = xmax = w->xyplot.xpts[0];
    for(i = 1; i < w->xyplot.numPoints; i++) {
      if (w->xyplot.xpts[i] < xmin)
	xmin = w->xyplot.xpts[i];
      if (w->xyplot.xpts[i] > xmax)
	xmax = w->xyplot.xpts[i];
    }

    ymin = ymax = w->xyplot.ypts[0];
    for(i = 1; i < w->xyplot.numPoints; i++) {
      if (w->xyplot.ypts[i] < ymin)
	ymin = w->xyplot.ypts[i];
      if (w->xyplot.ypts[i] > ymax)
	ymax = w->xyplot.ypts[i];
    }
  }

  b.xmin = xmin;
  b.ymin = ymin;
  b.xmax = xmax;
  b.ymax = ymax;
  XtSetArg(arg, XtNboundingBox, &b);
  XtSetValues(w, &arg, 1);
}

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

static void Initialize(AtXYPlotWidget request, AtXYPlotWidget new)
{
  BoundingBox b;
  Arg arg;

  if (new->xyplot.numPoints < 0) {
    AtWarning(new, "numPoints cannot be < 0.");
    new->xyplot.numPoints = 0;
  }
    
  if ((new->xyplot.numPoints == 0) &&
      ((new->xyplot.xPoints != NULL) || (new->xyplot.yPoints != NULL)))
    AtWarning(new, "numPoints is zero, but some points were specified.");
    
  if ((new->xyplot.numPoints != 0 ) &&
      ((new->xyplot.xPoints == NULL) || (new->xyplot.yPoints == NULL))) {
    AtWarning(new, "numPoints non-zero, but no points specified.");
    new->xyplot.numPoints = 0;
  }
    
  /* make a private copy of the data points 
   * this works even if numPoints == 0.
   * we want an allocated piece of memory even if numPoints == 0
   * so that realloc is guaranteed to work on it.
   */
  new->xyplot.xpts =
    (double *)XtMalloc(sizeof(double) * new->xyplot.numPoints);
#ifdef SYSV
  memcpy ((char *)new->xyplot.xpts, (char *)new->xyplot.xPoints,
	sizeof(double) * new->xyplot.numPoints);
#else
  bcopy((char *)new->xyplot.xPoints, (char *)new->xyplot.xpts,
	sizeof(double) * new->xyplot.numPoints);
#endif /* SYSV */    
  new->xyplot.ypts =
    (double *)XtMalloc(sizeof(double) * new->xyplot.numPoints);
#ifdef SYSV
  memcpy ((char *)new->xyplot.ypts, (char *)new->xyplot.yPoints,
	sizeof(double) * new->xyplot.numPoints);
#else
  bcopy((char *)new->xyplot.yPoints, (char *)new->xyplot.ypts,
	sizeof(double) * new->xyplot.numPoints);
#endif /* SYSV */    
  new->xyplot.pixpts =
    (XPoint *) XtCalloc(new->xyplot.numPoints, sizeof(XPoint));
  new->xyplot.oldpixpts =
    (XPoint *) XtCalloc(new->xyplot.numPoints, sizeof(XPoint));
    
  new->xyplot.valid = False;
  new->xyplot.refresh = False;
  new->xyplot.xPoints = NULL;
  new->xyplot.yPoints = NULL;

  /* xpoints and ypoints are always NULL unless set values is delivering
   * a set of points to be plotted
   */

  GetGC(new);

  /* figure out our bounding box */
  GetBoundingBox(new, &b);
#ifdef X11R4_INTRINSICS
  XtVaSetValues((Widget) new, XtNboundingBox, (XtArgVal) &b, NULL);
#else
  XtSetArg (arg, XtNboundingBox, (XtArgVal)&b);
  XtSetValues ((Widget) new, &arg, 1);
#endif
}

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

static void Destroy(AtXYPlotWidget w)
{
  XtFree(w->xyplot.xpts);
  XtFree(w->xyplot.ypts);
  XtFree(w->xyplot.pixpts);
  XtFree(w->xyplot.oldpixpts);
  XtReleaseGC(XtParent((Widget)w), w->xyplot.solidGc);
}

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

static Boolean SetValues(AtXYPlotWidget current,
			 AtXYPlotWidget request,
			 AtXYPlotWidget new)
{
  Boolean autoS;
  Arg a[2];
  BoundingBox b;

#ifdef X11R4_INTRINSICS
  XtVaGetValues((Widget) new,
		XtNboundingBox, (XtArgVal) &b, NULL);
#else
  XtSetArg (a[0], XtNboundingBox, (XtArgVal)&b);
  XtGetValues ((Widget) new, &a, 1);
#endif

#define Changed(field) (new->xyplot.field != current->xyplot.field)
#define PlotChanged(field) (new->plot.field != current->plot.field)
#define NEW new->xyplot
    
  XtSetArg(a[0], XtNautoScale, &autoS);
  XtGetValues(XtParent((Widget) new), a, 1);

  /* Check Plot's foreground & background to see if XYPlot's GC
     needs to change. */

  if (PlotChanged(foreground) || PlotChanged(background) ||
      PlotChanged(fastUpdate))  {
    XtReleaseGC(XtParent((Widget)new), new->xyplot.solidGc);
    GetGC(new);
    new->plot.redisplay = True;
  }

  if (Changed(connectPoints) || Changed(markPoints) ||
      Changed(horizontalImpulses) || Changed(verticalImpulses) ||
      Changed(horzImpulseInterval) || Changed(vertImpulseInterval) || 
      Changed(marker))
    new->plot.redisplay = True;

  if (Changed(numPoints)) {
    NEW.xpts = (double *) XtRealloc(NEW.xpts, 
				    sizeof(double) * new->xyplot.numPoints);
    NEW.ypts = (double *) XtRealloc(NEW.ypts, 
				    sizeof(double) * new->xyplot.numPoints);
    NEW.pixpts = (XPoint *) XtRealloc(NEW.pixpts, 
				      sizeof(XPoint) * new->xyplot.numPoints);
    NEW.oldpixpts = (XPoint *) XtRealloc(NEW.oldpixpts, 
					 sizeof(XPoint) * new->xyplot.numPoints);

#ifdef SYSV
    memcpy (NEW.xpts, NEW.xPoints, sizeof(double) * NEW.numPoints);
    memcpy (NEW.ypts, NEW.yPoints, sizeof(double) * NEW.numPoints);
#else
    bcopy(NEW.xPoints, NEW.xpts, sizeof(double) * NEW.numPoints);
    bcopy(NEW.yPoints, NEW.ypts, sizeof(double) * NEW.numPoints);
#endif /* SYSV */
    NEW.valid = False;
    NEW.xPoints = NULL;
    NEW.yPoints = NULL;
    GetBoundingBox(new, &b);
    new->plot.redisplay = True; /* refresh all plots */
  }
  else {

    /*  check if yPoints changed  */
    if (NEW.yPoints) { /* yPoints is non-null iff set values is being used
			* to modify the points to be plotted
			*/ 
#ifdef SYSV
      memcpy(NEW.ypts, NEW.yPoints,
	    sizeof(double) * NEW.numPoints);
#else
      bcopy(NEW.yPoints, NEW.ypts,
	    sizeof(double) * NEW.numPoints);
#endif /* SYSV */
      NEW.valid = False;
      NEW.yPoints = NULL;
      if (autoS) {       /* refresh everything */
	GetBoundingBox(new, &b);
	new->plot.redisplay = True;
      }
      else
	NEW.refresh = True; /* refresh only this plot */
    }

    /*  check if xPoints changed  */
    if (NEW.xPoints) {
#ifdef SYSV
      memcpy (NEW.xpts, NEW.xPoints,
	    sizeof(double) * NEW.numPoints);
#else
      bcopy(NEW.xPoints, NEW.xpts,
	    sizeof(double) * NEW.numPoints);
#endif /* SYSV */
      NEW.valid = False;
      NEW.xPoints = NULL;
      if (autoS) {       /* refresh everything */
	GetBoundingBox(new, &b);
	new->plot.redisplay = True;
      }
      else
	NEW.refresh = True; /* refresh only this plot */
    }
  }

  /*
   * if the plot needs a redisplay, and if the plot is not a subclass
   * of XYPlot, ask to be redisplayed.  If the plot is a subclass,
   * then the subclass will handle redisplay.
   */
  if (new->object.widget_class == atXYPlotWidgetClass)
    if (new->plot.redisplay || (NEW.refresh && !new->plot.fastUpdate))  {
      new->plot.redisplay = False;
      NEW.refresh = False;
#ifdef X11R4_INTRINSICS
      XtVaSetValues((Widget) new,
		    XtNneedsRedraw, True,
		    XtNboundingBox, (XtArgVal) &b, NULL); /* set a */
							  /* constraint resource */
#else
      XtSetArg (a[0], XtNneedsRedraw, True); 
      XtSetArg (a[1], XtNboundingBox, (XtArgVal) &b);
      XtSetValues, ((Widget)new, a, 2);
#endif
    }
    else if (NEW.refresh)  {
      NEW.refresh = False;
#ifdef X11R4_INTRINSICS
      XtVaSetValues((Widget) new, 
		    XtNneedsRefresh, True,
		    XtNboundingBox, (XtArgVal) &b, NULL);
#else
      XtSetArg (a[0], XtNneedsRedraw, True); 
      XtSetArg (a[1], XtNboundingBox, (XtArgVal) &b);
      XtSetValues, ((Widget)new, a, 2);
#endif
    }

#undef Changed    
#undef NEW

  return False;
}

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

#define EBARHALFWIDTH 2
#define SQUARERADIUS 2 
#define TRIANGLEBASE 2            /* actually 1/2 the base length */
#define DIAMONDWIDTH 3            /* actually 1/2 the diamond's width */
#define STARRADIUS 3               /* length from centre to end of tic */
#define CROSSRADIUS 3              /* length from centre to end of tic */
#define CIRCLERADIUS 3            /* radius of circle */
#define MAXSYMPTS  20

/* These point structures are defined for use with XDrawLine using
   CoordModeRelative, the location of the symbol is added to the
   first point in the list.
   */
static XPoint Triangle[] = { -2*TRIANGLEBASE,0,
			       3*TRIANGLEBASE, -2*TRIANGLEBASE,
			       0, 4*TRIANGLEBASE,
			       -3*TRIANGLEBASE, -2*TRIANGLEBASE,
			     };

static int Trianglepoints = XtNumber(Triangle);

static XPoint Star[] = { 0, 0, STARRADIUS, 0,
			   -2*STARRADIUS, 0, STARRADIUS, 0,
			   0, STARRADIUS, 0, -2*STARRADIUS,
			   0, STARRADIUS, STARRADIUS-1, STARRADIUS-1,
			   -STARRADIUS+1, -STARRADIUS+1, -STARRADIUS+1, STARRADIUS-1,
			   STARRADIUS-1, -STARRADIUS+1, STARRADIUS-1, -STARRADIUS+1,
			   -STARRADIUS+1, STARRADIUS-1, -STARRADIUS+1, -STARRADIUS+1
			   };

static int Starpoints = XtNumber(Star);

static XPoint Cross[] =  { 0, 0, CROSSRADIUS, 0,
			     -2*CROSSRADIUS, 0, CROSSRADIUS, 0,
			     0, CROSSRADIUS, 0, -2*CROSSRADIUS };

static int Crosspoints = XtNumber(Cross);

static XPoint Diamond[] = { DIAMONDWIDTH,0,
			      -DIAMONDWIDTH, DIAMONDWIDTH,
			      -DIAMONDWIDTH, -DIAMONDWIDTH,
			      DIAMONDWIDTH, -DIAMONDWIDTH,
			      DIAMONDWIDTH, DIAMONDWIDTH
			      };

static int Diamondpoints = XtNumber(Diamond);

static XPoint Point[] = { 0, 0 }; 

static int Pointpoints = XtNumber(Point);

static XPoint Circle[] = {
  3,-1, 0,2, -2,2, -2,0, -2,-2,
  0,-2, 2,-2, 2,0, 2,2,
};

static int Circlepoints = XtNumber(Circle);

static XPoint Square[] = {
  SQUARERADIUS, 0,
  0, SQUARERADIUS,
  -2*SQUARERADIUS, 0,
  0, -2*SQUARERADIUS,
  2*SQUARERADIUS,0,
  0,SQUARERADIUS,
};

static int Squarepoints = XtNumber(Square);

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

static void Recompute(AtPlotWidget plot, AtScale *xscale, AtScale *yscale)
{
  AtXYPlotWidget p = (AtXYPlotWidget) plot;
  int i;
  double xmin, ymin;

  for(i = 0; i < p->xyplot.numPoints; i++){
    p->xyplot.pixpts[i].x = AtScaleUserToPixel(xscale, p->xyplot.xpts[i]);
    p->xyplot.oldpixpts[i].x = p->xyplot.pixpts[i].x;
    p->xyplot.pixpts[i].y = AtScaleUserToPixel(yscale, p->xyplot.ypts[i]);
    p->xyplot.oldpixpts[i].y = p->xyplot.pixpts[i].y;
    }

  xmin = AtScaleGetLow(xscale);
  ymin = AtScaleGetLow(yscale);
  p->xyplot.zeroxpt = AtScaleUserToPixel(xscale, (xmin>0.0)?xmin:0.0);
  p->xyplot.zeroypt = AtScaleUserToPixel(yscale, (ymin>0.0)?ymin:0.0);
  
  p->xyplot.valid = True;
}

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

static void DrawSyms(Display *dpy, Window win, AtXYPlotWidget p,
		     XPoint *symbol, int npoints)
{
  int i;
  XPoint firstpoint;

  firstpoint = symbol[0];
    
  for(i=0; i < p->xyplot.numPoints; i++){
    symbol[0].x = firstpoint.x + p->xyplot.pixpts[i].x;
    symbol[0].y = firstpoint.y + p->xyplot.pixpts[i].y;
    XDrawLines(dpy, win, p->xyplot.solidGc,
	       symbol, npoints, CoordModePrevious);
  }
  symbol[0] = firstpoint;
}

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

static void Draw(Display *dpy, Window win, AtPlotWidget plot,
		 AtScale *xscale, AtScale *yscale, Region region)
{
  AtXYPlotWidget p = (AtXYPlotWidget) plot;
  int i;
  Arg a;
  Boolean refresh;
  int clipx, clipy;
  static GC tmpGc = NULL;
  static GC tmpGc2 = NULL;
  Pixmap clip_mask;
  int clipx2, clipy2;
  Pixmap clip_mask2;
  XRectangle clip_rect;
  XGCValues values;
  unsigned long valueMask;

  XtSetArg(a, XtNneedsRefresh, &refresh);
  XtGetValues(plot, &a, 1);

  if (refresh)
    DrawInc(dpy, win, plot, xscale, yscale);

  if (p->xyplot.valid == False) Recompute(plot, xscale, yscale);

  /* set the clipping region */
  if (tmpGc == (GC)NULL) {
    tmpGc = XCreateGC (dpy, win, 0, NULL);
  }
  valueMask = GCClipXOrigin | GCClipYOrigin | GCClipMask;
  XCopyGC (dpy, p->plot.gc, valueMask, tmpGc);


  if (tmpGc2 == (GC)NULL) {
    tmpGc2 = XCreateGC (dpy, win, 0, NULL);
  }
  valueMask = GCClipXOrigin | GCClipYOrigin | GCClipMask;
  XCopyGC (dpy, p->xyplot.solidGc, valueMask, tmpGc2);

  if (region == NULL) {
    clip_rect.x = AtScaleGetLowPix(xscale);
    clip_rect.y = AtScaleGetHighPix(yscale);
    clip_rect.width = AtScaleGetHighPix(xscale) - clip_rect.x;
    clip_rect.height = AtScaleGetLowPix(yscale) - clip_rect.y;
    
    XSetClipRectangles(dpy, p->plot.gc, 0,0,&clip_rect, 1, Unsorted);
    XSetClipRectangles(dpy, p->xyplot.solidGc,0,0,&clip_rect, 1, Unsorted);
  }
  else {
    XSetRegion (dpy, p->plot.gc, region);
    XSetRegion (dpy, p->xyplot.solidGc, region);
  }

  /* draw markers */
    
  if (p->xyplot.markPoints == True) {
    switch (p->xyplot.marker){
    case AtMarkerTRIANGLE:
      DrawSyms(dpy, win,p,Triangle,Trianglepoints); break;
    case AtMarkerSTAR:
      DrawSyms(dpy, win,p,Star,Starpoints); break;
    case AtMarkerCROSS:
      DrawSyms(dpy, win,p,Cross,Crosspoints); break;
    case AtMarkerDIAMOND:
      DrawSyms(dpy, win,p,Diamond, Diamondpoints); break;
    case AtMarkerPOINT:
      XDrawPoints(dpy, win, p->plot.gc,
		  p->xyplot.pixpts, p->xyplot.numPoints,CoordModeOrigin);
      break;
    case AtMarkerCIRCLE:
      DrawSyms(dpy, win,p,Circle,Circlepoints); break;
    case AtMarkerSQUARE:
      DrawSyms(dpy, win,p,Square,Squarepoints); break;
    }
  }
  
  /* connect points */
    
  if (p->xyplot.connectPoints == True) {
    XDrawLines(dpy, win, p->plot.gc, p->xyplot.pixpts, 
	       p->xyplot.numPoints, CoordModeOrigin);
  }
    
  /* Now for impulses */
    
  if (p->xyplot.horizontalImpulses == True) {
    if (p->xyplot.horzImpulseInterval < 1)  {
      XtWarning("XYPlot:  horizontalImpulseInterval < 0");
      p->xyplot.horzImpulseInterval = 1;
    }
    for(i = 0; i < p->xyplot.numPoints; i += p->xyplot.horzImpulseInterval)
      XDrawLine(dpy,win,p->xyplot.solidGc,
		p->xyplot.zeroxpt, p->xyplot.pixpts[i].y,
		p->xyplot.pixpts[i].x, p->xyplot.pixpts[i].y);
  }

  if (p->xyplot.verticalImpulses == True) {
    if (p->xyplot.vertImpulseInterval < 1)  {
      XtWarning("XYPlot:  verticalImpulseInterval < 0");
      p->xyplot.vertImpulseInterval = 1;
    }
    for(i = 0; i < p->xyplot.numPoints; i += p->xyplot.vertImpulseInterval)
      XDrawLine(dpy,win,p->xyplot.solidGc,
		p->xyplot.pixpts[i].x, p->xyplot.zeroypt,
		p->xyplot.pixpts[i].x, p->xyplot.pixpts[i].y);
  }
    
  /* now restore the clip region */
  /* We depend on valueMask being unchanged */
  XCopyGC (dpy, tmpGc, valueMask, p->plot.gc);
  XCopyGC (dpy, tmpGc2, valueMask, p->xyplot.solidGc);
}

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

/* The following two procedures, DrawSymsInc and DrawInc, allow for
   incremental updates to XYPlots. They erase the old plot by redrawing
   it using a gc with exclusive or function. Then they call the 
   regular Draw procedure to display the new (updated) XYPlot. 
*/

static void DrawSymsInc(Display *dpy, Window win, AtXYPlotWidget p,
			XPoint *symbol, int npoints)
{
  int i;
  XPoint firstpoint;
  
  firstpoint = symbol[0];
    
  for(i=0; i < p->xyplot.numPoints; i++){
    symbol[0].x = firstpoint.x + p->xyplot.oldpixpts[i].x;
    symbol[0].y = firstpoint.y + p->xyplot.oldpixpts[i].y;
    XDrawLines(dpy, win, p->xyplot.solidGc,
	       symbol, npoints, CoordModePrevious);
  }
  symbol[0] = firstpoint;
}

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

static void DrawInc(Display *dpy, Window win, AtPlotWidget plot,
		    AtScale *xscale, AtScale *yscale)
{
  AtXYPlotWidget p = (AtXYPlotWidget) plot;
  int i;
  int clipx, clipy;
  static GC tmpGc = NULL;
  static GC tmpGc2 = NULL;
  Pixmap clip_mask;
  int clipx2, clipy2;
  Pixmap clip_mask2;
  XRectangle clip_rect;
  XGCValues values;
  unsigned long valueMask;
  
  /* save the clipping region */
  
  if (tmpGc == (GC)NULL) {
    tmpGc = XCreateGC (dpy, win, 0, NULL);
  }
  valueMask = GCClipXOrigin | GCClipYOrigin | GCClipMask;
  XCopyGC (dpy, p->plot.gc, valueMask, tmpGc);

  if (tmpGc2 == (GC)NULL) {
    tmpGc2 = XCreateGC (dpy, win, 0, NULL);
  }
  valueMask = GCClipXOrigin | GCClipYOrigin | GCClipMask;
  XCopyGC (dpy, p->xyplot.solidGc, valueMask, tmpGc2);
    
  clip_rect.x = AtScaleGetLowPix(xscale);
  clip_rect.y = AtScaleGetHighPix(yscale);
  clip_rect.width = AtScaleGetHighPix(xscale) - clip_rect.x;
  clip_rect.height = AtScaleGetLowPix(yscale) - clip_rect.y;

  XSetClipRectangles(dpy, p->plot.gc, 0,0,&clip_rect, 1, Unsorted);
  XSetClipRectangles(dpy, p->xyplot.solidGc,0,0,&clip_rect, 1, Unsorted);
  
  /* draw markers */
    
  if (p->xyplot.markPoints == True) {
    switch (p->xyplot.marker){
    case AtMarkerTRIANGLE:
      DrawSymsInc(dpy, win,p,Triangle,Trianglepoints); break;
    case AtMarkerSTAR:
      DrawSymsInc(dpy, win,p,Star,Starpoints); break;
    case AtMarkerCROSS:
      DrawSymsInc(dpy, win,p,Cross,Crosspoints); break;
    case AtMarkerDIAMOND:
      DrawSymsInc(dpy, win,p,Diamond, Diamondpoints); break;
    case AtMarkerPOINT:
      XDrawPoints(dpy, win, p->plot.gc,
		  p->xyplot.oldpixpts, p->xyplot.numPoints,CoordModeOrigin);
      break;
    case AtMarkerCIRCLE:
      DrawSymsInc(dpy, win,p,Circle,Circlepoints); break;
    case AtMarkerSQUARE:
      DrawSymsInc(dpy, win,p,Square,Squarepoints); break;
    }
    
  }
  
  /* connect points */
    
  if (p->xyplot.connectPoints == True) {
    XDrawLines(dpy, win, p->plot.gc,
	       p->xyplot.oldpixpts, p->xyplot.numPoints, CoordModeOrigin);
  }
    
  /* Now for impulses */
    
  if (p->xyplot.horizontalImpulses == True) {
    if (p->xyplot.horzImpulseInterval < 1)  {
      XtWarning("XYPlot:  horizontalImpulseInterval < 0");
      p->xyplot.horzImpulseInterval = 1;
    }
    for(i=0; i < p->xyplot.numPoints; i += p->xyplot.horzImpulseInterval)
      XDrawLine(dpy,win,p->xyplot.solidGc,
		p->xyplot.zeroxpt, p->xyplot.oldpixpts[i].y,
		p->xyplot.oldpixpts[i].x, p->xyplot.oldpixpts[i].y);
  }

  if (p->xyplot.verticalImpulses == True) {
    if (p->xyplot.vertImpulseInterval < 1)  {
      XtWarning("XYPlot:  verticalImpulseInterval < 0");
      p->xyplot.vertImpulseInterval = 1;
    }
    for(i=0; i < p->xyplot.numPoints; i += p->xyplot.vertImpulseInterval)
      XDrawLine(dpy,win,p->xyplot.solidGc,
		p->xyplot.oldpixpts[i].x, p->xyplot.zeroypt,
		p->xyplot.oldpixpts[i].x, p->xyplot.oldpixpts[i].y);
  }
    
  /* now restore the clip region */
  /* We depend on valueMask being unchanged */
  XCopyGC (dpy, tmpGc, valueMask, p->plot.gc);
  XCopyGC (dpy, tmpGc2, valueMask, p->xyplot.solidGc);
}

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

static void DrawPS(FILE *f, AtPlotWidget plot, AtScale *xs, AtScale *ys)
{
  AtXYPlotWidget p = (AtXYPlotWidget) plot;
  double *xpts = (double *) XtMalloc(p->xyplot.numPoints * sizeof(double));
  double *ypts = (double *) XtMalloc(p->xyplot.numPoints * sizeof(double));
  int i;
  char *marker;
  double xmin, ymin;
  double baseline;
  
  /* adjust scales so I get greater accuracy */
  AtScaleResize(xs, AtScaleGetLowPix(xs)*100, AtScaleGetHighPix(xs)*100);
  AtScaleResize(ys, AtScaleGetLowPix(ys)*100, AtScaleGetHighPix(ys)*100);
  
  /* compute points */
  for(i=0; i < p->xyplot.numPoints; i++) {
    xpts[i] = AtScaleUserToPixel(xs, p->xyplot.xpts[i])/100.0;
    ypts[i] = AtScaleUserToPixel(ys, p->xyplot.ypts[i])/100.0;
  }
  
  fprintf(f, "%%%% BeginObject: AtXYPlot\ngsave\n");
  
  /* draw markers */
  if (p->xyplot.markPoints == True) {
    switch (p->xyplot.marker){
    case AtMarkerTRIANGLE: marker = "TRI"; break;
    case AtMarkerSTAR: marker = "STA";  break;
    case AtMarkerCROSS: marker = "CRO"; break;
    case AtMarkerDIAMOND: marker = "DIA"; break;
    case AtMarkerPOINT: marker = "DOT"; break;
    case AtMarkerCIRCLE: marker = "CIR"; break;
    case AtMarkerSQUARE: marker = "SQR"; break;
    }
    for(i=0; i < p->xyplot.numPoints; i++) 
      fprintf(f, "%g %g %s\n", xpts[i], ypts[i], marker);
  }
  
  /* connect points */
  if (p->xyplot.connectPoints == True) {
    fprintf(f, "gsave\n");
    fprintf(f, "%d setlinewidth\n",
	    (p->plot.lineWidth==0)?1:p->plot.lineWidth);
    if (p->plot.lineStyle != LineSolid)
      fprintf(f, "[%d] 0 setdash\n", p->plot.dashLength);
    
    fprintf(f, "%g %g M\n", xpts[0], ypts[0]);
    for(i=1; i < p->xyplot.numPoints; i++) {
      fprintf(f, "%g %g L\n", xpts[i], ypts[i]);
      if (i%50 == 0)
	fprintf(f, "stroke\n%g %g M\n", xpts[i], ypts[i]);
    }
    fprintf(f, "stroke\n grestore\n");
  }
  
  /* draw impulses */
  if (p->xyplot.horizontalImpulses == True) {
    xmin = AtScaleGetLow(xs);
    baseline = AtScaleUserToPixel(xs, (xmin > 0.0) ? xmin : 0.0) / 100.0;
    if (p->xyplot.vertImpulseInterval < 1)  {
      XtWarning("XYPlot:  verticalImpulseInterval < 0");
      p->xyplot.vertImpulseInterval = 1;
    }
    for(i=0; i < p->xyplot.numPoints; i += p->xyplot.vertImpulseInterval) {
      fprintf(f, "%g %g M %g %g L\n",baseline,ypts[i],xpts[i],ypts[i]);
      if (i%50==0) fprintf(f, "stroke\n");
    }
    fprintf(f, "stroke\n");
  }
  
  if (p->xyplot.verticalImpulses == True) {
    ymin = AtScaleGetLow(ys);
    baseline = AtScaleUserToPixel(ys, (ymin > 0.0) ? ymin : 0.0) / 100.0;
    if (p->xyplot.vertImpulseInterval < 1)  {
      XtWarning("XYPlot:  verticalImpulseInterval < 0");
      p->xyplot.vertImpulseInterval = 1;
    }
    for(i=0; i < p->xyplot.numPoints; i += p->xyplot.vertImpulseInterval) {
      fprintf(f, "%g %g M %g %g L\n",xpts[i],baseline,xpts[i],ypts[i]);
      if (i%50==0) fprintf(f, "stroke\n");
    }
    fprintf(f, "stroke\n");
  }
  
  fprintf(f, "grestore\n%%%% EndObject\n");
  XtFree(xpts);
  XtFree(ypts);
  
  /* adjust scales back to original value */
  AtScaleResize(xs, AtScaleGetLowPix(xs)/100, AtScaleGetHighPix(xs)/100);
  AtScaleResize(ys, AtScaleGetLowPix(ys)/100, AtScaleGetHighPix(ys)/100);
}

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

static void DrawIcon(Display *dpy, Window win, AtPlotWidget pw, int x, 
		     int y, int width, int height)
     
{
  AtXYPlotWidget p = (AtXYPlotWidget) pw;
  int markerpts = 0;
  XPoint *marker, firstpoint;
  
  if (p->xyplot.connectPoints)
    XDrawLine(dpy, win, p->plot.gc, x, y+(height/2), x+width, y+(height/2));
  
  if (p->xyplot.markPoints)  {
    switch (p->xyplot.marker)  {
    case AtMarkerTRIANGLE:
      marker = Triangle; markerpts = Trianglepoints;
      break;
    case AtMarkerSTAR:
      marker = Star; markerpts = Starpoints;
      break;
    case AtMarkerCROSS:
      marker = Cross; markerpts = Crosspoints;
      break;
    case AtMarkerDIAMOND:
      marker = Diamond; markerpts = Diamondpoints;
      break;
    case AtMarkerPOINT:
      XDrawPoint(dpy, win, p->plot.gc, x+(width/2), y+(height/2));
      markerpts = 0;
      break;
    case AtMarkerCIRCLE:
      marker = Circle; markerpts = Circlepoints;
      break;
    case AtMarkerSQUARE:
      marker = Square; markerpts = Squarepoints;
      break;
    }
    
    if (markerpts)  {
      firstpoint = marker[0];
      marker[0].x = firstpoint.x + x + (width/2);
      marker[0].y = firstpoint.y + y + (height/2);
      XDrawLines(dpy, win, p->xyplot.solidGc, marker, markerpts,
		 CoordModePrevious);
      marker[0] = firstpoint;
    }
  }
}

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

static void DrawIconPS(FILE *f, AtPlotWidget pw, int x, int y, 
		       int width, int height)
     
{
  AtXYPlotWidget p = (AtXYPlotWidget) pw;
  char *marker;
  
  fprintf(f, "%%%% BeginObject: AtXYPlot icon\ngsave\n");
  
  if (p->xyplot.connectPoints)  {
    fprintf(f, "gsave\n");
    if (p->plot.lineStyle != LineSolid)
      fprintf(f, "[%d] 0 setdash\n", p->plot.dashLength);
    fprintf(f, "%d %d M\n", x, y - (height/2));
    fprintf(f, "%d %d L\n", x + width, y - (height/2));
    fprintf(f, "stroke\ngrestore\n");
  }
  
  if (p->xyplot.markPoints)  {
    switch (p->xyplot.marker)  {
    case AtMarkerTRIANGLE:
      marker = "TRI"; break;
    case AtMarkerSTAR:
      marker = "STA"; break;
    case AtMarkerCROSS:
      marker = "CRO"; break;
    case AtMarkerDIAMOND:
      marker = "DIA"; break;
    case AtMarkerPOINT:
      marker = "DOT"; break;
    case AtMarkerCIRCLE:
      marker = "CIR"; break;
    case AtMarkerSQUARE:
      marker = "SQR"; break;
    }
    fprintf(f, "%d %d %s\n", x + (width/2), y - (height/2), marker);
  }
  fprintf(f, "grestore\n%%%% EndObject\n");
}



