// C++ file for base display classes 
// copyright 1992 Pittsburgh Supercomputing Center
// these will only be called if the actual display used doesn't have its own
#include <math.h>
#include "cgm.h"
#ifdef __MSDOS__
#include "cgmdisp.h"
#else
#include "cgmdisplay.h"
#endif
#include "hload1.h"
// error call
extern void myError(const char *inMsg, const char *inMsg2=NULL,
		    int severity=1);
////
// angle
////
const double angle::tanMax = 1e10; // maximum tangent value
#ifdef macintosh
 #pragma segment DIS1
#endif
////
// hershey font stuff
////
// hershey character
////
const int hersheyChar::maxOffset = sizeof(hf_array);
hersheyChar::hersheyChar(const genText *inText, int charNo, int fontNo)
{
  static const vdcPts *usePos = NULL;
  int offset, i;
  next = NULL;
  contents = NULL;
  if (!inText || (charNo > inText->size())) return; // safety check
  ////
  if (fontNo >= NO_HF) fontNo = 0; // existing font
  int wantChar = inText->contents()[charNo]; // character we want
  if ((wantChar < 0) || (wantChar >= MAX_HCHARS) ||
      ((offset = h_array[fontNo][wantChar]) < 0) ||
      (offset > maxOffset)) return; // no legal character
//  if (hf_array[offset] < 0) return; // no such character in this font
  float charSize = inText->info()->height() / 24; // 24 pixel square
  // do we have any rotation ?
  angle upVec(1, 0), baseVec(0, 1);
  if (inText->info()->orientation()) {
    upVec = angle(inText->info()->orientation()->y(0),
		  inText->info()->orientation()->x(0));
    baseVec = angle(inText->info()->orientation()->y(1),
		    inText->info()->orientation()->x(1));
  }
  ////
  // now we know we have some points
  if (inText->pos()) usePos = inText->pos(); // may be appended
  if (!usePos) return; // no where to put it
  // figure out the shifts
  unsigned char *hPtr = hf_array + offset;
  float temp1 = charSize * (128 - (int) hPtr[0]);
  float temp2 = charSize * ((int) hPtr[1] - 128);
  myShift1 =  temp1 * baseVec.cos();
  myShift2 =  temp2 * baseVec.cos();
  myShiftY1 = temp1 * baseVec.sin();
  myShiftY2 = temp2 * baseVec.sin();
  myHeight = myDepth = 0;
  // step over widths
  hPtr +=2;
  // prepare for loop
  int noPts = 0;
  float fx, fy;
  do {
    if (!hPtr[2*noPts] && (!hPtr[2*noPts+1] || (hPtr[2*noPts+1] == 128))) {
      // pen coming up or end of character description
      if (noPts) { // some to do
	if (usePos->type()) { // real vdc's
	  float *newFloat = new float[noPts * 2];
	  for (i=0; i<noPts; ++i) {
	    fx = hPtr[2*i];
	    fx -= 128;
	    fx *= charSize;
	    fy = - (int) hPtr[2*i+1];
	    fy += 140; // + 12 for bottom of box
	    fy *= charSize;
	    if (fy > myHeight) myHeight = fy;
	    if (-fy > myDepth) myDepth = -fy;
	    newFloat[2*i] = fx * baseVec.cos() + fy * upVec.cos();
	    newFloat[2*i+1] = fy * upVec.sin() + fx * baseVec.sin();
	  }
	  addPts(new vdcPts(newFloat, noPts));
	} else { // integer vdc's
	  int *newInt = new int[noPts * 2];
	  for (i=0; i<noPts; ++i) {
	    fx = hPtr[2*i];
	    fx -= 128;
	    fx *= charSize;
	    fy = - (int) hPtr[2*i+1];
	    fy += 140; // + 12 for bottom of box
	    fy *= charSize;
	    if (fy > myHeight) myHeight = fy;
	    if (-fy > myDepth) myDepth = -fy;
	    newInt[2*i] = (int) (fx * baseVec.cos() + fy * upVec.cos());
	    newInt[2*i+1] = (int) (fy * upVec.sin() + fx * baseVec.sin());
	  }
	  addPts(new vdcPts(newInt, noPts));
	}
      }
      if (hPtr[2*noPts+1]) { // more to come
	hPtr += noPts * 2 + 2;  // step over description
	noPts = 0; // start fresh
      } else break; // last polyline
    } else ++noPts; // normal point
  } while ((hPtr + noPts * 2) <= (hf_array + maxOffset)); // in legal position
}
////
// destructor
////
hersheyChar::~hersheyChar()
{
  vdcPts *myPtr, *nextPtr;
  for (myPtr = contents; myPtr; myPtr = nextPtr) {
    nextPtr = myPtr->next;
    delete myPtr;
  }
}
////
// scale the character
////
void hersheyChar::scale(float factor) 
{
  myShift1 *= factor;
  myShift2 *= factor;
  myShiftY1 *= factor;
  myShiftY2 *= factor;
  myHeight *= factor;
  myDepth *= factor;
  for (vdcPts *myPtr = contents; myPtr; myPtr = myPtr->next)
    myPtr->scale(factor);
}
////
// add some pts to a single character
////
void hersheyChar::addPts(vdcPts *inPts)
{
  if (!contents) contents = inPts;
  else {
    for (vdcPts *myPtr=contents; myPtr->next; myPtr = myPtr->next);
    myPtr->next = inPts;
  }
}
////
// display the character
////
int hersheyChar::display(baseDisplay *inDisplay)
{
  int ret = 1;
  for (vdcPts *myPtr=contents; myPtr; myPtr=myPtr->next) {
    ret = ret && inDisplay->polyline(myPtr);
  }
  return ret;
}
////
// shift the character
////
void hersheyChar::shift(float &xShift, float &yShift)
{
  for (vdcPts *myPtr=contents; myPtr; myPtr=myPtr->next) {
    myPtr->shift(xShift, yShift);
  }
}
////
// hershey string
////
hersheyString::hersheyString(const genText *inText) // constructor
{
  static float xStart, yStart;
  contents = NULL;
  myHeight = myDepth = 0;
  ////
  // create all of the characters
  for (int i=0; i<inText->size(); ++i)
    addChar(new hersheyChar(inText, i));
  ////
  if (inText->pos()) { // may be appended text
    xStart = inText->pos()->x(0);
    yStart = inText->pos()->y(0);
  }
  // is this restricted text ?
  const vdc *inW, *inH;
  if ((inW = inText->width()) && (inH = inText->height())) {
    float inWidth = inW->f();
    float inHeight = inH->f();
    float h = height();
    float d = depth();
    float w = width();
    if (inText->info()) w *= inText->info()->expan();
    if (d > 0) h += d;
    if (h && w) {
      float xHScale = inWidth / w;
      float yHScale = inHeight / h;
      float useHScale = (xHScale < yHScale) ? xHScale : yHScale;
      scale(useHScale); // scale the string
      if (d > 0) yStart += d * useHScale;
    }
  }
//  float yStart1 = yStart;
  // now take care of positioning
  float trueWidth = width(); // figure out true width of string
  if (inText->info()) trueWidth *= inText->info()->expan();
  ////
  if (inText->info()->align()) {
    switch(inText->info()->align()->h()) {
    case 0:
    case 1: break; // ok
    case 2: xStart -= 0.5 * trueWidth; break;
    case 3: xStart -= trueWidth; break;
    case 4: break; // fix later
    }
    switch(inText->info()->align()->v()) {
    case 0: break; // ok
    case 1: yStart -= height(); break;
    case 2: yStart -= 0.9 * height(); break;
    case 3: yStart -= 0.5 * height(); break;
    case 4: yStart -= 0.1 * height(); break;
    case 5:  break;
    case 6 : break; // fix later
    }
  }
  switch(inText->info()->path()) {
  case 0:
    right(inText, xStart, yStart); break;
  case 1:
    left(inText, xStart, yStart); break;
  case 2:
    up(inText, xStart, yStart); break;
  case 3:
    down(inText, xStart, yStart); break;
  }
}
////
// string width
////
float hersheyString::width() 
{
  float w=0;
  for (hersheyChar *p=contents; p; p=p->next) w += p->width();
  return w;
}
////
// scale the string
////
void hersheyString::scale(float factor) 
{
  myHeight *= factor;
  myDepth *= factor;
  for (hersheyChar *p=contents; p; p=p->next) p->scale(factor);
}
////
// destructor
////
hersheyString::~hersheyString() 
{
  hersheyChar *myPtr, *nextPtr;
  for (myPtr = contents; myPtr; myPtr = nextPtr) {
    nextPtr = myPtr->next;
    delete myPtr;
  }
}
////
// write out to the right
////
void hersheyString::right(const genText *inText, float &xStart, float &yStart)
{
  angle upVec(1, 0), baseVec(0, 1);
  float expand = 1;
  if (inText->info()->orientation()) {
    upVec = angle(inText->info()->orientation()->y(0),
		  inText->info()->orientation()->x(0));
    baseVec = angle(inText->info()->orientation()->y(1),
		    inText->info()->orientation()->x(1));
    if (upVec.size()) expand = baseVec.size() / upVec.size();
  }
  expand *= inText->info()->expan();
  for (hersheyChar *myPtr=contents; myPtr; myPtr=myPtr->next) {
    xStart += myPtr->shift1() * expand;
    yStart += myPtr->shiftY1();
    myPtr->shift(xStart, yStart);
    xStart += myPtr->shift2() * expand;
    yStart += myPtr->shiftY2();
  }
}
////
// write out to the left
////
void hersheyString::left(const genText *inText, float &xStart, float &yStart)
{
  angle upVec(1, 0), baseVec(0, 1);
  float expand = 1;
  if (inText->info()->orientation()) {
    upVec = angle(inText->info()->orientation()->y(0),
		  inText->info()->orientation()->x(0));
    baseVec = angle(inText->info()->orientation()->y(1),
		    inText->info()->orientation()->x(1));
    if (upVec.size()) expand = baseVec.size() / upVec.size();
  }
  expand *= inText->info()->expan();
  for (hersheyChar *myPtr=contents; myPtr; myPtr=myPtr->next) {
    xStart -= myPtr->shift2() * expand;
    yStart -= myPtr->shiftY2();
    myPtr->shift(xStart, yStart);
    xStart -= myPtr->shift1() * expand;
    yStart -= myPtr->shiftY1();
  }
}
////
// write up
////
void hersheyString::up(const genText *inText, float &xStart, float &yStart)
{
  float x, expand = 1;
  angle upVec(1, 0), baseVec(0, 1);
  if (inText->info()->orientation()) {
    upVec = angle(inText->info()->orientation()->y(0),
		  inText->info()->orientation()->x(0));
    baseVec = angle(inText->info()->orientation()->y(1),
		    inText->info()->orientation()->x(1));
    if (upVec.size()) expand = baseVec.size() / upVec.size();
  }
  expand *= inText->info()->expan();
  for (hersheyChar *myPtr=contents; myPtr; myPtr=myPtr->next) {
    x = xStart + myPtr->shift1() * expand;
    myPtr->shift(x, yStart);
    yStart += myPtr->height() * upVec.sin();
    xStart += myPtr->height() * upVec.cos();
  }
}
////
// write down
////
void hersheyString::down(const genText *inText, float &xStart, float &yStart)
{
  float x, expand = 1;
  angle upVec(1, 0), baseVec(0, 1);
  if (inText->info()->orientation()) {
    upVec = angle(inText->info()->orientation()->y(0),
		  inText->info()->orientation()->x(0));
    baseVec = angle(inText->info()->orientation()->y(1),
		    inText->info()->orientation()->x(1));
    if (upVec.size()) expand = baseVec.size() / upVec.size();
  }
  expand *= inText->info()->expan();
  for (hersheyChar *myPtr=contents; myPtr; myPtr=myPtr->next) {
    x = xStart + myPtr->shift1() * expand;
    myPtr->shift(x, yStart);
    yStart -= myPtr->height() * upVec.sin();
    xStart -= myPtr->height() * upVec.cos();
  }
}
void hersheyString::addChar(hersheyChar *inChar)
{
  if (!contents) contents = inChar;
  else {
    for (hersheyChar *myPtr = contents; myPtr->next; myPtr = myPtr->next);
    myPtr->next = inChar;
    if (inChar->height() > myHeight) myHeight = inChar->height();
    if (inChar->depth() > myDepth) myDepth = inChar->depth();
  }
}
int hersheyString::display(baseDisplay *inDisplay)
{
  int ret = 1;
  for (hersheyChar *myPtr=contents; myPtr; myPtr=myPtr->next)
    ret = ret && myPtr->display(inDisplay);
  return ret;
}
#ifdef macintosh
 #pragma segment DIS2
