| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
package org.locationtech.jts.geom.util; |
| 14 |
|
| 15 |
import org.locationtech.jts.geom.Coordinate; |
| 16 |
import org.locationtech.jts.math.Matrix; |
| 17 |
|
| 18 |
/** |
| 19 |
* Builds an {@link AffineTransformation} defined by a set of control vectors. |
| 20 |
* A control vector consists of a source point and a destination point, |
| 21 |
* which is the image of the source point under the desired transformation. |
| 22 |
* <p> |
| 23 |
* A transformation is well-defined |
| 24 |
* by a set of three control vectors |
| 25 |
* if and only if the source points are not collinear. |
| 26 |
* (In particular, the degenerate situation |
| 27 |
* where two or more source points are identical will not produce a well-defined transformation). |
| 28 |
* A well-defined transformation exists and is unique. |
| 29 |
* If the control vectors are not well-defined, the system of equations |
| 30 |
* defining the transformation matrix entries is not solvable, |
| 31 |
* and no transformation can be determined. |
| 32 |
* <p> |
| 33 |
* No such restriction applies to the destination points. |
| 34 |
* However, if the destination points are collinear or non-unique, |
| 35 |
* a non-invertible transformations will be generated. |
| 36 |
* <p> |
| 37 |
* This technique of recovering a transformation |
| 38 |
* from its effect on known points is used in the Bilinear Interpolated Triangulation |
| 39 |
* algorithm for warping planar surfaces. |
| 40 |
* |
| 41 |
* @author Martin Davis |
| 42 |
*/ |
| 43 |
public class AffineTransformationBuilder |
| 44 |
{ |
| 45 |
private Coordinate src0; |
| 46 |
private Coordinate src1; |
| 47 |
private Coordinate src2; |
| 48 |
private Coordinate dest0; |
| 49 |
private Coordinate dest1; |
| 50 |
private Coordinate dest2; |
| 51 |
|
| 52 |
|
| 53 |
private double m00, m01, m02, m10, m11, m12; |
| 54 |
|
| 55 |
|
| 56 |
/** |
| 57 |
* Constructs a new builder for |
| 58 |
* the transformation defined by the given |
| 59 |
* set of control point mappings. |
| 60 |
* |
| 61 |
* @param src0 a control point |
| 62 |
* @param src1 a control point |
| 63 |
* @param src2 a control point |
| 64 |
* @param dest0 the image of control point 0 under the required transformation |
| 65 |
* @param dest1 the image of control point 1 under the required transformation |
| 66 |
* @param dest2 the image of control point 2 under the required transformation |
| 67 |
*/ |
| 68 |
public AffineTransformationBuilder(Coordinate src0, |
| 69 |
Coordinate src1, |
| 70 |
Coordinate src2, |
| 71 |
Coordinate dest0, |
| 72 |
Coordinate dest1, |
| 73 |
Coordinate dest2) |
| 74 |
{ |
| 75 |
this.src0 = src0; |
| 76 |
this.src1 = src1; |
| 77 |
this.src2 = src2; |
| 78 |
this.dest0 = dest0; |
| 79 |
this.dest1 = dest1; |
| 80 |
this.dest2 = dest2; |
| 81 |
} |
| 82 |
|
| 83 |
/** |
| 84 |
* Computes the {@link AffineTransformation} |
| 85 |
* determined by the control point mappings, |
| 86 |
* or <code>null</code> if the control vectors do not determine a well-defined transformation. |
| 87 |
* |
| 88 |
* @return an affine transformation, or null if the control vectors do not determine a well-defined transformation |
| 89 |
*/ |
| 90 |
public AffineTransformation getTransformation() |
| 91 |
{ |
| 92 |
|
| 93 |
boolean isSolvable = compute(); |
| 94 |
if (isSolvable) |
| 95 |
return new AffineTransformation(m00, m01, m02, m10, m11, m12); |
| 96 |
return null; |
| 97 |
} |
| 98 |
|
| 99 |
/** |
| 100 |
* Computes the transformation matrix by |
| 101 |
* solving the two systems of linear equations |
| 102 |
* defined by the control point mappings, |
| 103 |
* if this is possible. |
| 104 |
* |
| 105 |
* @return true if the transformation matrix is solvable |
| 106 |
*/ |
| 107 |
private boolean compute() |
| 108 |
{ |
| 109 |
double[] bx = new double[] { dest0.x, dest1.x, dest2.x }; |
| 110 |
double[] row0 = solve(bx); |
| 111 |
if (row0 == null) return false; |
| 112 |
m00 = row0[0]; |
| 113 |
m01 = row0[1]; |
| 114 |
m02 = row0[2]; |
| 115 |
|
| 116 |
double[] by = new double[] { dest0.y, dest1.y, dest2.y }; |
| 117 |
double[] row1 = solve(by); |
| 118 |
if (row1 == null) return false; |
| 119 |
m10 = row1[0]; |
| 120 |
m11 = row1[1]; |
| 121 |
m12 = row1[2]; |
| 122 |
return true; |
| 123 |
} |
| 124 |
|
| 125 |
/** |
| 126 |
* Solves the transformation matrix system of linear equations |
| 127 |
* for the given right-hand side vector. |
| 128 |
* |
| 129 |
* @param b the vector for the right-hand side of the system |
| 130 |
* @return the solution vector, or <code>null</code> if no solution could be determined |
| 131 |
*/ |
| 132 |
private double[] solve(double[] b) |
| 133 |
{ |
| 134 |
double[][] a = new double[][] { |
| 135 |
{ src0.x, src0.y, 1 }, |
| 136 |
{ src1.x, src1.y, 1}, |
| 137 |
{ src2.x, src2.y, 1} |
| 138 |
}; |
| 139 |
return Matrix.solve(a, b); |
| 140 |
} |
| 141 |
} |
| 142 |
|