/*GraphCanvas.java*/
/*
 * author: Umberto Ravaioli
 * version 1.0 - Copyright: Amanogawa.com - All Rights Reserved
 */

import java.awt.*;

public class GraphCanvas extends Canvas{
    protected static final Color bgcolor = new Color(216,216,191);
    
    protected Image im;
    protected Graphics buf;
    protected int LeftMargin;
    protected int TopSep;
    protected int BottomSep;
    protected int RightMargin;
    protected int VPos=0;
    protected String XLabel, YLabel, ZLabel, TITLE;
    protected String X1, X2, X3, X1_mil, X2_mil;
    protected String Y1, Y2, Y3;
    protected int NumPoints;
    protected double x[];
    protected double y[];
    protected double ymin, ymax;
    protected double xmin, xmax;
    protected double stripwidth;
    protected int xx[];
    protected int yy[];
    protected double xRef;
    protected boolean Should_Plot_Zero_Line;
    protected boolean Should_Plot_Ref_Point;
    protected boolean MilsLabel, IsEpsilon;
    protected boolean IsYRangeMinSet, IsYRangeMaxSet;
    protected boolean IsXRangeMinSet, IsXRangeMaxSet;
    public static Font font1, font2;
    public double sfactor;

    public GraphCanvas(){
	super();
	setBackground(bgcolor);
        
	XLabel="x-axis";
	YLabel="y-axis";
	TITLE ="Unknown Title";
	Y1 = " ";
	Y2 = " ";
	Y3 = " ";
	X1 = " ";
	X2 = " ";
	X1_mil = " ";
	X2_mil = " ";
	X3 = " ";
	NumPoints = 1001;
	x = new double[NumPoints];
	y = new double[NumPoints];
	xx = new int[NumPoints];
	yy = new int[NumPoints];
	for(int i=0;i<NumPoints;i++){
	    x[i]=i;
	    y[i]=0.0;
	}
	IsYRangeMinSet=false;
	IsYRangeMaxSet=false;
	IsXRangeMinSet=false;
	IsXRangeMaxSet=false;
	
	xRef = 0.0;
	stripwidth = 0.6;
        
	Should_Plot_Zero_Line = false;
	Should_Plot_Ref_Point = false;
	MilsLabel = false;
	IsEpsilon = false;
    }
     
public void paint(Graphics g){
    if(im == null){
	im = createImage(getSize().width,getSize().height);
	buf = im.getGraphics();
	drawGraph(buf);
    }
    drawGraph(buf);
    g.drawImage(im,0,0,null);
}

//Addition to reduce flicker new routine
public void update(Graphics g){		// added to avoid clearing
	paint(g);
}

public void drawGraph(Graphics g){
	LeftMargin = (int)Math.ceil(sfactor*45);
        TopSep = (int)Math.ceil(sfactor*40);
        BottomSep = (int)Math.ceil(sfactor*40);
        RightMargin = (int)Math.ceil(sfactor*35);
        
        font1 = new Font("SanSerif",Font.PLAIN,(int)Math.ceil(sfactor*11));
        font2 = new Font("Serif",Font.PLAIN,(int)Math.ceil(sfactor*12));
        
	g.clearRect(0,0,getSize().width,getSize().height);
    	drawAxis(g);
	if(Should_Plot_Zero_Line){plotZeroLine(g);}
	
        g.setColor(Color.black);
        g.draw3DRect(0,0,getSize().width-1,getSize().height-1,false);
	
        drawTitle(g);
        drawTitle(g); // print again or smoothed fonts look washed out on dark
        plotPoints(g);
	plotRefPoint(g);
	
}

protected void ignition(){
    int height=getSize().height;
    int width=getSize().width;
    if(!IsYRangeMinSet){ymin = MaestroA.getMin(y);}
    if(!IsYRangeMaxSet){ymax = MaestroA.getMax(y);}
    if(!IsXRangeMaxSet){xmax = x[x.length-1];}
    if(!IsXRangeMinSet){xmin = x[0];}
    if(IsYRangeMinSet || IsYRangeMaxSet){
	MaestroA.confiner(y,ymax,ymin);
    }
    if(IsXRangeMinSet || IsXRangeMaxSet){
	MaestroA.confiner(x,xmax,xmin);
    }
    
    if(ymin==ymax){ymax=2*ymin+1;}
    if(ymin < 0.0){ymin = 0.0;} // to avoid going to -1 in some limit cases
    for(int i=0;i<xx.length;i++){
	xx[i]= (int)MaestroA.mapper(x[i],(double)(width-RightMargin),(double)(LeftMargin),xmax,xmin);
	yy[i]= (int)MaestroA.mapper(y[i],(double)TopSep,(double)(height-BottomSep),ymax,ymin);
    } 
    
    X1=""+MaestroA.rounder(x[0],3);
    X2=""+MaestroA.rounder(x[x.length-1],3);
    X1_mil=""+MaestroA.rounder(x[0]/0.0254,3);
    X2_mil=""+MaestroA.rounder(x[x.length-1]/0.0254,3);
    X3="|";
    Y1=""+MaestroA.rounder(ymin,3);
    if(ymax < 1.0e230){
	Y2=""+MaestroA.rounder(ymax,3);
    }
    else{
	Y2="";
    }
    Y3="_";
    
}

protected void plotZeroLine(Graphics g){
    if(ymin*ymax>0){return;}
    FontMetrics fm = g.getFontMetrics();
    g.setFont(font1);
    int yy;
    yy = (int)MaestroA.mapper(0.0,(double)TopSep,(double)(getSize().height-BottomSep),ymax,ymin);
    g.setColor(Color.red);
    g.drawLine(LeftMargin,yy,getSize().width-RightMargin,yy);
    g.drawString("0",LeftMargin-fm.stringWidth("0"),yy+fm.getHeight()/3);
}

public synchronized void plotZeroLine(boolean Should_Plot_Zero_Line){
    this.Should_Plot_Zero_Line = Should_Plot_Zero_Line;
}

protected void plotRefPoint(Graphics g){
    int myX=0, myY=0, i;
    i = (int)(NumPoints*(xRef-x[0])/(x[x.length-1]-x[0]));
    if(i < 0) {i = 0;}
    if(i >= NumPoints){i=NumPoints-1;}
    myX = (int)MaestroA.mapper(xRef,(double)(getSize().width-RightMargin),(double)(LeftMargin),xmax,xmin);
    if(y[i] > 0.0){
	myY = (int)MaestroA.mapper(y[i],(double)TopSep,(double)(getSize().height-BottomSep),ymax,ymin);
    }
    else{
	myY =getSize().height-BottomSep ;
    }
    g.setColor(Color.white);
    Graphics2D g2d = (Graphics2D)g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    
    g.drawLine(myX,myY,myX,getSize().height-BottomSep+(int)Math.ceil(sfactor*5));
    
    g.setColor(Color.red);
    MaestroG.fillCircle(myX,myY,(int)Math.ceil(sfactor*6),g);
    g.setColor(Color.white);
    MaestroG.drawCircle(myX,myY,(int)Math.ceil(sfactor*6),g);
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
    
}

public synchronized void plotRefPoint(boolean Should_Plot_Ref_Point){
    this.Should_Plot_Ref_Point = Should_Plot_Ref_Point;
}

protected void plotPoints(Graphics g){
    ignition();
    Graphics2D g2d = (Graphics2D)g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    
    g.setColor(Color.yellow);
    g.drawPolyline(xx,yy,xx.length);
}

protected void drawTitle(Graphics g){
    Graphics2D g2d = (Graphics2D)g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        
    FontMetrics fm = g.getFontMetrics();
    g.setFont(font1);
    g.setColor(Color.white);
    g.drawString(XLabel,(getSize().width-fm.stringWidth(XLabel))/2,
			 getSize().height-BottomSep+3*fm.getHeight()/2-(int)Math.ceil(sfactor*3));
    
    if(IsEpsilon){
        MaestroG.subscripterSymFirst2("\u03b5","r,eff","",g,(int)Math.ceil(sfactor*13),(int)Math.ceil(sfactor*8),getSize().height/2-(int)Math.ceil(sfactor*10));
        g.setFont(font1);
    }
    else{
        MaestroG.subscripter("Z","0","",g,(int)Math.ceil(sfactor*12),LeftMargin-(int)Math.ceil(sfactor*25),getSize().height/2-(int)Math.ceil(sfactor*10));
        g.setFont(font1);
    }
    
    g.setColor(Color.white);
    g.drawString(TITLE,(getSize().width-fm.stringWidth(TITLE))/2,TopSep/2);
    
    g.setColor(Color.yellow);
    g.drawString(X1,LeftMargin-(int)Math.ceil(sfactor*6),getSize().height-BottomSep+3*fm.getHeight()/2);
    g.drawString(X2,getSize().width-RightMargin-fm.stringWidth(X2)+(int)Math.ceil(sfactor*15),getSize().height-BottomSep+3*fm.getHeight()/2);
    if(MilsLabel){
	g.setColor(Color.green);
	//g.drawString(X1_mil,LeftMargin-6,getSize().height-BottomSep+3*fm.getHeight()/2+13);
	//g.drawString(X2_mil,getSize().width-RightMargin-fm.stringWidth(X2)+15-5,getSize().height-BottomSep+3*fm.getHeight()/2+13);
	g.setColor(Color.yellow);
    }
    
    if(stripwidth == 0.0 && !IsEpsilon){
	MaestroG.subscripterInfinityThree("","","","","",g,(int)Math.ceil(sfactor*12),(int)Math.ceil(sfactor*13),TopSep+(int)Math.ceil(sfactor*4));
        g.setFont(font1);
	g.drawLine((int)Math.ceil(sfactor*22),TopSep-(int)Math.ceil(sfactor*6),(int)Math.ceil(sfactor*22),TopSep-(int)Math.ceil(sfactor*16));
	//MaestroG.drawArrow((int)Math.ceil(sfactor*22),TopSep-(int)Math.ceil(sfactor*16),5,g);
        MaestroG.drawArrowScaled((int)Math.ceil(sfactor*22),TopSep-(int)Math.ceil(sfactor*16),1,sfactor,g);
    }
    else{
        g.drawString(Y2,Math.max((int)Math.ceil(sfactor*4),LeftMargin-fm.stringWidth(Y2))-(int)Math.ceil(sfactor*7),TopSep+(int)Math.ceil(sfactor*4));
    }
    //g.drawString(X3,getSize().width-RightMargin,getSize().height-BottomSep+(int)Math.ceil(sfactor*5));
    g.drawLine(getSize().width-RightMargin,getSize().height-BottomSep+(int)Math.ceil(sfactor*5),
               getSize().width-RightMargin,getSize().height-BottomSep);
    
    g.drawString(Y1,Math.max((int)Math.ceil(sfactor*4),LeftMargin-fm.stringWidth(Y1))-(int)Math.ceil(sfactor*7),getSize().height-BottomSep+(int)Math.ceil(sfactor*5));
    g.drawString(Y3,LeftMargin-(int)Math.ceil(sfactor*6),TopSep-(int)Math.ceil(sfactor*1));
    g.setColor(Color.black);
}

protected void drawAxis(Graphics g){
    Graphics2D g2d = (Graphics2D)g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        
    VPos = getSize().height-BottomSep;
    g.setColor(Color.white);
    //Vertical Axis
    g.drawLine(LeftMargin,TopSep/2,LeftMargin,getSize().height-BottomSep+(int)Math.ceil(sfactor*5));
    //MaestroG.drawArrow(LeftMargin,TopSep/2,5,g);
    MaestroG.drawArrowScaled(LeftMargin,TopSep/2,1,sfactor,g);
    //Horizontal Axis
    g.drawLine(LeftMargin-(int)Math.ceil(sfactor*5),VPos,getSize().width-RightMargin+(int)Math.ceil(sfactor*10),VPos);
    //MaestroG.drawArrow(getSize().width-RightMargin+(int)Math.ceil(sfactor*10),VPos,7,g);
    MaestroG.drawArrowScaled(getSize().width-RightMargin+(int)Math.ceil(sfactor*10),VPos,3,sfactor,g);
    
    g.setColor(Color.green);
    if(MilsLabel){
	//g.drawString("[mils]",(getSize().width)/2-5,getSize().height-7);
    }
    g.setColor(Color.white);
    if(IsEpsilon){
    //g.setFont(new Font("Serif",Font.PLAIN,14));
    //g.drawString("\u03b5",5,100);
    //g.setFont(new Font("SanSerif",Font.PLAIN,11));
    }
    else{
    //g.setFont(new Font("Serif",Font.PLAIN,12));
    g.setFont(font2);
    g.drawString("[ \u03a9 ]",(int)Math.ceil(sfactor*10),(int)Math.ceil(sfactor*120));
    g.setFont(font1);
    }
}

public final synchronized void setXLabel(String XLabel){
    this.XLabel=XLabel;
}

public final synchronized void setYLabel(String YLabel){
    this.YLabel=YLabel;
} 

public final synchronized void setTitle(String TITLE){
    this.TITLE = TITLE;
}

public final synchronized void setLabels(String TITLE, String YLabel, String XLabel, String ZLabel){
    this.TITLE=TITLE;
    this.YLabel = YLabel;
    this.XLabel = XLabel;
    this.ZLabel = ZLabel;
}
 
public void plot(double[] xdata, double[] ydata){
    if(NumPoints != xdata.length){
	NumPoints = xdata.length;
	x = new double[NumPoints];
	y = new double[NumPoints];
	xx = new int[NumPoints];
	yy = new int[NumPoints];
    }
    for(int i = 0; i < NumPoints; i++){
	x[i] = xdata[i];
	y[i] = ydata[i];
    }
    repaint();
}

public void setYRange(double ymax, double ymin){
    IsYRangeMaxSet=true;
    IsYRangeMinSet=true;
    this.ymax=ymax;
    this.ymin=ymin;
}

public void setYRangeMin(double ymin){
    IsYRangeMinSet=true;
    this.ymin=ymin;
}

public void setStripWidth(double stripwidth){
    this.stripwidth=stripwidth;
}

public void setYRangeMax(double ymax){
    IsYRangeMaxSet=true;
    this.ymax=ymax;
}

public void setLabelAxis(boolean MilsLabel){
    this.MilsLabel = MilsLabel;
}

public void setIsEpsilon(boolean IsEpsilon){
    this.IsEpsilon = IsEpsilon;
}

public void setAuto(){
    IsYRangeMaxSet=false;
    IsYRangeMinSet=false;
    IsXRangeMaxSet=false;
    IsXRangeMinSet=false;
}

public synchronized void setRefPoint(double xRef){
    this.xRef = xRef;
}

public synchronized void setScaling(double sfactor){
	this.sfactor = sfactor;
}   

}