#endif
////
// base display
// constructor
////
baseDisplay::baseDisplay()
{
  myPxlExtent = NULL;
}  
////
// graphical primitives
////
// disjoint polyline
////
int baseDisplay::disPolyline(const vdcPts *inPts) // emulate with a polyline
     // we do the disjoint polyline 2 pairs of points at a time
{
  int i, j, ret = 1;
  vdcPts *myPts = NULL;

  if (inPts->type()) { // real vdc's
    float *newFloat = new float[4];
    myPts = new vdcPts(newFloat, 2);

    for (i=0; (i<inPts->no()) && ret; i +=2) {
      for (j=0; j<4; ++j) newFloat[j] = inPts->f(2*i+j);
      ret = ret && polyline(myPts);
    }
  } else { // integer VDC's
    int *newInt = new int[4];
    myPts = new vdcPts(newInt, 2);

    for (i=0; (i<inPts->no()) && ret; i +=2) {
      for (j=0; j<4; ++j) newInt[j] = inPts->i(2*i+j);
      ret = ret && polyline(myPts);
    }
  }
  delete myPts; // clean up
  return ret;
}
////
// polymarker
////
int baseDisplay::polymarker(const vdcPts *inPts) // emulate with disjoint polyline
{
  int ret = 1, i, j;
  float *newFloat;
  int *newInt;
  vdcPts *myPts = NULL;
  float defSize = 0.01 * vdcHeight; // default size
  float markerSize; // marker size we shall use

  // simple descriptions of the markers as vectors, normalised to unit size
  // note that we call circle routine for a circle (number 4)
  const int noMarkers = 6; // for convenience; 4 + circle, 0 is a dummy
  // points in descriptions
  static const int noPoints[noMarkers] = {0, 2, 4, 6, 0, 4}; 
  // position of descriptions
  static const int offsets[noMarkers] = {0, 0, 2, 6, 12, 12}; 
  // descriptions 
  static float markers[32] = {
    0.0, 0.0, 0.0, 0.0,					// dot 
    0.0, 0.5, 0.0, -0.5, 0.5, 0.0, -0.5, 0.0,		// plus
    0.0, 0.5, 0.0, -0.5, 0.25, 0.433, -0.25, -0.433, 
    -0.25, 0.433,  0.25, -0.433, 			// asterisk 
    0.354, 0.354, -0.354, -0.354, 			
    -0.354, 0.354, 0.354, -0.354}; 			// cross 

  ////
  // figure out the size we shall use, 
  if (myMarkerSize) { // been told a size
    markerSize = myMarkerSize->type() ? // relative size required
      myMarkerSize->f() *  defSize // else
	: myMarkerSize->v()->f(); // absolute size
  } else markerSize = defSize; // default

  if (myMarkerType == 4) { // use a circle
    vdc *radius = NULL;
    if (inPts->type()) { // real vdc's
      newFloat = new float[2];
      radius = new vdc((float) (markerSize/2));
      myPts = new vdcPts(newFloat, 1);
      for (i=0; i<inPts->no() && ret; ++i) {
	newFloat[0] = inPts->fx(i);
	newFloat[1] = inPts->fy(i);
	ret = ret && circle(myPts, radius, 1);
      }
    } else { // integer vdc's
      newInt = new int[2];
      radius = new vdc((int) (markerSize/2));
      myPts = new vdcPts(newInt, 1);
      for (i=0; i<inPts->no() && ret; ++i) {
	newInt[0] = inPts->ix(i);
	newInt[1] = inPts->iy(i);
	ret = ret && circle(myPts, radius, 1);
      }
    }
    delete myPts;
    delete radius;
    return ret;
  } else if ((myMarkerType > 0) && (myMarkerType < 6)) {
    ////
    // use disjoint polyline
    float *floatPtr = markers + 2 * offsets[myMarkerType];

    if (inPts->type()) { // real vdc's
      newFloat = new float[2 * noPoints[myMarkerType]];
      myPts = new vdcPts(newFloat, noPoints[myMarkerType]);
      for (i=0; i<inPts->no() && ret; ++i) {
	for (j=0; j<noPoints[myMarkerType]; ++j) {
	  newFloat[2*j] = inPts->fx(i) + markerSize * floatPtr[2*j];
	  newFloat[2*j+1] = inPts->fy(i) + markerSize * floatPtr[2*j+1];
	}
	ret = ret && disPolyline(myPts);
      }
    } else { // integer VDC's
      newInt = new int[2 * noPoints[myMarkerType]];
      myPts = new vdcPts(newInt, noPoints[myMarkerType]);
      for (i=0; i<inPts->no() && ret; ++i) {
	for (j=0; j<noPoints[myMarkerType]; ++j) {
	  newInt[2*j] = inPts->ix(i) + (int) (markerSize * floatPtr[2*j]);
	  newInt[2*j+1] = inPts->iy(i) + (int) (markerSize * floatPtr[2*j+1]);
	}
	ret = ret && disPolyline(myPts);
      }
    }
    delete myPts;
  } // disjoint polyline emulation
  return ret;
}
#ifdef macintosh
 #pragma segment DIS3
