/** tpic.c **/


static void flushPath    (int invis);
static void flushDashed	 (int dotted);
static void flushSpline	 (void);
static void arc		 (int invis);

static void dot_at       (int x, int y);
static void line_btw     (int x0, int y0, int x1, int y1);
static void draw_ellipse (int xc, int yc, int xr, int yr);


struct tpic_msg   *tp;
struct driver_map *dm;

long hres, vres;
long path_len;

#define xRESOLUTION	hres
#define yRESOLUTION	vres

struct bitmap     map;
long		  upper_limit;
long		  lower_limit;




/*
 * APPROXIMATE integer distance between two points
 */
#define	dist(x0, y0, x1, y1)	(abs(x0-x1)+abs(y0-y1))

#define	TWOPI		(3.14159265359*2.0)

#define xCONV_PT(x) \
  ((x) < 0 ? -((-(x) * hres + 500) / 1000) \
	   : (((x) * hres + 500) / 1000) )
#define yCONV_PT(y) \
  ((y) < 0 ? -((-(y) * vres + 500) / 1000) \
	   : (((y) * vres + 500) / 1000) )






void work_with_tpic(struct tpic_msg *tpic, struct driver_map *dmap,
			long hresolution, long vresolution)
{
  tp = tpic;
  dm = dmap;

  hres = hresolution;
  vres = vresolution;
  path_len = tp->path_len;
  
  /* Initialisierung fuer 'graphics.c' */
  map.width  = dm->width;
  map.height = dm->height;
  map.pixptr = dm->pixptr;
  upper_limit = dm->upper_limit;
  lower_limit = dm->lower_limit;

  device_SetPenSize(xCONV_PT(tpic->pen_size), yCONV_PT(tpic->pen_size));

  switch (tpic->tpic_com) {
    case TPIC_FP:
	flushPath(FALSE);
    case TPIC_IP:
	flushPath(TRUE);
	break;
    case TPIC_DA:
	flushDashed(FALSE);
	break;
    case TPIC_DT:
	flushDashed(TRUE);
	break;
    case TPIC_SP:
    case TPIC_SPB:
	flushSpline();
	break;
    case TPIC_AR:
	arc(FALSE);
	break;
    case TPIC_IA:
	arc(TRUE);
	break;
    default:
	break;
  }
}



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


static void flushPath(int invis)
{
  int i, x, y;

  if (tp->path_len == 1) {
    pline("FP/IP path less than 2 points - ignored");
  }
  else {
    if (invis) {
      /* not jet implemented */
      ;
    }
    else {
      x = dm->x;
      y = dm->y;
      for (i=1; i<tp->path_len; i++) {
        device_DrawLine(
  		x + xCONV_PT(tp->xx[i]), y + yCONV_PT(tp->yy[i]),
  		x + xCONV_PT(tp->xx[i+1]), y + yCONV_PT(tp->yy[i+1]));
      }
    }
  }
}


