/*
 * @(#)Transform1D.java    1.0  18 February 2001
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import java.awt.*;
import java.awt.geom.*;
import java.io.Serializable;

/**
 * <P>A 1-dimensional transform class for simple affine scaling 
 * and for working with the components 
 * of a rectilinear 2-dimensional affine transform.</P>
 *
 * <P>The transform has the form 
 * <I>z</I> --> <I>factor</I> * <I>z</I> + <I>offset</I>.</P>
 *
 * @author  Richard Rasala
 * @author  Jeff Raab
 * @version 2.2
 * @since   1.0
 * @see AffineTransform
 */
public class Transform1D 
    implements Cloneable, Serializable 
{
    /** The scale factor for this transform. */
    protected double factor = 1.0;
    
    /** The offset for this transform. */
    protected double offset = 0.0;
    
    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * Constructs a transform object 
     * representing the identity transform.
     * 
     * @see #Transform1D(double, double)
     * @see #Transform1D(double, double, double, double)
     */
    public Transform1D() {}
    
    /**
     * Constructs a transform object
     * with the given scale factor and offset.
     *
     * @param f the scale factor for this transform
     * @param o the offset for this transform
     * @see #Transform1D()
     * @see #Transform1D(double, double, double, double)
     * @see #setTransform(double, double)
     */
    public Transform1D(double f, double o) {
        setTransform(f, o);
    }
    
    /**
     * Constructs a transform object 
     * mapping the interval [<I>a1</I>, <I>a2</I>]
     * to the interval [<I>b1</I>, <I>b2</I>].
     *
     * @param a1 the first endpoint of the first interval
     * @param a2 the second endpoint of the first interval
     * @param b1 the first endpoint of the second interval
     * @param b2 the second endpoint of the second interval
     * @see #Transform1D()
     * @see #Transform1D(double, double)
     * @see #setTransform(double, double, double, double)
     */
    public Transform1D(double a1, double a2, double b1, double b2) {
        setTransform(a1, a2, b1, b2);
    }
    
    ////////////////
    // Public API //
    ////////////////
    
    /**
     * Sets the scale factor and offset for this transform
     * to the given values.
     *
     * @param f the new factor
     * @param o the new offset
     * @see #Transform1D(double, double)
     * @see #setTransform(double, double, double, double)
     * @see #setTransform(Transform1D)
     */
    public void setTransform(double f, double o) {
        factor = f;
        offset = o;
    }

    /**
     * Sets the scale factor and offset for this transform
     * so that it maps the interval [<I>a1</I>, <I>a2</I>]
     * to the interval [<I>b1</I>, <I>b2</I>].
     *
     * @param a1 the new first endpoint of the first interval
     * @param a2 the new second endpoint of the first interval
     * @param b1 the new first endpoint of the second interval
     * @param b2 the new second endpoint of the second interval
     * @see #Transform1D(double, double, double, double)
     * @see #setTransform(double, double)
     * @see #setTransform(Transform1D)
     */
    public void setTransform(
        double a1, 
        double a2, 
        double b1, 
        double b2) 
    {
        // compute midpoints
        double ma = (a1 + a2) / 2.0;
        double mb = (b1 + b2) / 2.0;
        
        // compute differences
        double da = a2 - a1;
        double db = b2 - b1;
        
        // compute factor
        if (da != 0.0)
            factor = db / da;
        else
            factor = 0.0;
        
        // compute offset to send midpoint ma to midpoint mb
        offset = mb - factor * ma;
    }
    
    /**
     * Sets the scale factor and offset for this transform
     * to the scale factor and offset of the given transform.
     *
     * @see #setTransform(double, double)
     * @see #setTransform(double, double, double, double)
     */
    public void setTransform(Transform1D other) {
        factor = other.factor;
        offset = other.offset;
    };
     
    /**
     * Sets the scale factor for this transform
     * to the given value.
     *
     * @param f the new factor
     * @see #setTransform(double, double)
     * @see #getFactor()
     */
    public void setFactor(double f) {
        factor = f;
    }
    
    /**
     * Returns the scale factor for this transform.
     *
     * @see #setFactor(double)
     * @see #getOffset()
     */
    public double getFactor() { 
        return factor; 
    }
    
    /**
     * Sets the offset for this transform
     * to the given value.
     *
     * @param o the new offset
     * @see #setTransform(double, double)
     * @see #getOffset()
     */
    public void setOffset(double o) {
        offset = o;
    }
    
    /**
     * Return the offset for this transform.
     *
     * @see #setOffset(double)
     * @see #getFactor()
     */
    public double getOffset() { 
        return offset; 
    }
    
    /**
     * Returns the result of applying the transform
     * to the given value.
     *
     * The operation performed is 
     * <I>z</I> --> <I>factor</I> * <I>z</I> + <I>offset</I>.
     *
     * @see #inverseTransform(double)
     */
    public double transform(double z) {
        return factor * z + offset;
    }
    
    /**
     * Returns <CODE>true</CODE> if an inverse transform 
     * exists for this transform, and <CODE>false</CODE>
     * if an inverse transform does not exist.
     *
     * @see #inverseTransform(double)
     * @see #createInverse()
     */
    public boolean hasInverse() {
        return factor != 0.0;
    }
    
    /**
     * Returns the result of applying the inverse of this transform
     * to the given value.
     *
     * @see #hasInverse()
     * @see #createInverse()
     * @throws NoninvertibleTransformException
     *      if this transform cannot be inverted
     */
    public double inverseTransform(double w)
        throws NoninvertibleTransformException
    {
        if (hasInverse())
            return (w - offset) / factor;
        else
            throw new NoninvertibleTransformException
                ("Transform1D has no inverse");
    }
    
    /**
     * Returns a transform representing 
     * the inverse of this transform.
     *
     * @see #hasInverse()
     * @see #inverseTransform(double)
     * @throws NoninvertibleTransformException
     *      if this transform cannot be inverted
     */
    public Transform1D createInverse()
        throws NoninvertibleTransformException
    {
        if (hasInverse())
            return new Transform1D(1.0/factor, - offset/factor);
        else
            throw new NoninvertibleTransformException
                ("Transform1D has no inverse");
    }   
}

