FileDocCategorySizeDatePackage
Transform.javaAPI DocphoneME MR2 API (J2ME)12937Wed May 02 18:00:34 BST 2007com.sun.perseus.j2d

Transform.java

/*
 *
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
package com.sun.perseus.j2d;

import org.w3c.dom.DOMException;
import org.w3c.dom.svg.SVGException;
import org.w3c.dom.svg.SVGMatrix;

import com.sun.perseus.platform.MathSupport;

/**
 * Class for 2D transforms.
 *
 * @version $Id: Transform.java,v 1.8 2006/04/21 06:35:16 st125089 Exp $
 */
public class Transform implements SVGMatrix {
    /**
     * The (0, 0) component of this 3x3 transformation matrix.
     */
    float m0;

    /**
     * The (1, 0) component of this 3x3 transformation matrix.
     */
    float m1;

    /**
     * The (0, 1) component of this 3x3 transformation matrix.
     */
    float m2;

    /**
     * The (1, 1) component of this 3x3 transformation matrix.
     */
    float m3;

    /**
     * The (0, 2) component of this 3x3 transformation matrix.
     */
    float m4;

    /**
     * The (1, 2) component of this 3x3 transformation matrix.
     */
    float m5;

    /**
     * Constructs an <code>Transform</code> with a given set of
     * components, representing the transform:
     *
     * <pre>
     * x' = m0*x + m2*y + m4
     * y' = m1*x + m3*y + m5
     * </pre>
     *
     * or, representing the matrix:
     *
     * <pre>
     *    [ m0 m2 m4 ]
     *    [ m1 m3 m5 ]
     *    [ 0  0  1  ]
     * </pre>
     *
     * @param m0 (0, 0) matrix value
     * @param m1 (1, 0) matrix value
     * @param m2 (0, 1) matrix value
     * @param m3 (1, 1) matrix value
     * @param m4 (0, 2) matrix value
     * @param m5 (1, 2) matrix value
     */
    public Transform(final float m0, final float m1, final float m2,
                     final float m3, final float m4, final float m5) {
        setTransform(m0, m1, m2, m3, m4, m5);
    }

    /**
     * Sets the transform to the matrix defined by the input parameters:
     * 
     * <pre>
     *    [ m0 m2 m4 ]
     *    [ m1 m3 m5 ]
     *    [ 0  0  1  ]
     * </pre>
     *
     * @param m0 (0, 0) matrix value
     * @param m1 (1, 0) matrix value
     * @param m2 (0, 1) matrix value
     * @param m3 (1, 1) matrix value
     * @param m4 (0, 2) matrix value
     * @param m5 (1, 2) matrix value
     */
    public void setTransform(final float m0, final float m1, final float m2,
                             final float m3, final float m4, final float m5) {
        this.m0 = m0;
        this.m1 = m1;
        this.m2 = m2;
        this.m3 = m3;
        this.m4 = m4;
        this.m5 = m5;
    }

    /**
     * Sets the transform to the value defined by the input matrix. 
     *
     * @param transform the transform whose value should be copied. If null,
     * this transform is set to identity.
     */
    public void setTransform(final SVGMatrix transform) {
        Transform txf = (Transform) transform;

        if (txf == null) {
            m0 = 1;  // create IDENTITY transformation
            m1 = 0;
            m2 = 0;
            m3 = 1;
            m4 = 0;
            m5 = 0;
            return;
        }

        this.m0 = txf.m0;
        this.m1 = txf.m1;
        this.m2 = txf.m2;
        this.m3 = txf.m3;
        this.m4 = txf.m4;
        this.m5 = txf.m5;
    }

    /**
     * Transforms a point's coordinates.
     *
     * @param pt the point to transform
     * @param opt the transformed point coordinates.
     */
    public void transformPoint(final float[] pt,
                               final float[] opt) {
        opt[0] = pt[0] * m0 + pt[1] * m2 + m4;
        opt[1] = pt[0] * m1 + pt[1] * m3 + m5;
    }

