001: package com.vividsolutions.jump.geom;
002:
003: import com.vividsolutions.jts.algorithm.*;
004: import com.vividsolutions.jts.geom.*;
005:
006: /**
007: * Builds an {@link AffineTransformation} defined by three control points
008: * and their images under the transformation.
009: * This technique of recovering a transformation
010: * from its effect on known points is used in the Bilinear Interpolated Triangulation
011: * algorithm for warping planar surfaces.
012: * <p>
013: * A transformation is well-defined by a set of 3 control points as long as the
014: * set of points is not collinear (this includes the degenerate situation
015: * where two or more points are identical).
016: * If the control points are not well-defined, the system of equations
017: * defining the transformation matrix entries is not solvable,
018: * and no transformation can be determined.
019: *
020: * @author Martin Davis
021: */
022: public class AffineTransformationBuilder {
023: private Coordinate src0;
024: private Coordinate src1;
025: private Coordinate src2;
026: private Coordinate dest0;
027: private Coordinate dest1;
028: private Coordinate dest2;
029:
030: // the matrix entries for the transformation
031: private double m00, m01, m02, m10, m11, m12;
032:
033: /**
034: * Constructs a new builder for
035: * the transformation defined by the given
036: * set of control point mappings.
037: *
038: * @param src0 a control point
039: * @param src1 a control point
040: * @param src2 a control point
041: * @param dest0 the image of control point 0 under the required transformation
042: * @param dest1 the image of control point 1 under the required transformation
043: * @param dest2 the image of control point 2 under the required transformation
044: */
045: public AffineTransformationBuilder(Coordinate src0,
046: Coordinate src1, Coordinate src2, Coordinate dest0,
047: Coordinate dest1, Coordinate dest2) {
048: this .src0 = src0;
049: this .src1 = src1;
050: this .src2 = src2;
051: this .dest0 = dest0;
052: this .dest1 = dest1;
053: this .dest2 = dest2;
054: }
055:
056: /**
057: * Computes the {@link AffineTransformation}
058: * determined by the control point mappings,
059: * or <code>null</code> if the control points do not determine a unique transformation.
060: *
061: * @return an affine transformation
062: * @return null if the control points do not determine a unique transformation
063: */
064: public AffineTransformation getTransformation() {
065: boolean isSolvable = compute();
066: if (isSolvable)
067: return new AffineTransformation(m00, m01, m02, m10, m11,
068: m12);
069: return null;
070: }
071:
072: /**
073: * Computes the transformation matrix by
074: * solving the two systems of linear equations
075: * defined by the control point mappings,
076: * if this is possible.
077: *
078: * @return true if the transformation matrix is solvable
079: */
080: private boolean compute() {
081: double[] bx = new double[] { dest0.x, dest1.x, dest2.x };
082: double[] row0 = solve(bx);
083: if (row0 == null)
084: return false;
085: m00 = row0[0];
086: m01 = row0[1];
087: m02 = row0[2];
088:
089: double[] by = new double[] { dest0.y, dest1.y, dest2.y };
090: double[] row1 = solve(by);
091: if (row1 == null)
092: return false;
093: m10 = row1[0];
094: m11 = row1[1];
095: m12 = row1[2];
096: return true;
097: }
098:
099: /**
100: * Solves the transformation matrix system of linear equations
101: * for the given right-hand side vector.
102: *
103: * @param b the vector for the right-hand side of the system
104: * @return the solution vector
105: * @return null if no solution could be determined
106: */
107: private double[] solve(double[] b) {
108: double[][] a = new double[][] { { src0.x, src0.y, 1 },
109: { src1.x, src1.y, 1 }, { src2.x, src2.y, 1 } };
110: return Matrix.solve(a, b);
111: }
112: }
|