Class AffineTransformation

   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.geom.Coordinate;
  16 import org.locationtech.jts.geom.CoordinateSequence;
  17 import org.locationtech.jts.geom.CoordinateSequenceFilter;
  18 import org.locationtech.jts.geom.Geometry;
  19 import org.locationtech.jts.util.Assert;
  20 /**
  21  * Represents an affine transformation on the 2D Cartesian plane. 
  22  * It can be used to transform a {@link Coordinate} or {@link Geometry}.
  23  * An affine transformation is a mapping of the 2D plane into itself
  24  * via a series of transformations of the following basic types:
  25  * <ul>
  26  * <li>reflection (through a line)
  27  * <li>rotation (around the origin)
  28  * <li>scaling (relative to the origin)
  29  * <li>shearing (in both the X and Y directions)
  30  * <li>translation 
  31  * </ul>
  32  * In general, affine transformations preserve straightness and parallel lines,
  33  * but do not preserve distance or shape.
  34  * <p>
  35  * An affine transformation can be represented by a 3x3 
  36  * matrix in the following form:
  37  * <blockquote><pre>
  38  * T = | m00 m01 m02 |
  39  *     | m10 m11 m12 |
  40  *     |  0   0   1  |
  41  * </pre></blockquote>
  42  * A coordinate P = (x, y) can be transformed to a new coordinate P' = (x', y')
  43  * by representing it as a 3x1 matrix and using matrix multiplication to compute:
  44  * <blockquote><pre>
  45  * | x' |  = T x | x |
  46  * | y' |        | y |
  47  * | 1  |        | 1 |
  48  * </pre></blockquote>
  49  * <h3>Transformation Composition</h3>
  50  * Affine transformations can be composed using the {@link #compose} method.
  51  * Composition is computed via multiplication of the 
  52  * transformation matrices, and is defined as:
  53  * <blockquote><pre>
  54  * A.compose(B) = T<sub>B</sub> x T<sub>A</sub>
  55  * </pre></blockquote>
  56  * This produces a transformation whose effect is that of A followed by B.
  57  * The methods {@link #reflect}, {@link #rotate}, 
  58  * {@link #scale}, {@link #shear}, and {@link #translate} 
  59  * have the effect of composing a transformation of that type with
  60  * the transformation they are invoked on.  
  61  * <p>
  62  * The composition of transformations is in general <i>not</i> commutative.
  63  * 
  64  * <h3>Transformation Inversion</h3>
  65  * Affine transformations may be invertible or non-invertible.  
  66  * If a transformation is invertible, then there exists 
  67  * an inverse transformation which when composed produces 
  68  * the identity transformation.  
  69  * The {@link #getInverse} method
  70  * computes the inverse of a transformation, if one exists.
  71  * 
  72  * @author Martin Davis
  73  *
  74  */
  75 public class AffineTransformation
  76     implements Cloneable, CoordinateSequenceFilter
  77 {
  78   
  79   /**
  80    * Creates a transformation for a reflection about the 
  81    * line (x0,y0) - (x1,y1).
  82    * 
  83    * @param x0 the x-ordinate of a point on the reflection line
  84    * @param y0 the y-ordinate of a point on the reflection line
  85    * @param x1 the x-ordinate of a another point on the reflection line
  86    * @param y1 the y-ordinate of a another point on the reflection line
  87    * @return a transformation for the reflection
  88    */
  89   public static AffineTransformation reflectionInstance(double x0, double y0, double x1, double y1)
  90   {
  91     AffineTransformation trans = new AffineTransformation();
  92     trans.setToReflection(x0, y0, x1, y1);
  93     return trans;
  94   }
  95   
  96   /**
  97    * Creates a transformation for a reflection about the 
  98    * line (0,0) - (x,y).
  99    * 
 100    * @param x the x-ordinate of a point on the reflection line
 101    * @param y the y-ordinate of a point on the reflection line
 102    * @return a transformation for the reflection
 103    */
 104   public static AffineTransformation reflectionInstance(double x, double y)
 105   {
 106     AffineTransformation trans = new AffineTransformation();
 107     trans.setToReflection(x, y);
 108     return trans;
 109   }
 110   
 111   /**
 112    * Creates a transformation for a rotation
 113    * about the origin 
 114    * by an angle <i>theta</i>.
 115    * Positive angles correspond to a rotation 
 116    * in the counter-clockwise direction.
 117    * 
 118    * @param theta the rotation angle, in radians
 119    * @return a transformation for the rotation
 120    */
 121   public static AffineTransformation rotationInstance(double theta)
 122   {
 123     return rotationInstance(Math.sin(theta), Math.cos(theta));
 124   }
 125   
 126   /**
 127    * Creates a transformation for a rotation 
 128    * by an angle <i>theta</i>,
 129    * specified by the sine and cosine of the angle.
 130    * This allows providing exact values for sin(theta) and cos(theta)
 131    * for the common case of rotations of multiples of quarter-circles. 
 132    * 
 133    * @param sinTheta the sine of the rotation angle
 134    * @param cosTheta the cosine of the rotation angle
 135    * @return a transformation for the rotation
 136    */
 137   public static AffineTransformation rotationInstance(double sinTheta, double cosTheta)
 138   {
 139     AffineTransformation trans = new AffineTransformation();
 140     trans.setToRotation(sinTheta, cosTheta);
 141     return trans;
 142   }
 143   
 144   /**
 145    * Creates a transformation for a rotation
 146    * about the point (x,y) by an angle <i>theta</i>.
 147    * Positive angles correspond to a rotation 
 148    * in the counter-clockwise direction.
 149    * 
 150    * @param theta the rotation angle, in radians
 151    * @param x the x-ordinate of the rotation point
 152    * @param y the y-ordinate of the rotation point
 153    * @return a transformation for the rotation
 154    */
 155   public static AffineTransformation rotationInstance(double theta, double x, double y)
 156   {
 157     return rotationInstance(Math.sin(theta), Math.cos(theta), x, y);
 158   }
 159   
 160   /**
 161    * Creates a transformation for a rotation 
 162    * about the point (x,y) by an angle <i>theta</i>,
 163    * specified by the sine and cosine of the angle.
 164    * This allows providing exact values for sin(theta) and cos(theta)
 165    * for the common case of rotations of multiples of quarter-circles. 
 166    * 
 167    * @param sinTheta the sine of the rotation angle
 168    * @param cosTheta the cosine of the rotation angle
 169    * @param x the x-ordinate of the rotation point
 170    * @param y the y-ordinate of the rotation point
 171    * @return a transformation for the rotation
 172    */
 173   public static AffineTransformation rotationInstance(double sinTheta, double cosTheta, double x, double y)
 174   {
 175     AffineTransformation trans = new AffineTransformation();
 176     trans.setToRotation(sinTheta, cosTheta, x, y);
 177     return trans;
 178   }
 179   
 180   /**
 181    * Creates a transformation for a scaling relative to the origin.
 182    * 
 183    * @param xScale the value to scale by in the x direction
 184    * @param yScale the value to scale by in the y direction
 185    * @return a transformation for the scaling
 186    */
 187   public static AffineTransformation scaleInstance(double xScale, double yScale)
 188   {
 189     AffineTransformation trans = new AffineTransformation();
 190     trans.setToScale(xScale, yScale);
 191     return trans;
 192   }
 193   
 194   /**
 195    * Creates a transformation for a scaling relative to the point (x,y).
 196    * 
 197    * @param xScale the value to scale by in the x direction
 198    * @param yScale the value to scale by in the y direction
 199    * @param x the x-ordinate of the point to scale around
 200    * @param y the y-ordinate of the point to scale around
 201    * @return a transformation for the scaling
 202    */
 203   public static AffineTransformation scaleInstance(double xScale, double yScale, double x, double y)
 204   {
 205     AffineTransformation trans = new AffineTransformation();
 206     trans.translate(-x, -y);
 207     trans.scale(xScale, yScale);
 208     trans.translate(x, y);
 209     return trans;
 210   }
 211   
 212  
 213   /**
 214    * Creates a transformation for a shear.
 215    * 
 216    * @param xShear the value to shear by in the x direction
 217    * @param yShear the value to shear by in the y direction
 218    * @return a transformation for the shear
 219    */
 220   public static AffineTransformation shearInstance(double xShear, double yShear)
 221   {
 222     AffineTransformation trans = new AffineTransformation();
 223     trans.setToShear(xShear, yShear);
 224     return trans;
 225   }
 226   
 227   /**
 228    * Creates a transformation for a translation.
 229    * 
 230    * @param x the value to translate by in the x direction
 231    * @param y the value to translate by in the y direction
 232    * @return a transformation for the translation
 233    */  
 234   public static AffineTransformation translationInstance(double x, double y)
 235   {
 236     AffineTransformation trans = new AffineTransformation();
 237     trans.setToTranslation(x, y);
 238     return trans;
 239   }
 240   
 241   // affine matrix entries
 242   // (bottom row is always [ 0 0 1 ])
 243   private double m00;
 244   private double m01;
 245   private double m02;
 246   private double m10;
 247   private double m11;
 248   private double m12;
 249   
 250   /**
 251    * Constructs a new identity transformation
 252    */
 253   public AffineTransformation()
 254   {
 255     setToIdentity();
 256   }
 257   
 258   /**
 259    * Constructs a new transformation whose 
 260    * matrix has the specified values.
 261    * 
 262    * @param matrix an array containing the 6 values { m00, m01, m02, m10, m11, m12 }
 263    * @throws NullPointerException if matrix is null
 264    * @throws ArrayIndexOutOfBoundsException if matrix is too small 
 265    */
 266   public AffineTransformation(double[] matrix)
 267   {
 268     m00 = matrix[0];
 269     m01 = matrix[1];
 270     m02 = matrix[2];
 271     m10 = matrix[3];
 272     m11 = matrix[4];
 273     m12 = matrix[5];
 274   }
 275   
 276   /**
 277    * Constructs a new transformation whose 
 278    * matrix has the specified values.
 279    * 
 280    * @param m00 the entry for the [0, 0] element in the transformation matrix 
 281    * @param m01 the entry for the [0, 1] element in the transformation matrix
 282    * @param m02 the entry for the [0, 2] element in the transformation matrix
 283    * @param m10 the entry for the [1, 0] element in the transformation matrix
 284    * @param m11 the entry for the [1, 1] element in the transformation matrix
 285    * @param m12 the entry for the [1, 2] element in the transformation matrix
 286    */
 287   public AffineTransformation(double m00,
 288       double m01,
 289       double m02,
 290       double m10,
 291       double m11,
 292       double m12)
 293   {
 294     setTransformation(m00, m01, m02, m10, m11, m12);
 295   }
 296   
 297   /**
 298    * Constructs a transformation which is
 299    * a copy of the given one.
 300    * 
 301    * @param trans the transformation to copy
 302    */
 303   public AffineTransformation(AffineTransformation trans)
 304   {
 305     setTransformation(trans);
 306   }
 307   
 308   /**
 309    * Constructs a transformation
 310    * which maps the given source
 311    * points into the given destination points.
 312    * 
 313    * @param src0 source point 0
 314    * @param src1 source point 1
 315    * @param src2 source point 2
 316    * @param dest0 the mapped point for source point 0
 317    * @param dest1 the mapped point for source point 1
 318    * @param dest2 the mapped point for source point 2
 319    * 
 320    */
 321   public AffineTransformation(Coordinate src0,
 322       Coordinate src1,
 323       Coordinate src2,
 324       Coordinate dest0,
 325       Coordinate dest1,
 326       Coordinate dest2)
 327   {
 328   }
 329   
 330   /**
 331    * Sets this transformation to be the identity transformation.
 332    * The identity transformation has the matrix:
 333    * <blockquote><pre>
 334    * | 1 0 0 |
 335    * | 0 1 0 |
 336    * | 0 0 1 |
 337    * </pre></blockquote>
 338    * @return this transformation, with an updated matrix
 339    */
 340   public AffineTransformation setToIdentity()
 341   {
 342     m00 = 1.0;    m01 = 0.0;  m02 = 0.0;
 343     m10 = 0.0;    m11 = 1.0;  m12 = 0.0;
 344     return this;
 345   }
 346   
 347   /**
 348    * Sets this transformation's matrix to have the given values.
 349    * 
 350    * @param m00 the entry for the [0, 0] element in the transformation matrix 
 351    * @param m01 the entry for the [0, 1] element in the transformation matrix
 352    * @param m02 the entry for the [0, 2] element in the transformation matrix
 353    * @param m10 the entry for the [1, 0] element in the transformation matrix
 354    * @param m11 the entry for the [1, 1] element in the transformation matrix
 355    * @param m12 the entry for the [1, 2] element in the transformation matrix
 356    * @return this transformation, with an updated matrix
 357    */
 358   public AffineTransformation setTransformation(double m00,
 359       double m01,
 360       double m02,
 361       double m10,
 362       double m11,
 363       double m12)
 364   {
 365     this.m00 = m00;
 366     this.m01 = m01;
 367     this.m02 = m02;
 368     this.m10 = m10;
 369     this.m11 = m11;
 370     this.m12 = m12;
 371     return this;
 372   }
 373   
 374   /**
 375    * Sets this transformation to be a copy of the given one
 376    * 
 377    * @param trans a transformation to copy
 378    * @return this transformation, with an updated matrix
 379    */
 380   public AffineTransformation setTransformation(AffineTransformation trans)
 381   {
 382     m00 = trans.m00;    m01 = trans.m01;  m02 = trans.m02;
 383     m10 = trans.m10;    m11 = trans.m11;  m12 = trans.m12; 
 384     return this;
 385   }
 386   
 387   /**
 388    * Gets an array containing the entries
 389    * of the transformation matrix.
 390    * Only the 6 non-trivial entries are returned,
 391    * in the sequence:
 392    * <pre>
 393    * m00, m01, m02, m10, m11, m12
 394    * </pre>
 395    * 
 396    * @return an array of length 6
 397    */
 398   public double[] getMatrixEntries()
 399   {
 400     return new double[] { m00, m01, m02, m10, m11, m12 };
 401   }
 402   
 403   /**
 404   * Computes the determinant of the transformation matrix. 
 405   * The determinant is computed as:
 406   * <blockquote><pre>
 407   * | m00 m01 m02 |
 408   * | m10 m11 m12 | = m00 * m11 - m01 * m10
 409   * |  0   0   1  |
 410   * </pre></blockquote>
 411   * If the determinant is zero, 
 412   * the transform is singular (not invertible), 
 413   * and operations which attempt to compute
 414   * an inverse will throw a <tt>NoninvertibleTransformException</tt>. 
 415
 416   * @return the determinant of the transformation
 417   * @see #getInverse()
 418   */
 419   public double getDeterminant()
 420   {
 421     return m00 * m11 - m01 * m10;
 422   }
 423  
 424   /**
 425    * Computes the inverse of this transformation, if one
 426    * exists.
 427    * The inverse is the transformation which when 
 428    * composed with this one produces the identity 
 429    * transformation.
 430    * A transformation has an inverse if and only if it
 431    * is not singular (i.e. its
 432    * determinant is non-zero).
 433    * Geometrically, an transformation is non-invertible
 434    * if it maps the plane to a line or a point.
 435    * If no inverse exists this method
 436    * will throw a <tt>NoninvertibleTransformationException</tt>.
 437    * <p>
 438    * The matrix of the inverse is equal to the 
 439    * inverse of the matrix for the transformation.
 440    * It is computed as follows:
 441    * <blockquote><pre>  
 442    *                 1    
 443    * inverse(A)  =  ---   x  adjoint(A) 
 444    *                det 
 445    *
 446    *
 447    *             =   1       |  m11  -m01   m01*m12-m02*m11  |
 448    *                ---   x  | -m10   m00  -m00*m12+m10*m02  |
 449    *                det      |  0     0     m00*m11-m10*m01  |
 450    *
 451    *
 452    *
 453    *             = |  m11/det  -m01/det   m01*m12-m02*m11/det |
 454    *               | -m10/det   m00/det  -m00*m12+m10*m02/det |
 455    *               |   0           0          1               |
 456    *
 457    * </pre></blockquote>  
 458    *  
 459    * @return a new inverse transformation
 460    * @throws NoninvertibleTransformationException
 461    * @see #getDeterminant()
 462    */
 463   public AffineTransformation getInverse()
 464         throws NoninvertibleTransformationException
 465   {
 466     double det = getDeterminant();
 467     if (det == 0)
 468         throw new NoninvertibleTransformationException("Transformation is non-invertible");
 469     
 470     double im00 = m11 / det;
 471     double im10 = -m10 / det;
 472     double im01 = -m01 / det;
 473     double im11 = m00 / det;
 474     double im02 = (m01 * m12 - m02 * m11) / det;
 475     double im12 = (-m00 * m12 + m10 * m02) / det;
 476     
 477     return new AffineTransformation(im00, im01, im02, im10, im11, im12);
 478   }
 479   
 480   /**
 481    * Explicitly computes the math for a reflection.  May not work.
 482    * @param x0 the X ordinate of one point on the reflection line
 483    * @param y0 the Y ordinate of one point on the reflection line
 484    * @param x1 the X ordinate of another point on the reflection line
 485    * @param y1 the Y ordinate of another point on the reflection line
 486    * @return this transformation, with an updated matrix
 487    */
 488   public AffineTransformation setToReflectionBasic(double x0, double y0, double x1, double y1)
 489   {
 490     if (x0 == x1 && y0 == y1) {
 491       throw new IllegalArgumentException("Reflection line points must be distinct");
 492     }
 493     double dx = x1 - x0;
 494     double dy = y1 - y0;
 495     double d = Math.sqrt(dx * dx + dy * dy);
 496     double sin = dy / d;
 497     double cos = dx / d;
 498     double cs2 = 2 * sin * cos;
 499     double c2s2 = cos * cos - sin * sin;
 500     m00 = c2s2;   m01 = cs2;    m02 = 0.0;
 501     m10 = cs2;    m11 = -c2s2;  m12 = 0.0;
 502     return this;
 503   }
 504   
 505   /**
 506    * Sets this transformation to be a reflection 
 507    * about the line defined by a line <tt>(x0,y0) - (x1,y1)</tt>.
 508    * 
 509    * @param x0 the X ordinate of one point on the reflection line
 510    * @param y0 the Y ordinate of one point on the reflection line
 511    * @param x1 the X ordinate of another point on the reflection line
 512    * @param y1 the Y ordinate of another point on the reflection line
 513    * @return this transformation, with an updated matrix
 514    */
 515   public AffineTransformation setToReflection(double x0, double y0, double x1, double y1)
 516   {
 517     if (x0 == x1 && y0 == y1) {
 518       throw new IllegalArgumentException("Reflection line points must be distinct");
 519     }
 520     // translate line vector to origin
 521     setToTranslation(-x0, -y0);
 522     
 523     // rotate vector to positive x axis direction
 524     double dx = x1 - x0;
 525     double dy = y1 - y0;
 526     double d = Math.sqrt(dx * dx + dy * dy);
 527     double sin = dy / d;
 528     double cos = dx / d;
 529     rotate(-sin, cos);
 530     // reflect about the x axis
 531     scale(1, -1);
 532     // rotate back
 533     rotate(sin, cos);
 534     // translate back
 535     translate(x0, y0);
 536     return this;
 537   }
 538   
 539   /**
 540    * Sets this transformation to be a reflection 
 541    * about the line defined by vector (x,y).
 542    * The transformation for a reflection
 543    * is computed by:
 544    * <blockquote><pre>
 545    * d = sqrt(x<sup>2</sup> + y<sup>2</sup>)  
 546    * sin = y / d;
 547    * cos = x / d;
 548    * 
 549    * T<sub>ref</sub> = T<sub>rot(sin, cos)</sub> x T<sub>scale(1, -1)</sub> x T<sub>rot(-sin, cos)</sub>
 550    * </pre></blockquote> 
 551    * 
 552    * @param x the x-component of the reflection line vector
 553    * @param y the y-component of the reflection line vector
 554    * @return this transformation, with an updated matrix
 555    */
 556   public AffineTransformation setToReflection(double x, double y)
 557   {
 558     if (x == 0.0 && y == 0.0) {
 559       throw new IllegalArgumentException("Reflection vector must be non-zero");
 560     }
 561     
 562     /**
 563      * Handle special case - x = y.
 564      * This case is specified explicitly to avoid roundoff error.
 565      */
 566     if (x == y) {
 567       m00 = 0.0;
 568       m01 = 1.0;
 569       m02 = 0.0;
 570       m10 = 1.0;
 571       m11 = 0.0;
 572       m12 = 0.0;
 573       return this;
 574     }
 575     
 576     // rotate vector to positive x axis direction
 577     double d = Math.sqrt(x * x + y * y);
 578     double sin = y / d;
 579     double cos = x / d;
 580     rotate(-sin, cos);
 581     // reflect about the x-axis
 582     scale(1, -1);
 583     // rotate back
 584     rotate(sin, cos);
 585     return this;
 586   }
 587   
 588   /**
 589    * Sets this transformation to be a rotation around the origin.
 590    * A positive rotation angle corresponds 
 591    * to a counter-clockwise rotation.
 592    * The transformation matrix for a rotation
 593    * by an angle <tt>theta</tt>
 594    * has the value:
 595    * <blockquote><pre>  
 596    * |  cos(theta)  -sin(theta)   0 |
 597    * |  sin(theta)   cos(theta)   0 |
 598    * |           0            0   1 |
 599    * </pre></blockquote> 
 600    * 
 601    * @param theta the rotation angle, in radians
 602    * @return this transformation, with an updated matrix
 603    */
 604   public AffineTransformation setToRotation(double theta)
 605   {
 606     setToRotation(Math.sin(theta), Math.cos(theta));
 607     return this;
 608   }
 609   
 610   /**
 611    * Sets this transformation to be a rotation around the origin
 612    * by specifying the sin and cos of the rotation angle directly.
 613    * The transformation matrix for the rotation
 614    * has the value:
 615    * <blockquote><pre>  
 616    * |  cosTheta  -sinTheta   0 |
 617    * |  sinTheta   cosTheta   0 |
 618    * |         0          0   1 |
 619    * </pre></blockquote> 
 620    * 
 621    * @param sinTheta the sine of the rotation angle
 622    * @param cosTheta the cosine of the rotation angle
 623    * @return this transformation, with an updated matrix
 624    */
 625   public AffineTransformation setToRotation(double sinTheta, double cosTheta)
 626   {
 627     m00 = cosTheta;    m01 = -sinTheta;  m02 = 0.0;
 628     m10 = sinTheta;    m11 = cosTheta;   m12 = 0.0;
 629     return this;
 630   }
 631   
 632   /**
 633    * Sets this transformation to be a rotation
 634    * around a given point (x,y).
 635    * A positive rotation angle corresponds 
 636    * to a counter-clockwise rotation.
 637    * The transformation matrix for a rotation
 638    * by an angle <tt>theta</tt>
 639    * has the value:
 640    * <blockquote><pre>  
 641    * |  cosTheta  -sinTheta   x-x*cos+y*sin |
 642    * |  sinTheta   cosTheta   y-x*sin-y*cos |
 643    * |           0            0   1 |
 644    * </pre></blockquote> 
 645    * 
 646    * @param theta the rotation angle, in radians
 647    * @param x the x-ordinate of the rotation point
 648    * @param y the y-ordinate of the rotation point
 649    * @return this transformation, with an updated matrix
 650    */
 651   public AffineTransformation setToRotation(double theta, double x, double y)
 652   {
 653     setToRotation(Math.sin(theta), Math.cos(theta), x, y);
 654     return this;
 655   }
 656   
 657  
 658   /**
 659    * Sets this transformation to be a rotation
 660    * around a given point (x,y)
 661    * by specifying the sin and cos of the rotation angle directly.
 662    * The transformation matrix for the rotation
 663    * has the value:
 664    * <blockquote><pre>  
 665    * |  cosTheta  -sinTheta   x-x*cos+y*sin |
 666    * |  sinTheta   cosTheta   y-x*sin-y*cos |
 667    * |         0          0         1       |
 668    * </pre></blockquote> 
 669    * 
 670    * @param sinTheta the sine of the rotation angle
 671    * @param cosTheta the cosine of the rotation angle
 672    * @param x the x-ordinate of the rotation point
 673    * @param y the y-ordinate of the rotation point
 674    * @return this transformation, with an updated matrix
 675    */
 676   public AffineTransformation setToRotation(double sinTheta, double cosTheta, double x, double y)
 677   {
 678     m00 = cosTheta;    m01 = -sinTheta;  m02 = x - x * cosTheta + y * sinTheta;
 679     m10 = sinTheta;    m11 = cosTheta;   m12 = y - x * sinTheta - y * cosTheta;
 680     return this;
 681   }
 682   
 683   /**
 684    * Sets this transformation to be a scaling.
 685    * The transformation matrix for a scale
 686    * has the value:
 687    * <blockquote><pre>  
 688    * |  xScale      0  dx |
 689    * |  1      yScale  dy |
 690    * |  0           0   1 |
 691    * </pre></blockquote> 
 692    * 
 693    * @param xScale the amount to scale x-ordinates by
 694    * @param yScale the amount to scale y-ordinates by
 695    * @return this transformation, with an updated matrix
 696    */
 697   public AffineTransformation setToScale(double xScale, double yScale)
 698   {
 699     m00 = xScale;   m01 = 0.0;      m02 = 0.0;
 700     m10 = 0.0;      m11 = yScale;   m12 = 0.0;
 701     return this;
 702   }
 703   
 704   /**
 705    * Sets this transformation to be a shear.
 706    * The transformation matrix for a shear 
 707    * has the value:
 708    * <blockquote><pre>  
 709    * |  1      xShear  0 |
 710    * |  yShear      1  0 |
 711    * |  0           0  1 |
 712    * </pre></blockquote> 
 713    * Note that a shear of (1, 1) is <i>not</i> 
 714    * equal to shear(1, 0) composed with shear(0, 1).
 715    * Instead, shear(1, 1) corresponds to a mapping onto the 
 716    * line x = y.
 717    * 
 718    * @param xShear the x component to shear by
 719    * @param yShear the y component to shear by
 720    * @return this transformation, with an updated matrix
 721    */
 722   public AffineTransformation setToShear(double xShear, double yShear)
 723   {
 724     m00 = 1.0;      m01 = xShear;      m02 = 0.0;
 725     m10 = yShear;   m11 = 1.0;         m12 = 0.0;
 726     return this;
 727   }
 728   
 729   /**
 730    * Sets this transformation to be a translation.
 731    * For a translation by the vector (x, y)
 732    * the transformation matrix has the value:
 733    * <blockquote><pre>  
 734    * |  1  0  dx |
 735    * |  1  0  dy |
 736    * |  0  0   1 |
 737    * </pre></blockquote> 
 738    * @param dx the x component to translate by
 739    * @param dy the y component to translate by
 740    * @return this transformation, with an updated matrix
 741    */
 742   public AffineTransformation setToTranslation(double dx, double dy)
 743   {
 744     m00 = 1.0;  m01 = 0.0m02 = dx;
 745     m10 = 0.0;  m11 = 1.0m12 = dy;
 746     return this;
 747   }
 748   
 749   /**
 750    * Updates the value of this transformation
 751    * to that of a reflection transformation composed 
 752    * with the current value.
 753    * 
 754    * @param x0 the x-ordinate of a point on the line to reflect around
 755    * @param y0 the y-ordinate of a point on the line to reflect around
 756    * @param x1 the x-ordinate of a point on the line to reflect around
 757    * @param y1 the y-ordinate of a point on the line to reflect around
 758    * @return this transformation, with an updated matrix
 759    */
 760   public AffineTransformation reflect(double x0, double y0, double x1, double y1)
 761   {
 762     compose(reflectionInstance(x0, y0, x1, y1));
 763     return this;
 764   }
 765   
 766   /**
 767    * Updates the value of this transformation
 768    * to that of a reflection transformation composed 
 769    * with the current value.
 770    * 
 771    * @param x the x-ordinate of the line to reflect around
 772    * @param y the y-ordinate of the line to reflect around
 773    * @return this transformation, with an updated matrix
 774    */
 775   public AffineTransformation reflect(double x, double y)
 776   {
 777     compose(reflectionInstance(x, y));
 778     return this;
 779   }
 780   
 781   /**
 782    * Updates the value of this transformation
 783    * to that of a rotation transformation composed 
 784    * with the current value.
 785    * Positive angles correspond to a rotation 
 786    * in the counter-clockwise direction.
 787    * 
 788    * @param theta the angle to rotate by, in radians
 789    * @return this transformation, with an updated matrix
 790    */
 791   public AffineTransformation rotate(double theta)
 792   {
 793     compose(rotationInstance(theta));
 794     return this;
 795   }
 796   
 797   /**
 798    * Updates the value of this transformation
 799    * to that of a rotation around the origin composed 
 800    * with the current value,
 801    * with the sin and cos of the rotation angle specified directly.
 802    * 
 803    * @param sinTheta the sine of the angle to rotate by
 804    * @param cosTheta the cosine of the angle to rotate by
 805    * @return this transformation, with an updated matrix
 806    */
 807   public AffineTransformation rotate(double sinTheta, double cosTheta)
 808   {
 809     compose(rotationInstance(sinTheta, cosTheta));
 810     return this;
 811   }
 812   
 813   /**
 814    * Updates the value of this transformation
 815    * to that of a rotation around a given point composed 
 816    * with the current value.
 817    * Positive angles correspond to a rotation 
 818    * in the counter-clockwise direction.
 819    * 
 820    * @param theta the angle to rotate by, in radians
 821    * @param x the x-ordinate of the rotation point
 822    * @param y the y-ordinate of the rotation point
 823    * @return this transformation, with an updated matrix
 824    */
 825   public AffineTransformation rotate(double theta, double x, double y)
 826   {
 827     compose(rotationInstance(theta, x, y));
 828     return this;
 829   }
 830   
 831   /**
 832    * Updates the value of this transformation
 833    * to that of a rotation around a given point composed 
 834    * with the current value,
 835    * with the sin and cos of the rotation angle specified directly.
 836    * 
 837    * @param sinTheta the sine of the angle to rotate by
 838    * @param cosTheta the cosine of the angle to rotate by
 839    * @param x the x-ordinate of the rotation point
 840    * @param y the y-ordinate of the rotation point
 841    * @return this transformation, with an updated matrix
 842    */
 843   public AffineTransformation rotate(double sinTheta, double cosTheta, double x, double y)
 844   {
 845     compose(rotationInstance(sinTheta, cosTheta, x, y));
 846     return this;
 847   }
 848   
 849   /**
 850    * Updates the value of this transformation
 851    * to that of a scale transformation composed 
 852    * with the current value.
 853    * 
 854    * @param xScale the value to scale by in the x direction
 855    * @param yScale the value to scale by in the y direction
 856    * @return this transformation, with an updated matrix
 857    */
 858   public AffineTransformation scale(double xScale, double yScale)
 859   {
 860     compose(scaleInstance(xScale, yScale));
 861     return this;
 862   }
 863   
 864   /**
 865    * Updates the value of this transformation
 866    * to that of a shear transformation composed 
 867    * with the current value.
 868    * 
 869    * @param xShear the value to shear by in the x direction
 870    * @param yShear the value to shear by in the y direction
 871    * @return this transformation, with an updated matrix
 872    */
 873   public AffineTransformation shear(double xShear, double yShear)
 874   {
 875     compose(shearInstance(xShear, yShear));
 876     return this;
 877   }
 878  
 879   /**
 880    * Updates the value of this transformation
 881    * to that of a translation transformation composed 
 882    * with the current value.
 883    * 
 884    * @param x the value to translate by in the x direction
 885    * @param y the value to translate by in the y direction
 886    * @return this transformation, with an updated matrix
 887    */
 888   public AffineTransformation translate(double x, double y)
 889   {
 890     compose(translationInstance(x, y));
 891     return this;
 892   }
 893   
 894  
 895   /**
 896    * Updates this transformation to be
 897    * the composition of this transformation with the given {@link AffineTransformation}. 
 898    * This produces a transformation whose effect 
 899    * is equal to applying this transformation 
 900    * followed by the argument transformation.
 901    * Mathematically,
 902    * <blockquote><pre>
 903    * A.compose(B) = T<sub>B</sub> x T<sub>A</sub>
 904    * </pre></blockquote>
 905    * 
 906    * @param trans an affine transformation
 907    * @return this transformation, with an updated matrix
 908    */
 909   public AffineTransformation compose(AffineTransformation trans)
 910   {
 911     double mp00 = trans.m00 * m00 + trans.m01 * m10;
 912     double mp01 = trans.m00 * m01 + trans.m01 * m11;
 913     double mp02 = trans.m00 * m02 + trans.m01 * m12 + trans.m02;
 914     double mp10 = trans.m10 * m00 + trans.m11 * m10;
 915     double mp11 = trans.m10 * m01 + trans.m11 * m11;
 916     double mp12 = trans.m10 * m02 + trans.m11 * m12 + trans.m12;
 917     m00 = mp00;
 918     m01 = mp01;
 919     m02 = mp02;
 920     m10 = mp10;
 921     m11 = mp11;
 922     m12 = mp12;
 923     return this;
 924   }
 925   
 926   /**
 927    * Updates this transformation to be the composition 
 928    * of a given {@link AffineTransformation} with this transformation.
 929    * This produces a transformation whose effect 
 930    * is equal to applying the argument transformation 
 931    * followed by this transformation.
 932    * Mathematically,
 933    * <blockquote><pre>
 934    * A.composeBefore(B) = T<sub>A</sub> x T<sub>B</sub>
 935    * </pre></blockquote>
 936    * 
 937    * @param trans an affine transformation
 938    * @return this transformation, with an updated matrix
 939    */
 940   public AffineTransformation composeBefore(AffineTransformation trans)
 941   {
 942     double mp00 = m00 * trans.m00 + m01 * trans.m10;
 943     double mp01 = m00 * trans.m01 + m01 * trans.m11;
 944     double mp02 = m00 * trans.m02 + m01 * trans.m12 + m02;
 945     double mp10 = m10 * trans.m00 + m11 * trans.m10;
 946     double mp11 = m10 * trans.m01 + m11 * trans.m11;
 947     double mp12 = m10 * trans.m02 + m11 * trans.m12 + m12;
 948     m00 = mp00;
 949     m01 = mp01;
 950     m02 = mp02;
 951     m10 = mp10;
 952     m11 = mp11;
 953     m12 = mp12;
 954     return this;
 955   }
 956   
 957   /**
 958    * Applies this transformation to the <tt>src</tt> coordinate
 959    * and places the results in the <tt>dest</tt> coordinate
 960    * (which may be the same as the source).
 961    * 
 962    * @param src the coordinate to transform
 963    * @param dest the coordinate to accept the results 
 964    * @return the <tt>dest</tt> coordinate
 965    */
 966   public Coordinate transform(Coordinate src, Coordinate dest)
 967   {
 968     double xp = m00 * src.x + m01 * src.y + m02;
 969     double yp = m10 * src.x + m11 * src.y + m12;
 970     dest.x = xp;
 971     dest.y = yp;
 972     return dest;
 973   }
 974   
 975   /**
 976    * Creates a new {@link Geometry} which is the result
 977    * of this transformation applied to the input Geometry.
 978    * 
 979    *@param g  a <code>Geometry</code>
 980    *@return a transformed Geometry
 981    */
 982   public Geometry transform(Geometry g)
 983   {
 984     Geometry g2 = g.copy();
 985     g2.apply(this);
 986     return g2;    
 987   }
 988   
 989   /**
 990    * Applies this transformation to the i'th coordinate
 991    * in the given CoordinateSequence.
 992    * 
 993    *@param seq  a <code>CoordinateSequence</code>
 994    *@param i the index of the coordinate to transform
 995    */
 996   public void transform(CoordinateSequence seq, int i)
 997   {
 998     double xp = m00 * seq.getOrdinate(i, 0) + m01 * seq.getOrdinate(i, 1) + m02;
 999     double yp = m10 * seq.getOrdinate(i, 0) + m11 * seq.getOrdinate(i, 1) + m12;
1000     seq.setOrdinate(i, 0, xp);
1001     seq.setOrdinate(i, 1, yp);  
1002   }
1003   
1004   /**
1005    * Transforms the i'th coordinate in the input sequence
1006    * 
1007    *@param seq  a <code>CoordinateSequence</code>
1008    *@param i the index of the coordinate to transform
1009    */
1010   public void filter(CoordinateSequence seq, int i)
1011   {
1012     transform(seq, i);
1013   }
1014   
1015   public boolean isGeometryChanged()
1016   {
1017     return true;
1018   }
1019   
1020   /**
1021    * Reports that this filter should continue to be executed until 
1022    * all coordinates have been transformed.
1023    * 
1024    * @return false
1025    */
1026   public boolean isDone() 
1027   {
1028     return false;
1029   }
1030   
1031   /**
1032   * Tests if this transformation is the identity transformation.
1033   *
1034   * @return true if this is the identity transformation
1035   */
1036   public boolean isIdentity()
1037   {
1038     return (m00 == 1 && m01 == 0 && m02 == 0
1039           && m10 == 0 && m11 == 1 && m12 == 0);
1040   }
1041   
1042  /**
1043   * Tests if an object is an
1044   * <tt>AffineTransformation</tt>
1045   * and has the same matrix as 
1046   * this transformation.
1047   * 
1048   * @param obj an object to test
1049   * @return true if the given object is equal to this object
1050   */
1051   public boolean equals(Object obj)
1052   {
1053     if (obj == nullreturn false;
1054     if (! (obj instanceof AffineTransformation))
1055       return false;
1056     
1057     AffineTransformation trans = (AffineTransformation) obj;
1058     return m00 == trans.m00
1059     && m01 == trans.m01
1060     && m02 == trans.m02
1061     && m10 == trans.m10
1062     && m11 == trans.m11
1063     && m12 == trans.m12;
1064   }
1065   
1066   /**
1067    * Gets a text representation of this transformation.
1068    * The string is of the form:
1069    * <pre>
1070    * AffineTransformation[[m00, m01, m02], [m10, m11, m12]]
1071    * </pre>
1072    * 
1073    * @return a string representing this transformation
1074    * 
1075    */
1076   public String toString()
1077   {
1078     return "AffineTransformation[[" + m00 + ", " + m01 + ", " + m02 
1079     + "], ["
1080     + m10 + ", " + m11 + ", " + m12 + "]]";
1081   }
1082   
1083   /**
1084    * Clones this transformation
1085    * 
1086    * @return a copy of this transformation
1087    */
1088   public Object clone()
1089   {
1090       try {
1091           return super.clone();
1092       } catch(Exception ex) {
1093           Assert.shouldNeverReachHere();
1094       }
1095       return null;
1096   }
1097 }
1098