#endif
////
// text, emulate
////
int baseDisplay::text(const genText *inText)
{
  hersheyString *myString = new hersheyString(inText);
  int ret = myString->display(this);
  delete myString;
  return ret;
}
////
// polygon
////
int baseDisplay::polygon(const vdcPts *inPts) // emulate with a polyline
{
  int i, ret;
  vdcPts *myPts;
  if (inPts->type()) { // real vdc's
    float *newFloat = new float[2 * (inPts->no() + 1)];
    for (i=0; i<2 * inPts->no(); ++i) newFloat[i] = inPts->f(i);
    newFloat[i++] = inPts->fx(0); // complete polygon
    newFloat[i++] = inPts->fy(0);
    myPts = new vdcPts(newFloat, inPts->no() + 1);
  } else {
    int *newInt = new int[2 * (inPts->no() + 1)];
    for (i=0; i<2 * inPts->no(); ++i) newInt[i] = inPts->i(i);
    newInt[i++] = inPts->ix(0); // complete polygon
    newInt[i++] = inPts->iy(0);
    myPts = new vdcPts(newInt, inPts->no() + 1);
  }
  ret = polyline(myPts);
  delete myPts;
  return ret;
}
////
// polygon set, emulate with a polygon
////
int baseDisplay::polygonSet(const vdcPts *inPts, const int*) 
{
  return polygon(inPts);
}
////
// cell array
////
int baseDisplay::cells(const cellArray *inCarray) // emulate with a polygon
{
  int ret = (inCarray && inCarray->corners()) ?
    rectangle(inCarray->corners()) : 1;
  return ret;
}
////
// rectangle
////
int baseDisplay::rectangle(const vdcPts *inPts) // emulate with a polygon
{
  const int noIndices = 8;
  static const int permute[noIndices] = {0, 1, 0, 3, 2, 3, 2, 1};
  if (!inPts || (inPts->no() < 2)) {
    myError("too few points in a rectangle");
    return 1;
  }
  int ret, i;
  vdcPts *myPts;
  if (inPts->type()) { // real vdc's
    float *newFloat = new float[noIndices];
    for (i=0; i<noIndices; ++i) newFloat[i] = inPts->f(permute[i]);
    myPts = new vdcPts(newFloat, noIndices / 2);
  } else {
    int *newInt = new int[8];
    for (i=0; i<noIndices; ++i) newInt[i] = inPts->i(permute[i]);
    myPts = new vdcPts(newInt, 4);
  }
  ret = polygon(myPts);
  delete myPts;
  return ret;
}
////
// circle
////
int baseDisplay::circle(const vdcPts *centre, const vdc *radius, int lineOnly)
{
  ////
  // get the beginning and ending angles
  angle theta0(-0.0001, -1); // -pi - epsilon
  angle theta1(0.0001, -1); // -pi + epsilon
  // get a new set of points from our emulation routine
  vdcPts *myPts = getArc(centre, radius, &theta0, &theta1, 1);
  int ret = (lineOnly) ? polyline(myPts) : polygon(myPts);
  delete myPts;
  return ret;
}
////
// basic elliptical arc routine, defaults to circles
// i.e., if r2 == NULL and rotation == 0 will get a circle
////
vdcPts *baseDisplay::getArc(const vdcPts *centre, const vdc *radius,
			    const angle *theta0, const angle *theta1,
			    int closeType, const vdc *r2,
			    const angle *rotation)
{
// draw a circular arc from theta0 to theta1
// if closeType == 0 then we add a final point at the centre
// if closeType == 1 then we add a final point duplicating the first
  int i;
  vdcPts *myPts;
#ifdef __MSDOS__
  int totalPts = 5000; // maximum no of points
#else
  int totalPts = 10000; // maximum no of points
#endif
  int noPts = (closeType >= 0) ? totalPts - 1 : totalPts;
  ////
  // need the rotation angle, may be null
  double crot = (rotation) ? rotation->cos() : 1;
  double srot = (rotation) ? rotation->sin() : 0;
  ////
  // get the starting angle, theta0 - rotation, and finishing angle
  angle theta = (rotation) ? *theta0 - *rotation : *theta0;
  angle end = (rotation) ? *theta1 - *rotation : *theta1;
  ////
  // now figure out a reasonable stepping angle
  angle dtheta(1.0 / (1.0 + getSize(radius)), 1);
  ////
  // get the radii, etc.
  float fx, fy;
  if (centre->type()) { // real
    fx = centre->fx(0);
    fy = centre->fy(0);
  } else {
    fx = centre->ix(0);
    fy = centre->iy(0);
  }
  float fa = radius->f(); // first radius
  float fb = (r2) ? r2->f() : fa; // second radius
  // note that we make sure coincident start and end -> full circle
  if (centre->type()) { // real
    float *newFloat = new float[totalPts * 2];
    for (i=0; (i<noPts) &&
	 !(end.within(theta, theta + dtheta) && (i>1)); ++i) {
      newFloat[2*i] = fx + fa * theta.cos() * crot -
	fb * theta.sin() * srot;
      newFloat[2*i+1] = fy + fb * theta.sin() * crot +
	fa * theta.cos() * srot;
      theta +=dtheta;
    }
    if (closeType == 0) { // add point at centre
      newFloat[2*i] = fx;
      newFloat[2*i+1] = fy;
      ++i;
    } else if (closeType == 1) { // close with a chord
      newFloat[2*i] = newFloat[0];
      newFloat[2*i+1] = newFloat[1];
      ++i;
    } 
    myPts = new vdcPts(newFloat, i);
  } else { // integer
    int *newInt = new int[totalPts * 2];
    for (i=0; (i<noPts) &&
	 !(end.within(theta, theta + dtheta) && (i>1)); ++i) {
      newInt[2*i] = (int) (fx + fa * theta.cos() * crot -
			   fb * theta.sin() * srot);
      newInt[2*i+1] = (int) (fy + fb * theta.sin() * crot +
			     fa * theta.cos() * srot);
      theta +=dtheta;
    }
    if (closeType == 0) { // add point at centre
      newInt[2*i] = (int) fx;
      newInt[2*i+1] = (int) fy;
      ++i;
    } else if (closeType == 1) { // close with a chord
      newInt[2*i] = newInt[0];
      newInt[2*i+1] = newInt[1];
      ++i;
    } 
    myPts = new vdcPts(newInt, i);
  }
  return myPts;
}
////
// circular arc, 3 points, may close
// emulate with a polygon or polyline
// closeType == -1 => no closure
// closeType == 0 => pie
// closeType == 1 => chord
////
int baseDisplay::arc3Pt(const vdcPts *inPts, int closeType) 
{
// handy macro
#define SQ(in) ((double) (in) * (in))
  float *c = new float[2];
  int ret = 1;
  if (inPts->no() < 3) return ret; // something strange
  ////
  // first figure out centre, radius and 2 angles from 3 points
  // make sure they're not co-linear
  float t1 = ((inPts->x(2) - inPts->x(0)) * (inPts->y(1) - inPts->y(0)) -
	      (inPts->x(1) - inPts->x(0)) * (inPts->y(2) - inPts->y(0))) * 2;
  if (t1 == 0) return ret; // co-linear
  ////
  // more temporary variables
  float t2 = inPts->x2(1) - inPts->x2(0) + inPts->y2(1) - inPts->y2(0);
  float t3 = inPts->x2(2) - inPts->x2(0) + inPts->y2(2) - inPts->y2(0);
  ////
  // now get the center coordinates, x and y
  c[0] = ((inPts->y(2) - inPts->y(0)) * t2 -
	  (inPts->y(1) - inPts->y(0)) * t3) / t1; 
  c[1] = ((inPts->x(2) - inPts->x(0)) * t2 -
	  (inPts->x(1) - inPts->x(0)) * t3) / t1; 
  ////
  // now the radius
  float r = sqrt(SQ(inPts->x(0) - c[0]) + SQ(inPts->y(0) - c[1]));
  ////
  // now the angles
  angle theta0(inPts->y(0) - c[1], inPts->x(0) - c[0]);
  angle theta1(inPts->y(1) - c[1], inPts->x(1) - c[0]);
  angle theta2(inPts->y(2) - c[1], inPts->x(2) - c[0]);
  ////
  // we need to include the middle point between the start and end
  angle *start, *end;
  if (theta1.within(theta0, theta2)) {
    start = &theta0;
    end = &theta2;
  } else {
    start = &theta2;
    end = &theta0;
  }
  ////
  // prepare for the call to the arc routine
  vdcPts centre(c);
  vdc radius(r);
  ////
  // make the call
  vdcPts *myPts = getArc(&centre, &radius, start, end, closeType);
  ret = (closeType >= 0) ? polygon(myPts) : polyline(myPts);
  delete myPts;
  ////
  // all done
  return ret;
}
////
// circular arc, center, 2 vectors, radius, may close
// emulate with a polygon or polyline
////
int baseDisplay::arcCtr(const vdcPts *inPts, vdc *radius, int closeType) 
{
  int ret = 1;
  if (inPts->no() < 3) return ret; // something strange
  ////
  // first figure out 2 angles from info
  angle theta0(inPts->y(1), inPts->x(1));
  angle theta1(inPts->y(2), inPts->x(2));
  ////
  // now get the emulated points
  vdcPts *myPts = getArc(inPts, radius, &theta0, &theta1, closeType);
  ret = (closeType >= 0) ? polygon(myPts) : polyline(myPts);
  delete myPts;
  ////
  // all done
  return ret;
}
#ifdef macintosh
 #pragma segment DIS4
