# include "Ximage.h"

# define DrawLine(X1,Y1,X2,Y2) (XDrawLine (graphic->display, graphic->window, graphic->gc, (int)(X1), (int)(Y1), (int)(X2), (int)(Y2)))
# define DrawRectangle(X1,Y1,X2,Y2) (XDrawRectangle (graphic->display, graphic->window, graphic->gc, (int)(X1), (int)(Y1), (int)(X2), (int)(Y2)))
# define FillRectangle(X1,Y1,X2,Y2) (XFillRectangle (graphic->display, graphic->window, graphic->gc, (int)(X1), (int)(Y1), (int)(X2), (int)(Y2)))
# define DrawCircle(X,Y,R) (XDrawArc (graphic->display, graphic->window, graphic->gc, (int)(X-R), (int)(Y-R), abs(2*R), abs(2*R), 0, 23040))
# define FillCircle(X,Y,R) (XFillArc (graphic->display, graphic->window, graphic->gc, (int)(X-R), (int)(Y-R), abs(2*R+1), abs(2*R+1), 0, 23040))

# define CONNECT 0
# define HISTOGRAM 1
# define POINTS 2

# define XCENTER 0.0
# define YCENTER 0.0
# define CAPSTYLE CapButt
# define JOINSTYLE JoinMiter

static Graphic *graphic;

/* draw all objects for this Graph */
int DrawObjects (KapaGraphWidget *graph) {
  
  int i;
  
  for (i = 0; i < graph[0].Nobjects; i++) {
    if (DEBUG) fprintf (stderr, "object: %d\n", i);
    if (DEBUG) fprintf (stderr, "Npts: %d\n", graph[0].objects[i].Npts);
    DrawObjectN (graph, &graph[0].objects[i]);
  }    
  return (TRUE);
}

/* Draw a specific object in the graph */
int DrawObjectN (KapaGraphWidget *graph, Gobjects *object) {
  
  static char dash[2] = {5,10};
  static char dot[2] = {3,3};
  int lweight;
  
  // this function calls all of the supporting Draw... functions below
  graphic = GetGraphic();
  
  lweight = MAX (1, MIN (10, object[0].lweight));

  /** some notes on drawing lines in X:

      "In general, drawing a thin line will be faster than drawing a wide line of width
      one. However, because of their different drawing algorithms, thin lines may not mix
      well aesthetically with wide lines. If it is desirable to obtain precise and uniform
      results across all displays, a client should always use a line-width of one rather
      than a line-width of zero."  -- http://www.hpc.unimelb.edu.au/nec/g1ae02e/chap7.html

  */

  /* set line type */
  switch (object[0].ltype) {
    case 0:
      XSetLineAttributes (graphic->display, graphic->gc, lweight, LineSolid, CAPSTYLE, JOINSTYLE);
      break;
    case 1:
      XSetDashes (graphic->display, graphic->gc, 100, dash, 2);
      XSetLineAttributes (graphic->display, graphic->gc, lweight, LineOnOffDash, CAPSTYLE, JOINSTYLE);
      break;
    case 2:
      XSetDashes (graphic->display, graphic->gc, 10, dot, 2);
      XSetLineAttributes (graphic->display, graphic->gc, lweight, LineOnOffDash, CAPSTYLE, JOINSTYLE);
      break;
    default:
      XSetLineAttributes (graphic->display, graphic->gc, lweight, LineSolid, CAPSTYLE, JOINSTYLE);
      break;
  }

  // negative color values mean "scale color with object.z"
  if (object[0].color >= 0) {
    XSetForeground (graphic->display, graphic->gc, graphic->color[object[0].color]);
  }

  switch (object[0].style) {
    case CONNECT: 
      DrawConnect (graph, object);
      break;
    case HISTOGRAM:
      DrawHistogram (graph, object);
      break;
    case POINTS:
      DrawPoints (graph, object);
      break;
  }
    
  if (object[0].etype & 0x01) {
    DrawYErrors (graph, object);
  }
  if (object[0].etype & 0x02) {
    DrawXErrors (graph, object);
  }

  XSetLineAttributes (graphic->display, graphic->gc, 0, LineSolid, CAPSTYLE, JOINSTYLE);
  XSetForeground (graphic->display, graphic->gc, graphic->fore);
  return (TRUE);
}

