001: package com.vividsolutions.jump.geom;
002:
003: import com.vividsolutions.jts.geom.*;
004: import com.vividsolutions.jump.I18N;
005:
006: /**
007: * This class represents a affine transformation on the 2D Cartesian plane.
008: * It can be used to transform a {@link Coordinate} or {@link Geometry}.
009: * An affine transformation is a mapping of the 2D plane into itself
010: * via a series of transformations of the following basic types:
011: * <ul>
012: * <li>reflection
013: * <li>rotation
014: * <li>scaling
015: * <li>shearing
016: * <li>translation
017: * </ul>
018: * In general, affine transformations preserve straightness and parallel lines,
019: * but do not preserve distance or shape.
020: * <p>
021: * An affine transformation can be represented by a 3x3
022: * matrix in the following form:
023: * <blockquote><pre>
024: * T = | m00 m01 m02 |
025: * | m10 m11 m12 |
026: * | 0 0 1 |
027: * </pre></blockquote>
028: * A coordinate P = (x, y) can be transformed to a new coordinate P' = (x', y')
029: * by representing it as a 3x1 matrix and using matrix multiplication to compute:
030: * <blockquote><pre>
031: * | x' | = T x | x |
032: * | y' | | y |
033: * | 1 | | 1 |
034: * </pre></blockquote>
035: * Affine transformations can be composed using the {@link #compose} method.
036: * Composition is not commutative.
037: * Composition is computed via multiplication of the
038: * transformation matrices as follows:
039: * <blockquote><pre>
040: * A.compose(B) = T<sub>B</sub> x T<sub>A</sub>
041: * </pre></blockquote>
042: * This produces a transformation whose effect is that of A followed by B.
043: * The methods {@link #reflect}, {@link #rotate}, {@link #scale}, {@link #shear}, and {@link #translate}
044: * have the effect of composing a transformation of that type with
045: * the transformation they are applied to.
046: *
047: * @author Martin Davis
048: *
049: */
050: public class AffineTransformation implements Cloneable,
051: CoordinateFilter {
052:
053: /**
054: * Creates a transformation for a reflection about the
055: * line (x0,y0) - (x1,y1).
056: *
057: * @param x0 the x-ordinate of a point on the reflection line
058: * @param y0 the y-ordinate of a point on the reflection line
059: * @param x1 the x-ordinate of a another point on the reflection line
060: * @param y1 the y-ordinate of a another point on the reflection line
061: * @return a transformation for the reflection
062: */
063: public static AffineTransformation reflectionInstance(double x0,
064: double y0, double x1, double y1) {
065: AffineTransformation trans = new AffineTransformation();
066: trans.setToReflection(x0, y0, x1, y1);
067: return trans;
068: }
069:
070: /**
071: * Creates a transformation for a reflection about the
072: * line (0,0) - (x,y).
073: *
074: * @param x the x-ordinate of a point on the reflection line
075: * @param y the y-ordinate of a point on the reflection line
076: * @return a transformation for the reflection
077: */
078: public static AffineTransformation reflectionInstance(double x,
079: double y) {
080: AffineTransformation trans = new AffineTransformation();
081: trans.setToReflection(x, y);
082: return trans;
083: }
084:
085: /**
086: * Creates a transformation for a rotation
087: * about the origin
088: * by an angle <i>theta</i>.
089: * Positive angles correspond to a rotation
090: * in the counter-clockwise direction.
091: *
092: * @param theta the rotation angle, in radians
093: * @return a transformation for the rotation
094: */
095: public static AffineTransformation rotationInstance(double theta) {
096: return rotationInstance(Math.sin(theta), Math.cos(theta));
097: }
098:
099: /**
100: * Creates a transformation for a rotation
101: * by an angle <i>theta</i>,
102: * specified by the sine and cosine of the angle.
103: * This allows providing exact values for sin(theta) and cos(theta)
104: * for the common case of rotations of multiples of quarter-circles.
105: *
106: * @param sinTheta the sine of the rotation angle
107: * @param cosTheta the cosine of the rotation angle
108: * @return a transformation for the rotation
109: */
110: public static AffineTransformation rotationInstance(
111: double sinTheta, double cosTheta) {
112: AffineTransformation trans = new AffineTransformation();
113: trans.setToRotation(sinTheta, cosTheta);
114: return trans;
115: }
116:
117: public static AffineTransformation scaleInstance(double xScale,
118: double yScale) {
119: AffineTransformation trans = new AffineTransformation();
120: trans.setToScale(xScale, yScale);
121: return trans;
122: }
123:
124: public static AffineTransformation shearInstance(double xShear,
125: double yShear) {
126: AffineTransformation trans = new AffineTransformation();
127: trans.setToShear(xShear, yShear);
128: return trans;
129: }
130:
131: public static AffineTransformation translationInstance(double x,
132: double y) {
133: AffineTransformation trans = new AffineTransformation();
134: trans.setToTranslation(x, y);
135: return trans;
136: }
137:
138: // affine matrix entries
139: // (bottom row is always [ 0 0 1 ])
140: private double m00;
141: private double m01;
142: private double m02;
143: private double m10;
144: private double m11;
145: private double m12;
146:
147: /**
148: * Constructs a new identity transformation
149: *
150: */
151: public AffineTransformation() {
152: setToIdentity();
153: }
154:
155: /**
156: * Constructs a new transformation whose
157: * matrix has the specified values.
158: *
159: * @param matrix an array containing the 6 values { m00, m01, m02, m10, m11, m12 }
160: * @throws NullPointerException if matrix is null
161: * @throws ArrayIndexOutOfBoundsException if matrix is too small
162: */
163: public AffineTransformation(double[] matrix) {
164: m00 = matrix[0];
165: m01 = matrix[1];
166: m02 = matrix[2];
167: m10 = matrix[3];
168: m11 = matrix[4];
169: m12 = matrix[5];
170: }
171:
172: /**
173: * Constructs a new transformation whose
174: * matrix has the specified values.
175: *
176: * @param m00 the entry for the [0, 0] element in the transformation matrix
177: * @param m01 the entry for the [0, 1] element in the transformation matrix
178: * @param m02 the entry for the [0, 2] element in the transformation matrix
179: * @param m10 the entry for the [1, 0] element in the transformation matrix
180: * @param m11 the entry for the [1, 1] element in the transformation matrix
181: * @param m12 the entry for the [1, 2] element in the transformation matrix
182: */
183: public AffineTransformation(double m00, double m01, double m02,
184: double m10, double m11, double m12) {
185: setTransformation(m00, m01, m02, m10, m11, m12);
186: }
187:
188: /**
189: * Constructs a transformation which is
190: * a copy of the given one.
191: *
192: * @param trans the transformation to copy
193: */
194: public AffineTransformation(AffineTransformation trans) {
195: setTransformation(trans);
196: }
197:
198: /**
199: * Constructs a transformation
200: * which maps the given source
201: * points into the given destination points.
202: *
203: * @param src0 source point 0
204: * @param src1 source point 1
205: * @param src2 source point 2
206: * @param dest0 the mapped point for source point 0
207: * @param dest1 the mapped point for source point 1
208: * @param dest2 the mapped point for source point 2
209: *
210: */
211: public AffineTransformation(Coordinate src0, Coordinate src1,
212: Coordinate src2, Coordinate dest0, Coordinate dest1,
213: Coordinate dest2) {
214: }
215:
216: /**
217: * Sets this transformation to be the identity transformation.
218: * The identity transformation has the matrix:
219: * <blockquote><pre>
220: * | 1 0 0 |
221: * | 0 1 0 |
222: * | 0 0 1 |
223: * </pre></blockquote>
224: * @return this transformation, with an updated matrix
225: */
226: public AffineTransformation setToIdentity() {
227: m00 = 1.0;
228: m01 = 0.0;
229: m02 = 0.0;
230: m10 = 0.0;
231: m11 = 1.0;
232: m12 = 0.0;
233: return this ;
234: }
235:
236: /**
237: * Sets this transformation's matrix to have the given values.
238: *
239: * @param m00 the entry for the [0, 0] element in the transformation matrix
240: * @param m01 the entry for the [0, 1] element in the transformation matrix
241: * @param m02 the entry for the [0, 2] element in the transformation matrix
242: * @param m10 the entry for the [1, 0] element in the transformation matrix
243: * @param m11 the entry for the [1, 1] element in the transformation matrix
244: * @param m12 the entry for the [1, 2] element in the transformation matrix
245: * @return this transformation, with an updated matrix
246: */
247: public AffineTransformation setTransformation(double m00,
248: double m01, double m02, double m10, double m11, double m12) {
249: this .m00 = m00;
250: this .m01 = m01;
251: this .m02 = m02;
252: this .m10 = m10;
253: this .m11 = m11;
254: this .m12 = m12;
255: return this ;
256: }
257:
258: /**
259: * Sets this transformation to be a copy of the given one
260: *
261: * @param trans a transformation to copy
262: * @return this transformation, with an updated matrix
263: */
264: public AffineTransformation setTransformation(
265: AffineTransformation trans) {
266: m00 = trans.m00;
267: m01 = trans.m01;
268: m02 = trans.m02;
269: m10 = trans.m10;
270: m11 = trans.m11;
271: m12 = trans.m12;
272: return this ;
273: }
274:
275: /**
276: * Gets an array containing the entries
277: * of the transformation matrix.
278: * Only the 6 non-trivial entries are returned,
279: * in the sequence:
280: * <pre>
281: * m00, m01, m02, m10, m11, m12
282: * </pre>
283: *
284: * @return an array of length 6
285: */
286: public double[] getMatrixEntries() {
287: return new double[] { m00, m01, m02, m10, m11, m12 };
288: }
289:
290: /**
291: * Computes the determinant of the transformation matrix.
292: * The determinant is computed as:
293: * <blockquote><pre>
294: * | m00 m01 m02 |
295: * | m10 m11 m12 | = m00 * m11 - m01 * m10
296: * | 0 0 1 |
297: * </pre></blockquote>
298: * If the determinant is zero,
299: * the transform is singular (not invertible),
300: * and operations which attempt to compute
301: * an inverse will throw a <tt>NoninvertibleTransformException</tt>.
302:
303: * @return the determinant of the transformation
304: * @see #getInverse()
305: */
306: public double getDeterminant() {
307: return m00 * m11 - m01 * m10;
308: }
309:
310: /**
311: * Computes the inverse of this transformation, if one
312: * exists.
313: * The inverse is the transformation which when
314: * composed with this one produces the identity
315: * transformation.
316: * A transformation has an inverse if and only if it
317: * is not singular (i.e. its
318: * determinant is non-zero).
319: * Geometrically, an transformation is non-invertible
320: * if it maps the plane to a line or a point.
321: * If no inverse exists this method
322: * will throw a <tt>NoninvertibleTransformationException</tt>.
323: * <p>
324: * The matrix of the inverse is equal to the
325: * inverse of the matrix for the transformation.
326: * It is computed as follows:
327: * <blockquote><pre>
328: * 1
329: * inverse(A) = --- x adjoint(A)
330: * det
331: *
332: *
333: * = 1 | m11 -m01 m01*m12-m02*m11 |
334: * --- x | -m10 m00 -m00*m12+m10*m02 |
335: * det | 0 0 m00*m11-m10*m01 |
336: *
337: *
338: *
339: * = | m11/det -m01/det m01*m12-m02*m11/det |
340: * | -m10/det m00/det -m00*m12+m10*m02/det |
341: * | 0 0 1 |
342: *
343: * </pre></blockquote>
344: *
345: * @return a new inverse transformation
346: * @throws NoninvertibleTransformationException
347: * @see #getDeterminant()
348: */
349: public AffineTransformation getInverse()
350: throws NoninvertibleTransformationException {
351: double det = getDeterminant();
352: if (det == 0)
353: throw new NoninvertibleTransformationException(
354: I18N
355: .get("jump.geom.AffineTransformation.Transformation-is-non-invertible"));
356:
357: double im00 = m11 / det;
358: double im10 = -m10 / det;
359: double im01 = -m01 / det;
360: double im11 = m00 / det;
361: double im02 = (m01 * m12 - m02 * m11) / det;
362: double im12 = (-m00 * m12 + m10 * m02) / det;
363:
364: return new AffineTransformation(im00, im01, im02, im10, im11,
365: im12);
366: }
367:
368: /**
369: * Explicitly computes the math for a reflection. May not work.
370: * @param x0
371: * @param y0
372: * @param x1
373: * @param y1
374: * @return this transformation, with an updated matrix
375: */
376: public AffineTransformation setToReflectionBasic(double x0,
377: double y0, double x1, double y1) {
378: if (x0 == x1 && y0 == y1) {
379: throw new IllegalArgumentException(
380: I18N
381: .get("jump.geom.AffineTransformation.Reflection-line-points-must-be-distinct"));
382: }
383: double dx = x1 - x0;
384: double dy = y1 - y0;
385: double d = Math.sqrt(dx * dx + dy * dy);
386: double sin = dy / d;
387: double cos = dx / d;
388: double cs2 = 2 * sin * cos;
389: double c2s2 = cos * cos - sin * sin;
390: m00 = c2s2;
391: m01 = cs2;
392: m02 = 0.0;
393: m10 = cs2;
394: m11 = -c2s2;
395: m12 = 0.0;
396: return this ;
397: }
398:
399: public AffineTransformation setToReflection(double x0, double y0,
400: double x1, double y1) {
401: if (x0 == x1 && y0 == y1) {
402: throw new IllegalArgumentException(
403: I18N
404: .get("jump.geom.AffineTransformation.Reflection-line-points-must-be-distinct"));
405: }
406: // translate line vector to origin
407: setToTranslation(-x0, -y0);
408:
409: // rotate vector to positive x axis direction
410: double dx = x1 - x0;
411: double dy = y1 - y0;
412: double d = Math.sqrt(dx * dx + dy * dy);
413: double sin = dy / d;
414: double cos = dx / d;
415: rotate(-sin, cos);
416: // reflect about the x axis
417: scale(1, -1);
418: // rotate back
419: rotate(sin, cos);
420: // translate back
421: translate(x0, y0);
422: return this ;
423: }
424:
425: /**
426: * Sets this transformation to be a reflection
427: * about the line defined by vector (x,y).
428: * The transformation for a reflection
429: * is computed by:
430: * <blockquote><pre>
431: * d = sqrt(x<sup>2</sup> + y<sup>2</sup>)
432: * sin = x / d;
433: * cos = x / d;
434: *
435: * T<sub>ref</sub> = T<sub>rot(sin, cos)</sub> x T<sub>scale(1, -1)</sub> x T<sub>rot(-sin, cos)</sub
436: * </pre></blockquote>
437: *
438: * @param x the x-component of the reflection line vector
439: * @param y the y-component of the reflection line vector
440: * @return this transformation, with an updated matrix
441: */
442: public AffineTransformation setToReflection(double x, double y) {
443: if (x == 0.0 && y == 0.0) {
444: throw new IllegalArgumentException(
445: I18N
446: .get("jump.geom.AffineTransformation.Reflection-vector-must-be-non-zero"));
447: }
448: // rotate vector to positive x axis direction
449: double d = Math.sqrt(x * x + y * y);
450: double sin = y / d;
451: double cos = x / d;
452: rotate(-sin, cos);
453: // reflect about the x-axis
454: scale(1, -1);
455: // rotate back
456: rotate(sin, cos);
457: return this ;
458: }
459:
460: /**
461: * Sets this transformation to be a rotation.
462: * A positive rotation angle corresponds
463: * to a counter-clockwise rotation.
464: * The transformation matrix for a rotation
465: * by an angle <tt>theta</tt>
466: * has the value:
467: * <blockquote><pre>
468: * | cos(theta) -sin(theta) 0 |
469: * | sin(theta) cos(theta) 0 |
470: * | 0 0 1 |
471: * </pre></blockquote>
472: *
473: * @param theta the rotation angle, in radians
474: * @return this transformation, with an updated matrix
475: */
476: private AffineTransformation setToRotation(double theta) {
477: setToRotation(Math.sin(theta), Math.cos(theta));
478: return this ;
479: }
480:
481: /**
482: * Sets this transformation to be a rotation
483: * by specifying the sin and cos of the rotation angle directly.
484: * The transformation matrix for the rotation
485: * has the value:
486: * <blockquote><pre>
487: * | cosTheta -sinTheta 0 |
488: * | sinTheta cosTheta 0 |
489: * | 0 0 1 |
490: * </pre></blockquote>
491: *
492: * @param sinTheta the sine of the rotation angle
493: * @param cosTheta the cosine of the rotation angle
494: * @return this transformation, with an updated matrix
495: */
496: public AffineTransformation setToRotation(double sinTheta,
497: double cosTheta) {
498: m00 = cosTheta;
499: m01 = -sinTheta;
500: m02 = 0.0;
501: m10 = sinTheta;
502: m11 = cosTheta;
503: m12 = 0.0;
504: return this ;
505: }
506:
507: /**
508: * Sets this transformation to be a scaling.
509: * The transformation matrix for a scale
510: * has the value:
511: * <blockquote><pre>
512: * | xScale 0 dx |
513: * | 1 yScale dy |
514: * | 0 0 1 |
515: * </pre></blockquote>
516: *
517: * @param xScale the amount to scale x-ordinates by
518: * @param yScale the amount to scale y-ordinates by
519: * @return this transformation, with an updated matrix
520: */
521: public AffineTransformation setToScale(double xScale, double yScale) {
522: m00 = xScale;
523: m01 = 0.0;
524: m02 = 0.0;
525: m10 = 0.0;
526: m11 = yScale;
527: m12 = 0.0;
528: return this ;
529: }
530:
531: /**
532: * Sets this transformation to be a shear.
533: * The transformation matrix for a shear
534: * has the value:
535: * <blockquote><pre>
536: * | 1 xShear 0 |
537: * | yShear 1 0 |
538: * | 0 0 1 |
539: * </pre></blockquote>
540: * Note that a shear of (1, 1) is <i>not</i>
541: * equal to shear(1, 0) composed with shear(0, 1).
542: * Instead, shear(1, 1) corresponds to a mapping onto the
543: * line x = y.
544: *
545: * @param xShear the x component to shear by
546: * @param yShear the y component to shear by
547: * @return this transformation, with an updated matrix
548: */
549: public AffineTransformation setToShear(double xShear, double yShear) {
550: m00 = 1.0;
551: m01 = xShear;
552: m02 = 0.0;
553: m10 = yShear;
554: m11 = 1.0;
555: m12 = 0.0;
556: return this ;
557: }
558:
559: /**
560: * Sets this transformation to be a translation.
561: * For a translation by the vector (x, y)
562: * the transformation matrix has the value:
563: * <blockquote><pre>
564: * | 1 0 dx |
565: * | 1 0 dy |
566: * | 0 0 1 |
567: * </pre></blockquote>
568: * @param dx the x component to translate by
569: * @param dy the y component to translate by
570: * @return this transformation, with an updated matrix
571: */
572: public AffineTransformation setToTranslation(double dx, double dy) {
573: m00 = 1.0;
574: m01 = 0.0;
575: m02 = dx;
576: m10 = 0.0;
577: m11 = 1.0;
578: m12 = dy;
579: return this ;
580: }
581:
582: /**
583: * Updates the value of this transformation
584: * to that of a reflection transformation composed
585: * with the current value.
586: *
587: * @param x0 the x-ordinate of a point on the line to reflect around
588: * @param y0 the y-ordinate of a point on the line to reflect around
589: * @param x1 the x-ordinate of a point on the line to reflect around
590: * @param y1 the y-ordinate of a point on the line to reflect around
591: * @return this transformation, with an updated matrix
592: */
593: public AffineTransformation reflect(double x0, double y0,
594: double x1, double y1) {
595: compose(reflectionInstance(x0, y0, x1, y1));
596: return this ;
597: }
598:
599: /**
600: * Updates the value of this transformation
601: * to that of a reflection transformation composed
602: * with the current value.
603: *
604: * @param x the x-ordinate of the line to reflect around
605: * @param y the y-ordinate of the line to reflect around
606: * @return this transformation, with an updated matrix
607: */
608: public AffineTransformation reflect(double x, double y) {
609: compose(reflectionInstance(x, y));
610: return this ;
611: }
612:
613: /**
614: * Updates the value of this transformation
615: * to that of a rotation transformation composed
616: * with the current value.
617: *
618: * @param theta the angle to rotate by
619: * @return this transformation, with an updated matrix
620: */
621: public AffineTransformation rotate(double theta) {
622: compose(rotationInstance(theta));
623: return this ;
624: }
625:
626: /**
627: * Updates the value of this transformation
628: * to that of a rotation transformation composed
629: * with the current value.
630: *
631: * @param sinTheta the sine of the angle to rotate by
632: * @param cosTheta the cosine of the angle to rotate by
633: * @return this transformation, with an updated matrix
634: */
635: public AffineTransformation rotate(double sinTheta, double cosTheta) {
636: compose(rotationInstance(sinTheta, cosTheta));
637: return this ;
638: }
639:
640: /**
641: * Updates the value of this transformation
642: * to that of a scale transformation composed
643: * with the current value.
644: *
645: * @param xScale the value to scale by in the x direction
646: * @param yScale the value to scale by in the y direction
647: * @return this transformation, with an updated matrix
648: */
649: public AffineTransformation scale(double xScale, double yScale) {
650: compose(scaleInstance(xScale, yScale));
651: return this ;
652: }
653:
654: /**
655: * Updates the value of this transformation
656: * to that of a shear transformation composed
657: * with the current value.
658: *
659: * @param xShear the value to shear by in the x direction
660: * @param yShear the value to shear by in the y direction
661: * @return this transformation, with an updated matrix
662: */
663: public AffineTransformation shear(double xShear, double yShear) {
664: compose(shearInstance(xShear, yShear));
665: return this ;
666: }
667:
668: /**
669: * Updates the value of this transformation
670: * to that of a translation transformation composed
671: * with the current value.
672: *
673: * @param x the value to translate by in the x direction
674: * @param y the value to translate by in the y direction
675: * @return this transformation, with an updated matrix
676: */
677: public AffineTransformation translate(double x, double y) {
678: compose(translationInstance(x, y));
679: return this ;
680: }
681:
682: /**
683: * Composes the given {@link AffineTransformation}
684: * with this transformation.
685: * This produces a transformation whose effect
686: * is equal to applying this transformation
687: * followed by the argument transformation.
688: * Mathematically,
689: * <blockquote><pre>
690: * A.compose(B) = T<sub>B</sub> x T<sub>A</sub>
691: * </pre></blockquote>
692: *
693: * @param trans an affine transformation
694: * @return this transformation, with an updated matrix
695: */
696: public AffineTransformation compose(AffineTransformation trans) {
697: double mp00 = trans.m00 * m00 + trans.m01 * m10;
698: double mp01 = trans.m00 * m01 + trans.m01 * m11;
699: double mp02 = trans.m00 * m02 + trans.m01 * m12 + trans.m02;
700: double mp10 = trans.m10 * m00 + trans.m11 * m10;
701: double mp11 = trans.m10 * m01 + trans.m11 * m11;
702: double mp12 = trans.m10 * m02 + trans.m11 * m12 + trans.m12;
703: m00 = mp00;
704: m01 = mp01;
705: m02 = mp02;
706: m10 = mp10;
707: m11 = mp11;
708: m12 = mp12;
709: return this ;
710: }
711:
712: /**
713: * Composes this transformation
714: * with the given {@link AffineTransformation}.
715: * This produces a transformation whose effect
716: * is equal to applying the argument transformation
717: * followed by this transformation.
718: * Mathematically,
719: * <blockquote><pre>
720: * A.composeBefore(B) = T<sub>A</sub> x T<sub>B</sub>
721: * </pre></blockquote>
722: *
723: * @param trans an affine transformation
724: * @return this transformation, with an updated matrix
725: */
726: public AffineTransformation composeBefore(AffineTransformation trans) {
727: double mp00 = m00 * trans.m00 + m01 * trans.m10;
728: double mp01 = m00 * trans.m01 + m01 * trans.m11;
729: double mp02 = m00 * trans.m02 + m01 * trans.m12 + m02;
730: double mp10 = m10 * trans.m00 + m11 * trans.m10;
731: double mp11 = m10 * trans.m01 + m11 * trans.m11;
732: double mp12 = m10 * trans.m02 + m11 * trans.m12 + m12;
733: m00 = mp00;
734: m01 = mp01;
735: m02 = mp02;
736: m10 = mp10;
737: m11 = mp11;
738: m12 = mp12;
739: return this ;
740: }
741:
742: public Coordinate transform(Coordinate src, Coordinate dest) {
743: double xp = m00 * src.x + m01 * src.y + m02;
744: double yp = m10 * src.x + m11 * src.y + m12;
745: dest.x = xp;
746: dest.y = yp;
747: return dest;
748: }
749:
750: public CoordinateSequence transform(CoordinateSequence seq) {
751: for (int i = 0; i < seq.size(); i++) {
752: double xp = m00 * seq.getOrdinate(i, 0) + m01
753: * seq.getOrdinate(i, 1) + m02;
754: double yp = m10 * seq.getOrdinate(i, 0) + m11
755: * seq.getOrdinate(i, 1) + m12;
756: seq.setOrdinate(i, 0, xp);
757: seq.setOrdinate(i, 1, yp);
758: }
759: return seq;
760: }
761:
762: public void filter(Coordinate pt) {
763: double xp = m00 * pt.x + m01 * pt.y + m02;
764: double yp = m10 * pt.x + m11 * pt.y + m12;
765: pt.x = xp;
766: pt.y = yp;
767: }
768:
769: /**
770: * Tests if this transformation is the identity transformation.
771: *
772: * @return true if this is the identity transformation
773: */
774: public boolean isIdentity() {
775: return (m00 == 1 && m01 == 0 && m02 == 0 && m10 == 0
776: && m11 == 1 && m12 == 0);
777: }
778:
779: /**
780: * Tests if an object is an
781: * <tt>AffineTransformation</tt>
782: * and has the same matrix as
783: * this transformation.
784: *
785: * @param obj an object to test
786: * @return true if the given object is equal to this object
787: */
788: public boolean equals(Object obj) {
789: if (obj instanceof AffineTransformation)
790: return false;
791: AffineTransformation trans = (AffineTransformation) obj;
792: return m00 == trans.m00 && m01 == trans.m01 && m02 == trans.m02
793: && m10 == trans.m10 && m11 == trans.m11
794: && m12 == trans.m12;
795: }
796:
797: /**
798: * Gets a text representation of this transformation.
799: * The string is of the form:
800: * <pre>
801: * AffineTransformation[[m00, m01, m02], [m10, m11, m12]]
802: * </pre>
803: *
804: * @return a string representing this transformation
805: *
806: */
807: public String toString() {
808: return "AffineTransformation[[" + m00 + ", " + m01 + ", " + m02
809: + "], [" + m10 + ", " + m11 + ", " + m12 + "]]";
810: }
811:
812: /**
813: * Clones this transformation
814: *
815: * @return a copy of this transformation
816: */
817: public Object clone() {
818: return new AffineTransformation(this);
819: }
820: }
|