To define a new function that can be displayed on a plot in Java Analysis Studio you must define a class that extends one of the following classes:

jas.hist.Basic1DFunction
For a simple non-fittable function.
jas.hist.Fittable1DFunction
If the function is to be fittable.

In addition your class must implement jas.hist.HasHandles if you want the user to be able to manipulate the function by dragging handles in the histogram window.  Your class must implement the jas.hist.FunctionAdvancedOptions interface if you wish to have an advanced options dialog box available.  This dialog is suitable if your function has options that the user could not otherwise control.  For example, the order of the polynomial function is set by default to 2, but that can be changed in the advanced dialog for jas.hist.test.PolynomoialFunction to any other valid integer.

[Extending jas.hist.Basic1DFunction][Extending jas.hist.Fittable1DFunction][Implementing jas.hist.HasHandles][Implementing jas.hist.FunctionAdvancedOptions]

Extending jas.hist.Basic1DFunction

Instance variables:

You will need to define a series of parameters that define your function.  For example, the class jas.hist.test.PolynomialFunction has the following instance variables:

protected double[] p; // coefficients
protected int order;

Constructors:

There are two constructors in most classes.

  1. The first constructor is called when the function is first added to the graph.  It is public because it is called by the jas.swingstudio package.  It has four arguments which describe the boundaries of the graph that the function will be placed on.  In this constructor, your code should define a function that fits nicely in the given region by assigning parameters to the function calculated from the boundaries of the region it will appear in.  For example, the class jas.hist.test.GaussianFunction uses:
    public GaussianFunction(double xmin, double xmax, double ymin, double ymax)
    {
        max = ymin + (ymax - ymin) * 0.8;
        mean = (xmin + xmax) / 2.0;
        sigma = (xmax - xmin) / 6.0;
    }
    The curve's amplitude will be 80% of the region's height, the mean will be exactly in the middle, and standard deviation will be appropriate to the region's boundaries.
  2. The second constructor takes arguments that correspond to the function's parameters.   For example, in the class jas.hist.test.PolynomialFunction, the constructor is:
    PolynomialFunction(double[] coefficients)
    {
       order = coefficients.length - 1;
       p = coefficients;
    }

    In the class jas.hist.test.GaussianFunction, the constructor is:
    GaussianFunction(double amplitude, double mean, double sigma)
    {
       this.max = amplitude;
       this.mean = mean;
       this.sigma = sigma;
    }

Methods to override:


public double valueAt(double x) throws FunctionValueUndefined

This method simply returns f(x).  Omit the throws clause if the function is defined for all values of x.

public String[] getParameterNames()

This method returns names of all the parameters.  The class jas.hist.test.PolynomialFunction returns the array {"A", "B", "C", ...}, and the class jas.hist.test.StraightLineFunction simply returns {"slope", "yoffset"}.

public double[] getParameterValues()

Be sure to return the parameters in the same order as you returned their names in the above method.

public void setParameter(int index, double value) throws InvalidFunctionParameter

Note that indexes begin with zero.  Once the parameters have been changed, clearFit() should be called if the curve is fittable to indicate that the parameters no longer reflect a fit curve, and setChanged() chould be called to indicate that the curve has changed.  In the class jas.hist.test.GaussianFunction, the code is:

public void setParameter(int index, double value) throws InvalidFunctionParameter
{
    if      (index == 0) max   = value;
    else if (index == 1) mean  = value;
    else if (index == 2) sigma = value;
    else throw new IllegalArgumentException("Invalid index to setParameter()");

    clearFit();   /* the function is no longer considered
                   * to have been fit (use only for
                   * fittable functions that extend

                   * jas.hist.Fittable1DFunction
                   */
    setChanged(); /* update the image on the screen
                   * and indicate that the function
                   * has changed
                   */
}

public String getTitle()

Return a suitable descriptor.  For example, the class jas.hist.test.PolynomialFunction has:

return "Polynomial (order "+ order +")";

 

Extending jas.hist.Fittable1DFunction

Follow the procedure for extending jas.hist.Basic1DFunction, but overwrite the following additional methods:

public double valueAt(double x, double[] param) throws FunctionValueUndefined

