Class AffineTransformationFactory

  1 /*
  2  * Copyright (c) 2016 Vivid Solutions.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12  
 13 package org.locationtech.jts.geom.util;
 14  
 15 import org.locationtech.jts.algorithm.Angle;
 16 import org.locationtech.jts.geom.Coordinate;
 17  
 18 /**
 19  * Supports creating {@link AffineTransformation}s defined by various kinds of
 20  * inputs and transformation mapping rules.
 21  * 
 22  * @author Martin Davis
 23  * 
 24  */
 25 public class AffineTransformationFactory {
 26     /**
 27      * Creates a transformation from a set of three control vectors. A control
 28      * vector consists of a source point and a destination point, which is the
 29      * image of the source point under the desired transformation. Three control
 30      * vectors allows defining a fully general affine transformation.
 31      * 
 32      * @param src0
 33      * @param src1
 34      * @param src2
 35      * @param dest0
 36      * @param dest1
 37      * @param dest2
 38      * @return the computed transformation
 39      */
 40     public static AffineTransformation createFromControlVectors(Coordinate src0,
 41             Coordinate src1, Coordinate src2, Coordinate dest0, Coordinate dest1,
 42             Coordinate dest2) {
 43         AffineTransformationBuilder builder = new AffineTransformationBuilder(src0,
 44                 src1, src2, dest0, dest1, dest2);
 45         return builder.getTransformation();
 46     }
 47  
 48     /**
 49      * Creates an AffineTransformation defined by a pair of control vectors. A
 50      * control vector consists of a source point and a destination point, which is
 51      * the image of the source point under the desired transformation. The
 52      * computed transformation is a combination of one or more of a uniform scale,
 53      * a rotation, and a translation (i.e. there is no shear component and no
 54      * reflection)
 55      * 
 56      * @param src0
 57      * @param src1
 58      * @param dest0
 59      * @param dest1
 60      * @return the computed transformation, or null if the control vectors do not determine a well-defined transformation
 61      */
 62     public static AffineTransformation createFromControlVectors(Coordinate src0,
 63             Coordinate src1, Coordinate dest0, Coordinate dest1) {
 64         Coordinate rotPt = new Coordinate(dest1.x - dest0.x, dest1.y - dest0.y);
 65  
 66         double ang = Angle.angleBetweenOriented(src1, src0, rotPt);
 67  
 68         double srcDist = src1.distance(src0);
 69         double destDist = dest1.distance(dest0);
 70  
 71         if (srcDist == 0.0)
 72             return null;
 73  
 74         double scale = destDist / srcDist;
 75  
 76         AffineTransformation trans = AffineTransformation.translationInstance(
 77                 -src0.x, -src0.y);
 78         trans.rotate(ang);
 79         trans.scale(scale, scale);
 80         trans.translate(dest0.x, dest0.y);
 81         return trans;
 82     }
 83  
 84     /**
 85      * Creates an AffineTransformation defined by a single control vector. A
 86      * control vector consists of a source point and a destination point, which is
 87      * the image of the source point under the desired transformation. This
 88      * produces a translation.
 89      * 
 90      * @param src0
 91      *          the start point of the control vector
 92      * @param dest0
 93      *          the end point of the control vector
 94      * @return the computed transformation
 95      */
 96     public static AffineTransformation createFromControlVectors(Coordinate src0,
 97             Coordinate dest0) {
 98         double dx = dest0.x - src0.x;
 99         double dy = dest0.y - src0.y;
100         return AffineTransformation.translationInstance(dx, dy);
101     }
102  
103     /**
104      * Creates an AffineTransformation defined by a set of control vectors.
105      * Between one and three vectors must be supplied.
106      * 
107      * @param src
108      *          the source points of the vectors
109      * @param dest
110      *          the destination points of the vectors
111      * @return the computed transformation
112      * @throws IllegalArgumentException
113      *           if the control vector arrays are too short, long or of different
114      *           lengths
115      */
116     public static AffineTransformation createFromControlVectors(Coordinate[] src,
117             Coordinate[] dest) {
118         if (src.length != dest.length)
119             throw new IllegalArgumentException(
120                     "Src and Dest arrays are not the same length");
121         if (src.length <= 0)
122             throw new IllegalArgumentException("Too few control points");
123         if (src.length > 3)
124             throw new IllegalArgumentException("Too many control points");
125  
126         if (src.length == 1)
127             return createFromControlVectors(src[0], dest[0]);
128         if (src.length == 2)
129             return createFromControlVectors(src[0], src[1], dest[0], dest[1]);
130  
131         return createFromControlVectors(src[0], src[1], src[2], dest[0], dest[1],
132                 dest[2]);
133     }
134  
135     /**
136      * Creates an AffineTransformation defined by a mapping between two baselines.
137      * The computed transformation consists of:
138      * <ul>
139      * <li>a translation 
140      * from the start point of the source baseline to the start point of the destination baseline,
141      * <li>a rotation through the angle between the baselines about the destination start point,
142      * <li>and a scaling equal to the ratio of the baseline lengths.
143      * </ul>
144      * If the source baseline has zero length, an identity transformation is returned.
145      * 
146      * @param src0 the start point of the source baseline
147      * @param src1 the end point of the source baseline
148      * @param dest0 the start point of the destination baseline
149      * @param dest1 the end point of the destination baseline
150      * @return the computed transformation
151      */
152     public static AffineTransformation createFromBaseLines(
153             Coordinate src0, Coordinate src1, 
154             Coordinate dest0, Coordinate dest1) 
155     {
156         Coordinate rotPt = new Coordinate(src0.x + dest1.x - dest0.x, src0.y + dest1.y - dest0.y);
157  
158         double ang = Angle.angleBetweenOriented(src1, src0, rotPt);
159  
160         double srcDist = src1.distance(src0);
161         double destDist = dest1.distance(dest0);
162  
163         // return identity if transformation would be degenerate
164         if (srcDist == 0.0)
165             return new AffineTransformation();
166  
167         double scale = destDist / srcDist;
168  
169         AffineTransformation trans = AffineTransformation.translationInstance(
170                 -src0.x, -src0.y);
171         trans.rotate(ang);
172         trans.scale(scale, scale);
173         trans.translate(dest0.x, dest0.y);
174         return trans;
175     }
176  
177 }
178