/*

 * This is the CurvePanel class for the Famous Curves applet.

 * Author: Ben Soares < bs@st-and.ac.uk >

 *

 */



import java.awt.*;

import java.applet.*;



public class CurvePanel extends Panel {



 // class variable



 static Color darkGreen;



 // member variables



 int pointstage;		//stage at which a chosen point is (0=no point chosen, 1=point chosen)

 int pointX, pointY;		//chosen point position

 int circlestage;		//stage at which a chosen circle is (0=no circle chosen, 1=choosing radius, 2=dragging circle)

 int circleX, circleY, circleCX, circleCY, circleR;

				//chosen circle centre position, circumference point, and radius

 int associatedcurveindex;	//which associated curve option is selected

				/* (0=None, 1=Evolute, 2=Involute, 3=Inverse, 4=Pedal,

				 *  5=Negative Pedal, 6=Caustic, 7=Caustic (parallel lines)) */

 int associatedcurve[][];	//the pixel points of the associated curve

 Curve curve;			//the curve being dealt with

 boolean setaxis;		//have Curves class variables been set?

 boolean clear;			//do you want to clear the curvepanel picture?

 double a, b, c, d, e, f, h, k, m, n, p;// the curves original parameter values

 int steps = 1;				// how many steps to take at each line plot of curve

 // CurvePanel constructor methods



 public CurvePanel(Curve curve, int associatedcurveindex) {



  setBackground(Color.white);

  this.setaxis = false;

  this.curve = curve;

  Font font = new Font("Helvetica", Font.PLAIN, 12);

  setFont(font);

//  Label label = new Label(curve.name, Label.CENTER);

//  add(label);



  this.pointstage = 0;

  this.circlestage = 0;

  this.pointX = 0;

  this.pointY = 0;

  this.circleX = 0;

  this.circleY = 0;

  this.circleCX = 0;

  this.circleCY = 0;

  this.circleR = 0;

  this.associatedcurveindex = associatedcurveindex;

  this.clear = false;

  darkGreen = new Color(0,140,0);



 }



 public CurvePanel(Curve curve){



  setBackground(Color.white);

  this.setaxis = false;

   int xsc = (int)(size().width/(curve.xrange*(3/2)));

   int ysc = (int)(size().height/(curve.yrange*(3/2)));

   int sc = Math.min(xsc, ysc);

   Curve.xsc = sc;

   Curve.ysc = sc;

   Curve.Ox = (int)(curve.xb*Curve.xsc*(-3/2));

   Curve.Oy = (int)(curve.yt*Curve.xsc*(3/2));

   Curve.xsize = size().width;

   Curve.ysize = size().height;

  this.curve = curve;



  Font font = new Font("Helvetica", Font.PLAIN, 14);

  setFont(font);

//  Label label = new Label(curve.name, Label.CENTER);

//  add(label);



  this.pointstage = 0;

  this.circlestage = 0;

  this.pointX = 0;

  this.pointY = 0;

  this.circleX = 0;

  this.circleY = 0;

  this.circleCX = 0;

  this.circleCY = 0;

  this.circleR = 0;

  this.associatedcurveindex = 0;

  this.clear = false;

  darkGreen = new Color(0,140,0);



 } /* end of constructor methods */



 // recalculate associated curve method (for scales and translations)



 public void recalculate() {



  switch (associatedcurveindex) {



   case 0:			// "None"

    break;



   case 1:			// "Evolute"

    this.associatedcurve = curve.evolutePixels();

    break;



   case 2:			// "Involute"

    this.associatedcurve = curve.involutePixels(pointX, pointY);

    break;



   case 3:			// "Inverse"

    this.associatedcurve = curve.inversePixels(circleX, circleY, circleCX, circleCY);

    break;



   case 4:			// Pedal

    this.associatedcurve = curve.pedalPixels(pointX, pointY);

    break;



   case 5:			// Negative pedal

    this.associatedcurve = curve.negativePedalPixels(pointX, pointY);

    break;



   case 6:			// Caustic

    this.associatedcurve = curve.causticPixels(pointX, pointY);

    break;



   case 7:			// Caustic (parallel lines)

    this.associatedcurve = curve.causticPixels(pointX, pointY, new String("parallel"));

    break;



  }



  this.pointX = Curve.xPointToPixel(Curve.xPixelToPoint(pointX));

  this.pointY = Curve.yPointToPixel(Curve.yPixelToPoint(pointY));

  this.circleX = Curve.xPointToPixel(Curve.xPixelToPoint(circleX));

  this.circleY = Curve.yPointToPixel(Curve.yPixelToPoint(circleY));

  this.circleCX = Curve.xPointToPixel(Curve.xPixelToPoint(circleCX));

  this.circleCY = Curve.yPointToPixel(Curve.yPixelToPoint(circleCY));

  this.circleR = (int)(Math.sqrt((circleCX-circleX)*(circleCX-circleX) + (circleCY-circleY)*(circleCY-circleY)));



 } /* end of recalculate method */





