diff options
Diffstat (limited to 'libjava/java/awt/geom')
-rw-r--r-- | libjava/java/awt/geom/AffineTransform.java | 1461 | ||||
-rw-r--r-- | libjava/java/awt/geom/Arc2D.java | 1123 | ||||
-rw-r--r-- | libjava/java/awt/geom/Area.java | 183 | ||||
-rw-r--r-- | libjava/java/awt/geom/CubicCurve2D.java | 519 | ||||
-rw-r--r-- | libjava/java/awt/geom/Dimension2D.java | 81 | ||||
-rw-r--r-- | libjava/java/awt/geom/Ellipse2D.java | 100 | ||||
-rw-r--r-- | libjava/java/awt/geom/FlatteningPathIterator.java | 105 | ||||
-rw-r--r-- | libjava/java/awt/geom/GeneralPath.java | 361 | ||||
-rw-r--r-- | libjava/java/awt/geom/IllegalPathStateException.java | 30 | ||||
-rw-r--r-- | libjava/java/awt/geom/Line2D.java | 1190 | ||||
-rw-r--r-- | libjava/java/awt/geom/NoninvertibleTransformException.java | 25 | ||||
-rw-r--r-- | libjava/java/awt/geom/PathIterator.java | 164 | ||||
-rw-r--r-- | libjava/java/awt/geom/Point2D.java | 316 | ||||
-rw-r--r-- | libjava/java/awt/geom/QuadCurve2D.java | 486 | ||||
-rw-r--r-- | libjava/java/awt/geom/Rectangle2D.java | 1010 | ||||
-rw-r--r-- | libjava/java/awt/geom/RectangularShape.java | 484 | ||||
-rw-r--r-- | libjava/java/awt/geom/RoundRectangle2D.java | 177 |
17 files changed, 6430 insertions, 1385 deletions
diff --git a/libjava/java/awt/geom/AffineTransform.java b/libjava/java/awt/geom/AffineTransform.java index 436a842..3c9486e 100644 --- a/libjava/java/awt/geom/AffineTransform.java +++ b/libjava/java/awt/geom/AffineTransform.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2001, 2002 Free Software Foundation +/* AffineTransform.java -- transform coordinates between two 2-D spaces + Copyright (C) 2000, 2001, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -34,44 +35,276 @@ this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ + package java.awt.geom; -import java.awt.*; + +import java.awt.Shape; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; /** + * This class represents an affine transformation between two coordinate + * spaces in 2 dimensions. Such a transform preserves the "straightness" + * and "parallelness" of lines. The transform is built from a sequence of + * translations, scales, flips, rotations, and shears. + * + * <p>The transformation can be represented using matrix math on a 3x3 array. + * Given (x,y), the transformation (x',y') can be found by: + * <pre> + * [ x'] [ m00 m01 m02 ] [ x ] [ m00*x + m01*y + m02 ] + * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10*x + m11*y + m12 ] + * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] + * </pre> + * The bottom row of the matrix is constant, so a transform can be uniquely + * represented (as in toString) by "[[m00, m01, m02], [m10, m11, m12]]". + * * @author Tom Tromey <tromey@cygnus.com> - * @date April 16, 2000 + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status partially updated to 1.4, still has some problems */ - -/* Status: mostly complete. Search for fixme to see problems. - Also, TYPE_ returns are not handled correctly. */ - public class AffineTransform implements Cloneable, Serializable { + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 1330973210523860834L; + + /** + * The transformation is the identity (x' = x, y' = y). All other transforms + * have either a combination of the appropriate transform flag bits for + * their type, or the type GENERAL_TRANSFORM. + * + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ public static final int TYPE_IDENTITY = 0; - public static final int TYPE_FLIP = 64; - public static final int TYPE_GENERAL_ROTATION = 16; + + /** + * The transformation includes a translation - shifting in the x or y + * direction without changing length or angles. + * + * @see #TYPE_IDENTITY + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ + public static final int TYPE_TRANSLATION = 1; + + /** + * The transformation includes a uniform scale - length is scaled in both + * the x and y directions by the same amount, without affecting angles. + * This is mutually exclusive with TYPE_GENERAL_SCALE. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_SCALE + * @see #getType() + */ + public static final int TYPE_UNIFORM_SCALE = 2; + + /** + * The transformation includes a general scale - length is scaled in either + * or both the x and y directions, but by different amounts; without + * affecting angles. This is mutually exclusive with TYPE_UNIFORM_SCALE. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_SCALE + * @see #getType() + */ public static final int TYPE_GENERAL_SCALE = 4; - public static final int TYPE_GENERAL_TRANSFORM = 32; - public static final int TYPE_MASK_ROTATION = 24; + + /** + * This constant checks if either variety of scale transform is performed. + * + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + */ public static final int TYPE_MASK_SCALE = 6; + + /** + * The transformation includes a flip about an axis, swapping between + * right-handed and left-handed coordinate systems. In a right-handed + * system, the positive x-axis rotates counter-clockwise to the positive + * y-axis; in a left-handed system it rotates clockwise. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ + public static final int TYPE_FLIP = 64; + + /** + * The transformation includes a rotation of a multiple of 90 degrees (PI/2 + * radians). Angles are rotated, but length is preserved. This is mutually + * exclusive with TYPE_GENERAL_ROTATION. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_ROTATION + * @see #getType() + */ public static final int TYPE_QUADRANT_ROTATION = 8; - public static final int TYPE_TRANSLATION = 1; - public static final int TYPE_UNIFORM_SCALE = 2; - public AffineTransform () + /** + * The transformation includes a rotation by an arbitrary angle. Angles are + * rotated, but length is preserved. This is mutually exclusive with + * TYPE_QUADRANT_ROTATION. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_ROTATION + * @see #getType() + */ + public static final int TYPE_GENERAL_ROTATION = 16; + + /** + * This constant checks if either variety of rotation is performed. + * + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + */ + public static final int TYPE_MASK_ROTATION = 24; + + /** + * The transformation is an arbitrary conversion of coordinates which + * could not be decomposed into the other TYPEs. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #getType() + */ + public static final int TYPE_GENERAL_TRANSFORM = 32; + + /** + * The X coordinate scaling element of the transform matrix. + * + * @serial matrix[0,0] + */ + private double m00; + + /** + * The Y coordinate scaling element of the transform matrix. + * + * @serial matrix[1,0] + */ + private double m10; + + /** + * The X coordinate shearing element of the transform matrix. + * + * @serial matrix[0,1] + */ + private double m01; + + /** + * The Y coordinate shearing element of the transform matrix. + * + * @serial matrix[1,1] + */ + private double m11; + + /** + * The X coordinate translation element of the transform matrix. + * + * @serial matrix[0,2] + */ + private double m02; + + /** + * The Y coordinate translation element of the transform matrix. + * + * @serial matrix[1,2] + */ + private double m12; + + /** The type of this transform. */ + private transient int type; + + /** + * Construct a new identity transform: + * <pre> + * [ 1 0 0 ] + * [ 0 1 0 ] + * [ 0 0 1 ] + * </pre> + */ + public AffineTransform() { - setToIdentity (); + m00 = m11 = 1; } - public AffineTransform (AffineTransform tx) + /** + * Create a new transform which copies the given one. + * + * @param tx the transform to copy + * @throws NullPointerException if tx is null + */ + public AffineTransform(AffineTransform tx) { - setTransform (tx); + setTransform(tx); } - public AffineTransform (float m00, float m10, - float m01, float m11, - float m02, float m12) + /** + * Construct a transform with the given matrix entries: + * <pre> + * [ m00 m01 m02 ] + * [ m10 m11 m12 ] + * [ 0 0 1 ] + * </pre> + * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public AffineTransform(float m00, float m10, + float m01, float m11, + float m02, float m12) { this.m00 = m00; this.m10 = m10; @@ -79,24 +312,54 @@ public class AffineTransform implements Cloneable, Serializable this.m11 = m11; this.m02 = m02; this.m12 = m12; - this.type = TYPE_GENERAL_TRANSFORM; + updateType(); } - public AffineTransform (float[] flatmatrix) + /** + * Construct a transform from a sequence of float entries. The array must + * have at least 4 entries, which has a translation factor of 0; or 6 + * entries, for specifying all parameters: + * <pre> + * [ f[0] f[2] (f[4]) ] + * [ f[1] f[3] (f[5]) ] + * [ 0 0 1 ] + * </pre> + * + * @param f the matrix to copy from, with at least 4 (6) entries + * @throws NullPointerException if f is null + * @throws ArrayIndexOutOfBoundsException if f is too small + */ + public AffineTransform(float[] f) { - m00 = flatmatrix[0]; - m10 = flatmatrix[1]; - m01 = flatmatrix[2]; - m11 = flatmatrix[3]; - if (flatmatrix.length >= 6) + m00 = f[0]; + m10 = f[1]; + m01 = f[2]; + m11 = f[3]; + if (f.length >= 6) { - m02 = flatmatrix[4]; - m12 = flatmatrix[5]; + m02 = f[4]; + m12 = f[5]; } + updateType(); } - public AffineTransform (double m00, double m10, double m01, - double m11, double m02, double m12) + /** + * Construct a transform with the given matrix entries: + * <pre> + * [ m00 m01 m02 ] + * [ m10 m11 m12 ] + * [ 0 0 1 ] + * </pre> + * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public AffineTransform(double m00, double m10, double m01, + double m11, double m02, double m12) { this.m00 = m00; this.m10 = m10; @@ -104,222 +367,532 @@ public class AffineTransform implements Cloneable, Serializable this.m11 = m11; this.m02 = m02; this.m12 = m12; - this.type = TYPE_GENERAL_TRANSFORM; + updateType(); } - public AffineTransform (double[] flatmatrix) + /** + * Construct a transform from a sequence of double entries. The array must + * have at least 4 entries, which has a translation factor of 0; or 6 + * entries, for specifying all parameters: + * <pre> + * [ d[0] d[2] (d[4]) ] + * [ d[1] d[3] (d[5]) ] + * [ 0 0 1 ] + * </pre> + * + * @param d the matrix to copy from, with at least 4 (6) entries + * @throws NullPointerException if d is null + * @throws ArrayIndexOutOfBoundsException if d is too small + */ + public AffineTransform(double[] d) { - m00 = flatmatrix[0]; - m10 = flatmatrix[1]; - m01 = flatmatrix[2]; - m11 = flatmatrix[3]; - if (flatmatrix.length >= 6) + m00 = d[0]; + m10 = d[1]; + m01 = d[2]; + m11 = d[3]; + if (d.length >= 6) { - m02 = flatmatrix[4]; - m12 = flatmatrix[5]; + m02 = d[4]; + m12 = d[5]; } + updateType(); } - public static AffineTransform getTranslateInstance (double tx, double ty) + /** + * Returns a translation transform: + * <pre> + * [ 1 0 tx ] + * [ 0 1 ty ] + * [ 0 0 1 ] + * </pre> + * + * @param tx the x translation distance + * @param ty the y translation distance + * @return the translating transform + */ + public static AffineTransform getTranslateInstance(double tx, double ty) { - AffineTransform t = new AffineTransform (); - t.setToTranslation (tx, ty); + AffineTransform t = new AffineTransform(); + t.setToTranslation(tx, ty); return t; } - public static AffineTransform getRotateInstance (double theta) + /** + * Returns a rotation transform. A positive angle (in radians) rotates + * the positive x-axis to the positive y-axis: + * <pre> + * [ cos(theta) -sin(theta) 0 ] + * [ sin(theta) cos(theta) 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param theta the rotation angle + * @return the rotating transform + */ + public static AffineTransform getRotateInstance(double theta) { - AffineTransform t = new AffineTransform (); - t.setToRotation (theta); + AffineTransform t = new AffineTransform(); + t.setToRotation(theta); return t; } - public static AffineTransform getRotateInstance (double theta, - double x, double y) + /** + * Returns a rotation transform about a point. A positive angle (in radians) + * rotates the positive x-axis to the positive y-axis. This is the same + * as calling: + * <pre> + * AffineTransform tx = new AffineTransform(); + * tx.setToTranslation(x, y); + * tx.rotate(theta); + * tx.translate(-x, -y); + * </pre> + * + * <p>The resulting matrix is: + * <pre> + * [ cos(theta) -sin(theta) x-x*cos+y*sin ] + * [ sin(theta) cos(theta) y-x*sin-y*cos ] + * [ 0 0 1 ] + * </pre> + * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + * @return the rotating transform + */ + public static AffineTransform getRotateInstance(double theta, + double x, double y) { - AffineTransform t = new AffineTransform (); - t.rotate (theta, x, y); + AffineTransform t = new AffineTransform(); + t.setToTranslation(x, y); + t.rotate(theta); + t.translate(-x, -y); return t; } - public static AffineTransform getScaleInstance (double sx, double sy) + /** + * Returns a scaling transform: + * <pre> + * [ sx 0 0 ] + * [ 0 sy 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param sx the x scaling factor + * @param sy the y scaling factor + * @return the scaling transform + */ + public static AffineTransform getScaleInstance(double sx, double sy) { - AffineTransform t = new AffineTransform (); - t.setToScale (sx, sy); + AffineTransform t = new AffineTransform(); + t.setToScale(sx, sy); return t; } - public static AffineTransform getShearInstance (double shx, double shy) + /** + * Returns a shearing transform (points are shifted in the x direction based + * on a factor of their y coordinate, and in the y direction as a factor of + * their x coordinate): + * <pre> + * [ 1 shx 0 ] + * [ shy 1 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param shx the x shearing factor + * @param shy the y shearing factor + * @return the shearing transform + */ + public static AffineTransform getShearInstance(double shx, double shy) { - AffineTransform t = new AffineTransform (); - t.setToShear (shx, shy); + AffineTransform t = new AffineTransform(); + t.setToShear(shx, shy); return t; } - public int getType () + /** + * Returns the type of this transform. The result is always valid, although + * it may not be the simplest interpretation (in other words, there are + * sequences of transforms which reduce to something simpler, which this + * does not always detect). The result is either TYPE_GENERAL_TRANSFORM, + * or a bit-wise combination of TYPE_TRANSLATION, the mutually exclusive + * TYPE_*_ROTATIONs, and the mutually exclusive TYPE_*_SCALEs. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + */ + public int getType() { return type; } - public double getDeterminant () + /** + * Return the determinant of this transform matrix. If the determinant is + * non-zero, the transform is invertible; otherwise operations which require + * an inverse throw a NoninvertibleTransformException. A result very near + * zero, due to rounding errors, may indicate that inversion results do not + * carry enough precision to be meaningful. + * + * <p>If this is a uniform scale transformation, the determinant also + * represents the squared value of the scale. Otherwise, it carries little + * additional meaning. The determinant is calculated as: + * <pre> + * | m00 m01 m02 | + * | m10 m11 m12 | = m00 * m11 - m01 * m10 + * | 0 0 1 | + * </pre> + * + * @return the determinant + * @see #createInverse() + */ + public double getDeterminant() { return m00 * m11 - m01 * m10; } - public void getMatrix (double[] flatmatrix) + /** + * Return the matrix of values used in this transform. If the matrix has + * fewer than 6 entries, only the scale and shear factors are returned; + * otherwise the translation factors are copied as well. The resulting + * values are: + * <pre> + * [ d[0] d[2] (d[4]) ] + * [ d[1] d[3] (d[5]) ] + * [ 0 0 1 ] + * </pre> + * + * @param d the matrix to store the results into; with 4 (6) entries + * @throws NullPointerException if d is null + * @throws ArrayIndexOutOfBoundsException if d is too small + */ + public void getMatrix(double[] d) { - flatmatrix[0] = m00; - flatmatrix[1] = m10; - flatmatrix[2] = m01; - flatmatrix[3] = m11; - if (flatmatrix.length >= 6) + d[0] = m00; + d[1] = m10; + d[2] = m01; + d[3] = m11; + if (d.length >= 6) { - flatmatrix[4] = m02; - flatmatrix[5] = m12; + d[4] = m02; + d[5] = m12; } } - public double getScaleX () + /** + * Returns the X coordinate scaling factor of the matrix. + * + * @return m00 + * @see #getMatrix(double[]) + */ + public double getScaleX() { return m00; } - public double getScaleY () + /** + * Returns the Y coordinate scaling factor of the matrix. + * + * @return m11 + * @see #getMatrix(double[]) + */ + public double getScaleY() { return m11; } - public double getShearX () + /** + * Returns the X coordinate shearing factor of the matrix. + * + * @return m01 + * @see #getMatrix(double[]) + */ + public double getShearX() { return m01; } - public double getShearY () + /** + * Returns the Y coordinate shearing factor of the matrix. + * + * @return m10 + * @see #getMatrix(double[]) + */ + public double getShearY() { return m10; } - public double getTranslateX () + /** + * Returns the X coordinate translation factor of the matrix. + * + * @return m02 + * @see #getMatrix(double[]) + */ + public double getTranslateX() { return m02; } - public double getTranslateY () + /** + * Returns the Y coordinate translation factor of the matrix. + * + * @return m12 + * @see #getMatrix(double[]) + */ + public double getTranslateY() { return m12; } - public void translate (double tx, double ty) + /** + * Concatenate a translation onto this transform. This is equivalent, but + * more efficient than + * <code>concatenate(AffineTransform.getTranslateInstance(tx, ty))</code>. + * + * @param tx the x translation distance + * @param ty the y translation distance + * @see #getTranslateInstance(double, double) + * @see #concatenate(AffineTransform) + */ + public void translate(double tx, double ty) { m02 += tx * m00 + ty * m01; m12 += tx * m10 + ty * m11; + updateType(); } - public void rotate (double theta) + /** + * Concatenate a rotation onto this transform. This is equivalent, but + * more efficient than + * <code>concatenate(AffineTransform.getRotateInstance(theta))</code>. + * + * @param theta the rotation angle + * @see #getRotateInstance(double) + * @see #concatenate(AffineTransform) + */ + public void rotate(double theta) { - double c = Math.cos (theta); - double s = Math.sin (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; + updateType(); } - public void rotate (double theta, double x, double y) + /** + * Concatenate a rotation about a point onto this transform. This is + * equivalent, but more efficient than + * <code>concatenate(AffineTransform.getRotateInstance(theta, x, y))</code>. + * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + * @see #getRotateInstance(double, double, double) + * @see #concatenate(AffineTransform) + */ + public void rotate(double theta, double x, double y) { - translate (x, y); - rotate (theta); - translate (-x, -y); + translate(x, y); + rotate(theta); + translate(-x, -y); } - public void scale (double sx, double sy) + /** + * Concatenate a scale onto this transform. This is equivalent, but more + * efficient than + * <code>concatenate(AffineTransform.getScaleInstance(sx, sy))</code>. + * + * @param sx the x scaling factor + * @param sy the y scaling factor + * @see #getScaleInstance(double, double) + * @see #concatenate(AffineTransform) + */ + public void scale(double sx, double sy) { m00 *= sx; m01 *= sy; m10 *= sx; m11 *= sy; + updateType(); } - public void shear (double shx, double shy) + /** + * Concatenate a shearing onto this transform. This is equivalent, but more + * efficient than + * <code>concatenate(AffineTransform.getShearInstance(sx, sy))</code>. + * + * @param shx the x shearing factor + * @param shy the y shearing factor + * @see #getShearInstance(double, double) + * @see #concatenate(AffineTransform) + */ + 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; + updateType(); } - public void setToIdentity () + /** + * Reset this transform to the identity (no transformation): + * <pre> + * [ 1 0 0 ] + * [ 0 1 0 ] + * [ 0 0 1 ] + * </pre> + */ + public void setToIdentity() { m00 = m11 = 1; m01 = m02 = m10 = m12 = 0; type = TYPE_IDENTITY; } - public void setToTranslation (double tx, double ty) + /** + * Set this transform to a translation: + * <pre> + * [ 1 0 tx ] + * [ 0 1 ty ] + * [ 0 0 1 ] + * </pre> + * + * @param tx the x translation distance + * @param ty the y translation distance + */ + public void setToTranslation(double tx, double ty) { m00 = m11 = 1; m01 = m10 = 0; m02 = tx; m12 = ty; - type = TYPE_TRANSLATION; + type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; } - public void setToRotation (double theta) + /** + * Set this transform to a rotation. A positive angle (in radians) rotates + * the positive x-axis to the positive y-axis: + * <pre> + * [ cos(theta) -sin(theta) 0 ] + * [ sin(theta) cos(theta) 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param theta the rotation angle + */ + public void setToRotation(double theta) { - double c = Math.cos (theta); - double s = Math.sin (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; + type = (c == 1 ? TYPE_IDENTITY + : c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION + : TYPE_GENERAL_ROTATION); } - public void setToRotation (double theta, double x, double y) + /** + * Set this transform to a rotation about a point. A positive angle (in + * radians) rotates the positive x-axis to the positive y-axis. This is the + * same as calling: + * <pre> + * tx.setToTranslation(x, y); + * tx.rotate(theta); + * tx.translate(-x, -y); + * </pre> + * + * <p>The resulting matrix is: + * <pre> + * [ cos(theta) -sin(theta) x-x*cos+y*sin ] + * [ sin(theta) cos(theta) y-x*sin-y*cos ] + * [ 0 0 1 ] + * </pre> + * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + */ + public void setToRotation(double theta, double x, double y) { - double c = Math.cos (theta); - double s = Math.sin (theta); - + double c = Math.cos(theta); + double s = Math.sin(theta); m00 = c; m01 = -s; m02 = x - x * c + y * s; m10 = s; m11 = c; m12 = y - x * s - y * c; - type = TYPE_GENERAL_TRANSFORM; + updateType(); } - public void setToScale (double sx, double sy) + /** + * Set this transform to a scale: + * <pre> + * [ sx 0 0 ] + * [ 0 sy 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param sx the x scaling factor + * @param sy the y scaling factor + */ + 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; + type = (sx != sy ? TYPE_GENERAL_SCALE + : sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE); } - public void setToShear (double shx, double shy) + /** + * Set this transform to a shear (points are shifted in the x direction based + * on a factor of their y coordinate, and in the y direction as a factor of + * their x coordinate): + * <pre> + * [ 1 shx 0 ] + * [ shy 1 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param shx the x shearing factor + * @param shy the y shearing factor + */ + public void setToShear(double shx, double shy) { m00 = m11 = 1; m01 = shx; m10 = shy; m02 = m12 = 0; - type = TYPE_GENERAL_TRANSFORM; + updateType(); } - public void setTransform (AffineTransform tx) + /** + * Set this transform to a copy of the given one. + * + * @param tx the transform to copy + * @throws NullPointerException if tx is null + */ + public void setTransform(AffineTransform tx) { m00 = tx.m00; m01 = tx.m01; @@ -330,8 +903,23 @@ public class AffineTransform implements Cloneable, Serializable type = tx.type; } - public void setTransform (double m00, double m10, double m01, - double m11, double m02, double m12) + /** + * Set this transform to the given values: + * <pre> + * [ m00 m01 m02 ] + * [ m10 m11 m12 ] + * [ 0 0 1 ] + * </pre> + * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public void setTransform(double m00, double m10, double m01, + double m11, double m02, double m12) { this.m00 = m00; this.m10 = m10; @@ -339,10 +927,22 @@ public class AffineTransform implements Cloneable, Serializable this.m11 = m11; this.m02 = m02; this.m12 = m12; - this.type = 0; // FIXME + updateType(); } - public void concatenate (AffineTransform tx) + /** + * Set this transform to the result of performing the original version of + * this followed by tx. This is commonly used when chaining transformations + * from one space to another. In matrix form: + * <pre> + * [ this ] = [ this ] x [ tx ] + * </pre> + * + * @param tx the transform to concatenate + * @throws NullPointerException if tx is null + * @see #preConcatenate(AffineTransform) + */ + public void concatenate(AffineTransform tx) { double n00 = m00 * tx.m00 + m01 * tx.m10; double n01 = m00 * tx.m01 + m01 * tx.m11; @@ -350,16 +950,29 @@ public class AffineTransform implements Cloneable, Serializable 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; + updateType(); } - public void preConcatenate (AffineTransform tx) + /** + * Set this transform to the result of performing tx followed by the + * original version of this. This is less common than normal concatenation, + * but can still be used to chain transformations from one space to another. + * In matrix form: + * <pre> + * [ this ] = [ tx ] x [ this ] + * </pre> + * + * @param tx the transform to concatenate + * @throws NullPointerException if tx is null + * @see #concatenate(AffineTransform) + */ + public void preConcatenate(AffineTransform tx) { double n00 = tx.m00 * m00 + tx.m01 * m10; double n01 = tx.m00 * m01 + tx.m01 * m11; @@ -367,328 +980,490 @@ public class AffineTransform implements Cloneable, Serializable 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; + updateType(); } - public AffineTransform createInverse () + /** + * Returns a transform, which if concatenated to this one, will result in + * the identity transform. This is useful for undoing transformations, but + * is only possible if the original transform has an inverse (ie. does not + * map multiple points to the same line or point). A transform exists only + * if getDeterminant() has a non-zero value. + * + * @return a new inverse transform + * @throws NoninvertibleTransformException if inversion is not possible + * @see #getDeterminant() + */ + public AffineTransform createInverse() throws NoninvertibleTransformException { - double det = getDeterminant (); + 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); + throw new NoninvertibleTransformException("can't invert transform"); + return new AffineTransform(m11 / det, -m10 / det, m01 / det, -m00 / det, + -m02, -m12); } - public Point2D transform (Point2D src, Point2D dst) + /** + * Perform this transformation on the given source point, and store the + * result in the destination (creating it if necessary). It is safe for + * src and dst to be the same. + * + * @param src the source point + * @param dst the destination, or null + * @return the transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + */ + 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 (); + dst = new Point2D.Double(); + 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); - + dst.setLocation(nx, ny); return dst; } - public void transform (Point2D[] src, int srcOff, - Point2D[] dst, int dstOff, - int num) + /** + * Perform this transformation on an array of points, storing the results + * in another (possibly same) array. This will not create a destination + * array, but will create points for the null entries of the destination. + * The transformation is done sequentially. While having a single source + * and destination point be the same is safe, you should be aware that + * duplicate references to the same point in the source, and having the + * source overlap the destination, may result in your source points changing + * from a previous transform before it is their turn to be evaluated. + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points (may have null entries) + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null, or src has null + * entries + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + * @throws ArrayStoreException if new points are incompatible with 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; - } + while (--num >= 0) + dst[dstOff] = transform(src[srcOff++], dst[dstOff++]); } - public void transform (float[] srcPts, int srcOff, - float[] dstPts, int dstOff, - int num) + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another (possibly same) array. This will not + * create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(float[] srcPts, int srcOff, + float[] dstPts, int dstOff, int num) { - while (num-- > 0) + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) { - 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; + float[] f = new float[2 * num]; + System.arraycopy(srcPts, srcOff, f, 0, 2 * num); + srcPts = f; + } + while (--num >= 0) + { + float x = srcPts[srcOff++]; + float y = srcPts[srcOff++]; + dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); + dstPts[dstOff++] = (float) (m10 * x + m10 * y + m12); } } - public void transform (double[] srcPts, int srcOff, - double[] dstPts, int dstOff, - int num) + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another (possibly same) array. This will not + * create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) { - while (num-- > 0) + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) + { + double[] d = new double[2 * num]; + System.arraycopy(srcPts, srcOff, d, 0, 2 * num); + srcPts = d; + } + 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; + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y + m02; + dstPts[dstOff++] = m10 * x + m10 * y + m12; } } - public void transform (float[] srcPts, int srcOff, - double[] dstPts, int dstOff, - int num) + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another array. This will not create a destination + * array. + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(float[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) { - while (num-- > 0) + 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; + float x = srcPts[srcOff++]; + float y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y + m02; + dstPts[dstOff++] = m10 * x + m10 * y + m12; } } - public void transform (double[] srcPts, int srcOff, - float[] dstPts, int dstOff, - int num) + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another array. This will not create a destination + * array. + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(double[] srcPts, int srcOff, + float[] dstPts, int dstOff, int num) { - while (num-- > 0) + 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; + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); + dstPts[dstOff++] = (float) (m10 * x + m10 * y + m12); } } - public Point2D inverseTransform (Point2D src, Point2D dst) + /** + * Perform the inverse of this transformation on the given source point, + * and store the result in the destination (creating it if necessary). It + * is safe for src and dst to be the same. + * + * @param src the source point + * @param dst the destination, or null + * @return the inverse transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + * @throws NoninvertibleTransformException if the inverse does not exist + * @see #getDeterminant() + */ + public Point2D inverseTransform(Point2D src, Point2D dst) throws NoninvertibleTransformException { - double det = getDeterminant (); + double det = getDeterminant(); if (det == 0) - throw new NoninvertibleTransformException ("couldn't invert transform"); - + 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); + dst = new Point2D.Double(); + double x = src.getX(); + double y = src.getY(); + double nx = (m11 * x + -m10 * y) / det - m02; + double ny = (m01 * x + -m00 * y) / det - m12; + dst.setLocation(nx, ny); return dst; } - public void inverseTransform (double[] srcPts, int srcOff, - double[] dstPts, int dstOff, - int num) + /** + * Perform the inverse of this transformation on an array of points, in + * (x,y) pairs, storing the results in another (possibly same) array. This + * will not create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + * @throws NoninvertibleTransformException if the inverse does not exist + * @see #getDeterminant() + */ + public void inverseTransform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) throws NoninvertibleTransformException { - double det = getDeterminant (); + double det = getDeterminant(); if (det == 0) - throw new NoninvertibleTransformException ("couldn't invert transform"); - - while (num-- > 0) + throw new NoninvertibleTransformException("couldn't invert transform"); + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) + { + double[] d = new double[2 * num]; + System.arraycopy(srcPts, srcOff, d, 0, 2 * num); + srcPts = d; + } + 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; + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = (m11 * x + -m10 * y) / det - m02; + dstPts[dstOff++] = (m01 * x + -m00 * y) / det - m12; } } - public Point2D deltaTransform (Point2D src, Point2D dst) + /** + * Perform this transformation, less any translation, on the given source + * point, and store the result in the destination (creating it if + * necessary). It is safe for src and dst to be the same. The reduced + * transform is equivalent to: + * <pre> + * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] + * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] + * </pre> + * + * @param src the source point + * @param dst the destination, or null + * @return the delta transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + */ + public Point2D deltaTransform(Point2D src, Point2D dst) { if (dst == null) - dst = new Point2D.Double (); - double x = src.getX (); - double y = src.getY (); + 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); + dst.setLocation(nx, ny); return dst; } - public void deltaTransform (double[] srcPts, int srcOff, - double[] dstPts, int dstOff, - int num) + /** + * Perform this transformation, less any translation, on an array of points, + * in (x,y) pairs, storing the results in another (possibly same) array. + * This will not create a destination array. All sources are copied before + * the transformation, so that no result will overwrite a point that has + * not yet been evaluated. The reduced transform is equivalent to: + * <pre> + * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] + * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] + * </pre> + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void deltaTransform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, + int num) { - while (num-- > 0) + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) { - 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; + double[] d = new double[2 * num]; + System.arraycopy(srcPts, srcOff, d, 0, 2 * num); + srcPts = d; + } + while (--num >= 0) + { + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y; + dstPts[dstOff++] = m10 * x + m11 * y; } } - public Shape createTransformedShape (Shape pSrc) + /** + * Return a new Shape, based on the given one, where the path of the shape + * has been transformed by this transform. Notice that this uses GeneralPath, + * which only stores points in float precision. + * + * @param src the shape source to transform + * @return the shape, transformed by this + * @throws NullPointerException if src is null + * @see GeneralPath#transform(AffineTransform) + */ + public Shape createTransformedShape(Shape src) { - // FIXME - return null; + GeneralPath p = new GeneralPath(src); + p.transform(this); + return p; } - public String toString () + /** + * Returns a string representation of the transform, in the format: + * <code>"AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" + * + m10 + ", " + m11 + ", " + m12 + "]]"</code>. + * + * @return the string representation + */ + public String toString() { - // FIXME - return null; + return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" + + m10 + ", " + m11 + ", " + m12 + "]]"; } - public boolean isIdentity () + /** + * Tests if this transformation is the identity: + * <pre> + * [ 1 0 0 ] + * [ 0 1 0 ] + * [ 0 0 1 ] + * </pre> + * + * @return true if this is the identity transform + */ + public boolean isIdentity() { + // Rather than rely on type, check explicitly. return (m00 == 1 && m01 == 0 && m02 == 0 - && m10 == 0 && m11 == 1 && m12 == 0); + && m10 == 0 && m11 == 1 && m12 == 0); } - public Object clone () + /** + * Create a new transform of the same run-time type, with the same + * transforming properties as this one. + * + * @return the clone + */ + public Object clone() { - return new AffineTransform (this); + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } } - public int hashCode () + /** + * Return the hashcode for this transformation. The formula is not + * documented, but appears to be the same as: + * <pre> + * long l = Double.doubleToLongBits(getScaleX()); + * l = l * 31 + Double.doubleToLongBits(getShearY()); + * l = l * 31 + Double.doubleToLongBits(getShearX()); + * l = l * 31 + Double.doubleToLongBits(getScaleY()); + * l = l * 31 + Double.doubleToLongBits(getTranslateX()); + * l = l * 31 + Double.doubleToLongBits(getTranslateY()); + * return (int) ((l >> 32) ^ l); + * </pre> + * + * @return the hashcode + */ + public int hashCode() { - // FIXME - return 23; + long l = Double.doubleToLongBits(m00); + l = l * 31 + Double.doubleToLongBits(m10); + l = l * 31 + Double.doubleToLongBits(m01); + l = l * 31 + Double.doubleToLongBits(m11); + l = l * 31 + Double.doubleToLongBits(m02); + l = l * 31 + Double.doubleToLongBits(m12); + return (int) ((l >> 32) ^ l); } - public boolean equals (Object obj) + /** + * Compares two transforms for equality. This returns true if they have the + * same matrix values. + * + * @param o the transform to compare + * @return true if it is equal + */ + 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; -} + && m10 == t.m10 && m11 == t.m11 && m12 == t.m12); + } + + /** + * Helper to decode the type from the matrix. This is not guaranteed + * to find the optimal type, but at least it will be valid. + */ + private void updateType() + { + double det = getDeterminant(); + if (det == 0) + { + type = TYPE_GENERAL_TRANSFORM; + return; + } + // Scale (includes rotation by PI) or translation. + if (m01 == 0 && m10 == 0) + { + if (m00 == m11) + type = m00 == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE; + else + type = TYPE_GENERAL_SCALE; + if (m02 != 0 || m12 != 0) + type |= TYPE_TRANSLATION; + } + // Rotation. + else if (m00 == m11 && m01 == -m10) + { + type = m00 == 0 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION; + if (det != 1) + type |= TYPE_UNIFORM_SCALE; + if (m02 != 0 || m12 != 0) + type |= TYPE_TRANSLATION; + } + else + type = TYPE_GENERAL_TRANSFORM; + } + + /** + * Reads a transform from an object stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if there is a problem deserializing + * @throws IOException if there is a problem deserializing + */ + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + updateType(); + } +} // class AffineTransform diff --git a/libjava/java/awt/geom/Arc2D.java b/libjava/java/awt/geom/Arc2D.java new file mode 100644 index 0000000..d62fa67 --- /dev/null +++ b/libjava/java/awt/geom/Arc2D.java @@ -0,0 +1,1123 @@ +/* Arc2D.java -- represents an arc in 2-D space + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.util.NoSuchElementException; + +/** + * This class represents all arcs (segments of an ellipse in 2-D space). The + * arcs are defined by starting angle and extent (arc length) in degrees, as + * opposed to radians (like the rest of Java), and can be open, chorded, or + * wedge shaped. The angles are skewed according to the ellipse, so that 45 + * degrees always points to the upper right corner (positive x, negative y) + * of the bounding rectangle. A positive extent draws a counterclockwise arc, + * and while the angle can be any value, the path iterator only traverses the + * first 360 degrees. Storage is up to the subclasses. + * + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4, but still missing functionality + */ +public abstract class Arc2D extends RectangularShape +{ + /** + * An open arc, with no segment connecting the endpoints. This type of + * arc still contains the same points as a chorded version. + */ + public static final int OPEN = 0; + + /** + * A closed arc with a single segment connecting the endpoints (a chord). + */ + public static final int CHORD = 1; + + /** + * A closed arc with two segments, one from each endpoint, meeting at the + * center of the ellipse. + */ + public static final int PIE = 2; + + /** The closure type of this arc. */ + private int type; + + /** + * Create a new arc, with the specified closure type. + * + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. + * @throws IllegalArgumentException if type is invalid + */ + protected Arc2D(int type) + { + if (type < OPEN || type > PIE) + throw new IllegalArgumentException(); + this.type = type; + } + + /** + * Get the starting angle of the arc in degrees. + * + * @return the starting angle + * @see #setAngleStart(double) + */ + public abstract double getAngleStart(); + + /** + * Get the extent angle of the arc in degrees. + * + * @return the extent angle + * @see #setAngleExtent(double) + */ + public abstract double getAngleExtent(); + + /** + * Return the closure type of the arc. + * + * @return the closure type + * @see #OPEN + * @see #CHORD + * @see #PIE + * @see #setArcType(int) + */ + public int getArcType() + { + return type; + } + + /** + * Returns the starting point of the arc. + * + * @return the start point + */ + public Point2D getStartPoint() + { + double angle = getAngleStart() * (-180 / Math.PI); + double x = (Math.cos(angle) * getWidth() + getX()) / 2; + double y = (Math.sin(angle) * getHeight() + getY()) / 2; + return new Point2D.Double(x, y); + } + + /** + * Returns the ending point of the arc. + * + * @return the end point + */ + public Point2D getEndPoint() + { + double angle = (getAngleStart() + getAngleExtent()) * (-180 / Math.PI); + double x = (Math.cos(angle) * getWidth() + getX()) / 2; + double y = (Math.sin(angle) * getHeight() + getY()) / 2; + return new Point2D.Double(x, y); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param x the new x coordinate of the lower left of the bounding box + * @param y the new y coordinate of the lower left of the bounding box + * @param w the new width of the bounding box + * @param h the new height of the bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public abstract void setArc(double x, double y, double w, double h, + double start, double extent, int type); + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param p the lower left point of the bounding box + * @param d the dimensions of the bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if p or d is null + */ + public void setArc(Point2D p, Dimension2D d, + double start, double extent, int type) + { + setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(), + start, extent, type); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param r the new bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public void setArc(Rectangle2D r, double start, double extent, int type) + { + setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(), + start, extent, type); + } + + /** + * Set the parameters of the arc from the given one. + * + * @param a the arc to copy + * @throws NullPointerException if a is null + */ + public void setArc(Arc2D a) + { + setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), + a.getAngleStart(), a.getAngleExtent(), a.getArcType()); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). This controls the center point and radius, so the arc will be + * circular. + * + * @param x the x coordinate of the center of the circle + * @param y the y coordinate of the center of the circle + * @param r the radius of the circle + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArcByCenter(double x, double y, double r, + double start, double extent, int type) + { + setArc(x - r, y - r, r + r, r + r, start, extent, type); + } + + /** + * Sets the parameters of the arc by finding the tangents of two lines, and + * using the specified radius. The arc will be circular, will begin on the + * tangent point of the line extending from p1 to p2, and will end on the + * tangent point of the line extending from p2 to p3. + * + * XXX What happens if the points are colinear, or the radius negative? + * + * @param p1 the first point + * @param p2 the tangent line intersection point + * @param p3 the third point + * @param r the radius of the arc + * @throws NullPointerException if any point is null + */ + public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double r) + { + // XXX Implement. + throw new Error("not implemented"); + } + + /** + * Set the start, in degrees. + * + * @param start the new start angle + * @see #getAngleStart() + */ + public abstract void setAngleStart(double start); + + /** + * Set the extent, in degrees. + * + * @param extent the new extent angle + * @see #getAngleExtent() + */ + public abstract void setAngleExtent(double extent); + + /** + * Sets the starting angle to the angle of the given point relative to + * the center of the arc. The extent remains constant; in other words, + * this rotates the arc. + * + * @param p the new start point + * @throws NullPointerException if p is null + * @see #getStartPoint() + * @see #getAngleStart() + */ + public void setAngleStart(Point2D p) + { + double x = ((p.getX() * 2) - getX()) / getWidth(); + double y = ((p.getY() * 2) - getY()) / getHeight(); + setAngleStart(Math.atan2(y, x) * (-180 / Math.PI)); + } + + /** + * Sets the starting and extent angles to those of the given points + * relative to the center of the arc. The arc will be non-empty, and will + * extend counterclockwise. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + * @see #setAngleStart(Point2D) + */ + public void setAngles(double x1, double y1, double x2, double y2) + { + // Normalize the points. + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + x1 = ((x1 * 2) - mx) / mw; + y1 = ((y1 * 2) - my) / mh; + x2 = ((x2 * 2) - mx) / mw; + y2 = ((y2 * 2) - my) / mh; + double start = Math.atan2(y1, x1) * (-180 / Math.PI); + double extent = Math.atan2(y2, x2) * (-180 / Math.PI) - start; + if (extent < 0) + extent += 360; + setAngleStart(start); + setAngleExtent(extent); + } + + /** + * Sets the starting and extent angles to those of the given points + * relative to the center of the arc. The arc will be non-empty, and will + * extend counterclockwise. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + * @see #setAngleStart(Point2D) + */ + public void setAngles(Point2D p1, Point2D p2) + { + setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Set the closure type of this arc. + * + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @see #getArcType() + */ + public void setArcType(int type) + { + if (type < OPEN || type > PIE) + throw new IllegalArgumentException(); + this.type = type; + } + + /** + * Sets the location and bounds of the ellipse of which this arc is a part. + * + * @param x the new x coordinate + * @param y the new y coordinate + * @param w the new width + * @param h the new height + * @see #getFrame() + */ + public void setFrame(double x, double y, double w, double h) + { + setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type); + } + + /** + * Gets the bounds of the arc. This is much tighter than + * <code>getBounds</code>, as it takes into consideration the start and + * end angles, and the center point of a pie wedge, rather than just the + * overall ellipse. + * + * @return the bounds of the arc + * @see #getBounds() + */ + public Rectangle2D getBounds2D() + { + double extent = getAngleExtent(); + if (Math.abs(extent) >= 360) + return makeBounds(getX(), getY(), getWidth(), getHeight()); + // XXX Finish implementing. + throw new Error("not implemented"); + } + + /** + * Construct a bounding box in a precision appropriate for the subclass. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @return the rectangle for use in getBounds2D + */ + protected abstract Rectangle2D makeBounds(double x, double y, + double w, double h); + + /** + * Tests if the given angle, in degrees, is included in the arc. + * + * XXX Does this normalize all angles to -180 - 180 first? + * + * @param a the angle to test + * @return true if it is contained + */ + public boolean containsAngle(double a) + { + // XXX Implement. + throw new Error("not implemented"); + } + + /** + * Determines if the arc contains the given point. If the bounding box + * is empty, then this will return false. + * + * @param x the x coordinate to test + * @param y the y coordinate to test + * @return true if the point is inside the arc + */ + public boolean contains(double x, double y) + { + double w = getWidth(); + double h = getHeight(); + if (w <= 0 || h <= 0) + return false; + // XXX Finish implementing. + throw new Error("not implemented"); + } + + /** + * Tests if a given rectangle intersects the area of the arc. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the two shapes share common points + */ + public boolean intersects(double x, double y, double w, double h) + { + double mw = getWidth(); + double mh = getHeight(); + if (mw <= 0 || mh <= 0 || w <= 0 || h <= 0) + return false; + // XXX Finish implementing. + throw new Error("not implemented"); + } + + /** + * Tests if a given rectangle is contained in the area of the arc. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the arc contains the rectangle + */ + public boolean contains(double x, double y, double w, double h) + { + double mw = getWidth(); + double mh = getHeight(); + if (mw <= 0 || mh <= 0 || w <= 0 || h <= 0) + return false; + // XXX Finish implementing. + throw new Error("not implemented"); + } + + /** + * Tests if a given rectangle is contained in the area of the arc. + * + * @param r the rectangle + * @return true if the arc contains the rectangle + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns an iterator over this arc, with an optional transformation. + * This iterator is threadsafe, so future modifications to the arc do not + * affect the iteration. + * + * @param at the transformation, or null + * @return a path iterator + */ + public PathIterator getPathIterator(AffineTransform at) + { + return new ArcIterator(this, at); + } + + /** + * This class is used to iterate over an arc. Since ellipses are a subclass + * of arcs, this is used by Ellipse2D as well. + * + * @author Eric Blake <ebb9@email.byu.edu> + */ + static final class ArcIterator implements PathIterator + { + /** The current iteration. */ + private int current; + + /** The last iteration. */ + private final int limit; + + /** The optional transformation. */ + private final AffineTransform xform; + + /** The x coordinate of the bounding box. */ + private final double x; + + /** The y coordinate of the bounding box. */ + private final double y; + + /** The width of the bounding box. */ + private final double w; + + /** The height of the bounding box. */ + private final double h; + + /** The start angle, in radians (not degrees). */ + private final double start; + + /** The extent angle, in radians (not degrees). */ + private final double extent; + + /** The arc closure type. */ + private final int type; + + /** + * Construct a new iterator over an arc. + * + * @param a the arc + * @param xform the transform + */ + ArcIterator(Arc2D a, AffineTransform xform) + { + this.xform = xform; + x = a.getX(); + y = a.getY(); + w = a.getWidth(); + h = a.getHeight(); + start = a.getAngleStart() * (Math.PI / 180); + extent = a.getAngleExtent() * (Math.PI / 180); + type = a.type; + double e = extent < 0 ? -extent : extent; + if (w < 0 || h < 0) + limit = -1; + else if (e == 0) + limit = type; + else if (e <= 90) + limit = type + 1; + else if (e <= 180) + limit = type + 2; + else if (e <= 270) + limit = type + 3; + else + limit = type + 4; + } + + /** + * Construct a new iterator over an ellipse. + * + * @param e the ellipse + * @param xform the transform + */ + ArcIterator(Ellipse2D e, AffineTransform xform) + { + this.xform = xform; + x = e.getX(); + y = e.getY(); + w = e.getWidth(); + h = e.getHeight(); + start = 0; + extent = -2 * Math.PI; + type = CHORD; + limit = (w < 0 || h < 0) ? -1 : 5; + } + + /** + * Return the winding rule. + * + * @return {@link PathIterator#WIND_NON_ZERO} + */ + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + /** + * Test if the iteration is complete. + * + * @return true if more segments exist + */ + public boolean isDone() + { + return current > limit; + } + + /** + * Advance the iterator. + */ + public void next() + { + current++; + } + + /** + * Put the current segment into the array, and return the segment type. + * + * @param coords an array of 6 elements + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + */ + public int currentSegment(float[] coords) + { + if (current > limit) + throw new NoSuchElementException("arc iterator out of bounds"); + if (current == 0) + { + coords[0] = (float) (Math.cos(start) * w + x) / 2; + coords[1] = (float) (Math.sin(start) * h + y) / 2; + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + if (type != OPEN && current == limit) + return SEG_CLOSE; + if (type == PIE && current == limit - 1) + { + coords[0] = (float) (x + w / 2); + coords[1] = (float) (y + h / 2); + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_LINETO; + } + // XXX Fill coords with 2 control points and next quarter point + coords[0] = (float) 0; + coords[1] = (float) 0; + coords[2] = (float) 0; + coords[3] = (float) 0; + coords[4] = (float) 0; + coords[5] = (float) 0; + if (xform != null) + xform.transform(coords, 0, coords, 0, 3); + return SEG_CUBICTO; + } + + /** + * Put the current segment into the array, and return the segment type. + * + * @param coords an array of 6 elements + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + */ + public int currentSegment(double[] coords) + { + if (current > limit) + throw new NoSuchElementException("arc iterator out of bounds"); + if (current == 0) + { + coords[0] = (Math.cos(start) * w + x) / 2; + coords[1] = (Math.sin(start) * h + y) / 2; + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + if (type != OPEN && current == limit) + return SEG_CLOSE; + if (type == PIE && current == limit - 1) + { + coords[0] = (float) (x + w / 2); + coords[1] = (float) (y + h / 2); + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_LINETO; + } + // XXX Fill coords with 2 control points and next quarter point + coords[0] = 0; + coords[1] = 0; + coords[2] = 0; + coords[3] = 0; + coords[4] = 0; + coords[5] = 0; + if (xform != null) + xform.transform(coords, 0, coords, 0, 3); + return SEG_CUBICTO; + } + } // class ArcIterator + + /** + * This class implements an arc in double precision. + * + * @author Eric Blake <ebb9@email.byu.edu + * @since 1.2 + */ + public static class Double extends Arc2D + { + /** The x coordinate of the box bounding the ellipse of this arc. */ + public double x; + + /** The y coordinate of the box bounding the ellipse of this arc. */ + public double y; + + /** The width of the box bounding the ellipse of this arc. */ + public double width; + + /** The height of the box bounding the ellipse of this arc. */ + public double height; + + /** The start angle of this arc, in degrees. */ + public double start; + + /** The extent angle of this arc, in degrees. */ + public double extent; + + /** + * Create a new, open arc at (0,0) with 0 extent. + */ + public Double() + { + super(OPEN); + } + + /** + * Create a new arc of the given type at (0,0) with 0 extent. + * + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Double(int type) + { + super(type); + } + + /** + * Create a new arc with the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Double(double x, double y, double w, double h, + double start, double extent, int type) + { + super(type); + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + } + + /** + * Create a new arc with the given dimensions. + * + * @param r the bounding box + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public Double(Rectangle2D r, double start, double extent, int type) + { + super(type); + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); + this.start = start; + this.extent = extent; + } + + /** + * Return the x coordinate of the bounding box. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate of the bounding box. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width of the bounding box. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height of the bounding box. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Return the start angle of the arc, in degrees. + * + * @return the value of start + */ + public double getAngleStart() + { + return start; + } + + /** + * Return the extent of the arc, in degrees. + * + * @return the value of extent + */ + public double getAngleExtent() + { + return extent; + } + + /** + * Tests if the arc contains points. + * + * @return true if the arc has no interior + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the arc to the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArc(double x, double y, double w, double h, + double start, double extent, int type) + { + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + setArcType(type); + } + + /** + * Sets the start angle of the arc. + * + * @param start the new start angle + */ + public void setAngleStart(double start) + { + this.start = start; + } + + /** + * Sets the extent angle of the arc. + * + * @param start the new extent angle + */ + public void setAngleExtent(double extent) + { + this.extent = extent; + } + + /** + * Creates a tight bounding box given dimensions that more precise than + * the bounding box of the ellipse. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + protected Rectangle2D makeBounds(double x, double y, double w, double h) + { + return new Rectangle2D.Double(x, y, w, h); + } + } // class Double + + /** + * This class implements an arc in float precision. + * + * @author Eric Blake <ebb9@email.byu.edu + * @since 1.2 + */ + public static class Float extends Arc2D + { + /** The x coordinate of the box bounding the ellipse of this arc. */ + public float x; + + /** The y coordinate of the box bounding the ellipse of this arc. */ + public float y; + + /** The width of the box bounding the ellipse of this arc. */ + public float width; + + /** The height of the box bounding the ellipse of this arc. */ + public float height; + + /** The start angle of this arc, in degrees. */ + public float start; + + /** The extent angle of this arc, in degrees. */ + public float extent; + + /** + * Create a new, open arc at (0,0) with 0 extent. + */ + public Float() + { + super(OPEN); + } + + /** + * Create a new arc of the given type at (0,0) with 0 extent. + * + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Float(int type) + { + super(type); + } + + /** + * Create a new arc with the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Float(float x, float y, float w, float h, + float start, float extent, int type) + { + super(type); + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + } + + /** + * Create a new arc with the given dimensions. + * + * @param r the bounding box + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public Float(Rectangle2D r, float start, float extent, int type) + { + super(type); + x = (float) r.getX(); + y = (float) r.getY(); + width = (float) r.getWidth(); + height = (float) r.getHeight(); + this.start = start; + this.extent = extent; + } + + /** + * Return the x coordinate of the bounding box. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate of the bounding box. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width of the bounding box. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height of the bounding box. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Return the start angle of the arc, in degrees. + * + * @return the value of start + */ + public double getAngleStart() + { + return start; + } + + /** + * Return the extent of the arc, in degrees. + * + * @return the value of extent + */ + public double getAngleExtent() + { + return extent; + } + + /** + * Tests if the arc contains points. + * + * @return true if the arc has no interior + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the arc to the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArc(double x, double y, double w, double h, + double start, double extent, int type) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + this.start = (float) start; + this.extent = (float) extent; + setArcType(type); + } + + /** + * Sets the start angle of the arc. + * + * @param start the new start angle + */ + public void setAngleStart(double start) + { + this.start = (float) start; + } + + /** + * Sets the extent angle of the arc. + * + * @param start the new extent angle + */ + public void setAngleExtent(double extent) + { + this.extent = (float) extent; + } + + /** + * Creates a tight bounding box given dimensions that more precise than + * the bounding box of the ellipse. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + protected Rectangle2D makeBounds(double x, double y, double w, double h) + { + return new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h); + } + } // class Float +} // class Arc2D diff --git a/libjava/java/awt/geom/Area.java b/libjava/java/awt/geom/Area.java new file mode 100644 index 0000000..85bc642 --- /dev/null +++ b/libjava/java/awt/geom/Area.java @@ -0,0 +1,183 @@ +/* Area.java -- represents a shape built by constructive area geometry + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; + +/** + * STUBS ONLY + * XXX Implement and document. + */ +public class Area implements Shape, Cloneable +{ + public Area() + { + } + public Area(Shape s) + { + } + public void add(Area a) + { + // XXX Implement. + throw new Error("not implemented"); + } + public void subtract(Area a) + { + // XXX Implement. + throw new Error("not implemented"); + } + public void intersect(Area a) + { + // XXX Implement. + throw new Error("not implemented"); + } + public void exclusiveOr(Area a) + { + // XXX Implement. + throw new Error("not implemented"); + } + public void reset() + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean isEmpty() + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean isPolygonal() + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean isRectangular() + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean isSingular() + { + // XXX Implement. + throw new Error("not implemented"); + } + public Rectangle2D getBounds2D() + { + // XXX Implement. + throw new Error("not implemented"); + } + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + /** + * Create a new area of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + public boolean equals(Area a) + { + // XXX Implement. + throw new Error("not implemented"); + } + + public void transform(AffineTransform at) + { + // XXX Implement. + throw new Error("not implemented"); + } + public Area createTransformedArea(AffineTransform at) + { + Area a = (Area) clone(); + a.transform(at); + return a; + } + public boolean contains(double x, double y) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + public boolean contains(double x, double y, double w, double h) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public boolean intersects(double x, double y, double w, double h) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + public PathIterator getPathIterator(AffineTransform at) + { + // XXX Implement. + throw new Error("not implemented"); + } + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } +} // class Area diff --git a/libjava/java/awt/geom/CubicCurve2D.java b/libjava/java/awt/geom/CubicCurve2D.java new file mode 100644 index 0000000..2d303c7 --- /dev/null +++ b/libjava/java/awt/geom/CubicCurve2D.java @@ -0,0 +1,519 @@ +/* CubicCurve2D.java -- represents a parameterized cubic curve in 2-D space + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +/** + * STUBS ONLY + * XXX Implement and document. + */ +public abstract class CubicCurve2D implements Shape, Cloneable +{ + protected CubicCurve2D() + { + } + + public abstract double getX1(); + public abstract double getY1(); + public abstract Point2D getP1(); + public abstract double getCtrlX1(); + public abstract double getCtrlY1(); + public abstract Point2D getCtrlP1(); + public abstract double getCtrlX2(); + public abstract double getCtrlY2(); + public abstract Point2D getCtrlP2(); + public abstract double getX2(); + public abstract double getY2(); + public abstract Point2D getP2(); + + public abstract void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2); + public void setCurve(double[] coords, int offset) + { + setCurve(coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++]); + } + public void setCurve(Point2D p1, Point2D c1, Point2D c2, Point2D p2) + { + setCurve(p1.getX(), p1.getY(), c1.getX(), c1.getY(), + c2.getX(), c2.getY(), p2.getX(), p2.getY()); + } + public void setCurve(Point2D[] pts, int offset) + { + setCurve(pts[offset].getX(), pts[offset++].getY(), + pts[offset].getX(), pts[offset++].getY(), + pts[offset].getX(), pts[offset++].getY(), + pts[offset].getX(), pts[offset++].getY()); + } + public void setCurve(CubicCurve2D c) + { + setCurve(c.getX1(), c.getY1(), c.getCtrlX1(), c.getCtrlY1(), + c.getCtrlX2(), c.getCtrlY2(), c.getX2(), c.getY2()); + } + public static double getFlatnessSq(double x1, double y1, double cx1, + double cy1, double cx2, double cy2, + double x2, double y2) + { + // XXX Implement. + throw new Error("not implemented"); + } + public static double getFlatness(double x1, double y1, double cx1, + double cy1, double cx2, double cy2, + double x2, double y2) + { + return Math.sqrt(getFlatnessSq(x1, y1, cx1, cy1, cx2, cy2, x2, y2)); + } + public static double getFlatnessSq(double[] coords, int offset) + { + return getFlatnessSq(coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++]); + } + public static double getFlatness(double[] coords, int offset) + { + return Math.sqrt(getFlatnessSq(coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++])); + } + public double getFlatnessSq() + { + return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), + getCtrlX2(), getCtrlY2(), getX2(), getY2()); + } + public double getFlatness() + { + return Math.sqrt(getFlatnessSq(getX1(), getY1(), getCtrlX1(), + getCtrlY1(), getCtrlX2(), getCtrlY2(), + getX2(), getY2())); + } + + public void subdivide(CubicCurve2D l, CubicCurve2D r) + { + if (l == null) + l = new CubicCurve2D.Double(); + if (r == null) + r = new CubicCurve2D.Double(); + // Use empty slots at end to share single array. + double[] d = new double[] { getX1(), getY1(), getCtrlX1(), getCtrlY1(), + getCtrlX2(), getCtrlY2(), getX2(), getY2(), + 0, 0, 0, 0, 0, 0 }; + subdivide(d, 0, d, 0, d, 6); + l.setCurve(d, 0); + r.setCurve(d, 6); + } + public static void subdivide(CubicCurve2D src, + CubicCurve2D l, CubicCurve2D r) + { + src.subdivide(l, r); + } + public static void subdivide(double[] src, int srcOff, + double[] left, int leftOff, + double[] right, int rightOff) + { + // XXX Implement. + throw new Error("not implemented"); + } + public static int solveCubic(double[] eqn) + { + return solveCubic(eqn, eqn); + } + public static int solveCubic(double[] eqn, double[] res) + { + if (eqn[3] == 0) + return QuadCurve2D.solveQuadratic(eqn, res); + // XXX Implement. + throw new Error("not implemented"); + } + + public boolean contains(double x, double y) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + public boolean intersects(double x, double y, double w, double h) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + public boolean contains(double x, double y, double w, double h) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + /** Current coordinate. */ + private int current; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current < 2; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + if (current == 0) + { + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + if (current == 1) + { + coords[0] = (float) getCtrlX1(); + coords[1] = (float) getCtrlY1(); + coords[2] = (float) getCtrlX2(); + coords[3] = (float) getCtrlY2(); + coords[4] = (float) getX2(); + coords[5] = (float) getY2(); + if (at != null) + at.transform(coords, 0, coords, 0, 3); + return SEG_CUBICTO; + } + throw new NoSuchElementException("cubic iterator out of bounds"); + } + + public int currentSegment(double[] coords) + { + if (current == 0) + { + coords[0] = getX1(); + coords[1] = getY1(); + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + if (current == 1) + { + coords[0] = getCtrlX1(); + coords[1] = getCtrlY1(); + coords[2] = getCtrlX2(); + coords[3] = getCtrlY2(); + coords[4] = getX2(); + coords[5] = getY2(); + if (at != null) + at.transform(coords, 0, coords, 0, 3); + return SEG_CUBICTO; + } + throw new NoSuchElementException("cubic iterator out of bounds"); + } + }; + } + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Create a new curve of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * STUBS ONLY + */ + public static class Double extends CubicCurve2D + { + public double x1; + public double y1; + public double ctrlx1; + public double ctrly1; + public double ctrlx2; + public double ctrly2; + public double x2; + public double y2; + + public Double() + { + } + + public Double(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + public double getX1() + { + return x1; + } + public double getY1() + { + return y1; + } + public Point2D getP1() + { + return new Point2D.Double(x1, y1); + } + + public double getCtrlX1() + { + return ctrlx1; + } + public double getCtrlY1() + { + return ctrly1; + } + public Point2D getCtrlP1() + { + return new Point2D.Double(ctrlx1, ctrly1); + } + + public double getCtrlX2() + { + return ctrlx2; + } + public double getCtrlY2() + { + return ctrly2; + } + public Point2D getCtrlP2() + { + return new Point2D.Double(ctrlx2, ctrly2); + } + + public double getX2() + { + return x2; + } + public double getY2() + { + return y2; + } + public Point2D getP2() + { + return new Point2D.Double(x2, y2); + } + + public void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + public Rectangle2D getBounds2D() + { + double nx1 = Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2)); + double ny1 = Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2)); + double nx2 = Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2)); + double ny2 = Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2)); + return new Rectangle2D.Double(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } // class Double + + /** + * STUBS ONLY + */ + public static class Float extends CubicCurve2D + { + public float x1; + public float y1; + public float ctrlx1; + public float ctrly1; + public float ctrlx2; + public float ctrly2; + public float x2; + public float y2; + + public Float() + { + } + + public Float(float x1, float y1, float cx1, float cy1, + float cx2, float cy2, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + public double getX1() + { + return x1; + } + public double getY1() + { + return y1; + } + public Point2D getP1() + { + return new Point2D.Float(x1, y1); + } + + public double getCtrlX1() + { + return ctrlx1; + } + public double getCtrlY1() + { + return ctrly1; + } + public Point2D getCtrlP1() + { + return new Point2D.Float(ctrlx1, ctrly1); + } + + public double getCtrlX2() + { + return ctrlx2; + } + public double getCtrlY2() + { + return ctrly2; + } + public Point2D getCtrlP2() + { + return new Point2D.Float(ctrlx2, ctrly2); + } + + public double getX2() + { + return x2; + } + public double getY2() + { + return y2; + } + public Point2D getP2() + { + return new Point2D.Float(x2, y2); + } + + public void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2) + { + this.x1 = (float) x1; + this.y1 = (float) y1; + ctrlx1 = (float) cx1; + ctrly1 = (float) cy1; + ctrlx2 = (float) cx2; + ctrly2 = (float) cy2; + this.x2 = (float) x2; + this.y2 = (float) y2; + } + public void setCurve(float x1, float y1, float cx1, float cy1, + float cx2, float cy2, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + public Rectangle2D getBounds2D() + { + float nx1 = (float) Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2)); + float ny1 = (float) Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2)); + float nx2 = (float) Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2)); + float ny2 = (float) Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2)); + return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } // class Float +} // class CubicCurve2D diff --git a/libjava/java/awt/geom/Dimension2D.java b/libjava/java/awt/geom/Dimension2D.java index d9f09f0..e111e54 100644 --- a/libjava/java/awt/geom/Dimension2D.java +++ b/libjava/java/awt/geom/Dimension2D.java @@ -1,4 +1,5 @@ -/* Copyright (C) 1999, 2000, 2002 Free Software Foundation +/* Dimension2D.java -- abstraction of a dimension + Copyright (C) 1999, 2000, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -34,39 +35,79 @@ this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ + package java.awt.geom; /** + * This stores a dimension in 2-dimensional space - a width (along the x-axis) + * and height (along the y-axis). The storage is left to subclasses. + * * @author Per Bothner <bothner@cygnus.com> - * @date February, 1999. - */ - -/* Written using online API docs for JDK 1.2 beta from http://www.javasoft.com. - * Status: Believed complete and correct. + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 */ - public abstract class Dimension2D implements Cloneable { + /** + * The default constructor. + */ + protected Dimension2D() + { + } + + /** + * Get the width of this dimension. A negative result, while legal, is + * undefined in meaning. + * + * @return the width + */ public abstract double getWidth(); + + /** + * Get the height of this dimension. A negative result, while legal, is + * undefined in meaning. + * + * @return the height + */ public abstract double getHeight(); - public abstract void setSize (double width, double height); + /** + * Set the size of this dimension to the requested values. Loss of precision + * may occur. + * + * @param w the new width + * @param h the new height + */ + public abstract void setSize(double w, double h); - public void setSize (Dimension2D dim) + /** + * Set the size of this dimension to the requested value. Loss of precision + * may occur. + * + * @param d the dimension containing the new values + * @throws NullPointerException if d is null + */ + public void setSize(Dimension2D d) { - setSize(dim.getWidth(), dim.getHeight()); + setSize(d.getWidth(), d.getHeight()); } - public Object clone () + /** + * Create a new dimension of the same run-time type with the same contents + * as this one. + * + * @return the clone + */ + public Object clone() { try - { - return super.clone (); - } - catch (CloneNotSupportedException _) {return null;} - } - - protected Dimension2D () - { + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } } -} +} // class Dimension2D diff --git a/libjava/java/awt/geom/Ellipse2D.java b/libjava/java/awt/geom/Ellipse2D.java index 033c0ef..223a193 100644 --- a/libjava/java/awt/geom/Ellipse2D.java +++ b/libjava/java/awt/geom/Ellipse2D.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2002 Free Software Foundation +/* Ellipse2D.java -- represents an ellipse in 2-D space + Copyright (C) 2000, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -38,39 +39,40 @@ package java.awt.geom; /** * @author Tom Tromey <tromey@cygnus.com> - * @date April 16, 2000 + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status still needs documentation */ - public abstract class Ellipse2D extends RectangularShape { - protected Ellipse2D () + protected Ellipse2D() { } - public boolean contains (double x, double y) + public boolean contains(double x, double y) { - double rx = getWidth () / 2; - double ry = getHeight () / 2; - double tx = (x - getCenterX ()) / rx; - double ty = (y - getCenterY ()) / ry; + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double tx = (x - getCenterX()) / rx; + double ty = (y - getCenterY()) / ry; return tx * tx + ty * ty <= 1.0; } - public boolean contains (double x, double y, double w, double h) + public boolean contains(double x, double y, double w, double h) { double x2 = x + w; double y2 = y + h; - return (contains (x, y) && contains (x, y2) - && contains (x2, y) && contains (x2, y2)); + return (contains(x, y) && contains(x, y2) + && contains(x2, y) && contains(x2, y2)); } - public PathIterator getPathIterator (AffineTransform at) + public PathIterator getPathIterator(AffineTransform at) { - // fixme; - return null; + // An ellipse is just a complete arc. + return new Arc2D.ArcIterator(this, at); } - public boolean intersects (double x, double y, double w, double h) + public boolean intersects(double x, double y, double w, double h) { // fixme return false; @@ -83,57 +85,56 @@ public abstract class Ellipse2D extends RectangularShape public double x; public double y; - public Double () + public Double() { - height = width = x = y = 0; } - public Double (double x, double y, double w, double h) + public Double(double x, double y, double w, double h) { this.x = x; this.y = y; - this.height = h; - this.width = w; + height = h; + width = w; } - public Rectangle2D getBounds2D () + public Rectangle2D getBounds2D() { - return new Rectangle2D.Double (x, y, width, height); + return new Rectangle2D.Double(x, y, width, height); } - public double getHeight () + public double getHeight() { return height; } - public double getWidth () + public double getWidth() { return width; } - public double getX () + public double getX() { return x; } - public double getY () + public double getY() { return y; } - public boolean isEmpty () + public boolean isEmpty() { return height <= 0 || width <= 0; } - public void setFrame (double x, double y, double w, double h) + public void setFrame(double x, double y, double w, double h) { this.x = x; this.y = y; - this.height = h; - this.width = w; + height = h; + width = w; } - } + } // class Double public static class Float extends Ellipse2D { @@ -142,12 +143,11 @@ public abstract class Ellipse2D extends RectangularShape public float x; public float y; - public Float () + public Float() { - height = width = x = y = 0; } - public Float (float x, float y, float w, float h) + public Float(float x, float y, float w, float h) { this.x = x; this.y = y; @@ -155,50 +155,50 @@ public abstract class Ellipse2D extends RectangularShape this.width = w; } - public Rectangle2D getBounds2D () + public Rectangle2D getBounds2D() { - return new Rectangle2D.Float (x, y, width, height); + return new Rectangle2D.Float(x, y, width, height); } - public double getHeight () + public double getHeight() { return height; } - public double getWidth () + public double getWidth() { return width; } - public double getX () + public double getX() { return x; } - public double getY () + public double getY() { return y; } - public boolean isEmpty () + public boolean isEmpty() { return height <= 0 || width <= 0; } - public void setFrame (float x, float y, float w, float h) + public void setFrame(float x, float y, float w, float h) { this.x = x; this.y = y; - this.height = h; - this.width = w; + height = h; + width = w; } - public void setFrame (double x, double y, double w, double h) + public void setFrame(double x, double y, double w, double h) { this.x = (float) x; this.y = (float) y; - this.height = (float) h; - this.width = (float) w; + height = (float) h; + width = (float) w; } - } -} + } // class Float +} // class Ellipse2D diff --git a/libjava/java/awt/geom/FlatteningPathIterator.java b/libjava/java/awt/geom/FlatteningPathIterator.java new file mode 100644 index 0000000..a7a57ef --- /dev/null +++ b/libjava/java/awt/geom/FlatteningPathIterator.java @@ -0,0 +1,105 @@ +/* FlatteningPathIterator.java -- performs interpolation of curved paths + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +/** + * This class can be used to perform the flattening required by the Shape + * interface. It interpolates a curved path segment into a sequence of flat + * ones within a certain flatness, up to a recursion limit. + * + * @author Eric Blake <ebb9@email.byu.edu> + * @see Shape + * @see RectangularShape#getPathIterator(AffineTransform, double) + * @since 1.2 + * @status STUBS ONLY + */ +public class FlatteningPathIterator implements PathIterator +{ + // The iterator we are applied to. + private PathIterator subIterator; + private double flatness; + private int limit; + + public FlatteningPathIterator(PathIterator src, double flatness) + { + this(src, flatness, 10); + } + public FlatteningPathIterator(PathIterator src, double flatness, int limit) + { + subIterator = src; + this.flatness = flatness; + this.limit = limit; + if (flatness < 0 || limit < 0) + throw new IllegalArgumentException(); + } + + public double getFlatness() + { + return flatness; + } + + public int getRecursionLimit() + { + return limit; + } + + public int getWindingRule() + { + return subIterator.getWindingRule(); + } + + public boolean isDone() + { + return subIterator.isDone(); + } + + public void next() + { + throw new Error("not implemented"); + } + + public int currentSegment(double[] coords) + { + throw new Error("not implemented"); + } + public int currentSegment(float[] coords) + { + throw new Error("not implemented"); + } +} // class FlatteningPathIterator diff --git a/libjava/java/awt/geom/GeneralPath.java b/libjava/java/awt/geom/GeneralPath.java new file mode 100644 index 0000000..267db6c --- /dev/null +++ b/libjava/java/awt/geom/GeneralPath.java @@ -0,0 +1,361 @@ +/* GeneralPath.java -- represents a shape built from subpaths + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; + +/** + * STUBS ONLY + * XXX Implement and document. Note that Sun's implementation only expects + * float precision, not double. + */ +public final class GeneralPath implements Shape, Cloneable +{ + public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; + public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; + + /** Initial size if not specified. */ + private static final int INIT_SIZE = 20; + + /** The winding rule. */ + private int rule; + /** + * The path type in points. Note that points[index] maps to + * types[index >> 1]; the control points of quad and cubic paths map as + * well but are ignored. + */ + private byte[] types; + /** + * The list of all points seen. Since you can only append floats, it makes + * sense for this to be a float[]. I have no idea why Sun didn't choose to + * allow a general path of double precision points. + */ + private float[] points; + /** The index of the most recent moveto point, or null. */ + private int subpath = -1; + /** The next available index into points. */ + private int index; + + public GeneralPath() + { + this(WIND_NON_ZERO, INIT_SIZE); + } + public GeneralPath(int rule) + { + this(rule, INIT_SIZE); + } + public GeneralPath(int rule, int capacity) + { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) + throw new IllegalArgumentException(); + this.rule = rule; + if (capacity < INIT_SIZE) + capacity = INIT_SIZE; + types = new byte[capacity >> 1]; + points = new float[capacity]; + } + public GeneralPath(Shape s) + { + types = new byte[INIT_SIZE >> 1]; + points = new float[INIT_SIZE]; + PathIterator pi = s.getPathIterator(null); + setWindingRule(pi.getWindingRule()); + append(pi, false); + } + + public void moveTo(float x, float y) + { + subpath = index; + ensureSize(index + 2); + types[index >> 1] = PathIterator.SEG_MOVETO; + points[index++] = x; + points[index++] = y; + } + public void lineTo(float x, float y) + { + ensureSize(index + 2); + types[index >> 1] = PathIterator.SEG_LINETO; + points[index++] = x; + points[index++] = y; + } + public void quadTo(float x1, float y1, float x2, float y2) + { + ensureSize(index + 4); + types[index >> 1] = PathIterator.SEG_QUADTO; + points[index++] = x1; + points[index++] = y1; + points[index++] = x2; + points[index++] = y2; + } + public void curveTo(float x1, float y1, float x2, float y2, + float x3, float y3) + { + ensureSize(index + 6); + types[index >> 1] = PathIterator.SEG_QUADTO; + points[index++] = x1; + points[index++] = y1; + points[index++] = x2; + points[index++] = y2; + points[index++] = x3; + points[index++] = y3; + } + public void closePath() + { + ensureSize(index + 2); + types[index >> 1] = PathIterator.SEG_CLOSE; + points[index++] = points[subpath]; + points[index++] = points[subpath + 1]; + } + + public void append(Shape s, boolean connect) + { + append(s.getPathIterator(null), connect); + } + public void append(PathIterator i, boolean connect) + { + float[] f = new float[6]; + while (! i.isDone()) + { + int result = i.currentSegment(f); + switch (result) + { + case PathIterator.SEG_MOVETO: + if (! connect) + { + moveTo(f[0], f[1]); + break; + } + if (subpath >= 0 && f[0] == points[subpath] + && f[1] == points[subpath + 1]) + break; + // Fallthrough. + case PathIterator.SEG_LINETO: + lineTo(f[0], f[1]); + break; + case PathIterator.SEG_QUADTO: + quadTo(f[0], f[1], f[2], f[3]); + break; + case PathIterator.SEG_CUBICTO: + curveTo(f[0], f[1], f[2], f[3], f[4], f[5]); + break; + default: + closePath(); + } + connect = false; + } + } + + public int getWindingRule() + { + return rule; + } + public void setWindingRule(int rule) + { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) + throw new IllegalArgumentException(); + this.rule = rule; + } + + public Point2D getCurrentPoint() + { + if (subpath < 0) + return null; + return new Point2D.Float(points[subpath], points[subpath + 1]); + } + public void reset() + { + subpath = -1; + index = 0; + } + + public void transform(AffineTransform xform) + { + xform.transform(points, 0, points, 0, index >> 1); + } + public Shape createTransformedShape(AffineTransform xform) + { + GeneralPath p = new GeneralPath(this); + p.transform(xform); + return p; + } + + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + public Rectangle2D getBounds2D() + { + // XXX Implement. + throw new Error("not implemented"); + } + + public boolean contains(double x, double y) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + public boolean contains(double x, double y, double w, double h) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public boolean intersects(double x, double y, double w, double h) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + int current = 0; + + public int getWindingRule() + { + return rule; + } + + public boolean isDone() + { + return current >= index; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + if (current >= index) + return SEG_CLOSE; + int result = types[current >> 1]; + int i = 0; + if (result == 3) + { + coords[i++] = points[current++]; + coords[i++] = points[current++]; + } + if (result == 2) + { + coords[i++] = points[current++]; + coords[i++] = points[current++]; + } + if (result < 2) + { + coords[i++] = points[current++]; + coords[i++] = points[current++]; + if (at != null) + at.transform(coords, 0, coords, 0, result == 0 ? 1 : result); + } + return result; + } + + public int currentSegment(double[] coords) + { + if (current >= index) + return SEG_CLOSE; + int result = types[current >> 1]; + int i = 0; + if (result == 3) + { + coords[i++] = points[current++]; + coords[i++] = points[current++]; + } + if (result == 2) + { + coords[i++] = points[current++]; + coords[i++] = points[current++]; + } + if (result < 2) + { + coords[i++] = points[current++]; + coords[i++] = points[current++]; + if (at != null) + at.transform(coords, 0, coords, 0, result == 0 ? 1 : result); + } + return result; + } + }; + } + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Create a new shape of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + // This class is final; no need to use super.clone(). + return new GeneralPath(this); + } + + private void ensureSize(int size) + { + if (subpath < 0) + throw new IllegalPathStateException("need initial moveto"); + if (size <= points.length) + return; + byte[] b = new byte[points.length]; + System.arraycopy(types, 0, b, 0, index >> 1); + types = b; + float[] f = new float[points.length << 1]; + System.arraycopy(points, 0, f, 0, index); + points = f; + } +} // class GeneralPath diff --git a/libjava/java/awt/geom/IllegalPathStateException.java b/libjava/java/awt/geom/IllegalPathStateException.java index 3e36b08..f0e388a 100644 --- a/libjava/java/awt/geom/IllegalPathStateException.java +++ b/libjava/java/awt/geom/IllegalPathStateException.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2002 Free Software Foundation +/* IllegalPathStateException.java -- an operation was in an illegal path state + Copyright (C) 2000, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -37,19 +38,34 @@ exception statement from your version. */ package java.awt.geom; /** + * Thrown when an operation on a path is in an illegal state, such as appending + * a segment to a <code>GeneralPath</code> without an initial moveto. + * * @author Tom Tromey <tromey@cygnus.com> - * @date July 17, 2000 + * @see GeneralPath + * @status updated to 1.4 */ - public class IllegalPathStateException extends RuntimeException { - public IllegalPathStateException () + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -5158084205220481094L; + + /** + * Create an exception with no message. + */ + public IllegalPathStateException() { - super (); } - public IllegalPathStateException (String msg) + /** + * Create an exception with a message. + * + * @param msg the message + */ + public IllegalPathStateException(String msg) { - super (msg); + super(msg); } } diff --git a/libjava/java/awt/geom/Line2D.java b/libjava/java/awt/geom/Line2D.java index 608b566..9e55110 100644 --- a/libjava/java/awt/geom/Line2D.java +++ b/libjava/java/awt/geom/Line2D.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2001, 2002 Free Software Foundation +/* Line2D.java -- represents a line in 2-D space, plus operations on a line + Copyright (C) 2000, 2001, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -38,325 +39,769 @@ package java.awt.geom; import java.awt.Rectangle; import java.awt.Shape; +import java.util.NoSuchElementException; /** + * Represents a directed line bewteen two points in (x,y) Cartesian space. + * Remember, on-screen graphics have increasing x from left-to-right, and + * increasing y from top-to-bottom. The storage is left to subclasses. + * * @author Tom Tromey <tromey@cygnus.com> - * @date April 21, 2001 + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 */ - public abstract class Line2D implements Shape, Cloneable { - protected Line2D () + /** + * The default constructor. + */ + protected Line2D() { } - public Object clone () + /** + * Return the x coordinate of the first point. + * + * @return the starting x coordinate + */ + public abstract double getX1(); + + /** + * Return the y coordinate of the first point. + * + * @return the starting y coordinate + */ + public abstract double getY1(); + + /** + * Return the first point. + * + * @return the starting point + */ + public abstract Point2D getP1(); + + /** + * Return the x coordinate of the second point. + * + * @return the ending x coordinate + */ + public abstract double getX2(); + + /** + * Return the y coordinate of the second point. + * + * @return the ending y coordinate + */ + public abstract double getY2(); + + /** + * Return the second point. + * + * @return the ending point + */ + public abstract Point2D getP2(); + + /** + * Set the coordinates of the line to the given coordinates. Loss of + * precision may occur due to rounding issues. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + */ + public abstract void setLine(double x1, double y1, double x2, double y2); + + /** + * Set the coordinates to the given points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public void setLine(Point2D p1, Point2D p2) { - try - { - return super.clone (); - } - catch (CloneNotSupportedException _) - { - // Can't happen. - return null; - } + setLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()); } - public boolean contains (double x, double y) + /** + * Set the coordinates to those of the given line. + * + * @param l the line to copy + * @throws NullPointerException if l is null + */ + public void setLine(Line2D l) { - double x1 = getX1 (); - double t1 = (x - x1) / (getX2 () - x1); - if (t1 < 0 || t1 > 1) - return false; - double y1 = getY1 (); - double t2 = (y - y1) / (getY2 () - y1); - // FIXME: use of == here is bogus - return t2 >= 0 && t2 <= 1 && t1 == t2; + setLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); } - public boolean contains (double x, double y, double w, double h) + /** + * Computes the relative rotation direction needed to pivot the line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + * @param px the reference x coordinate + * @param py the reference y coordinate + * @return the relative rotation direction + */ + public static int relativeCCW(double x1, double y1, double x2, double y2, + double px, double py) { - return false; + if ((x1 == x2 && y1 == y2) + || (x1 == px && y1 == py)) + return 0; // Coincident points. + // Translate to the origin. + x2 -= x1; + y2 -= y1; + px -= x1; + py -= y1; + double slope2 = y2 / x2; + double slopep = py / px; + if (slope2 == slopep || (x2 == 0 && px == 0)) + return y2 > 0 // Colinear. + ? (py < 0 ? -1 : py > y2 ? 1 : 0) + : (py > 0 ? -1 : py < y2 ? 1 : 0); + if (x2 >= 0 && slope2 >= 0) + return px >= 0 // Quadrant 1. + ? (slope2 > slopep ? 1 : -1) + : (slope2 < slopep ? 1 : -1); + if (y2 > 0) + return px < 0 // Quadrant 2. + ? (slope2 > slopep ? 1 : -1) + : (slope2 < slopep ? 1 : -1); + if (slope2 >= 0.0) + return px >= 0 // Quadrant 3. + ? (slope2 < slopep ? 1 : -1) + : (slope2 > slopep ? 1 : -1); + return px < 0 // Quadrant 4. + ? (slope2 < slopep ? 1 : -1) + : (slope2 > slopep ? 1 : -1); } - public boolean contains (Point2D p) + /** + * Computes the relative rotation direction needed to pivot this line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param px the reference x coordinate + * @param py the reference y coordinate + * @return the relative rotation direction + * @see #relativeCCW(double, double, double, double, double, double) + */ + public int relativeCCW(double px, double py) { - return contains (p.getX (), p.getY ()); + return relativeCCW(getX1(), getY1(), getX2(), getY2(), px, py); } - public boolean contains (Rectangle2D r) + /** + * Computes the relative rotation direction needed to pivot this line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param p the reference point + * @return the relative rotation direction + * @throws NullPointerException if p is null + * @see #relativeCCW(double, double, double, double, double, double) + */ + public int relativeCCW(Point2D p) { - return false; + return relativeCCW(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); } - public Rectangle getBounds () + /** + * Test if the line segment (x1,y1)->(x2,y2) intersects the line segment + * (x3,y3)->(x4,y4). + * + * @param x1 the first x coordinate of the first segment + * @param y1 the first y coordinate of the first segment + * @param x2 the second x coordinate of the first segment + * @param y2 the second y coordinate of the first segment + * @param x3 the first x coordinate of the second segment + * @param y3 the first y coordinate of the second segment + * @param x4 the second x coordinate of the second segment + * @param y4 the second y coordinate of the second segment + * @return true if the segments intersect + */ + public static boolean linesIntersect(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) { - double x1 = getX1 (); - double y1 = getY1 (); - double x2 = getX2 (); - double y2 = getY2 (); - - double x = Math.min (x1, x2); - double y = Math.min (y1, y2); - double w = Math.abs (x1 - x2); - double h = Math.abs (y1 - y2); - - return new Rectangle ((int) x, (int) y, (int) w, (int) h); + double beta = (((y1 - y3) * (x4 - x3) + (x1 - x3) * (y4 - y3)) + / ((y2 - y1) * (x4 - x3) + (x2 - x1) * (y4 - y3))); + if (beta < 0.0 || beta > 1.0) + return false; + double alpha = (x1 + beta * (x2 - x1) - x3) / (x4 - x3); + return alpha >= 0.0 && alpha <= 1.0; } - public abstract Point2D getP1 (); - public abstract Point2D getP2 (); - - public PathIterator getPathIterator (AffineTransform at) + /** + * Test if this line intersects the line given by (x1,y1)->(x2,y2). + * + * @param x1 the first x coordinate of the other segment + * @param y1 the first y coordinate of the other segment + * @param x2 the second x coordinate of the other segment + * @param y2 the second y coordinate of the other segment + * @return true if the segments intersect + * @see #linesIntersect(double, double, double, double, + * double, double, double, double) + */ + public boolean intersectsLine(double x1, double y1, double x2, double y2) { - return getPathIterator (at, 0); + return linesIntersect(getX1(), getY1(), getX2(), getY2(), + x1, y1, x2, y2); } - public PathIterator getPathIterator (AffineTransform at, double flatness) + /** + * Test if this line intersects the given line. + * + * @param l the other segment + * @return true if the segments intersect + * @throws NullPointerException if l is null + * @see #linesIntersect(double, double, double, double, + * double, double, double, double) + */ + public boolean intersectsLine(Line2D l) { - return at.new Iterator (new Iterator ()); + return linesIntersect(getX1(), getY1(), getX2(), getY2(), + l.getX1(), l.getY1(), l.getX2(), l.getY2()); } - public abstract double getX1 (); - public abstract double getY1 (); - public abstract double getX2 (); - public abstract double getY2 (); - - public boolean intersects (double x, double y, double w, double h) + /** + * Measures the square of the shortest distance from the reference point + * to a point on the line segment. If the point is on the segment, the + * result will be 0. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the segment + * @see #ptSegDist(double, double, double, double, double, double) + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public static double ptSegDistSq(double x1, double y1, double x2, double y2, + double px, double py) { - double x1 = getX1 (); - double y1 = getY1 (); - double x2 = getX2 (); - double y2 = getY2 (); - - if (x1 >= x && x1 <= x + w && y1 >= y && y1 <= y +h) - return true; - if (x2 >= x && x2 <= x + w && y2 >= y && y2 <= y +h) - return true; - - double x3 = x + w; - double y3 = y + h; - - return (linesIntersect (x1, y1, x2, y2, x, y, x, y3) - || linesIntersect (x1, y1, x2, y2, x, y3, x3, y3) - || linesIntersect (x1, y1, x2, y2, x3, y3, x3, y) - || linesIntersect (x1, y1, x2, y2, x3, y, x, y)); - } - - public boolean intersects (Rectangle2D r) - { - return intersects (r.getX (), r.getY (), r.getWidth (), r.getHeight ()); - } + double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); - public boolean intersectsLine (double x1, double y1, double x2, double y2) - { - return linesIntersect (getX1 (), getY1 (), getX2 (), getY2(), - x1, y1, x2, y2); - } + double x, y; + if (pd2 == 0) + { + // Points are coincident. + x = x1; + y = y2; + } + else + { + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; + + if (u < 0) + { + // "Off the end" + x = x1; + y = y1; + } + else if (u > 1.0) + { + x = x2; + y = y2; + } + else + { + x = x1 + u * (x2 - x1); + y = y1 + u * (y2 - y1); + } + } - public boolean intersectsLine (Line2D l) - { - return linesIntersect (getX1 (), getY1 (), getX2 (), getY2(), - l.getX1 (), l.getY1 (), l.getX2 (), l.getY2 ()); + return (x - px) * (x - px) + (y - py) * (y - py); } - public static boolean linesIntersect (double x1, double y1, - double x2, double y2, - double x3,double y3, - double x4, double y4) + /** + * Measures the shortest distance from the reference point to a point on + * the line segment. If the point is on the segment, the result will be 0. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the segment + * @see #ptSegDistSq(double, double, double, double, double, double) + * @see #ptLineDist(double, double, double, double, double, double) + */ + public static double ptSegDist(double x1, double y1, double x2, double y2, + double px, double py) { - double beta = (((y1 - y3) * (x4 - x3) + (x1 - x3) * (y4 - y3)) - / ((y2 - y1) * (x4 - x3) + (x2 - x1) * (y4 - y3))); - if (beta < 0.0 || beta > 1.0) - return false; - double alpha = (x1 + beta * (x2 - x1) - x3) / (x4 - x3); - return alpha >= 0.0 && alpha <= 1.0; + return Math.sqrt(ptSegDistSq(x1, y1, x2, y2, px, py)); } - public double ptLineDist (double px, double py) + /** + * Measures the square of the shortest distance from the reference point + * to a point on this line segment. If the point is on the segment, the + * result will be 0. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the segment + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public double ptSegDistSq(double px, double py) { - return ptLineDist (getX1 (), getY1 (), getX2 (), getY2 (), - px, py); + return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), px, py); } - public static double ptLineDist (double x1, double y1, - double x2, double y2, - double px, double py) + /** + * Measures the square of the shortest distance from the reference point + * to a point on this line segment. If the point is on the segment, the + * result will be 0. + * + * @param p the point + * @return the square of the distance from the point to the segment + * @throws NullPointerException if p is null + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public double ptSegDistSq(Point2D p) { - return Math.sqrt (ptLineDistSq (x1, y1, x2, y2, px, py)); + return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); } - public double ptLineDist (Point2D p) + /** + * Measures the shortest distance from the reference point to a point on + * this line segment. If the point is on the segment, the result will be 0. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the segment + * @see #ptSegDist(double, double, double, double, double, double) + */ + public double ptSegDist(double px, double py) { - return ptLineDist (getX1 (), getY1 (), getX2 (), getY2 (), - p.getX (), p.getY ()); + return ptSegDist(getX1(), getY1(), getX2(), getY2(), px, py); } - public double ptLineDistSq (double px, double py) + /** + * Measures the shortest distance from the reference point to a point on + * this line segment. If the point is on the segment, the result will be 0. + * + * @param p the point + * @return the distance from the point to the segment + * @throws NullPointerException if p is null + * @see #ptSegDist(double, double, double, double, double, double) + */ + public double ptSegDist(Point2D p) { - return ptLineDistSq (getX1 (), getY1 (), getX2 (), getY2 (), - px, py); + return ptSegDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); } - public static double ptLineDistSq (double x1, double y1, - double x2, double y2, - double px, double py) + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from the segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the extended line + * @see #ptLineDist(double, double, double, double, double, double) + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public static double ptLineDistSq(double x1, double y1, double x2, double y2, + double px, double py) { double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); double x, y; if (pd2 == 0) { - // Points are coincident. - x = x1; - y = y2; + // Points are coincident. + x = x1; + y = y2; } else { - double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; - x = x1 + u * (x2 - x1); - y = y1 + u * (y2 - y1); + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; + x = x1 + u * (x2 - x1); + y = y1 + u * (y2 - y1); } return (x - px) * (x - px) + (y - py) * (y - py); } - public double ptLineDistSq (Point2D p) + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from the segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the extended line + * @see #ptLineDistSq(double, double, double, double, double, double) + * @see #ptSegDist(double, double, double, double, double, double) + */ + public static double ptLineDist(double x1, double y1, + double x2, double y2, + double px, double py) { - return ptLineDistSq (getX1 (), getY1 (), getX2 (), getY2 (), - p.getX (), p.getY ()); + return Math.sqrt(ptLineDistSq(x1, y1, x2, y2, px, py)); } - public double ptSegDist (double px, double py) + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from this segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the extended line + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public double ptLineDistSq(double px, double py) { - return ptSegDist (getX1 (), getY1 (), getX2 (), getY2 (), - px, py); + return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), px, py); } - public static double ptSegDist (double x1, double y1, - double x2, double y2, - double px, double py) + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from this segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param p the point + * @return the square of the distance from the point to the extended line + * @throws NullPointerException if p is null + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public double ptLineDistSq(Point2D p) { - return Math.sqrt (ptSegDistSq (x1, y1, x2, y2, px, py)); + return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), + p.getX(), p.getY()); } - public double ptSegDist (Point2D p) + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from this segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the extended line + * @see #ptLineDist(double, double, double, double, double, double) + */ + public double ptLineDist(double px, double py) { - return ptSegDist (getX1 (), getY1 (), getX2 (), getY2 (), - p.getX (), p.getY ()); + return ptLineDist(getX1(), getY1(), getX2(), getY2(), px, py); } - public double ptSegDistSq (double px, double py) + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from this segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param p the point + * @return the distance from the point to the extended line + * @throws NullPointerException if p is null + * @see #ptLineDist(double, double, double, double, double, double) + */ + public double ptLineDist(Point2D p) { - return ptSegDistSq (getX1 (), getY1 (), getX2 (), getY2 (), - px, py); + return ptLineDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); } - public static double ptSegDistSq (double x1, double y1, - double x2, double y2, - double px, double py) + /** + * Test if a point is contained inside the line. Since a line has no area, + * this returns false. + * + * @param x the x coordinate + * @param y the y coordinate + * @return false; the line does not contain points + */ + public boolean contains(double x, double y) { - double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + return false; + } - double x, y; - if (pd2 == 0) - { - // Points are coincident. - x = x1; - y = y2; - } - else - { - double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; - - if (u < 0) - { - // "Off the end" - x = x1; - y = y1; - } - else if (u > 1.0) - { - x = x2; - y = y2; - } - else - { - x = x1 + u * (x2 - x1); - y = y1 + u * (y2 - y1); - } - } + /** + * Test if a point is contained inside the line. Since a line has no area, + * this returns false. + * + * @param p the point + * @return false; the line does not contain points + */ + public boolean contains(Point2D p) + { + return false; + } - return (x - px) * (x - px) + (y - py) * (y - py); + /** + * Tests if this line intersects the interior of the specified rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the line intersects the rectangle + */ + public boolean intersects(double x, double y, double w, double h) + { + if (w <= 0 || h <= 0) + return false; + double x1 = getX1(); + double y1 = getY1(); + double x2 = getX2(); + double y2 = getY2(); + + if (x1 >= x && x1 <= x + w && y1 >= y && y1 <= y + h) + return true; + if (x2 >= x && x2 <= x + w && y2 >= y && y2 <= y + h) + return true; + + double x3 = x + w; + double y3 = y + h; + + return (linesIntersect(x1, y1, x2, y2, x, y, x, y3) + || linesIntersect(x1, y1, x2, y2, x, y3, x3, y3) + || linesIntersect(x1, y1, x2, y2, x3, y3, x3, y) + || linesIntersect(x1, y1, x2, y2, x3, y, x, y)); } - public double ptSegDistSq (Point2D p) + /** + * Tests if this line intersects the interior of the specified rectangle. + * + * @param r the rectangle + * @return true if the line intersects the rectangle + * @throws NullPointerException if r is null + */ + public boolean intersects(Rectangle2D r) { - return ptSegDistSq (getX1 (), getY1 (), getX2 (), getY2 (), - p.getX (), p.getY ()); + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Tests if the line contains a rectangle. Since lines have no area, this + * always returns false. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return false; the line does not contain points + */ + public boolean contains(double x, double y, double w, double h) + { + return false; + } + + /** + * Tests if the line contains a rectangle. Since lines have no area, this + * always returns false. + * + * @param r the rectangle + * @return false; the line does not contain points + */ + public boolean contains(Rectangle2D r) + { + return false; } - public int relativeCCW (double px, double py) + /** + * Gets a bounding box (not necessarily minimal) for this line. + * + * @return the integer bounding box + * @see #getBounds2D() + */ + public Rectangle getBounds() { - return relativeCCW (getX1 (), getY1 (), - getX2 (), getY2 (), - px, py); + return getBounds2D().getBounds(); } - public static int relativeCCW (double x1, double y1, - double x2, double y2, - double px, double py) + /** + * Return a path iterator, possibly applying a transform on the result. This + * iterator is not threadsafe. + * + * @param at the transform, or null + * @return a new path iterator + */ + public PathIterator getPathIterator(final AffineTransform at) { - // This is a somewhat silly way to compute this. - // Please write a better one. - double a1 = Math.atan2 (y2 - y1, x2 - x1); - double a2 = Math.atan2 (py - y1, px - x1); + return new PathIterator() + { + /** Current coordinate. */ + private int current; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current < 2; + } + + public void next() + { + current++; + } - double a = (a1 - a2) % (2 * Math.PI); - if (a == 0 || a == Math.PI) + public int currentSegment(float[] coords) { - double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)); - if (u < 0.0) - return 1; - else if (u > 1.0) - return -1; - else - return 0; + int result; + switch (current) + { + case 0: + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = (float) getX2(); + coords[1] = (float) getY2(); + result = SEG_LINETO; + break; + default: + throw new NoSuchElementException("line iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return result; } - return (a > 0 && a < Math.PI) ? 1 : -1; + public int currentSegment(double[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = getX1(); + coords[1] = getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = getX2(); + coords[1] = getY2(); + result = SEG_LINETO; + break; + default: + throw new NoSuchElementException("line iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return result; + } + }; } - public int relativeCCW (Point2D p) + /** + * Return a flat path iterator, possibly applying a transform on the result. + * This iterator is not threadsafe. + * + * @param at the transform, or null + * @param flatness ignored, since lines are already flat + * @return a new path iterator + * @see #getPathIterator(AffineTransform) + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) { - return relativeCCW (getX1 (), getY1 (), - getX2 (), getY2 (), - p.getX (), p.getY ()); + return getPathIterator(at); } - public abstract void setLine (double x1, double y1, double x2, double y2); - - public void setLine (Line2D l) + /** + * Create a new line of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() { - setLine (l.getX1 (), l.getY1 (), l.getX2 (), l.getY2 ()); + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } } - public void setLine (Point2D p1, Point2D p2) + /** + * This class defines a point in <code>double</code> precision. + * + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 + */ + public static class Double extends Line2D { - setLine (p1.getX (), p1.getY (), p2.getX (), p2.getY ()); - } + /** The x coordinate of the first point. */ + public double x1; - public static class Float extends Line2D - { - float x1, y1, x2, y2; + /** The y coordinate of the first point. */ + public double y1; + + /** The x coordinate of the second point. */ + public double x2; + + /** The y coordinate of the second point. */ + public double y2; - public Float () + /** + * Construct the line segment (0,0)->(0,0). + */ + public Double() { - this (0.0F, 0.0F, 0.0F, 0.0F); } - public Float (float x1, float y1, float x2, float y2) + /** + * Construct the line segment with the specified points. + * + * @param x1 the x coordinate of the first point + * @param y1 the y coordinate of the first point + * @param x2 the x coordinate of the second point + * @param y2 the y coordinate of the second point + */ + public Double(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; @@ -364,80 +809,149 @@ public abstract class Line2D implements Shape, Cloneable this.y2 = y2; } - public Float (Point2D p1, Point2D p2) - { - this.x1 = (float) p1.getX (); - this.y1 = (float) p1.getY (); - this.x2 = (float) p2.getX (); - this.y2 = (float) p2.getY (); - } - - public Rectangle2D getBounds2D () + /** + * Construct the line segment with the specified points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public Double(Point2D p1, Point2D p2) { - float x = Math.min (x1, x2); - float w = Math.abs (x1 - x2); - float y = Math.min (y1, y2); - float h = Math.abs (y1 - y2); - return new Rectangle2D.Float (x, y, w, h); + x1 = p1.getX(); + y1 = p1.getY(); + x2 = p2.getX(); + y2 = p2.getY(); } - public Point2D getP1 () + /** + * Return the x coordinate of the first point. + * + * @return the value of x1 + */ + public double getX1() { - return new Point2D.Float (x1, y1); - } - - public Point2D getP2 () - { - return new Point2D.Float (x2, y2); + return x1; } - public double getX1 () + /** + * Return the y coordinate of the first point. + * + * @return the value of y1 + */ + public double getY1() { - return x1; + return y1; } - public double getY1 () + /** + * Return the first point. + * + * @return the point (x1,y1) + */ + public Point2D getP1() { - return y1; + return new Point2D.Double(x1, y1); } - public double getX2 () + /** + * Return the x coordinate of the second point. + * + * @return the value of x2 + */ + public double getX2() { return x2; } - public double getY2 () + /** + * Return the y coordinate of the second point. + * + * @return the value of y2 + */ + public double getY2() { return y2; } - public void setLine (double x1, double y1, double x2, double y2) + /** + * Return the second point. + * + * @return the point (x2,y2) + */ + public Point2D getP2() { - this.x1 = (float) x1; - this.y1 = (float) y1; - this.x2 = (float) x2; - this.y2 = (float) y2; + return new Point2D.Double(x2, y2); } - public void setLine (float x1, float y1, float x2, float y2) + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } - } - public static class Double extends Line2D + /** + * Return the exact bounds of this line segment. + * + * @return the bounding box + */ + public Rectangle2D getBounds2D() + { + double x = Math.min(x1, x2); + double y = Math.min(y1, y2); + double w = Math.abs(x1 - x2); + double h = Math.abs(y1 - y2); + return new Rectangle2D.Double(x, y, w, h); + } + } // class Double + + /** + * This class defines a point in <code>float</code> precision. + * + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 + */ + public static class Float extends Line2D { - double x1, y1, x2, y2; + /** The x coordinate of the first point. */ + public float x1; + + /** The y coordinate of the first point. */ + public float y1; + + /** The x coordinate of the second point. */ + public float x2; - public Double () + /** The y coordinate of the second point. */ + public float y2; + + /** + * Construct the line segment (0,0)->(0,0). + */ + public Float() { - this (0.0, 0.0, 0.0, 0.0); } - public Double (double x1, double y1, double x2, double y2) + /** + * Construct the line segment with the specified points. + * + * @param x1 the x coordinate of the first point + * @param y1 the y coordinate of the first point + * @param x2 the x coordinate of the second point + * @param y2 the y coordinate of the second point + */ + public Float(float x1, float y1, float x2, float y2) { this.x1 = x1; this.y1 = y1; @@ -445,129 +959,125 @@ public abstract class Line2D implements Shape, Cloneable this.y2 = y2; } - public Double (Point2D p1, Point2D p2) + /** + * Construct the line segment with the specified points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public Float(Point2D p1, Point2D p2) { - this.x1 = (double) p1.getX (); - this.y1 = p1.getY (); - this.x2 = p2.getX (); - this.y2 = p2.getY (); + x1 = (float) p1.getX(); + y1 = (float) p1.getY(); + x2 = (float) p2.getX(); + y2 = (float) p2.getY(); } - public Rectangle2D getBounds2D () + /** + * Return the x coordinate of the first point. + * + * @return the value of x1 + */ + public double getX1() { - double x = Math.min (x1, x2); - double w = Math.abs (x1 - x2); - double y = Math.min (y1, y2); - double h = Math.abs (y1 - y2); - return new Rectangle2D.Double (x, y, w, h); + return x1; } - public Point2D getP1 () + /** + * Return the y coordinate of the first point. + * + * @return the value of y1 + */ + public double getY1() { - return new Point2D.Double (x1, y1); + return y1; } - public Point2D getP2 () + /** + * Return the first point. + * + * @return the point (x1,y1) + */ + public Point2D getP1() { - return new Point2D.Double (x2, y2); + return new Point2D.Float(x1, y1); } - public double getX1 () + /** + * Return the x coordinate of the second point. + * + * @return the value of x2 + */ + public double getX2() { - return x1; + return x2; } - public double getY1 () + /** + * Return the y coordinate of the second point. + * + * @return the value of y2 + */ + public double getY2() { - return y1; + return y2; } - public double getX2 () + /** + * Return the second point. + * + * @return the point (x2,y2) + */ + public Point2D getP2() { - return x2; + return new Point2D.Float(x2, y2); } - public double getY2 () + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(double x1, double y1, double x2, double y2) { - return y2; + this.x1 = (float) x1; + this.y1 = (float) y1; + this.x2 = (float) x2; + this.y2 = (float) y2; } - public void setLine (double x1, double y1, double x2, double y2) + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(float x1, float y1, float x2, float y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } - } - - // This implements the PathIterator for all line objects that don't - // override getPathIterator. - private class Iterator implements PathIterator - { - // Current coordinate. - private int coord; - - private static final int START = 0; - private static final int END_PLUS_ONE = 2; - - public Iterator () - { - coord = START; - } - public int currentSegment (double[] coords) + /** + * Return the exact bounds of this line segment. + * + * @return the bounding box + */ + public Rectangle2D getBounds2D() { - int r = SEG_MOVETO; - if (coord == 0) - { - coords[0] = getX1 (); - coords[1] = getY1 (); - } - else if (coord == 1) - { - coords[0] = getX2 (); - coords[1] = getY2 (); - } - else - r = SEG_CLOSE; - - return r; + float x = Math.min(x1, x2); + float y = Math.min(y1, y2); + float w = Math.abs(x1 - x2); + float h = Math.abs(y1 - y2); + return new Rectangle2D.Float(x, y, w, h); } - - public int currentSegment (float[] coords) - { - int r = SEG_MOVETO; - if (coord == 0) - { - coords[0] = (float) getX1 (); - coords[1] = (float) getY1 (); - } - else if (coord == 1) - { - coords[0] = (float) getX2 (); - coords[1] = (float) getY2 (); - } - else - r = SEG_CLOSE; - - return r; - } - - public int getWindingRule () - { - return WIND_NON_ZERO; - } - - public boolean isDone () - { - return coord == END_PLUS_ONE; - } - - public void next () - { - if (coord < END_PLUS_ONE) - ++coord; - } - } -} + } // class Float +} // class Line2D diff --git a/libjava/java/awt/geom/NoninvertibleTransformException.java b/libjava/java/awt/geom/NoninvertibleTransformException.java index cd561cf..40b2c18 100644 --- a/libjava/java/awt/geom/NoninvertibleTransformException.java +++ b/libjava/java/awt/geom/NoninvertibleTransformException.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2002 Free Software Foundation +/* NoninvertibleTransformException.java -- a transform can't be inverted + Copyright (C) 2000, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -37,14 +38,28 @@ exception statement from your version. */ package java.awt.geom; /** + * Thrown if an operation requires an inverse of an + * <code>AffineTransform</code>, but the transform is in a non-invertible + * state. + * * @author Tom Tromey <tromey@cygnus.com> - * @date July 15, 2000 + * @see AffineTransform + * @status updated to 1.4 */ - public class NoninvertibleTransformException extends Exception { - public NoninvertibleTransformException (String s) + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 6137225240503990466L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoninvertibleTransformException(String s) { - super (s); + super(s); } } diff --git a/libjava/java/awt/geom/PathIterator.java b/libjava/java/awt/geom/PathIterator.java index 50178d3..871c9a8 100644 --- a/libjava/java/awt/geom/PathIterator.java +++ b/libjava/java/awt/geom/PathIterator.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2002 Free Software Foundation +/* PathIterator.java -- describes a shape by iterating over its vertices + Copyright (C) 2000, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -37,23 +38,152 @@ exception statement from your version. */ package java.awt.geom; /** + * This interface provides a directed path over the boundary of a shape. The + * path can contain 1st through 3rd order Bezier curves (lines, and quadratic + * and cubic splines). A shape can have multiple disjoint paths via the + * MOVETO directive, and can close a circular path back to the previos + * MOVETO via the CLOSE directive. + * * @author Tom Tromey <tromey@cygnus.com> - * @date April 16, 2000 + * @author Eric Blake <ebb9@email.byu.edu> + * @see Shape + * @see Stroke + * @see FlatteningPathIterator + * @since 1.2 + * @status updated to 1.4 */ - public interface PathIterator { - public static final int SEG_CLOSE = 4; - public static final int SEG_CUBICTO = 3; - public static final int SEG_LINETO = 1; - public static final int SEG_MOVETO = 0; - public static final int SEG_QUADTO = 2; - public static final int WIND_EVEN_ODD = 0; - public static final int WIND_NON_ZERO = 1; - - public int currentSegment (double[] coords); - public int currentSegment (float[] coords); - public int getWindingRule (); - public boolean isDone (); - public void next (); -} + /** + * The even-odd winding mode: a point is internal to the shape if a ray + * from the point to infinity (in any direction) crosses an odd number of + * segments. + */ + int WIND_EVEN_ODD = 0; + + /** + * The non-zero winding mode: a point is internal to the shape if a ray + * from the point to infinity (in any direction) crosses a different number + * of segments headed clockwise than those headed counterclockwise. + */ + int WIND_NON_ZERO = 1; + + /** + * Starts a new subpath. There is no segment from the previous vertex. + */ + int SEG_MOVETO = 0; + + /** + * The current segment is a line. + */ + int SEG_LINETO = 1; + + /** + * The current segment is a quadratic parametric curve. It is interpolated + * as t varies from 0 to 1 over the current point (CP), first control point + * (P1), and final interpolated control point (P2): + * <pre> + * P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2 + * 0 <= t <= 1 + * B(n,m) = mth coefficient of nth degree Bernstein polynomial + * = C(n,m) * t^(m) * (1 - t)^(n-m) + * C(n,m) = Combinations of n things, taken m at a time + * = n! / (m! * (n-m)!) + * </pre> + */ + int SEG_QUADTO = 2; + + /** + * The current segment is a cubic parametric curve (more commonly known as + * a Bezier curve). It is interpolated as t varies from 0 to 1 over the + * current point (CP), first control point (P1), the second control point + * (P2), and final interpolated control point (P3): + * <pre> + * P(t) = B(3,0)*CP + B(3,1)*P1 + B(3,2)*P2 + B(3,3)*P3 + * 0 <= t <= 1 + * B(n,m) = mth coefficient of nth degree Bernstein polynomial + * = C(n,m) * t^(m) * (1 - t)^(n-m) + * C(n,m) = Combinations of n things, taken m at a time + * = n! / (m! * (n-m)!) + * </pre> + */ + int SEG_CUBICTO = 3; + + /** + * The current segment closes a loop by an implicit line to the previous + * SEG_MOVETO coordinate. + */ + int SEG_CLOSE = 4; + + /** + * Returns the winding rule to determine which points are inside this path. + * + * @return the winding rule + * @see #WIND_EVEN_ODD + * @see #WIND_NON_ZERO + */ + int getWindingRule(); + + /** + * Tests if the iterator is exhausted. If this returns false, currentSegment + * and next may throw a NoSuchElementException (although this is not + * required). + * + * @return true if the iteration is complete + */ + boolean isDone(); + + /** + * Advance to the next segment in the iteration. It is not specified what + * this does if called when isDone() returns false. + * + * @throws java.util.NoSuchElementException optional when isDone() is true + */ + void next(); + + /** + * Returns the coordinates of the next point(s), as well as the type of + * line segment. The input array must be at least a float[6], to accomodate + * up to three (x,y) point pairs (although if you know the iterator is + * flat, you can probably get by with a float[2]). If the returned type is + * SEG_MOVETO or SEG_LINETO, the first point in the array is modified; if + * the returned type is SEG_QUADTO, the first two points are modified; if + * the returned type is SEG_CUBICTO, all three points are modified; and if + * the returned type is SEG_CLOSE, the array is untouched. + * + * @param coords the array to place the point coordinates in + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + * @throws java.util.NoSuchElementException optional when isDone() is true + * @see #SEG_MOVETO + * @see #SEG_LINETO + * @see #SEG_QUADTO + * @see #SEG_CUBICTO + * @see #SEG_CLOSE + */ + int currentSegment(float[] coords); + + /** + * Returns the coordinates of the next point(s), as well as the type of + * line segment. The input array must be at least a double[6], to accomodate + * up to three (x,y) point pairs (although if you know the iterator is + * flat, you can probably get by with a double[2]). If the returned type is + * SEG_MOVETO or SEG_LINETO, the first point in the array is modified; if + * the returned type is SEG_QUADTO, the first two points are modified; if + * the returned type is SEG_CUBICTO, all three points are modified; and if + * the returned type is SEG_CLOSE, the array is untouched. + * + * @param coords the array to place the point coordinates in + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + * @throws java.util.NoSuchElementException optional when isDone() is true + * @see #SEG_MOVETO + * @see #SEG_LINETO + * @see #SEG_QUADTO + * @see #SEG_CUBICTO + * @see #SEG_CLOSE + */ + int currentSegment(double[] coords); +} // interface PathIterator diff --git a/libjava/java/awt/geom/Point2D.java b/libjava/java/awt/geom/Point2D.java index 598402b..48b12f6 100644 --- a/libjava/java/awt/geom/Point2D.java +++ b/libjava/java/awt/geom/Point2D.java @@ -1,4 +1,5 @@ -/* Copyright (C) 1999, 2000, 2002 Free Software Foundation +/* Point2D.java -- generic point in 2-D space + Copyright (C) 1999, 2000, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -37,161 +38,358 @@ exception statement from your version. */ package java.awt.geom; /** + * This class implements a generic point in 2D Cartesian space. The storage + * representation is left up to the subclass. Point includes two useful + * nested classes, for float and double storage respectively. + * * @author Per Bothner <bothner@cygnus.com> - * @date February 8, 1999. + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 */ - -/* Written using "Java Class Libraries", 2nd edition, plus online - * API docs for JDK 1.2 beta from http://www.javasoft.com. - * Status: Believed complete and correct, except that neither toString - * nor hashCode have been compared with JDK output. - */ - public abstract class Point2D implements Cloneable { + /** + * The default constructor. + * + * @see Point + * @see Point2D.Float + * @see Point2D.Double + */ + protected Point2D() + { + } + + /** + * Get the X coordinate, in double precision. + * + * @return the x coordinate + */ public abstract double getX(); - public abstract double getY(); - public abstract void setLocation (double x, double y); + /** + * Get the Y coordinate, in double precision. + * + * @return the y coordinate + */ + public abstract double getY(); - protected Point2D () + /** + * Set the location of this point to the new coordinates. There may be a + * loss of precision. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public abstract void setLocation(double x, double y); + + /** + * Set the location of this point to the new coordinates. There may be a + * loss of precision. + * + * @param p the point to copy + * @throws NullPointerException if p is null + */ + public void setLocation(Point2D p) { + setLocation(p.getX(), p.getY()); } - public void setLocation (Point2D pt) { setLocation(pt.getX(), pt.getY()); } - - static public double distanceSq (double X1, double Y1, double X2, double Y2) + /** + * Return the square of the distance between two points. + * + * @param x1 the x coordinate of point 1 + * @param y1 the y coordinate of point 1 + * @param x2 the x coordinate of point 2 + * @param y2 the y coordinate of point 2 + * @return (x2 - x1)^2 + (y2 - y1)^2 + */ + public static double distanceSq(double x1, double y1, double x2, double y2) { - X2 -= X1; - Y2 -= Y1; - return X2*X2 + Y2*Y2; + x2 -= x1; + y2 -= y1; + return x2 * x2 + y2 * y2; } - static public double distance (double X1, double Y1, double X2, double Y2) + /** + * Return the distance between two points. + * + * @param x1 the x coordinate of point 1 + * @param y1 the y coordinate of point 1 + * @param x2 the x coordinate of point 2 + * @param y2 the y coordinate of point 2 + * @return the distance from (x1,y1) to (x2,y2) + */ + static public double distance(double x1, double y1, double x2, double y2) { - return Math.sqrt(distanceSq(X1, Y1, X2, Y2)); + return Math.sqrt(distanceSq(x1, y1, x2, y2)); } - public double distanceSq (double PX, double PY) + /** + * Return the square of the distance from this point to the given one. + * + * @param x the x coordinate of the other point + * @param y the y coordinate of the other point + * @return the square of the distance + */ + public double distanceSq(double x, double y) { - return distanceSq (getX(), PX, getY(), PY); + return distanceSq(getX(), x, getY(), y); } - public double distance (double PX, double PY) + /** + * Return the square of the distance from this point to the given one. + * + * @param p the other point + * @return the square of the distance + * @throws NullPointerException if p is null + */ + public double distanceSq(Point2D p) { - return distance (getX(), PX, getY(), PY); + return distanceSq(getX(), p.getX(), getY(), p.getY()); } - public double distanceSq (Point2D pt) + /** + * Return the distance from this point to the given one. + * + * @param x the x coordinate of the other point + * @param y the y coordinate of the other point + * @return the distance + */ + public double distance(double x, double y) { - return distanceSq (getX(), pt.getX(), getY(), pt.getY()); + return distance(getX(), x, getY(), y); } - public double distance (Point2D pt) + /** + * Return the distance from this point to the given one. + * + * @param p the other point + * @return the distance + * @throws NullPointerException if p is null + */ + public double distance(Point2D p) { - return distance (getX(), pt.getX(), getY(), pt.getY()); + return distance(getX(), p.getX(), getY(), p.getY()); } - public int hashCode() { return (int) getX() ^ (int) getY(); } - + /** + * Create a new point of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ public Object clone() { try - { - return super.clone (); - } - catch (CloneNotSupportedException _) {return null;} + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } } - public boolean equals (Object o) + /** + * Return the hashcode for this point. The formula is not documented, but + * appears to be the same as: + * <pre> + * long l = Double.doubleToLongBits(getY()); + * l = l * 31 ^ Double.doubleToLongBits(getX()); + * return (int) ((l >> 32) ^ l); + * </pre> + * + * @return the hashcode + */ + public int hashCode() + { + // Talk about a fun time reverse engineering this one! + long l = java.lang.Double.doubleToLongBits(getY()); + l = l * 31 ^ java.lang.Double.doubleToLongBits(getX()); + return (int) ((l >> 32) ^ l); + } + + /** + * Compares two points for equality. This returns true if they have the + * same coordinates. + * + * @param o the point to compare + * @return true if it is equal + */ + public boolean equals(Object o) { if (! (o instanceof Point2D)) return false; Point2D p = (Point2D) o; - return getX () == p.getX () && getY () == p.getY (); + return getX() == p.getX() && getY() == p.getY(); } + /** + * This class defines a point in <code>double</code> precision. + * + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 + */ public static class Double extends Point2D { + /** The X coordinate. */ public double x; + + /** The Y coordinate. */ public double y; - public Double () + /** + * Create a new point at (0,0). + */ + public Double() { - x = 0; - y = 0; } - public Double (double x, double y) + /** + * Create a new point at (x,y). + * + * @param x the x coordinate + * @param y the y coordinate + */ + public Double(double x, double y) { this.x = x; this.y = y; } - public double getX () + /** + * Return the x coordinate. + * + * @return the x coordinate + */ + public double getX() { return x; } - public double getY () + /** + * Return the y coordinate. + * + * @return the y coordinate + */ + public double getY() { return y; } - public void setLocation (double x, double y) + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(double x, double y) { this.x = x; this.y = y; } - public String toString () + /** + * Returns a string representation of this object. The format is: + * <code>"Point2D.Double[" + x + ", " + y + ']'</code>. + * + * @return a string representation of this object + */ + public String toString() { - return "(" + x + ", " + y + ")"; + return "Point2D.Double[" + x + ", " + y + ']'; } - } - + } // class Double + + /** + * This class defines a point in <code>float</code> precision. + * + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 + */ public static class Float extends Point2D { + /** The X coordinate. */ public float x; + + /** The Y coordinate. */ public float y; - public Float () + /** + * Create a new point at (0,0). + */ + public Float() { - x = 0; - y = 0; } - public Float (float x, float y) + /** + * Create a new point at (x,y). + * + * @param x the x coordinate + * @param y the y coordinate + */ + public Float(float x, float y) { this.x = x; this.y = y; } - public double getX () + /** + * Return the x coordinate. + * + * @return the x coordinate + */ + public double getX() { return x; } - public double getY () + /** + * Return the y coordinate. + * + * @return the y coordinate + */ + public double getY() { return y; } - public void setLocation (double x, double y) + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(double x, double y) { this.x = (float) x; this.y = (float) y; } - public void setLocation (float x, float y) + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(float x, float y) { this.x = x; this.y = y; } - public String toString () + /** + * Returns a string representation of this object. The format is: + * <code>"Point2D.Float[" + x + ", " + y + ']'</code>. + * + * @return a string representation of this object + */ + public String toString() { - return "(" + x + ", " + y + ")"; + return "Point2D.Float[" + x + ", " + y + ']'; } - } -} + } // class Float +} // class Point2D diff --git a/libjava/java/awt/geom/QuadCurve2D.java b/libjava/java/awt/geom/QuadCurve2D.java new file mode 100644 index 0000000..2f1aafa --- /dev/null +++ b/libjava/java/awt/geom/QuadCurve2D.java @@ -0,0 +1,486 @@ +/* QuadCurve2D.java -- represents a parameterized quadratic curve in 2-D space + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +/** + * STUBS ONLY + * XXX Implement and document. + */ +public abstract class QuadCurve2D implements Shape, Cloneable +{ + protected QuadCurve2D() + { + } + + public abstract double getX1(); + public abstract double getY1(); + public abstract Point2D getP1(); + public abstract double getCtrlX(); + public abstract double getCtrlY(); + public abstract Point2D getCtrlPt(); + public abstract double getX2(); + public abstract double getY2(); + public abstract Point2D getP2(); + + public abstract void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2); + public void setCurve(double[] coords, int offset) + { + setCurve(coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++]); + } + public void setCurve(Point2D p1, Point2D c, Point2D p2) + { + setCurve(p1.getX(), p1.getY(), c.getX(), c.getY(), + p2.getX(), p2.getY()); + } + public void setCurve(Point2D[] pts, int offset) + { + setCurve(pts[offset].getX(), pts[offset++].getY(), + pts[offset].getX(), pts[offset++].getY(), + pts[offset].getX(), pts[offset++].getY()); + } + public void setCurve(QuadCurve2D c) + { + setCurve(c.getX1(), c.getY1(), c.getCtrlX(), c.getCtrlY(), + c.getX2(), c.getY2()); + } + public static double getFlatnessSq(double x1, double y1, double cx, + double cy, double x2, double y2) + { + // XXX Implement. + throw new Error("not implemented"); + } + public static double getFlatness(double x1, double y1, double cx, double cy, + double x2, double y2) + { + return Math.sqrt(getFlatnessSq(x1, y1, cx, cy, x2, y2)); + } + public static double getFlatnessSq(double[] coords, int offset) + { + return getFlatnessSq(coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++]); + } + public static double getFlatness(double[] coords, int offset) + { + return Math.sqrt(getFlatnessSq(coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++])); + } + public double getFlatnessSq() + { + return getFlatnessSq(getX1(), getY1(), getCtrlX(), getCtrlY(), + getX2(), getY2()); + } + public double getFlatness() + { + return Math.sqrt(getFlatnessSq(getX1(), getY1(), getCtrlX(), getCtrlY(), + getX2(), getY2())); + } + + public void subdivide(QuadCurve2D l, QuadCurve2D r) + { + if (l == null) + l = new QuadCurve2D.Double(); + if (r == null) + r = new QuadCurve2D.Double(); + // Use empty slots at end to share single array. + double[] d = new double[] { getX1(), getY1(), getCtrlX(), getCtrlY(), + getX2(), getY2(), 0, 0, 0, 0 }; + subdivide(d, 0, d, 0, d, 4); + l.setCurve(d, 0); + r.setCurve(d, 4); + } + public static void subdivide(QuadCurve2D src, QuadCurve2D l, QuadCurve2D r) + { + src.subdivide(l, r); + } + public static void subdivide(double[] src, int srcOff, + double[] left, int leftOff, + double[] right, int rightOff) + { + // XXX Implement. + throw new Error("not implemented"); + } + public static int solveQuadratic(double[] eqn) + { + return solveQuadratic(eqn, eqn); + } + public static int solveQuadratic(double[] eqn, double[] res) + { + double c = eqn[0]; + double b = eqn[1]; + double a = eqn[2]; + if (a == 0) + { + if (b == 0) + return -1; + res[0] = -c / b; + return 1; + } + c /= a; + b /= a * 2; + double det = Math.sqrt(b * b - c); + if (det != det) + return 0; + // For fewer rounding errors, we calculate the two roots differently. + if (b > 0) + { + res[0] = -b - det; + res[1] = -c / (b + det); + } + else + { + res[0] = -c / (b - det); + res[1] = -b + det; + } + return 2; + } + + public boolean contains(double x, double y) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + public boolean intersects(double x, double y, double w, double h) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + public boolean contains(double x, double y, double w, double h) + { + // XXX Implement. + throw new Error("not implemented"); + } + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + /** Current coordinate. */ + private int current; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current < 2; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + if (current == 0) + { + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + if (current == 1) + { + coords[0] = (float) getCtrlX(); + coords[1] = (float) getCtrlY(); + coords[2] = (float) getX2(); + coords[3] = (float) getY2(); + if (at != null) + at.transform(coords, 0, coords, 0, 2); + return SEG_QUADTO; + } + throw new NoSuchElementException("quad iterator out of bounds"); + } + + public int currentSegment(double[] coords) + { + if (current == 0) + { + coords[0] = getX1(); + coords[1] = getY1(); + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + if (current == 1) + { + coords[0] = getCtrlX(); + coords[1] = getCtrlY(); + coords[2] = getX2(); + coords[3] = getY2(); + if (at != null) + at.transform(coords, 0, coords, 0, 2); + return SEG_QUADTO; + } + throw new NoSuchElementException("quad iterator out of bounds"); + } + }; + } + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Create a new curve of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * STUBS ONLY + */ + public static class Double extends QuadCurve2D + { + public double x1; + public double y1; + public double ctrlx; + public double ctrly; + public double x2; + public double y2; + + public Double() + { + } + + public Double(double x1, double y1, double cx, double cy, + double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + public double getX1() + { + return x1; + } + public double getY1() + { + return y1; + } + public Point2D getP1() + { + return new Point2D.Double(x1, y1); + } + + public double getCtrlX() + { + return ctrlx; + } + public double getCtrlY() + { + return ctrly; + } + public Point2D getCtrlPt() + { + return new Point2D.Double(ctrlx, ctrly); + } + + public double getX2() + { + return x2; + } + public double getY2() + { + return y2; + } + public Point2D getP2() + { + return new Point2D.Double(x2, y2); + } + + public void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + public Rectangle2D getBounds2D() + { + double nx1 = Math.min(Math.min(x1, ctrlx), x2); + double ny1 = Math.min(Math.min(y1, ctrly), y2); + double nx2 = Math.max(Math.max(x1, ctrlx), x2); + double ny2 = Math.max(Math.max(y1, ctrly), y2); + return new Rectangle2D.Double(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } // class Double + + /** + * STUBS ONLY + */ + public static class Float extends QuadCurve2D + { + public float x1; + public float y1; + public float ctrlx; + public float ctrly; + public float x2; + public float y2; + + public Float() + { + } + + public Float(float x1, float y1, float cx, float cy, + float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + public double getX1() + { + return x1; + } + public double getY1() + { + return y1; + } + public Point2D getP1() + { + return new Point2D.Float(x1, y1); + } + + public double getCtrlX() + { + return ctrlx; + } + public double getCtrlY() + { + return ctrly; + } + public Point2D getCtrlPt() + { + return new Point2D.Float(ctrlx, ctrly); + } + + public double getX2() + { + return x2; + } + public double getY2() + { + return y2; + } + public Point2D getP2() + { + return new Point2D.Float(x2, y2); + } + + public void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2) + { + this.x1 = (float) x1; + this.y1 = (float) y1; + ctrlx = (float) cx; + ctrly = (float) cy; + this.x2 = (float) x2; + this.y2 = (float) y2; + } + public void setCurve(float x1, float y1, float cx, float cy, + float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + public Rectangle2D getBounds2D() + { + float nx1 = (float) Math.min(Math.min(x1, ctrlx), x2); + float ny1 = (float) Math.min(Math.min(y1, ctrly), y2); + float nx2 = (float) Math.max(Math.max(x1, ctrlx), x2); + float ny2 = (float) Math.max(Math.max(y1, ctrly), y2); + return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } // class Float +} // class CubicCurve2D diff --git a/libjava/java/awt/geom/Rectangle2D.java b/libjava/java/awt/geom/Rectangle2D.java index eeb1925..e0a278a 100644 --- a/libjava/java/awt/geom/Rectangle2D.java +++ b/libjava/java/awt/geom/Rectangle2D.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2001, 2002 Free Software Foundation +/* Rectangle2D.java -- generic rectangles in 2-D space + Copyright (C) 2000, 2001, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -34,431 +35,952 @@ this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ + package java.awt.geom; +import java.util.NoSuchElementException; + /** + * This class describes a rectangle by a point (x,y) and dimension (w x h). + * The actual storage is left up to subclasses. + * + * <p>It is valid for a rectangle to have negative width or height; but it + * is considered to have no area or internal points. Therefore, the behavior + * in methods like <code>contains</code> or <code>intersects</code> is + * undefined unless the rectangle has positive width and height. + * * @author Tom Tromey <tromey@cygnus.com> - * @date April 16, 2000 + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 */ - public abstract class Rectangle2D extends RectangularShape { + /** + * The point lies left of the rectangle (p.x < r.x). + * + * @see #outcode() + */ public static final int OUT_LEFT = 1; + + /** + * The point lies above the rectangle (p.y < r.y). + * + * @see #outcode() + */ public static final int OUT_TOP = 2; + + /** + * The point lies right of the rectangle (p.x > r.maxX). + * + * @see #outcode() + */ public static final int OUT_RIGHT = 4; + + /** + * The point lies below of the rectangle (p.y > r.maxY). + * + * @see #outcode() + */ public static final int OUT_BOTTOM = 8; - protected Rectangle2D () + /** + * Default constructor. + */ + protected Rectangle2D() { } - /** Set the bounding box of this rectangle. - * @param x X coordinate - * @param y Y coordinate - * @param w Width - * @param h Height + /** + * Set the bounding box of this rectangle. + * + * @param x the new X coordinate + * @param y the new Y coordinate + * @param w the new width + * @param h the new height */ - public abstract void setRect (double x, double y, double w, double h); + public abstract void setRect(double x, double y, double w, double h); - /** Set the bounding box of this rectangle. - * @param r Bounding rectangle. + /** + * Set the bounding box of this rectangle from the given one. + * + * @param r rectangle to copy + * @throws NullPointerException if r is null */ - public void setRect (Rectangle2D r) + public void setRect(Rectangle2D r) { - setRect (r.getX (), r.getY (), r.getWidth (), r.getHeight ()); + setRect(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } - /** Returns true if line segment intersects interior of this - * rectangle. - * @param x1 X coordinate of first end of line segment - * @param y1 Y coordinate of first end of line segment - * @param x2 X coordinate of second end of line segment - * @param y2 Y coordinate of segment end of line segment + /** + * Tests if the specified line intersects the interior of this rectangle. + * + * @param x1 the first x coordinate of line segment + * @param y1 the first y coordinate of line segment + * @param x2 the second x coordinate of line segment + * @param y2 the second y coordinate of line segment + * @return true if the line intersects the rectangle */ - public boolean intersectsLine (double x1, double y1, double x2, double y2) + public boolean intersectsLine(double x1, double y1, double x2, double y2) { - int o1 = outcode (x1, y1); - int o2 = outcode (x2, y2); - - double mx = getX (); - double my = getY (); - double mx2 = getWidth (); - double my2 = getHeight (); - x1 -= mx; - x2 -= mx; - y1 -= my; - y2 -= my; - - // Here we handle all lines that stay entirely on one side of the - // rectangle. We also handle some lines that intersect the - // rectangle. All vertical and horizontal lines are handled here. - int xor = o1 ^ o2; - int or = o1 | o2; - if ((xor & (OUT_BOTTOM | OUT_TOP)) == 0) - { - if ((or & (OUT_BOTTOM | OUT_TOP)) != 0) - return false; - return ((o1 & (OUT_LEFT | OUT_RIGHT)) != 0 - || (o2 & (OUT_LEFT | OUT_RIGHT)) != 0); - } - else if ((xor & (OUT_LEFT | OUT_RIGHT)) == 0) - { - if ((or & (OUT_LEFT | OUT_RIGHT)) != 0) - return false; - return ((o1 & (OUT_BOTTOM | OUT_TOP)) != 0 - || (o2 & (OUT_BOTTOM | OUT_TOP)) != 0); - } - - double dx = x2 - x1; - double dy = y2 - y1; + double x = getX(); + double y = getY(); + double w = getWidth(); + double h = getHeight(); + if (w <= 0 || h <= 0) + return false; - double t1l = - x1 / dx; - double t1h = (mx2 - x1) / dx; + if (x1 >= x && x1 <= x + w && y1 >= y && y1 <= y + h) + return true; + if (x2 >= x && x2 <= x + w && y2 >= y && y2 <= y + h) + return true; - if (t1l >= t1h) - return false; - double t2l = - y1 / dy; - double t2h = (my2 - y1) / dy; + double x3 = x + w; + double y3 = y + h; - if (t2l >= t2h || t2l >= t1h || t2h < t1l) - return false; - return true; + return (Line2D.linesIntersect(x1, y1, x2, y2, x, y, x, y3) + || Line2D.linesIntersect(x1, y1, x2, y2, x, y3, x3, y3) + || Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x3, y) + || Line2D.linesIntersect(x1, y1, x2, y2, x3, y, x, y)); } - /** Return true if line segment intersects interior of this - * rectangle. - * @param l The line segment + /** + * Tests if the specified line intersects the interior of this rectangle. + * + * @param l the line segment + * @return true if the line intersects the rectangle + * @throws NullPointerException if l is null */ -// public boolean intersectsLine (Line2D l) -// { -// } + public boolean intersectsLine(Line2D l) + { + return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); + } - public abstract int outcode (double x, double y); + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + */ + public abstract int outcode(double x, double y); + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param p the point to check + * @return the binary OR of the result + * @throws NullPointerException if p is null + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + */ + public int outcode(Point2D p) + { + return outcode(p.getX(), p.getY()); + } - public int outcode (Point2D p) + /** + * Set the bounding box of this rectangle. + * + * @param x the new X coordinate + * @param y the new Y coordinate + * @param w the new width + * @param h the new height + */ + public void setFrame(double x, double y, double w, double h) { - return outcode (p.getX (), p.getY ()); + setRect(x, y, w, h); } - /** Set bounding frame for this rectangle. - * @param x X coordinate - * @param y Y coordinate - * @param w Width - * @param h Height + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle */ - public void setFrame (double x, double y, double w, double h) + public Rectangle2D getBounds2D() { - setRect (x, y, w, h); + return (Rectangle2D) clone(); } - public Rectangle2D getBounds2D () + /** + * Test if the given point is contained in the rectangle. + * + * @param x the x coordinate of the point + * @param y the y coordinate of the point + * @return true if (x,y) is in the rectangle + */ + public boolean contains(double x, double y) { - // FIXME. - return (Rectangle2D) clone (); + double mx = getX(); + double my = getY(); + double w = getWidth(); + double h = getHeight(); + return w > 0 && h > 0 && x >= mx && x < mx + w && y >= my && y < my + h; } - public boolean contains (double x, double y) + /** + * Tests if the given rectangle intersects this one. In other words, test if + * the two rectangles share at least one internal point. + * + * @param x the x coordinate of the other rectangle + * @param y the y coordinate of the other rectangle + * @param w the width of the other rectangle + * @param h the height of the other rectangle + * @return true if the rectangles intersect + */ + public boolean intersects(double x, double y, double w, double h) { - double mx = getX (); - double mw = getWidth (); - if (x < mx || x >= mx + mw) - return false; - double my = getY (); - double mh = getHeight (); - return y >= my && y < my + mh; + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + return w > 0 && h > 0 && mw > 0 && mh > 0 + && x < mx + mw && x + w > mx && y < my + mh && y + h > my; } - public boolean intersects (double x, double y, double w, double h) + /** + * Tests if this rectangle contains the given one. In other words, test if + * this rectangle contains all points in the given one. + * + * @param x the x coordinate of the other rectangle + * @param y the y coordinate of the other rectangle + * @param w the width of the other rectangle + * @param h the height of the other rectangle + * @return true if this rectangle contains the other + */ + public boolean contains(double x, double y, double w, double h) { - double mx = getX (); - double mw = getWidth (); - if (x < mx || x >= mx + mw || x + w < mx || x + w >= mx + mw) - return false; - double my = getY (); - double mh = getHeight (); - return y >= my && y < my + mh && y + h >= my && y + h < my + mh; + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + return w > 0 && h > 0 && mw > 0 && mh > 0 + && x >= mx && x + w <= mx + mw && y >= my && y + h <= my + mh; } - public boolean contains (double x, double y, double w, double h) + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public abstract Rectangle2D createIntersection(Rectangle2D r); + + /** + * Intersects a pair of rectangles, and places the result in the + * destination; this can be used to avoid object creation. This method + * even works when the destination is also a source, although you stand + * to lose the original data. + * + * @param src1 the first source + * @param src2 the second source + * @param dest the destination for the intersection + * @throws NullPointerException if any rectangle is null + */ + public static void intersect(Rectangle2D src1, Rectangle2D src2, + Rectangle2D dest) { - return contains (x, y) && contains (x + w, y + h); + double x = Math.max(src1.getX(), src2.getX()); + double y = Math.max(src1.getY(), src2.getY()); + double maxx = Math.min(src1.getMaxX(), src2.getMaxX()); + double maxy = Math.min(src1.getMaxY(), src2.getMaxY()); + dest.setRect(x, y, maxx - x, maxy - y); } - public abstract Rectangle2D createIntersection (Rectangle2D r); + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public abstract Rectangle2D createUnion(Rectangle2D r); + + /** + * Joins a pair of rectangles, and places the result in the destination; + * this can be used to avoid object creation. This method even works when + * the destination is also a source, although you stand to lose the + * original data. + * + * @param src1 the first source + * @param src2 the second source + * @param dest the destination for the union + * @throws NullPointerException if any rectangle is null + */ + public static void union(Rectangle2D src1, Rectangle2D src2, + Rectangle2D dest) + { + double x = Math.min(src1.getX(), src2.getX()); + double y = Math.min(src1.getY(), src2.getY()); + double maxx = Math.max(src1.getMaxX(), src2.getMaxX()); + double maxy = Math.max(src1.getMaxY(), src2.getMaxY()); + dest.setRect(x, y, maxx - x, maxy - y); + } - public static void intersect (Rectangle2D src1, Rectangle2D src2, - Rectangle2D dest) + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to <code>contains</code> may + * return false. + * + * @param x the X coordinate of the point to add to this rectangle + * @param y the Y coordinate of the point to add to this rectangle + */ + public void add(double newx, double newy) { - double x1l = src1.getMinX (); - double x2l = src2.getMinX (); - double nxl = Math.max (x1l, x2l); - double x1h = src1.getMaxX (); - double x2h = src2.getMaxX (); - double nxh = Math.min (x1h, x2h); - if (nxh < nxl) - nxh = nxl; - double y1l = src1.getMinY (); - double y2l = src2.getMinY (); - double nyl = Math.max (y1l, y2l); - double y1h = src1.getMaxY (); - double y2h = src2.getMaxY (); - double nyh = Math.min (y1h, y2h); - if (nyh < nyl) - nyh = nyl; - dest.setFrameFromDiagonal (nxl, nyl, nxh, nyh); + double minx = Math.min(getX(), newx); + double maxx = Math.max(getMaxX(), newx); + double miny = Math.min(getY(), newy); + double maxy = Math.max(getMaxY(), newy); + setRect(minx, miny, maxx - minx, maxy - miny); } - public abstract Rectangle2D createUnion (Rectangle2D r); + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to <code>contains</code> may + * return false. + * + * @param p the point to add to this rectangle + * @throws NullPointerException if p is null + */ + public void add(Point2D p) + { + add(p.getX(), p.getY()); + } - public static void union (Rectangle2D src1, Rectangle2D src2, - Rectangle2D dest) + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified rectangle. + * + * @param r the rectangle to add to this rectangle + * @throws NullPointerException if r is null + * @see #union(Rectangle2D) + */ + public void add(Rectangle2D r) { - double x1l = src1.getMinX (); - double x2l = src2.getMinX (); - double nxl = Math.max (x1l, x2l); - double x1h = src1.getMaxX (); - double x2h = src2.getMaxX (); - double nxh = Math.min (x1h, x2h); - double y1l = src1.getMinY (); - double y2l = src2.getMinY (); - double nyl = Math.max (y1l, y2l); - double y1h = src1.getMaxY (); - double y2h = src2.getMaxY (); - double nyh = Math.min (y1h, y2h); - dest.setFrameFromDiagonal (nxl, nyl, nxh, nyh); + union(this, r, this); } - public void add (double newx, double newy) + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. This iterator is thread + * safe; modifications to the rectangle do not affect the results of this + * path instance. + * + * @param transform an optional transform to apply to the iterator + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(final AffineTransform at) { - double minx = Math.min (getMinX (), newx); - double maxx = Math.max (getMaxX (), newx); - double miny = Math.min (getMinY (), newy); - double maxy = Math.max (getMaxY (), newy); - setFrameFromDiagonal (minx, miny, maxx, maxy); + final double minx = getX(); + final double miny = getY(); + final double maxx = minx + getWidth(); + final double maxy = miny + getHeight(); + return new PathIterator() + { + /** Current coordinate. */ + private int current = (maxx >= minx && maxy >= miny) ? 6 : 0; + + public int getWindingRule() + { + return WIND_EVEN_ODD; + } + + public boolean isDone() + { + return current > 5; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + switch (current) + { + case 1: + coords[0] = (float) maxx; + coords[1] = (float) miny; + break; + case 2: + coords[0] = (float) maxx; + coords[1] = (float) maxy; + break; + case 3: + coords[0] = (float) minx; + coords[1] = (float) maxy; + break; + case 0: + case 4: + coords[0] = (float) minx; + coords[1] = (float) miny; + break; + case 5: + return SEG_CLOSE; + default: + throw new NoSuchElementException("rect iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return current == 0 ? SEG_MOVETO : SEG_LINETO; + } + + public int currentSegment(double[] coords) + { + switch (current) + { + case 1: + coords[0] = maxx; + coords[1] = miny; + break; + case 2: + coords[0] = maxx; + coords[1] = maxy; + break; + case 3: + coords[0] = minx; + coords[1] = maxy; + break; + case 0: + case 4: + coords[0] = minx; + coords[1] = miny; + break; + case 5: + return SEG_CLOSE; + default: + throw new NoSuchElementException("rect iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return current == 0 ? SEG_MOVETO : SEG_LINETO; + } + }; } - public void add (Point2D p) + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. This iterator is thread + * safe; modifications to the rectangle do not affect the results of this + * path instance. As the rectangle is already flat, the flatness parameter + * is ignored. + * + * @param transform an optional transform to apply to the iterator + * @param double the maximum distance for deviation from the real boundary + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) { - add (p.getX (), p.getY ()); + return getPathIterator(at); } - public void add (Rectangle2D r) + /** + * Return the hashcode for this rectangle. The formula is not documented, but + * appears to be the same as: + * <pre> + * long l = Double.doubleToLongBits(getX()) + * + 37 * Double.doubleToLongBits(getY()) + * + 43 * Double.doubleToLongBits(getWidth()) + * + 47 * Double.doubleToLongBits(getHeight()); + * return (int) ((l >> 32) ^ l); + * </pre> + * + * @return the hashcode + */ + public int hashCode() { - add (r.getMinX (), r.getMinY ()); - add (r.getMaxX (), r.getMaxY ()); + // Talk about a fun time reverse engineering this one! + long l = java.lang.Double.doubleToLongBits(getX()) + + 37 * java.lang.Double.doubleToLongBits(getY()) + + 43 * java.lang.Double.doubleToLongBits(getWidth()) + + 47 * java.lang.Double.doubleToLongBits(getHeight()); + return (int) ((l >> 32) ^ l); } - public PathIterator getPathIterator (AffineTransform at) + /** + * Tests this rectangle for equality against the specified object. This + * will be true if an only if the specified object is an instance of + * Rectangle2D with the same coordinates and dimensions. + * + * @param obj the object to test against for equality + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) { - // We know the superclass just ignores the flatness parameter. - return getPathIterator (at, 0); + if (! (obj instanceof Rectangle2D)) + return false; + Rectangle2D r = (Rectangle2D) obj; + return r.getX() == getX() && r.getY() == getY() + && r.getWidth() == getWidth() && r.getHeight() == getHeight(); } + /** + * This class defines a rectangle in <code>double</code> precision. + * + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 + */ public static class Double extends Rectangle2D { - public double height; - public double width; + /** The x coordinate of the lower left corner. */ public double x; + + /** The y coordinate of the lower left corner. */ public double y; - public Double () + /** The width of the rectangle. */ + public double width; + + /** The height of the rectangle. */ + public double height; + + /** + * Create a rectangle at (0,0) with width 0 and height 0. + */ + public Double() { - height = width = x = y = 0; } - public Double (double x, double y, double w, double h) + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public Double(double x, double y, double w, double h) { this.x = x; this.y = y; - this.width = w; - this.height = h; + width = w; + height = h; } - public double getX () + /** + * Return the X coordinate. + * + * @return the value of x + */ + public double getX() { return x; } - public double getY () + /** + * Return the Y coordinate. + * + * @return the value of y + */ + public double getY() { return y; } - public double getWidth () + /** + * Return the width. + * + * @return the value of width + */ + public double getWidth() { return width; } - public double getHeight () + /** + * Return the height. + * + * @return the value of height + */ + public double getHeight() { return height; } - public boolean isEmpty () + /** + * Test if the rectangle is empty. + * + * @return true if width or height is not positive + */ + public boolean isEmpty() { return width <= 0 || height <= 0; } - public void setRect (double x, double y, double w, double h) + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(double x, double y, double w, double h) { this.x = x; this.y = y; - this.width = w; - this.height = h; + width = w; + height = h; } - public void setRect (Rectangle2D r) + /** + * Set the contents of this rectangle to those specified. + * + * @param r the rectangle to copy + * @throws NullPointerException if r is null + */ + public void setRect(Rectangle2D r) { - this.x = r.getX (); - this.y = r.getY (); - this.width = r.getWidth (); - this.height = r.getHeight (); + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); } - public int outcode (double x, double y) + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + * @since 1.2 + */ + public int outcode(double x, double y) { - int code = 0; - if (x < this.x) - code |= OUT_LEFT; - else if (x >= this.x + this.width) - code |= OUT_RIGHT; - if (y < this.y) - code |= OUT_TOP; - else if (y >= this.y + this.height) - code |= OUT_BOTTOM; - return code; + int result = 0; + if (width <= 0) + result |= OUT_LEFT | OUT_RIGHT; + else if (x < this.x) + result |= OUT_LEFT; + else if (x > this.x + width) + result |= OUT_RIGHT; + if (height <= 0) + result |= OUT_BOTTOM | OUT_TOP; + else if (y < this.y) // Remember that +y heads top-to-bottom. + result |= OUT_TOP; + else if (y > this.y + height) + result |= OUT_BOTTOM; + return result; } - public Rectangle2D getBounds2D () + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle + */ + public Rectangle2D getBounds2D() { - return new Rectangle2D.Double (x, y, width, height); + return new Double(x, y, width, height); } - public Rectangle2D createIntersection (Rectangle2D r) + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public Rectangle2D createIntersection(Rectangle2D r) { - Double res = new Double (); - intersect (this, r, res); + Double res = new Double(); + intersect(this, r, res); return res; } - public Rectangle2D createUnion (Rectangle2D r) + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public Rectangle2D createUnion(Rectangle2D r) { - Double res = new Double (); - union (this, r, res); + Double res = new Double(); + union(this, r, res); return res; } - public String toString () + /** + * Returns a string representation of this rectangle. This is in the form + * <code>getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + * + ",h=" + height + ']'</code>. + * + * @return a string representation of this rectangle + */ + public String toString() { - return "fixme"; + return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + + ",h=" + height + ']'; } - } - + } // class Double + + /** + * This class defines a rectangle in <code>float</code> precision. + * + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @status updated to 1.4 + */ public static class Float extends Rectangle2D { - public float height; - public float width; + /** The x coordinate of the lower left corner. */ public float x; + + /** The y coordinate of the lower left corner. */ public float y; - public Float () + /** The width of the rectangle. */ + public float width; + + /** The height of the rectangle. */ + public float height; + + /** + * Create a rectangle at (0,0) with width 0 and height 0. + */ + public Float() { - height = width = x = y = 0; } - public Float (float x, float y, float w, float h) + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public Float(float x, float y, float w, float h) { this.x = x; this.y = y; - this.width = w; - this.height = h; + width = w; + height = h; } - public double getX () + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + Float(double x, double y, double w, double h) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + } + + /** + * Return the X coordinate. + * + * @return the value of x + */ + public double getX() { return x; } - public double getY () + /** + * Return the Y coordinate. + * + * @return the value of y + */ + public double getY() { return y; } - public double getWidth () + /** + * Return the width. + * + * @return the value of width + */ + public double getWidth() { return width; } - public double getHeight () + /** + * Return the height. + * + * @return the value of height + */ + public double getHeight() { return height; } - public boolean isEmpty () + /** + * Test if the rectangle is empty. + * + * @return true if width or height is not positive + */ + public boolean isEmpty() { return width <= 0 || height <= 0; } - public void setRect (double x, double y, double w, double h) + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(float x, float y, float w, float h) { - this.x = (float) x; - this.y = (float) y; - this.width = (float) w; - this.height = (float) h; + this.x = x; + this.y = y; + width = w; + height = h; } - public void setRect (float x, float y, float w, float h) + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(double x, double y, double w, double h) { - this.x = x; - this.y = y; - this.width = w; - this.height = h; + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; } - public void setRect (Rectangle2D r) + /** + * Set the contents of this rectangle to those specified. + * + * @param r the rectangle to copy + * @throws NullPointerException if r is null + */ + public void setRect(Rectangle2D r) { - this.x = (float) r.getX (); - this.y = (float) r.getY (); - this.width = (float) r.getWidth (); - this.height = (float) r.getHeight (); + x = (float) r.getX(); + y = (float) r.getY(); + width = (float) r.getWidth(); + height = (float) r.getHeight(); } - public int outcode (double x, double y) + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + * @since 1.2 + */ + public int outcode(double x, double y) { - int code = 0; - if (x < this.x) - code |= OUT_LEFT; - else if (x >= this.x + this.width) - code |= OUT_RIGHT; - if (y < this.y) - code |= OUT_TOP; - else if (y >= this.y + this.height) - code |= OUT_BOTTOM; - return code; + int result = 0; + if (width <= 0) + result |= OUT_LEFT | OUT_RIGHT; + else if (x < this.x) + result |= OUT_LEFT; + else if (x > this.x + width) + result |= OUT_RIGHT; + if (height <= 0) + result |= OUT_BOTTOM | OUT_TOP; + else if (y < this.y) // Remember that +y heads top-to-bottom. + result |= OUT_TOP; + else if (y > this.y + height) + result |= OUT_BOTTOM; + return result; } - public Rectangle2D getBounds2D () + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle + */ + public Rectangle2D getBounds2D() { - return new Rectangle2D.Float (x, y, width, height); + return new Float(x, y, width, height); } - public Rectangle2D createIntersection (Rectangle2D r) + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public Rectangle2D createIntersection(Rectangle2D r) { - Float res = new Float (); - intersect (this, r, res); + Float res = new Float(); + intersect(this, r, res); return res; } - public Rectangle2D createUnion (Rectangle2D r) + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public Rectangle2D createUnion(Rectangle2D r) { - Float res = new Float (); - union (this, r, res); + Float res = new Float(); + union(this, r, res); return res; } - public String toString () + /** + * Returns a string representation of this rectangle. This is in the form + * <code>getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + * + ",h=" + height + ']'</code>. + * + * @return a string representation of this rectangle + */ + public String toString() { - return "fixme"; + return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + + ",h=" + height + ']'; } - } -} + } // class Float +} // class Rectangle2D diff --git a/libjava/java/awt/geom/RectangularShape.java b/libjava/java/awt/geom/RectangularShape.java index 8975128..1801e80 100644 --- a/libjava/java/awt/geom/RectangularShape.java +++ b/libjava/java/awt/geom/RectangularShape.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2002 Free Software Foundation +/* RectangularShape.java -- a rectangular frame for several generic shapes + Copyright (C) 2000, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -34,77 +35,196 @@ this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ + package java.awt.geom; -import java.awt.*; -import java.awt.geom.Rectangle2D; + +import java.awt.Rectangle; +import java.awt.Shape; /** + * This class provides a generic framework, and several helper methods, for + * subclasses which represent geometric objects inside a rectangular frame. + * This does not specify any geometry except for the bounding box. + * * @author Tom Tromey <tromey@cygnus.com> - * @date April 16, 2000 + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.2 + * @see Arc2D + * @see Ellipse2D + * @see Rectangle2D + * @see RoundRectangle2D + * @status updated to 1.4 */ - public abstract class RectangularShape implements Shape, Cloneable { - protected RectangularShape () + /** + * Default constructor. + */ + protected RectangularShape() { } - public abstract double getX (); - public abstract double getY (); - public abstract double getWidth (); - public abstract double getHeight (); - - public double getMinX () - { - return Math.min (getX (), getX () + getWidth ()); + /** + * Get the x coordinate of the upper-left corner of the framing rectangle. + * + * @return the x coordinate + */ + public abstract double getX(); + + /** + * Get the y coordinate of the upper-left corner of the framing rectangle. + * + * @return the y coordinate + */ + public abstract double getY(); + + /** + * Get the width of the framing rectangle. + * + * @return the width + */ + public abstract double getWidth(); + + /** + * Get the height of the framing rectangle. + * + * @return the height + */ + public abstract double getHeight(); + + /** + * Get the minimum x coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getX() even when + * getWidth() is negative. + * + * @return the minimum x coordinate + */ + public double getMinX() + { + return getX(); } - public double getMinY () + /** + * Get the minimum y coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getY() even when + * getHeight() is negative. + * + * @return the minimum y coordinate + */ + public double getMinY() { - return Math.min (getY (), getY () + getHeight ()); + return getY(); } - public double getMaxX () - { - return Math.max (getX (), getX () + getWidth ()); + /** + * Get the maximum x coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getX()+getWidth() even + * when getWidth() is negative. + * + * @return the maximum x coordinate + */ + public double getMaxX() + { + return getX() + getWidth(); } - public double getMaxY () + /** + * Get the maximum y coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getY()+getHeight() even + * when getHeight() is negative. + * + * @return the maximum y coordinate + */ + public double getMaxY() { - return Math.max (getY (), getY () + getHeight ()); + return getY() + getHeight(); } - public double getCenterX () + /** + * Return the x coordinate of the center point of the framing rectangle. + * + * @return the central x coordinate + */ + public double getCenterX() { - return getX () + getWidth () / 2; + return getX() + getWidth() / 2; } - public double getCenterY () + /** + * Return the y coordinate of the center point of the framing rectangle. + * + * @return the central y coordinate + */ + public double getCenterY() { - return getY () + getHeight () / 2; + return getY() + getHeight() / 2; } - public Rectangle2D getFrame () + /** + * Return the frame around this object. Note that this may be a looser + * bounding box than getBounds2D. + * + * @return the frame, in double precision + * @see #setFrame(double, double, double, double) + */ + public Rectangle2D getFrame() { - return new Rectangle2D.Double (getX (), getY (), - getWidth (), getHeight ()); + return new Rectangle2D.Double(getX(), getY(), getWidth(), getHeight()); } - public abstract boolean isEmpty (); - public abstract void setFrame (double x, double y, double w, double h); - - public void setFrame (Point2D loc, Dimension2D size) + /** + * Test if the shape is empty, meaning that no points are inside it. + * + * @return true if the shape is empty + */ + public abstract boolean isEmpty(); + + /** + * Set the framing rectangle of this shape to the given coordinate and size. + * + * @param x the new x coordinate + * @param y the new y coordinate + * @param w the new width + * @param h the new height + * @see #getFrame() + */ + public abstract void setFrame(double x, double y, double w, double h); + + /** + * Set the framing rectangle of this shape to the given coordinate and size. + * + * @param p the new point + * @param d the new dimension + * @throws NullPointerException if p or d is null + * @see #getFrame() + */ + public void setFrame(Point2D p, Dimension2D d) { - setFrame (loc.getX (), loc.getY (), size.getWidth (), size.getHeight ()); + setFrame(p.getX(), p.getY(), d.getWidth(), d.getHeight()); } - public void setFrame (Rectangle2D r) + /** + * Set the framing rectangle of this shape to the given rectangle. + * + * @param r the new framing rectangle + * @throws NullPointerException if r is null + * @see #getFrame() + */ + public void setFrame(Rectangle2D r) { - setFrame (r.getX (), r.getY (), r.getWidth (), r.getHeight ()); + setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } - public void setFrameFromDiagonal (double x1, double y1, - double x2, double y2) + /** + * Set the framing rectangle of this shape using two points on a diagonal. + * The area will be positive. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + */ + public void setFrameFromDiagonal(double x1, double y1, double x2, double y2) { if (x1 > x2) { @@ -118,206 +238,148 @@ public abstract class RectangularShape implements Shape, Cloneable y2 = y1; y1 = t; } - setFrame (x1, y1, x2 - x1, y2 - y1); + setFrame(x1, y1, x2 - x1, y2 - y1); } - public void setFrameFromDiagonal (Point2D p1, Point2D p2) + /** + * Set the framing rectangle of this shape using two points on a diagonal. + * The area will be positive. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public void setFrameFromDiagonal(Point2D p1, Point2D p2) { - setFrameFromDiagonal (p1.getX (), p1.getY (), - p2.getX (), p2.getY ()); + setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY()); } - public void setFrameFromCenter (double centerX, double centerY, - double cornerX, double cornerY) + /** + * Set the framing rectangle of this shape using the center of the frame, + * and one of the four corners. The area will be positive. + * + * @param centerX the x coordinate at the center + * @param centerY the y coordinate at the center + * @param cornerX the x coordinate at a corner + * @param cornerY the y coordinate at a corner + */ + public void setFrameFromCenter(double centerX, double centerY, + double cornerX, double cornerY) { - double halfw = Math.abs (cornerX - centerX); - double halfh = Math.abs (cornerY - centerY); - setFrame (centerX - halfw, centerY - halfh, - 2 * halfw, 2 * halfh); + double halfw = Math.abs(cornerX - centerX); + double halfh = Math.abs(cornerY - centerY); + setFrame(centerX - halfw, centerY - halfh, halfw + halfw, halfh + halfh); } - public void setFrameFromCenter (Point2D center, Point2D corner) + /** + * Set the framing rectangle of this shape using the center of the frame, + * and one of the four corners. The area will be positive. + * + * @param center the center point + * @param corner a corner point + * @throws NullPointerException if either point is null + */ + public void setFrameFromCenter(Point2D center, Point2D corner) { - setFrameFromCenter (center.getX (), center.getY (), - corner.getX (), corner.getY ()); + setFrameFromCenter(center.getX(), center.getY(), + corner.getX(), corner.getY()); } - public boolean contains (Point2D p) + /** + * Tests if a point is inside the boundary of the shape. + * + * @param p the point to test + * @return true if the point is inside the shape + * @throws NullPointerException if p is null + * @see #contains(double, double) + */ + public boolean contains(Point2D p) { - double x = p.getX (); - double y = p.getY (); - double rx = getX (); - double ry = getY (); - double w = getWidth (); - double h = getHeight (); - return x >= rx && x < rx + w && y >= ry && y < ry + h; + return contains(p.getX(), p.getY()); } - public boolean intersects (Rectangle2D r) + /** + * Tests if a rectangle and this shape share common internal points. + * + * @param r the rectangle to test + * @return true if the rectangle intersects this shpae + * @throws NullPointerException if r is null + * @see #intersects(double, double, double, double) + */ + public boolean intersects(Rectangle2D r) { - double x = getX (); - double w = getWidth (); - double mx = r.getX (); - double mw = r.getWidth (); - if (x < mx || x >= mx + mw || x + w < mx || x + w >= mx + mw) - return false; - double y = getY (); - double h = getHeight (); - double my = r.getY (); - double mh = r.getHeight (); - return y >= my && y < my + mh && y + h >= my && y + h < my + mh; + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } - private boolean containsPoint (double x, double y) + /** + * Tests if the shape completely contains the given rectangle. + * + * @param r the rectangle to test + * @return true if r is contained in this shape + * @throws NullPointerException if r is null + * @see #contains(double, double, double, double) + */ + public boolean contains(Rectangle2D r) { - double mx = getX (); - double mw = getWidth (); - if (x < mx || x >= mx + mw) - return false; - double my = getY (); - double mh = getHeight (); - return y >= my && y < my + mh; + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } - public boolean contains (Rectangle2D r) + /** + * Returns a bounding box for this shape, in integer format. Notice that you + * may get a tighter bound with getBounds2D. If the frame is empty, the + * box is the default empty box at the origin. + * + * @return a bounding box + */ + public Rectangle getBounds() { - return (containsPoint (r.getMinX (), r.getMinY ()) - && containsPoint (r.getMaxX (), r.getMaxY ())); + if (isEmpty()) + return new Rectangle(); + double x = getX(); + double y = getY(); + double maxx = Math.ceil(x + getWidth()); + double maxy = Math.ceil(y + getHeight()); + x = Math.floor(x); + y = Math.floor(y); + return new Rectangle((int) x, (int) y, (int) (maxx - x), (int) (maxy - y)); } - public Rectangle getBounds () + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. The path is + * flattened until all segments differ from the curve by at most the value + * of the flatness parameter, within the limits of the default interpolation + * recursion limit of 1024 segments between actual points. Each call + * returns a new object, independent from others in use. The result is + * threadsafe if and only if the iterator returned by + * {@link #getPathIterator(AffineTransform)} is as well. + * + * @param transform an optional transform to apply to the iterator + * @param flatness the desired flatness + * @return a new iterator over the boundary + * @throws IllegalArgumentException if flatness is invalid + * @since 1.2 + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) { - return new Rectangle ((int) getX (), (int) getY (), - (int) getWidth (), (int) getHeight ()); + return new FlatteningPathIterator(getPathIterator(at), flatness); } - public PathIterator getPathIterator (AffineTransform at, double flatness) - { - return at.new Iterator (new Iterator ()); - } - - public Object clone () + /** + * Create a new shape of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() { try - { - return super.clone (); - } - catch (CloneNotSupportedException _) {return null;} - } - - // This implements the PathIterator for all RectangularShape objects - // that don't override getPathIterator. - private class Iterator implements PathIterator - { - // Our current coordinate. - private int coord; - - private static final int START = 0; - private static final int END_PLUS_ONE = 5; - - public Iterator () - { - coord = START; - } - - public int currentSegment (double[] coords) - { - int r; - switch (coord) - { - case 0: - coords[0] = getX (); - coords[1] = getY (); - r = SEG_MOVETO; - break; - - case 1: - coords[0] = getX () + getWidth (); - coords[1] = getY (); - r = SEG_LINETO; - break; - - case 2: - coords[0] = getX () + getWidth (); - coords[1] = getY () + getHeight (); - r = SEG_LINETO; - break; - - case 3: - coords[0] = getX (); - coords[1] = getY () + getHeight (); - r = SEG_LINETO; - break; - - case 4: - r = SEG_CLOSE; - break; - - default: - // It isn't clear what to do if the caller calls us after - // isDone returns true. - r = SEG_CLOSE; - break; - } - - return r; - } - - public int currentSegment (float[] coords) - { - int r; - switch (coord) - { - case 0: - coords[0] = (float) getX (); - coords[1] = (float) getY (); - r = SEG_MOVETO; - break; - - case 1: - coords[0] = (float) (getX () + getWidth ()); - coords[1] = (float) getY (); - r = SEG_LINETO; - break; - - case 2: - coords[0] = (float) (getX () + getWidth ()); - coords[1] = (float) (getY () + getHeight ()); - r = SEG_LINETO; - break; - - case 3: - coords[0] = (float) getX (); - coords[1] = (float) (getY () + getHeight ()); - r = SEG_LINETO; - break; - - case 4: - default: - // It isn't clear what to do if the caller calls us after - // isDone returns true. We elect to keep returning - // SEG_CLOSE. - r = SEG_CLOSE; - break; - } - - return r; - } - - public int getWindingRule () - { - return WIND_NON_ZERO; - } - - public boolean isDone () - { - return coord == END_PLUS_ONE; - } - - public void next () - { - if (coord < END_PLUS_ONE) - ++coord; - } + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } } -} +} // class RectangularShape diff --git a/libjava/java/awt/geom/RoundRectangle2D.java b/libjava/java/awt/geom/RoundRectangle2D.java index b518568..ec7e461 100644 --- a/libjava/java/awt/geom/RoundRectangle2D.java +++ b/libjava/java/awt/geom/RoundRectangle2D.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2000, 2002 Free Software Foundation +/* RoundRectangle2D.java -- represents a rectangle with rounded corners + Copyright (C) 2000, 2002 Free Software Foundation This file is part of GNU Classpath. @@ -43,10 +44,10 @@ package java.awt.geom; public abstract class RoundRectangle2D extends RectangularShape { /** Return the arc height of this round rectangle. */ - public abstract double getArcHeight (); + public abstract double getArcHeight(); /** Return the arc width of this round rectangle. */ - public abstract double getArcWidth (); + public abstract double getArcWidth(); /** Set the values of this round rectangle * @param x The x coordinate @@ -56,13 +57,13 @@ public abstract class RoundRectangle2D extends RectangularShape * @param arcWidth The arc width * @param arcHeight The arc height */ - public abstract void setRoundRect (double x, double y, double w, double h, - double arcWidth, double arcHeight); + public abstract void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight); /** Create a RoundRectangle2D. This is protected because this class * is abstract and cannot be instantiated. */ - protected RoundRectangle2D () + protected RoundRectangle2D() { } @@ -70,22 +71,22 @@ public abstract class RoundRectangle2D extends RectangularShape * @param x The x coordinate * @param y The y coordinate */ - public boolean contains (double x, double y) + public boolean contains(double x, double y) { - double mx = getX (); - double mw = getWidth (); + double mx = getX(); + double mw = getWidth(); if (x < mx || x >= mx + mw) return false; - double my = getY (); - double mh = getHeight (); + double my = getY(); + double mh = getHeight(); if (y < my || y >= my + mh) return false; // Now check to see if the point is in range of an arc. - double dy = Math.min (Math.abs (my - y), Math.abs (my + mh - y)); - double dx = Math.min (Math.abs (mx - x), Math.abs (mx + mw - x)); - double aw = getArcWidth (); - double ah = getArcHeight (); + double dy = Math.min(Math.abs(my - y), Math.abs(my + mh - y)); + double dx = Math.min(Math.abs(mx - x), Math.abs(mx + mw - x)); + double aw = getArcWidth(); + double ah = getArcHeight(); if (dx > aw || dy > ah) return true; @@ -105,18 +106,18 @@ public abstract class RoundRectangle2D extends RectangularShape * @param w The width * @param h The height */ - public boolean contains (double x, double y, double w, double h) + public boolean contains(double x, double y, double w, double h) { // We have to check all four points here (for ordinary rectangles // we can just check opposing corners). - return (contains (x, y) && contains (x + w, h) - && contains (x, y + h) && contains (x + w, y + h)); + return (contains(x, y) && contains(x + w, h) + && contains(x, y + h) && contains(x + w, y + h)); } /** Return a new path iterator which iterates over this rectangle. * @param at An affine transform to apply to the object */ - public PathIterator getPathIterator (AffineTransform at) + public PathIterator getPathIterator(AffineTransform at) { // FIXME. return null; @@ -128,15 +129,15 @@ public abstract class RoundRectangle2D extends RectangularShape * @param w The width * @param h The height */ - public boolean intersects (double x, double y, double w, double h) + public boolean intersects(double x, double y, double w, double h) { // Here we can use the same code we use for an ordinary rectangle. - double mx = getX (); - double mw = getWidth (); + double mx = getX(); + double mw = getWidth(); if (x < mx || x >= mx + mw || x + w < mx || x + w >= mx + mw) return false; - double my = getY (); - double mh = getHeight (); + double my = getY(); + double mh = getHeight(); return y >= my && y < my + mh && y + h >= my && y + h < my + mh; } @@ -146,48 +147,47 @@ public abstract class RoundRectangle2D extends RectangularShape * @param w The width * @param h The height */ - public void setFrame (double x, double y, double w, double h) + public void setFrame(double x, double y, double w, double h) { // This is a bit lame. - setRoundRect (x, y, w, h, getArcWidth (), getArcHeight ()); + setRoundRect(x, y, w, h, getArcWidth(), getArcHeight()); } /** Set the values of this round rectangle to be the same as those * of the argument. * @param rr The round rectangle to copy */ - public void setRoundRect (RoundRectangle2D rr) + public void setRoundRect(RoundRectangle2D rr) { - setRoundRect (rr.getX (), rr.getY (), rr.getWidth (), rr.getHeight (), - rr.getArcWidth (), rr.getArcHeight ()); + setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(), + rr.getArcWidth(), rr.getArcHeight()); } /** A subclass of RoundRectangle which keeps its parameters as - * floats. */ - public static class Float extends RoundRectangle2D + * doubles. */ + public static class Double extends RoundRectangle2D { /** The height of the corner arc. */ - public float archeight; + public double archeight; /** The width of the corner arc. */ - public float arcwidth; + public double arcwidth; /** The x coordinate of this object. */ - public float x; + public double x; /** The y coordinate of this object. */ - public float y; + public double y; /** The width of this object. */ - public float width; + public double width; /** The height of this object. */ - public float height; + public double height; /** Construct a new instance, with all parameters set to 0. */ - public Float () + public Double() { - this (0, 0, 0, 0, 0, 0); } /** Construct a new instance with the given arguments. @@ -198,8 +198,8 @@ public abstract class RoundRectangle2D extends RectangularShape * @param arcWidth The arc width * @param arcHeight The arc height */ - public Float (float x, float y, float w, float h, - float arcWidth, float arcHeight) + public Double(double x, double y, double w, double h, + double arcWidth, double arcHeight) { this.x = x; this.y = y; @@ -209,48 +209,48 @@ public abstract class RoundRectangle2D extends RectangularShape this.archeight = arcHeight; } - public double getArcHeight () + public double getArcHeight() { return archeight; } - public double getArcWidth () + public double getArcWidth() { return arcwidth; } - public Rectangle2D getBounds2D () + public Rectangle2D getBounds2D() { - return new Rectangle2D.Float (x, y, width, height); + return new Rectangle2D.Double(x, y, width, height); } - public double getX () + public double getX() { return x; } - public double getY () + public double getY() { return y; } - public double getWidth () + public double getWidth() { return width; } - public double getHeight () + public double getHeight() { return height; } - public boolean isEmpty () + public boolean isEmpty() { return width <= 0 || height <= 0; } - public void setRoundRect (float x, float y, float w, float h, - float arcWidth, float arcHeight) + public void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight) { this.x = x; this.y = y; @@ -259,45 +259,33 @@ public abstract class RoundRectangle2D extends RectangularShape this.arcwidth = arcWidth; this.archeight = arcHeight; } - - public void setRoundRect (double x, double y, double w, double h, - double arcWidth, double arcHeight) - { - this.x = (float) x; - this.y = (float) y; - this.width = (float) w; - this.height = (float) h; - this.arcwidth = (float) arcWidth; - this.archeight = (float) arcHeight; - } - } + } // class Double /** A subclass of RoundRectangle which keeps its parameters as - * doubles. */ - public static class Double extends RoundRectangle2D + * floats. */ + public static class Float extends RoundRectangle2D { /** The height of the corner arc. */ - public double archeight; + public float archeight; /** The width of the corner arc. */ - public double arcwidth; + public float arcwidth; /** The x coordinate of this object. */ - public double x; + public float x; /** The y coordinate of this object. */ - public double y; + public float y; /** The width of this object. */ - public double width; + public float width; /** The height of this object. */ - public double height; + public float height; /** Construct a new instance, with all parameters set to 0. */ - public Double () + public Float() { - this (0, 0, 0, 0, 0, 0); } /** Construct a new instance with the given arguments. @@ -308,8 +296,8 @@ public abstract class RoundRectangle2D extends RectangularShape * @param arcWidth The arc width * @param arcHeight The arc height */ - public Double (double x, double y, double w, double h, - double arcWidth, double arcHeight) + public Float(float x, float y, float w, float h, + float arcWidth, float arcHeight) { this.x = x; this.y = y; @@ -319,48 +307,48 @@ public abstract class RoundRectangle2D extends RectangularShape this.archeight = arcHeight; } - public double getArcHeight () + public double getArcHeight() { return archeight; } - public double getArcWidth () + public double getArcWidth() { return arcwidth; } - public Rectangle2D getBounds2D () + public Rectangle2D getBounds2D() { - return new Rectangle2D.Double (x, y, width, height); + return new Rectangle2D.Float(x, y, width, height); } - public double getX () + public double getX() { return x; } - public double getY () + public double getY() { return y; } - public double getWidth () + public double getWidth() { return width; } - public double getHeight () + public double getHeight() { return height; } - public boolean isEmpty () + public boolean isEmpty() { return width <= 0 || height <= 0; } - public void setRoundRect (double x, double y, double w, double h, - double arcWidth, double arcHeight) + public void setRoundRect(float x, float y, float w, float h, + float arcWidth, float arcHeight) { this.x = x; this.y = y; @@ -369,5 +357,16 @@ public abstract class RoundRectangle2D extends RectangularShape this.arcwidth = arcWidth; this.archeight = arcHeight; } - } -} + + public void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight) + { + this.x = (float) x; + this.y = (float) y; + this.width = (float) w; + this.height = (float) h; + this.arcwidth = (float) arcWidth; + this.archeight = (float) arcHeight; + } + } // class Float +} // class RoundRectangle2D |