static void flushDashed(int dotted)
{
  long *xx = &(tp->xx[0]);
  long *yy = &(tp->yy[0]);
  int i, numdots, x0, y0, x1, y1;
  int cx0, cy0, cx1, cy1;
  double d, spacesize, a, b, dx, dy, milliperdash;
  float inchesperdash;

  sscanf(tp->opt_float[0], "%g", &inchesperdash);
  if (tp->path_len <= 1 || inchesperdash <= 0.0) {
    pline("*** illegal conditions for dotted/dashed line (%d, %d)", tp->path_len, inchesperdash);
    return;
  }

    milliperdash = inchesperdash * 1000.0;
    x0 = xx[1];	y0 = yy[1];
    x1 = xx[2];	y1 = yy[2];
    dx = x1 - x0;
    dy = y1 - y0;
    if (dotted) {
	numdots = sqrt(dx*dx + dy*dy) / milliperdash + 0.5;
	if (numdots == 0) numdots = 1;
	for (i=0; i <= numdots; i++) {
	    a = (float) i / (float) numdots;
	    cx0 = x0 + a*dx + 0.5;
	    cy0 = y0 + a*dy + 0.5;
	    dot_at(cx0, cy0);
	}
    } else {
	d = sqrt(dx*dx + dy*dy);
	if (d <= 2.0*milliperdash)
	    line_btw(x0, y0, x1, y1);
	else {
	    numdots = d / (2.0*milliperdash) + 1.0;
	    spacesize = (d - numdots * milliperdash) / (numdots - 1);
	    for (i=0; i<numdots-1; i++) {
		a = i * (milliperdash + spacesize) / d;
		b = a + milliperdash / d;
		cx0 = x0 + a*dx + 0.5;
		cy0 = y0 + a*dy + 0.5;
		cx1 = x0 + b*dx + 0.5;
		cy1 = y0 + b*dy + 0.5;
		line_btw(cx0, cy0, cx1, cy1);
		b += spacesize / d;
	    }
	    cx0 = x0 + b*dx + 0.5;
	    cy0 = y0 + b*dy + 0.5;
	    line_btw(cx0, cy0, x1, y1);
	}
    }
}

static void flushSpline(void)
{
  long *xx = &(tp->xx[0]);
  long *yy = &(tp->yy[0]);
  int xp, yp, N, lastx, lasty;
  int t1, t2, t3, steps, j;
  int i, w;
  int ipd;

#if 0
  if( *cp ) {
    float inchesPerDash;

    if( sscanf(cp, "%f", &inchesPerDash) != 1 ) {
	Warning("Illegal format for dotted/dashed spline", "");
	return;
    }

    ipd = (int)(1000.0 * inchesPerDash);	/* to get milli-inches */
  }

  if( ipd < 0 )	{	/* dotted */
  } else {		/* dashed */
  }
#endif

  N = path_len + 1;
  xx[0] = xx[1];	yy[0] = yy[1];
  xx[N] = xx[N-1];	yy[N] = yy[N-1];
  for (i=0; i<N-1; i++) {	/* interval */
	/* original was:
	 * steps = (dist(xx[i], yy[i], xx[i+1], yy[i+1]) +
	 *	 dist(xx[i+1], yy[i+1], xx[i+2], yy[i+2])) / 80;
	 */
	steps = (dist(xx[i], yy[i], xx[i+1], yy[i+1]) +
		 dist(xx[i+1], yy[i+1], xx[i+2], yy[i+2])) * xRESOLUTION / 4000;

 	w  = 500 / steps;
	t1 = w * w / 20;
	w -= 500;
	t2 = (750000 - w * w) / 10;
	w -= 500;
	t3 = w * w / 20;
	lastx = (t1*xx[i+2] + t2*xx[i+1] + t3*xx[i] + 50000) / 100000;
	lasty = (t1*yy[i+2] + t2*yy[i+1] + t3*yy[i] + 50000) / 100000;

	for (j=1; j<steps; j++) {	/* points within */
	    w  = (j*1000 + 500) / steps;
	    t1 = w * w / 20;
	    w -= 500;
	    t2 = (750000 - w * w) / 10;
	    w -= 500;
	    t3 = w * w / 20;
	    xp = (t1*xx[i+2] + t2*xx[i+1] + t3*xx[i] + 50000) / 100000;
	    yp = (t1*yy[i+2] + t2*yy[i+1] + t3*yy[i] + 50000) / 100000;
	    line_btw(lastx, lasty, xp, yp);
	    lastx = xp;
	    lasty = yp;
	}
  }
}