 // beginning of scale method



 public void scale (double f) {



  this.pointX = Curve.xPointToPixel(f*Curve.xPixelToPoint(this.pointX));

  this.pointY = Curve.yPointToPixel(f*Curve.yPixelToPoint(this.pointY));

  this.circleX = Curve.xPointToPixel(f*Curve.xPixelToPoint(this.circleX));

  this.circleY = Curve.yPointToPixel(f*Curve.yPixelToPoint(this.circleY));

  this.circleCX = Curve.xPointToPixel(f*Curve.xPixelToPoint(this.circleCX));

  this.circleCY = Curve.yPointToPixel(f*Curve.yPixelToPoint(this.circleCY));

  this.circleR = (int)(Math.sqrt((circleCX-circleX)*(circleCX-circleX)+(circleCY-circleY)*(circleCY-circleY)));



 } /* end of scale method */



 // beginning of translate method



 public void translate (int xt, int yt) {



  this.pointX = this.pointX + xt;

  this.pointY = this.pointY + yt;

  this.circleX = this.circleX + xt;

  this.circleY = this.circleY + yt;

  this.circleCX = this.circleCX + xt;

  this.circleCY = this.circleCY + yt;



 } /* end of translate method */



 // beginning of setParameters method



 public void setParameters(double a, double b, double c, double d, double e, double f, double h, double k, double m, double n, double p) {

  this.a = a;

  this.b = b;

  this.c = c;

  this.d = d;

  this.e = e;

  this.f = f;

  this.h = h;

  this.k = k;

  this.m = m;

  this.n = n;

  this.p = p;

 } /* end of setParameters method */



 // beginning of resetCurveParameters method



 public void resetCurveParameters() {

  this.curve.a = this.a;

  this.curve.b = this.b;

  this.curve.c = this.c;

  this.curve.d = this.d;

  this.curve.e = this.e;

  this.curve.f = this.f;

  this.curve.h = this.h;

  this.curve.k = this.k;

  this.curve.m = this.m;

  this.curve.n = this.n;

  this.curve.p = this.p;

 } /* end of resetCurveParameters method */



 // mousedown method



 public boolean mouseDown(Event e, int mx, int my) {



  switch (associatedcurveindex) {



   case 0:			// "None"

    break;



   case 1:			// "Evolute"

    // note this should be drawn from when it is chosen

    break;



   case 2:			// "Involute"

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.involutePixels(pointX, pointY);

    repaint();

    break;



   case 3:			// "Inverse"

    boolean redraw = true;

    double mR = Math.sqrt( (mx-circleX)*(mx-circleX) + (my-circleY)*(my-circleY) );	// mouse distance from centre

    switch (circlestage) {

     case 0:		// no circle yet

      circleX = mx;

      circleY = my;

      circleCX = mx;

      circleCY = my;

      circleR = 2;

      circlestage = 1;

      break;

     case 1:		// after choosing radius

      if ( circleR-1 < mR && mR < circleR+2 ) {		// if mouse is "on" circumference

       circleCX = mx;

       circleCY = my;

       circleR = (int)(mR);

       circlestage = 1;

       redraw = false;

      } else if ( mR < 4 ) {				// if mouse is "on" centre

       circleX = mx;

       circleY = my;

       circlestage = 2;

       redraw = false;

      } else {						// otherwise start new circle

       circleX = mx;

       circleY = my;

       circleCX = mx;

       circleCY = my;

       circleR = 2;

       circlestage = 1;

      }

      break;

     case 2:		// after dragging circle (same as after choosing radius)

      if ( circleR-2 < mR && mR < circleR+3 ) {		// if mouse is "on" circumference

       circleCX = mx;

       circleCY = my;

       circleR = (int)(mR);

       circlestage = 1;

       redraw = false;

      } else if ( mR < 3 ) {				// if mouse is "on" centre

       circleX = mx;

       circleY = my;

       circlestage = 2;

       redraw = false;

      } else {						// otherwise start new circle

       circleX = mx;

       circleY = my;

       circleCX = mx;

       circleCY = my;

       circleR = 2;

       circlestage = 1;

      }

      break;

    }

    associatedcurve = curve.inversePixels(circleX, circleY, circleCX, circleCY);

    if (redraw) { repaint(); }

    break;



   case 4:			// Pedal

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.pedalPixels(pointX, pointY);

    repaint();

    break;



   case 5:			// Negative pedal

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.negativePedalPixels(pointX, pointY);

    repaint();

    break;



   case 6:			// Caustic

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.causticPixels(pointX, pointY);

    repaint();

    break;



   case 7:			// Caustic (parallel lines)

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.causticPixels(pointX, pointY, new String("parallel"));

    repaint();

    break;



  }



  return true;



 } /* end of mousedown method */