    /**
     * Constructs an <code>Transform</code> representing the same
     * transform as the given <code>Transform</code>.  Future changes
     * to the object supplied as the <code>transform</code> parameter
     * will not affect the newly constructed <code>Transform</code>.
     * If <code>transform</code> is null, the matrix is initialized
     * to be the identity matrix.
     *
     * @param transform defines the initial
     *        state for the newly constructed <code>Transform</code>.
     */
    public Transform(final SVGMatrix transform) {
        setTransform(transform);
    }

    /**
     * The components of the matrix denoted a to f in the API are :
     *
     *    [ a c e ]
     *    [ b d f ]
     *    [ 0 0 1 ]
     *
     * @param index component index for this matrix
     * @return the component of the matrix corresponding to the index.
     * @throws DOMException  - INDEX_SIZE_ERR if the <code>index</code> is 
     * invalid.
     */
    public float getComponent(final int index) throws DOMException {
        switch (index) {
          case 0:
              return m0;
          case 1:
              return m1;
          case 2:
              return m2;
          case 3:
              return m3;
          case 4:
              return m4;
          case 5:
              return m5;
          default:
              throw new DOMException
                  (DOMException.INDEX_SIZE_ERR, 
                   Messages.formatMessage
                   (Messages.ERROR_OUT_OF_BOUND_PARAMETER_VALUE,
                    new String[] {"SVGMatrix",
                                  "getComponent",
                                  "index",
                                  "" + index}));
        }
    }

    /**
     *
     */
    public SVGMatrix mMultiply(final SVGMatrix secondMatrix) 
                 throws NullPointerException {
        if (secondMatrix == null) {
            throw new NullPointerException();
        }

        Transform sm = (Transform) secondMatrix;

        float t0 = sm.m0;
        float t1 = sm.m1;
        float t2 = sm.m2;
        float t3 = sm.m3;
        float t4 = sm.m4;
        float t5 = sm.m5;

        float mM0 = m0;
        float mM1 = m2;
        m0  = t0 * mM0 + t1 * mM1;
        m2  = t2 * mM0 + t3 * mM1;
        m4 += t4 * mM0 + t5 * mM1;

        mM0 = m1;
        mM1 = m3;
        m1  = t0 * mM0 + t1 * mM1;
        m3  = t2 * mM0 + t3 * mM1;
        m5 += t4 * mM0 + t5 * mM1;

        return this;
    }

    /**
     * Returns the inverse matrix.
     *
     * @return the inverted matrix.
     * @throws SVGException  - SVG_MATRIX_NOT_INVERTABLE when determinant 
     * of this matrix is zero.
     */
    public SVGMatrix inverse() throws SVGException {
        Transform svm = new Transform(1, 0, 0, 1, 0, 0);
        inverse(svm);
        return svm;
    }

    /**
     * Returns true if the matrix is invertible.
     *
     * @return true if the matrix is invertible.
     */
    public boolean isInvertible() {
        return (m0 * m3 - m1 * m2) != 0;
    }

    /**
     * Inverses this transform and stores the resulting transform into 
     * the input transform.
     *
     * @param txf the SVGMatrix into which the result should be stored.
     * @throws SVGException  - SVG_MATRIX_NOT_INVERTABLE when determinant 
     * of this matrix is zero.
     */
    public SVGMatrix inverse(SVGMatrix txf) throws SVGException {
        Transform svm = (Transform) txf;

        float det = m0 * m3 - m2 * m1;
        if (MathSupport.abs(det) <= Float.MIN_VALUE) {
            throw new SVGException(SVGException.SVG_MATRIX_NOT_INVERTABLE, 
                                   this.toString());
        }

        if (svm == null) {
            svm = new Transform(1, 0, 0, 1, 0, 0);
        }
        
        if (isIdentity()) {
            svm.setTransform(1, 0, 0, 1, 0, 0);
            return svm;
        }

        svm.m0 = m3 / det;
        svm.m2 = -m2 / det;
        svm.m4 = (m2 * m5 - m3 * m4) / det;
        svm.m1 = -m1 / det;
        svm.m3 = m0 / det;
        svm.m5 = (m1 * m4 - m0 * m5) / det;

        return svm;
    }


