All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.kernel.geom.AffineTransform Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.kernel.geom;

import java.util.Objects;

public class AffineTransform implements Cloneable {


    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_IDENTITY = 0;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_TRANSLATION = 1;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_UNIFORM_SCALE = 2;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_GENERAL_SCALE = 4;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_QUADRANT_ROTATION = 8;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_GENERAL_ROTATION = 16;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_GENERAL_TRANSFORM = 32;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_FLIP = 64;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE;

    /**
     * The type of affine transformation. See {@link AffineTransform#getType()}.
     */
    public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION;

    /**
     * The TYPE_UNKNOWN is an initial type value.
     */
    static final int TYPE_UNKNOWN = -1;

    /**
     * The min value equivalent to zero. If absolute value less then ZERO it considered as zero.
     */
    static final double ZERO = 1E-10;

    /**
     * The values of transformation matrix
     */
    double m00;
    double m10;
    double m01;
    double m11;
    double m02;
    double m12;

    /**
     * The transformation type
     */
    int type;

    public AffineTransform() {
        type = TYPE_IDENTITY;
        m00 = m11 = 1;
        m10 = m01 = m02 = m12 = 0;
    }

    public AffineTransform(AffineTransform t) {
        this.type = t.type;
        this.m00 = t.m00;
        this.m10 = t.m10;
        this.m01 = t.m01;
        this.m11 = t.m11;
        this.m02 = t.m02;
        this.m12 = t.m12;
    }

    public AffineTransform(double m00, double m10, double m01, double m11, double m02, double m12) {
        this.type = TYPE_UNKNOWN;
        this.m00 = m00;
        this.m10 = m10;
        this.m01 = m01;
        this.m11 = m11;
        this.m02 = m02;
        this.m12 = m12;
    }

    public AffineTransform(float[] matrix) {
        this.type = TYPE_UNKNOWN;
        m00 = matrix[0];
        m10 = matrix[1];
        m01 = matrix[2];
        m11 = matrix[3];
        if (matrix.length > 4) {
            m02 = matrix[4];
            m12 = matrix[5];
        }
    }

    public AffineTransform(double[] matrix) {
        this.type = TYPE_UNKNOWN;
        m00 = matrix[0];
        m10 = matrix[1];
        m01 = matrix[2];
        m11 = matrix[3];
        if (matrix.length > 4) {
            m02 = matrix[4];
            m12 = matrix[5];
        }
    }