/******/
void DrawConnect (KapaGraphWidget *graph, Gobjects *object) {
  
  int i;
  float *x, *y;
  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
  double sx0, sy0, sx1, sy1;
  double X0, X1, Y0, Y1;

  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
  
  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
  
  bx = bxi + bxj;
  by = byi + byj;
  
  X0 = graph[0].axis[0].fx;
  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
  Y0 = graph[0].axis[1].fy;
  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;

  x = object[0].x; y = object[0].y;
  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
  if (i >= object[0].Npts) return;
  sx0 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
  sy0 = x[i]*myi + y[i]*myj + by + YCENTER;

  for (i++; i < object[0].Npts; i++) {
    if (!(finite(x[i]) && finite(y[i]))) continue;
    sx1 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
    sy1 = x[i]*myi + y[i]*myj + by + YCENTER;
    
    ClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
    /* DrawLine (sx0, sy0, sx1, sy1); */
    sx0 = sx1; sy0 = sy1;
  }
  
}

void ClipLine (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0) {

  /* skip line segement if both points are beyond box */
  if ((x0 <= X0) && (x1 <= X0)) return;
  if ((x0 >= X1) && (x1 >= X1)) return;
  if ((y0 <= Y0) && (y1 <= Y0)) return;
  if ((y0 >= Y1) && (y1 >= Y1)) return;

  /* replace x0,y0 if outside box */
  if ((x0 < X0) && (x1 >= X0)) {
    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
    x0 = X0;
  }
  if ((x0 > X1) && (x1 <= X1)) {
    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
    x0 = X1;
  }
  if ((y0 < Y0) && (y1 >= Y0)) {
    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
    y0 = Y0;
  }
  if ((y0 > Y1) && (y1 <= Y1)) {
    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
    y0 = Y1;
  }

  /* skip line segement if both points are beyond box */
  if ((x0 <= X0) && (x1 <= X0)) return;
  if ((x0 >= X1) && (x1 >= X1)) return;
  if ((y0 <= Y0) && (y1 <= Y0)) return;
  if ((y0 >= Y1) && (y1 >= Y1)) return;

  /* replace x1,y1 if outside box */
  if ((x1 < X0) && (x0 >= X0)) {
    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
    x1 = X0;
  }
  if ((x1 > X1) && (x0 <= X1)) {
    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
    x1 = X1;
  }
  if ((y1 < Y0) && (y0 >= Y0)) {
    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
    y1 = Y0;
  }
  if ((y1 > Y1) && (y0 <= Y1)) {
    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
    y1 = Y1;
  }
  DrawLine (x0, y0, x1, y1);
}
  

