/* * The JTS Topology Suite is a collection of Java classes that * implement the fundamental operations required to validate a given * geo-spatial data set to a known topological specification. * * Copyright (C) 2001 Vivid Solutions * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.revolsys.geometry.model.util; import com.revolsys.geometry.math.Matrix; import com.revolsys.geometry.model.Point; /** * Builds an {@link AffineTransformation} defined by a set of control vectors. * A control vector consists of a source point and a destination point, * which is the image of the source point under the desired transformation. * <p> * A transformation is well-defined * by a set of three control vectors * if and only if the source points are not collinear. * (In particular, the degenerate situation * where two or more source points are identical will not produce a well-defined transformation). * A well-defined transformation exists and is unique. * If the control vectors are not well-defined, the system of equations * defining the transformation matrix entries is not solvable, * and no transformation can be determined. * <p> * No such restriction applies to the destination points. * However, if the destination points are collinear or non-unique, * a non-invertible transformations will be generated. * <p> * This technique of recovering a transformation * from its effect on known points is used in the Bilinear Interpolated Triangulation * algorithm for warping planar surfaces. * * @author Martin Davis */ public class AffineTransformationBuilder { private final Point dest0; private final Point dest1; private final Point dest2; // the matrix entries for the transformation private double m00, m01, m02, m10, m11, m12; private final Point src0; private final Point src1; private final Point src2; /** * 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(final Point src0, final Point src1, final Point src2, final Point dest0, final Point dest1, final Point dest2) { this.src0 = src0; this.src1 = src1; this.src2 = src2; this.dest0 = dest0; this.dest1 = dest1; this.dest2 = dest2; } /** * 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() { final double[] bx = new double[] { this.dest0.getX(), this.dest1.getX(), this.dest2.getX() }; final double[] row0 = solve(bx); if (row0 == null) { return false; } this.m00 = row0[0]; this.m01 = row0[1]; this.m02 = row0[2]; final double[] by = new double[] { this.dest0.getY(), this.dest1.getY(), this.dest2.getY() }; final double[] row1 = solve(by); if (row1 == null) { return false; } this.m10 = row1[0]; this.m11 = row1[1]; this.m12 = row1[2]; return true; } /** * Computes the {@link AffineTransformation} * determined by the control point mappings, * or <code>null</code> if the control vectors do not determine a well-defined transformation. * * @return an affine transformation * @return null if the control vectors do not determine a well-defined transformation */ public AffineTransformation getTransformation() { // compute full 3-point transformation final boolean isSolvable = compute(); if (isSolvable) { return new AffineTransformation(this.m00, this.m01, this.m02, this.m10, this.m11, this.m12); } return null; } /** * 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(final double[] b) { final double[][] a = new double[][] { { this.src0.getX(), this.src0.getY(), 1 }, { this.src1.getX(), this.src1.getY(), 1 }, { this.src2.getX(), this.src2.getY(), 1 } }; return Matrix.solve(a, b); } }