//[skeleton1/
package visual.statik;

import java.awt.geom.*;


/**
 * A partial implementation of the TransformableContent
 * interface that keeps track of transformation information
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class AbstractTransformableContent 
                implements TransformableContent
{

    protected boolean          relocated, rerotated, rescaled;
    protected double           angle;
    protected double           xScale, yScale;
    protected double           x, y;    
    protected double           xRotation, yRotation;    


    /**
     * Default Constructor
     */
    public AbstractTransformableContent()
    {
       setTransformationRequired(false);
       
       angle     = 0.0;
       xScale    = 1.0;
       yScale    = 1.0;
       x         = 0.0;
       y         = 0.0;
       xRotation = 0.0;
       yRotation = 0.0;
    }
//]skeleton1.
//[getAffineTransform.

    /**
     * Get the (concatenated) AffineTransform
     *
     * @return  The rotation, scaling and translation concatenated
     */
    protected AffineTransform getAffineTransform()
    {
       // We could use an object pool rather than local 
       // variables.  Rough tests estimate that this method 
       // would require 1/3 as much time with an object pool.
       AffineTransform     at, rotation, scaling, translation;
       Rectangle2D         bounds;

       // Start with the identity transform
       at = AffineTransform.getTranslateInstance(0.0, 0.0);

       if (rerotated)
       {
          bounds = getBounds2D(false);
          
          
          rotation = AffineTransform.getRotateInstance(angle, 
                                                   xRotation,
                                                   yRotation);
          at.preConcatenate(rotation);
       }

       
       if (rescaled)
       {          
          scaling = AffineTransform.getScaleInstance(xScale, 
                                                     yScale);
          at.preConcatenate(scaling);
       }
       

       if (relocated)
       {
          translation = AffineTransform.getTranslateInstance(x, 
                                                             y);
          at.preConcatenate(translation);
       }

       return at;
    }
//]getAffineTransform.
    
//[skeleton2.    
    /**
    * Returns a high precision bounding box of the appropriately
    * transformed content (required by TransformedContent) 
    *
    * @return   The bounding box
    */
    public Rectangle2D getBounds2D()
    {
       return getBounds2D(true);       
    }
    

   
    /**
     * Returns a high precision bounding box of the Content
     * either before or after it is transformed
     *
     * @param    transformed    true to get the BB of the transformed content
     * @return   The bounding box
     */
    public abstract Rectangle2D getBounds2D(boolean transformed);
       

    /**
     * Set the translation
     * (required by TransformedContent)
     *
     * @param x   The x posiiton
     * @param y   The y position
     */
    public void setLocation(double x, double y)
    {
       this.x = x;
       this.y = y;
       relocated = true;       
    }


    /**
     * Set the angle of rotation around the midpoint
     * (required by TransformedContent)
     *
     * @param angle   The rotation angle
     * @param x       The x-coordinate of the point to rotate around
     * @param y       The y-coordinate of the point to rotate around
     */
    public void setRotation(double angle, double x, double y)
    {
       this.angle = angle;
       xRotation  = x;
       yRotation  = y;       
       rerotated  = true;       
    }
    

    /**
     * Set the scaling
     * (required by TransformedContent)
     *
     * @param xScale   The scaling in the horizontal dimension
     * @param yScale   The scaling in the vertical  dimension
     */
    public void setScale(double xScale, double yScale)
    {
       this.xScale = xScale;
       this.yScale = yScale;
       rescaled    = true;       
    }
    

    /**
     * Set the scaling
     *
     * @param scale   The scaling (in both dimensions)
     */
    public void setScale(double scale)
    {
       setScale(scale, scale);
    }


    /**
     * Set whether this AbstractTransformableContent object
     * needs to be transformed before being used
     *
     * @param required   true to indicate that a transformation is requried
     */
    protected void setTransformationRequired(boolean required)
    {
       relocated = required;
       rerotated = required;
       rescaled  = required;
    }
    

    /**
     * Does this AbstractTransformableContent object need
     * to be transformed before being used?
     *
     * @return  true to indicate a transformation is required
     */
    protected boolean isTransformationRequired()
    {
       return (relocated || rerotated || rescaled);
    }
    
}
//]skeleton2.
