/*
* @(#)TransformUtils.java
*
* $Date: 2014-11-08 19:55:43 +0100 (Szo, 08 nov. 2014) $
*
* Copyright (c) 2011 by Jeremy Wood.
* All rights reserved.
*
* The copyright of this software is owned by Jeremy Wood.
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* Jeremy Wood. For details see accompanying license terms.
*
* This software is probably, but not necessarily, discussed here:
* https://javagraphics.java.net/
*
* That site should also contain the most recent official version
* of this software. (See the SVN repository for more details.)
*/
package com.bric.geom;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import com.bric.math.Equations;
/** This is a collection of methods that deal with AffineTransforms.
* Note the PerspectiveTransform class already has static methods that
* perform similar functions.
*
**/
public class TransformUtils {
/**
* Compute the rotation angle of an affine transformation.
* Counter-clockwise rotation is considered positive.
*
* @param transform the AffineTransform to analyze.
* @return rotation angle in radians (beween -pi and pi),
* or NaN if the transformation is bogus.
*/
public static double getRotationAngle(AffineTransform transform) {
transform = (AffineTransform) transform.clone();
// Eliminate any post-translation
transform.preConcatenate(AffineTransform.getTranslateInstance(
-transform.getTranslateX(), -transform.getTranslateY()));
Point2D p1 = new Point2D.Double(1,0);
p1 = transform.transform(p1,p1);
return Math.atan2(p1.getY(),p1.getX());
}
/** Given 3 points, this will return the <code>AffineTransform</code> that links each <code>initial</code>
* to <code>final</code> point.
* <P> This uses the <code>solve(matrix,true)</code> method.
* @param initialP1 the point that transforms into finalP1
* @param initialP2 the point that transforms into finalP2
* @param initialP3 the point that transforms into finalP3
* @param finalP1 the point that originated at initialP1
* @param finalP2 the point that originated at initialP2
* @param finalP3 the point that originated at initialP3
* @return an AffineTransform that maps from the initial points to the final points.
*/
public static AffineTransform createAffineTransform(Point2D initialP1,Point2D initialP2,Point2D initialP3,
Point2D finalP1,Point2D finalP2,Point2D finalP3) {
return createAffineTransform(initialP1.getX(), initialP1.getY(),
initialP2.getX(),initialP2.getY(),
initialP3.getX(),initialP3.getY(),
finalP1.getX(),finalP1.getY(),
finalP2.getX(),finalP2.getY(),
finalP3.getX(),finalP3.getY() );
}
/** This will return the <code>AffineTransform</code> that maps one rectangle
* onto another.
* <P> This uses the <code>solve(matrix,true)</code> method.
* @param r1 the rectangle that transforms into r2
* @param r2 the rectangle that originated at r1
* @return an AffineTransform that maps from the initial rectangle to the final rectangle.
*/
public static AffineTransform createAffineTransform(Rectangle2D r1,Rectangle2D r2) {
return createAffineTransform(r1.getMinX(), r1.getMinY(),
r1.getMaxX(), r1.getMinY(),
r1.getMinX(), r1.getMaxY(),
r2.getMinX(), r2.getMinY(),
r2.getMaxX(), r2.getMinY(),
r2.getMinX(), r2.getMaxY() );
}
/** Given 3 points, this will return the <code>AffineTransform</code> that links each <code>initial</code>
* to <code>final</code> point.
* <P> This uses the <code>solve(matrix,true)</code> method.
* @param oldX1 the x-coordinate of the untransformed first point
* @param oldY1 the y-coordinate of the untransformed first point
* @param oldX2 the x-coordinate of the untransformed second point
* @param oldY2 the y-coordinate of the untransformed second point
* @param oldX3 the x-coordinate of the untransformed third point
* @param oldY3 the y-coordinate of the untransformed third point
* @param newX1 the x-coordinate of the transformed first point
* @param newY1 the y-coordinate of the transformed first point
* @param newX2 the x-coordinate of the transformed second point
* @param newY2 the y-coordinate of the transformed second point
* @param newX3 the x-coordinate of the transformed third point
* @param newY3 the y-coordinate of the transformed third point
* @return an AffineTransform that maps from the initial points to the final points.
*/
public static AffineTransform createAffineTransform(double oldX1,double oldY1,
double oldX2,double oldY2,
double oldX3,double oldY3,
double newX1,double newY1,
double newX2,double newY2,
double newX3,double newY3) {
double[][] matrix = { {oldX1, oldY1, 1, newX1},
{oldX2, oldY2, 1, newX2},
{oldX3, oldY3, 1, newX3} };
try {
Equations.solve(matrix,true);
} catch(RuntimeException e) {
System.err.println("( "+oldX1+", "+oldY1+") -> ( "+newX1+", "+newY1+")");
System.err.println("( "+oldX2+", "+oldY2+") -> ( "+newX2+", "+newY2+")");
System.err.println("( "+oldX3+", "+oldY3+") -> ( "+newX3+", "+newY3+")");
throw e;
}
double m00 = matrix[0][3];
double m01 = matrix[1][3];
double m02 = matrix[2][3];
matrix = new double[][] { { oldX1, oldY1, 1, newY1 },
{ oldX2, oldY2, 1, newY2 },
{ oldX3, oldY3, 1, newY3 } };
Equations.solve(matrix,true);
double m10 = matrix[0][3];
double m11 = matrix[1][3];
double m12 = matrix[2][3];
return new AffineTransform(m00, m10, m01, m11, m02,m12);
}
/** Transitions from one AffineTransform to another.
*
* @param a the initial AffineTransform
* @param b the final AffineTransform
* @param progress a float between zero and one, where zero
* represents <code>a</code> and one represents <code>b</code>.
* Values outside this range will not throw an exception, but they will
* make some funky results.
* @param createNewObject indicates whether a new AffineTransform
* should be constructed, or if one of the arguments can be
* used to store the results
* @return a transform that is somehow between <code>a</code> and <code>b</code>.
*/
public static AffineTransform tween(AffineTransform a,AffineTransform b,float progress,boolean createNewObject) {
AffineTransform dest = (createNewObject) ? new AffineTransform() : a;
dest.setTransform(
a.getScaleX()*(1-progress)+b.getScaleX()*progress,
a.getShearY()*(1-progress)+b.getShearY()*progress,
a.getShearX()*(1-progress)+b.getShearX()*progress,
a.getScaleY()*(1-progress)+b.getScaleY()*progress,
a.getTranslateX()*(1-progress)+b.getTranslateX()*progress,
a.getTranslateY()*(1-progress)+b.getTranslateY()*progress);
return dest;
}
}