#endif
////
// ellipse, 3 pts (centre and 2 CDP's)
// emulate with a polygon
////
int baseDisplay::ellipse(const vdcPts *inPts) 
{
  if (inPts->no() < 3) return 1; // something strange
  vdc *radius1, *radius2;

  // get the 2 radii
  float r1 = sqrt(SQ(inPts->x(1) - inPts->x(0)) +
		  SQ(inPts->y(1) - inPts->y(0)));
  float r2 = sqrt(SQ(inPts->x(2) - inPts->x(0)) +
		  SQ(inPts->y(2) - inPts->y(0)));
  ////
  // get the beginning and ending angles
  angle theta0(-0.001, -1); // -pi - epsilon
  angle theta1(0.001, -1); // -pi + epsilon
  // figure out the angle of rotation
  angle rotation(inPts->y(1) - inPts->y(0), inPts->x(1) - inPts->x(0));

  if (inPts->type()) { // real vdc's
    radius1 = new vdc(r1);
    radius2 = new vdc(r2);
  } else { // integer
    radius1 = new vdc((int) r1);
    radius2 = new vdc((int) r2);
  }
  // get a new set of points from our emulation routine
  vdcPts *myPts = getArc(inPts, radius1, &theta0, &theta1,
			 -1, radius2, &rotation);
  int ret = polygon(myPts); // do a polygon
  delete myPts;
  delete radius1;
  delete radius2;
  return ret;
}
////
// elliptical arc, 5 pts plus close type
// emulate with a polygon or polyline
////
int baseDisplay::ellipArc(const vdcPts *inPts, int closeType) 
{
  if (inPts->no() < 5) return 1; // something strange
  vdc *radius1, *radius2;

  // get the 2 radii
  float r1 = sqrt(SQ(inPts->x(1) - inPts->x(0)) +
		  SQ(inPts->y(1) - inPts->y(0)));
  float r2 = sqrt(SQ(inPts->x(2) - inPts->x(0)) +
		  SQ(inPts->y(2) - inPts->y(0)));
  ////
  // figure out the angle of rotation
  angle rotation(inPts->y(1) - inPts->y(0), inPts->x(1) - inPts->x(0));
  ////
  // get the starting and ending angles
  angle theta0(inPts->y(3), inPts->x(3));
  angle theta1(inPts->y(4), inPts->x(4));

  if (inPts->type()) { // real vdc's
    radius1 = new vdc(r1);
    radius2 = new vdc(r2);
  } else { // integer
    radius1 = new vdc((int) r1);
    radius2 = new vdc((int) r2);
  }
  // get a new set of points from our emulation routine
  vdcPts *myPts = getArc(inPts, radius1, &theta0, &theta1,
			 closeType, radius2, &rotation);
  int ret = (closeType >= 0) ? polygon(myPts) : polyline(myPts);
  delete myPts;
  delete radius1;
  delete radius2;
  return ret;
}
////
// function called at beginning of each new picture
///
void baseDisplay::newPic()
{
  // 
  // reset defaults
  // control elements
  //
  myTrans = 1;
  myClipRect = NULL;
  myClip = 0; // fix later
  // attributes
  myLineIndex = 1;
  myLineType = 1;
  myLineWidth = NULL;
  myMarkerIndex = 1;
  myMarkerType = 3;
  myMarkerSize = NULL;
  myTextIndex = 1;
  myFontIndex = 1;
  myTextPrec = 0;
  myCharExpan = 1.0;
  myCharSpace = 0;
  myCharHeight = NULL;
  myCharOri = NULL;
  myTextPath = 0;
  myTextAlign = NULL;
  myEdgeIndex = 1;
  myEdgeType = 1;
  myEdgeWidth = NULL;
  myCharSetIndex = 1;
  myAltCharSetIndex = 1;
  myFillIndex = 1;
  myIntStyle = 0;
  myHatchIndex = 1;
  myPatIndex = 1;
  myEdgeVis = 0;
  myFillRefPt = NULL;
}
////
// function called at beginning of each new picture body
 ///
