// Riemann Sum Applet
// Concept by Bill Ziemer
// Programming by Brendon Cheves & Bill Ziemer
// e-mail: brendon@csulb.edu  wziemer@csulb.edu

// Copyright 1996 by California State University, Long Beach
// expr package Copyright 1996 by Darius Bacon

// Created 4/19/96
// Last update 10/16/96


// java packages
import java.applet.*;
import java.awt.*;
import java.lang.Math;


// our packages
import expr.*;			// f(x) parser by Darius Bacon
import cool_utils.*;	// cool utilities we made


public class Riemann extends Applet
{
	static final boolean DEBUG = false;

	Color current_color = Color.black;

	Button helpButton, aboutButton, plusdx, minusdx, okBtn;

	Label sliderLabel,areaSumLabel, realAreaLabel,errorLabel;

	int	dx = 80;

	double	theSum, theIntegral, areaSum, realArea, approxErr, error;
   		
    CardLayout
    	theCards;
    	
    String	theTopStyle = "Constant, Random point";
    
    Canvas	theCanvas;
    
    Panel	theDrawingPanel, theSliderPanel, theFeedbackPanel, okBtnPanel,
    		mainCd, aboutCd, helpCd, theAboutPanel, theHelpPanel, dxPanel;
    
    Rectangle	dxWidth;
    	
    Image offscreenImage;

   	Graphics offscreenGraphics, offscreenInstance;

	MultiLineLabel aboutLabel;
	
	Dimension d;
	
	// EZGridLayout manager stuff
	EZGridSettings gridSettings;
	EZGridLayout EZGL;

