AffineTransforms2D changes for revisions 24484:24485

This commit contains the following changes:

Command line:

svn diff --extensions "--unified --ignore-space-change --ignore-all-space --ignore-eol-style" -r24484:24485 https://svn.osgeo.org/geotools/trunk/modules/library/referencing/src/main/java/org/geotools/referencing/operation/matrix/XAffineTransform.java
Revision 24484Revision 24485
 */
public abstract class XAffineTransform extends AffineTransform {
    /**
     * Serial number for interoperability with different versions.
     */
    private static final long serialVersionUID = 5215291166450556451L;
 */
public abstract class XAffineTransform extends AffineTransform {
    /**
     * Redefines {@link #isIdentity()} default behaviour from {@link AffineTransform}
     * in order to keep into account roundings errors.
     *
     * <p>
     * For example if we have the following affine transnform
     *
     * 1.0000000000000000001   0               0
     * 0               0.999999999999999999999  0
     * 0               0                 1
     *
     * I would say that is pretty hard to state that is not the identity.
     *
     * <P>
     * Keep in mind that the original check is pretty simple, see below here:
     *
     *   return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY));
     *
     *
     *  @param tr {@link AffineTransform} to be checked for identity.
     *  @param tolerance to use when checking for identity.
     *  return <code>true</code> id this tranformation is *close* enough to the
     *       indentity, <code>false</code> otherwise.
     *  @since 2.3.1
     */
    public final static boolean isIdentity(final AffineTransform tr,double tolerance) {
        tolerance=Math.abs(tolerance);
        boolean isIdentity=tr.isIdentity();
        if(!isIdentity){
           //get scale
         final double scale=getScale(tr);
         if(Math.abs(scale-1)>tolerance)
          return false;
         //rotation
         final double rotation =XAffineTransform.getRotation(tr);
         if(Math.abs(rotation)>tolerance)
          return false;
         //translations
         final double transX=tr.getTranslateX();
         if(Math.abs(transX)>tolerance)
          return false;
         final double transY=tr.getTranslateY();
         if(Math.abs(transY)>tolerance)
          return false;
         //shear
         final double shearX=tr.getShearX();
         if(Math.abs(shearX)>tolerance)
          return false;
         final double shearY=tr.getShearY();
         if(Math.abs(shearY)>tolerance)
          return false;

         //scale NOT SURE WE NEED THIS
         final double scaleX=tr.getScaleX();
         if(Math.abs(scaleX-1)>tolerance)
          return false;
         final double scaleY=tr.getScaleY();
         if(Math.abs(scaleY-1)>tolerance)
          return false;
         return true;


           ////
           //
           // Proposed replacement less performace though
           //
           ////
//       final AffineTransform clonedTransformation=(AffineTransform) tr.clone();
//       XAffineTransform.round(clonedTransformation, tolerance);
//       if(clonedTransformation.isIdentity())
//          return true;
        }
       return isIdentity;
    }
    /**
     * Check whether or not this {@link XAffineTransform} is te identity by
     * using the provided <code>tolerance</code>.
     *
     *
     * @param tolerance to use for this check.
     * @return true if the check succeeds, faflse otherwise.
     * @since 2.3.1
     */
    public boolean isIdentity(double tolerance) {
        return isIdentity(this, tolerance);
    }
    /**
     * Serial number for interoperability with different versions.
     */
    private static final long serialVersionUID = 5215291166450556451L;
/**
 * Tolerance value for floating point comparisons.
 */
private static final double EPS = 1E-6;

/**
 * Constructs a new {@code XAffineTransform} that is a
/**
 * Tolerance value for floating point comparisons.
 */
public static final double EPS = 1E-6;

/**
 * Constructs a new {@code XAffineTransform} that is a
 *
 * @return The direct transform of the {@code bounds} rectangle.
 */
public static Rectangle2D transform(final AffineTransform transform,
                                    final Rectangle2D     bounds,
                                    final Rectangle2D     dest)
{
 *
 * @return The direct transform of the {@code bounds} rectangle.
 */
public final static Rectangle2D transform(final AffineTransform transform,
                                    final Rectangle2D     bounds,
                                    final Rectangle2D     dest)
{
 * @return The inverse transform of the {@code bounds} rectangle.
 * @throws NoninvertibleTransformException if the affine transform can't be inverted.
 */
public static Rectangle2D inverseTransform(final AffineTransform transform,
                                           final Rectangle2D     bounds,
                                           final Rectangle2D     dest)
        throws NoninvertibleTransformException
 * @return The inverse transform of the {@code bounds} rectangle.
 * @throws NoninvertibleTransformException if the affine transform can't be inverted.
 */
public final static Rectangle2D inverseTransform(final AffineTransform transform,
                                           final Rectangle2D     bounds,
                                           final Rectangle2D     dest)
        throws NoninvertibleTransformException
 * @return The inverse transform of the {@code source} point.
 * @throws NoninvertibleTransformException if the affine transform can't be inverted.
 */
public static Point2D inverseDeltaTransform(final AffineTransform transform,
                                            final Point2D         source,
                                            final Point2D         dest)
        throws NoninvertibleTransformException
 * @return The inverse transform of the {@code source} point.
 * @throws NoninvertibleTransformException if the affine transform can't be inverted.
 */
public final static Point2D inverseDeltaTransform(final AffineTransform transform,
                                            final Point2D         source,
                                            final Point2D         dest)
        throws NoninvertibleTransformException
 * preserved, {@code -1} if the transform seems to swap axis to the (<var>y</var>,
 * <var>x</var>) axis order, or {@code 0} if this method can not make a decision.
 */
public static int getSwapXY(final AffineTransform tr) {
    final int flip = getFlip(tr);
    if (flip != 0) {
        final double scaleX = getScaleX0(tr);
 * preserved, {@code -1} if the transform seems to swap axis to the (<var>y</var>,
 * <var>x</var>) axis order, or {@code 0} if this method can not make a decision.
 */
public final static int getSwapXY(final AffineTransform tr) {
    final int flip = getFlip(tr);
    if (flip != 0) {
        final double scaleX = getScaleX0(tr);
 * @return An estimation of the rotation angle in radians, or {@link Double#NaN NaN}
 *         if the angle can not be estimated.
 */
public static double getRotation(final AffineTransform tr) {
    final int flip = getFlip(tr);
    if (flip != 0) {
        final double scaleX = getScaleX0(tr);
 * @return An estimation of the rotation angle in radians, or {@link Double#NaN NaN}
 *         if the angle can not be estimated.
 */
public final static double getRotation(final AffineTransform tr) {
    final int flip = getFlip(tr);
    if (flip != 0) {
        final double scaleX = getScaleX0(tr);
 * boolean flipped = (tr.{@linkplain #getType() getType()} & {@linkplain #TYPE_FLIP}) != 0;
 * </code></blockquote>
 */
public static int getFlip(final AffineTransform tr) {
    final int scaleX = XMath.sgn(tr.getScaleX());
    final int scaleY = XMath.sgn(tr.getScaleY());
    final int shearX = XMath.sgn(tr.getShearX());
 * boolean flipped = (tr.{@linkplain #getType() getType()} & {@linkplain #TYPE_FLIP}) != 0;
 * </code></blockquote>
 */
public final static int getFlip(final AffineTransform tr) {
    final int scaleX = XMath.sgn(tr.getScaleX());
    final int scaleY = XMath.sgn(tr.getScaleY());
    final int shearX = XMath.sgn(tr.getShearX());
 * effect of eventual flip and rotation. This factor is calculated by
 * <IMG src="{@docRoot}/org/geotools/display/canvas/doc-files/scaleX0.png">.
 */
public static double getScaleX0(final AffineTransform tr) {
    return XMath.hypot(tr.getScaleX(), tr.getShearX());
}
 * effect of eventual flip and rotation. This factor is calculated by
 * <IMG src="{@docRoot}/org/geotools/display/canvas/doc-files/scaleX0.png">.
 */
public final static double getScaleX0(final AffineTransform tr) {
    return XMath.hypot(tr.getScaleX(), tr.getShearX());
}
 * effect of eventual flip and rotation. This factor is calculated by
 * <IMG src="{@docRoot}/org/geotools/display/canvas/doc-files/scaleY0.png">.
 */
public static double getScaleY0(final AffineTransform tr) {
    return XMath.hypot(tr.getScaleY(), tr.getShearY());
}
 * effect of eventual flip and rotation. This factor is calculated by
 * <IMG src="{@docRoot}/org/geotools/display/canvas/doc-files/scaleY0.png">.
 */
public final  static double getScaleY0(final AffineTransform tr) {
    return XMath.hypot(tr.getScaleY(), tr.getShearY());
}
 * The way to compute such a "global" scale is somewhat arbitrary and may change
 * in a future version.
 */
public static double getScale(final AffineTransform tr) {
    return 0.5 * (getScaleX0(tr) + getScaleY0(tr));
}
 * The way to compute such a "global" scale is somewhat arbitrary and may change
 * in a future version.
 */
public final static double getScale(final AffineTransform tr) {
    return 0.5 * (getScaleX0(tr) + getScaleY0(tr));
}
 * @return   Affine transform of a zoom which leaves the
 *          (<var>x</var>,<var>y</var>) coordinate unchanged.
 */
public static AffineTransform getScaleInstance(final double sx, final double sy,
                                               final double  x, final double  y) {
    return new AffineTransform(sx, 0, 0, sy, (1-sx)*x, (1-sy)*y);
}
 * @return   Affine transform of a zoom which leaves the
 *          (<var>x</var>,<var>y</var>) coordinate unchanged.
 */
public final  static AffineTransform getScaleInstance(final double sx, final double sy,
                                               final double  x, final double  y) {
    return new AffineTransform(sx, 0, 0, sy, (1-sx)*x, (1-sy)*y);
}
 * nearest whole numbers. This rounding up is useful, for example, for
 * speeding up image displays.  Above all, it is efficient when we know that
 * a matrix has a chance of being close to the similarity matrix.
 */
public static void round(final AffineTransform tr) {
    double r;
    final double m00, m01, m10, m11;
    if (Math.abs((m00 = Math.rint(r = tr.getScaleX())) - r) <= EPS &&
        Math.abs((m01 = Math.rint(r = tr.getShearX())) - r) <= EPS &&
        Math.abs((m11 = Math.rint(r = tr.getScaleY())) - r) <= EPS &&
        Math.abs((m10 = Math.rint(r = tr.getShearY())) - r) <= EPS)
    {
        if ((m00!=0 || m01!=0) && (m10!=0 || m11!=0)) {
            double m02=Math.rint(r=tr.getTranslateX()); if (!(Math.abs(m02-r)<=EPS)) m02=r;
            double m12=Math.rint(r=tr.getTranslateY()); if (!(Math.abs(m12-r)<=EPS)) m12=r;
            tr.setTransform(m00, m10, m01, m11, m02, m12);
        }
    }
 * nearest whole numbers. This rounding up is useful, for example, for
 * speeding up image displays.  Above all, it is efficient when we know that
 * a matrix has a chance of being close to the similarity matrix.
 *
 * <p>
 * It is crucial to note that this method uses a default rounding threshold
 * whose value is hedl by the field {@link #EPS} which is {@value #EPS}.
 */
public final static void round(final AffineTransform tr) {
  round(tr,EPS);
}

/**
 * Checks whether the matrix coefficients are close to whole numbers.
 * If this is the case, these coefficients will be rounded up to the
 * nearest whole numbers. This rounding up is useful, for example, for
 * speeding up image displays.  Above all, it is efficient when we know that
 * a matrix has a chance of being close to the similarity matrix.
 */
public final static void round(final AffineTransform tr,final double CUSTOM_EPS) {
    double r;
    final double m00, m01, m10, m11;
    if (Math.abs((m00 = Math.rint(r = tr.getScaleX())) - r) <= CUSTOM_EPS &&
        Math.abs((m01 = Math.rint(r = tr.getShearX())) - r) <= CUSTOM_EPS &&
        Math.abs((m11 = Math.rint(r = tr.getScaleY())) - r) <= CUSTOM_EPS &&
        Math.abs((m10 = Math.rint(r = tr.getShearY())) - r) <= CUSTOM_EPS)
    {
        if ((m00!=0 || m01!=0) && (m10!=0 || m11!=0)) {
            double m02=Math.rint(r=tr.getTranslateX()); if (!(Math.abs(m02-r)<=CUSTOM_EPS)) m02=r;
            double m12=Math.rint(r=tr.getTranslateY()); if (!(Math.abs(m12-r)<=CUSTOM_EPS)) m12=r;
            tr.setTransform(m00, m10, m01, m11, m02, m12);
        }
    }