/* @(#)FunctionsAndPlots.java   13 September 2006 */

/* Useful imports */

import edu.neu.ccs.*;
import edu.neu.ccs.gui.*;
import edu.neu.ccs.codec.*;
import edu.neu.ccs.console.*;
import edu.neu.ccs.filter.*;
import edu.neu.ccs.jpf.*;
import edu.neu.ccs.parser.*;
import edu.neu.ccs.pedagogy.*;
import edu.neu.ccs.quick.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import java.util.*;
import java.math.*;
import java.beans.*;
import java.lang.reflect.*;
import java.net.URL;
import java.util.regex.*;
import java.text.ParseException;

/**
 * Class FunctionsAndPlots collects test methods that
 * evaluate expressions, define functions, and plot
 * functions.
 */
public class FunctionsAndPlots extends JPF 
{
    
    public static void main(String[] args) { 
        // LookAndFeelTools.showSelectLookAndFeelDialog();
        LookAndFeelTools.adjustAllDefaultFontSizes(4);
        
        new FunctionsAndPlots();
    }
    
    
    /**
     * The parser that will be used to evaluate functions
     * for the purpose of making plots.
     */
    BaseParser parser = ParserUtilities.getDefaultParser();
    
    
    // A series of sample function objects to illustrate
    // how to define SimpleFunction objects in code
     
    SimpleFunction square =
        new SimpleFunction
            ("square", "x", "x^2");
    
    
    SimpleFunction cube =
        new SimpleFunction
            ("cube", "x", "x^3");
    
    
    SimpleFunction cuberoot =
        new SimpleFunction
            ("cuberoot", "x", "root(x,3)");
    
    
    SimpleFunction fourth =
        new SimpleFunction
            ("fourth", "x", "x^4");
    
    
    SimpleFunction fourthroot =
        new SimpleFunction
            ("fourthroot", "x", "root(x,4)");
    
    
    /**
     * This method will lead to a GUI button that opens up a panel
     * in which the double input x may be entered as an expression
     * that is then evaluated and returned.
     * 
     * This is the easiest way to take advantage of the built-in
     * JPT expression parser.
     */
    public double Evaluate(double x) {
        return x;
    }
    
    
    /**
     * This method will permit the user to evaluate one or more
     * double expressions in the console window.
     * 
     * The user can stop the process by pressing return after the
     * prompt message.
     */
    public void EvaluateInConsole() {
        while (true) {
            try {
                console.out.println
                    (console.in.requestDouble("Enter Double"));
                
                console.out.println();
            }
            catch (CancelledException ce) {
                break;
            }
        }
        
        console.out.println();
    }
    
    
    /**
     * This method brings up the JPT expression evaluation pane
     * that permits evaluation of expressions to type BigInteger,
     * double, or boolean.
     */
    public void ExpressionEvaluationPane() {
        ExpressionEvaluationPane.main(null);
    }
    
    
    /**
     * This method brings up the JPT pane that manages the
     * definition of SimpleFunction objects.
     *
     * A SimpleFunction is defined by 3 strings: its name,
     * its comma-separated list of parameters, and its body.
     */
    public void SimpleFunctionPane() {
        SimpleFunctionPane.main(null);
    }
    
    
    /**
     * This method brings up the JPT pane that manages the
     * definition of SimpleFunction objects and permits the
     * definitions to be saved to disk and read from disk.
     *
     * A SimpleFunction is defined by 3 strings: its name,
     * its comma-separated list of parameters, and its body.
     */
    public void SimpleFunctionPaneWithIO() {
        SimpleFunctionPaneWithIO.main(null);
    }
    
    
    /**
     * The method brings up a pane that combines
     * a simple function definition pane
     * and an expression evaluation pane.
     */
    public void SimpleFunctionBuilder() {
        SimpleFunctionBuilder.main(null);
    }
    
    
    /**
     * The method brings up a pane that combines
     * a simple function definition pane with IO
     * and an expression evaluation pane.
     */
    public void SimpleFunctionBuilderWithIO() {
        SimpleFunctionBuilderWithIO.main(null);
    }
    
    
    /**
     * Plots one or more functions installed in the JPT parser.
     * These functions can be built-in functions or can be
     * user defined SimpleFunction objects.
     * 
     * The functions are provided by giving a comma-separated
     * list of function names.
     * 
     * The plot interval is from xMin to xMax.
     * 
     * The variable steps is the number of subdivisions of the
     * plot interval.  Since endpoints are plotted, there will
     * be (steps+1) plot points for each function.
     */
    public void PlotTest
        (String functionNames, double xMin, double xMax, int steps)
    {
        // Call on the JPT parser to build a 2-dimensional table of
        // values for the functions provided.
        
        // Show an error dialog if an error occurs.
        
        Point2D[][] data = null;
        
        try {
            data = parser.makeTable(functionNames, xMin, xMax, steps);
        }
        catch (ParseException pe) {
            GeneralDialog.showOKDialog
                (pe.getMessage(), "Function Error");
            
            return;
        }
        
        if ((data == null) || (data.length == 0)) {
            GeneralDialog.showOKDialog
                ("No function names", "Function Error");
            
            return;
        }
        
        // Pass on the plot task to a helper method.
        PlotData(data, xMin, xMax, steps);
    }
    
    
    /**
     * Read in a polynomial function object and plot it.
     * 
     * The plot interval is from xMin to xMax.
     * 
     * The variable steps is the number of subdivisions of the
     * plot interval.  Since endpoints are plotted, there will
     * be (steps+1) plot points for the function.
     */
    public void PlotPolynomial
        (XPolynomial fcn, double xMin, double xMax, int steps)
    {
        // Pass on the plot task to a helper method.
        PlotFunction(fcn, xMin, xMax, steps);
    }
    
    
    /**
     * Read in a fourier series function object and plot it.
     * 
     * The plot interval is from xMin to xMax.
     * 
     * The variable steps is the number of subdivisions of the
     * plot interval.  Since endpoints are plotted, there will
     * be (steps+1) plot points for the function.
     */
    public void PlotFourierSeries
        (XFourier fcn, double xMin, double xMax, int steps)
    {
        // Pass on the plot task to a helper method.
        PlotFunction(fcn, xMin, xMax, steps);
    }
    