    /**
     *
     */
    public SVGMatrix mTranslate(final float x, final float y) {
        m4 = x * m0 + y * m2 + m4;
        m5 = x * m1 + y * m3 + m5;

        return this;
    }

    /**
     *
     */
    public SVGMatrix mScale(final float scaleFactor) {
        m0 *= scaleFactor;
        m2 *= scaleFactor;
        m1 *= scaleFactor;
        m3 *= scaleFactor;
        return this;
    }

    /**
     * Post-multiplies a scale transformation on the current matrix and
     * returns the resulting matrix.  
     * <p>
     * <pre>
     *                [   scaleFactorX      0          0   ]
     *                [   0          scaleFactorY      0   ]
     *                [   0                 0          1   ]
     * </pre>
     * </p>
     *
     * @param scaleFactorX the factor by which coordinates are scaled along the
     *                    X axis direction.
     * @param scaleFactorY the factor by which coordinates are scaled along the
     *                    Y axis direction.
     * @return this matrix scaled by the scaleFactor.
     */
    public SVGMatrix mScale(final float scaleFactorX, 
                            final float scaleFactorY) {
        m0 *= scaleFactorX;
        m2 *= scaleFactorY;
        m1 *= scaleFactorX;
        m3 *= scaleFactorY;
        return this;
    }

    /**
     *
     */
    public SVGMatrix mRotate(final float angle) {
        final float angl = MathSupport.toRadians(angle);
        final float trigTOLERANCE = 1E-7f;
        final float sin = MathSupport.sin(angl);
        final float cos = MathSupport.cos(angl);

        if (MathSupport.abs(sin) < trigTOLERANCE) {  // angle = 0 or 180 deg.
            if (cos < 0f) {         // angle = 180 deg.
                m0 = -m0;
                m3 = -m3;
                m2 = -m2;
                m1 = -m1;
            }
            return this;
        }

        if (MathSupport.abs(cos) < trigTOLERANCE) {  // angle = 90 or 270 deg.
            if (sin < 0f) {         // angle = 270 deg
                float mM0 = m0;
                m0  = -m2;
                m2  = mM0;
                mM0 = m1;
                m1  = -m3;
                m3  = mM0;
            } else {                 // angle = 90 deg
                float mM0 = m0;
                m0  = m2;
                m2  = -mM0;
                mM0 = m1;
                m1  = m3;
                m3  = -mM0;
           }
           return this;
        }

        float mM0, mM1;
        mM0 = m0;
        mM1 = m2;
        m0 =  cos * mM0 + sin * mM1;
        m2 = -sin * mM0 + cos * mM1;
        mM0 = m1;
        mM1 = m3;
        m1 =  cos * mM0 + sin * mM1;
        m3 = -sin * mM0 + cos * mM1;

        return this;
    }

    /**
     * Returns <code>true</code> if the current <code>SVGMatrix</code>
     * is equivalent to the identity matrix, <code>false</code>
     * otherwise.
     *
     * @return true if this matrix is identity, false otherwise.
     */
    public boolean isIdentity() {
        return (m0 == 1f 
                &&
                m2 == 0f 
                &&
                m4 == 0f 
                &&
                m1 == 0f 
                &&
                m3 == 1f 
                &&
                m5 == 0f);
    }

    /**
     * @return true if obj is a Transform and all its components are the 
     *         same as this instance.
     */
    public boolean equals(final Object obj) {
        if (obj == null || !(obj instanceof Transform)) {
            return false;
        }

        if (obj == this) {
            return true;
        }

        Transform t = (Transform) obj;
        return t.m0 == m0
            &&
            t.m1 == m1
            &&
            t.m2 == m2
            &&
            t.m3 == m3
            &&
            t.m4 == m4
            &&
            t.m5 == m5;
    }

    /**
     * @param a 6x1 float array containing the transform matrix.
     * @return true if this transform is equal to the input matrix.
     */
    public boolean equals(final float[][] m) {
        return m[0][0] == m0
            &&
            m[1][0] == m1
            &&
            m[2][0] == m2
            &&
            m[3][0] == m3
            &&
            m[4][0] == m4
            &&
            m[5][0] == m5;
    }
}