void baseDisplay::newPicBody(float inWidth, float inHeight,
			     float xOff, float yOff)
{
  // store the vdc extent
  vdcWidth = inWidth;
  vdcHeight = inHeight;
  // get the extent of the output device
  devWidth = pxlExtent()->width();
  devHeight = pxlExtent()->height();
  // how much do we have to scale ? (take care of non-square pixels later)
  xScale = devWidth / vdcWidth;
  yScale = devHeight / vdcHeight;
  // use the smaller scale (largest enclosed rectangle)
  useScale = (yScale < xScale) ? yScale : xScale;
  // get the offsets
  useOff[0] = pxlExtent()->x(0);
  useOff[1] = pxlExtent()->y(0); 
  // need to keep these separate for inverting devices
  useOff[2] = xOff * useScale;
  useOff[3] = yOff * useScale;
}
////
// get scaled points into integer form
////
int baseDisplay::getPts(const vdcPts *inPts, int* &outPtr, int close) 
{
  int i;

  // get the memory
  outPtr = close ? new int[inPts->no() * 2 + 2] : new int[inPts->no() * 2];

  // move over the scaled points
  if (inPts->type()) { // real vdc's
    if (invert()) { // need to invert the picture
      for (i=0; i<inPts->no(); ++i) {
	outPtr[2*i] = (int) (useOff[0] + useOff[2] + useScale * inPts->fx(i));
	outPtr[2*i+1] = (int) (devHeight + useOff[3]
			       - useOff[1] - useScale * inPts->fy(i));
      }
    } else { // normal picture
      for (i=0; i<2*inPts->no(); ++i)
	outPtr[i] = (int) (useOff[i % 2] + useOff[2 + (i % 2)]
			   + useScale * inPts->f(i));
    }
  } else { // integer VDC's
    if (invert()) { // need to invert the picture
      for (i=0; i<inPts->no(); ++i) {
	outPtr[2*i] = (int) (useOff[0] + useOff[2] + useScale * inPts->ix(i));
	outPtr[2*i+1] = (int) (devHeight - useOff[1]
			       + useOff[3] - useScale * inPts->iy(i));
      }
    } else { // normal picture
      for (i=0; i<2*inPts->no(); ++i)
	outPtr[i] = (int) (useOff[i % 2] +  + useOff[2 + (i % 2)]
			   + useScale * inPts->i(i));
    }
  }
  if (close) { // want to close the path
    outPtr[2 * inPts->no()] = outPtr[0];
    outPtr[2 * inPts->no() + 1] = outPtr[1];
  }
  return 1;
}
////
// get scaled points into float form
////
int baseDisplay::getPts(const vdcPts *inPts, float* &outPtr, int close) 
{
  int i;
  // get the memory
  outPtr = close ? new float[inPts->no() * 2 + 2] : new float[inPts->no() * 2];

  // move over the scaled points
  if (inPts->type()) { // real vdc's
    if (invert()) { // need to invert the picture
      for (i=0; i<inPts->no(); ++i) {
	outPtr[2*i] = useOff[0] + useOff[2] + useScale * inPts->fx(i);
	outPtr[2*i+1] = devHeight - useOff[1] + useOff[3]
	  - useScale * inPts->fy(i);
      }
    } else { // normal picture
      for (i=0; i<2*inPts->no(); ++i)
	outPtr[i] = useOff[i % 2] + useOff[2 + (i % 2)]
	  + useScale * inPts->f(i);
    }
  } else { // integer VDC's
    if (invert()) { // need to invert the picture
      for (i=0; i<inPts->no(); ++i) {
	outPtr[2*i] = useOff[0] + useOff[2] + useScale * inPts->ix(i);
	outPtr[2*i+1] = devHeight - useOff[1] + useOff[3]
	  - useScale * inPts->iy(i);
      }
    } else { // normal picture
      for (i=0; i<2*inPts->no(); ++i)
	outPtr[i] = useOff[i % 2] + useOff[2 + (i % 2)]
	  + useScale * inPts->i(i);
    }
  }
  if (close) { // want to close the path
    outPtr[2 * inPts->no()] = outPtr[0];
    outPtr[2 * inPts->no() + 1] = outPtr[1];
  }
  return 1;
}
////
// get scaled points into short form
////
int baseDisplay::getPts(const vdcPts *inPts, short* &outPtr, int close) 
{
  int i;
  // get the memory
  outPtr = (close) ? new short[inPts->no() * 2 + 2]
    : new short[inPts->no() * 2];
  // move over the scaled points
  if (inPts->type()) { // real vdc's
    if (invert()) { // need to invert the picture
      for (i=0; i<inPts->no(); ++i) {
	outPtr[2*i] = (short) (useOff[0] + useOff[2] +
			       useScale * inPts->fx(i));
	outPtr[2*i+1] = (short)(devHeight - useOff[1] + useOff[3]
				-useScale * inPts->fy(i));
      }
    } else { // normal picture
      for (i=0; i<2*inPts->no(); ++i)
	outPtr[i] = (short) (useOff[i % 2] + useOff[2 + (i % 2)]
			     + useScale * inPts->f(i));
    }
  } else { // integer VDC's
    if (invert()) { // need to invert the picture
      for (i=0; i<inPts->no(); ++i) {
	outPtr[2*i] = (short) (useOff[0] + useOff[2] +
			       useScale * inPts->ix(i));
	outPtr[2*i+1] = (short)(devHeight - useOff[1] + useOff[3]
				- useScale * inPts->iy(i));
      }
    } else { // normal picture
      for (i=0; i<2*inPts->no(); ++i)
	outPtr[i] = (short) (useOff[i % 2] + useOff[2 + (i % 2)]
			     + useScale * inPts->i(i));
    }
  }
  if (close) { // want to close the path
    outPtr[2 * inPts->no()] = outPtr[0];
    outPtr[2 * inPts->no() + 1] = outPtr[1];
  }
  return 1;
}
////
// attributes
////
void baseDisplay::colrs(const colrTable*) // colour table test implementation
{
//  fprintf(stderr, "colour table set\n");
}
