/* Copyright (C) 2000 Free Software Foundation This file is part of libjava. This software is copyrighted work licensed under the terms of the Libjava License. Please consult the file "LIBJAVA_LICENSE" for details. */ package java.awt.geom; import java.awt.*; import java.io.Serializable; /** * @author Tom Tromey * @date April 16, 2000 */ /* Status: mostly complete. Search for fixme to see problems. Also, TYPE_ returns are not handled correctly. */ public class AffineTransform implements Cloneable, Serializable { static final int TYPE_IDENTITY = 0; static final int TYPE_FLIP = 64; static final int TYPE_GENERAL_ROTATION = 16; static final int TYPE_GENERAL_SCALE = 4; static final int TYPE_GENERAL_TRANSFORM = 32; static final int TYPE_MASK_ROTATION = 24; static final int TYPE_MASK_SCALE = 6; static final int TYPE_QUADRANT_ROTATION = 8; static final int TYPE_TRANSLATION = 1; static final int TYPE_UNIFORM_SCALE = 2; public AffineTransform () { setToIdentity (); } public AffineTransform (AffineTransform tx) { setTransform (tx); } public AffineTransform (float m00, float m10, float m01, float m11, float m02, float m12) { this.m00 = m00; this.m10 = m10; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; this.type = 0; // fixme; } public AffineTransform (float[] flatmatrix) { m00 = flatmatrix[0]; m10 = flatmatrix[1]; m01 = flatmatrix[2]; m11 = flatmatrix[3]; if (flatmatrix.length >= 6) { m02 = flatmatrix[4]; m12 = flatmatrix[5]; } } public AffineTransform (double m00, double m10, double m01, double m11, double m02, double m12) { this.m00 = m00; this.m10 = m10; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; this.type = TYPE_GENERAL_TRANSFORM; } public AffineTransform (double[] flatmatrix) { m00 = flatmatrix[0]; m10 = flatmatrix[1]; m01 = flatmatrix[2]; m11 = flatmatrix[3]; if (flatmatrix.length >= 6) { m02 = flatmatrix[4]; m12 = flatmatrix[5]; } } public static AffineTransform getTranslateInstance (double tx, double ty) { AffineTransform t = new AffineTransform (); t.setToTranslation (tx, ty); return t; } public static AffineTransform getRotateInstance (double theta) { AffineTransform t = new AffineTransform (); t.setToRotation (theta); return t; } public static AffineTransform getRotateInstance (double theta, double x, double y) { AffineTransform t = new AffineTransform (); t.rotate (theta, x, y); return t; } public static AffineTransform getScaleInstance (double sx, double sy) { AffineTransform t = new AffineTransform (); t.setToScale (sx, sy); return t; } public static AffineTransform getShearInstance (double shx, double shy) { AffineTransform t = new AffineTransform (); t.setToShear (shx, shy); return t; } public int getType () { return type; } public double getDeterminant () { return m00 * m11 - m01 * m10; } public void getMatrix (double[] flatmatrix) { flatmatrix[0] = m00; flatmatrix[1] = m10; flatmatrix[2] = m01; flatmatrix[3] = m11; if (flatmatrix.length >= 6) { flatmatrix[4] = m02; flatmatrix[5] = m12; } } public double getScaleX () { return m00; } public double getScaleY () { return m11; } public double getShearX () { return m01; } public double getShearY () { return m10; } public double getTranslateX () { return m02; } public double getTranslateY () { return m12; } public void translate (double tx, double ty) { m02 += tx * m00 + ty * m01; m12 += tx * m10 + ty * m11; } public void rotate (double theta) { double c = Math.cos (theta); double s = Math.sin (theta); double n00 = m00 * c + m01 * s; double n01 = m00 * -s + m01 * c; double n10 = m10 * c + m11 * s; double n11 = m10 * -s + m11 * c; m00 = n00; m01 = n01; m10 = n10; m11 = n11; } public void rotate (double theta, double x, double y) { translate (x, y); rotate (theta); translate (-x, -y); } public void scale (double sx, double sy) { m00 *= sx; m01 *= sy; m10 *= sx; m11 *= sy; } public void shear (double shx, double shy) { double n00 = m00 + shx * m01; double n01 = shx * m00 + m01; double n10 = m10 * shy + m11; double n11 = shx * m10 + m11; m00 = n00; m01 = n01; m10 = n10; m11 = n11; } public void setToIdentity () { m00 = m11 = 1; m01 = m02 = m10 = m12 = 0; type = TYPE_IDENTITY; } public void setToTranslation (double tx, double ty) { m00 = m11 = 1; m01 = m10 = 0; m02 = tx; m12 = ty; type = TYPE_TRANSLATION; } public void setToRotation (double theta) { double c = Math.cos (theta); double s = Math.sin (theta); m00 = c; m01 = -s; m02 = 0; m10 = s; m11 = c; m12 = 0; type = TYPE_GENERAL_ROTATION; } public void setToScale (double sx, double sy) { m00 = sx; m01 = m02 = m10 = m12 = 0; m11 = sy; type = (sx == sy) ? TYPE_UNIFORM_SCALE : TYPE_GENERAL_SCALE; } public void setToShear (double shx, double shy) { m00 = m11 = 1; m01 = shx; m10 = shy; m02 = m12 = 0; type = 0; // FIXME } public void setTransform (AffineTransform tx) { m00 = tx.m00; m01 = tx.m01; m02 = tx.m02; m10 = tx.m10; m11 = tx.m11; m12 = tx.m12; type = tx.type; } public void setTransform (double m00, double m10, double m01, double m11, double m02, double m12) { this.m00 = m00; this.m10 = m10; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; this.type = 0; // FIXME } public void concatentate (AffineTransform tx) { double n00 = m00 * tx.m00 + m01 * tx.m10; double n01 = m00 * tx.m01 + m01 * tx.m11; double n02 = m00 * tx.m02 + m01 * tx.m12 + m02; double n10 = m10 * tx.m00 + m11 * tx.m10; double n11 = m10 * tx.m01 + m11 * tx.m11; double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; m00 = n00; m01 = n01; m02 = n02; m10 = n10; m11 = n11; m12 = n12; } public void preConcatenate (AffineTransform tx) { double n00 = tx.m00 * m00 + tx.m01 * m10; double n01 = tx.m00 * m01 + tx.m01 * m11; double n02 = tx.m00 * m02 + tx.m01 * m12 + tx.m02; double n10 = tx.m10 * m00 + tx.m11 * m10; double n11 = tx.m10 * m01 + tx.m11 * m11; double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; m00 = n00; m01 = n01; m02 = n02; m10 = n10; m11 = n11; m12 = n12; } public AffineTransform createInverse () throws NoninvertibleTransformException { double det = getDeterminant (); if (det == 0) throw new NoninvertibleTransformException ("can't invert transform"); double i00 = m11 / det; double i01 = -m10 / det; double i02 = 0; double i10 = m01 / det; double i11 = -m00 / det; double i12 = 0; return new AffineTransform (i00, i01, i02, i10, i11, i12); } public Point2D transform (Point2D src, Point2D dst) { if (dst == null) dst = new Point2D.Double (); // We compute and set separately to correctly overwrite if // src==dst. double x = src.getX (); double y = src.getY (); double nx = m00 * x + m01 * y + m02; double ny = m10 * x + m11 * y + m12; dst.setLocation (nx, ny); return dst; } public void transform (Point2D[] src, int srcOff, Point2D[] dst, int dstOff, int num) { while (num-- > 0) { dst[dstOff] = transform (src[srcOff], dst[dstOff]); ++srcOff; ++dstOff; } } public void transform (float[] srcPts, int srcOff, float[] dstPts, int dstOff, int num) { while (num-- > 0) { float x = srcPts[srcOff]; float y = srcPts[srcOff + 1]; srcOff += 2; float nx = (float) (m00 * x + m01 * y + m02); float ny = (float) (m10 * x + m10 * y + m12); dstPts[dstOff] = nx; dstPts[dstOff + 1] = ny; dstOff += 2; } } public void transform (double[] srcPts, int srcOff, double[] dstPts, int dstOff, int num) { while (num-- > 0) { double x = srcPts[srcOff]; double y = srcPts[srcOff + 1]; srcOff += 2; double nx = m00 * x + m01 * y + m02; double ny = m10 * x + m10 * y + m12; dstPts[dstOff] = nx; dstPts[dstOff + 1] = ny; dstOff += 2; } } public void transform (float[] srcPts, int srcOff, double[] dstPts, int dstOff, int num) { while (num-- > 0) { float x = srcPts[srcOff]; float y = srcPts[srcOff + 1]; srcOff += 2; double nx = m00 * x + m01 * y + m02; double ny = m10 * x + m10 * y + m12; dstPts[dstOff] = nx; dstPts[dstOff + 1] = ny; dstOff += 2; } } public void transform (double[] srcPts, int srcOff, float[] dstPts, int dstOff, int num) { while (num-- > 0) { double x = srcPts[srcOff]; double y = srcPts[srcOff + 1]; srcOff += 2; float nx = (float) (m00 * x + m01 * y + m02); float ny = (float) (m10 * x + m10 * y + m12); dstPts[dstOff] = nx; dstPts[dstOff + 1] = ny; dstOff += 2; } } public Point2D inverseTransform (Point2D src, Point2D dst) throws NoninvertibleTransformException { double det = getDeterminant (); if (det == 0) throw new NoninvertibleTransformException ("couldn't invert transform"); if (dst == null) dst = new Point2D.Double (); double x = src.getX (); double y = src.getY (); double nx = (m11 * x + - m10 * y) / det; double ny = (m01 * x + - m00 * y) / det; dst.setLocation (nx, ny); return dst; } public void inverseTransform (double[] srcPts, int srcOff, double[] dstPts, int dstOff, int num) throws NoninvertibleTransformException { double det = getDeterminant (); if (det == 0) throw new NoninvertibleTransformException ("couldn't invert transform"); while (num-- > 0) { double x = srcPts[srcOff]; double y = srcPts[srcOff + 1]; double nx = (m11 * x + - m10 * y) / det; double ny = (m01 * x + - m00 * y) / det; dstPts[dstOff] = nx; dstPts[dstOff + 1] = ny; dstOff += 2; srcOff += 2; } } public Point2D deltaTransform (Point2D src, Point2D dst) { if (dst == null) dst = new Point2D.Double (); double x = src.getX (); double y = src.getY (); double nx = m00 * x + m01 * y; double ny = m10 * x + m11 * y; dst.setLocation (nx, ny); return dst; } public void deltaTransform (double[] srcPts, int srcOff, double[] dstPts, int dstOff, int num) { while (num-- > 0) { double x = srcPts[srcOff]; double y = srcPts[srcOff + 1]; double nx = m00 * x + m01 * y; double ny = m10 * x + m11 * y; dstPts[dstOff] = nx; dstPts[dstOff + 1] = ny; dstOff += 2; srcOff += 2; } } public Shape createTransformedShape (Shape pSrc) { // FIXME return null; } public String toString () { // FIXME return null; } public boolean isIdentity () { return (m00 == 1 && m01 == 0 && m02 == 0 && m10 == 0 && m11 == 1 && m12 == 0); } public Object clone () { return new AffineTransform (this); } public int hashCode () { // FIXME return 23; } public boolean equals (Object obj) { if (! (obj instanceof AffineTransform)) return false; AffineTransform t = (AffineTransform) obj; return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02 && m10 == t.m10 && m11 == t.m11 && m12 == t.m12); } // This iterator is used to apply an AffineTransform to some other // iterator. It is not private because we want to be able to access // it from the rest of this package. class Iterator implements PathIterator { // The iterator we are applied to. private PathIterator subIterator; public Iterator (PathIterator subIterator) { this.subIterator = subIterator; } public int currentSegment (double[] coords) { int r = subIterator.currentSegment (coords); int count = 0; switch (r) { case SEG_CUBICTO: count = 3; break; case SEG_QUADTO: count = 2; break; case SEG_LINETO: case SEG_MOVETO: count = 1; break; default: // Error. But how to report? case SEG_CLOSE: break; } transform (coords, 0, coords, 0, count); return r; } public int currentSegment (float[] coords) { int r = subIterator.currentSegment (coords); int count = 0; switch (r) { case SEG_CUBICTO: count = 3; break; case SEG_QUADTO: count = 2; break; case SEG_LINETO: case SEG_MOVETO: count = 1; break; default: // Error. But how to report? case SEG_CLOSE: break; } transform (coords, 0, coords, 0, count); return r; } public int getWindingRule () { return subIterator.getWindingRule (); } public boolean isDone () { return subIterator.isDone (); } public void next () { subIterator.next (); } } private double m00, m01, m02; private double m10, m11, m12; private int type; }