 // mousedrag method



 public boolean mouseDrag(Event e, int mx, int my) {



  switch (associatedcurveindex) {



   case 0:			// "None"

    break;



   case 1:			// "Evolute"

    // note that this should be drawn from when it is chosen

    break;



   case 2:			// "Involute"

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.involutePixels(pointX, pointY);

    repaint();

    break;



   case 3:			// "Inverse"

    switch (circlestage) {

     case 0:		// no circle yet (this should never happen)

      circleX = mx;

      circleY = my;

      circleCX = mx;

      circleCY = my;

      circleR = 2;

      circlestage = 1;

      break;

     case 1:		// choosing radius

      double mR = Math.sqrt( (mx-circleX)*(mx-circleX) + (my-circleY)*(my-circleY) );	// mouse distance from centre

      circleCX = mx;

      circleCY = my;

      circleR = Math.max(2,(int)(mR));

      circlestage = 1;

      break;

     case 2:		// dragging circle

      circleX = mx;

      circleY = my;

      circleCX = mx + circleR;

      circleCY = my;

      circlestage = 2;

      break;

    }

    associatedcurve = curve.inversePixels(circleX, circleY, circleCX, circleCY);

    repaint();

    break;



   case 4:			// Pedal

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.pedalPixels(pointX, pointY);

    repaint();

    break;



   case 5:			// Negative pedal

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.negativePedalPixels(pointX, pointY);

    repaint();

    break;



   case 6:			// Caustic

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.causticPixels(pointX, pointY);

    repaint();

    break;



   case 7:			// Caustic (parallel lines)

    pointX = mx;

    pointY = my;

    pointstage = 1;

    associatedcurve = curve.causticPixels(pointX, pointY, new String("parallel"));

    repaint();

    break;



  }



  return true;



 } /* end of mousedrag method */



 // paint method