static void arc(int invis)
{
  int xc, yc, xrad, yrad, n;
  int lastx, lasty;
  float start_angle, end_angle, angle, theta, r;
  double xradius, yradius;

  xc   = tp->opt_long[0];
  yc   = tp->opt_long[1];
  xrad = tp->opt_long[2];
  yrad = tp->opt_long[3];
  sscanf(tp->opt_float[0], "%g", &start_angle);
  sscanf(tp->opt_float[1], "%g", &end_angle);

  /* fuer `invis' == true sollte die Ellipse nicht gezeichnet, sondern
   * nur ausgefuellt werden.... wir machen nichts...
   */
  if( invis )
    return;

  /* ... und Warnung fuer folgende Faelle ?? */
  if( start_angle > end_angle ) {
    angle = start_angle;  start_angle = end_angle;  end_angle = angle;
  }
  if( xrad < 0 ) xrad = -xrad;
  if( yrad < 0 ) yrad = -yrad;

  /* We have a specialized fast way to draw closed circles/ellipses */
  if (start_angle <= 0.0 && end_angle >= 6.283) {
    draw_ellipse(xc, yc, xrad, yrad);
    return;
  }

  r = ((float) (xrad + yrad)) / 2.0;
  theta = sqrt(1.0 / r);
  n = 0.3 * TWOPI / theta + 0.5;
  if (n < 12)
    n = 12;
  else if (n > 80)
    n = 80;
  /* n /= 2; */
  theta = TWOPI / n;

  xradius = xrad;
  yradius = yrad;
  lastx = xc + (int)(xradius * cos(start_angle) + 0.5);
  lasty = yc + (int)(yradius * sin(start_angle) + 0.5);

#ifdef __GNUC__
  /* GCC lib fuer den ST hat in _addsf3() anscheinend einen Fehler :-( */
  angle = (double)start_angle + theta;
#else
  angle = start_angle + theta;
#endif

  n += 10;
  while (--n > 0 && angle < end_angle) {
    xrad = xc + (int) (xradius*cos(angle) + 0.5);
    yrad = yc + (int) (yradius*sin(angle) + 0.5);
    line_btw(lastx, lasty, xrad, yrad);
    lastx = xrad;
    lasty = yrad;
    angle += theta;
  }

  if( n <= 0 ) {
    pline("Error: in ellipse startangle %f endangle %f (theta %f)",
	start_angle, end_angle, theta);
  }

  xrad = xc + (int)(xradius*cos(end_angle) + 0.5);
  yrad = yc + (int)(yradius*sin(end_angle) + 0.5);
  line_btw(lastx, lasty, xrad, yrad);
}



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

  static void
dot_at(int x, int y)
{
  x = dm->x + xCONV_PT(x);
  y = dm->y + yCONV_PT(y);

  device_DrawPoint(x, y);
}


  static void
line_btw(int x0, int y0, int x1, int y1)
{
  x0 = dm->x + xCONV_PT(x0);
  y0 = dm->y + yCONV_PT(y0);
  x1 = dm->x + xCONV_PT(x1);
  y1 = dm->y + yCONV_PT(y1);

  device_DrawLine(x0, y0, x1, y1);
}




/*
 * Draw an ellipse with the indicated center and radices.
 */
  static void
draw_ellipse(int xc, int yc, int xr, int yr)
{ double angle, theta;
  int n, px0, py0, px1, py1;

  angle = (xr + yr) / 2.0;
  theta = sqrt(1.0 / angle);
  n = TWOPI / theta + 0.5;
  if (n < 12)
    n = 12;
  else if (n > 80)
    n = 80;
  n /= 2;
  theta = TWOPI / n;

  px0 = xc + xr;	/* cos(0) = 1 */
  py0 = yc;		/* Sin(0) = 0 */
  angle = theta;

  while (angle <= TWOPI) {
    px1 = xc + xr*cos(angle) + 0.5;
    py1 = yc + yr*sin(angle) + 0.5;
    line_btw(px0, py0, px1, py1);
    px0 = px1;
    py0 = py1;
    angle += theta;
  }
  line_btw(px0, py0, xc + xr, yc);
}

