public abstract class Curve {

  public abstract double x(double t);
  public abstract double y(double t);
  
  // screen coordinates
  public int x_center; // x-coordinate of the origin
  public int y_center; // y-coordinate of the origin
  public int radius;   // distance to unit circle
  
  public void setScreen(int x_center, int y_center, int radius) {
    this.x_center = x_center; 
    this.y_center = y_center;
    this.radius = radius;
  }
  
  private int xScreen(double t) {
    return (int)Math.round(x_center+radius*x(t));
  }
  
  private int yScreen(double t) {
    return (int)Math.round(y_center+radius*y(t));  
  }

  // Determine if a curve between t=a and t=b is bent at t=c.  
  // Say it is if C is outside a narrow ellipse.
  // If it is bent there, subdivide the interval.
  private SCL bent (double a, double b, double c, SCL list) {
    int a1 = xScreen(a);
    int a2 = yScreen(a);
	int b1 = xScreen(b);
	int b2 = yScreen(b);
	int c1 = xScreen(c);
	int c2 = yScreen(c);
	double excess =
            Math.sqrt((a1-c1)*(a1-c1) + (a2-c2)*(a2-c2)) +
		    Math.sqrt((b1-c1)*(b1-c1) + (b2-c2)*(b2-c2)) -
		    Math.sqrt((a1-b1)*(a1-b1) + (a2-b2)*(a2-b2));
    if (excess > 0.03) {
      list = interpolate(list,a,c);
      list = interpolate(list,c,b);
    }
    return list;
  }
  
  // Add to the list the coordinates of the curve (f(t),g(t)) for t
  // between a and b. It is assumed that the point (f(a),g(a)) is 
  // already on the list. Enough points will be interpolated between a 
  // and b so that the approximating polygon looks like the curve.  
  // The last point to be included will be (f(b),g(b)).}
  public SCL interpolate (SCL list, double a, double b) {
    // first try bending it at the midpoint
    SCL result = bent(a,b,(a+b)/2.0,list);
    if (result!=list) return result;    
	// now try 4 random points
	for (int i=0; i<4; ++i) {
	  double t = Math.random();
	  result = bent(a,b,t*a+(1.0-t)*b,list);
	  if (result!=list) return result;
	} // for
    // it's a straight line
    int b1 = xScreen(b);
    int b2 = yScreen(b);
    if ((list.x!= b1 || list.y!=b2) && b1!=Double.NaN && b2!=Double.NaN)
      list = new SCL(list,b1,b2);
    return list; // it's a straight line
  } // interpolate
  
}