AffineTransformationFactory.java
/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.geom.util;
import org.locationtech.jts.algorithm.Angle;
import org.locationtech.jts.geom.Coordinate;
/**
* Supports creating {@link AffineTransformation}s defined by various kinds of
* inputs and transformation mapping rules.
*
* @author Martin Davis
*
*/
public class AffineTransformationFactory {
/**
* Creates a transformation from a set of three 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. Three control
* vectors allows defining a fully general affine transformation.
*
* @param src0
* @param src1
* @param src2
* @param dest0
* @param dest1
* @param dest2
* @return the computed transformation
*/
public static AffineTransformation createFromControlVectors(Coordinate src0,
Coordinate src1, Coordinate src2, Coordinate dest0, Coordinate dest1,
Coordinate dest2) {
AffineTransformationBuilder builder = new AffineTransformationBuilder(src0,
src1, src2, dest0, dest1, dest2);
return builder.getTransformation();
}
/**
* Creates an AffineTransformation defined by a pair 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. The
* computed transformation is a combination of one or more of a uniform scale,
* a rotation, and a translation (i.e. there is no shear component and no
* reflection)
*
* @param src0
* @param src1
* @param dest0
* @param dest1
* @return the computed transformation, or null if the control vectors do not determine a well-defined transformation
*/
public static AffineTransformation createFromControlVectors(Coordinate src0,
Coordinate src1, Coordinate dest0, Coordinate dest1) {
Coordinate rotPt = new Coordinate(dest1.x - dest0.x, dest1.y - dest0.y);
double ang = Angle.angleBetweenOriented(src1, src0, rotPt);
double srcDist = src1.distance(src0);
double destDist = dest1.distance(dest0);
if (srcDist == 0.0)
return null;
double scale = destDist / srcDist;
AffineTransformation trans = AffineTransformation.translationInstance(
-src0.x, -src0.y);
trans.rotate(ang);
trans.scale(scale, scale);
trans.translate(dest0.x, dest0.y);
return trans;
}
/**
* Creates an AffineTransformation defined by a single control vector. A
* control vector consists of a source point and a destination point, which is
* the image of the source point under the desired transformation. This
* produces a translation.
*
* @param src0
* the start point of the control vector
* @param dest0
* the end point of the control vector
* @return the computed transformation
*/
public static AffineTransformation createFromControlVectors(Coordinate src0,
Coordinate dest0) {
double dx = dest0.x - src0.x;
double dy = dest0.y - src0.y;
return AffineTransformation.translationInstance(dx, dy);
}
/**
* Creates an AffineTransformation defined by a set of control vectors.
* Between one and three vectors must be supplied.
*
* @param src
* the source points of the vectors
* @param dest
* the destination points of the vectors
* @return the computed transformation
* @throws IllegalArgumentException
* if the control vector arrays are too short, long or of different
* lengths
*/
public static AffineTransformation createFromControlVectors(Coordinate[] src,
Coordinate[] dest) {
if (src.length != dest.length)
throw new IllegalArgumentException(
"Src and Dest arrays are not the same length");
if (src.length <= 0)
throw new IllegalArgumentException("Too few control points");
if (src.length > 3)
throw new IllegalArgumentException("Too many control points");
if (src.length == 1)
return createFromControlVectors(src[0], dest[0]);
if (src.length == 2)
return createFromControlVectors(src[0], src[1], dest[0], dest[1]);
return createFromControlVectors(src[0], src[1], src[2], dest[0], dest[1],
dest[2]);
}
/**
* Creates an AffineTransformation defined by a mapping between two baselines.
* The computed transformation consists of:
* <ul>
* <li>a translation
* from the start point of the source baseline to the start point of the destination baseline,
* <li>a rotation through the angle between the baselines about the destination start point,
* <li>and a scaling equal to the ratio of the baseline lengths.
* </ul>
* If the source baseline has zero length, an identity transformation is returned.
*
* @param src0 the start point of the source baseline
* @param src1 the end point of the source baseline
* @param dest0 the start point of the destination baseline
* @param dest1 the end point of the destination baseline
* @return the computed transformation
*/
public static AffineTransformation createFromBaseLines(
Coordinate src0, Coordinate src1,
Coordinate dest0, Coordinate dest1)
{
Coordinate rotPt = new Coordinate(src0.x + dest1.x - dest0.x, src0.y + dest1.y - dest0.y);
double ang = Angle.angleBetweenOriented(src1, src0, rotPt);
double srcDist = src1.distance(src0);
double destDist = dest1.distance(dest0);
// return identity if transformation would be degenerate
if (srcDist == 0.0)
return new AffineTransformation();
double scale = destDist / srcDist;
AffineTransformation trans = AffineTransformation.translationInstance(
-src0.x, -src0.y);
trans.rotate(ang);
trans.scale(scale, scale);
trans.translate(dest0.x, dest0.y);
return trans;
}
}