/******/
/* simplify the code abit by finding triplets, watch out for a histogram of 2 points */
void DrawHistogram (KapaGraphWidget *graph, Gobjects *object) {

  int i;
  float *x, *y;
  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
  double sx0, sy0, sx1, sy1, sxa, sya, sxo, syo;
  double X0, X1, Y0, Y1;

  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
  
  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
  
  bx = bxi + bxj;
  by = byi + byj;
  
  X0 = graph[0].axis[0].fx;
  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
  Y0 = graph[0].axis[1].fy;
  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;

  /* find the first valid datapoint */
  x = object[0].x; y = object[0].y;
  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
  if (i >= object[0].Npts) return;

  /* first valid data point */
  sx0 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
  sy0 = x[i]*myi + y[i]*myj + by + YCENTER;
  sx0 = MIN (MAX (sx0, X0), X1);
  sy0 = MAX (MIN (sy0, Y0), Y1);
  
  /* find the second valid datapoint */
  for (i++; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
  if (i >= object[0].Npts) return;

  /* second valid data point */
  sx1 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
  sy1 = x[i]*myi + y[i]*myj + by + YCENTER;
  sx1 = MIN (MAX (sx1, X0), X1);
  sy1 = MAX (MIN (sy1, Y0), Y1);
  
  /* connect first point to second point */
  sxa = sx0 - 0.5*(sx1 - sx0);
  sya = MAX (sy0, Y0);
  DrawLine (sx0, sy0, sxa, sy0);
  DrawLine (sxa, sy0, sxa, sya);
  
  /* draw segment equal distance behind first point and down to x-axis */
  sxa = 0.5*(sx0 + sx1);
  DrawLine (sx0, sy0, sxa, sy0);
  DrawLine (sxa, sy0, sxa, sy1);
  DrawLine (sxa, sy1, sx1, sy1);
  sx0 = sx1;
  sy0 = sy1;
  
  /* continue with rest of points */
  sxo = syo = 0;
  for (i++; i < object[0].Npts; i++) {
    if (!(finite(x[i]) && finite(y[i]))) continue;
    sx1 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
    sy1 = x[i]*myi + y[i]*myj + by + YCENTER;
    sx1 = MIN (MAX (sx1, X0), X1);
    sy1 = MAX (MIN (sy1, Y0), Y1);
    sxa = 0.5*(sx0 + sx1);
    DrawLine (sx0, sy0, sxa, sy0);
    DrawLine (sxa, sy0, sxa, sy1);
    DrawLine (sxa, sy1, sx1, sy1);
    sxo = sx0; syo = sy0;
    sx0 = sx1; sy0 = sy1;
  }
  
  /* draw segment equal distance after last point and down to x-axis */
  sxa = sx1 + 0.5*(sx1 - sxo);
  sya = MAX (sy1, Y0);
  DrawLine (sx1, sy1, sxa, sy1);
  DrawLine (sxa, sy1, sxa, sya);
}

# if (0)
void FillHistogram (KapaGraphWidget *graph, Gobjects *object) {

  int i;
  float *x, *y;
  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
  double sx0, sy0, sx1, sy1, sxa, sya, sxo, syo;
  double X0, X1, Y0, Y1;

  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
  
  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
  
  bx = bxi + bxj;
  by = byi + byj;
  
  X0 = graph[0].axis[0].fx;
  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
  Y0 = graph[0].axis[1].fy;
  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;

  /* find the first valid datapoint */
  x = object[0].x; y = object[0].y;
  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
  if (i >= object[0].Npts) return;

  /* first valid data point */
  sx0 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
  sy0 = x[i]*myi + y[i]*myj + by + YCENTER;
  sx0 = MIN (MAX (sx0, X0), X1);
  sy0 = MAX (MIN (sy0, Y0), Y1);
  
  /* find the second valid datapoint */
  for (i++; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
  if (i >= object[0].Npts) return;

  /* second valid data point */
  sx1 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
  sy1 = x[i]*myi + y[i]*myj + by + YCENTER;
  sx1 = MIN (MAX (sx1, X0), X1);
  sy1 = MAX (MIN (sy1, Y0), Y1);
  
  /* connect first point to second point */
  sxa = sx0 - 0.5*(sx1 - sx0);
  sya = MAX (sy0, Y0);
  DrawLine (sx0, sy0, sxa, sy0);
  DrawLine (sxa, sy0, sxa, sya);
  
  /* draw segment equal distance behind first point and down to x-axis */
  sxa = 0.5*(sx0 + sx1);
  DrawLine (sx0, sy0, sxa, sy0);
  DrawLine (sxa, sy0, sxa, sy1);
  DrawLine (sxa, sy1, sx1, sy1);
  sx0 = sx1;
  sy0 = sy1;
  
  /* continue with rest of points */
  for (i++; i < object[0].Npts; i++) {
    if (!(finite(x[i]) && finite(y[i]))) continue;
    sx1 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
    sy1 = x[i]*myi + y[i]*myj + by + YCENTER;
    sx1 = MIN (MAX (sx1, X0), X1);
    sy1 = MAX (MIN (sy1, Y0), Y1);
    sxa = 0.5*(sx0 + sx1);
    DrawLine (sx0, sy0, sxa, sy0);
    DrawLine (sxa, sy0, sxa, sy1);
    DrawLine (sxa, sy1, sx1, sy1);
    sxo = sx0; syo = sy0;
    sx0 = sx1; sy0 = sy1;
  }
  
  /* draw segment equal distance after last point and down to x-axis */
  sxa = sx1 + 0.5*(sx1 - sxo);
  sya = MAX (sy1, Y0);
  DrawLine (sx1, sy1, sxa, sy1);
  DrawLine (sxa, sy1, sxa, sya);
}
# endif

/******/
void DrawPoints (KapaGraphWidget *graph, Gobjects *object) {

  int i;
  float *x, *y, *z;
  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
  int sx, sy, sx1, sy1, sx2, sy2, ds, dz, D;
  
  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
  
  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
  
  bx = bxi + bxj;
  by = byi + byj;
  
  Graphic *graphic = GetGraphic();

  /**** points are scaled by object.z ***/
  int scaleSize = (object[0].size < 0);
  int scaleColor = (object[0].color < 0);

  ds = 0.5 * (graphic->dx + graphic->dy) * 0.003 * object[0].size;
  dz = 0.5 * (graphic->dx + graphic->dy) * 0.010;
  x = object[0].x; y = object[0].y; z = object[0].z;

  if (object[0].ptype == 0) {	/* filled box */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	FillRectangle (sx - D, sy - D, 2*D + 1, 2*D + 1);
      }
    }
  }
  if (object[0].ptype == 1) {	/* open box */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	DrawRectangle (sx - D, sy - D, 2*D, 2*D);
      }
    }
  }
  if (object[0].ptype == 2) { /* cross */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	DrawLine (sx - D, sy, sx + D + 1, sy);
	DrawLine (sx, sy - D, sx, sy + D + 1);
      }
    }
  }
  if (object[0].ptype == 3) {	/* x */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	DrawLine (sx - D, sy + D, sx + D + 1, sy - D - 1);
	DrawLine (sx - D, sy - D, sx + D + 1, sy + D + 1);
      }
    }
  }
  if (object[0].ptype == 4) {	/* filled triangle */
    XPoint points[4];
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	points[0].x = sx - D;  points[0].y = sy + 0.58*D;  
	points[1].x = sx + D;  points[1].y = sy + 0.58*D;  
	points[2].x = sx;      points[2].y = sy - 1.15*D;  
	points[3].x = sx - D;  points[3].y = sy + 0.58*D;  
	XFillPolygon (graphic->display, graphic->window, graphic->gc, points, 4, Convex, CoordModeOrigin);
      }
    }
  }
  if (object[0].ptype == 5) {	/* open triangle */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	DrawLine (sx - D, sy + 0.58*D, sx + D, sy + 0.58*D);
	DrawLine (sx + D, sy + 0.58*D, sx,     sy - 1.15*D);
	DrawLine (sx,     sy - 1.15*D, sx - D, sy + 0.58*D);
      }
    }
  }
  if (object[0].ptype == 6) {	/* Y */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	DrawLine (sx, sy, sx - D, sy - 0.58*D);
	DrawLine (sx, sy, sx + D, sy - 0.58*D);
	DrawLine (sx, sy, sx,     sy + 1.15*D);
      }
    }
  }
  if (object[0].ptype == 7) {	/* 0 */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	DrawCircle (sx, sy, D);
      }
    }
  }
  if (object[0].ptype == 8) {	/* pentagon */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	DrawLine (sx + 0.00*D, sy - 1.00*D, sx + 0.95*D, sy - 0.31*D);
	DrawLine (sx + 0.95*D, sy - 0.31*D, sx + 0.58*D, sy + 0.81*D);
	DrawLine (sx + 0.58*D, sy + 0.81*D, sx - 0.58*D, sy + 0.81*D);
	DrawLine (sx - 0.58*D, sy + 0.81*D, sx - 0.95*D, sy - 0.31*D);
	DrawLine (sx - 0.95*D, sy - 0.31*D, sx + 0.00*D, sy - 1.00*D);
      }
    }
  }
  if (object[0].ptype == 9) {	/* hexagon */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	DrawLine (sx -      D, sy,          sx - 0.50*D, sy + 0.87*D);
	DrawLine (sx - 0.50*D, sy + 0.87*D, sx + 0.50*D, sy + 0.87*D);
	DrawLine (sx + 0.50*D, sy + 0.87*D, sx +      D, sy);

	DrawLine (sx +      D, sy,          sx + 0.50*D, sy - 0.87*D);
	DrawLine (sx + 0.50*D, sy - 0.87*D, sx - 0.50*D, sy - 0.87*D);
	DrawLine (sx - 0.50*D, sy - 0.87*D, sx -      D, sy);
      }
    }
  }
  if (object[0].ptype == 10) {	/* filled 0 */
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	FillCircle (sx, sy, D);
      }
    }
  }
  if (object[0].ptype == 12) {	/* filled triangle (down) */
    XPoint points[4];
    for (i = 0; i < object[0].Npts; i++) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy = x[i]*myi + y[i]*myj + by + YCENTER;
      if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
	if (scaleColor) {
	  if (!finite(z[i])) continue;
	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
	}
	D = scaleSize ? dz*z[i] : ds;
	points[0].x = sx - D;  points[0].y = sy - 0.58*D;  
	points[1].x = sx + D;  points[1].y = sy - 0.58*D;  
	points[2].x = sx;      points[2].y = sy + 1.15*D;  
	points[3].x = sx - D;  points[3].y = sy - 0.58*D;  
	XFillPolygon (graphic->display, graphic->window, graphic->gc, points, 4, Convex, CoordModeOrigin);
      }
    }
  }
  if (object[0].ptype == 100) {	/* connect a pair of points */

    double X0 = graph[0].axis[0].fx;
    double X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
    double Y0 = graph[0].axis[1].fy;
    double Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;

    for (i = 0; i + 1 < object[0].Npts; i+=2) {
      if (!(finite(x[i]) && finite(y[i]))) continue;
      sx1 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
      sy1 = x[i]*myi + y[i]*myj + by + YCENTER;
      if (!(finite(x[i+1]) && finite(y[i+1]))) continue;
      sx2 = x[i+1]*mxi + y[i+1]*mxj + bx + XCENTER;
      sy2 = x[i+1]*myi + y[i+1]*myj + by + YCENTER;
      ClipLine (sx1, sy1, sx2, sy2, X0, Y0, X1, Y1);
    }
  }
}
    
