/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; 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.vividsolutions.jump.warp;
import Jama.Matrix;
import com.vividsolutions.jts.geom.Coordinate;
/**
* An AffineTransform implementation that is initialized by specifying
* three points and the three points they map to.
* <p>From http://graphics.lcs.mit.edu/classes/6.837/F01/Lecture07/lecture07.pdf:
* <pre>
* [ x1_ ] = [ x1 y1 1 0 0 0 ] [ a11 ]
* [ y1_ ] = [ 0 0 0 x1 y1 1 ] [ a12 ]
* [ x2_ ] = [ x2 y2 1 0 0 0 ] [ a13 ]
* [ y2_ ] = [ 0 0 0 x2 y2 1 ] [ a21 ]
* [ x3_ ] = [ x3 y3 1 0 0 0 ] [ a22 ]
* [ y3_ ] = [ 0 0 0 x3 y3 1 ] [ a23 ]
* x_ = X a
* Solution: a = Xinv x_
* </pre>
*/
public class AffineTransform extends CoordinateTransform {
private Matrix a;
/**
* A transformation that maps p1 to p1_ via a translation (no rotation or shear).
* @param p1 a point
* @param p1_ the point it maps to
*/
public AffineTransform(Coordinate p1, Coordinate p1_) {
Coordinate p2 = new Coordinate(p1.x + 10, p1.y);
Coordinate p2_ = new Coordinate(p1_.x + 10, p1_.y);
Coordinate p3 = new Coordinate(p1.x, p1.y + 10);
Coordinate p3_ = new Coordinate(p1_.x, p1_.y + 10);
initialize(p1, p1_, p2, p2_, p3, p3_);
}
/**
* A transformation that maps p1 to p1_ and p2 to p2_ via a translation,
* rotation, and scaling (no "relative" shear).
* @param p1 a point
* @param p1_ the point p1 maps to
* @param p2 another point
* @param p2_ the point p2 maps to
*/
public AffineTransform(Coordinate p1, Coordinate p1_, Coordinate p2,
Coordinate p2_) {
Coordinate p3 = rotate90(p1, p2);
Coordinate p3_ = rotate90(p1_, p2_);
initialize(p1, p1_, p2, p2_, p3, p3_);
}
/**
* A transformation that maps p1 to p1_, p2 to p2_ and p3 to p3_.
* @param p1 a point
* @param p1_ the point p1 maps to
* @param p2 another point
* @param p2_ the point p2 maps to
* @param p3 another point
* @param p3_ the point p3 maps to
*/
public AffineTransform(Coordinate p1, Coordinate p1_, Coordinate p2,
Coordinate p2_, Coordinate p3, Coordinate p3_) {
initialize(p1, p1_, p2, p2_, p3, p3_);
}
/**
* Determines where a point would end up if it were rotated 90 degrees about
* another point.
* @param a the fixed point
* @param b the point to rotate (b itself will not be changed)
* @return b rotated 90 degrees clockwise about a
*/
public static Coordinate rotate90(Coordinate a, Coordinate b) {
return new Coordinate(b.y - a.y + a.x, a.x - b.x + a.y);
}
private void initialize(Coordinate p1, Coordinate p1_, Coordinate p2,
Coordinate p2_, Coordinate p3, Coordinate p3_) {
double[][] Xarray = {
{ p1.x, p1.y, 1, 0, 0, 0 },
{ 0, 0, 0, p1.x, p1.y, 1 },
{ p2.x, p2.y, 1, 0, 0, 0 },
{ 0, 0, 0, p2.x, p2.y, 1 },
{ p3.x, p3.y, 1, 0, 0, 0 },
{ 0, 0, 0, p3.x, p3.y, 1 }
};
Matrix X = new Matrix(Xarray);
double[][] x_array = {
{ p1_.x },
{ p1_.y },
{ p2_.x },
{ p2_.y },
{ p3_.x },
{ p3_.y }
};
Matrix x_ = new Matrix(x_array);
a = X.solve(x_);
}
/**
* Applies the affine transform to a point.
* From http://graphics.lcs.mit.edu/classes/6.837/F01/Lecture07/lecture07.pdf:
* <pre>
* [ x_ ] = [ a11 a12 a13 ] [ x ]
* [ y_ ] = [ a21 a22 a23 ] [ y ]
* [ 1 ] = [ 0 0 1 ] [ 1 ]
* </pre>
* @param c the input to the affine transform
* @return the result of applying the affine transform to c
*/
public Coordinate transform(Coordinate c) {
double x_ = (a.get(0, 0) * c.x) + (a.get(1, 0) * c.y) + a.get(2, 0);
double y_ = (a.get(3, 0) * c.x) + (a.get(4, 0) * c.y) + a.get(5, 0);
return new Coordinate(x_, y_);
}
}