
import java.awt.*;

public class bspline extends BufferedApplet
{
   int w = 1, h, I = -1;
   double S = 8, p[] = new double[(int)S + 4];
   Color lightGreen = new Color(160,255,160);

   public void render(Graphics g) {
      w = bounds().width;                 // FETCH APPLET WIDTH,HT
      h = bounds().height;
      g.setColor(Color.white);            // FILL BACKGROUND
      g.fillRect(0,0,w,h);
      g.setColor(Color.green);             // DRAW Y=0 AXIS
      g.drawLine(0,h/2,w,h/2);
      for (int i = 0 ; i < p.length ; i++) {
         int x = X2x(i-1);
	 g.setColor(lightGreen);
         g.drawLine(x,0,x,h);             // DRAW VERTICAL STRIPES
	 g.setColor(Color.black);
	 g.fillOval(x-4,Y2y(p[i])-4,8,8); // DRAW KEY HANDLES
      }
      g.setColor(Color.red);
      for (int x = 0 ; x < w ; x++)
	 g.drawLine(x,f2(x),x+1,f2(x+1));   // DRAW THE CURVE
      g.setColor(Color.blue);
      for (int x = 0 ; x < w ; x++)
	 g.drawLine(x,f1(x),x+1,f1(x+1));   // DRAW THE CURVE
      g.setColor(Color.black);
      for (int x = 0 ; x < w ; x++) {
	 g.drawLine(x,f(x),x+1,f(x+1));     // DRAW THE CURVE
	 g.drawLine(x,f(x)+1,x+1,f(x+1)+1);
      }
   }

   // CONVERT BETWEEN PIXELS (x,y) AND CURVE COORDS (X,Y)

   double x2X(int x) { return S * x / w; }
   double y2Y(int y) { return (h/2-y) * S / w; }
   int X2x(double X) { return (int)(w * X / S); }
   int Y2y(double Y) { return h/2 - (int)(Y * w / S); }

   int i_prev = -10000;

   int f(int x) {         // FUNCTION DEFINING CURVE, IN PIXEL COORDS
      double t = x2X(x);

      int i = (int)Math.floor(t);
      t -= i; 
      double A=p[i], B=p[i+1], C=p[i+2], D=p[i+3];

      double a = (  -A + 3*B - 3*C + D) / 6;
      double b = ( 3*A - 6*B + 3*C    ) / 6;
      double c = (-3*A       + 3*C    ) / 6;
      double d = (   A + 4*B +   C    ) / 6;

      t = ((a * t + b) * t + c) * t + d;

      return Y2y(t);
   }
   int f1(int x) {
      double t = x2X(x);

      int i = (int)Math.floor(t);
      t -= i; 
      double A=p[i], B=p[i+1], C=p[i+2], D=p[i+3];

      double b = (-3*A + 9*B - 9*C + 3*D) / 6;
      double c = ( 6*A -12*B + 6*C      ) / 6;
      double d = (-3*A       + 3*C      ) / 6;

      t = (b * t + c) * t + d;

      return Y2y(t);
   }
   int f2(int x) {
      double t = x2X(x);

      int i = (int)Math.floor(t);
      t -= i; 
      double A=p[i], B=p[i+1], C=p[i+2], D=p[i+3];

      double c = (-6*A +18*B -18*C + 6*D) / 6;
      double d = ( 6*A -12*B + 6*C      ) / 6;

      t = c * t + d;

      return Y2y(t);
   }
/*
                                  t=0     t=1

   fA(t) =  -t3 +  3t2 - 3t + 1    1       0  
           -3t2 +  6t  - 3        -3       0
           -6t  +  6               6       0


   fB(t) =  3t3 -  6t2      + 4    4       1
            9t2 - 12t              0      -3
           18t  - 12             -12       6


   fC(t) = -3t3 +  3t2 + 3t + 1    1       4
           -9t2 +  6t  + 3         3       0
          -18t  +  6               6     -12


   fD(t) =   t3                    0       1
            3t2                    0       3
            6t                     0       6


   0 6 -12
   1*(0+6)/2 = 3
   1*(6-12)/2 = -3

   A = width of first segment
   B = width of second segment

   assume second derivative f2 at A = 1, and zero area under f2 between 0 and A+B

   f2 is piecewise linear:  t     = 0 ... A ...  A+B

                            f2(t) = 0 ... 1 ... -A/B-1

                                                A = -B(1+q)  =>  -A/B = 1+q  =>  q = -A/B-1

   f1 is piecewise quadratic:

      Betw 0 and A:    f1(t) = tt/(2A)        f1(0) = 0   f1(A) = A/2
                       f2(t) = t/A


      Betw A and A+B:  f (t) = auu + bu      f1(0) = A/2   f1'(0) = 1   f1(B) = 0
                       f'(t) = 2au + b + 1




   fB1 = ? + ? t + ? tt
   fB2 = d - ? t

   derivative at left of A = derivative at right of B = 0

   integral of f2 over A = A(0+2d/A)/2 =  d = change in f1 over A
   integral of f2 over B = B(2d/A+?)/2 = -d = change in f1 over B

   ? = -2d (1/B + 1/A)

   f2(0 < t < A) = d t / A
   f2(A < t < B) = d - 2d (1/B + 1/A) t

   f1(0 < t < A) = d t2 / A / 2
   f1(A < t < B) = d t - (d/B + d/A) t2

*/

/*
   Generalizing to unevenly spaced knots would allow a really
   useful general purpose utility.

   If we were to make uneven knots, the interaction might be:
      Click on a keypoint (except left or rightmost ones) to delete it
      Click on space between keypoints to create a new keypoint
      Drag on a vertical line to move it left/right
      Drag on a keypoint to move it up/down
*/

   int mode;
   public boolean keyUp(Event e, int key) {
      mode = key;
      damage = true;
      return true;
   }

   public boolean mouseDown(Event e, int x, int y) {
      I = (int)(x2X(x) + .5) + 1;
      return true;
   }
   public boolean mouseDrag(Event e, int x, int y) {
      if (I >= 0 && I < p.length) {
	 p[I] = y2Y(y);
	 damage = true;
      }
      return true;
   }
}