    /**
     * Method returns type of affine transformation.
     * 

* Transform matrix is * m00 m01 m02 * m10 m11 m12 *

* According analytic geometry new basis vectors are (m00, m01) and (m10, m11), * translation vector is (m02, m12). Original basis vectors are (1, 0) and (0, 1). * Type transformations classification: *

    *
  • {@link AffineTransform#TYPE_IDENTITY} - new basis equals original one and zero translation *
  • {@link AffineTransform#TYPE_TRANSLATION} - translation vector isn't zero *
  • {@link AffineTransform#TYPE_UNIFORM_SCALE} - vectors length of new basis equals *
  • {@link AffineTransform#TYPE_GENERAL_SCALE} - vectors length of new basis doesn't equal *
  • {@link AffineTransform#TYPE_FLIP} - new basis vector orientation differ from original one *
  • {@link AffineTransform#TYPE_QUADRANT_ROTATION} - new basis is rotated by 90, 180, 270, or 360 degrees *
  • {@link AffineTransform#TYPE_GENERAL_ROTATION} - new basis is rotated by arbitrary angle *
  • {@link AffineTransform#TYPE_GENERAL_TRANSFORM} - transformation can't be inversed *
* * @return the type of this AffineTransform */ public int getType() { if (this.type != TYPE_UNKNOWN) { return this.type; } int type = 0; if (m00 * m01 + m10 * m11 != 0.0) { type |= TYPE_GENERAL_TRANSFORM; return type; } if (m02 != 0.0 || m12 != 0.0) { type |= TYPE_TRANSLATION; } else if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) { type = TYPE_IDENTITY; return type; } if (m00 * m11 - m01 * m10 < 0.0) { type |= TYPE_FLIP; } double dx = m00 * m00 + m10 * m10; double dy = m01 * m01 + m11 * m11; if (dx != dy) { type |= TYPE_GENERAL_SCALE; } else if (dx != 1.0) { type |= TYPE_UNIFORM_SCALE; } if ((m00 == 0.0 && m11 == 0.0) || (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0))) { type |= TYPE_QUADRANT_ROTATION; } else if (m01 != 0.0 || m10 != 0.0) { type |= TYPE_GENERAL_ROTATION; } return type; } public double getScaleX() { return m00; } public double getScaleY() { return m11; } public double getShearX() { return m01; } public double getShearY() { return m10; } public double getTranslateX() { return m02; } public double getTranslateY() { return m12; } public boolean isIdentity() { return getType() == TYPE_IDENTITY; } public void getMatrix(float[] matrix) { matrix[0] = (float) m00; matrix[1] = (float) m10; matrix[2] = (float) m01; matrix[3] = (float) m11; if (matrix.length > 4) { matrix[4] = (float) m02; matrix[5] = (float) m12; } } public void getMatrix(double[] matrix) { matrix[0] = m00; matrix[1] = m10; matrix[2] = m01; matrix[3] = m11; if (matrix.length > 4) { matrix[4] = m02; matrix[5] = m12; } } public double getDeterminant() { return m00 * m11 - m01 * m10; } public void setTransform(float m00, float m10, float m01, float m11, float m02, float m12) { this.type = TYPE_UNKNOWN; this.m00 = m00; this.m10 = m10; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; } public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12) { this.type = TYPE_UNKNOWN; this.m00 = m00; this.m10 = m10; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; } public void setTransform(AffineTransform t) { type = t.type; setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12); } public void setToIdentity() { type = TYPE_IDENTITY; m00 = m11 = 1; m10 = m01 = m02 = m12 = 0; } public void setToTranslation(double mx, double my) { m00 = m11 = 1; m01 = m10 = 0; m02 = mx; m12 = my; if (mx == 0 && my == 0) { type = TYPE_IDENTITY; } else { type = TYPE_TRANSLATION; } } public void setToScale(double scx, double scy) { m00 = scx; m11 = scy; m10 = m01 = m02 = m12 = 0; if (scx != 1.0 || scy != 1) { type = TYPE_UNKNOWN; } else { type = TYPE_IDENTITY; } } public void setToShear(double shx, double shy) { m00 = m11 = 1; m02 = m12 = 0; m01 = shx; m10 = shy; if (shx != 0.0 || shy != 0.0) { type = TYPE_UNKNOWN; } else { type = TYPE_IDENTITY; } } /** * Set this affine transformation to represent a rotation over the passed angle * * @param angle angle to rotate over in radians */ public void setToRotation(double angle) { double sin = Math.sin(angle); double cos = Math.cos(angle); if (Math.abs(cos) < ZERO) { cos = 0.0; sin = sin > 0.0 ? 1.0 : -1.0; } else if (Math.abs(sin) < ZERO) { sin = 0.0; cos = cos > 0.0 ? 1.0 : -1.0; } m00 = m11 = (float) cos; m01 = (float) -sin; m10 = (float) sin; m02 = m12 = 0; type = TYPE_UNKNOWN; } /** * Set this affine transformation to represent a rotation over the passed angle, * using the passed point as the center of rotation * * @param angle angle to rotate over in radians * @param px x-coordinate of center of rotation * @param py y-coordinate of center of rotation */ public void setToRotation(double angle, double px, double py) { setToRotation(angle); m02 = px * (1 - m00) + py * m10; m12 = py * (1 - m00) - px * m10; type = TYPE_UNKNOWN; } public static AffineTransform getTranslateInstance(double mx, double my) { AffineTransform t = new AffineTransform(); t.setToTranslation(mx, my); return t; } public static AffineTransform getScaleInstance(double scx, double scY) { AffineTransform t = new AffineTransform(); t.setToScale(scx, scY); return t; } public static AffineTransform getShearInstance(double shx, double shy) { AffineTransform m = new AffineTransform(); m.setToShear(shx, shy); return m; } /** * Get an affine transformation representing a counter-clockwise rotation over the passed angle * * @param angle angle in radians to rotate over * @return {@link AffineTransform} representing the rotation */ public static AffineTransform getRotateInstance(double angle) { AffineTransform t = new AffineTransform(); t.setToRotation(angle); return t; } /** * Get an affine transformation representing a counter-clockwise rotation over the passed angle, * using the passed point as the center of rotation * * @param angle angle in radians to rotate over * @param x x-coordinate of center of rotation * @param y y-coordinate of center of rotation * @return {@link AffineTransform} representing the rotation */ public static AffineTransform getRotateInstance(double angle, double x, double y) { AffineTransform t = new AffineTransform(); t.setToRotation(angle, x, y); return t; } public void translate(double mx, double my) { concatenate(AffineTransform.getTranslateInstance(mx, my)); } public void scale(double scx, double scy) { concatenate(AffineTransform.getScaleInstance(scx, scy)); } public void shear(double shx, double shy) { concatenate(AffineTransform.getShearInstance(shx, shy)); } /** * Add a counter-clockwise rotation to this transformation * * @param angle angle in radians to rotate over */ public void rotate(double angle) { concatenate(AffineTransform.getRotateInstance(angle)); } /** * Add a counter-clockwise rotation to this transformation, * using the passed point as the center of rotation * @param angle angle in radians to rotate over * @param px x-coordinate of center of rotation * @param py y-coordinate of center of rotation */ public void rotate(double angle, double px, double py) { concatenate(AffineTransform.getRotateInstance(angle, px, py)); } /** * Multiply matrix of two AffineTransform objects * * @param t1 - the AffineTransform object is a multiplicand * @param t2 - the AffineTransform object is a multiplier * @return an AffineTransform object that is a result of t1 multiplied by matrix t2. */ AffineTransform multiply(AffineTransform t1, AffineTransform t2) { return new AffineTransform( t1.m00 * t2.m00 + t1.m10 * t2.m01, t1.m00 * t2.m10 + t1.m10 * t2.m11, t1.m01 * t2.m00 + t1.m11 * t2.m01, t1.m01 * t2.m10 + t1.m11 * t2.m11, t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12); } public void concatenate(AffineTransform t) { setTransform(multiply(t, this)); } public void preConcatenate(AffineTransform t) { setTransform(multiply(this, t)); } public AffineTransform createInverse() throws NoninvertibleTransformException { double det = getDeterminant(); if (Math.abs(det) < ZERO) { // awt.204=Determinant is zero //$NON-NLS-1$ throw new NoninvertibleTransformException(NoninvertibleTransformException.DETERMINANT_IS_ZERO_CANNOT_INVERT_TRANSFORMATION); } return new AffineTransform( m11 / det, -m10 / det, -m01 / det, m00 / det, (m01 * m12 - m11 * m02) / det, (m10 * m02 - m00 * m12) / det ); } public Point transform(Point src, Point dst) { if (dst == null) { dst = new Point(); } double x = src.getX(); double y = src.getY(); dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12); return dst; } public void transform(Point[] src, int srcOff, Point[] dst, int dstOff, int length) { while (--length >= 0) { Point srcPoint = src[srcOff++]; double x = srcPoint.getX(); double y = srcPoint.getY(); Point dstPoint = dst[dstOff]; if (dstPoint == null) { dstPoint = new Point(); } dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12); dst[dstOff++] = dstPoint; } } public void transform(double[] src, int srcOff, double[] dst, int dstOff, int length) { int step = 2; if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) { srcOff = srcOff + length * 2 - 2; dstOff = dstOff + length * 2 - 2; step = -2; } while (--length >= 0) { double x = src[srcOff + 0]; double y = src[srcOff + 1]; dst[dstOff + 0] = x * m00 + y * m01 + m02; dst[dstOff + 1] = x * m10 + y * m11 + m12; srcOff += step; dstOff += step; } } public void transform(float[] src, int srcOff, float[] dst, int dstOff, int length) { int step = 2; if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) { srcOff = srcOff + length * 2 - 2; dstOff = dstOff + length * 2 - 2; step = -2; } while (--length >= 0) { float x = src[srcOff + 0]; float y = src[srcOff + 1]; dst[dstOff + 0] = (float) (x * m00 + y * m01 + m02); dst[dstOff + 1] = (float) (x * m10 + y * m11 + m12); srcOff += step; dstOff += step; } } public void transform(float[] src, int srcOff, double[] dst, int dstOff, int length) { while (--length >= 0) { float x = src[srcOff++]; float y = src[srcOff++]; dst[dstOff++] = x * m00 + y * m01 + m02; dst[dstOff++] = x * m10 + y * m11 + m12; } } public void transform(double[] src, int srcOff, float[] dst, int dstOff, int length) { while (--length >= 0) { double x = src[srcOff++]; double y = src[srcOff++]; dst[dstOff++] = (float) (x * m00 + y * m01 + m02); dst[dstOff++] = (float) (x * m10 + y * m11 + m12); } } public Point deltaTransform(Point src, Point dst) { if (dst == null) { dst = new Point(); } double x = src.getX(); double y = src.getY(); dst.setLocation(x * m00 + y * m01, x * m10 + y * m11); return dst; } public void deltaTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) { while (--length >= 0) { double x = src[srcOff++]; double y = src[srcOff++]; dst[dstOff++] = x * m00 + y * m01; dst[dstOff++] = x * m10 + y * m11; } } public Point inverseTransform(Point src, Point dst) throws NoninvertibleTransformException { double det = getDeterminant(); if (Math.abs(det) < ZERO) { // awt.204=Determinant is zero //$NON-NLS-1$ throw new NoninvertibleTransformException(NoninvertibleTransformException.DETERMINANT_IS_ZERO_CANNOT_INVERT_TRANSFORMATION); } if (dst == null) { dst = new Point(); } double x = src.getX() - m02; double y = src.getY() - m12; dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10) / det); return dst; } public void inverseTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) throws NoninvertibleTransformException { double det = getDeterminant(); if (Math.abs(det) < ZERO) { // awt.204=Determinant is zero //$NON-NLS-1$ throw new NoninvertibleTransformException(NoninvertibleTransformException.DETERMINANT_IS_ZERO_CANNOT_INVERT_TRANSFORMATION); } while (--length >= 0) { double x = src[srcOff++] - m02; double y = src[srcOff++] - m12; dst[dstOff++] = (x * m11 - y * m01) / det; dst[dstOff++] = (y * m00 - x * m10) / det; } } public void inverseTransform(float[] src, int srcOff, float[] dst, int dstOff, int length) throws NoninvertibleTransformException { float det = (float) getDeterminant(); if (Math.abs(det) < ZERO) { // awt.204=Determinant is zero //$NON-NLS-1$ throw new NoninvertibleTransformException(NoninvertibleTransformException.DETERMINANT_IS_ZERO_CANNOT_INVERT_TRANSFORMATION); } while (--length >= 0) { float x = (float) (src[srcOff++] - m02); float y = (float) (src[srcOff++] - m12); dst[dstOff++] = (float) ((x * m11 - y * m01) / det); dst[dstOff++] = (float) ((y * m00 - x * m10) / det); } } /** * Creates a "deep copy" of this AffineTransform, meaning the object returned by this method will be independent * of the object being cloned. * * @return the copied AffineTransform. */ @Override public AffineTransform clone() throws CloneNotSupportedException { // super.clone is safe to return since all of the AffineTransform's fields are primitive. return (AffineTransform) super.clone(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AffineTransform that = (AffineTransform) o; return Double.compare(that.m00, m00) == 0 && Double.compare(that.m10, m10) == 0 && Double.compare(that.m01, m01) == 0 && Double.compare(that.m11, m11) == 0 && Double.compare(that.m02, m02) == 0 && Double.compare(that.m12, m12) == 0; } @Override public int hashCode() { return Objects.hash(m00, m10, m01, m11, m02, m12); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy