// Path: src/main/java/pythagoras/util/NoninvertibleTransformException.java
// public class NoninvertibleTransformException extends RuntimeException
// {
//     private static final long serialVersionUID = 5208863644264280750L;
// 
//     public NoninvertibleTransformException (String s) {
//         super(s);
//     }
// }


import pythagoras.util.NoninvertibleTransformException;
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras

package pythagoras.f;


/**
 * Implements an affine (3x2 matrix) transform. The transformation matrix has the form:
 * <pre>{@code
 * [ m00, m10, tx ]
 * [ m01, m11, ty ]
 * [   0,   0,  1 ]
 * }</pre>
 */
public class AffineTransform extends AbstractTransform
{
    /** Identifies the affine transform in {@link #generality}. */
    public static final int GENERALITY = 4;

    /** The scale, rotation and shear components of this transform. */
    public float m00, m01, m10, m11;

    /** The translation components of this transform. */
    public float tx, ty;

    /** Creates an affine transform configured with the identity transform. */
    public AffineTransform () {
        this(1, 0, 0, 1, 0, 0);
    }

    /** Creates an affine transform from the supplied scale, rotation and translation. */
    public AffineTransform (float scale, float angle, float tx, float ty) {
        this(scale, scale, angle, tx, ty);
    }

    /** Creates an affine transform from the supplied scale, rotation and translation. */
    public AffineTransform (float scaleX, float scaleY, float angle, float tx, float ty) {
        float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
        this.m00 =  cosa * scaleX; this.m01 = sina * scaleY;
        this.m10 = -sina * scaleX; this.m11 = cosa * scaleY;
        this.tx  =  tx;            this.ty  = ty;
    }

    /** Creates an affine transform with the specified transform matrix. */
    public AffineTransform (float m00, float m01, float m10, float m11, float tx, float ty) {
        this.m00 = m00; this.m01 = m01;
        this.m10 = m10; this.m11 = m11;
        this.tx  = tx;  this.ty  = ty;
    }

    /** Sets this affine transform matrix to {@code other}.
      * @return this instance, for chaining. */
    public AffineTransform set (AffineTransform other) {
        return setTransform(other.m00, other.m01, other.m10, other.m11, other.tx, other.ty);
    }

    @Override // from Transform
    public float uniformScale () {
        // the square root of the signed area of the parallelogram spanned by the axis vectors
        float cp = m00*m11 - m01*m10;
        return (cp < 0f) ? -FloatMath.sqrt(-cp) : FloatMath.sqrt(cp);
    }

    @Override // from Transform
    public float scaleX () {
        return FloatMath.sqrt(m00*m00 + m01*m01);
    }

    @Override // from Transform
    public float scaleY () {
        return FloatMath.sqrt(m10*m10 + m11*m11);
    }

    @Override // from Transform
    public float rotation () {
        // use the iterative polar decomposition algorithm described by Ken Shoemake:
        // http://www.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf

        // start with the contents of the upper 2x2 portion of the matrix
        float n00 = m00, n10 = m10;
        float n01 = m01, n11 = m11;
        for (int ii = 0; ii < 10; ii++) {
            // store the results of the previous iteration
            float o00 = n00, o10 = n10;
            float o01 = n01, o11 = n11;

            // compute average of the matrix with its inverse transpose
            float det = o00*o11 - o10*o01;
            if (Math.abs(det) == 0f) {
                // determinant is zero; matrix is not invertible
// Path: src/main/java/pythagoras/util/NoninvertibleTransformException.java
// public class NoninvertibleTransformException extends RuntimeException
// {
//     private static final long serialVersionUID = 5208863644264280750L;
// 
//     public NoninvertibleTransformException (String s) {
//         super(s);
//     }
// }


// Path: src/main/java/pythagoras/f/AffineTransform.java
import pythagoras.util.NoninvertibleTransformException;

//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras

package pythagoras.f;


/**
 * Implements an affine (3x2 matrix) transform. The transformation matrix has the form:
 * <pre>{@code
 * [ m00, m10, tx ]
 * [ m01, m11, ty ]
 * [   0,   0,  1 ]
 * }</pre>
 */
public class AffineTransform extends AbstractTransform
{
    /** Identifies the affine transform in {@link #generality}. */
    public static final int GENERALITY = 4;

    /** The scale, rotation and shear components of this transform. */
    public float m00, m01, m10, m11;

    /** The translation components of this transform. */
    public float tx, ty;

    /** Creates an affine transform configured with the identity transform. */
    public AffineTransform () {
        this(1, 0, 0, 1, 0, 0);
    }

    /** Creates an affine transform from the supplied scale, rotation and translation. */
    public AffineTransform (float scale, float angle, float tx, float ty) {
        this(scale, scale, angle, tx, ty);
    }

    /** Creates an affine transform from the supplied scale, rotation and translation. */
    public AffineTransform (float scaleX, float scaleY, float angle, float tx, float ty) {
        float sina = FloatMath.sin(angle), cosa = FloatMath.cos(angle);
        this.m00 =  cosa * scaleX; this.m01 = sina * scaleY;
        this.m10 = -sina * scaleX; this.m11 = cosa * scaleY;
        this.tx  =  tx;            this.ty  = ty;
    }

    /** Creates an affine transform with the specified transform matrix. */
    public AffineTransform (float m00, float m01, float m10, float m11, float tx, float ty) {
        this.m00 = m00; this.m01 = m01;
        this.m10 = m10; this.m11 = m11;
        this.tx  = tx;  this.ty  = ty;
    }

    /** Sets this affine transform matrix to {@code other}.
      * @return this instance, for chaining. */
    public AffineTransform set (AffineTransform other) {
        return setTransform(other.m00, other.m01, other.m10, other.m11, other.tx, other.ty);
    }

    @Override // from Transform
    public float uniformScale () {
        // the square root of the signed area of the parallelogram spanned by the axis vectors
        float cp = m00*m11 - m01*m10;
        return (cp < 0f) ? -FloatMath.sqrt(-cp) : FloatMath.sqrt(cp);
    }

    @Override // from Transform
    public float scaleX () {
        return FloatMath.sqrt(m00*m00 + m01*m01);
    }

    @Override // from Transform
    public float scaleY () {
        return FloatMath.sqrt(m10*m10 + m11*m11);
    }

    @Override // from Transform
    public float rotation () {
        // use the iterative polar decomposition algorithm described by Ken Shoemake:
        // http://www.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf

        // start with the contents of the upper 2x2 portion of the matrix
        float n00 = m00, n10 = m10;
        float n01 = m01, n11 = m11;
        for (int ii = 0; ii < 10; ii++) {
            // store the results of the previous iteration
            float o00 = n00, o10 = n10;
            float o01 = n01, o11 = n11;

            // compute average of the matrix with its inverse transpose
            float det = o00*o11 - o10*o01;
            if (Math.abs(det) == 0f) {
                // determinant is zero; matrix is not invertible
                throw new NoninvertibleTransformException(this.toString());