/******/
void DrawXErrors (KapaGraphWidget *graph, Gobjects *object) {
  
  int i, bar;
  float *x, *y, *dxm, *dxp;
  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
  double sx0, sy0, sx1, sy1, sz, sx10, sx11, X0, X1, Y0, Y1;

  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
  
  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
  
  bx = bxi + bxj;
  by = byi + byj;
  
  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
  bar = object[0].ebar; sz = object[0].size*graph[0].axis[1].dfy*0.03;
  
  X0 = graph[0].axis[0].fx;
  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
  Y0 = graph[0].axis[1].fy;
  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;

  for (i = 0; i < object[0].Npts; i++) {
    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
    sx0 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
    sy0 = x[i]*myi + y[i]*myj + by + YCENTER;
    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx + XCENTER;
    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by + YCENTER;
    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy))) 
    {
      ClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
      if (bar) {
	sx10 = sy1 - sz;
	sx11 = sy1 + sz;
	ClipLine (sx1, sx10, sx1, sx11, X0, Y0, X1, Y1);
      }
    }
    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
    sx0 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
    sy0 = x[i]*myi + y[i]*myj + by + YCENTER;
    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx + XCENTER;
    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by + YCENTER;
    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
    {
      ClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
      if (bar) {
	sx10 = sy1 - sz;
	sx11 = sy1 + sz;
	ClipLine (sx1, sx10, sx1, sx11, X0, Y0, X1, Y1);
      }
    }
  }
}
    