This method returns the value at the function defined with the parameters specified.   For example, the class jas.hist.test.GaussianFunction has the following implementation:
public double valueAt(double x, double[] a)
{
    return a[0] * Math.exp( -Math.pow( (x - a[1]) / a[2], 2 ) / 2.0 );
}
Where a[0] is the amplitude, a[1] is the mean, and a[2] is the standard deviation.

abstract public void setFit(Fitter fit, double[] param) throws InvalidFunctionParameter

This method sets the parameters according to a fit.  This method must invoke the setFit(fit) method to set the fitter, plus the setChanged() method.  In jas.hist.test.PolynomialFunction, the implementation is:
public void setFit(Fitter fit, double[] value) throws InvalidFunctionParameter
{
    if (value.length != p.length)
        throw new IllegalArgumentException
              ("Argument to set parameters is of wrong length");

    p = value; /* adopt new coefficients */

    setFit(fit);
    setChanged(); /* indicate that the curve has changed */
}

 

Implementing jas.hist.HasHandles

The following method must be implemented:

public abstract Handle[] getHandles(double xlow, double xhigh, double ylow, double yhigh)

The parameters (xlow, xhigh, ...) hold the bounds to the on-screen plot.  They can be used in assigning positions to the handles.  In the method getHandles(), create and return an array of handles that will be used to alter the curve.  jas.hist.Handle is an abstract class with three abstract methods that you must implement:

public double getX()
public double getY()

These methods return the location of the handle.  You may wish to use the method's parameters (xlow, xhigh, ...) in assigning locations to parameters.

protected void moveTo(double x, double y)

This method is called when the handle has been moved.  Use the given x and y values to modify the parameters of the function.

Here is the annotated implementation of getHandles() from jas.hist.test.GaussianFunction:

public Handle[] getHandles(double xLow, double xHigh, double yLow, double yHigh)
{
    Handle[] result = new Handle[3];
   
/* there will be 3 handles */
    result[0] = new Handle()
   
/* an anonymous class */
    {
        
/* this handle is at the mean of the distribution */
        public void moveTo(double x, double y)
                     /* when this handle has been moved to the x and y
                      * values supplied as parameters, the parameters
                      * of the Gaussian function are modified accordingly. */
        {
            mean = x;
            max = y;
            clearFit();
           
/* the function is no longer considered to have been fit */
            setChanged();
/* register that the function has been changed */
        }
        public double getX()
        {
            return mean;
        }
        public double getY()
        {
            return max;
        }
    };
    result[1] = new Handle()
    {
             /* This handle is at the left of the mean
             * at half of the curve's amplitude */
        public void moveTo(double x, double y)
        {
            sigma = (mean - x) / fwhm;
            clearFit();
            setChanged();
        }
        public double getY()
        {
            return max / 2.0;
        }
        public double getX()
        {
            return mean - sigma*fwhm;
        }
    };
    result[2] = new Handle()
    {
            /* This handle is at the right of the mean
               * at half of the curve's amplitude */
        public void moveTo(double x, double y)
        {
            sigma = (x - mean) / fwhm;
            clearFit();
            setChanged();
        }
        public double getY()
        {
            return max / 2.0;
        }
        public double getX()
        {
            return mean + sigma*fwhm;
        }
    };
    return result;
}

Implementing jas.hist.FunctionAdvancedOptions

    You will want to implement this interface if your function has specific options which the user cannot change any other way.  The interface jas.hist.FunctionAdvancedOptions contains only one method:
    public void openAdvancedDialog(Frame f, JASHist hist)
    This method is called when the user clicks on the 'Advanced Options' button or selects 'Advanced Options' from one of the menus.  It passes as an argument a reference to the java.awt.Frame object that you should use as the parent frame for the dialog box.  It also receives a reference to the current plot. In many cases, you will not need this.
    In most cases, your method implementation will create a new instance of a private internal class.  For example, the order of the polynomial functions can be changed from its default (2) in the dialog generated by the class jas.hist.test.PolynomialFunction$PolynomialFunctionAdvancedDialog, and the functions to include in a sum of functions can be set in the dialog from the class jas.hist.test.SumOfFunctions$SumOfFunctionsAdvancedDialog.  The implementation of this method in jas.hist.test.SumOfFunctions$SumOfFunctionsFunctionAdvancedDialog is:
public void openAdvancedDialog(Frame f, JASHist hist)
{
    new SumOfFunctionsAdvancedDialog(f);
}


Page maintained by Jonas Gifford.   Last updated 01/14/04.