///////////////////////////////////////////////////////////////////////
//
// GraphIT: Plot curves in a framed graph area. 
//
///////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1997-98 by the IBM Corporation. All Rights Reserved.
//
// Author:          Angel L. Diaz     aldiaz@us.ibm.com
//                  Barry M. Trager   bmt@watson.ibm.com
//                  Robert S. Sutor   sutor@us.ibm.com
//
// IBM MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
// THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IBM SHALL NOT BE LIABLE FOR
// ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING OR MODIFYING
// THIS SOFTWARE OR ITS DERIVATIVES.
//
///////////////////////////////////////////////////////////////////////

import java.awt.*;
import java.applet.Applet;

public class GraphIT extends Applet {
  boolean debug = false;

  public static final int SINCURVE = 1;
  public static final int COSCURVE = 2;
  public static final int TANCURVE = 3;

  private FramedGraphArea framedGraphArea = null;
  private TrigCurve       trigCurve        = null;

  public void init() {
    showStatus( "GraphIT Initializing ..." ); 

    if (debug) 
	   System.out.println("GraphIT: Dimension " + size() );

    GridBagLayout gridBag = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();

    setLayout(gridBag);

    trigCurve        = new TrigCurve( TrigCurve.SINCURVE );
    framedGraphArea  = new FramedGraphArea(this, trigCurve);

    c.fill = GridBagConstraints.BOTH;
    c.weighty = 1.0;
	c.weightx = 1.0;
	c.gridheight = GridBagConstraints.RELATIVE;
    c.gridwidth = GridBagConstraints.REMAINDER; //end row
    gridBag.setConstraints(framedGraphArea, c);
    add(framedGraphArea);

    validate();
  } 
    
  public void start() {
     showStatus( "GraphIT Ready!" );
  }

  public void stop() {
  }

  public void destroy() {
  }
 
  public Insets insets() {
     return new Insets(4,4,5,5);
  }
  
  public String getAppletInfo() {
     return "GraphIT (named " + getParameter("name") + ") by A. Diaz, B. Trager, R. Sutor";
  }

  public void setParam( int p ) {
     framedGraphArea.setParam( p );
  } 

  public void setCurve( int curveID ) {
     framedGraphArea.setCurve( curveID );
  } 
}

class FramedGraphArea extends Panel {
  private GraphArea graphArea = null;

  public FramedGraphArea(GraphIT GraphITApp, CurveInterface Curve ) {
    super();

    //Set layout to one that makes its contents as big as possible.
    setLayout(new GridLayout(1,0));

    graphArea = new GraphArea(GraphITApp,Curve);
    add( graphArea );
    validate();
    }

    public Insets insets() {
        return new Insets(4,4,5,5);
    }

    public void paint(Graphics g) {
        Dimension d = size();

        g.setColor(Color.black);
        g.draw3DRect(0, 0, d.width - 1, d.height - 1, true);
        g.draw3DRect(3, 3, d.width - 7, d.height - 7, false);
    }

  public void setParam( int p ) {
     graphArea.setParam( p );
  }

  public void setCurve( int curveID ) {
     graphArea.setCurve( curveID );
  } 

}

class GraphArea extends Canvas {
  private Point point          = null;

  private GraphIT GraphITApp   = null;
  private CurveInterface Curve = null;

  private Dimension offDimension;
  private Image offImage       = null;
  private Graphics offGraphics = null;

  public GraphArea( GraphIT GraphITApp, CurveInterface Curve) {
     super();
     this.GraphITApp = GraphITApp;
	 this.Curve      = Curve;
  }

  public void setParam( int p ) {
     Curve.setParam( p );
	 repaint( );
  }

  public void setCurve( int curveID ) {
     Curve.setCurve( curveID );
	 repaint( );
  } 

  public boolean mouseDown(Event event, int x, int y) {
    if (point == null) {
       point = new Point(x, y);
    } else {
       point.x = x;
       point.y = y;
    }
    repaint();

    return false;
  }

  public void paint(Graphics g) {
     update(g);
  }

  public void update(Graphics g) {
    Dimension d = size();
    boolean fillSquare;
        boolean fillNextFrame;
        int rowWidth = 0;
        int x = 0, y = 0;
        int w, h;
        int tmp;

    //Create the offscreen graphics context, if no good one exists.
    if ( (offGraphics == null)
         || (d.width != offDimension.width)
         || (d.height != offDimension.height) ) {
         offDimension = d;
         offImage = createImage(d.width, d.height);
         offGraphics = offImage.getGraphics();
    }

    //Erase the previous image.
	offGraphics.setColor(Color.white);
    offGraphics.fillRect(0, 0, d.width, d.height);
    offGraphics.setColor(Color.black);

    // draw axes and curve
	Curve.drawAxes( offGraphics, d );	 
	Curve.drawCurve( offGraphics, d );

    //Paint the image onto the screen.
    g.drawImage(offImage, 0, 0, this);
  }

}

interface CurveInterface  {
  public void setParam( int p );
  public void setCurve( int curveID );
  public void drawAxes(Graphics g, Dimension d);
  public void drawCurve(Graphics g, Dimension d);
  public double evaluateCurve( double p );
};

class TrigCurve implements CurveInterface {
  public static final int SINCURVE = 1;
  public static final int COSCURVE = 2;
  public static final int TANCURVE = 3;

  private int curveID = 0;
  private int param   = 10;

  public TrigCurve( int curveID ) {
     this.curveID = curveID;
  }

  public void setParam( int p ) {
     param = p;
  }

  public void setCurve( int curveID ) {
     this.curveID = curveID;
  }

  public void drawAxes(Graphics g, Dimension d) {	
    int xone  = d.width;
    int xhalf = xone / 2;
    int yone  = d.height;
	int yhalf = yone / 2;

	g.setColor(Color.black);
	g.drawLine(0, yhalf, xone, yhalf);
	g.drawLine(xhalf, 0, xhalf, yone);
  }

  public void drawCurve(Graphics g, Dimension d) {
	int xone   = d.width;
	int yone   = d.height;
	int ypos   = param*yone/100;
	int yscale = yone/2;

	g.setColor(Color.red);
	double xmin = -10.0;
	double xmax = 10.0;
	double xrange = xmax - xmin;

	double xval = xmin;
	double yval = evaluateCurve(param/20.0*xval);
	int x1 = (int)((xval-xmin)/xrange * xone);
	int y1 = (int)((-yval+1)*yscale);

	int nsteps = 500;
	double xstep = xrange / nsteps ;
			
	for (int i=0; i<nsteps; i++) {	
	   xval = xval + xstep;
	   yval = evaluateCurve(param/20.0*xval);
	   int x2 = (int)((xval-xmin)/xrange * xone);
	   int y2 = (int)((-yval+1)*yscale);
	   g.drawLine(x1, y1, x2, y2);
	   x1 = x2;
	   y1 = y2;
	}
  }

  public double evaluateCurve( double p ) {
     double value = 0;
     switch( curveID ) {
	    case SINCURVE:
		   value = Math.sin( p ); 
		break;
		case COSCURVE:
		   value = Math.cos( p );
		break;
		case TANCURVE:
		   value = Math.tan( p );
		break;
		default:
		   value = Math.sin( p );
		break;
	}
    return value;
  }
}