/******/
void DrawYErrors (KapaGraphWidget *graph, Gobjects *object) {

  int i, bar;
  float *x, *y, *dym, *dyp;
  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
  double sx0, sy0, sx1, sy1, sz, sx10, sx11, X0, X1, Y0, Y1;

  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
  
  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
  
  bx = bxi + bxj;
  by = byi + byj;
  
  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
  bar = object[0].ebar; sz = object[0].size*graph[0].axis[0].dfx*0.03;
  
  X0 = graph[0].axis[0].fx;
  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
  Y0 = graph[0].axis[1].fy;
  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;

  for (i = 0; i < object[0].Npts; i++) {
    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
    sx0 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
    sy0 = x[i]*myi + y[i]*myj + by + YCENTER;
    sx1 = sx0 + dyp[i]*mxj;
    sy1 = sy0 + dyp[i]*myj;
    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
    {
      ClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
      if (bar) {
	sx10 = sx1 - sz;
	sx11 = sx1 + sz;
	ClipLine (sx10, sy1, sx11, sy1, X0, Y0, X1, Y1);
      }
    }
    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
    sx0 = x[i]*mxi + y[i]*mxj + bx + XCENTER;
    sy0 = x[i]*myi + y[i]*myj + by + YCENTER;
    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx + XCENTER;
    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by + YCENTER;
    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
    {
      ClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
      if (bar) {
	sx10 = sx1 - sz;
	sx11 = sx1 + sz;
	ClipLine (sx10, sy1, sx11, sy1, X0, Y0, X1, Y1);
      }
    }
  }
}
