package com.vividsolutions.jump.geom;
import com.vividsolutions.jts.algorithm.*;
import com.vividsolutions.jts.geom.*;
/**
* Builds an {@link AffineTransformation} defined by three control points
* and their images under the transformation.
* This technique of recovering a transformation
* from its effect on known points is used in the Bilinear Interpolated Triangulation
* algorithm for warping planar surfaces.
* <p>
* A transformation is well-defined by a set of 3 control points as long as the
* set of points is not collinear (this includes the degenerate situation
* where two or more points are identical).
* If the control points are not well-defined, the system of equations
* defining the transformation matrix entries is not solvable,
* and no transformation can be determined.
*
* @author Martin Davis
*/
public class AffineTransformationBuilder
{
private Coordinate src0;
private Coordinate src1;
private Coordinate src2;
private Coordinate dest0;
private Coordinate dest1;
private Coordinate dest2;
// the matrix entries for the transformation
private double m00, m01, m02, m10, m11, m12;
/**
* Constructs a new builder for
* the transformation defined by the given
* set of control point mappings.
*
* @param src0 a control point
* @param src1 a control point
* @param src2 a control point
* @param dest0 the image of control point 0 under the required transformation
* @param dest1 the image of control point 1 under the required transformation
* @param dest2 the image of control point 2 under the required transformation
*/
public AffineTransformationBuilder(Coordinate src0,
Coordinate src1,
Coordinate src2,
Coordinate dest0,
Coordinate dest1,
Coordinate dest2)
{
this.src0 = src0;
this.src1 = src1;
this.src2 = src2;
this.dest0 = dest0;
this.dest1 = dest1;
this.dest2 = dest2;
}
/**
* Computes the {@link AffineTransformation}
* determined by the control point mappings,
* or <code>null</code> if the control points do not determine a unique transformation.
*
* @return an affine transformation
* @return null if the control points do not determine a unique transformation
*/
public AffineTransformation getTransformation()
{
boolean isSolvable = compute();
if (isSolvable)
return new AffineTransformation(m00, m01, m02, m10, m11, m12);
return null;
}
/**
* Computes the transformation matrix by
* solving the two systems of linear equations
* defined by the control point mappings,
* if this is possible.
*
* @return true if the transformation matrix is solvable
*/
private boolean compute()
{
double[] bx = new double[] { dest0.x, dest1.x, dest2.x };
double[] row0 = solve(bx);
if (row0 == null) return false;
m00 = row0[0];
m01 = row0[1];
m02 = row0[2];
double[] by = new double[] { dest0.y, dest1.y, dest2.y };
double[] row1 = solve(by);
if (row1 == null) return false;
m10 = row1[0];
m11 = row1[1];
m12 = row1[2];
return true;
}
/**
* Solves the transformation matrix system of linear equations
* for the given right-hand side vector.
*
* @param b the vector for the right-hand side of the system
* @return the solution vector
* @return null if no solution could be determined
*/
private double[] solve(double[] b)
{
double[][] a = new double[][] {
{ src0.x, src0.y, 1 },
{ src1.x, src1.y, 1},
{ src2.x, src2.y, 1}
};
return Matrix.solve(a, b);
}
}