 public void paint(Graphics g) {



  if (!this.setaxis) {

   int sc;

   sc = (int)(size().height/(curve.yrange));

   Curve.xsc = sc;

   Curve.ysc = sc;

   Curve.Ox = (int)(-curve.xb*sc+(size().width-size().height)/2);

//   Curve.Ox = (int)(size().width/2 + (curve.xb+curve.xt)/2*sc)

   Curve.Oy = (int)(+curve.yt*sc);

   Curve.xsize = size().width;

   Curve.ysize = size().height;

   this.curve.setCurve();

   this.curve.translate(0, 0);

   this.translate(0, 0);

   this.setaxis = true;

  }

  if (this.clear) {

   g.setColor(Color.white);

   g.fillRect(0, 0, size().width-1, size().height-1);

   this.clear = false;

  }



  g.setColor(Color.lightGray);

  //draw axis

  g.drawLine(Curve.Ox, 0, Curve.Ox, size().height);

  g.drawLine(0, Curve.Oy, size().width, Curve.Oy);



  g.setColor(Color.blue);

  //draw the main curve

  int acx1, acy1, acx2, acy2, acx0, acy0;

  int j = 0;

  for (int i=0; i<=curve.nop-steps; i+=steps) {

   acx1 = curve.fxpixels[i];

   acy1 = curve.fypixels[i];

   acx2 = curve.fxpixels[i+steps];

   acy2 = curve.fypixels[i+steps];

   // the next "if" checks to see at least one of the endpoints is on screen

   if ((0 < acx2 && acx2 < Curve.xsize && 0 < acy2 && acy2 < Curve.ysize) || (0 < acx1 && acx1 < Curve.xsize && 0 < acy1 && acy1 < Curve.ysize)) {

    // the next "if" checks to see the points aren't too far away

    if (Math.abs(acx1 - acx2) < size().height/2 && Math.abs(acy1 - acy2) < size().width/2) {

     g.drawLine(acx1, acy1, acx2, acy2);

    }

   }

   j = i;

  }

  if (j!=curve.nop-1) {

   j++;

   acx1 = curve.fxpixels[j];

   acy1 = curve.fypixels[j];

   acx2 = curve.fxpixels[curve.fxpixels.length-1];

   acy2 = curve.fypixels[curve.fxpixels.length-1];

   // the next "if" checks to see at least one of the endpoints is on screen

   if ((0 < acx2 && acx2 < Curve.xsize && 0 < acy2 && acy2 < Curve.ysize) || (0 < acx1 && acx1 < Curve.xsize && 0 < acy1 && acy1 < Curve.ysize)) {

    // the next "if" checks to see the points aren't too far away

    if (Math.abs(acx1 - acx2) < size().height/2 && Math.abs(acy1 - acy2) < size().width/2) {

     g.drawLine(acx1, acy1, acx2, acy2);

    }

   }

  }

   



  g.setColor(Color.green);

  //draw a point if appropriate

  if (pointstage > 0) {

   g.fillArc(pointX-2, pointY-2, 4, 4, 0, 360);

  }

  //draw circle if appropriate

  if (circlestage > 0) {

   g.fillArc(circleX-2, circleY-2, 4, 4, 0, 360);

   g.setColor(darkGreen);

   g.drawArc(circleX-circleR, circleY-circleR, 2*circleR, 2*circleR, 0, 360);

  }

  //draw a line if appropriate

  if (associatedcurveindex == 7 && pointstage>0) {

   g.setColor(darkGreen);

   if (pointX == Curve.Ox) {

    if (pointY == Curve.Oy) {

     g.drawLine(0, Curve.Oy, size().width, Curve.Oy);

    } else {

     g.drawLine(Curve.Ox, 0, Curve.Ox, size().height);

    }

   } else {

    int linepoints[][] = Curve.lineEnds(Curve.yPixelToPoint(pointY)/Curve.xPixelToPoint(pointX), 0.0);

    g.drawLine(linepoints[0][0], linepoints[1][0], linepoints[0][1], linepoints[1][1]);

   }

  }



  g.setColor(Color.red);

  //draw associated curve if appropriate

  if ( (associatedcurveindex>0 && pointstage+circlestage>0) || (associatedcurveindex==1) ) {

   for (int i=0; i<=curve.nop+2-steps; i+=steps) {

    acx1 = associatedcurve[0][i];

    acy1 = associatedcurve[1][i];

    acx2 = associatedcurve[0][i+steps];

    acy2 = associatedcurve[1][i+steps];

    // the next "if" checks to see at least one of the endpoints is on screen

    if ((0 < acx2 && acx2 < Curve.xsize && 0 < acy2 && acy2 < Curve.ysize) || (0 < acx1 && acx1 < Curve.xsize && 0 < acy1 && acy1 < Curve.ysize)) {

     // the next "if" checks to see the points aren't too far away

     if (Math.abs(acx1 - acx2) < size().height/2 && Math.abs(acy1 - acy2) < size().width/2) {

      g.drawLine(acx1, acy1, acx2, acy2);

     }

    }

    j = i;

   }

   if (j!=curve.nop-1) {

    j++;

    acx1 = associatedcurve[0][j];

    acy1 = associatedcurve[1][j];

    acx2 = associatedcurve[0][associatedcurve[0].length-1];

    acy2 = associatedcurve[1][associatedcurve[1].length-1];

    // the next "if" checks to see at least one of the endpoints is on screen

    if ((0 < acx2 && acx2 < Curve.xsize && 0 < acy2 && acy2 < Curve.ysize) || (0 < acx1 && acx1 < Curve.xsize && 0 < acy1 && acy1 < Curve.ysize)) {

     // the next "if" checks to see the points aren't too far away

     if (Math.abs(acx1 - acx2) < size().height/2 && Math.abs(acy1 - acy2) < size().width/2) {

      g.drawLine(acx1, acy1, acx2, acy2);

     }

    }

   }

  }



  g.setColor(darkGreen);

  // outline point or centre of circle if appropriate

  if (pointstage > 0 && associatedcurveindex != 7) {

   g.drawArc(pointX-2, pointY-2, 4, 4, 0, 360);

  }

  if (circlestage > 0) {

   g.drawArc(circleX-2, circleY-2, 4, 4, 0, 360);

  }



 } /* end of paint method */



} /* end of CurvePanel class */