    /**
     * Plot a function object that implements Function.OneArg.
     * 
     * The plot interval is from xMin to xMax.
     * 
     * The variable steps is the number of subdivisions of the
     * plot interval.  Since endpoints are plotted, there will
     * be (steps+1) plot points for the function.
     */
    public void PlotFunction
        (Function.OneArg fcn, double xMin, double xMax, int steps)
    {
        if (fcn == null)
            return;
        
        // Use the DataTables2D class to make the table of values
        Point2D[] data =
            DataTables2D.makeTable(fcn, xMin, xMax, steps);
        
        // Pass on the plot task to a helper method.
        PlotData(data, xMin, xMax, steps);
    }
    
    
    /**
     * Plot a 1-dimensional data array as a single function.
     * 
     * Scale the plot based on the data values.
     */
    public void PlotData
        (Point2D[] data, double xMin, double xMax, int steps)
    {
        if ((data == null) || (data.length == 0)) {
            GeneralDialog.showOKDialog
                ("No plot data", "Plot Error");
            
            return;
        }
        
        // Prepare the graphics window
        
        window.clearPanelAndSequence();
        
        Graphics2D g = window.getBufferGraphics();
        
        // Image bounds
        
        double w = window.getWidth();
        double h = window.getHeight();
        
        Rectangle2D image = new XRect(0, 0, w, h);
        
        // Use the data to find the world bounds for the plot
        
        PlotTool plot = new PlotTool();
        
        Rectangle2D world = PlotTool.makeBoundsRectangle2D(data);
        world.add(0, 0);
        
        plot.setWorldBounds(world);
        plot.setImageBounds(image);
        plot.setInset(10);
        
        Stroke stroke = new BasicStroke(2);
        
        // Plot
        
        plot.autoTickMarks(g);
        plot.autoGridLines(g);
        plot.autoAxes(g);
        
        plot.plotData(g, data, Colors.red, stroke);
        
        // Repaint the graphics window
        
        window.repaint();
    }
    
    
    /**
     * Plot a 2-dimensional data array as multiple functions.
     * 
     * Scale the plot based on the data values.
     */
    public void PlotData
        (Point2D[][] data, double xMin, double xMax, int steps)
    {
        if ((data == null) || (data.length == 0)) {
            GeneralDialog.showOKDialog
                ("No plot data", "Plot Error");
            
            return;
        }
        
        // Prepare the graphics window
        
        window.clearPanelAndSequence();
        
        Graphics2D g = window.getBufferGraphics();
        
        // Image bounds
        
        double w = window.getWidth();
        double h = window.getHeight();
        
        Rectangle2D image = new XRect(0, 0, w, h);
        
        // Use the data to find the world bounds for the plot
        
        PlotTool plot = new PlotTool();
        
        Rectangle2D world = PlotTool.makeBoundsRectangle2D(data);
        world.add(0, 0);
        
        plot.setWorldBounds(world);
        plot.setImageBounds(image);
        plot.setInset(10);
        
        Stroke stroke = new BasicStroke(2);
        
        // Plot
        
        plot.autoTickMarks(g);
        plot.autoGridLines(g);
        plot.autoAxes(g);
        
        plot.plotData(g, data, Colors.red, stroke);
        
        // Repaint the graphics window
        
        window.repaint();
    }


}
