001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Denis M. Kishenko
019: * @version $Revision$
020: */package java.awt.geom;
021:
022: import java.awt.Shape;
023: import java.io.IOException;
024: import java.io.Serializable;
025:
026: import org.apache.harmony.awt.internal.nls.Messages;
027: import org.apache.harmony.misc.HashCode;
028:
029: public class AffineTransform implements Cloneable, Serializable {
030:
031: private static final long serialVersionUID = 1330973210523860834L;
032:
033: public static final int TYPE_IDENTITY = 0;
034: public static final int TYPE_TRANSLATION = 1;
035: public static final int TYPE_UNIFORM_SCALE = 2;
036: public static final int TYPE_GENERAL_SCALE = 4;
037: public static final int TYPE_QUADRANT_ROTATION = 8;
038: public static final int TYPE_GENERAL_ROTATION = 16;
039: public static final int TYPE_GENERAL_TRANSFORM = 32;
040: public static final int TYPE_FLIP = 64;
041: public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE
042: | TYPE_GENERAL_SCALE;
043: public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION
044: | TYPE_GENERAL_ROTATION;
045:
046: /**
047: * The <code>TYPE_UNKNOWN</code> is an initial type value
048: */
049: static final int TYPE_UNKNOWN = -1;
050:
051: /**
052: * The min value equivalent to zero. If absolute value less then ZERO it considered as zero.
053: */
054: static final double ZERO = 1E-10;
055:
056: /**
057: * The values of transformation matrix
058: */
059: double m00;
060: double m10;
061: double m01;
062: double m11;
063: double m02;
064: double m12;
065:
066: /**
067: * The transformation <code>type</code>
068: */
069: transient int type;
070:
071: public AffineTransform() {
072: type = TYPE_IDENTITY;
073: m00 = m11 = 1.0;
074: m10 = m01 = m02 = m12 = 0.0;
075: }
076:
077: public AffineTransform(AffineTransform t) {
078: this .type = t.type;
079: this .m00 = t.m00;
080: this .m10 = t.m10;
081: this .m01 = t.m01;
082: this .m11 = t.m11;
083: this .m02 = t.m02;
084: this .m12 = t.m12;
085: }
086:
087: public AffineTransform(float m00, float m10, float m01, float m11,
088: float m02, float m12) {
089: this .type = TYPE_UNKNOWN;
090: this .m00 = m00;
091: this .m10 = m10;
092: this .m01 = m01;
093: this .m11 = m11;
094: this .m02 = m02;
095: this .m12 = m12;
096: }
097:
098: public AffineTransform(double m00, double m10, double m01,
099: double m11, double m02, double m12) {
100: this .type = TYPE_UNKNOWN;
101: this .m00 = m00;
102: this .m10 = m10;
103: this .m01 = m01;
104: this .m11 = m11;
105: this .m02 = m02;
106: this .m12 = m12;
107: }
108:
109: public AffineTransform(float[] matrix) {
110: this .type = TYPE_UNKNOWN;
111: m00 = matrix[0];
112: m10 = matrix[1];
113: m01 = matrix[2];
114: m11 = matrix[3];
115: if (matrix.length > 4) {
116: m02 = matrix[4];
117: m12 = matrix[5];
118: }
119: }
120:
121: public AffineTransform(double[] matrix) {
122: this .type = TYPE_UNKNOWN;
123: m00 = matrix[0];
124: m10 = matrix[1];
125: m01 = matrix[2];
126: m11 = matrix[3];
127: if (matrix.length > 4) {
128: m02 = matrix[4];
129: m12 = matrix[5];
130: }
131: }
132:
133: /*
134: * Method returns type of affine transformation.
135: *
136: * Transform matrix is
137: * m00 m01 m02
138: * m10 m11 m12
139: *
140: * According analytic geometry new basis vectors are (m00, m01) and (m10, m11),
141: * translation vector is (m02, m12). Original basis vectors are (1, 0) and (0, 1).
142: * Type transformations classification:
143: * TYPE_IDENTITY - new basis equals original one and zero translation
144: * TYPE_TRANSLATION - translation vector isn't zero
145: * TYPE_UNIFORM_SCALE - vectors length of new basis equals
146: * TYPE_GENERAL_SCALE - vectors length of new basis doesn't equal
147: * TYPE_FLIP - new basis vector orientation differ from original one
148: * TYPE_QUADRANT_ROTATION - new basis is rotated by 90, 180, 270, or 360 degrees
149: * TYPE_GENERAL_ROTATION - new basis is rotated by arbitrary angle
150: * TYPE_GENERAL_TRANSFORM - transformation can't be inversed
151: */
152: public int getType() {
153: if (type != TYPE_UNKNOWN) {
154: return type;
155: }
156:
157: int type = 0;
158:
159: if (m00 * m01 + m10 * m11 != 0.0) {
160: type |= TYPE_GENERAL_TRANSFORM;
161: return type;
162: }
163:
164: if (m02 != 0.0 || m12 != 0.0) {
165: type |= TYPE_TRANSLATION;
166: } else if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) {
167: type = TYPE_IDENTITY;
168: return type;
169: }
170:
171: if (m00 * m11 - m01 * m10 < 0.0) {
172: type |= TYPE_FLIP;
173: }
174:
175: double dx = m00 * m00 + m10 * m10;
176: double dy = m01 * m01 + m11 * m11;
177: if (dx != dy) {
178: type |= TYPE_GENERAL_SCALE;
179: } else if (dx != 1.0) {
180: type |= TYPE_UNIFORM_SCALE;
181: }
182:
183: if ((m00 == 0.0 && m11 == 0.0)
184: || (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0))) {
185: type |= TYPE_QUADRANT_ROTATION;
186: } else if (m01 != 0.0 || m10 != 0.0) {
187: type |= TYPE_GENERAL_ROTATION;
188: }
189:
190: return type;
191: }
192:
193: public double getScaleX() {
194: return m00;
195: }
196:
197: public double getScaleY() {
198: return m11;
199: }
200:
201: public double getShearX() {
202: return m01;
203: }
204:
205: public double getShearY() {
206: return m10;
207: }
208:
209: public double getTranslateX() {
210: return m02;
211: }
212:
213: public double getTranslateY() {
214: return m12;
215: }
216:
217: public boolean isIdentity() {
218: return getType() == TYPE_IDENTITY;
219: }
220:
221: public void getMatrix(double[] matrix) {
222: matrix[0] = m00;
223: matrix[1] = m10;
224: matrix[2] = m01;
225: matrix[3] = m11;
226: if (matrix.length > 4) {
227: matrix[4] = m02;
228: matrix[5] = m12;
229: }
230: }
231:
232: public double getDeterminant() {
233: return m00 * m11 - m01 * m10;
234: }
235:
236: public void setTransform(double m00, double m10, double m01,
237: double m11, double m02, double m12) {
238: this .type = TYPE_UNKNOWN;
239: this .m00 = m00;
240: this .m10 = m10;
241: this .m01 = m01;
242: this .m11 = m11;
243: this .m02 = m02;
244: this .m12 = m12;
245: }
246:
247: public void setTransform(AffineTransform t) {
248: type = t.type;
249: setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12);
250: }
251:
252: public void setToIdentity() {
253: type = TYPE_IDENTITY;
254: m00 = m11 = 1.0;
255: m10 = m01 = m02 = m12 = 0.0;
256: }
257:
258: public void setToTranslation(double mx, double my) {
259: m00 = m11 = 1.0;
260: m01 = m10 = 0.0;
261: m02 = mx;
262: m12 = my;
263: if (mx == 0.0 && my == 0.0) {
264: type = TYPE_IDENTITY;
265: } else {
266: type = TYPE_TRANSLATION;
267: }
268: }
269:
270: public void setToScale(double scx, double scy) {
271: m00 = scx;
272: m11 = scy;
273: m10 = m01 = m02 = m12 = 0.0;
274: if (scx != 1.0 || scy != 1.0) {
275: type = TYPE_UNKNOWN;
276: } else {
277: type = TYPE_IDENTITY;
278: }
279: }
280:
281: public void setToShear(double shx, double shy) {
282: m00 = m11 = 1.0;
283: m02 = m12 = 0.0;
284: m01 = shx;
285: m10 = shy;
286: if (shx != 0.0 || shy != 0.0) {
287: type = TYPE_UNKNOWN;
288: } else {
289: type = TYPE_IDENTITY;
290: }
291: }
292:
293: public void setToRotation(double angle) {
294: double sin = Math.sin(angle);
295: double cos = Math.cos(angle);
296: if (Math.abs(cos) < ZERO) {
297: cos = 0.0;
298: sin = sin > 0.0 ? 1.0 : -1.0;
299: } else if (Math.abs(sin) < ZERO) {
300: sin = 0.0;
301: cos = cos > 0.0 ? 1.0 : -1.0;
302: }
303: m00 = m11 = cos;
304: m01 = -sin;
305: m10 = sin;
306: m02 = m12 = 0.0;
307: type = TYPE_UNKNOWN;
308: }
309:
310: public void setToRotation(double angle, double px, double py) {
311: setToRotation(angle);
312: m02 = px * (1.0 - m00) + py * m10;
313: m12 = py * (1.0 - m00) - px * m10;
314: type = TYPE_UNKNOWN;
315: }
316:
317: public static AffineTransform getTranslateInstance(double mx,
318: double my) {
319: AffineTransform t = new AffineTransform();
320: t.setToTranslation(mx, my);
321: return t;
322: }
323:
324: public static AffineTransform getScaleInstance(double scx,
325: double scY) {
326: AffineTransform t = new AffineTransform();
327: t.setToScale(scx, scY);
328: return t;
329: }
330:
331: public static AffineTransform getShearInstance(double shx,
332: double shy) {
333: AffineTransform m = new AffineTransform();
334: m.setToShear(shx, shy);
335: return m;
336: }
337:
338: public static AffineTransform getRotateInstance(double angle) {
339: AffineTransform t = new AffineTransform();
340: t.setToRotation(angle);
341: return t;
342: }
343:
344: public static AffineTransform getRotateInstance(double angle,
345: double x, double y) {
346: AffineTransform t = new AffineTransform();
347: t.setToRotation(angle, x, y);
348: return t;
349: }
350:
351: public void translate(double mx, double my) {
352: concatenate(AffineTransform.getTranslateInstance(mx, my));
353: }
354:
355: public void scale(double scx, double scy) {
356: concatenate(AffineTransform.getScaleInstance(scx, scy));
357: }
358:
359: public void shear(double shx, double shy) {
360: concatenate(AffineTransform.getShearInstance(shx, shy));
361: }
362:
363: public void rotate(double angle) {
364: concatenate(AffineTransform.getRotateInstance(angle));
365: }
366:
367: public void rotate(double angle, double px, double py) {
368: concatenate(AffineTransform.getRotateInstance(angle, px, py));
369: }
370:
371: /**
372: * Multiply matrix of two AffineTransform objects
373: * @param t1 - the AffineTransform object is a multiplicand
374: * @param t2 - the AffineTransform object is a multiplier
375: * @return an AffineTransform object that is a result of t1 multiplied by matrix t2.
376: */
377: AffineTransform multiply(AffineTransform t1, AffineTransform t2) {
378: return new AffineTransform(t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00
379: t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01
380: t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10
381: t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11
382: t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02
383: t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12);// m12
384: }
385:
386: public void concatenate(AffineTransform t) {
387: setTransform(multiply(t, this ));
388: }
389:
390: public void preConcatenate(AffineTransform t) {
391: setTransform(multiply(this , t));
392: }
393:
394: public AffineTransform createInverse()
395: throws NoninvertibleTransformException {
396: double det = getDeterminant();
397: if (Math.abs(det) < ZERO) {
398: // awt.204=Determinant is zero
399: throw new NoninvertibleTransformException(Messages
400: .getString("awt.204")); //$NON-NLS-1$
401: }
402: return new AffineTransform(m11 / det, // m00
403: -m10 / det, // m10
404: -m01 / det, // m01
405: m00 / det, // m11
406: (m01 * m12 - m11 * m02) / det, // m02
407: (m10 * m02 - m00 * m12) / det // m12
408: );
409: }
410:
411: public Point2D transform(Point2D src, Point2D dst) {
412: if (dst == null) {
413: if (src instanceof Point2D.Double) {
414: dst = new Point2D.Double();
415: } else {
416: dst = new Point2D.Float();
417: }
418: }
419:
420: double x = src.getX();
421: double y = src.getY();
422:
423: dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11
424: + m12);
425: return dst;
426: }
427:
428: public void transform(Point2D[] src, int srcOff, Point2D[] dst,
429: int dstOff, int length) {
430: while (--length >= 0) {
431: Point2D srcPoint = src[srcOff++];
432: double x = srcPoint.getX();
433: double y = srcPoint.getY();
434: Point2D dstPoint = dst[dstOff];
435: if (dstPoint == null) {
436: if (srcPoint instanceof Point2D.Double) {
437: dstPoint = new Point2D.Double();
438: } else {
439: dstPoint = new Point2D.Float();
440: }
441: }
442: dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y
443: * m11 + m12);
444: dst[dstOff++] = dstPoint;
445: }
446: }
447:
448: public void transform(double[] src, int srcOff, double[] dst,
449: int dstOff, int length) {
450: int step = 2;
451: if (src == dst && srcOff < dstOff
452: && dstOff < srcOff + length * 2) {
453: srcOff = srcOff + length * 2 - 2;
454: dstOff = dstOff + length * 2 - 2;
455: step = -2;
456: }
457: while (--length >= 0) {
458: double x = src[srcOff + 0];
459: double y = src[srcOff + 1];
460: dst[dstOff + 0] = x * m00 + y * m01 + m02;
461: dst[dstOff + 1] = x * m10 + y * m11 + m12;
462: srcOff += step;
463: dstOff += step;
464: }
465: }
466:
467: public void transform(float[] src, int srcOff, float[] dst,
468: int dstOff, int length) {
469: int step = 2;
470: if (src == dst && srcOff < dstOff
471: && dstOff < srcOff + length * 2) {
472: srcOff = srcOff + length * 2 - 2;
473: dstOff = dstOff + length * 2 - 2;
474: step = -2;
475: }
476: while (--length >= 0) {
477: float x = src[srcOff + 0];
478: float y = src[srcOff + 1];
479: dst[dstOff + 0] = (float) (x * m00 + y * m01 + m02);
480: dst[dstOff + 1] = (float) (x * m10 + y * m11 + m12);
481: srcOff += step;
482: dstOff += step;
483: }
484: }
485:
486: public void transform(float[] src, int srcOff, double[] dst,
487: int dstOff, int length) {
488: while (--length >= 0) {
489: float x = src[srcOff++];
490: float y = src[srcOff++];
491: dst[dstOff++] = x * m00 + y * m01 + m02;
492: dst[dstOff++] = x * m10 + y * m11 + m12;
493: }
494: }
495:
496: public void transform(double[] src, int srcOff, float[] dst,
497: int dstOff, int length) {
498: while (--length >= 0) {
499: double x = src[srcOff++];
500: double y = src[srcOff++];
501: dst[dstOff++] = (float) (x * m00 + y * m01 + m02);
502: dst[dstOff++] = (float) (x * m10 + y * m11 + m12);
503: }
504: }
505:
506: public Point2D deltaTransform(Point2D src, Point2D dst) {
507: if (dst == null) {
508: if (dst instanceof Point2D.Double) {
509: dst = new Point2D.Double();
510: } else {
511: dst = new Point2D.Float();
512: }
513: }
514:
515: double x = src.getX();
516: double y = src.getY();
517:
518: dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
519: return dst;
520: }
521:
522: public void deltaTransform(double[] src, int srcOff, double[] dst,
523: int dstOff, int length) {
524: while (--length >= 0) {
525: double x = src[srcOff++];
526: double y = src[srcOff++];
527: dst[dstOff++] = x * m00 + y * m01;
528: dst[dstOff++] = x * m10 + y * m11;
529: }
530: }
531:
532: public Point2D inverseTransform(Point2D src, Point2D dst)
533: throws NoninvertibleTransformException {
534: double det = getDeterminant();
535: if (Math.abs(det) < ZERO) {
536: // awt.204=Determinant is zero
537: throw new NoninvertibleTransformException(Messages
538: .getString("awt.204")); //$NON-NLS-1$
539: }
540:
541: if (dst == null) {
542: if (src instanceof Point2D.Double) {
543: dst = new Point2D.Double();
544: } else {
545: dst = new Point2D.Float();
546: }
547: }
548:
549: double x = src.getX() - m02;
550: double y = src.getY() - m12;
551:
552: dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10)
553: / det);
554: return dst;
555: }
556:
557: public void inverseTransform(double[] src, int srcOff,
558: double[] dst, int dstOff, int length)
559: throws NoninvertibleTransformException {
560: double det = getDeterminant();
561: if (Math.abs(det) < ZERO) {
562: // awt.204=Determinant is zero
563: throw new NoninvertibleTransformException(Messages
564: .getString("awt.204")); //$NON-NLS-1$
565: }
566:
567: while (--length >= 0) {
568: double x = src[srcOff++] - m02;
569: double y = src[srcOff++] - m12;
570: dst[dstOff++] = (x * m11 - y * m01) / det;
571: dst[dstOff++] = (y * m00 - x * m10) / det;
572: }
573: }
574:
575: public Shape createTransformedShape(Shape src) {
576: if (src == null) {
577: return null;
578: }
579: if (src instanceof GeneralPath) {
580: return ((GeneralPath) src).createTransformedShape(this );
581: }
582: PathIterator path = src.getPathIterator(this );
583: GeneralPath dst = new GeneralPath(path.getWindingRule());
584: dst.append(path, false);
585: return dst;
586: }
587:
588: @Override
589: public String toString() {
590: return getClass().getName()
591: + "[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
592: + m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
593: }
594:
595: @Override
596: public Object clone() {
597: try {
598: return super .clone();
599: } catch (CloneNotSupportedException e) {
600: throw new InternalError();
601: }
602: }
603:
604: @Override
605: public int hashCode() {
606: HashCode hash = new HashCode();
607: hash.append(m00);
608: hash.append(m01);
609: hash.append(m02);
610: hash.append(m10);
611: hash.append(m11);
612: hash.append(m12);
613: return hash.hashCode();
614: }
615:
616: @Override
617: public boolean equals(Object obj) {
618: if (obj == this ) {
619: return true;
620: }
621: if (obj instanceof AffineTransform) {
622: AffineTransform t = (AffineTransform) obj;
623: return m00 == t.m00 && m01 == t.m01 && m02 == t.m02
624: && m10 == t.m10 && m11 == t.m11 && m12 == t.m12;
625: }
626: return false;
627: }
628:
629: /**
630: * Write AffineTrasform object to the output steam.
631: * @param stream - the output stream
632: * @throws IOException - if there are I/O errors while writing to the output strem
633: */
634: private void writeObject(java.io.ObjectOutputStream stream)
635: throws IOException {
636: stream.defaultWriteObject();
637: }
638:
639: /**
640: * Read AffineTransform object from the input stream
641: * @param stream - the input steam
642: * @throws IOException - if there are I/O errors while reading from the input strem
643: * @throws ClassNotFoundException - if class could not be found
644: */
645: private void readObject(java.io.ObjectInputStream stream)
646: throws IOException, ClassNotFoundException {
647: stream.defaultReadObject();
648: type = TYPE_UNKNOWN;
649: }
650:
651: }
|