/*
 *  Copyright (C) 2000, Institute for MicroTherapy
 *
 *  This software and supporting documentation were developed by
 *
 *    University of Witten/Herdecke
 *    Department of Radiology and MicroTherapy
 *    Institute for MicroTherapy
 *    Medical computer science
 *    
 *    Universitaetsstrasse 142
 *    44799 Bochum, Germany
 *    
 *    http://www.microtherapy.de/go/cs
 *    mailto:computer.science@microtherapy.de
 *
 *  THIS SOFTWARE IS MADE AVAILABLE,  AS IS,  AND THE INSTITUTE MAKES  NO 
 *  WARRANTY REGARDING THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY
 *  OR FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES 
 *  OR ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY 
 *  AND PERFORMANCE OF THE SOFTWARE IS WITH THE USER.
 *
 *  $Author: kleber $
 *  $Date: 2001/06/06 10:32:30 $
 *  $Revision: 1.1.1.1 $
 *  $State: Exp $
 */

package viewer.paint;

import java.awt.geom.*;

/**
 * The <code>GeometryTool</code> class is a collection
 * of geometrical utilities. <p>
 *
 * @author Annacker
 * @version $Id: GeometryTool.java,v 1.1.1.1 2001/06/06 10:32:30 kleber Exp $
 */
public final class GeometryTool
{
    /**
     * The constructor was implemented only to prevent
     * the <code>GeometryTool</code> from being instantiated.
     */
    private GeometryTool()
    {
        // intentionally left blank
    }
    
    /**
     * Returns a cubic spline following the given coordinates.
     * The precision is given as ratio of lines to draw between
     * two coordinates to the distance between them. <p>
     *
     * @param coords the coordinates to follow.
     * @param precision the precision/smoothness (see above).
     *
     * @return the path of the spline.
     */
    public static GeneralPath getCubicSpline(Point2D.Double[] coords, double precision)
    {
        // generate resulting path
        GeneralPath path = new GeneralPath();
        
        // do we have enough points for a spline?
        int n = coords.length;
        if(n > 2)
        {
            // yes, get cubic polynomials, separately for x- and y-coordinates
            double[] x = new double[n];
            double[] y = new double[n];
            for(int i = 0; i < n; i++)
            {
                x[i] = coords[i].getX();
                y[i] = coords[i].getY();
            }
            CubicPolynomial[] xPoly = calculateCubicPolynomials(x);
            CubicPolynomial[] yPoly = calculateCubicPolynomials(y);
            
            // interpolate path point by point
            path.moveTo(xPoly[0].calculate(0.0), yPoly[0].calculate(0.0));
            for(int i = 0; i < xPoly.length; i++)
            {
                double step = 1.0 / (coords[0].distance(coords[1]) * precision);
	            for(double j = step; j <= 1.0; j += step)
                {
                    path.lineTo(xPoly[i].calculate(j), yPoly[i].calculate(j));
                }
            }
        }
        else
        {
            // no, can we create a straight path?
            if(n > 1)
            {
                // yes, do it
                path.moveTo((float)coords[0].getX(), (float)coords[0].getY());
                path.lineTo((float)coords[1].getX(), (float)coords[1].getY());
            }
        }

        // return the spline path
        return path;
    }
    
        
    /**
     * Calculates the cubic polynomials that interpolate
     * the given values. There are given as many polynomials
     * as coordinates minus one. <p>
     *
     * @param   y the values to interpolate.
     *
     * @return  the cubic polynomials.
     */
    private static CubicPolynomial[] calculateCubicPolynomials(double[] y)
    {
        // calculate the derivatives at the given points.
        int n = y.length - 1;
        double[] gamma = new double[n + 1];
        double[] delta = new double[n + 1];
        double[] derivative = new double[n + 1];

        gamma[0] = 1.0 / 2.0;
        for(int i = 1; i < n; i++)
        {
            gamma[i] = 1.0 / (4.0 - gamma[i - 1]);
        }
        gamma[n] = 1.0 / (2.0 - gamma[n - 1]);
    
        delta[0] = 3.0 * (y[1] - y[0]) * gamma[0];
        for(int i = 1; i < n; i++)
        {
            delta[i] = (3.0 * (y[i + 1] - y[i - 1]) - delta[i - 1]) * gamma[i];
        }
        delta[n] = (3.0 * (y[n] - y[n - 1]) - delta[n - 1]) * gamma[n];
    
        derivative[n] = delta[n];
        for(int i = n - 1; i >= 0; i--)
        {
            derivative[i] = delta[i] - gamma[i] * derivative[i + 1];
        }

        // calculate the coefficients of the cubic polynomials
        CubicPolynomial[] poly = new CubicPolynomial[n];
        for(int i = 0; i < n; i++)
        {
            poly[i] = new CubicPolynomial(y[i], derivative[i],
                            3.0 * (y[i + 1] - y[i]) - 2.0 * derivative[i] - derivative[i + 1],
		                    2.0 * (y[i] - y[i + 1]) + derivative[i] + derivative[i + 1]);
        }
    
        // return the polynomials
        return poly;
    }

}

/**
 * The <code>CubicPolynomial</code> class only holds and calculates
 * cubic polynomials as a helper for {@link GeometryTool#getCubicSpline}. <p>
 * 
 * @author  Kai Annacker
 * @version $Id: GeometryTool.java,v 1.1.1.1 2001/06/06 10:32:30 kleber Exp $
 */
class CubicPolynomial
{
    /**
     * The constant coefficient.
     */
    private double a;

    /**
     * The linear coefficient.
    */
    private double b;
    
    /**
     * The quadratic coefficient.
     */
    private double c;
    
    /**
     * The cubic coefficient.
     */
    private double d;

    /**
     * Creates a new cubic polynomial with the given coefficients. <p>
     *
     * @param a constant coefficient.
     * @param b linear coefficient.
     * @param c quadratic coefficient.
     * @param d cubic coefficient.
     */
    protected CubicPolynomial(double a,double b,double c,double d)
    {
        // copy the coefficients
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
    }

    /**
     * Calculates the cubic polynomial for the given value. <p>
     *
     * @param   x the value to calculate for.
     *
     * @return the polynomial value.
     */
    protected float calculate(double x)
    {
        return (float)((((d * x) + c) * x + b) * x + a);
    }
}

/*
 *  CVS Log
 *  $Log: GeometryTool.java,v $
 *  Revision 1.1.1.1  2001/06/06 10:32:30  kleber
 *  Init commit for DICOMscope 3.5
 *  Create new CVS
 *
*/
