/*************************************************************************
*                                                                        *
*  This source code file, and compiled classes derived from it, can      *
*  be used and distributed without restriction, including for commercial *
*  use.  (Attribution is not required but is appreciated.)               * 
*                                                                        *
*   David J. Eck                                                         *
*   Department of Mathematics and Computer Science                       *
*   Hobart and William Smith Colleges                                    *
*   Geneva, New York 14456,   USA                                        *
*   Email: eck@hws.edu          WWW: http://math.hws.edu/eck/            *
*                                                                        *
*************************************************************************/
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import edu.hws.jcm.data.*;
import edu.hws.jcm.draw.*;
import edu.hws.jcm.awt.*;

public class SecantTangentApplet  extends Applet {

   DisplayCanvas canvas;  // (Defined here so it can be used in the stop() method.)
   

   public void stop() {
        // When applet is stopped, release the memory used for double-buffering of the canvas.
      canvas.releaseResources();
   }
   

   public void init() {  // The init() method sets up the applet.
         
      // Create the parser to parse function definitions.
      
      Parser parser = new Parser();
      Variable x = new Variable("x");
      parser.add(x);


      // Create the basic input elements.

      ComputeButton computeButton = new ComputeButton("New Function");

      VariableInput x1Input = new VariableInput(); // x-coord where tangent line is drawn.
      VariableInput x2Input = new VariableInput(); // x-coord for other point of secant line.

      ExpressionInput input = new ExpressionInput(" sqrt(x)", parser);
      Function func = input.getFunction(x);

      Graph1D graph = new Graph1D(func);
      graph.setColor(Color.black);


      // Create two DraggablePoint objects, which will be the points on the canvas
      // that the user can drag.  The x-coordinate of drag1 will be tied later to
      // x1Input, so that either drag1 or x1Input can be used for setting the
      // values of the point.  Same for drag2 and x2Input.
      
      DraggablePoint drag1 = new DraggablePoint();  // point where tangent is drawn
      DraggablePoint drag2 = new DraggablePoint();  // other point on secant line
      
      drag1.clampY(func);   // Both points are clamped to move along the function.
      drag2.clampY(func);
      
      drag1.setLocation(1,0);  // Set locations, but the y-coords will be reset
      drag2.setLocation(3,0);  //       according to the clamping specified above.
      
      drag1.setColor(Color.red);        // drag1 is red...
      drag1.setGhostColor(Color.pink);  //   but is shown in pink if it's off the graph
      
      drag2.setColor(new Color(0,200,0));           // drag2 is green...
      drag2.setGhostColor(new Color(180,225,180));  //    or light green if off the graph.


      // Create the tangent line and the secant line.  

      DrawGeometric secant = new DrawGeometric(DrawGeometric.INFINITE_LINE_ABSOLUTE,  
                                      drag1.getXVar(), drag1.getYVar(), 
                                      drag2.getXVar(), drag2.getYVar());  // line through 2 points
      secant.setColor(new Color(0,200,0));
                                      
      TangentLine tangent = new TangentLine(drag1.getXVar(), func);
      tangent.setColor(Color.red);
      
      
      // Create a DrawString to display the slopes of the tangent and secant.
      // (Note how the ValueMath class is used to create Value objects that
      // compute the slopes.)
      
      Value tangentSlope = new ValueMath(func.derivative(1), drag1.getXVar());
      Value secantSlope = new ValueMath( new ValueMath(drag2.getYVar(), drag1.getYVar(), '-'),
                                         new ValueMath(drag2.getXVar(), drag1.getXVar(), '-'),
                                         '/');
      DrawString info = new DrawString( "Tangent Slope = #\n Secant Slope = #", 
                              DrawString.TOP_LEFT,
                              new Value[] { tangentSlope, secantSlope } );
      info.setFont(new Font("Monospaced",Font.PLAIN,10));
      info.setBackgroundColor(Color.lightGray);  /// Text area is filled with this color
                                                 /// before the text is drawn.
      info.setFrameWidth(1);  /// If the frame width is greater than 0, then a frame of this
                              /// width is drawn around the text.
      

      // Create the canvas and limit control panel, and add all the visible
      // objects to the canvas.

      canvas = new DisplayCanvas(new CoordinateRect(-1,5,-1,3));
      LimitControlPanel limits =
           new LimitControlPanel( LimitControlPanel.SET_LIMITS | LimitControlPanel.RESTORE 
                                   | LimitControlPanel.EQUALIZE | LimitControlPanel.ZOOM_IN 
                                   | LimitControlPanel.ZOOM_OUT, false);
      limits.addCoords(canvas);
      limits.setBackground(Color.lightGray);
      limits.setErrorReporter(canvas);
      
      canvas.add(new Grid());  // A grid of horizontal and vertical lines.
                               // (This needs to be behind the axes.).
      canvas.add(new Axes());
      canvas.add(drag1);
      canvas.add(drag2);
      canvas.add(tangent);
      canvas.add(secant);
      canvas.add(graph);
      canvas.add(info);
      
      
      // Lay out the components in the applet.  Since I use Panels
      // and not JCMPanels, I have to set up the conrollers by hand.
      
      Panel bottom = new Panel();
      bottom.setLayout(new BorderLayout(3,3));
      Panel funcPanel = new Panel();
      funcPanel.setBackground(Color.lightGray);
      funcPanel.setLayout(new BorderLayout(3,3));
      funcPanel.add(input, BorderLayout.CENTER);
      funcPanel.add(computeButton, BorderLayout.EAST);
      funcPanel.add(new Label(" f(x) = "), BorderLayout.WEST);
      bottom.add(funcPanel,BorderLayout.NORTH);
      Panel xInputPanel = new Panel();
      xInputPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
      xInputPanel.setBackground(Color.lightGray);
      xInputPanel.add(new Label("Tangent at x = "));
      xInputPanel.add(x1Input);
      xInputPanel.add(new Label("  Secant to x = "));
      xInputPanel.add(x2Input);
      bottom.add(xInputPanel, BorderLayout.CENTER);
      
      setLayout(new BorderLayout(3,3));
      setBackground(Color.darkGray);
      add(canvas, BorderLayout.CENTER);
      add(limits, BorderLayout.EAST);
      add(bottom, BorderLayout.SOUTH);
   

      // Set up Controllers for the applet.  This is essential for
      // making the applet active.  I use two controllers -- graphControl,
      // which recomputes everything when the definition of the function
      // is changed, and dragControl, which only recomputes stuff that
      // relates to the tangent line and secant line.

      Controller graphControl = new Controller();
      Controller dragControl  = new Controller();

      graphControl.add(input);       // graphControl checks the function input box.
      graphControl.add(graph);       // It will recompute the graph.
      graphControl.add(dragControl); // It will also tell dragControl to do its stuff.
      
      input.setOnUserAction(graphControl);         // graphControl.compute() is called when the
                                                   //    user hits return in the function input.
      computeButton.setOnUserAction(graphControl); // It is also called when the user hits
                                                   //    the compute button.
                                                   
      graphControl.setErrorReporter(canvas);  // Errors that occur when graphControl.compute() is
                                              //    running are reported on the canvas.
      
      dragControl.add(x1Input);  // dragControl checks the contents of the x-inputs
      dragControl.add(x2Input);  //    and recomputes everything except the graph.
      dragControl.add(drag1);
      dragControl.add(drag2);
      dragControl.add(tangent);
      dragControl.add(secant);
      dragControl.add(info);

      drag1.setOnUserAction(dragControl);     // dragControl.compute() is called when the
      drag2.setOnUserAction(dragControl);     //    user drags one of the points or types
      x1Input.setOnTextChange(dragControl);   //    in one of the x-input boxes.
      x2Input.setOnTextChange(dragControl);

      
      // By adding Tie's to dragControl, we make sure that the positions of the
      // draggable points are synchronized with the contents of the x-input boxes.

      dragControl.add(new Tie((Tieable)drag1.getXVar(), x1Input));
      dragControl.add(new Tie((Tieable)drag2.getXVar(), x2Input));


   } // end init()


   public Insets getInsets() {
        // Set up a three-pixel border between the edges of the applet and its contents.
      return new Insets(3,3,3,3);
   }

} // end class SecantTangentApplet


