package edu.cornell.lassp.houle.HysSym;

import java.awt.*;
import java.awt.image.*;
import java.util.*;

/**
*
* XyPlot is an X-Y plotting class.  XyPlot is an <b>XyConsumer</b> and gets
* X-Y values to plot through the <tt>receiveXy()</tt> method.  For an
* example of usage see the <b>hys</b> applet which sets one up and the
* <b>HysLattice</b> class which feeds it information.
*
* <P>
* <A HREF="../src/edu/cornell/lassp/houle/HysSym/XyPlot.java" TARGET="edu.cornell.lassp.houle.source">
* Source code </A> is available.
*
* @see XyConsumer
* @see HysLattice
* @see hys
*
* @author <A HREF="http://www.msc.cornell.edu/~houle" TARGET="edu.cornell.lassp.houle.author"> Paul Houle </A> (E-mail: <A HREF="mailto:ph18@cornell.edu">ph18@cornell.edu</A>)
* @version 0.9a
*/

public class XyPlot extends Canvas implements XyConsumer {

    Image dbuff;
    Graphics buffg,visual; 
    Dimension d;
    Color bgColor=Color.blue;
    Color fgColor=Color.white;
    Color cursorColor=Color.red;
 
    double X1,X2,Y1,Y2;   // corners of world
    double Xf,Xo,Yf,Yo;   // Factor and Offset for world -> viewport xlation

    double Xc,Yc;         // Current X and Y coordinates.

    int Xs,Ys;
    int Xmargin=2;
    int Ymargin=2;

    boolean markCurrent=false;

/**
*
* Construct an XyPlot.
*
* @param width width in pixels
* @param height height in pixels 
*/
    public XyPlot(int width,int height) {
	    	    
	d=new Dimension(width,height);
        Xs=width;
        Ys=height;

        resize(d);
	setWorld(0.0,1.0,1.0,0.0);
    };

/*
*
* Set world coordinates.  World coordinates are the range of the plot
*
* @param x1 x coordinate of upper left corner
* @param y1 y coordinate of upper left corner
* @param x2 x coordinate of lower right corner
* @param y2 y coordinate of lower right corner
*
*/

    public void setWorld(double x1,double y1,double x2,double y2) {

	X1=x1;   Y1=y1;
        X2=x2;   Y2=y2;

	Xf=(Xs-2*Xmargin)/(X2-X1);
        Yf=(Ys-2*Ymargin)/(Y2-Y1);

	Xo=Xmargin-X1*Xf;
	Yo=Ymargin-Y1*Yf;
};

/*
*
* Implements <tt>XyConsumer.receiveXy(x,y)</tt>.
* @see XyConsumer
*
*/
    public void receiveXy(double x,double y) {

	plotPoint(x,y);
	Xc=x;
	Yc=y;
};

// actually plot a "point" at x,y

    void plotPoint(double x,double y) {

	int Xi,Yi;

	Xi=(int) Math.round(x*Xf+Xo);
	Yi=(int) Math.round(y*Yf+Yo);

	plotSymbol(Xi,Yi,0);
};

// plot a "symbol" at (Xi,Yi)

    void plotSymbol(int Xi,int Yi,int symb) {

        if(buffg !=null ) {
	    buffg.setColor(fgColor);
	    buffg.drawRect(Xi,Yi,1,1);
            repaint(1000);
        };

}; 

// The paint method has a bit of weirdness that was explained by some
// people at sun.  In a perfect world,  we'd create the image that we
// are using for "double-buffering" in the constructor.  This doesn't
// work.  createImage returns a null if it is called ~before~ a
// component has been incorporated on screen.  Since you have to construct
// a component before you can add it,  this is a problem.

// Solution: the paint method checks to see if the buffer exists (by
// seeing if it's value is "null") and then if it doesn't it creates
// it.

    public void paint(Graphics g) {

	if (dbuff == null) {

           dbuff=createImage(d.width,d.height);
           buffg=dbuff.getGraphics();
           buffg.setColor(bgColor);
	   buffg.fillRect(0,0,Xs,Ys);
        };

	g.drawImage(dbuff,0,0,null);


	int Xi=(int) Math.round(Xc*Xf+Xo);
	int Yi=(int) Math.round(Yc*Yf+Yo);
	g.setColor(cursorColor);
	g.drawLine(Xmargin,Yi,Xs-Xmargin,Yi);
	g.drawLine(Xi,Ymargin,Xi,Ys-Ymargin);
    };

// the default update method clears the component back to background
// before we draw.  We don't need this because we're going to paint the
// whole thing over anyway and don't need an unsightly grey flash as
// the image is being computed.

public void update(Graphics g){
 paint(g);
 }

	
};

