	// Our function class, based on the Java Polygon class
	ZFunction theFunction;

	
    public void init()
    {
     	resize(640,420);
   	   	
   	   	error = 1E-10;
   	   	
  	   	theFunction = new ZFunction();
   	   	
    	// create a card layout
    	theCards = new CardLayout(10,10);
		this.setLayout(theCards);
 		
 		// create the main card
 		mainCd = new Panel();
 		this.add("main",mainCd);
 		mainCd.setLayout(new BorderLayout(10,10));
 		
 		// create the about card
 		aboutCd = new Panel();
 		this.add("about",aboutCd);
  		aboutCd.setLayout(new BorderLayout(10,10));
		
 		// create the help card
 		helpCd = new Panel();
 		this.add("help",helpCd);
  		helpCd.setLayout(new BorderLayout(10,10));
  

    	// create the drawing panel and add it to the main cd
   		theDrawingPanel = new Panel();
		mainCd.add("Center",theDrawingPanel);
 		theDrawingPanel.setLayout(new BorderLayout(10,10));

		  		  	
	  	// create the EZGridLayout
  		EZGL = new EZGridLayout(12,2);
	
		
		// create the feedback panel and add it to the main cd
 		theFeedbackPanel = new Panel();
		mainCd.add("South",theFeedbackPanel);
 		theFeedbackPanel.setLayout(EZGL);
 		 
 		minusdx = new Button("-");
		gridSettings = new EZGridSettings(minusdx,1,1,1,1);
		theFeedbackPanel.add(minusdx);
 		EZGL.addLayoutInfo(gridSettings);


  		dxPanel = new Panel();
 		dxPanel.setLayout(new FlowLayout());
 		gridSettings = new EZGridSettings(dxPanel,2,1,2,1);
		theFeedbackPanel.add(dxPanel);
		EZGL.addLayoutInfo(gridSettings);

 		dxWidth = new Rectangle(2,2,dx,12);
 		 		
 		plusdx = new Button("+");
		gridSettings = new EZGridSettings(plusdx,4,1,1,1);
		theFeedbackPanel.add(plusdx);
		EZGL.addLayoutInfo(gridSettings);
				 				
		sliderLabel = new Label();
		sliderLabel.setText("dx = " + String.valueOf(dx/theFunction.XSCALE));
		gridSettings = new EZGridSettings(sliderLabel,5,1,2,1);
		theFeedbackPanel.add(sliderLabel);
		EZGL.addLayoutInfo(gridSettings);

		areaSumLabel = new Label();
		gridSettings = new EZGridSettings(areaSumLabel,7,1,3,1);
		theFeedbackPanel.add(areaSumLabel);
		EZGL.addLayoutInfo(gridSettings);

		realAreaLabel = new Label();
		gridSettings = new EZGridSettings(realAreaLabel,10,1,3,1);
		theFeedbackPanel.add(realAreaLabel);
		EZGL.addLayoutInfo(gridSettings);
		
		errorLabel = new Label();
		gridSettings = new EZGridSettings(errorLabel,7,2,3,1);
		theFeedbackPanel.add(errorLabel);
		EZGL.addLayoutInfo(gridSettings);
		
		helpButton = new Button("Help");
		gridSettings = new EZGridSettings(helpButton,1,2,1,1);
		theFeedbackPanel.add(helpButton);
		EZGL.addLayoutInfo(gridSettings);

		aboutButton = new Button("About...");
		gridSettings = new EZGridSettings(aboutButton,2,2,1,1);
		theFeedbackPanel.add(aboutButton);
		EZGL.addLayoutInfo(gridSettings);
		
		theFeedbackPanel.resize(preferredSize());
		
		// create the about panel and add it to the about cd
		theAboutPanel = new Panel();
		aboutCd.add("Center",theAboutPanel);
		theAboutPanel.setLayout(new FlowLayout());
		
		// create the OK button panel and add it to the about cd
		okBtnPanel = new Panel();
		aboutCd.add("South",okBtnPanel);
		
		// add the stuff to the about panel
		Font theFont = new Font("TimesRoman", Font.BOLD, 18);
		aboutLabel = new MultiLineLabel(
			"Riemann Sum Applet\nCopyright 1996 by California State University, Long Beach\nWritten by Bill Ziemer and Brendon Cheves\nFunction Parsing Copyright 1996 by Darius Bacon",
			10, 10, MultiLineLabel.CENTER);
		aboutLabel.setFont(theFont);
		theAboutPanel.add(aboutLabel);
		
		// add the OK button
		okBtn = new Button("OK");
		okBtnPanel.add(okBtn);

		// initialize the offscreen stuff
		d = theDrawingPanel.size();

		offscreenImage = this.createImage(600,500);
		offscreenGraphics = offscreenImage.getGraphics();
		if (offscreenGraphics == null) System.out.println("Error: Null offscreen graphics");
		
		// parse the default function
		theFunction.parse("log(7 * x + 7) + cos(7 * x)");

		//store the real answer for the area
		realArea = theFunction.area(0.0,1.0,error);
        
		// finally, show the main card
		theCards.show(this,"main");
    }
    
    
    public void paint(Graphics g)
    {
    	// who cares about g, we know where we want to draw
		offscreenGraphics.clearRect(0,0,600,500);

	    if(theTopStyle.equals("Constant, Random point"))
	    {
			theFunction.drawRectangles(offscreenGraphics,dx);	    	
	    }
	    if(theTopStyle.equals("Constant, Left point"))
	    {
			theFunction.drawRectangles(offscreenGraphics,dx,1.0);
	    }
		if(theTopStyle.equals("Constant, Mid-point"))
		{
			theFunction.drawRectangles(offscreenGraphics,dx,0.5);
		}
	    if(theTopStyle.equals("Linear"))
	    {
			theFunction.drawTrapezoids(offscreenGraphics,dx);
		}
	    if(theTopStyle.equals("Quadratic"))
	    {
			theFunction.drawQuadratics(offscreenGraphics,dx);
		}
		
		theFunction.plot(offscreenGraphics);
		
		drawAxis(offscreenGraphics);
 		drawLimits(offscreenGraphics);
		drawLabels(offscreenGraphics);
		
		// force the offscreen buffer to draw into the drawing panel
		theDrawingPanel.getGraphics().drawImage(offscreenImage,0,0,theDrawingPanel);   		
		
		// update the dx width graphic
		drawdxWidth(dxPanel.getGraphics());
	}
     
    
	// Override update() for double buffering
	public void update(Graphics g)
	{
		paint(g);
	}


