0001: /*
0002: * Copyright (C) 2005, 2006 data2c GmbH (www.data2c.com)
0003: *
0004: * Author: Wolfgang S. Kechel - wolfgang.kechel@data2c.com
0005: *
0006: * J2ME version of java.awt.geom.AffineTransform.
0007: */
0008:
0009: //#ifndef j2se
0010: package org.awt.geom;
0011:
0012: //#ifdef notdef
0013: import java.awt.Shape;
0014:
0015: //#endif
0016:
0017: /**
0018: * The <code>AffineTransform</code> class represents a 2D affine transform
0019: * that performs a linear mapping from 2D coordinates to other 2D
0020: * coordinates that preserves the "straightness" and
0021: * "parallelness" of lines. Affine transformations can be constructed
0022: * using sequences of translations, scales, flips, rotations, and shears.
0023: * <p>
0024: * Such a coordinate transformation can be represented by a 3 row by
0025: * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix
0026: * transforms source coordinates <code>(x, y)</code> into
0027: * destination coordinates <code>(x', y')</code> by considering
0028: * them to be a column vector and multiplying the coordinate vector
0029: * by the matrix according to the following process:
0030: * <pre>
0031: * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
0032: * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]
0033: * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
0034: * </pre>
0035: */
0036: public class AffineTransform /*implements Cloneable, java.io.Serializable*/{
0037: /*
0038: * This constant is only useful for the cached type field.
0039: * It indicates that the type has been decached and must be recalculated.
0040: */
0041: private static final int TYPE_UNKNOWN = -1;
0042:
0043: /**
0044: * This constant indicates that the transform defined by this object
0045: * is an identity transform.
0046: * An identity transform is one in which the output coordinates are
0047: * always the same as the input coordinates.
0048: * If this transform is anything other than the identity transform,
0049: * the type will either be the constant GENERAL_TRANSFORM or a
0050: * combination of the appropriate flag bits for the various coordinate
0051: * conversions that this transform performs.
0052: * @see #TYPE_TRANSLATION
0053: * @see #TYPE_UNIFORM_SCALE
0054: * @see #TYPE_GENERAL_SCALE
0055: * @see #TYPE_FLIP
0056: * @see #TYPE_QUADRANT_ROTATION
0057: * @see #TYPE_GENERAL_ROTATION
0058: * @see #TYPE_GENERAL_TRANSFORM
0059: * @see #getType
0060: */
0061: public static final int TYPE_IDENTITY = 0;
0062:
0063: /**
0064: * This flag bit indicates that the transform defined by this object
0065: * performs a translation in addition to the conversions indicated
0066: * by other flag bits.
0067: * A translation moves the coordinates by a constant amount in x
0068: * and y without changing the length or angle of vectors.
0069: * @see #TYPE_IDENTITY
0070: * @see #TYPE_UNIFORM_SCALE
0071: * @see #TYPE_GENERAL_SCALE
0072: * @see #TYPE_FLIP
0073: * @see #TYPE_QUADRANT_ROTATION
0074: * @see #TYPE_GENERAL_ROTATION
0075: * @see #TYPE_GENERAL_TRANSFORM
0076: * @see #getType
0077: */
0078: public static final int TYPE_TRANSLATION = 1;
0079:
0080: /**
0081: * This flag bit indicates that the transform defined by this object
0082: * performs a uniform scale in addition to the conversions indicated
0083: * by other flag bits.
0084: * A uniform scale multiplies the length of vectors by the same amount
0085: * in both the x and y directions without changing the angle between
0086: * vectors.
0087: * This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag.
0088: * @see #TYPE_IDENTITY
0089: * @see #TYPE_TRANSLATION
0090: * @see #TYPE_GENERAL_SCALE
0091: * @see #TYPE_FLIP
0092: * @see #TYPE_QUADRANT_ROTATION
0093: * @see #TYPE_GENERAL_ROTATION
0094: * @see #TYPE_GENERAL_TRANSFORM
0095: * @see #getType
0096: */
0097: public static final int TYPE_UNIFORM_SCALE = 2;
0098:
0099: /**
0100: * This flag bit indicates that the transform defined by this object
0101: * performs a general scale in addition to the conversions indicated
0102: * by other flag bits.
0103: * A general scale multiplies the length of vectors by different
0104: * amounts in the x and y directions without changing the angle
0105: * between perpendicular vectors.
0106: * This flag bit is mutually exclusive with the TYPE_UNIFORM_SCALE flag.
0107: * @see #TYPE_IDENTITY
0108: * @see #TYPE_TRANSLATION
0109: * @see #TYPE_UNIFORM_SCALE
0110: * @see #TYPE_FLIP
0111: * @see #TYPE_QUADRANT_ROTATION
0112: * @see #TYPE_GENERAL_ROTATION
0113: * @see #TYPE_GENERAL_TRANSFORM
0114: * @see #getType
0115: */
0116: public static final int TYPE_GENERAL_SCALE = 4;
0117:
0118: /**
0119: * This constant is a bit mask for any of the scale flag bits.
0120: * @see #TYPE_UNIFORM_SCALE
0121: * @see #TYPE_GENERAL_SCALE
0122: */
0123: public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE);
0124:
0125: /**
0126: * This flag bit indicates that the transform defined by this object
0127: * performs a mirror image flip about some axis which changes the
0128: * normally right handed coordinate system into a left handed
0129: * system in addition to the conversions indicated by other flag bits.
0130: * A right handed coordinate system is one where the positive X
0131: * axis rotates counterclockwise to overlay the positive Y axis
0132: * similar to the direction that the fingers on your right hand
0133: * curl when you stare end on at your thumb.
0134: * A left handed coordinate system is one where the positive X
0135: * axis rotates clockwise to overlay the positive Y axis similar
0136: * to the direction that the fingers on your left hand curl.
0137: * There is no mathematical way to determine the angle of the
0138: * original flipping or mirroring transformation since all angles
0139: * of flip are identical given an appropriate adjusting rotation.
0140: * @see #TYPE_IDENTITY
0141: * @see #TYPE_TRANSLATION
0142: * @see #TYPE_UNIFORM_SCALE
0143: * @see #TYPE_GENERAL_SCALE
0144: * @see #TYPE_QUADRANT_ROTATION
0145: * @see #TYPE_GENERAL_ROTATION
0146: * @see #TYPE_GENERAL_TRANSFORM
0147: * @see #getType
0148: */
0149: public static final int TYPE_FLIP = 64;
0150: /* NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public
0151: * circulation and the flag bits could no longer be conveniently
0152: * renumbered without introducing binary incompatibility in outside
0153: * code.
0154: */
0155:
0156: /**
0157: * This flag bit indicates that the transform defined by this object
0158: * performs a quadrant rotation by some multiple of 90 degrees in
0159: * addition to the conversions indicated by other flag bits.
0160: * A rotation changes the angles of vectors by the same amount
0161: * regardless of the original direction of the vector and without
0162: * changing the length of the vector.
0163: * This flag bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag.
0164: * @see #TYPE_IDENTITY
0165: * @see #TYPE_TRANSLATION
0166: * @see #TYPE_UNIFORM_SCALE
0167: * @see #TYPE_GENERAL_SCALE
0168: * @see #TYPE_FLIP
0169: * @see #TYPE_GENERAL_ROTATION
0170: * @see #TYPE_GENERAL_TRANSFORM
0171: * @see #getType
0172: */
0173: public static final int TYPE_QUADRANT_ROTATION = 8;
0174:
0175: /**
0176: * This flag bit indicates that the transform defined by this object
0177: * performs a rotation by an arbitrary angle in addition to the
0178: * conversions indicated by other flag bits.
0179: * A rotation changes the angles of vectors by the same amount
0180: * regardless of the original direction of the vector and without
0181: * changing the length of the vector.
0182: * This flag bit is mutually exclusive with the
0183: * TYPE_QUADRANT_ROTATION flag.
0184: * @see #TYPE_IDENTITY
0185: * @see #TYPE_TRANSLATION
0186: * @see #TYPE_UNIFORM_SCALE
0187: * @see #TYPE_GENERAL_SCALE
0188: * @see #TYPE_FLIP
0189: * @see #TYPE_QUADRANT_ROTATION
0190: * @see #TYPE_GENERAL_TRANSFORM
0191: * @see #getType
0192: */
0193: public static final int TYPE_GENERAL_ROTATION = 16;
0194:
0195: /**
0196: * This constant is a bit mask for any of the rotation flag bits.
0197: * @see #TYPE_QUADRANT_ROTATION
0198: * @see #TYPE_GENERAL_ROTATION
0199: */
0200: public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION);
0201:
0202: /**
0203: * This constant indicates that the transform defined by this object
0204: * performs an arbitrary conversion of the input coordinates.
0205: * If this transform can be classified by any of the above constants,
0206: * the type will either be the constant TYPE_IDENTITY or a
0207: * combination of the appropriate flag bits for the various coordinate
0208: * conversions that this transform performs.
0209: * @see #TYPE_IDENTITY
0210: * @see #TYPE_TRANSLATION
0211: * @see #TYPE_UNIFORM_SCALE
0212: * @see #TYPE_GENERAL_SCALE
0213: * @see #TYPE_FLIP
0214: * @see #TYPE_QUADRANT_ROTATION
0215: * @see #TYPE_GENERAL_ROTATION
0216: * @see #getType
0217: */
0218: public static final int TYPE_GENERAL_TRANSFORM = 32;
0219:
0220: /**
0221: * This constant is used for the internal state variable to indicate
0222: * that no calculations need to be performed and that the source
0223: * coordinates only need to be copied to their destinations to
0224: * complete the transformation equation of this transform.
0225: * @see #APPLY_TRANSLATE
0226: * @see #APPLY_SCALE
0227: * @see #APPLY_SHEAR
0228: * @see #state
0229: */
0230: static final int APPLY_IDENTITY = 0;
0231:
0232: /**
0233: * This constant is used for the internal state variable to indicate
0234: * that the translation components of the matrix (m02 and m12) need
0235: * to be added to complete the transformation equation of this transform.
0236: * @see #APPLY_IDENTITY
0237: * @see #APPLY_SCALE
0238: * @see #APPLY_SHEAR
0239: * @see #state
0240: */
0241: static final int APPLY_TRANSLATE = 1;
0242:
0243: /**
0244: * This constant is used for the internal state variable to indicate
0245: * that the scaling components of the matrix (m00 and m11) need
0246: * to be factored in to complete the transformation equation of
0247: * this transform. If the APPLY_SHEAR bit is also set then it
0248: * indicates that the scaling components are not both 0.0. If the
0249: * APPLY_SHEAR bit is not also set then it indicates that the
0250: * scaling components are not both 1.0. If neither the APPLY_SHEAR
0251: * nor the APPLY_SCALE bits are set then the scaling components
0252: * are both 1.0, which means that the x and y components contribute
0253: * to the transformed coordinate, but they are not multiplied by
0254: * any scaling factor.
0255: * @see #APPLY_IDENTITY
0256: * @see #APPLY_TRANSLATE
0257: * @see #APPLY_SHEAR
0258: * @see #state
0259: */
0260: static final int APPLY_SCALE = 2;
0261:
0262: /**
0263: * This constant is used for the internal state variable to indicate
0264: * that the shearing components of the matrix (m01 and m10) need
0265: * to be factored in to complete the transformation equation of this
0266: * transform. The presence of this bit in the state variable changes
0267: * the interpretation of the APPLY_SCALE bit as indicated in its
0268: * documentation.
0269: * @see #APPLY_IDENTITY
0270: * @see #APPLY_TRANSLATE
0271: * @see #APPLY_SCALE
0272: * @see #state
0273: */
0274: static final int APPLY_SHEAR = 4;
0275:
0276: /*
0277: * For methods which combine together the state of two separate
0278: * transforms and dispatch based upon the combination, these constants
0279: * specify how far to shift one of the states so that the two states
0280: * are mutually non-interfering and provide constants for testing the
0281: * bits of the shifted (HI) state. The methods in this class use
0282: * the convention that the state of "this" transform is unshifted and
0283: * the state of the "other" or "argument" transform is shifted (HI).
0284: */
0285: private static final int HI_SHIFT = 3;
0286: private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT;
0287: private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT;
0288: private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT;
0289: private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT;
0290:
0291: /**
0292: * The X coordinate scaling element of the 3x3
0293: * affine transformation matrix.
0294: *
0295: * @serial
0296: */
0297: double m00;
0298:
0299: /**
0300: * The Y coordinate shearing element of the 3x3
0301: * affine transformation matrix.
0302: *
0303: * @serial
0304: */
0305: double m10;
0306:
0307: /**
0308: * The X coordinate shearing element of the 3x3
0309: * affine transformation matrix.
0310: *
0311: * @serial
0312: */
0313: double m01;
0314:
0315: /**
0316: * The Y coordinate scaling element of the 3x3
0317: * affine transformation matrix.
0318: *
0319: * @serial
0320: */
0321: double m11;
0322:
0323: /**
0324: * The X coordinate of the translation element of the
0325: * 3x3 affine transformation matrix.
0326: *
0327: * @serial
0328: */
0329: double m02;
0330:
0331: /**
0332: * The Y coordinate of the translation element of the
0333: * 3x3 affine transformation matrix.
0334: *
0335: * @serial
0336: */
0337: double m12;
0338:
0339: /**
0340: * This field keeps track of which components of the matrix need to
0341: * be applied when performing a transformation.
0342: * @see #APPLY_IDENTITY
0343: * @see #APPLY_TRANSLATE
0344: * @see #APPLY_SCALE
0345: * @see #APPLY_SHEAR
0346: */
0347: transient int state;
0348:
0349: /**
0350: * This field caches the current transformation type of the matrix.
0351: * @see #TYPE_IDENTITY
0352: * @see #TYPE_TRANSLATION
0353: * @see #TYPE_UNIFORM_SCALE
0354: * @see #TYPE_GENERAL_SCALE
0355: * @see #TYPE_FLIP
0356: * @see #TYPE_QUADRANT_ROTATION
0357: * @see #TYPE_GENERAL_ROTATION
0358: * @see #TYPE_GENERAL_TRANSFORM
0359: * @see #TYPE_UNKNOWN
0360: * @see #getType
0361: */
0362: private transient int type;
0363:
0364: private AffineTransform(double m00, double m10, double m01,
0365: double m11, double m02, double m12, int state) {
0366: this .m00 = m00;
0367: this .m10 = m10;
0368: this .m01 = m01;
0369: this .m11 = m11;
0370: this .m02 = m02;
0371: this .m12 = m12;
0372: this .state = state;
0373: this .type = TYPE_UNKNOWN;
0374: }
0375:
0376: /**
0377: * Constructs a new <code>AffineTransform</code> representing the
0378: * Identity transformation.
0379: */
0380: public AffineTransform() {
0381: m00 = m11 = 1.0;
0382: // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */
0383: // state = APPLY_IDENTITY; /* Not needed. */
0384: // type = TYPE_IDENTITY; /* Not needed. */
0385: }
0386:
0387: /**
0388: * Constructs a new <code>AffineTransform</code> that is a copy of
0389: * the specified <code>AffineTransform</code> object.
0390: * @param Tx the <code>AffineTransform</code> object to copy
0391: */
0392: public AffineTransform(AffineTransform Tx) {
0393: this .m00 = Tx.m00;
0394: this .m10 = Tx.m10;
0395: this .m01 = Tx.m01;
0396: this .m11 = Tx.m11;
0397: this .m02 = Tx.m02;
0398: this .m12 = Tx.m12;
0399: this .state = Tx.state;
0400: this .type = Tx.type;
0401: }
0402:
0403: /**
0404: * Constructs a new <code>AffineTransform</code> from 6 floating point
0405: * values representing the 6 specifiable entries of the 3x3
0406: * transformation matrix.
0407: * @param m00, m01, m02, m10, m11, m12 the
0408: * 6 floating point values that compose the 3x3 transformation matrix
0409: */
0410: public AffineTransform(float m00, float m10, float m01, float m11,
0411: float m02, float m12) {
0412: this .m00 = m00;
0413: this .m10 = m10;
0414: this .m01 = m01;
0415: this .m11 = m11;
0416: this .m02 = m02;
0417: this .m12 = m12;
0418: updateState();
0419: }
0420:
0421: /**
0422: * Constructs a new <code>AffineTransform</code> from an array of
0423: * floating point values representing either the 4 non-translation
0424: * enries or the 6 specifiable entries of the 3x3 transformation
0425: * matrix. The values are retrieved from the array as
0426: * { m00 m10 m01 m11 [m02 m12]}.
0427: * @param flatmatrix the float array containing the values to be set
0428: * in the new <code>AffineTransform</code> object. The length of the
0429: * array is assumed to be at least 4. If the length of the array is
0430: * less than 6, only the first 4 values are taken. If the length of
0431: * the array is greater than 6, the first 6 values are taken.
0432: */
0433: public AffineTransform(float[] flatmatrix) {
0434: m00 = flatmatrix[0];
0435: m10 = flatmatrix[1];
0436: m01 = flatmatrix[2];
0437: m11 = flatmatrix[3];
0438: if (flatmatrix.length > 5) {
0439: m02 = flatmatrix[4];
0440: m12 = flatmatrix[5];
0441: }
0442: updateState();
0443: }
0444:
0445: /**
0446: * Constructs a new <code>AffineTransform</code> from 6 double
0447: * precision values representing the 6 specifiable entries of the 3x3
0448: * transformation matrix.
0449: * @param m00, m01, m02, m10, m11, m12 the
0450: * 6 floating point values that compose the 3x3 transformation matrix
0451: */
0452: public AffineTransform(double m00, double m10, double m01,
0453: double m11, double m02, double m12) {
0454: this .m00 = m00;
0455: this .m10 = m10;
0456: this .m01 = m01;
0457: this .m11 = m11;
0458: this .m02 = m02;
0459: this .m12 = m12;
0460: updateState();
0461: }
0462:
0463: /**
0464: * Constructs a new <code>AffineTransform</code> from an array of
0465: * double precision values representing either the 4 non-translation
0466: * entries or the 6 specifiable entries of the 3x3 transformation
0467: * matrix. The values are retrieved from the array as
0468: * { m00 m10 m01 m11 [m02 m12]}.
0469: * @param flatmatrix the double array containing the values to be set
0470: * in the new <code>AffineTransform</code> object. The length of the
0471: * array is assumed to be at least 4. If the length of the array is
0472: * less than 6, only the first 4 values are taken. If the length of
0473: * the array is greater than 6, the first 6 values are taken.
0474: */
0475: public AffineTransform(double[] flatmatrix) {
0476: m00 = flatmatrix[0];
0477: m10 = flatmatrix[1];
0478: m01 = flatmatrix[2];
0479: m11 = flatmatrix[3];
0480: if (flatmatrix.length > 5) {
0481: m02 = flatmatrix[4];
0482: m12 = flatmatrix[5];
0483: }
0484: updateState();
0485: }
0486:
0487: /**
0488: * Returns a transform representing a translation transformation.
0489: * The matrix representing the returned transform is:
0490: * <pre>
0491: * [ 1 0 tx ]
0492: * [ 0 1 ty ]
0493: * [ 0 0 1 ]
0494: * </pre>
0495: * @param tx the distance by which coordinates are translated in the
0496: * X axis direction
0497: * @param ty the distance by which coordinates are translated in the
0498: * Y axis direction
0499: * @return an <code>AffineTransform</code> object that represents a
0500: * translation transformation, created with the specified vector.
0501: */
0502: public static AffineTransform getTranslateInstance(double tx,
0503: double ty) {
0504: AffineTransform Tx = new AffineTransform();
0505: Tx.setToTranslation(tx, ty);
0506: return Tx;
0507: }
0508:
0509: /**
0510: * Returns a transform representing a rotation transformation.
0511: * The matrix representing the returned transform is:
0512: * <pre>
0513: * [ cos(theta) -sin(theta) 0 ]
0514: * [ sin(theta) cos(theta) 0 ]
0515: * [ 0 0 1 ]
0516: * </pre>
0517: * Rotating with a positive angle theta rotates points on the positive
0518: * x axis toward the positive y axis.
0519: * @param theta the angle of rotation in radians
0520: * @return an <code>AffineTransform</code> object that is a rotation
0521: * transformation, created with the specified angle of rotation.
0522: */
0523: public static AffineTransform getRotateInstance(double theta) {
0524: AffineTransform Tx = new AffineTransform();
0525: Tx.setToRotation(theta);
0526: return Tx;
0527: }
0528:
0529: /**
0530: * Returns a transform that rotates coordinates around an anchor point.
0531: * This operation is equivalent to translating the coordinates so
0532: * that the anchor point is at the origin (S1), then rotating them
0533: * about the new origin (S2), and finally translating so that the
0534: * intermediate origin is restored to the coordinates of the original
0535: * anchor point (S3).
0536: * <p>
0537: * This operation is equivalent to the following sequence of calls:
0538: * <pre>
0539: * AffineTransform Tx = new AffineTransform();
0540: * Tx.setToTranslation(x, y); // S3: final translation
0541: * Tx.rotate(theta); // S2: rotate around anchor
0542: * Tx.translate(-x, -y); // S1: translate anchor to origin
0543: * </pre>
0544: * The matrix representing the returned transform is:
0545: * <pre>
0546: * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
0547: * [ sin(theta) cos(theta) y-x*sin-y*cos ]
0548: * [ 0 0 1 ]
0549: * </pre>
0550: * Rotating with a positive angle theta rotates points on the positive
0551: * x axis toward the positive y axis.
0552: * @param theta the angle of rotation in radians
0553: * @param x, y the coordinates of the anchor point of the
0554: * rotation
0555: * @return an <code>AffineTransform</code> object that rotates
0556: * coordinates around the specified point by the specified angle of
0557: * rotation.
0558: */
0559: public static AffineTransform getRotateInstance(double theta,
0560: double x, double y) {
0561: AffineTransform Tx = new AffineTransform();
0562: Tx.setToRotation(theta, x, y);
0563: return Tx;
0564: }
0565:
0566: /**
0567: * Returns a transform representing a scaling transformation.
0568: * The matrix representing the returned transform is:
0569: * <pre>
0570: * [ sx 0 0 ]
0571: * [ 0 sy 0 ]
0572: * [ 0 0 1 ]
0573: * </pre>
0574: * @param sx the factor by which coordinates are scaled along the
0575: * X axis direction
0576: * @param sy the factor by which coordinates are scaled along the
0577: * Y axis direction
0578: * @return an <code>AffineTransform</code> object that scales
0579: * coordinates by the specified factors.
0580: */
0581: public static AffineTransform getScaleInstance(double sx, double sy) {
0582: AffineTransform Tx = new AffineTransform();
0583: Tx.setToScale(sx, sy);
0584: return Tx;
0585: }
0586:
0587: /**
0588: * Returns a transform representing a shearing transformation.
0589: * The matrix representing the returned transform is:
0590: * <pre>
0591: * [ 1 shx 0 ]
0592: * [ shy 1 0 ]
0593: * [ 0 0 1 ]
0594: * </pre>
0595: * @param shx the multiplier by which coordinates are shifted in the
0596: * direction of the positive X axis as a factor of their Y coordinate
0597: * @param shy the multiplier by which coordinates are shifted in the
0598: * direction of the positive Y axis as a factor of their X coordinate
0599: * @return an <code>AffineTransform</code> object that shears
0600: * coordinates by the specified multipliers.
0601: */
0602: public static AffineTransform getShearInstance(double shx,
0603: double shy) {
0604: AffineTransform Tx = new AffineTransform();
0605: Tx.setToShear(shx, shy);
0606: return Tx;
0607: }
0608:
0609: /**
0610: * Retrieves the flag bits describing the conversion properties of
0611: * this transform.
0612: * The return value is either one of the constants TYPE_IDENTITY
0613: * or TYPE_GENERAL_TRANSFORM, or a combination of the
0614: * appriopriate flag bits.
0615: * A valid combination of flag bits is an exclusive OR operation
0616: * that can combine
0617: * the TYPE_TRANSLATION flag bit
0618: * in addition to either of the
0619: * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits
0620: * as well as either of the
0621: * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits.
0622: * @return the OR combination of any of the indicated flags that
0623: * apply to this transform
0624: * @see #TYPE_IDENTITY
0625: * @see #TYPE_TRANSLATION
0626: * @see #TYPE_UNIFORM_SCALE
0627: * @see #TYPE_GENERAL_SCALE
0628: * @see #TYPE_QUADRANT_ROTATION
0629: * @see #TYPE_GENERAL_ROTATION
0630: * @see #TYPE_GENERAL_TRANSFORM
0631: */
0632: public int getType() {
0633: if (type == TYPE_UNKNOWN) {
0634: calculateType();
0635: }
0636: return type;
0637: }
0638:
0639: /**
0640: * This is the utility function to calculate the flag bits when
0641: * they have not been cached.
0642: * @see #getType
0643: */
0644: private void calculateType() {
0645: int ret = TYPE_IDENTITY;
0646: boolean sgn0, sgn1;
0647: double M0, M1, M2, M3;
0648: updateState();
0649: switch (state) {
0650: default:
0651: stateError();
0652: break;
0653: /* NOTREACHED */
0654: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0655: ret = TYPE_TRANSLATION;
0656: /* NOBREAK */
0657: case (APPLY_SHEAR | APPLY_SCALE):
0658: if ((M0 = m00) * (M2 = m01) + (M3 = m10) * (M1 = m11) != 0) {
0659: // Transformed unit vectors are not perpendicular...
0660: this .type = TYPE_GENERAL_TRANSFORM;
0661: return;
0662: }
0663: sgn0 = (M0 >= 0.0);
0664: sgn1 = (M1 >= 0.0);
0665: if (sgn0 == sgn1) {
0666: // sgn(M0) == sgn(M1) therefore sgn(M2) == -sgn(M3)
0667: // This is the "unflipped" (right-handed) state
0668: if (M0 != M1 || M2 != -M3) {
0669: ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE);
0670: } else if (M0 * M1 - M2 * M3 != 1.0) {
0671: ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE);
0672: } else {
0673: ret |= TYPE_GENERAL_ROTATION;
0674: }
0675: } else {
0676: // sgn(M0) == -sgn(M1) therefore sgn(M2) == sgn(M3)
0677: // This is the "flipped" (left-handed) state
0678: if (M0 != -M1 || M2 != M3) {
0679: ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE);
0680: } else if (M0 * M1 - M2 * M3 != 1.0) {
0681: ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE);
0682: } else {
0683: ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP);
0684: }
0685: }
0686: break;
0687: case (APPLY_SHEAR | APPLY_TRANSLATE):
0688: ret = TYPE_TRANSLATION;
0689: /* NOBREAK */
0690: case (APPLY_SHEAR):
0691: sgn0 = ((M0 = m01) >= 0.0);
0692: sgn1 = ((M1 = m10) >= 0.0);
0693: if (sgn0 != sgn1) {
0694: // Different signs - simple 90 degree rotation
0695: if (M0 != -M1) {
0696: ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
0697: } else if (M0 != 1.0 && M0 != -1.0) {
0698: ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
0699: } else {
0700: ret |= TYPE_QUADRANT_ROTATION;
0701: }
0702: } else {
0703: // Same signs - 90 degree rotation plus an axis flip too
0704: if (M0 == M1) {
0705: ret |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE);
0706: } else {
0707: ret |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE);
0708: }
0709: }
0710: break;
0711: case (APPLY_SCALE | APPLY_TRANSLATE):
0712: ret = TYPE_TRANSLATION;
0713: /* NOBREAK */
0714: case (APPLY_SCALE):
0715: sgn0 = ((M0 = m00) >= 0.0);
0716: sgn1 = ((M1 = m11) >= 0.0);
0717: if (sgn0 == sgn1) {
0718: if (sgn0) {
0719: // Both scaling factors non-negative - simple scale
0720: // Note: APPLY_SCALE implies M0, M1 are not both 1
0721: if (M0 == M1) {
0722: ret |= TYPE_UNIFORM_SCALE;
0723: } else {
0724: ret |= TYPE_GENERAL_SCALE;
0725: }
0726: } else {
0727: // Both scaling factors negative - 180 degree rotation
0728: if (M0 != M1) {
0729: ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
0730: } else if (M0 != -1.0) {
0731: ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
0732: } else {
0733: ret |= TYPE_QUADRANT_ROTATION;
0734: }
0735: }
0736: } else {
0737: // Scaling factor signs different - flip about some axis
0738: if (M0 == -M1) {
0739: if (M0 == 1.0 || M0 == -1.0) {
0740: ret |= TYPE_FLIP;
0741: } else {
0742: ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE);
0743: }
0744: } else {
0745: ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE);
0746: }
0747: }
0748: break;
0749: case (APPLY_TRANSLATE):
0750: ret = TYPE_TRANSLATION;
0751: break;
0752: case (APPLY_IDENTITY):
0753: break;
0754: }
0755: this .type = ret;
0756: }
0757:
0758: /**
0759: * Returns the determinant of the matrix representation of the transform.
0760: * The determinant is useful both to determine if the transform can
0761: * be inverted and to get a single value representing the
0762: * combined X and Y scaling of the transform.
0763: * <p>
0764: * If the determinant is non-zero, then this transform is
0765: * invertible and the various methods that depend on the inverse
0766: * transform do not need to throw a
0767: * {@link NoninvertibleTransformException}.
0768: * If the determinant is zero then this transform can not be
0769: * inverted since the transform maps all input coordinates onto
0770: * a line or a point.
0771: * If the determinant is near enough to zero then inverse transform
0772: * operations might not carry enough precision to produce meaningful
0773: * results.
0774: * <p>
0775: * If this transform represents a uniform scale, as indicated by
0776: * the <code>getType</code> method then the determinant also
0777: * represents the square of the uniform scale factor by which all of
0778: * the points are expanded from or contracted towards the origin.
0779: * If this transform represents a non-uniform scale or more general
0780: * transform then the determinant is not likely to represent a
0781: * value useful for any purpose other than determining if inverse
0782: * transforms are possible.
0783: * <p>
0784: * Mathematically, the determinant is calculated using the formula:
0785: * <pre>
0786: * | m00 m01 m02 |
0787: * | m10 m11 m12 | = m00 * m11 - m01 * m10
0788: * | 0 0 1 |
0789: * </pre>
0790: *
0791: * @return the determinant of the matrix used to transform the
0792: * coordinates.
0793: * @see #getType
0794: * @see #createInverse
0795: * @see #inverseTransform
0796: * @see #TYPE_UNIFORM_SCALE
0797: */
0798: public double getDeterminant() {
0799: switch (state) {
0800: default:
0801: stateError();
0802: return 0;
0803: /* NOTREACHED */
0804: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0805: case (APPLY_SHEAR | APPLY_SCALE):
0806: return m00 * m11 - m01 * m10;
0807: case (APPLY_SHEAR | APPLY_TRANSLATE):
0808: case (APPLY_SHEAR):
0809: return -(m01 * m10);
0810: case (APPLY_SCALE | APPLY_TRANSLATE):
0811: case (APPLY_SCALE):
0812: return m00 * m11;
0813: case (APPLY_TRANSLATE):
0814: case (APPLY_IDENTITY):
0815: return 1.0;
0816: }
0817: }
0818:
0819: /**
0820: * Manually recalculates the state of the transform when the matrix
0821: * changes too much to predict the effects on the state.
0822: * The following table specifies what the various settings of the
0823: * state field say about the values of the corresponding matrix
0824: * element fields.
0825: * Note that the rules governing the SCALE fields are slightly
0826: * different depending on whether the SHEAR flag is also set.
0827: * <pre>
0828: * SCALE SHEAR TRANSLATE
0829: * m00/m11 m01/m10 m02/m12
0830: *
0831: * IDENTITY 1.0 0.0 0.0
0832: * TRANSLATE (TR) 1.0 0.0 not both 0.0
0833: * SCALE (SC) not both 1.0 0.0 0.0
0834: * TR | SC not both 1.0 0.0 not both 0.0
0835: * SHEAR (SH) 0.0 not both 0.0 0.0
0836: * TR | SH 0.0 not both 0.0 not both 0.0
0837: * SC | SH not both 0.0 not both 0.0 0.0
0838: * TR | SC | SH not both 0.0 not both 0.0 not both 0.0
0839: * </pre>
0840: */
0841: void updateState() {
0842: if (m01 == 0.0 && m10 == 0.0) {
0843: if (m00 == 1.0 && m11 == 1.0) {
0844: if (m02 == 0.0 && m12 == 0.0) {
0845: state = APPLY_IDENTITY;
0846: type = TYPE_IDENTITY;
0847: } else {
0848: state = APPLY_TRANSLATE;
0849: type = TYPE_TRANSLATION;
0850: }
0851: } else {
0852: if (m02 == 0.0 && m12 == 0.0) {
0853: state = APPLY_SCALE;
0854: type = TYPE_UNKNOWN;
0855: } else {
0856: state = (APPLY_SCALE | APPLY_TRANSLATE);
0857: type = TYPE_UNKNOWN;
0858: }
0859: }
0860: } else {
0861: if (m00 == 0.0 && m11 == 0.0) {
0862: if (m02 == 0.0 && m12 == 0.0) {
0863: state = APPLY_SHEAR;
0864: type = TYPE_UNKNOWN;
0865: } else {
0866: state = (APPLY_SHEAR | APPLY_TRANSLATE);
0867: type = TYPE_UNKNOWN;
0868: }
0869: } else {
0870: if (m02 == 0.0 && m12 == 0.0) {
0871: state = (APPLY_SHEAR | APPLY_SCALE);
0872: type = TYPE_UNKNOWN;
0873: } else {
0874: state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);
0875: type = TYPE_UNKNOWN;
0876: }
0877: }
0878: }
0879: }
0880:
0881: /*
0882: * Convenience method used internally to throw exceptions when
0883: * a case was forgotten in a switch statement.
0884: */
0885: private void stateError() {
0886: //#ifdef notdef
0887: throw new InternalError(
0888: "missing case in transform state switch");
0889: //#else
0890: System.out.println("missing case in transform state switch");
0891: //#endif
0892: }
0893:
0894: /**
0895: * Retrieves the 6 specifiable values in the 3x3 affine transformation
0896: * matrix and places them into an array of double precisions values.
0897: * The values are stored in the array as
0898: * { m00 m10 m01 m11 m02 m12 }.
0899: * An array of 4 doubles can also be specified, in which case only the
0900: * first four elements representing the non-transform
0901: * parts of the array are retrieved and the values are stored into
0902: * the array as { m00 m10 m01 m11 }
0903: * @param flatmatrix the double array used to store the returned
0904: * values.
0905: * @see #getScaleX
0906: * @see #getScaleY
0907: * @see #getShearX
0908: * @see #getShearY
0909: * @see #getTranslateX
0910: * @see #getTranslateY
0911: */
0912: public void getMatrix(double[] flatmatrix) {
0913: flatmatrix[0] = m00;
0914: flatmatrix[1] = m10;
0915: flatmatrix[2] = m01;
0916: flatmatrix[3] = m11;
0917: if (flatmatrix.length > 5) {
0918: flatmatrix[4] = m02;
0919: flatmatrix[5] = m12;
0920: }
0921: }
0922:
0923: /**
0924: * Returns the X coordinate scaling element (m00) of the 3x3
0925: * affine transformation matrix.
0926: * @return a double value that is the X coordinate of the scaling
0927: * element of the affine transformation matrix.
0928: * @see #getMatrix
0929: */
0930: public double getScaleX() {
0931: return m00;
0932: }
0933:
0934: /**
0935: * Returns the Y coordinate scaling element (m11) of the 3x3
0936: * affine transformation matrix.
0937: * @return a double value that is the Y coordinate of the scaling
0938: * element of the affine transformation matrix.
0939: * @see #getMatrix
0940: */
0941: public double getScaleY() {
0942: return m11;
0943: }
0944:
0945: /**
0946: * Returns the X coordinate shearing element (m01) of the 3x3
0947: * affine transformation matrix.
0948: * @return a double value that is the X coordinate of the shearing
0949: * element of the affine transformation matrix.
0950: * @see #getMatrix
0951: */
0952: public double getShearX() {
0953: return m01;
0954: }
0955:
0956: /**
0957: * Returns the Y coordinate shearing element (m10) of the 3x3
0958: * affine transformation matrix.
0959: * @return a double value that is the Y coordinate of the shearing
0960: * element of the affine transformation matrix.
0961: * @see #getMatrix
0962: */
0963: public double getShearY() {
0964: return m10;
0965: }
0966:
0967: /**
0968: * Returns the X coordinate of the translation element (m02) of the
0969: * 3x3 affine transformation matrix.
0970: * @return a double value that is the X coordinate of the translation
0971: * element of the affine transformation matrix.
0972: * @see #getMatrix
0973: */
0974: public double getTranslateX() {
0975: return m02;
0976: }
0977:
0978: /**
0979: * Returns the Y coordinate of the translation element (m12) of the
0980: * 3x3 affine transformation matrix.
0981: * @return a double value that is the Y coordinate of the translation
0982: * element of the affine transformation matrix.
0983: * @see #getMatrix
0984: */
0985: public double getTranslateY() {
0986: return m12;
0987: }
0988:
0989: /**
0990: * Concatenates this transform with a translation transformation.
0991: * This is equivalent to calling concatenate(T), where T is an
0992: * <code>AffineTransform</code> represented by the following matrix:
0993: * <pre>
0994: * [ 1 0 tx ]
0995: * [ 0 1 ty ]
0996: * [ 0 0 1 ]
0997: * </pre>
0998: * @param tx the distance by which coordinates are translated in the
0999: * X axis direction
1000: * @param ty the distance by which coordinates are translated in the
1001: * Y axis direction
1002: */
1003: public void translate(double tx, double ty) {
1004: switch (state) {
1005: default:
1006: stateError();
1007: break;
1008: /* NOTREACHED */
1009: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1010: m02 = tx * m00 + ty * m01 + m02;
1011: m12 = tx * m10 + ty * m11 + m12;
1012: if (m02 == 0.0 && m12 == 0.0) {
1013: state = APPLY_SHEAR | APPLY_SCALE;
1014: if (type != TYPE_UNKNOWN) {
1015: type -= TYPE_TRANSLATION;
1016: }
1017: }
1018: return;
1019: case (APPLY_SHEAR | APPLY_SCALE):
1020: m02 = tx * m00 + ty * m01;
1021: m12 = tx * m10 + ty * m11;
1022: if (m02 != 0.0 || m12 != 0.0) {
1023: state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
1024: type |= TYPE_TRANSLATION;
1025: }
1026: return;
1027: case (APPLY_SHEAR | APPLY_TRANSLATE):
1028: m02 = ty * m01 + m02;
1029: m12 = tx * m10 + m12;
1030: if (m02 == 0.0 && m12 == 0.0) {
1031: state = APPLY_SHEAR;
1032: if (type != TYPE_UNKNOWN) {
1033: type -= TYPE_TRANSLATION;
1034: }
1035: }
1036: return;
1037: case (APPLY_SHEAR):
1038: m02 = ty * m01;
1039: m12 = tx * m10;
1040: if (m02 != 0.0 || m12 != 0.0) {
1041: state = APPLY_SHEAR | APPLY_TRANSLATE;
1042: type |= TYPE_TRANSLATION;
1043: }
1044: return;
1045: case (APPLY_SCALE | APPLY_TRANSLATE):
1046: m02 = tx * m00 + m02;
1047: m12 = ty * m11 + m12;
1048: if (m02 == 0.0 && m12 == 0.0) {
1049: state = APPLY_SCALE;
1050: if (type != TYPE_UNKNOWN) {
1051: type -= TYPE_TRANSLATION;
1052: }
1053: }
1054: return;
1055: case (APPLY_SCALE):
1056: m02 = tx * m00;
1057: m12 = ty * m11;
1058: if (m02 != 0.0 || m12 != 0.0) {
1059: state = APPLY_SCALE | APPLY_TRANSLATE;
1060: type |= TYPE_TRANSLATION;
1061: }
1062: return;
1063: case (APPLY_TRANSLATE):
1064: m02 = tx + m02;
1065: m12 = ty + m12;
1066: if (m02 == 0.0 && m12 == 0.0) {
1067: state = APPLY_IDENTITY;
1068: type = TYPE_IDENTITY;
1069: }
1070: return;
1071: case (APPLY_IDENTITY):
1072: m02 = tx;
1073: m12 = ty;
1074: if (tx != 0.0 || ty != 0.0) {
1075: state = APPLY_TRANSLATE;
1076: type = TYPE_TRANSLATION;
1077: }
1078: return;
1079: }
1080: }
1081:
1082: /**
1083: * Concatenates this transform with a rotation transformation.
1084: * This is equivalent to calling concatenate(R), where R is an
1085: * <code>AffineTransform</code> represented by the following matrix:
1086: * <pre>
1087: * [ cos(theta) -sin(theta) 0 ]
1088: * [ sin(theta) cos(theta) 0 ]
1089: * [ 0 0 1 ]
1090: * </pre>
1091: * Rotating with a positive angle theta rotates points on the positive
1092: * x axis toward the positive y axis.
1093: * @param theta the angle of rotation in radians
1094: */
1095: public void rotate(double theta) {
1096: double sin = Math.sin(theta);
1097: double cos = Math.cos(theta);
1098: if (Math.abs(sin) < 1E-15) {
1099: if (cos < 0.0) {
1100: m00 = -m00;
1101: m11 = -m11;
1102: int state = this .state;
1103: if ((state & (APPLY_SHEAR)) != 0) {
1104: // If there was a shear, then this rotation has no
1105: // effect on the state.
1106: m01 = -m01;
1107: m10 = -m10;
1108: } else {
1109: // No shear means the SCALE state may toggle when
1110: // m00 and m11 are negated.
1111: if (m00 == 1.0 && m11 == 1.0) {
1112: this .state = state & ~APPLY_SCALE;
1113: } else {
1114: this .state = state | APPLY_SCALE;
1115: }
1116: }
1117: type = TYPE_UNKNOWN;
1118: }
1119: return;
1120: }
1121: if (Math.abs(cos) < 1E-15) {
1122: if (sin < 0.0) {
1123: double M0 = m00;
1124: m00 = -m01;
1125: m01 = M0;
1126: M0 = m10;
1127: m10 = -m11;
1128: m11 = M0;
1129: } else {
1130: double M0 = m00;
1131: m00 = m01;
1132: m01 = -M0;
1133: M0 = m10;
1134: m10 = m11;
1135: m11 = -M0;
1136: }
1137: int state = rot90conversion[this .state];
1138: if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE
1139: && m00 == 1.0 && m11 == 1.0) {
1140: state -= APPLY_SCALE;
1141: }
1142: this .state = state;
1143: type = TYPE_UNKNOWN;
1144: return;
1145: }
1146: double M0, M1;
1147: M0 = m00;
1148: M1 = m01;
1149: m00 = cos * M0 + sin * M1;
1150: m01 = -sin * M0 + cos * M1;
1151: M0 = m10;
1152: M1 = m11;
1153: m10 = cos * M0 + sin * M1;
1154: m11 = -sin * M0 + cos * M1;
1155: updateState();
1156: }
1157:
1158: private static int rot90conversion[] = { APPLY_SHEAR,
1159: APPLY_SHEAR | APPLY_TRANSLATE, APPLY_SHEAR,
1160: APPLY_SHEAR | APPLY_TRANSLATE, APPLY_SCALE,
1161: APPLY_SCALE | APPLY_TRANSLATE, APPLY_SHEAR | APPLY_SCALE,
1162: APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE, };
1163:
1164: /**
1165: * Concatenates this transform with a transform that rotates
1166: * coordinates around an anchor point.
1167: * This operation is equivalent to translating the coordinates so
1168: * that the anchor point is at the origin (S1), then rotating them
1169: * about the new origin (S2), and finally translating so that the
1170: * intermediate origin is restored to the coordinates of the original
1171: * anchor point (S3).
1172: * <p>
1173: * This operation is equivalent to the following sequence of calls:
1174: * <pre>
1175: * translate(x, y); // S3: final translation
1176: * rotate(theta); // S2: rotate around anchor
1177: * translate(-x, -y); // S1: translate anchor to origin
1178: * </pre>
1179: * Rotating with a positive angle theta rotates points on the positive
1180: * x axis toward the positive y axis.
1181: * @param theta the angle of rotation in radians
1182: * @param x, y the coordinates of the anchor point of the
1183: * rotation
1184: */
1185: public void rotate(double theta, double x, double y) {
1186: // REMIND: Simple for now - optimize later
1187: translate(x, y);
1188: rotate(theta);
1189: translate(-x, -y);
1190: }
1191:
1192: /**
1193: * Concatenates this transform with a scaling transformation.
1194: * This is equivalent to calling concatenate(S), where S is an
1195: * <code>AffineTransform</code> represented by the following matrix:
1196: * <pre>
1197: * [ sx 0 0 ]
1198: * [ 0 sy 0 ]
1199: * [ 0 0 1 ]
1200: * </pre>
1201: * @param sx the factor by which coordinates are scaled along the
1202: * X axis direction
1203: * @param sy the factor by which coordinates are scaled along the
1204: * Y axis direction
1205: */
1206: public void scale(double sx, double sy) {
1207: int state = this .state;
1208: switch (state) {
1209: default:
1210: stateError();
1211: break;
1212: /* NOTREACHED */
1213: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1214: case (APPLY_SHEAR | APPLY_SCALE):
1215: m00 *= sx;
1216: m11 *= sy;
1217: /* NOBREAK */
1218: case (APPLY_SHEAR | APPLY_TRANSLATE):
1219: case (APPLY_SHEAR):
1220: m01 *= sy;
1221: m10 *= sx;
1222: if (m01 == 0 && m10 == 0) {
1223: this .state = state - APPLY_SHEAR;
1224: // REMIND: Is it possible for m00 and m11 to be both 1.0?
1225: }
1226: this .type = TYPE_UNKNOWN;
1227: return;
1228: case (APPLY_SCALE | APPLY_TRANSLATE):
1229: case (APPLY_SCALE):
1230: m00 *= sx;
1231: m11 *= sy;
1232: if (m00 == 1.0 && m11 == 1.0) {
1233: this .state = (state &= APPLY_TRANSLATE);
1234: this .type = (state == APPLY_IDENTITY ? TYPE_IDENTITY
1235: : TYPE_TRANSLATION);
1236: } else {
1237: this .type = TYPE_UNKNOWN;
1238: }
1239: return;
1240: case (APPLY_TRANSLATE):
1241: case (APPLY_IDENTITY):
1242: m00 = sx;
1243: m11 = sy;
1244: if (sx != 1.0 || sy != 1.0) {
1245: this .state = state | APPLY_SCALE;
1246: this .type = TYPE_UNKNOWN;
1247: }
1248: return;
1249: }
1250: }
1251:
1252: /**
1253: * Concatenates this transform with a shearing transformation.
1254: * This is equivalent to calling concatenate(SH), where SH is an
1255: * <code>AffineTransform</code> represented by the following matrix:
1256: * <pre>
1257: * [ 1 shx 0 ]
1258: * [ shy 1 0 ]
1259: * [ 0 0 1 ]
1260: * </pre>
1261: * @param shx the multiplier by which coordinates are shifted in the
1262: * direction of the positive X axis as a factor of their Y coordinate
1263: * @param shy the multiplier by which coordinates are shifted in the
1264: * direction of the positive Y axis as a factor of their X coordinate
1265: */
1266: public void shear(double shx, double shy) {
1267: int state = this .state;
1268: switch (state) {
1269: default:
1270: stateError();
1271: break;
1272: /* NOTREACHED */
1273: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1274: case (APPLY_SHEAR | APPLY_SCALE):
1275: double M0,
1276: M1;
1277: M0 = m00;
1278: M1 = m01;
1279: m00 = M0 + M1 * shy;
1280: m01 = M0 * shx + M1;
1281:
1282: M0 = m10;
1283: M1 = m11;
1284: m10 = M0 + M1 * shy;
1285: m11 = M0 * shx + M1;
1286: updateState();
1287: break;
1288: case (APPLY_SHEAR | APPLY_TRANSLATE):
1289: case (APPLY_SHEAR):
1290: m00 = m01 * shy;
1291: m11 = m10 * shx;
1292: if (m00 != 0.0 || m11 != 0.0) {
1293: this .state = state | APPLY_SCALE;
1294: }
1295: this .type = TYPE_UNKNOWN;
1296: break;
1297: case (APPLY_SCALE | APPLY_TRANSLATE):
1298: case (APPLY_SCALE):
1299: m01 = m00 * shx;
1300: m10 = m11 * shy;
1301: if (m01 != 0.0 || m10 != 0.0) {
1302: this .state = state | APPLY_SHEAR;
1303: }
1304: this .type = TYPE_UNKNOWN;
1305: break;
1306: case (APPLY_TRANSLATE):
1307: case (APPLY_IDENTITY):
1308: m01 = shx;
1309: m10 = shy;
1310: if (m01 != 0.0 || m10 != 0.0) {
1311: this .state = state | APPLY_SCALE | APPLY_SHEAR;
1312: this .type = TYPE_UNKNOWN;
1313: }
1314: break;
1315: }
1316: }
1317:
1318: /**
1319: * Resets this transform to the Identity transform.
1320: */
1321: public void setToIdentity() {
1322: m00 = m11 = 1.0;
1323: m10 = m01 = m02 = m12 = 0.0;
1324: state = APPLY_IDENTITY;
1325: type = TYPE_IDENTITY;
1326: }
1327:
1328: /**
1329: * Sets this transform to a translation transformation.
1330: * The matrix representing this transform becomes:
1331: * <pre>
1332: * [ 1 0 tx ]
1333: * [ 0 1 ty ]
1334: * [ 0 0 1 ]
1335: * </pre>
1336: * @param tx the distance by which coordinates are translated in the
1337: * X axis direction
1338: * @param ty the distance by which coordinates are translated in the
1339: * Y axis direction
1340: */
1341: public void setToTranslation(double tx, double ty) {
1342: m00 = m11 = 1.0;
1343: m10 = m01 = 0.0;
1344: m02 = tx;
1345: m12 = ty;
1346: if (tx != 0.0 || ty != 0.0) {
1347: state = APPLY_TRANSLATE;
1348: type = TYPE_TRANSLATION;
1349: } else {
1350: state = APPLY_IDENTITY;
1351: type = TYPE_IDENTITY;
1352: }
1353: }
1354:
1355: /**
1356: * Sets this transform to a rotation transformation.
1357: * The matrix representing this transform becomes:
1358: * <pre>
1359: * [ cos(theta) -sin(theta) 0 ]
1360: * [ sin(theta) cos(theta) 0 ]
1361: * [ 0 0 1 ]
1362: * </pre>
1363: * Rotating with a positive angle theta rotates points on the positive
1364: * x axis toward the positive y axis.
1365: * @param theta the angle of rotation in radians
1366: */
1367: public void setToRotation(double theta) {
1368: m02 = m12 = 0.0;
1369: double sin = Math.sin(theta);
1370: double cos = Math.cos(theta);
1371: if (Math.abs(sin) < 1E-15) {
1372: m01 = m10 = 0.0;
1373: if (cos < 0) {
1374: m00 = m11 = -1.0;
1375: state = APPLY_SCALE;
1376: type = TYPE_QUADRANT_ROTATION;
1377: } else {
1378: m00 = m11 = 1.0;
1379: state = APPLY_IDENTITY;
1380: type = TYPE_IDENTITY;
1381: }
1382: return;
1383: }
1384: if (Math.abs(cos) < 1E-15) {
1385: m00 = m11 = 0.0;
1386: if (sin < 0.0) {
1387: m01 = 1.0;
1388: m10 = -1.0;
1389: } else {
1390: m01 = -1.0;
1391: m10 = 1.0;
1392: }
1393: state = APPLY_SHEAR;
1394: type = TYPE_QUADRANT_ROTATION;
1395: return;
1396: }
1397: m00 = cos;
1398: m01 = -sin;
1399: m10 = sin;
1400: m11 = cos;
1401: state = APPLY_SHEAR | APPLY_SCALE;
1402: type = TYPE_GENERAL_ROTATION;
1403: }
1404:
1405: /**
1406: * Sets this transform to a translated rotation transformation.
1407: * This operation is equivalent to translating the coordinates so
1408: * that the anchor point is at the origin (S1), then rotating them
1409: * about the new origin (S2), and finally translating so that the
1410: * intermediate origin is restored to the coordinates of the original
1411: * anchor point (S3).
1412: * <p>
1413: * This operation is equivalent to the following sequence of calls:
1414: * <pre>
1415: * setToTranslation(x, y); // S3: final translation
1416: * rotate(theta); // S2: rotate around anchor
1417: * translate(-x, -y); // S1: translate anchor to origin
1418: * </pre>
1419: * The matrix representing this transform becomes:
1420: * <pre>
1421: * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
1422: * [ sin(theta) cos(theta) y-x*sin-y*cos ]
1423: * [ 0 0 1 ]
1424: * </pre>
1425: * Rotating with a positive angle theta rotates points on the positive
1426: * x axis toward the positive y axis.
1427: * @param theta the angle of rotation in radians
1428: * @param x, y the coordinates of the anchor point of the
1429: * rotation
1430: */
1431: public void setToRotation(double theta, double x, double y) {
1432: setToRotation(theta);
1433: double sin = m10;
1434: double oneMinusCos = 1.0 - m00;
1435: m02 = x * oneMinusCos + y * sin;
1436: m12 = y * oneMinusCos - x * sin;
1437: if (m02 != 0.0 || m12 != 0.0) {
1438: state |= APPLY_TRANSLATE;
1439: type |= TYPE_TRANSLATION;
1440: }
1441: }
1442:
1443: /**
1444: * Sets this transform to a scaling transformation.
1445: * The matrix representing this transform becomes:
1446: * <pre>
1447: * [ sx 0 0 ]
1448: * [ 0 sy 0 ]
1449: * [ 0 0 1 ]
1450: * </pre>
1451: * @param sx the factor by which coordinates are scaled along the
1452: * X axis direction
1453: * @param sy the factor by which coordinates are scaled along the
1454: * Y axis direction
1455: */
1456: public void setToScale(double sx, double sy) {
1457: m01 = m02 = m10 = m12 = 0.0;
1458: m00 = sx;
1459: m11 = sy;
1460: if (sx != 1.0 || sy != 1.0) {
1461: state = APPLY_SCALE;
1462: type = TYPE_UNKNOWN;
1463: } else {
1464: state = APPLY_IDENTITY;
1465: type = TYPE_IDENTITY;
1466: }
1467: }
1468:
1469: /**
1470: * Sets this transform to a shearing transformation.
1471: * The matrix representing this transform becomes:
1472: * <pre>
1473: * [ 1 shx 0 ]
1474: * [ shy 1 0 ]
1475: * [ 0 0 1 ]
1476: * </pre>
1477: * @param shx the multiplier by which coordinates are shifted in the
1478: * direction of the positive X axis as a factor of their Y coordinate
1479: * @param shy the multiplier by which coordinates are shifted in the
1480: * direction of the positive Y axis as a factor of their X coordinate
1481: */
1482: public void setToShear(double shx, double shy) {
1483: m00 = m11 = 1.0;
1484: m02 = m12 = 0.0;
1485: m01 = shx;
1486: m10 = shy;
1487: if (shx != 0.0 || shy != 0.0) {
1488: state = (APPLY_SHEAR | APPLY_SCALE);
1489: type = TYPE_UNKNOWN;
1490: } else {
1491: state = APPLY_IDENTITY;
1492: type = TYPE_IDENTITY;
1493: }
1494: }
1495:
1496: /**
1497: * Sets this transform to a copy of the transform in the specified
1498: * <code>AffineTransform</code> object.
1499: * @param Tx the <code>AffineTransform</code> object from which to
1500: * copy the transform
1501: */
1502: public void setTransform(AffineTransform Tx) {
1503: this .m00 = Tx.m00;
1504: this .m10 = Tx.m10;
1505: this .m01 = Tx.m01;
1506: this .m11 = Tx.m11;
1507: this .m02 = Tx.m02;
1508: this .m12 = Tx.m12;
1509: this .state = Tx.state;
1510: this .type = Tx.type;
1511: }
1512:
1513: /**
1514: * Sets this transform to the matrix specified by the 6
1515: * double precision values.
1516: * @param m00, m01, m02, m10, m11, m12 the
1517: * 6 floating point values that compose the 3x3 transformation matrix
1518: */
1519: public void setTransform(double m00, double m10, double m01,
1520: double m11, double m02, double m12) {
1521: this .m00 = m00;
1522: this .m10 = m10;
1523: this .m01 = m01;
1524: this .m11 = m11;
1525: this .m02 = m02;
1526: this .m12 = m12;
1527: updateState();
1528: }
1529:
1530: /**
1531: * Concatenates an <code>AffineTransform</code> <code>Tx</code> to
1532: * this <code>AffineTransform</code> Cx in the most commonly useful
1533: * way to provide a new user space
1534: * that is mapped to the former user space by <code>Tx</code>.
1535: * Cx is updated to perform the combined transformation.
1536: * Transforming a point p by the updated transform Cx' is
1537: * equivalent to first transforming p by <code>Tx</code> and then
1538: * transforming the result by the original transform Cx like this:
1539: * Cx'(p) = Cx(Tx(p))
1540: * In matrix notation, if this transform Cx is
1541: * represented by the matrix [this] and <code>Tx</code> is represented
1542: * by the matrix [Tx] then this method does the following:
1543: * <pre>
1544: * [this] = [this] x [Tx]
1545: * </pre>
1546: * @param Tx the <code>AffineTransform</code> object to be
1547: * concatenated with this <code>AffineTransform</code> object.
1548: * @see #preConcatenate
1549: */
1550: public void concatenate(AffineTransform Tx) {
1551: double M0, M1;
1552: double T00, T01, T10, T11;
1553: double T02, T12;
1554: int mystate = state;
1555: int txstate = Tx.state;
1556: switch ((txstate << HI_SHIFT) | mystate) {
1557:
1558: /* ---------- Tx == IDENTITY cases ---------- */
1559: case (HI_IDENTITY | APPLY_IDENTITY):
1560: case (HI_IDENTITY | APPLY_TRANSLATE):
1561: case (HI_IDENTITY | APPLY_SCALE):
1562: case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
1563: case (HI_IDENTITY | APPLY_SHEAR):
1564: case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
1565: case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
1566: case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1567: return;
1568:
1569: /* ---------- this == IDENTITY cases ---------- */
1570: case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
1571: m01 = Tx.m01;
1572: m10 = Tx.m10;
1573: /* NOBREAK */
1574: case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
1575: m00 = Tx.m00;
1576: m11 = Tx.m11;
1577: /* NOBREAK */
1578: case (HI_TRANSLATE | APPLY_IDENTITY):
1579: m02 = Tx.m02;
1580: m12 = Tx.m12;
1581: state = txstate;
1582: type = Tx.type;
1583: return;
1584: case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY):
1585: m01 = Tx.m01;
1586: m10 = Tx.m10;
1587: /* NOBREAK */
1588: case (HI_SCALE | APPLY_IDENTITY):
1589: m00 = Tx.m00;
1590: m11 = Tx.m11;
1591: state = txstate;
1592: type = Tx.type;
1593: return;
1594: case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY):
1595: m02 = Tx.m02;
1596: m12 = Tx.m12;
1597: /* NOBREAK */
1598: case (HI_SHEAR | APPLY_IDENTITY):
1599: m01 = Tx.m01;
1600: m10 = Tx.m10;
1601: m00 = m11 = 0.0;
1602: state = txstate;
1603: type = Tx.type;
1604: return;
1605:
1606: /* ---------- Tx == TRANSLATE cases ---------- */
1607: case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1608: case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
1609: case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
1610: case (HI_TRANSLATE | APPLY_SHEAR):
1611: case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
1612: case (HI_TRANSLATE | APPLY_SCALE):
1613: case (HI_TRANSLATE | APPLY_TRANSLATE):
1614: translate(Tx.m02, Tx.m12);
1615: return;
1616:
1617: /* ---------- Tx == SCALE cases ---------- */
1618: case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1619: case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
1620: case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
1621: case (HI_SCALE | APPLY_SHEAR):
1622: case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
1623: case (HI_SCALE | APPLY_SCALE):
1624: case (HI_SCALE | APPLY_TRANSLATE):
1625: scale(Tx.m00, Tx.m11);
1626: return;
1627:
1628: /* ---------- Tx == SHEAR cases ---------- */
1629: case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1630: case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
1631: T01 = Tx.m01;
1632: T10 = Tx.m10;
1633: M0 = m00;
1634: m00 = m01 * T10;
1635: m01 = M0 * T01;
1636: M0 = m10;
1637: m10 = m11 * T10;
1638: m11 = M0 * T01;
1639: type = TYPE_UNKNOWN;
1640: return;
1641: case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
1642: case (HI_SHEAR | APPLY_SHEAR):
1643: m00 = m01 * Tx.m10;
1644: m01 = 0.0;
1645: m11 = m10 * Tx.m01;
1646: m10 = 0.0;
1647: state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
1648: type = TYPE_UNKNOWN;
1649: return;
1650: case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1651: case (HI_SHEAR | APPLY_SCALE):
1652: m01 = m00 * Tx.m01;
1653: m00 = 0.0;
1654: m10 = m11 * Tx.m10;
1655: m11 = 0.0;
1656: state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
1657: type = TYPE_UNKNOWN;
1658: return;
1659: case (HI_SHEAR | APPLY_TRANSLATE):
1660: m00 = 0.0;
1661: m01 = Tx.m01;
1662: m10 = Tx.m10;
1663: m11 = 0.0;
1664: state = APPLY_TRANSLATE | APPLY_SHEAR;
1665: type = TYPE_UNKNOWN;
1666: return;
1667: }
1668: // If Tx has more than one attribute, it is not worth optimizing
1669: // all of those cases...
1670: T00 = Tx.m00;
1671: T01 = Tx.m01;
1672: T02 = Tx.m02;
1673: T10 = Tx.m10;
1674: T11 = Tx.m11;
1675: T12 = Tx.m12;
1676: switch (mystate) {
1677: default:
1678: stateError();
1679: return;
1680: /* NOTREACHED */
1681: case (APPLY_SHEAR | APPLY_SCALE):
1682: state = mystate | txstate;
1683: /* NOBREAK */
1684: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1685: M0 = m00;
1686: M1 = m01;
1687: m00 = T00 * M0 + T10 * M1;
1688: m01 = T01 * M0 + T11 * M1;
1689: m02 += T02 * M0 + T12 * M1;
1690:
1691: M0 = m10;
1692: M1 = m11;
1693: m10 = T00 * M0 + T10 * M1;
1694: m11 = T01 * M0 + T11 * M1;
1695: m12 += T02 * M0 + T12 * M1;
1696: type = TYPE_UNKNOWN;
1697: return;
1698:
1699: case (APPLY_SHEAR | APPLY_TRANSLATE):
1700: case (APPLY_SHEAR):
1701: M0 = m01;
1702: m00 = T10 * M0;
1703: m01 = T11 * M0;
1704: m02 += T12 * M0;
1705:
1706: M0 = m10;
1707: m10 = T00 * M0;
1708: m11 = T01 * M0;
1709: m12 += T02 * M0;
1710: break;
1711:
1712: case (APPLY_SCALE | APPLY_TRANSLATE):
1713: case (APPLY_SCALE):
1714: M0 = m00;
1715: m00 = T00 * M0;
1716: m01 = T01 * M0;
1717: m02 += T02 * M0;
1718:
1719: M0 = m11;
1720: m10 = T10 * M0;
1721: m11 = T11 * M0;
1722: m12 += T12 * M0;
1723: break;
1724:
1725: case (APPLY_TRANSLATE):
1726: m00 = T00;
1727: m01 = T01;
1728: m02 += T02;
1729:
1730: m10 = T10;
1731: m11 = T11;
1732: m12 += T12;
1733: state = txstate | APPLY_TRANSLATE;
1734: type = TYPE_UNKNOWN;
1735: return;
1736: }
1737: updateState();
1738: }
1739:
1740: /**
1741: * Concatenates an <code>AffineTransform</code> <code>Tx</code> to
1742: * this <code>AffineTransform</code> Cx
1743: * in a less commonly used way such that <code>Tx</code> modifies the
1744: * coordinate transformation relative to the absolute pixel
1745: * space rather than relative to the existing user space.
1746: * Cx is updated to perform the combined transformation.
1747: * Transforming a point p by the updated transform Cx' is
1748: * equivalent to first transforming p by the original transform
1749: * Cx and then transforming the result by
1750: * <code>Tx</code> like this:
1751: * Cx'(p) = Tx(Cx(p))
1752: * In matrix notation, if this transform Cx
1753: * is represented by the matrix [this] and <code>Tx</code> is
1754: * represented by the matrix [Tx] then this method does the
1755: * following:
1756: * <pre>
1757: * [this] = [Tx] x [this]
1758: * </pre>
1759: * @param Tx the <code>AffineTransform</code> object to be
1760: * concatenated with this <code>AffineTransform</code> object.
1761: * @see #concatenate
1762: */
1763: public void preConcatenate(AffineTransform Tx) {
1764: double M0, M1;
1765: double T00, T01, T10, T11;
1766: double T02, T12;
1767: int mystate = state;
1768: int txstate = Tx.state;
1769: switch ((txstate << HI_SHIFT) | mystate) {
1770: case (HI_IDENTITY | APPLY_IDENTITY):
1771: case (HI_IDENTITY | APPLY_TRANSLATE):
1772: case (HI_IDENTITY | APPLY_SCALE):
1773: case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
1774: case (HI_IDENTITY | APPLY_SHEAR):
1775: case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
1776: case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
1777: case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1778: // Tx is IDENTITY...
1779: return;
1780:
1781: case (HI_TRANSLATE | APPLY_IDENTITY):
1782: case (HI_TRANSLATE | APPLY_SCALE):
1783: case (HI_TRANSLATE | APPLY_SHEAR):
1784: case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
1785: // Tx is TRANSLATE, this has no TRANSLATE
1786: m02 = Tx.m02;
1787: m12 = Tx.m12;
1788: state = mystate | APPLY_TRANSLATE;
1789: type |= TYPE_TRANSLATION;
1790: return;
1791:
1792: case (HI_TRANSLATE | APPLY_TRANSLATE):
1793: case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
1794: case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
1795: case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1796: // Tx is TRANSLATE, this has one too
1797: m02 = m02 + Tx.m02;
1798: m12 = m12 + Tx.m12;
1799: return;
1800:
1801: case (HI_SCALE | APPLY_TRANSLATE):
1802: case (HI_SCALE | APPLY_IDENTITY):
1803: // Only these two existing states need a new state
1804: state = mystate | APPLY_SCALE;
1805: /* NOBREAK */
1806: case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1807: case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
1808: case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
1809: case (HI_SCALE | APPLY_SHEAR):
1810: case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
1811: case (HI_SCALE | APPLY_SCALE):
1812: // Tx is SCALE, this is anything
1813: T00 = Tx.m00;
1814: T11 = Tx.m11;
1815: if ((mystate & APPLY_SHEAR) != 0) {
1816: m01 = m01 * T00;
1817: m10 = m10 * T11;
1818: if ((mystate & APPLY_SCALE) != 0) {
1819: m00 = m00 * T00;
1820: m11 = m11 * T11;
1821: }
1822: } else {
1823: m00 = m00 * T00;
1824: m11 = m11 * T11;
1825: }
1826: if ((mystate & APPLY_TRANSLATE) != 0) {
1827: m02 = m02 * T00;
1828: m12 = m12 * T11;
1829: }
1830: type = TYPE_UNKNOWN;
1831: return;
1832: case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
1833: case (HI_SHEAR | APPLY_SHEAR):
1834: mystate = mystate | APPLY_SCALE;
1835: /* NOBREAK */
1836: case (HI_SHEAR | APPLY_TRANSLATE):
1837: case (HI_SHEAR | APPLY_IDENTITY):
1838: case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1839: case (HI_SHEAR | APPLY_SCALE):
1840: state = mystate ^ APPLY_SHEAR;
1841: /* NOBREAK */
1842: case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1843: case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
1844: // Tx is SHEAR, this is anything
1845: T01 = Tx.m01;
1846: T10 = Tx.m10;
1847:
1848: M0 = m00;
1849: m00 = m10 * T01;
1850: m10 = M0 * T10;
1851:
1852: M0 = m01;
1853: m01 = m11 * T01;
1854: m11 = M0 * T10;
1855:
1856: M0 = m02;
1857: m02 = m12 * T01;
1858: m12 = M0 * T10;
1859: type = TYPE_UNKNOWN;
1860: return;
1861: }
1862: // If Tx has more than one attribute, it is not worth optimizing
1863: // all of those cases...
1864: T00 = Tx.m00;
1865: T01 = Tx.m01;
1866: T02 = Tx.m02;
1867: T10 = Tx.m10;
1868: T11 = Tx.m11;
1869: T12 = Tx.m12;
1870: switch (mystate) {
1871: default:
1872: stateError();
1873: return;
1874: /* NOTREACHED */
1875: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1876: M0 = m02;
1877: M1 = m12;
1878: T02 += M0 * T00 + M1 * T01;
1879: T12 += M0 * T10 + M1 * T11;
1880:
1881: /* NOBREAK */
1882: case (APPLY_SHEAR | APPLY_SCALE):
1883: m02 = T02;
1884: m12 = T12;
1885:
1886: M0 = m00;
1887: M1 = m10;
1888: m00 = M0 * T00 + M1 * T01;
1889: m10 = M0 * T10 + M1 * T11;
1890:
1891: M0 = m01;
1892: M1 = m11;
1893: m01 = M0 * T00 + M1 * T01;
1894: m11 = M0 * T10 + M1 * T11;
1895: break;
1896:
1897: case (APPLY_SHEAR | APPLY_TRANSLATE):
1898: M0 = m02;
1899: M1 = m12;
1900: T02 += M0 * T00 + M1 * T01;
1901: T12 += M0 * T10 + M1 * T11;
1902:
1903: /* NOBREAK */
1904: case (APPLY_SHEAR):
1905: m02 = T02;
1906: m12 = T12;
1907:
1908: M0 = m10;
1909: m00 = M0 * T01;
1910: m10 = M0 * T11;
1911:
1912: M0 = m01;
1913: m01 = M0 * T00;
1914: m11 = M0 * T10;
1915: break;
1916:
1917: case (APPLY_SCALE | APPLY_TRANSLATE):
1918: M0 = m02;
1919: M1 = m12;
1920: T02 += M0 * T00 + M1 * T01;
1921: T12 += M0 * T10 + M1 * T11;
1922:
1923: /* NOBREAK */
1924: case (APPLY_SCALE):
1925: m02 = T02;
1926: m12 = T12;
1927:
1928: M0 = m00;
1929: m00 = M0 * T00;
1930: m10 = M0 * T10;
1931:
1932: M0 = m11;
1933: m01 = M0 * T01;
1934: m11 = M0 * T11;
1935: break;
1936:
1937: case (APPLY_TRANSLATE):
1938: M0 = m02;
1939: M1 = m12;
1940: T02 += M0 * T00 + M1 * T01;
1941: T12 += M0 * T10 + M1 * T11;
1942:
1943: /* NOBREAK */
1944: case (APPLY_IDENTITY):
1945: m02 = T02;
1946: m12 = T12;
1947:
1948: m00 = T00;
1949: m10 = T10;
1950:
1951: m01 = T01;
1952: m11 = T11;
1953:
1954: state = mystate | txstate;
1955: type = TYPE_UNKNOWN;
1956: return;
1957: }
1958: updateState();
1959: }
1960:
1961: /**
1962: * Returns an <code>AffineTransform</code> object representing the
1963: * inverse transformation.
1964: * The inverse transform Tx' of this transform Tx
1965: * maps coordinates transformed by Tx back
1966: * to their original coordinates.
1967: * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
1968: * <p>
1969: * If this transform maps all coordinates onto a point or a line
1970: * then it will not have an inverse, since coordinates that do
1971: * not lie on the destination point or line will not have an inverse
1972: * mapping.
1973: * The <code>getDeterminant</code> method can be used to determine if this
1974: * transform has no inverse, in which case an exception will be
1975: * thrown if the <code>createInverse</code> method is called.
1976: * @return a new <code>AffineTransform</code> object representing the
1977: * inverse transformation.
1978: * @see #getDeterminant
1979: * @exception NoninvertibleTransformException
1980: * if the matrix cannot be inverted.
1981: */
1982: public AffineTransform createInverse()
1983: throws NoninvertibleTransformException {
1984: double det;
1985: switch (state) {
1986: default:
1987: stateError();
1988: return null;
1989: /* NOTREACHED */
1990: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
1991: det = m00 * m11 - m01 * m10;
1992: if (Math.abs(det) <= Double.MIN_VALUE) {
1993: throw new NoninvertibleTransformException(
1994: "Determinant is " + det);
1995: }
1996: return new AffineTransform(m11 / det, -m10 / det, -m01
1997: / det, m00 / det, (m01 * m12 - m11 * m02) / det,
1998: (m10 * m02 - m00 * m12) / det, (APPLY_SHEAR
1999: | APPLY_SCALE | APPLY_TRANSLATE));
2000: case (APPLY_SHEAR | APPLY_SCALE):
2001: det = m00 * m11 - m01 * m10;
2002: if (Math.abs(det) <= Double.MIN_VALUE) {
2003: throw new NoninvertibleTransformException(
2004: "Determinant is " + det);
2005: }
2006: return new AffineTransform(m11 / det, -m10 / det, -m01
2007: / det, m00 / det, 0.0, 0.0,
2008: (APPLY_SHEAR | APPLY_SCALE));
2009: case (APPLY_SHEAR | APPLY_TRANSLATE):
2010: if (m01 == 0.0 || m10 == 0.0) {
2011: throw new NoninvertibleTransformException(
2012: "Determinant is 0");
2013: }
2014: return new AffineTransform(0.0, 1.0 / m01, 1.0 / m10, 0.0,
2015: -m12 / m10, -m02 / m01,
2016: (APPLY_SHEAR | APPLY_TRANSLATE));
2017: case (APPLY_SHEAR):
2018: if (m01 == 0.0 || m10 == 0.0) {
2019: throw new NoninvertibleTransformException(
2020: "Determinant is 0");
2021: }
2022: return new AffineTransform(0.0, 1.0 / m01, 1.0 / m10, 0.0,
2023: 0.0, 0.0, (APPLY_SHEAR));
2024: case (APPLY_SCALE | APPLY_TRANSLATE):
2025: if (m00 == 0.0 || m11 == 0.0) {
2026: throw new NoninvertibleTransformException(
2027: "Determinant is 0");
2028: }
2029: return new AffineTransform(1.0 / m00, 0.0, 0.0, 1.0 / m11,
2030: -m02 / m00, -m12 / m11,
2031: (APPLY_SCALE | APPLY_TRANSLATE));
2032: case (APPLY_SCALE):
2033: if (m00 == 0.0 || m11 == 0.0) {
2034: throw new NoninvertibleTransformException(
2035: "Determinant is 0");
2036: }
2037: return new AffineTransform(1.0 / m00, 0.0, 0.0, 1.0 / m11,
2038: 0.0, 0.0, (APPLY_SCALE));
2039: case (APPLY_TRANSLATE):
2040: return new AffineTransform(1.0, 0.0, 0.0, 1.0, -m02, -m12,
2041: (APPLY_TRANSLATE));
2042: case (APPLY_IDENTITY):
2043: return new AffineTransform();
2044: }
2045:
2046: /* NOTREACHED */
2047: }
2048:
2049: /**
2050: * Transforms the specified <code>ptSrc</code> and stores the result
2051: * in <code>ptDst</code>.
2052: * If <code>ptDst</code> is <code>null</code>, a new {@link Point2D}
2053: * object is allocated and then the result of the transformation is
2054: * stored in this object.
2055: * In either case, <code>ptDst</code>, which contains the
2056: * transformed point, is returned for convenience.
2057: * If <code>ptSrc</code> and <code>ptDst</code> are the same
2058: * object, the input point is correctly overwritten with
2059: * the transformed point.
2060: * @param ptSrc the specified <code>Point2D</code> to be transformed
2061: * @param ptDst the specified <code>Point2D</code> that stores the
2062: * result of transforming <code>ptSrc</code>
2063: * @return the <code>ptDst</code> after transforming
2064: * <code>ptSrc</code> and stroring the result in <code>ptDst</code>.
2065: */
2066: public Point2D transform(Point2D ptSrc, Point2D ptDst) {
2067: if (ptDst == null) {
2068: if (ptSrc instanceof Point2D.Double) {
2069: ptDst = new Point2D.Double();
2070: } else {
2071: ptDst = new Point2D.Float();
2072: }
2073: }
2074: // Copy source coords into local variables in case src == dst
2075: double x = ptSrc.getX();
2076: double y = ptSrc.getY();
2077: switch (state) {
2078: default:
2079: stateError();
2080: break;
2081: /* NOTREACHED */
2082: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2083: ptDst.setLocation(x * m00 + y * m01 + m02, x * m10 + y
2084: * m11 + m12);
2085: break;
2086: case (APPLY_SHEAR | APPLY_SCALE):
2087: ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
2088: break;
2089: case (APPLY_SHEAR | APPLY_TRANSLATE):
2090: ptDst.setLocation(y * m01 + m02, x * m10 + m12);
2091: break;
2092: case (APPLY_SHEAR):
2093: ptDst.setLocation(y * m01, x * m10);
2094: break;
2095: case (APPLY_SCALE | APPLY_TRANSLATE):
2096: ptDst.setLocation(x * m00 + m02, y * m11 + m12);
2097: break;
2098: case (APPLY_SCALE):
2099: ptDst.setLocation(x * m00, y * m11);
2100: break;
2101: case (APPLY_TRANSLATE):
2102: ptDst.setLocation(x + m02, y + m12);
2103: break;
2104: case (APPLY_IDENTITY):
2105: ptDst.setLocation(x, y);
2106: break;
2107: }
2108: return ptDst;
2109: }
2110:
2111: /**
2112: * Transforms an array of point objects by this transform.
2113: * If any element of the <code>ptDst</code> array is
2114: * <code>null</code>, a new <code>Point2D</code> object is allocated
2115: * and stored into that element before storing the results of the
2116: * transformation.
2117: * <p>
2118: * Note that this method does not take any precautions to
2119: * avoid problems caused by storing results into <code>Point2D</code>
2120: * objects that will be used as the source for calculations
2121: * further down the source array.
2122: * This method does guarantee that if a specified <code>Point2D</code>
2123: * object is both the source and destination for the same single point
2124: * transform operation then the results will not be stored until
2125: * the calculations are complete to avoid storing the results on
2126: * top of the operands.
2127: * If, however, the destination <code>Point2D</code> object for one
2128: * operation is the same object as the source <code>Point2D</code>
2129: * object for another operation further down the source array then
2130: * the original coordinates in that point are overwritten before
2131: * they can be converted.
2132: * @param ptSrc the array containing the source point objects
2133: * @param ptDst the array into which the transform point objects are
2134: * returned
2135: * @param srcOff the offset to the first point object to be
2136: * transformed in the source array
2137: * @param dstOff the offset to the location of the first
2138: * transformed point object that is stored in the destination array
2139: * @param numPts the number of point objects to be transformed
2140: */
2141: public void transform(Point2D[] ptSrc, int srcOff, Point2D[] ptDst,
2142: int dstOff, int numPts) {
2143: int state = this .state;
2144: while (--numPts >= 0) {
2145: // Copy source coords into local variables in case src == dst
2146: Point2D src = ptSrc[srcOff++];
2147: double x = src.getX();
2148: double y = src.getY();
2149: Point2D dst = ptDst[dstOff++];
2150: if (dst == null) {
2151: if (src instanceof Point2D.Double) {
2152: dst = new Point2D.Double();
2153: } else {
2154: dst = new Point2D.Float();
2155: }
2156: ptDst[dstOff - 1] = dst;
2157: }
2158: switch (state) {
2159: default:
2160: stateError();
2161: break;
2162: /* NOTREACHED */
2163: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2164: dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y
2165: * m11 + m12);
2166: break;
2167: case (APPLY_SHEAR | APPLY_SCALE):
2168: dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
2169: break;
2170: case (APPLY_SHEAR | APPLY_TRANSLATE):
2171: dst.setLocation(y * m01 + m02, x * m10 + m12);
2172: break;
2173: case (APPLY_SHEAR):
2174: dst.setLocation(y * m01, x * m10);
2175: break;
2176: case (APPLY_SCALE | APPLY_TRANSLATE):
2177: dst.setLocation(x * m00 + m02, y * m11 + m12);
2178: break;
2179: case (APPLY_SCALE):
2180: dst.setLocation(x * m00, y * m11);
2181: break;
2182: case (APPLY_TRANSLATE):
2183: dst.setLocation(x + m02, y + m12);
2184: break;
2185: case (APPLY_IDENTITY):
2186: dst.setLocation(x, y);
2187: break;
2188: }
2189: }
2190: }
2191:
2192: /**
2193: * Transforms an array of floating point coordinates by this transform.
2194: * The two coordinate array sections can be exactly the same or
2195: * can be overlapping sections of the same array without affecting the
2196: * validity of the results.
2197: * This method ensures that no source coordinates are overwritten by a
2198: * previous operation before they can be transformed.
2199: * The coordinates are stored in the arrays starting at the specified
2200: * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
2201: * @param srcPts the array containing the source point coordinates.
2202: * Each point is stored as a pair of x, y coordinates.
2203: * @param dstPts the array into which the transformed point coordinates
2204: * are returned. Each point is stored as a pair of x, y
2205: * coordinates.
2206: * @param srcOff the offset to the first point to be transformed
2207: * in the source array
2208: * @param dstOff the offset to the location of the first
2209: * transformed point that is stored in the destination array
2210: * @param numPts the number of points to be transformed
2211: */
2212: public void transform(float[] srcPts, int srcOff, float[] dstPts,
2213: int dstOff, int numPts) {
2214: double M00, M01, M02, M10, M11, M12; // For caching
2215: if (dstPts == srcPts && dstOff > srcOff
2216: && dstOff < srcOff + numPts * 2) {
2217: // If the arrays overlap partially with the destination higher
2218: // than the source and we transform the coordinates normally
2219: // we would overwrite some of the later source coordinates
2220: // with results of previous transformations.
2221: // To get around this we use arraycopy to copy the points
2222: // to their final destination with correct overwrite
2223: // handling and then transform them in place in the new
2224: // safer location.
2225: System
2226: .arraycopy(srcPts, srcOff, dstPts, dstOff,
2227: numPts * 2);
2228: // srcPts = dstPts; // They are known to be equal.
2229: srcOff = dstOff;
2230: }
2231: switch (state) {
2232: default:
2233: stateError();
2234: return;
2235: /* NOTREACHED */
2236: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2237: M00 = m00;
2238: M01 = m01;
2239: M02 = m02;
2240: M10 = m10;
2241: M11 = m11;
2242: M12 = m12;
2243: while (--numPts >= 0) {
2244: double x = srcPts[srcOff++];
2245: double y = srcPts[srcOff++];
2246: dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
2247: dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
2248: }
2249: return;
2250: case (APPLY_SHEAR | APPLY_SCALE):
2251: M00 = m00;
2252: M01 = m01;
2253: M10 = m10;
2254: M11 = m11;
2255: while (--numPts >= 0) {
2256: double x = srcPts[srcOff++];
2257: double y = srcPts[srcOff++];
2258: dstPts[dstOff++] = (float) (M00 * x + M01 * y);
2259: dstPts[dstOff++] = (float) (M10 * x + M11 * y);
2260: }
2261: return;
2262: case (APPLY_SHEAR | APPLY_TRANSLATE):
2263: M01 = m01;
2264: M02 = m02;
2265: M10 = m10;
2266: M12 = m12;
2267: while (--numPts >= 0) {
2268: double x = srcPts[srcOff++];
2269: dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
2270: dstPts[dstOff++] = (float) (M10 * x + M12);
2271: }
2272: return;
2273: case (APPLY_SHEAR):
2274: M01 = m01;
2275: M10 = m10;
2276: while (--numPts >= 0) {
2277: double x = srcPts[srcOff++];
2278: dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
2279: dstPts[dstOff++] = (float) (M10 * x);
2280: }
2281: return;
2282: case (APPLY_SCALE | APPLY_TRANSLATE):
2283: M00 = m00;
2284: M02 = m02;
2285: M11 = m11;
2286: M12 = m12;
2287: while (--numPts >= 0) {
2288: dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
2289: dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
2290: }
2291: return;
2292: case (APPLY_SCALE):
2293: M00 = m00;
2294: M11 = m11;
2295: while (--numPts >= 0) {
2296: dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
2297: dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
2298: }
2299: return;
2300: case (APPLY_TRANSLATE):
2301: M02 = m02;
2302: M12 = m12;
2303: while (--numPts >= 0) {
2304: dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
2305: dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
2306: }
2307: return;
2308: case (APPLY_IDENTITY):
2309: if (srcPts != dstPts || srcOff != dstOff) {
2310: System.arraycopy(srcPts, srcOff, dstPts, dstOff,
2311: numPts * 2);
2312: }
2313: return;
2314: }
2315: }
2316:
2317: /**
2318: * Transforms an array of double precision coordinates by this transform.
2319: * The two coordinate array sections can be exactly the same or
2320: * can be overlapping sections of the same array without affecting the
2321: * validity of the results.
2322: * This method ensures that no source coordinates are
2323: * overwritten by a previous operation before they can be transformed.
2324: * The coordinates are stored in the arrays starting at the indicated
2325: * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
2326: * @param srcPts the array containing the source point coordinates.
2327: * Each point is stored as a pair of x, y coordinates.
2328: * @param dstPts the array into which the transformed point
2329: * coordinates are returned. Each point is stored as a pair of
2330: * x, y coordinates.
2331: * @param srcOff the offset to the first point to be transformed
2332: * in the source array
2333: * @param dstOff the offset to the location of the first
2334: * transformed point that is stored in the destination array
2335: * @param numPts the number of point objects to be transformed
2336: */
2337: public void transform(double[] srcPts, int srcOff, double[] dstPts,
2338: int dstOff, int numPts) {
2339: double M00, M01, M02, M10, M11, M12; // For caching
2340: if (dstPts == srcPts && dstOff > srcOff
2341: && dstOff < srcOff + numPts * 2) {
2342: // If the arrays overlap partially with the destination higher
2343: // than the source and we transform the coordinates normally
2344: // we would overwrite some of the later source coordinates
2345: // with results of previous transformations.
2346: // To get around this we use arraycopy to copy the points
2347: // to their final destination with correct overwrite
2348: // handling and then transform them in place in the new
2349: // safer location.
2350: System
2351: .arraycopy(srcPts, srcOff, dstPts, dstOff,
2352: numPts * 2);
2353: // srcPts = dstPts; // They are known to be equal.
2354: srcOff = dstOff;
2355: }
2356: switch (state) {
2357: default:
2358: stateError();
2359: return;
2360: /* NOTREACHED */
2361: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2362: M00 = m00;
2363: M01 = m01;
2364: M02 = m02;
2365: M10 = m10;
2366: M11 = m11;
2367: M12 = m12;
2368: while (--numPts >= 0) {
2369: double x = srcPts[srcOff++];
2370: double y = srcPts[srcOff++];
2371: dstPts[dstOff++] = M00 * x + M01 * y + M02;
2372: dstPts[dstOff++] = M10 * x + M11 * y + M12;
2373: }
2374: return;
2375: case (APPLY_SHEAR | APPLY_SCALE):
2376: M00 = m00;
2377: M01 = m01;
2378: M10 = m10;
2379: M11 = m11;
2380: while (--numPts >= 0) {
2381: double x = srcPts[srcOff++];
2382: double y = srcPts[srcOff++];
2383: dstPts[dstOff++] = M00 * x + M01 * y;
2384: dstPts[dstOff++] = M10 * x + M11 * y;
2385: }
2386: return;
2387: case (APPLY_SHEAR | APPLY_TRANSLATE):
2388: M01 = m01;
2389: M02 = m02;
2390: M10 = m10;
2391: M12 = m12;
2392: while (--numPts >= 0) {
2393: double x = srcPts[srcOff++];
2394: dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
2395: dstPts[dstOff++] = M10 * x + M12;
2396: }
2397: return;
2398: case (APPLY_SHEAR):
2399: M01 = m01;
2400: M10 = m10;
2401: while (--numPts >= 0) {
2402: double x = srcPts[srcOff++];
2403: dstPts[dstOff++] = M01 * srcPts[srcOff++];
2404: dstPts[dstOff++] = M10 * x;
2405: }
2406: return;
2407: case (APPLY_SCALE | APPLY_TRANSLATE):
2408: M00 = m00;
2409: M02 = m02;
2410: M11 = m11;
2411: M12 = m12;
2412: while (--numPts >= 0) {
2413: dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
2414: dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
2415: }
2416: return;
2417: case (APPLY_SCALE):
2418: M00 = m00;
2419: M11 = m11;
2420: while (--numPts >= 0) {
2421: dstPts[dstOff++] = M00 * srcPts[srcOff++];
2422: dstPts[dstOff++] = M11 * srcPts[srcOff++];
2423: }
2424: return;
2425: case (APPLY_TRANSLATE):
2426: M02 = m02;
2427: M12 = m12;
2428: while (--numPts >= 0) {
2429: dstPts[dstOff++] = srcPts[srcOff++] + M02;
2430: dstPts[dstOff++] = srcPts[srcOff++] + M12;
2431: }
2432: return;
2433: case (APPLY_IDENTITY):
2434: if (srcPts != dstPts || srcOff != dstOff) {
2435: System.arraycopy(srcPts, srcOff, dstPts, dstOff,
2436: numPts * 2);
2437: }
2438: return;
2439: }
2440:
2441: /* NOTREACHED */
2442: }
2443:
2444: /**
2445: * Transforms an array of floating point coordinates by this transform
2446: * and stores the results into an array of doubles.
2447: * The coordinates are stored in the arrays starting at the specified
2448: * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
2449: * @param srcPts the array containing the source point coordinates.
2450: * Each point is stored as a pair of x, y coordinates.
2451: * @param dstPts the array into which the transformed point coordinates
2452: * are returned. Each point is stored as a pair of x, y
2453: * coordinates.
2454: * @param srcOff the offset to the first point to be transformed
2455: * in the source array
2456: * @param dstOff the offset to the location of the first
2457: * transformed point that is stored in the destination array
2458: * @param numPts the number of points to be transformed
2459: */
2460: public void transform(float[] srcPts, int srcOff, double[] dstPts,
2461: int dstOff, int numPts) {
2462: double M00, M01, M02, M10, M11, M12; // For caching
2463: switch (state) {
2464: default:
2465: stateError();
2466: return;
2467: /* NOTREACHED */
2468: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2469: M00 = m00;
2470: M01 = m01;
2471: M02 = m02;
2472: M10 = m10;
2473: M11 = m11;
2474: M12 = m12;
2475: while (--numPts >= 0) {
2476: double x = srcPts[srcOff++];
2477: double y = srcPts[srcOff++];
2478: dstPts[dstOff++] = M00 * x + M01 * y + M02;
2479: dstPts[dstOff++] = M10 * x + M11 * y + M12;
2480: }
2481: return;
2482: case (APPLY_SHEAR | APPLY_SCALE):
2483: M00 = m00;
2484: M01 = m01;
2485: M10 = m10;
2486: M11 = m11;
2487: while (--numPts >= 0) {
2488: double x = srcPts[srcOff++];
2489: double y = srcPts[srcOff++];
2490: dstPts[dstOff++] = M00 * x + M01 * y;
2491: dstPts[dstOff++] = M10 * x + M11 * y;
2492: }
2493: return;
2494: case (APPLY_SHEAR | APPLY_TRANSLATE):
2495: M01 = m01;
2496: M02 = m02;
2497: M10 = m10;
2498: M12 = m12;
2499: while (--numPts >= 0) {
2500: double x = srcPts[srcOff++];
2501: dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
2502: dstPts[dstOff++] = M10 * x + M12;
2503: }
2504: return;
2505: case (APPLY_SHEAR):
2506: M01 = m01;
2507: M10 = m10;
2508: while (--numPts >= 0) {
2509: double x = srcPts[srcOff++];
2510: dstPts[dstOff++] = M01 * srcPts[srcOff++];
2511: dstPts[dstOff++] = M10 * x;
2512: }
2513: return;
2514: case (APPLY_SCALE | APPLY_TRANSLATE):
2515: M00 = m00;
2516: M02 = m02;
2517: M11 = m11;
2518: M12 = m12;
2519: while (--numPts >= 0) {
2520: dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
2521: dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
2522: }
2523: return;
2524: case (APPLY_SCALE):
2525: M00 = m00;
2526: M11 = m11;
2527: while (--numPts >= 0) {
2528: dstPts[dstOff++] = M00 * srcPts[srcOff++];
2529: dstPts[dstOff++] = M11 * srcPts[srcOff++];
2530: }
2531: return;
2532: case (APPLY_TRANSLATE):
2533: M02 = m02;
2534: M12 = m12;
2535: while (--numPts >= 0) {
2536: dstPts[dstOff++] = srcPts[srcOff++] + M02;
2537: dstPts[dstOff++] = srcPts[srcOff++] + M12;
2538: }
2539: return;
2540: case (APPLY_IDENTITY):
2541: while (--numPts >= 0) {
2542: dstPts[dstOff++] = srcPts[srcOff++];
2543: dstPts[dstOff++] = srcPts[srcOff++];
2544: }
2545: return;
2546: }
2547:
2548: /* NOTREACHED */
2549: }
2550:
2551: /**
2552: * Transforms an array of double precision coordinates by this transform
2553: * and stores the results into an array of floats.
2554: * The coordinates are stored in the arrays starting at the specified
2555: * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
2556: * @param srcPts the array containing the source point coordinates.
2557: * Each point is stored as a pair of x, y coordinates.
2558: * @param dstPts the array into which the transformed point
2559: * coordinates are returned. Each point is stored as a pair of
2560: * x, y coordinates.
2561: * @param srcOff the offset to the first point to be transformed
2562: * in the source array
2563: * @param dstOff the offset to the location of the first
2564: * transformed point that is stored in the destination array
2565: * @param numPts the number of point objects to be transformed
2566: */
2567: public void transform(double[] srcPts, int srcOff, float[] dstPts,
2568: int dstOff, int numPts) {
2569: double M00, M01, M02, M10, M11, M12; // For caching
2570: switch (state) {
2571: default:
2572: stateError();
2573: return;
2574: /* NOTREACHED */
2575: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2576: M00 = m00;
2577: M01 = m01;
2578: M02 = m02;
2579: M10 = m10;
2580: M11 = m11;
2581: M12 = m12;
2582: while (--numPts >= 0) {
2583: double x = srcPts[srcOff++];
2584: double y = srcPts[srcOff++];
2585: dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
2586: dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
2587: }
2588: return;
2589: case (APPLY_SHEAR | APPLY_SCALE):
2590: M00 = m00;
2591: M01 = m01;
2592: M10 = m10;
2593: M11 = m11;
2594: while (--numPts >= 0) {
2595: double x = srcPts[srcOff++];
2596: double y = srcPts[srcOff++];
2597: dstPts[dstOff++] = (float) (M00 * x + M01 * y);
2598: dstPts[dstOff++] = (float) (M10 * x + M11 * y);
2599: }
2600: return;
2601: case (APPLY_SHEAR | APPLY_TRANSLATE):
2602: M01 = m01;
2603: M02 = m02;
2604: M10 = m10;
2605: M12 = m12;
2606: while (--numPts >= 0) {
2607: double x = srcPts[srcOff++];
2608: dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
2609: dstPts[dstOff++] = (float) (M10 * x + M12);
2610: }
2611: return;
2612: case (APPLY_SHEAR):
2613: M01 = m01;
2614: M10 = m10;
2615: while (--numPts >= 0) {
2616: double x = srcPts[srcOff++];
2617: dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
2618: dstPts[dstOff++] = (float) (M10 * x);
2619: }
2620: return;
2621: case (APPLY_SCALE | APPLY_TRANSLATE):
2622: M00 = m00;
2623: M02 = m02;
2624: M11 = m11;
2625: M12 = m12;
2626: while (--numPts >= 0) {
2627: dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
2628: dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
2629: }
2630: return;
2631: case (APPLY_SCALE):
2632: M00 = m00;
2633: M11 = m11;
2634: while (--numPts >= 0) {
2635: dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
2636: dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
2637: }
2638: return;
2639: case (APPLY_TRANSLATE):
2640: M02 = m02;
2641: M12 = m12;
2642: while (--numPts >= 0) {
2643: dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
2644: dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
2645: }
2646: return;
2647: case (APPLY_IDENTITY):
2648: while (--numPts >= 0) {
2649: dstPts[dstOff++] = (float) (srcPts[srcOff++]);
2650: dstPts[dstOff++] = (float) (srcPts[srcOff++]);
2651: }
2652: return;
2653: }
2654:
2655: /* NOTREACHED */
2656: }
2657:
2658: /**
2659: * Inverse transforms the specified <code>ptSrc</code> and stores the
2660: * result in <code>ptDst</code>.
2661: * If <code>ptDst</code> is <code>null</code>, a new
2662: * <code>Point2D</code> object is allocated and then the result of the
2663: * transform is stored in this object.
2664: * In either case, <code>ptDst</code>, which contains the transformed
2665: * point, is returned for convenience.
2666: * If <code>ptSrc</code> and <code>ptDst</code> are the same
2667: * object, the input point is correctly overwritten with the
2668: * transformed point.
2669: * @param ptSrc the point to be inverse transformed
2670: * @param ptDst the resulting transformed point
2671: * @return <code>ptDst</code>, which contains the result of the
2672: * inverse transform.
2673: * @exception NoninvertibleTransformException if the matrix cannot be
2674: * inverted.
2675: */
2676: public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst)
2677: throws NoninvertibleTransformException {
2678: if (ptDst == null) {
2679: if (ptSrc instanceof Point2D.Double) {
2680: ptDst = new Point2D.Double();
2681: } else {
2682: ptDst = new Point2D.Float();
2683: }
2684: }
2685: // Copy source coords into local variables in case src == dst
2686: double x = ptSrc.getX();
2687: double y = ptSrc.getY();
2688: switch (state) {
2689: default:
2690: stateError();
2691: return null;
2692: /* NOTREACHED */
2693: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2694: x -= m02;
2695: y -= m12;
2696: /* NOBREAK */
2697: case (APPLY_SHEAR | APPLY_SCALE):
2698: double det = m00 * m11 - m01 * m10;
2699: if (Math.abs(det) <= Double.MIN_VALUE) {
2700: throw new NoninvertibleTransformException(
2701: "Determinant is " + det);
2702: }
2703: ptDst.setLocation((x * m11 - y * m01) / det, (y * m00 - x
2704: * m10)
2705: / det);
2706: return ptDst;
2707: case (APPLY_SHEAR | APPLY_TRANSLATE):
2708: x -= m02;
2709: y -= m12;
2710: /* NOBREAK */
2711: case (APPLY_SHEAR):
2712: if (m01 == 0.0 || m10 == 0.0) {
2713: throw new NoninvertibleTransformException(
2714: "Determinant is 0");
2715: }
2716: ptDst.setLocation(y / m10, x / m01);
2717: return ptDst;
2718: case (APPLY_SCALE | APPLY_TRANSLATE):
2719: x -= m02;
2720: y -= m12;
2721: /* NOBREAK */
2722: case (APPLY_SCALE):
2723: if (m00 == 0.0 || m11 == 0.0) {
2724: throw new NoninvertibleTransformException(
2725: "Determinant is 0");
2726: }
2727: ptDst.setLocation(x / m00, y / m11);
2728: return ptDst;
2729: case (APPLY_TRANSLATE):
2730: ptDst.setLocation(x - m02, y - m12);
2731: return ptDst;
2732: case (APPLY_IDENTITY):
2733: ptDst.setLocation(x, y);
2734: return ptDst;
2735: }
2736:
2737: /* NOTREACHED */
2738: }
2739:
2740: /**
2741: * Inverse transforms an array of double precision coordinates by
2742: * this transform.
2743: * The two coordinate array sections can be exactly the same or
2744: * can be overlapping sections of the same array without affecting the
2745: * validity of the results.
2746: * This method ensures that no source coordinates are
2747: * overwritten by a previous operation before they can be transformed.
2748: * The coordinates are stored in the arrays starting at the specified
2749: * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
2750: * @param srcPts the array containing the source point coordinates.
2751: * Each point is stored as a pair of x, y coordinates.
2752: * @param dstPts the array into which the transformed point
2753: * coordinates are returned. Each point is stored as a pair of
2754: * x, y coordinates.
2755: * @param srcOff the offset to the first point to be transformed
2756: * in the source array
2757: * @param dstOff the offset to the location of the first
2758: * transformed point that is stored in the destination array
2759: * @param numPts the number of point objects to be transformed
2760: * @exception NoninvertibleTransformException if the matrix cannot be
2761: * inverted.
2762: */
2763: public void inverseTransform(double[] srcPts, int srcOff,
2764: double[] dstPts, int dstOff, int numPts)
2765: throws NoninvertibleTransformException {
2766: double M00, M01, M02, M10, M11, M12; // For caching
2767: double det;
2768: if (dstPts == srcPts && dstOff > srcOff
2769: && dstOff < srcOff + numPts * 2) {
2770: // If the arrays overlap partially with the destination higher
2771: // than the source and we transform the coordinates normally
2772: // we would overwrite some of the later source coordinates
2773: // with results of previous transformations.
2774: // To get around this we use arraycopy to copy the points
2775: // to their final destination with correct overwrite
2776: // handling and then transform them in place in the new
2777: // safer location.
2778: System
2779: .arraycopy(srcPts, srcOff, dstPts, dstOff,
2780: numPts * 2);
2781: // srcPts = dstPts; // They are known to be equal.
2782: srcOff = dstOff;
2783: }
2784: switch (state) {
2785: default:
2786: stateError();
2787: return;
2788: /* NOTREACHED */
2789: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2790: M00 = m00;
2791: M01 = m01;
2792: M02 = m02;
2793: M10 = m10;
2794: M11 = m11;
2795: M12 = m12;
2796: det = M00 * M11 - M01 * M10;
2797: if (Math.abs(det) <= Double.MIN_VALUE) {
2798: throw new NoninvertibleTransformException(
2799: "Determinant is " + det);
2800: }
2801: while (--numPts >= 0) {
2802: double x = srcPts[srcOff++] - M02;
2803: double y = srcPts[srcOff++] - M12;
2804: dstPts[dstOff++] = (x * M11 - y * M01) / det;
2805: dstPts[dstOff++] = (y * M00 - x * M10) / det;
2806: }
2807: return;
2808: case (APPLY_SHEAR | APPLY_SCALE):
2809: M00 = m00;
2810: M01 = m01;
2811: M10 = m10;
2812: M11 = m11;
2813: det = M00 * M11 - M01 * M10;
2814: if (Math.abs(det) <= Double.MIN_VALUE) {
2815: throw new NoninvertibleTransformException(
2816: "Determinant is " + det);
2817: }
2818: while (--numPts >= 0) {
2819: double x = srcPts[srcOff++];
2820: double y = srcPts[srcOff++];
2821: dstPts[dstOff++] = (x * M11 - y * M01) / det;
2822: dstPts[dstOff++] = (y * M00 - x * M10) / det;
2823: }
2824: return;
2825: case (APPLY_SHEAR | APPLY_TRANSLATE):
2826: M01 = m01;
2827: M02 = m02;
2828: M10 = m10;
2829: M12 = m12;
2830: if (M01 == 0.0 || M10 == 0.0) {
2831: throw new NoninvertibleTransformException(
2832: "Determinant is 0");
2833: }
2834: while (--numPts >= 0) {
2835: double x = srcPts[srcOff++] - M02;
2836: dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10;
2837: dstPts[dstOff++] = x / M01;
2838: }
2839: return;
2840: case (APPLY_SHEAR):
2841: M01 = m01;
2842: M10 = m10;
2843: if (M01 == 0.0 || M10 == 0.0) {
2844: throw new NoninvertibleTransformException(
2845: "Determinant is 0");
2846: }
2847: while (--numPts >= 0) {
2848: double x = srcPts[srcOff++];
2849: dstPts[dstOff++] = srcPts[srcOff++] / M10;
2850: dstPts[dstOff++] = x / M01;
2851: }
2852: return;
2853: case (APPLY_SCALE | APPLY_TRANSLATE):
2854: M00 = m00;
2855: M02 = m02;
2856: M11 = m11;
2857: M12 = m12;
2858: if (M00 == 0.0 || M11 == 0.0) {
2859: throw new NoninvertibleTransformException(
2860: "Determinant is 0");
2861: }
2862: while (--numPts >= 0) {
2863: dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00;
2864: dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11;
2865: }
2866: return;
2867: case (APPLY_SCALE):
2868: M00 = m00;
2869: M11 = m11;
2870: if (M00 == 0.0 || M11 == 0.0) {
2871: throw new NoninvertibleTransformException(
2872: "Determinant is 0");
2873: }
2874: while (--numPts >= 0) {
2875: dstPts[dstOff++] = srcPts[srcOff++] / M00;
2876: dstPts[dstOff++] = srcPts[srcOff++] / M11;
2877: }
2878: return;
2879: case (APPLY_TRANSLATE):
2880: M02 = m02;
2881: M12 = m12;
2882: while (--numPts >= 0) {
2883: dstPts[dstOff++] = srcPts[srcOff++] - M02;
2884: dstPts[dstOff++] = srcPts[srcOff++] - M12;
2885: }
2886: return;
2887: case (APPLY_IDENTITY):
2888: if (srcPts != dstPts || srcOff != dstOff) {
2889: System.arraycopy(srcPts, srcOff, dstPts, dstOff,
2890: numPts * 2);
2891: }
2892: return;
2893: }
2894:
2895: /* NOTREACHED */
2896: }
2897:
2898: /**
2899: * Transforms the relative distance vector specified by
2900: * <code>ptSrc</code> and stores the result in <code>ptDst</code>.
2901: * A relative distance vector is transformed without applying the
2902: * translation components of the affine transformation matrix
2903: * using the following equations:
2904: * <pre>
2905: * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ]
2906: * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ]
2907: * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ]
2908: * </pre>
2909: * If <code>ptDst</code> is <code>null</code>, a new
2910: * <code>Point2D</code> object is allocated and then the result of the
2911: * transform is stored in this object.
2912: * In either case, <code>ptDst</code>, which contains the
2913: * transformed point, is returned for convenience.
2914: * If <code>ptSrc</code> and <code>ptDst</code> are the same object,
2915: * the input point is correctly overwritten with the transformed
2916: * point.
2917: * @param ptSrc the distance vector to be delta transformed
2918: * @param ptDst the resulting transformed distance vector
2919: * @return <code>ptDst</code>, which contains the result of the
2920: * transformation.
2921: */
2922: public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) {
2923: if (ptDst == null) {
2924: if (ptSrc instanceof Point2D.Double) {
2925: ptDst = new Point2D.Double();
2926: } else {
2927: ptDst = new Point2D.Float();
2928: }
2929: }
2930: // Copy source coords into local variables in case src == dst
2931: double x = ptSrc.getX();
2932: double y = ptSrc.getY();
2933: switch (state) {
2934: default:
2935: stateError();
2936: return null;
2937: /* NOTREACHED */
2938: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
2939: case (APPLY_SHEAR | APPLY_SCALE):
2940: ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
2941: return ptDst;
2942: case (APPLY_SHEAR | APPLY_TRANSLATE):
2943: case (APPLY_SHEAR):
2944: ptDst.setLocation(y * m01, x * m10);
2945: return ptDst;
2946: case (APPLY_SCALE | APPLY_TRANSLATE):
2947: case (APPLY_SCALE):
2948: ptDst.setLocation(x * m00, y * m11);
2949: return ptDst;
2950: case (APPLY_TRANSLATE):
2951: case (APPLY_IDENTITY):
2952: ptDst.setLocation(x, y);
2953: return ptDst;
2954: }
2955:
2956: /* NOTREACHED */
2957: }
2958:
2959: /**
2960: * Transforms an array of relative distance vectors by this
2961: * transform.
2962: * A relative distance vector is transformed without applying the
2963: * translation components of the affine transformation matrix
2964: * using the following equations:
2965: * <pre>
2966: * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ]
2967: * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ]
2968: * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ]
2969: * </pre>
2970: * The two coordinate array sections can be exactly the same or
2971: * can be overlapping sections of the same array without affecting the
2972: * validity of the results.
2973: * This method ensures that no source coordinates are
2974: * overwritten by a previous operation before they can be transformed.
2975: * The coordinates are stored in the arrays starting at the indicated
2976: * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
2977: * @param srcPts the array containing the source distance vectors.
2978: * Each vector is stored as a pair of relative x, y coordinates.
2979: * @param dstPts the array into which the transformed distance vectors
2980: * are returned. Each vector is stored as a pair of relative
2981: * x, y coordinates.
2982: * @param srcOff the offset to the first vector to be transformed
2983: * in the source array
2984: * @param dstOff the offset to the location of the first
2985: * transformed vector that is stored in the destination array
2986: * @param numPts the number of vector coordinate pairs to be
2987: * transformed
2988: */
2989: public void deltaTransform(double[] srcPts, int srcOff,
2990: double[] dstPts, int dstOff, int numPts) {
2991: double M00, M01, M10, M11; // For caching
2992: if (dstPts == srcPts && dstOff > srcOff
2993: && dstOff < srcOff + numPts * 2) {
2994: // If the arrays overlap partially with the destination higher
2995: // than the source and we transform the coordinates normally
2996: // we would overwrite some of the later source coordinates
2997: // with results of previous transformations.
2998: // To get around this we use arraycopy to copy the points
2999: // to their final destination with correct overwrite
3000: // handling and then transform them in place in the new
3001: // safer location.
3002: System
3003: .arraycopy(srcPts, srcOff, dstPts, dstOff,
3004: numPts * 2);
3005: // srcPts = dstPts; // They are known to be equal.
3006: srcOff = dstOff;
3007: }
3008: switch (state) {
3009: default:
3010: stateError();
3011: return;
3012: /* NOTREACHED */
3013: case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
3014: case (APPLY_SHEAR | APPLY_SCALE):
3015: M00 = m00;
3016: M01 = m01;
3017: M10 = m10;
3018: M11 = m11;
3019: while (--numPts >= 0) {
3020: double x = srcPts[srcOff++];
3021: double y = srcPts[srcOff++];
3022: dstPts[dstOff++] = x * M00 + y * M01;
3023: dstPts[dstOff++] = x * M10 + y * M11;
3024: }
3025: return;
3026: case (APPLY_SHEAR | APPLY_TRANSLATE):
3027: case (APPLY_SHEAR):
3028: M01 = m01;
3029: M10 = m10;
3030: while (--numPts >= 0) {
3031: double x = srcPts[srcOff++];
3032: dstPts[dstOff++] = srcPts[srcOff++] * M01;
3033: dstPts[dstOff++] = x * M10;
3034: }
3035: return;
3036: case (APPLY_SCALE | APPLY_TRANSLATE):
3037: case (APPLY_SCALE):
3038: M00 = m00;
3039: M11 = m11;
3040: while (--numPts >= 0) {
3041: dstPts[dstOff++] = srcPts[srcOff++] * M00;
3042: dstPts[dstOff++] = srcPts[srcOff++] * M11;
3043: }
3044: return;
3045: case (APPLY_TRANSLATE):
3046: case (APPLY_IDENTITY):
3047: if (srcPts != dstPts || srcOff != dstOff) {
3048: System.arraycopy(srcPts, srcOff, dstPts, dstOff,
3049: numPts * 2);
3050: }
3051: return;
3052: }
3053:
3054: /* NOTREACHED */
3055: }
3056:
3057: /* *
3058: * Returns a new {@link Shape} object defined by the geometry of the
3059: * specified <code>Shape</code> after it has been transformed by
3060: * this transform.
3061: * @param pSrc the specified <code>Shape</code> object to be
3062: * transformed by this transform.
3063: * @return a new <code>Shape</code> object that defines the geometry
3064: * of the transformed <code>Shape</code>.
3065: */
3066: //#ifdef notdef
3067: public Shape createTransformedShape(Shape pSrc) {
3068: if (pSrc == null) {
3069: return null;
3070: }
3071:
3072: if (pSrc instanceof GeneralPath) {
3073: return ((GeneralPath) pSrc).createTransformedShape(this );
3074: } else {
3075: PathIterator pi = pSrc.getPathIterator(this );
3076: GeneralPath gp = new GeneralPath(pi.getWindingRule());
3077: gp.append(pi, false);
3078: return gp;
3079: }
3080:
3081: /* NOTREACHED */
3082: }
3083:
3084: //#endif
3085:
3086: private static double _matround(double matval) {
3087: // Round values to sane precision for printing
3088: // Note that Math.sin(Math.PI) has an error of about 10^-16
3089: //return Math.rint(matval * 1E15) / 1E15;
3090: return matval;
3091: }
3092:
3093: /**
3094: * Returns a <code>String</code> that represents the value of this
3095: * {@link Object}.
3096: * @return a <code>String</code> representing the value of this
3097: * <code>Object</code>.
3098: */
3099: public String toString() {
3100: return ("AffineTransform[[" + _matround(m00) + ", "
3101: + _matround(m01) + ", " + _matround(m02) + "], ["
3102: + _matround(m10) + ", " + _matround(m11) + ", "
3103: + _matround(m12) + "]]");
3104: }
3105:
3106: /**
3107: * Returns <code>true</code> if this <code>AffineTransform</code> is
3108: * an identity transform.
3109: * @return <code>true</code> if this <code>AffineTransform</code> is
3110: * an identity transform; <code>false</code> otherwise.
3111: */
3112: public boolean isIdentity() {
3113: return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY));
3114: }
3115:
3116: /* *
3117: * Returns a copy of this <code>AffineTransform</code> object.
3118: * @return an <code>Object</code> that is a copy of this
3119: * <code>AffineTransform</code> object.
3120: */
3121: //#ifdef notdef
3122: public Object clone() {
3123: try {
3124: return super .clone();
3125: } catch (CloneNotSupportedException e) {
3126: // this shouldn't happen, since we are Cloneable
3127: throw new InternalError();
3128: }
3129: }
3130:
3131: //#endif
3132:
3133: /**
3134: * Returns the hashcode for this transform.
3135: * @return a hash code for this transform.
3136: */
3137: public int hashCode() {
3138: long bits = Double.doubleToLongBits(m00);
3139: bits = bits * 31 + Double.doubleToLongBits(m01);
3140: bits = bits * 31 + Double.doubleToLongBits(m02);
3141: bits = bits * 31 + Double.doubleToLongBits(m10);
3142: bits = bits * 31 + Double.doubleToLongBits(m11);
3143: bits = bits * 31 + Double.doubleToLongBits(m12);
3144: return (((int) bits) ^ ((int) (bits >> 32)));
3145: }
3146:
3147: /**
3148: * Returns <code>true</code> if this <code>AffineTransform</code>
3149: * represents the same affine coordinate transform as the specified
3150: * argument.
3151: * @param obj the <code>Object</code> to test for equality with this
3152: * <code>AffineTransform</code>
3153: * @return <code>true</code> if <code>obj</code> equals this
3154: * <code>AffineTransform</code> object; <code>false</code> otherwise.
3155: */
3156: public boolean equals(Object obj) {
3157: if (!(obj instanceof AffineTransform)) {
3158: return false;
3159: }
3160:
3161: AffineTransform a = (AffineTransform) obj;
3162:
3163: return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02)
3164: && (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12));
3165: }
3166:
3167: /* Serialization support. A readObject method is neccessary because
3168: * the state field is part of the implementation of this particular
3169: * AffineTransform and not part of the public specification. The
3170: * state variable's value needs to be recalculated on the fly by the
3171: * readObject method as it is in the 6-argument matrix constructor.
3172: */
3173: //#ifdef notdef
3174: private void writeObject(java.io.ObjectOutputStream s)
3175: throws java.lang.ClassNotFoundException,
3176: java.io.IOException {
3177: s.defaultWriteObject();
3178: }
3179:
3180: private void readObject(java.io.ObjectInputStream s)
3181: throws java.lang.ClassNotFoundException,
3182: java.io.IOException {
3183: s.defaultReadObject();
3184: updateState();
3185: }
3186: //#endif
3187: }
3188: //#endif
|