//
import java.awt.*;

public class testCubic extends BufferedApplet
{
   int w = 0, h, type = 0, is = -1;
   Cubic spline;
   double G[] = {0,1,0,1};
   Rectangle S[] = new Rectangle[4];
   Color splineBgColor = new Color(240,240,255);
   Color bgColor = new Color(200,200,200);
   Color sliderColor = new Color(255,240,240);
   Color slideColor = new Color(220,200,200);
   String name[] = {"BEZIER", "BSPLINE", "CATMULL-ROM", "HERMITE"};
   Font font = new Font("Helvetica", Font.PLAIN, 30);
   Color dotColor[] = {Color.black,Color.red,Color.green.darker(),Color.blue};

   public void render(Graphics g) {
      if (w == 0) {
         w = bounds().width;
         h = bounds().height;
	 createSpline();
	 for (int i = 0 ; i < 4 ; i++)
	    S[i] = new Rectangle(h/3, (h/3-80)/2 + 20*i, h/3, 20);
      }

      // DRAW THE BACKGROUND COLOR

      g.setColor(bgColor);
      g.fillRect(0,0,w,h);

      // DRAW THE SPLINE

      g.setColor(splineBgColor);
      g.fill3DRect(h/3,h/3,h/3,h/3, true);
      g.setColor(Color.black);
      g.drawRect(h/3,h/3,h/3,h/3);
      for (int x = h/3 ; x < 2*h/3 ; x++) {
	 int y = Y2y(spline.eval(x2X(x)));
	 g.drawLine(x,y,x,y+1);
      }
      switch (type){
      case 0: drawDots(g, 0, 1./3, 2./3, 1); break;
      case 1: drawDots(g, -.95, 0, 1, 1.95); break;
      case 2: drawDots(g, -.95, 0, 1, 1.95); break;
      case 3: drawDots(g, 0, 1, .1, .9); break;
      }

      // DRAW THE SLIDERS

      for (int i = 0 ; i < 4 ; i++) {
	 Rectangle s = S[i];
         g.setColor(sliderColor);
	 g.fill3DRect(s.x, s.y, s.width, s.height, true);
	 int x = (int)(s.x + s.height/2 + G[i] * (s.width - s.height));
	 g.setColor(dotColor[i]);
	 g.fill3DRect(x-s.height/2, s.y, s.height-1, s.height-1, true);
      }

      // SHOW THE SPLINE TYPE

      g.setColor(Color.black);
      g.setFont(font);
      g.drawString(name[type], h/3, 5*h/6);

   }

   // DRAW THE FOUR KNOT DOTS

   void drawDots(Graphics g, double a, double b, double c, double d) {
      g.setColor(dotColor[0]);
      drawDot(g, a, G[0]);
      g.setColor(dotColor[1]);
      drawDot(g, b, G[1]);
      g.setColor(dotColor[2]);
      drawDot(g, c, G[2]);
      g.setColor(dotColor[3]);
      drawDot(g, d, G[3]);
   }
   void drawDot(Graphics g, double X, double Y) {
      g.fillOval(X2x(X) - 10, Y2y(Y) - 10, 20, 20);
   }

   // CONVERT BETWEEN PIXELS AND SPLINE COORDINATES

   double x2X(int x) { return (x-h/3.) / (h/3.); }
   double y2Y(int y) { return 1 - (y-h/3.) / (h/3.); }
   int X2x(double X) { return (int)(h/3 + X*h/3); }
   int Y2y(double Y) { return (int)(h/3 + (1-Y)*h/3); }

   // RESPOND TO USER INPUT

   public boolean mouseDown(Event e, int x, int y) {

      // CHANGING SPLINE TYPE

      if (y > 2*h/3) {
	 type = (type + 1) % 4;
	 createSpline();
	 return true;
      }

      // STARTING TO DRAG ON A SLIDER

      for (is = 3 ; is >= 0 ; is--)
	 if (S[is].contains(x,y)) {
	    setG(x);
	    break;
         }
      return true;
   }

   public boolean mouseDrag(Event e, int x, int y) {
      if (is >= 0)
	 setG(x);
      return true;
   }

   // SET ONE GEOMETRY INPUT VALUE

   void setG(int x) {
      Rectangle s = S[is];
      G[is] = Math.max(0, Math.min(1, ((double)x - (s.x + s.height/2)) / (s.width - s.height)));
      createSpline();
   }

   // CREATE THE SPLINE

   void createSpline() {
      switch (type) {
      case 0: spline = new Cubic(Cubic.BEZIER     , G); break;
      case 1: spline = new Cubic(Cubic.BSPLINE    , G); break;
      case 2: spline = new Cubic(Cubic.CATMULL_ROM, G); break;
      case 3: spline = new Cubic(Cubic.HERMITE    , G); break;
      }
      damage = true;
   }
}