	public Insets insets()
	{
		return new Insets(10,10,10,10);
	}

 	
 	// handle GUI events
    public boolean action(Event e, Object arg)
	{
		double outputdx;
		
       	if(e.target == minusdx)
      	{
       		if(dx>1)
      		{
      			dx--;
      			dxWidth.width = dx;
      			paint(theDrawingPanel.getGraphics());

      		}
      		return true;
      	}
      	else if(e.target == plusdx)
      	{
      		if(dx < 260)
      		{
      			dx++;
      			dxWidth.width = dx;
      			paint(theDrawingPanel.getGraphics());

      		}
      		return true;
      	}
 
      	else if(e.target == aboutButton)
      	{
			theCards.show(this,"about");
      		return true;
      	}
      	else if(e.target == okBtn)
      	{
 			theCards.show(this,"main");
     		return true;
      	}

 		else return super.action(e,arg);	
	}

	
	// draw the axis
	public void drawAxis(Graphics g)
	{
 	  	g.setColor(Color.black);
		
		// x axis
        g.drawLine(0,theFunction.yRealToPixel(0),580,theFunction.yRealToPixel(0));	
 
        // y axis
        g.drawLine(40,20,40,500);
        
        g.drawString("X",585,theFunction.yRealToPixel(0) + 4);
        g.drawString("Y",38,17);
 	}


	// draw all the necessary labels
	public void drawLabels(Graphics g)
	{
		double outputdx;
		
		outputdx = theFunction.xPixelToReal(dx + 40) - ((1E3 * theFunction.xPixelToReal(dx + 40)) % 1) / 1E3;
		error = 1E-10;
 
	 	sliderLabel.setText("dx = " + String.valueOf(outputdx));
		areaSumLabel.setText("Area = " + String.valueOf( theFunction.areaSum - ((1E6 * theFunction.areaSum) % 1) / 1E6));
		realAreaLabel.setText("Real Area = " + String.valueOf(realArea - ((1E6 * realArea) % 1) / 1E6));
		errorLabel.setText("Error = " + String.valueOf( realArea - theFunction.areaSum ));
	}

	
	// draw limits in the proper places
	public void drawLimits(Graphics g)
	{
      	g.setColor(Color.red);
		
		// x limit
		g.drawString(".5",(int)(theFunction.XSCALE * .5) + 44,theFunction.yRealToPixel(0) + 14);
		g.drawLine((int)(theFunction.XSCALE * .5) + 40,theFunction.yRealToPixel(0) - 2,(int)(theFunction.XSCALE * .5) + 40,theFunction.yRealToPixel(0) + 2);

		g.drawString("1.0",(int)theFunction.XSCALE + 44,theFunction.yRealToPixel(0) + 14);
		g.drawLine((int)theFunction.XSCALE + 40,theFunction.yRealToPixel(0) - 2,(int)theFunction.XSCALE + 40,theFunction.yRealToPixel(0) + 2);
		
		// y limit
		theFunction.xvar.set_value(.5);
		g.drawString(String.valueOf(theFunction.expr.value() - ((1E2 * theFunction.expr.value()) %1) / 1E2),5,theFunction.yRealToPixel(theFunction.expr.value()) + 3);
		g.drawLine(38,theFunction.yRealToPixel(theFunction.expr.value()),42,theFunction.yRealToPixel(theFunction.expr.value()));
		
  	}

 	
 	// update the dx width graphic
    public void drawdxWidth(Graphics g)
    {
		g.setColor(Color.lightGray);
		g.fillRect(dxWidth.x,dxWidth.y,88,dxWidth.height);
		
		g.setColor(Color.darkGray);
		g.fill3DRect(dxWidth.x,dxWidth.y,dxWidth.width,dxWidth.height,true);

		g.setColor(Color.black);
		g.drawRect(dxWidth.x,dxWidth.y,88,dxWidth.height);
    }
    
	
	// Return info for an about box browser option
	public String getAppletInfo()
	{
		return "Riemann Sum Applet by Bill Ziemer & Brendon Cheves";
	}


	// Called from JavaScript only
	public void drawFunction(String f)
	{
		theFunction.parse(f);
		realArea = theFunction.area(0.0,1.0,error);
		paint(theDrawingPanel.getGraphics());
	}


	// Called from JavaScript only
	public void changeTops(String t)
	{
		theTopStyle = t;
		paint(theDrawingPanel.getGraphics());
	}
}



