/* AffineTransform.java -- transform coordinates between two 2-D spaces Copyright (C) 2000, 2001, 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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.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. * *
The transformation can be represented using matrix math on a 3x3 array. * Given (x,y), the transformation (x',y') can be found by: *
* [ 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 ] ** The bottom row of the matrix is constant, so a transform can be uniquely * represented (as in {@link #toString()}) by * "[[m00, m01, m02], [m10, m11, m12]]". * * @author Tom Tromey (tromey@cygnus.com) * @author Eric Blake (ebb9@email.byu.edu) * @since 1.2 * @status partially updated to 1.4, still has some problems */ 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; /** * 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; /** * 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; /** * 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 shearing 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 scaling 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: *
* [ 1 0 0 ] * [ 0 1 0 ] * [ 0 0 1 ] **/ public AffineTransform() { m00 = m11 = 1; } /** * 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); } /** * Construct a transform with the given matrix entries: *
* [ m00 m01 m02 ] * [ m10 m11 m12 ] * [ 0 0 1 ] ** * @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; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; updateType(); } /** * 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: *
* [ f[0] f[2] (f[4]) ] * [ f[1] f[3] (f[5]) ] * [ 0 0 1 ] ** * @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 = f[0]; m10 = f[1]; m01 = f[2]; m11 = f[3]; if (f.length >= 6) { m02 = f[4]; m12 = f[5]; } updateType(); } /** * Construct a transform with the given matrix entries: *
* [ m00 m01 m02 ] * [ m10 m11 m12 ] * [ 0 0 1 ] ** * @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; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; updateType(); } /** * 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: *
* [ d[0] d[2] (d[4]) ] * [ d[1] d[3] (d[5]) ] * [ 0 0 1 ] ** * @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 = d[0]; m10 = d[1]; m01 = d[2]; m11 = d[3]; if (d.length >= 6) { m02 = d[4]; m12 = d[5]; } updateType(); } /** * Returns a translation transform: *
* [ 1 0 tx ] * [ 0 1 ty ] * [ 0 0 1 ] ** * @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.m02 = tx; t.m12 = ty; t.type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; return t; } /** * Returns a rotation transform. A positive angle (in radians) rotates * the positive x-axis to the positive y-axis: *
* [ cos(theta) -sin(theta) 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 0 1 ] ** * @param theta the rotation angle * @return the rotating transform */ public static AffineTransform getRotateInstance(double theta) { AffineTransform t = new AffineTransform(); t.setToRotation(theta); return t; } /** * 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: *
* AffineTransform tx = new AffineTransform(); * tx.setToTranslation(x, y); * tx.rotate(theta); * tx.translate(-x, -y); ** *
The resulting matrix is: *
* [ cos(theta) -sin(theta) x-x*cos+y*sin ] * [ sin(theta) cos(theta) y-x*sin-y*cos ] * [ 0 0 1 ] ** * @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.setToTranslation(x, y); t.rotate(theta); t.translate(-x, -y); return t; } /** * Returns a scaling transform: *
* [ sx 0 0 ] * [ 0 sy 0 ] * [ 0 0 1 ] ** * @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); return t; } /** * 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): *
* [ 1 shx 0 ] * [ shy 1 0 ] * [ 0 0 1 ] ** * @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); return t; } /** * 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. * * @return The type. * * @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; } /** * 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. * *
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: *
* | m00 m01 m02 | * | m10 m11 m12 | = m00 * m11 - m01 * m10 * | 0 0 1 | ** * @return the determinant * @see #createInverse() */ public double getDeterminant() { return m00 * m11 - m01 * m10; } /** * 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: *
* [ d[0] d[2] (d[4]) ] * [ d[1] d[3] (d[5]) ] * [ 0 0 1 ] ** * @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) { d[0] = m00; d[1] = m10; d[2] = m01; d[3] = m11; if (d.length >= 6) { d[4] = m02; d[5] = m12; } } /** * Returns the X coordinate scaling factor of the matrix. * * @return m00 * @see #getMatrix(double[]) */ public double getScaleX() { return m00; } /** * Returns the Y coordinate scaling factor of the matrix. * * @return m11 * @see #getMatrix(double[]) */ public double getScaleY() { return m11; } /** * Returns the X coordinate shearing factor of the matrix. * * @return m01 * @see #getMatrix(double[]) */ public double getShearX() { return m01; } /** * Returns the Y coordinate shearing factor of the matrix. * * @return m10 * @see #getMatrix(double[]) */ public double getShearY() { return m10; } /** * Returns the X coordinate translation factor of the matrix. * * @return m02 * @see #getMatrix(double[]) */ public double getTranslateX() { return m02; } /** * Returns the Y coordinate translation factor of the matrix. * * @return m12 * @see #getMatrix(double[]) */ public double getTranslateY() { return m12; } /** * Concatenate a translation onto this transform. This is equivalent, but * more efficient than *
concatenate(AffineTransform.getTranslateInstance(tx, ty))
.
*
* @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();
}
/**
* Concatenate a rotation onto this transform. This is equivalent, but
* more efficient than
* concatenate(AffineTransform.getRotateInstance(theta))
.
*
* @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 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();
}
/**
* Concatenate a rotation about a point onto this transform. This is
* equivalent, but more efficient than
* concatenate(AffineTransform.getRotateInstance(theta, x, y))
.
*
* @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);
}
/**
* Concatenate a scale onto this transform. This is equivalent, but more
* efficient than
* concatenate(AffineTransform.getScaleInstance(sx, sy))
.
*
* @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();
}
/**
* Concatenate a shearing onto this transform. This is equivalent, but more
* efficient than
* concatenate(AffineTransform.getShearInstance(sx, sy))
.
*
* @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 + (shy * m01);
double n01 = m01 + (shx * m00);
double n10 = m10 + (shy * m11);
double n11 = m11 + (shx * m10);
m00 = n00;
m01 = n01;
m10 = n10;
m11 = n11;
updateType();
}
/**
* Reset this transform to the identity (no transformation):
* * [ 1 0 0 ] * [ 0 1 0 ] * [ 0 0 1 ] **/ public void setToIdentity() { m00 = m11 = 1; m01 = m02 = m10 = m12 = 0; type = TYPE_IDENTITY; } /** * Set this transform to a translation: *
* [ 1 0 tx ] * [ 0 1 ty ] * [ 0 0 1 ] ** * @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 = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; } /** * Set this transform to a rotation. A positive angle (in radians) rotates * the positive x-axis to the positive y-axis: *
* [ cos(theta) -sin(theta) 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 0 1 ] ** * @param theta the rotation angle */ public void setToRotation(double theta) { double c = Math.cos(theta); double s = Math.sin(theta); m00 = c; m01 = -s; m02 = 0; m10 = s; m11 = c; m12 = 0; type = (c == 1 ? TYPE_IDENTITY : c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION); } /** * 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: *
* tx.setToTranslation(x, y); * tx.rotate(theta); * tx.translate(-x, -y); ** *
The resulting matrix is: *
* [ cos(theta) -sin(theta) x-x*cos+y*sin ] * [ sin(theta) cos(theta) y-x*sin-y*cos ] * [ 0 0 1 ] ** * @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); m00 = c; m01 = -s; m02 = x - x * c + y * s; m10 = s; m11 = c; m12 = y - x * s - y * c; updateType(); } /** * Set this transform to a scale: *
* [ sx 0 0 ] * [ 0 sy 0 ] * [ 0 0 1 ] ** * @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_GENERAL_SCALE : sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE); } /** * 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): *
* [ 1 shx 0 ] * [ shy 1 0 ] * [ 0 0 1 ] ** * @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; updateType(); } /** * 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; m02 = tx.m02; m10 = tx.m10; m11 = tx.m11; m12 = tx.m12; type = tx.type; } /** * Set this transform to the given values: *
* [ m00 m01 m02 ] * [ m10 m11 m12 ] * [ 0 0 1 ] ** * @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; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; updateType(); } /** * 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: *
* [ this ] = [ this ] x [ tx ] ** * @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; double n02 = m00 * tx.m02 + m01 * tx.m12 + m02; double n10 = m10 * tx.m00 + m11 * tx.m10; double n11 = m10 * tx.m01 + m11 * tx.m11; double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; m00 = n00; m01 = n01; m02 = n02; m10 = n10; m11 = n11; m12 = n12; updateType(); } /** * 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: *
* [ this ] = [ tx ] x [ this ] ** * @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; double n02 = tx.m00 * m02 + tx.m01 * m12 + tx.m02; double n10 = tx.m10 * m00 + tx.m11 * m10; double n11 = tx.m10 * m01 + tx.m11 * m11; double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; m00 = n00; m01 = n01; m02 = n02; m10 = n10; m11 = n11; m12 = n12; updateType(); } /** * 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. * * The inverse is calculated as: * *
* * Let A be the matrix for which we want to find the inverse: * * A = [ m00 m01 m02 ] * [ m10 m11 m12 ] * [ 0 0 1 ] * * * 1 * inverse (A) = --- x adjoint(A) * det * * * * = 1 [ m11 -m01 m01*m12-m02*m11 ] * --- x [ -m10 m00 -m00*m12+m10*m02 ] * det [ 0 0 m00*m11-m10*m01 ] * * * * = [ m11/det -m01/det m01*m12-m02*m11/det ] * [ -m10/det m00/det -m00*m12+m10*m02/det ] * [ 0 0 1 ] * * ** * * * @return a new inverse transform * @throws NoninvertibleTransformException if inversion is not possible * @see #getDeterminant() */ public AffineTransform createInverse() throws NoninvertibleTransformException { double det = getDeterminant(); if (det == 0) throw new NoninvertibleTransformException("can't invert transform"); double im00 = m11 / det; double im10 = -m10 / det; double im01 = -m01 / det; double im11 = m00 / det; double im02 = (m01 * m12 - m02 * m11) / det; double im12 = (-m00 * m12 + m10 * m02) / det; return new AffineTransform (im00, im10, im01, im11, im02, im12); } /** * 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(); double x = src.getX(); double y = src.getY(); double nx = m00 * x + m01 * y + m02; double ny = m10 * x + m11 * y + m12; dst.setLocation(nx, ny); return dst; } /** * 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++]); } /** * 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 srcPts the array of source points * @param srcOff the starting offset into src * @param dstPts 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) { if (srcPts == dstPts && dstOff > srcOff && num > 1 && srcOff + 2 * num > dstOff) { 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 + m11 * y + m12); } } /** * 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 srcPts the array of source points * @param srcOff the starting offset into src * @param dstPts 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) { 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++]; dstPts[dstOff++] = m00 * x + m01 * y + m02; dstPts[dstOff++] = m10 * x + m11 * y + m12; } } /** * 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 srcPts the array of source points * @param srcOff the starting offset into src * @param dstPts 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) { float x = srcPts[srcOff++]; float y = srcPts[srcOff++]; dstPts[dstOff++] = m00 * x + m01 * y + m02; dstPts[dstOff++] = m10 * x + m11 * y + m12; } } /** * 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 srcPts the array of source points * @param srcOff the starting offset into src * @param dstPts 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) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); } } /** * 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 { return createInverse().transform(src, dst); } /** * 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 srcPts the array of source points * @param srcOff the starting offset into src * @param dstPts 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 { createInverse().transform(srcPts, srcOff, dstPts, dstOff, num); } /** * 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: *
* [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] ** * @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(); double nx = m00 * x + m01 * y; double ny = m10 * x + m11 * y; dst.setLocation(nx, ny); return dst; } /** * 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: *
* [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] ** * @param srcPts the array of source points * @param srcOff the starting offset into src * @param dstPts 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) { 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++]; dstPts[dstOff++] = m00 * x + m01 * y; dstPts[dstOff++] = m10 * x + m11 * y; } } /** * 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,
null
if src is
* null
.
* @see GeneralPath#transform(AffineTransform)
*/
public Shape createTransformedShape(Shape src)
{
if(src == null)
return null;
GeneralPath p = new GeneralPath(src);
p.transform(this);
return p;
}
/**
* Returns a string representation of the transform, in the format:
* "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], ["
* + m10 + ", " + m11 + ", " + m12 + "]]"
.
*
* @return the string representation
*/
public String toString()
{
return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], ["
+ m10 + ", " + m11 + ", " + m12 + "]]";
}
/**
* Tests if this transformation is the identity:
* * [ 1 0 0 ] * [ 0 1 0 ] * [ 0 0 1 ] ** * @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); } /** * Create a new transform of the same run-time type, with the same * transforming properties as this one. * * @return the clone */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw (Error) new InternalError().initCause(e); // Impossible } } /** * Return the hashcode for this transformation. The formula is not * documented, but appears to be the same as: *
* long l = Double.doubleToLongBits(getScaleX()); * l = l * 31 + Double.doubleToLongBits(getShearX()); * l = l * 31 + Double.doubleToLongBits(getTranslateX()); * l = l * 31 + Double.doubleToLongBits(getShearY()); * l = l * 31 + Double.doubleToLongBits(getScaleY()); * l = l * 31 + Double.doubleToLongBits(getTranslateY()); * return (int) ((l >> 32) ^ l); ** * @return the hashcode */ public int hashCode() { long l = Double.doubleToLongBits(m00); l = l * 31 + Double.doubleToLongBits(m01); l = l * 31 + Double.doubleToLongBits(m02); l = l * 31 + Double.doubleToLongBits(m10); l = l * 31 + Double.doubleToLongBits(m11); l = l * 31 + Double.doubleToLongBits(m12); return (int) ((l >> 32) ^ l); } /** * Compares two transforms for equality. This returns true if they have the * same matrix values. * * @param obj 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); } /** * 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 X-Content-Type-Options: nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 40279 Content-Disposition: inline; filename="Arc2D.java" Last-Modified: Thu, 01 May 2025 00:06:13 GMT Expires: Thu, 01 May 2025 00:11:13 GMT ETag: "928c5cfc8997ed97f196b4e1888b6a15398e396a" /* Arc2D.java -- represents an arc in 2-D space Copyright (C) 2002, 2003, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) * @author Sven de Marothy (sven@physto.se) * @since 1.2 */ 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. This is package-private to avoid an * accessor method. */ 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 = Math.toRadians(getAngleStart()); double rx = getWidth() / 2; double ry = getHeight() / 2; double x = getX() + rx + rx * Math.cos(angle); double y = getY() + ry - ry * Math.sin(angle); return new Point2D.Double(x, y); } /** * Returns the ending point of the arc. * * @return the end point */ public Point2D getEndPoint() { double angle = Math.toRadians(getAngleStart() + getAngleExtent()); double rx = getWidth() / 2; double ry = getHeight() / 2; double x = getX() + rx + rx * Math.cos(angle); double y = getY() + ry - ry * Math.sin(angle); 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 upper left of the bounding box * @param y the new y coordinate of the upper 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 upper 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) { if ((p2.getX() - p1.getX()) * (p3.getY() - p1.getY()) - (p3.getX() - p1.getX()) * (p2.getY() - p1.getY()) > 0) { Point2D p = p3; p3 = p1; p1 = p; } // normalized tangent vectors double dx1 = (p1.getX() - p2.getX()) / p1.distance(p2); double dy1 = (p1.getY() - p2.getY()) / p1.distance(p2); double dx2 = (p2.getX() - p3.getX()) / p3.distance(p2); double dy2 = (p2.getY() - p3.getY()) / p3.distance(p2); double theta1 = Math.atan2(dx1, dy1); double theta2 = Math.atan2(dx2, dy2); double dx = r * Math.cos(theta2) - r * Math.cos(theta1); double dy = -r * Math.sin(theta2) + r * Math.sin(theta1); if (theta1 < 0) theta1 += 2 * Math.PI; if (theta2 < 0) theta2 += 2 * Math.PI; if (theta2 < theta1) theta2 += 2 * Math.PI; // Vectors of the lines, not normalized, note we change // the direction of line 2. dx1 = p1.getX() - p2.getX(); dy1 = p1.getY() - p2.getY(); dx2 = p3.getX() - p2.getX(); dy2 = p3.getY() - p2.getY(); // Calculate the tangent point to the second line double t2 = -(dx1 * dy - dy1 * dx) / (dx2 * dy1 - dx1 * dy2); double x2 = t2 * (p3.getX() - p2.getX()) + p2.getX(); double y2 = t2 * (p3.getY() - p2.getY()) + p2.getY(); // calculate the center point double x = x2 - r * Math.cos(theta2); double y = y2 + r * Math.sin(theta2); setArc(x - r, y - r, 2 * r, 2 * r, Math.toDegrees(theta1), Math.toDegrees(theta2 - theta1), getArcType()); } /** * 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) { // Normalize. double x = p.getX() - (getX() + getWidth() / 2); double y = p.getY() - (getY() + getHeight() / 2); setAngleStart(Math.toDegrees(Math.atan2(-y, x))); } /** * 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 - (mx + mw / 2); y1 = y1 - (my + mh / 2); x2 = x2 - (mx + mw / 2); y2 = y2 - (my + mh / 2); double start = Math.toDegrees(Math.atan2(-y1, x1)); double extent = Math.toDegrees(Math.atan2(-y2, x2)) - 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 *
getBounds
, 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());
// Find the minimal bounding box. This determined by its extrema,
// which are the center, the endpoints of the arc, and any local
// maximum contained by the arc.
double rX = getWidth() / 2;
double rY = getHeight() / 2;
double centerX = getX() + rX;
double centerY = getY() + rY;
Point2D p1 = getStartPoint();
Rectangle2D result = makeBounds(p1.getX(), p1.getY(), 0, 0);
result.add(getEndPoint());
if (type == PIE)
result.add(centerX, centerY);
if (containsAngle(0))
result.add(centerX + rX, centerY);
if (containsAngle(90))
result.add(centerX, centerY - rY);
if (containsAngle(180))
result.add(centerX - rX, centerY);
if (containsAngle(270))
result.add(centerX, centerY + rY);
return result;
}
/**
* 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.
* All angles are normalized to be between 0 and 360 degrees.
*
* @param a the angle to test
* @return true if it is contained
*/
public boolean containsAngle(double a)
{
double start = getAngleStart();
double extent = getAngleExtent();
double end = start + extent;
if (extent == 0)
return false;
if (extent >= 360 || extent <= -360)
return true;
if (extent < 0)
{
end = start;
start += extent;
}
start %= 360;
while (start < 0)
start += 360;
end %= 360;
while (end < start)
end += 360;
a %= 360;
while (a < start)
a += 360;
return a >= start && a < end; // starting angle included, ending angle not
}
/**
* Determines if the arc contains the given point. If the bounding box
* is empty, then this will return false.
*
* The area considered 'inside' an arc of type OPEN is the same as the
* area inside an equivalent filled CHORD-type arc. The area considered
* 'inside' a CHORD-type arc is the same as the filled area.
*
* @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();
double extent = getAngleExtent();
if (w <= 0 || h <= 0 || extent == 0)
return false;
double mx = getX() + w / 2;
double my = getY() + h / 2;
double dx = (x - mx) * 2 / w;
double dy = (y - my) * 2 / h;
if ((dx * dx + dy * dy) >= 1.0)
return false;
double angle = Math.toDegrees(Math.atan2(-dy, dx));
if (getArcType() == PIE)
return containsAngle(angle);
double a1 = Math.toRadians(getAngleStart());
double a2 = Math.toRadians(getAngleStart() + extent);
double x1 = mx + getWidth() * Math.cos(a1) / 2;
double y1 = my - getHeight() * Math.sin(a1) / 2;
double x2 = mx + getWidth() * Math.cos(a2) / 2;
double y2 = my - getHeight() * Math.sin(a2) / 2;
double sgn = ((x2 - x1) * (my - y1) - (mx - x1) * (y2 - y1)) * ((x2 - x1) * (y
- y1) - (x - x1) * (y2 - y1));
if (Math.abs(extent) > 180)
{
if (containsAngle(angle))
return true;
return sgn > 0;
}
else
{
if (! containsAngle(angle))
return false;
return sgn < 0;
}
}
/**
* Tests if a given rectangle intersects the area of the arc.
*
* For a definition of the 'inside' area, see the contains() method.
* @see #contains(double, double)
*
* @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 extent = getAngleExtent();
if (extent == 0)
return false;
if (contains(x, y) || contains(x, y + h) || contains(x + w, y)
|| contains(x + w, y + h))
return true;
Rectangle2D rect = new Rectangle2D.Double(x, y, w, h);
double a = getWidth() / 2.0;
double b = getHeight() / 2.0;
double mx = getX() + a;
double my = getY() + b;
double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart()));
double y1 = my - b * Math.sin(Math.toRadians(getAngleStart()));
double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent));
double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent));
if (getArcType() != CHORD)
{
// check intersections against the pie radii
if (rect.intersectsLine(mx, my, x1, y1))
return true;
if (rect.intersectsLine(mx, my, x2, y2))
return true;
}
else// check the chord
if (rect.intersectsLine(x1, y1, x2, y2))
return true;
// Check the Arc segment against the four edges
double dx;
// Check the Arc segment against the four edges
double dy;
dy = y - my;
dx = a * Math.sqrt(1 - ((dy * dy) / (b * b)));
if (! java.lang.Double.isNaN(dx))
{
if (mx + dx >= x && mx + dx <= x + w
&& containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
return true;
if (mx - dx >= x && mx - dx <= x + w
&& containsAngle(Math.toDegrees(Math.atan2(-dy, -dx))))
return true;
}
dy = (y + h) - my;
dx = a * Math.sqrt(1 - ((dy * dy) / (b * b)));
if (! java.lang.Double.isNaN(dx))
{
if (mx + dx >= x && mx + dx <= x + w
&& containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
return true;
if (mx - dx >= x && mx - dx <= x + w
&& containsAngle(Math.toDegrees(Math.atan2(-dy, -dx))))
return true;
}
dx = x - mx;
dy = b * Math.sqrt(1 - ((dx * dx) / (a * a)));
if (! java.lang.Double.isNaN(dy))
{
if (my + dy >= y && my + dy <= y + h
&& containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
return true;
if (my - dy >= y && my - dy <= y + h
&& containsAngle(Math.toDegrees(Math.atan2(dy, dx))))
return true;
}
dx = (x + w) - mx;
dy = b * Math.sqrt(1 - ((dx * dx) / (a * a)));
if (! java.lang.Double.isNaN(dy))
{
if (my + dy >= y && my + dy <= y + h
&& containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
return true;
if (my - dy >= y && my - dy <= y + h
&& containsAngle(Math.toDegrees(Math.atan2(dy, dx))))
return true;
}
// Check whether the arc is contained within the box
if (rect.contains(mx, my))
return true;
return false;
}
/**
* 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 extent = getAngleExtent();
if (extent == 0)
return false;
if (! (contains(x, y) && contains(x, y + h) && contains(x + w, y)
&& contains(x + w, y + h)))
return false;
Rectangle2D rect = new Rectangle2D.Double(x, y, w, h);
double a = getWidth() / 2.0;
double b = getHeight() / 2.0;
double mx = getX() + a;
double my = getY() + b;
double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart()));
double y1 = my - b * Math.sin(Math.toRadians(getAngleStart()));
double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent));
double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent));
if (getArcType() != CHORD)
{
// check intersections against the pie radii
if (rect.intersectsLine(mx, my, x1, y1))
return false;
if (rect.intersectsLine(mx, my, x2, y2))
return false;
}
else if (rect.intersectsLine(x1, y1, x2, y2))
return false;
return true;
}
/**
* 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
*/
public ArcIterator(Arc2D a, AffineTransform xform)
{
this.xform = xform;
x = a.getX();
y = a.getY();
w = a.getWidth();
h = a.getHeight();
double start = Math.toRadians(a.getAngleStart());
double extent = Math.toRadians(a.getAngleExtent());
this.start = start;
this.extent = extent;
type = a.type;
if (w < 0 || h < 0)
limit = -1;
else if (extent == 0)
limit = type;
else if (Math.abs(extent) <= Math.PI / 2.0)
limit = type + 1;
else if (Math.abs(extent) <= Math.PI)
limit = type + 2;
else if (Math.abs(extent) <= 3.0 * (Math.PI / 2.0))
limit = type + 3;
else
limit = type + 4;
}
/**
* Construct a new iterator over an ellipse.
*
* @param e the ellipse
* @param xform the transform
*/
public 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)
{
double[] double_coords = new double[6];
int code = currentSegment(double_coords);
for (int i = 0; i < 6; ++i)
coords[i] = (float) double_coords[i];
return code;
}
/**
* 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)
{
double rx = w / 2;
double ry = h / 2;
double xmid = x + rx;
double ymid = y + ry;
if (current > limit)
throw new NoSuchElementException("arc iterator out of bounds");
if (current == 0)
{
coords[0] = xmid + rx * Math.cos(start);
coords[1] = ymid - ry * Math.sin(start);
if (xform != null)
xform.transform(coords, 0, coords, 0, 1);
return SEG_MOVETO;
}
if (type != OPEN && current == limit)
return SEG_CLOSE;
if ((current == limit - 1) && (type == PIE))
{
coords[0] = xmid;
coords[1] = ymid;
if (xform != null)
xform.transform(coords, 0, coords, 0, 1);
return SEG_LINETO;
}
// note that this produces a cubic approximation of the arc segment,
// not a true ellipsoid. there's no ellipsoid path segment code,
// unfortunately. the cubic approximation looks about right, though.
double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0);
double quad = (Math.PI / 2.0);
double curr_begin;
double curr_extent;
if (extent > 0)
{
curr_begin = start + (current - 1) * quad;
curr_extent = Math.min((start + extent) - curr_begin, quad);
}
else
{
curr_begin = start - (current - 1) * quad;
curr_extent = Math.max((start + extent) - curr_begin, -quad);
}
double portion_of_a_quadrant = Math.abs(curr_extent / quad);
double x0 = xmid + rx * Math.cos(curr_begin);
double y0 = ymid - ry * Math.sin(curr_begin);
double x1 = xmid + rx * Math.cos(curr_begin + curr_extent);
double y1 = ymid - ry * Math.sin(curr_begin + curr_extent);
AffineTransform trans = new AffineTransform();
double[] cvec = new double[2];
double len = kappa * portion_of_a_quadrant;
double angle = curr_begin;
// in a hypothetical "first quadrant" setting, our first control
// vector would be sticking up, from [1,0] to [1,kappa].
//
// let us recall however that in java2d, y coords are upside down
// from what one would consider "normal" first quadrant rules, so we
// will *subtract* the y value of this control vector from our first
// point.
cvec[0] = 0;
if (extent > 0)
cvec[1] = len;
else
cvec[1] = -len;
trans.scale(rx, ry);
trans.rotate(angle);
trans.transform(cvec, 0, cvec, 0, 1);
coords[0] = x0 + cvec[0];
coords[1] = y0 - cvec[1];
// control vector #2 would, ideally, be sticking out and to the
// right, in a first quadrant arc segment. again, subtraction of y.
cvec[0] = 0;
if (extent > 0)
cvec[1] = -len;
else
cvec[1] = len;
trans.rotate(curr_extent);
trans.transform(cvec, 0, cvec, 0, 1);
coords[2] = x1 + cvec[0];
coords[3] = y1 - cvec[1];
// end point
coords[4] = x1;
coords[5] = y1;
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 extent 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 extent 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
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 89471
Content-Disposition: inline; filename="Area.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "51f914f288f8e2d6575f995c9fd153a5204930d7"
/* Area.java -- represents a shape built by constructive area geometry
Copyright (C) 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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.Vector;
/**
* The Area class represents any area for the purpose of
* Constructive Area Geometry (CAG) manipulations. CAG manipulations
* work as an area-wise form of boolean logic, where the basic operations are:
* * * The contains and intersects() methods are also more accurate than the * specification of #Shape requires.
* * Please note that constructing an Area can be slow * (Self-intersection resolving is proportional to the square of * the number of segments).
* @see #add(Area)
* @see #subtract(Area)
* @see #intersect(Area)
* @see #exclusiveOr(Area)
*
* @author Sven de Marothy (sven@physto.se)
*
* @since 1.2
* @status Works, but could be faster and more reliable.
*/
public class Area implements Shape, Cloneable
{
/**
* General numerical precision
*/
private static final double EPSILON = 1E-11;
/**
* recursive subdivision epsilon - (see getRecursionDepth)
*/
private static final double RS_EPSILON = 1E-13;
/**
* Snap distance - points within this distance are considered equal
*/
private static final double PE_EPSILON = 1E-11;
/**
* Segment vectors containing solid areas and holes
* This is package-private to avoid an accessor method.
*/
Vector
*
* If the Shape is self-intersecting, the created Area will consist
* of non-self-intersecting subpaths, and any inner paths which
* are found redundant in accordance with the Shape's winding rule
* will not be included.
*
* @param s the shape (
*
* This is strictly qualified. An area is considered rectangular if:
* @return true if the above criteria are met, false otherwise
*/
public boolean isRectangular()
{
if (isEmpty())
return true;
if (holes.size() != 0 || solids.size() != 1)
return false;
Segment path = solids.elementAt(0);
if (! path.isPolygonal())
return false;
int nCorners = 0;
Segment s = path;
do
{
Segment s2 = s.next;
double d1 = (s.P2.getX() - s.P1.getX())*(s2.P2.getX() - s2.P1.getX())/
((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2)));
double d2 = (s.P2.getY() - s.P1.getY())*(s2.P2.getY() - s2.P1.getY())/
((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2)));
double dotproduct = d1 + d2;
// For some reason, only rectangles on the XY axis count.
if (d1 != 0 && d2 != 0)
return false;
if (Math.abs(dotproduct) == 0) // 90 degree angle
nCorners++;
else if ((Math.abs(1.0 - dotproduct) > 0)) // 0 degree angle?
return false; // if not, return false
s = s.next;
}
while (s != path);
return nCorners == 4;
}
/**
* Returns whether the Area consists of more than one simple
* (non self-intersecting) subpath.
*
* @return true if the Area consists of none or one simple subpath,
* false otherwise.
*/
public boolean isSingular()
{
return (holes.size() == 0 && solids.size() <= 1);
}
/**
* Returns the bounding box of the Area. Unlike the CubicCurve2D and
* QuadraticCurve2D classes, this method will return the tightest possible
* bounding box, evaluating the extreme points of each curved segment.
* @return the bounding box
*/
public Rectangle2D getBounds2D()
{
if (solids.size() == 0)
return new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0);
double xmin;
double xmax;
double ymin;
double ymax;
xmin = xmax = solids.elementAt(0).P1.getX();
ymin = ymax = solids.elementAt(0).P1.getY();
for (int path = 0; path < solids.size(); path++)
{
Rectangle2D r = solids.elementAt(path).getPathBounds();
xmin = Math.min(r.getMinX(), xmin);
ymin = Math.min(r.getMinY(), ymin);
xmax = Math.max(r.getMaxX(), xmax);
ymax = Math.max(r.getMaxY(), ymax);
}
return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin)));
}
/**
* Returns the bounds of this object in Rectangle format.
* Please note that this may lead to loss of precision.
*
* @return The bounds.
* @see #getBounds2D()
*/
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
{
Area clone = new Area();
for (int i = 0; i < solids.size(); i++)
clone.solids.add(solids.elementAt(i).cloneSegmentList());
for (int i = 0; i < holes.size(); i++)
clone.holes.add(holes.elementAt(i).cloneSegmentList());
return clone;
}
catch (CloneNotSupportedException e)
{
throw (Error) new InternalError().initCause(e); // Impossible
}
}
/**
* Compares two Areas.
*
* @param area the area to compare against this area (
*
* This method should always produce the correct results, unlike for other
* classes in geom.
*
* @param x the x-coordinate of the rectangle.
* @param y the y-coordinate of the rectangle.
* @param w the width of the the rectangle.
* @param h the height of the rectangle.
* @return
*
* This method should always produce the correct results, unlike for other
* classes in geom.
*
* @param r the rectangle.
* @return The curve does not keep any reference to the passed point
* objects. Therefore, a later change to The curve does not keep references to the passed point
* objects. Therefore, a later change to the In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. In comparison to C1,
* control point C2 is father away from the gray line. Therefore,
* the result will be the square of the distance between C2 and the
* gray line, i.e. the squared length of the red line.
*
* @param x1 the x coordinate of the start point P1.
* @param y1 the y coordinate of the start point P1.
* @param cx1 the x coordinate of the first control point C1.
* @param cy1 the y coordinate of the first control point C1.
* @param cx2 the x coordinate of the second control point C2.
* @param cy2 the y coordinate of the second control point C2.
* @param x2 the x coordinate of the end point P2.
* @param y2 the y coordinate of the end point P2.
*/
public static double getFlatnessSq(double x1, double y1, double cx1,
double cy1, double cx2, double cy2,
double x2, double y2)
{
return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, cx1, cy1),
Line2D.ptSegDistSq(x1, y1, x2, y2, cx2, cy2));
}
/**
* Calculates the flatness of a cubic curve, directly specifying
* each coordinate value. The flatness is the maximal distance of a
* control point to the line between start and end point.
*
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. In comparison to C1,
* control point C2 is father away from the gray line. Therefore,
* the result will be the distance between C2 and the gray line,
* i.e. the length of the red line.
*
* @param x1 the x coordinate of the start point P1.
* @param y1 the y coordinate of the start point P1.
* @param cx1 the x coordinate of the first control point C1.
* @param cy1 the y coordinate of the first control point C1.
* @param cx2 the x coordinate of the second control point C2.
* @param cy2 the y coordinate of the second control point C2.
* @param x2 the x coordinate of the end point P2.
* @param y2 the y coordinate of the end point P2.
*/
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));
}
/**
* Calculates the squared flatness of a cubic curve, specifying the
* coordinate values in an array. The flatness is the maximal
* distance of a control point to the line between start and end
* point.
*
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. In comparison to C1,
* control point C2 is father away from the gray line. Therefore,
* the result will be the square of the distance between C2 and the
* gray line, i.e. the squared length of the red line.
*
* @param coords an array containing the coordinate values. The
* x coordinate of the start point P1 is located at
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. In comparison to C1,
* control point C2 is father away from the gray line. Therefore,
* the result will be the distance between C2 and the gray line,
* i.e. the length of the red line.
*
* @param coords an array containing the coordinate values. The
* x coordinate of the start point P1 is located at
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. In comparison to C1,
* control point C2 is father away from the gray line. Therefore,
* the result will be the square of the distance between C2 and the
* gray line, i.e. the squared length of the red line.
*/
public double getFlatnessSq()
{
return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(),
getCtrlX2(), getCtrlY2(), getX2(), getY2());
}
/**
* Calculates the flatness of this curve. The flatness is the
* maximal distance of a control point to the line between start and
* end point.
*
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. In comparison to C1,
* control point C2 is father away from the gray line. Therefore,
* the result will be the distance between C2 and the gray line,
* i.e. the length of the red line.
*/
public double getFlatness()
{
return Math.sqrt(getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(),
getCtrlX2(), getCtrlY2(), getX2(), getY2()));
}
/**
* Subdivides this curve into two halves.
*
* The left end point and the right start point will always be
* identical. Memory-concious programmers thus may want to pass the
* same array for both For some background about solving cubic equations, see the
* article “Cubic Formula” in PlanetMath. For an extensive
* library of numerical algorithms written in the C programming
* language, see the GNU
* Scientific Library, from which this implementation was
* adapted.
*
* @param eqn an array with the coefficients of the equation. When
* this procedure has returned, For some background about solving cubic equations, see the
* article “Cubic Formula” in PlanetMath. For an extensive
* library of numerical algorithms written in the C programming
* language, see the GNU
* Scientific Library, from which this implementation was
* adapted.
*
* @see QuadCurve2D#solveQuadratic(double[],double[])
*
* @param eqn an array with the coefficients of the equation.
*
* @param res an array into which the non-complex roots will be
* stored. The results may be in an arbitrary order. It is safe to
* pass the same array object reference for both The above drawing illustrates in which area points are
* considered “inside” a CubicCurve2D.
*/
public boolean contains(double x, double y)
{
if (! getBounds2D().contains(x, y))
return false;
return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0);
}
/**
* Determines whether a point lies inside the area bounded
* by the curve and the straight line connecting its end points.
*
* The above drawing illustrates in which area points are
* considered “inside” a CubicCurve2D.
*/
public boolean contains(Point2D p)
{
return contains(p.getX(), p.getY());
}
/**
* Determines whether any part of a rectangle is inside the area bounded
* by the curve and the straight line connecting its end points.
*
* The above drawing illustrates in which area points are
* considered “inside” in a CubicCurve2D.
* @see #contains(double, double)
*/
public boolean intersects(double x, double y, double w, double h)
{
if (! getBounds2D().contains(x, y, w, h))
return false;
/* Does any edge intersect? */
if (getAxisIntersections(x, y, true, w) != 0 /* top */
|| getAxisIntersections(x, y + h, true, w) != 0 /* bottom */
|| getAxisIntersections(x + w, y, false, h) != 0 /* right */
|| getAxisIntersections(x, y, false, h) != 0) /* left */
return true;
/* No intersections, is any point inside? */
if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0)
return true;
return false;
}
/**
* Determines whether any part of a Rectangle2D is inside the area bounded
* by the curve and the straight line connecting its end points.
* @see #intersects(double, double, double, double)
*/
public boolean intersects(Rectangle2D r)
{
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Determine whether a rectangle is entirely inside the area that is bounded
* by the curve and the straight line connecting its end points.
*
* The above drawing illustrates in which area points are
* considered “inside” a CubicCurve2D.
* @see #contains(double, double)
*/
public boolean contains(double x, double y, double w, double h)
{
if (! getBounds2D().intersects(x, y, w, h))
return false;
/* Does any edge intersect? */
if (getAxisIntersections(x, y, true, w) != 0 /* top */
|| getAxisIntersections(x, y + h, true, w) != 0 /* bottom */
|| getAxisIntersections(x + w, y, false, h) != 0 /* right */
|| getAxisIntersections(x, y, false, h) != 0) /* left */
return false;
/* No intersections, is any point inside? */
if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0)
return true;
return false;
}
/**
* Determine whether a Rectangle2D is entirely inside the area that is
* bounded by the curve and the straight line connecting its end points.
*
* The above drawing illustrates in which area points are
* considered “inside” a CubicCurve2D.
* @see #contains(double, double)
*/
public boolean contains(Rectangle2D r)
{
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Determines the smallest rectangle that encloses the
* curve’s start, end and control points.
*/
public Rectangle getBounds()
{
return getBounds2D().getBounds();
}
public PathIterator getPathIterator(final AffineTransform at)
{
return new PathIterator()
{
/** Current coordinate. */
private int current = 0;
public int getWindingRule()
{
return WIND_NON_ZERO;
}
public boolean isDone()
{
return current >= 2;
}
public void next()
{
current++;
}
public int currentSegment(float[] coords)
{
int result;
switch (current)
{
case 0:
coords[0] = (float) getX1();
coords[1] = (float) getY1();
result = SEG_MOVETO;
break;
case 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();
result = SEG_CUBICTO;
break;
default:
throw new NoSuchElementException("cubic iterator out of bounds");
}
if (at != null)
at.transform(coords, 0, coords, 0, 3);
return result;
}
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] = getCtrlX1();
coords[1] = getCtrlY1();
coords[2] = getCtrlX2();
coords[3] = getCtrlY2();
coords[4] = getX2();
coords[5] = getY2();
result = SEG_CUBICTO;
break;
default:
throw new NoSuchElementException("cubic iterator out of bounds");
}
if (at != null)
at.transform(coords, 0, coords, 0, 3);
return result;
}
};
}
public PathIterator getPathIterator(AffineTransform at, double flatness)
{
return new FlatteningPathIterator(getPathIterator(at), flatness);
}
/**
* Create a new curve 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
}
}
/**
* Helper method used by contains() and intersects() methods, that
* returns the number of curve/line intersections on a given axis
* extending from a certain point.
*
* @param x x coordinate of the origin point
* @param y y coordinate of the origin point
* @param useYaxis axis used, if true the positive Y axis is used,
* false uses the positive X axis.
*
* This is an implementation of the line-crossings algorithm,
* Detailed in an article on Eric Haines' page:
* http://www.acm.org/tog/editors/erich/ptinpoly/
*
* A special-case not adressed in this code is self-intersections
* of the curve, e.g. if the axis intersects the self-itersection,
* the degenerate roots of the polynomial will erroneously count as
* a single intersection of the curve, and not two.
*/
private int getAxisIntersections(double x, double y, boolean useYaxis,
double distance)
{
int nCrossings = 0;
double a0;
double a1;
double a2;
double a3;
double b0;
double b1;
double b2;
double b3;
double[] r = new double[4];
int nRoots;
a0 = a3 = 0.0;
if (useYaxis)
{
a0 = getY1() - y;
a1 = getCtrlY1() - y;
a2 = getCtrlY2() - y;
a3 = getY2() - y;
b0 = getX1() - x;
b1 = getCtrlX1() - x;
b2 = getCtrlX2() - x;
b3 = getX2() - x;
}
else
{
a0 = getX1() - x;
a1 = getCtrlX1() - x;
a2 = getCtrlX2() - x;
a3 = getX2() - x;
b0 = getY1() - y;
b1 = getCtrlY1() - y;
b2 = getCtrlY2() - y;
b3 = getY2() - y;
}
/* If the axis intersects a start/endpoint, shift it up by some small
amount to guarantee the line is 'inside'
If this is not done, bad behaviour may result for points on that axis.*/
if (a0 == 0.0 || a3 == 0.0)
{
double small = getFlatness() * EPSILON;
if (a0 == 0.0)
a0 -= small;
if (a3 == 0.0)
a3 -= small;
}
if (useYaxis)
{
if (Line2D.linesIntersect(b0, a0, b3, a3, EPSILON, 0.0, distance, 0.0))
nCrossings++;
}
else
{
if (Line2D.linesIntersect(a0, b0, a3, b3, 0.0, EPSILON, 0.0, distance))
nCrossings++;
}
r[0] = a0;
r[1] = 3 * (a1 - a0);
r[2] = 3 * (a2 + a0 - 2 * a1);
r[3] = a3 - 3 * a2 + 3 * a1 - a0;
if ((nRoots = solveCubic(r)) != 0)
for (int i = 0; i < nRoots; i++)
{
double t = r[i];
if (t >= 0.0 && t <= 1.0)
{
double crossing = -(t * t * t) * (b0 - 3 * b1 + 3 * b2 - b3)
+ 3 * t * t * (b0 - 2 * b1 + b2)
+ 3 * t * (b1 - b0) + b0;
if (crossing > 0.0 && crossing <= distance)
nCrossings++;
}
}
return (nCrossings);
}
/**
* A two-dimensional curve that is parameterized with a cubic
* function and stores coordinate values in double-precision
* floating-point format.
*
* @see CubicCurve2D.Float
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Sascha Brawer (brawer@dandelis.ch)
*/
public static class Double extends CubicCurve2D
{
/**
* The x coordinate of the curve’s start point.
*/
public double x1;
/**
* The y coordinate of the curve’s start point.
*/
public double y1;
/**
* The x coordinate of the curve’s first control point.
*/
public double ctrlx1;
/**
* The y coordinate of the curve’s first control point.
*/
public double ctrly1;
/**
* The x coordinate of the curve’s second control point.
*/
public double ctrlx2;
/**
* The y coordinate of the curve’s second control point.
*/
public double ctrly2;
/**
* The x coordinate of the curve’s end point.
*/
public double x2;
/**
* The y coordinate of the curve’s end point.
*/
public double y2;
/**
* Constructs a new CubicCurve2D that stores its coordinate values
* in double-precision floating-point format. All points are
* initially at position (0, 0).
*/
public Double()
{
}
/**
* Constructs a new CubicCurve2D that stores its coordinate values
* in double-precision floating-point format, specifying the
* initial position of each point.
*
*
* @param x - x coordinate of the point.
* @param y - y coordinate of the point.
* @return true if the point is within the ellipse, false otherwise.
*/
public boolean contains(double x, double y)
{
double rx = getWidth() / 2;
double ry = getHeight() / 2;
double tx = (x - (getX() + rx)) / rx;
double ty = (y - (getY() + ry)) / ry;
return tx * tx + ty * ty < 1.0;
}
/**
* Determines if a rectangle is completely contained within the
* ellipse.
* @param x - x coordinate of the upper-left corner of the rectangle
* @param y - y coordinate of the upper-left corner of the rectangle
* @param w - width of the rectangle
* @param h - height of the rectangle
* @return true if the rectangle is completely contained, false otherwise.
*/
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));
}
/**
* Returns a PathIterator object corresponding to the ellipse.
*
* Note: An ellipse cannot be represented exactly in PathIterator
* segments, the outline is thefore approximated with cubic
* Bezier segments.
*
* @param at an optional transform.
* @return A path iterator.
*/
public PathIterator getPathIterator(AffineTransform at)
{
// An ellipse is just a complete arc.
return new Arc2D.ArcIterator(this, at);
}
/**
* Determines if a rectangle intersects any part of the ellipse.
* @param x - x coordinate of the upper-left corner of the rectangle
* @param y - y coordinate of the upper-left corner of the rectangle
* @param w - width of the rectangle
* @param h - height of the rectangle
* @return true if the rectangle intersects the ellipse, false otherwise.
*/
public boolean intersects(double x, double y, double w, double h)
{
Rectangle2D r = new Rectangle2D.Double(x, y, w, h);
if (! r.intersects(getX(), getY(), getWidth(), getHeight()))
return false;
if (contains(x, y) || contains(x, y + h) || contains(x + w, y)
|| contains(x + w, y + h))
return true;
Line2D l1 = new Line2D.Double(getX(), getY() + (getHeight() / 2),
getX() + getWidth(),
getY() + (getHeight() / 2));
Line2D l2 = new Line2D.Double(getX() + (getWidth() / 2), getY(),
getX() + (getWidth() / 2),
getY() + getHeight());
if (l1.intersects(r) || l2.intersects(r))
return true;
return false;
}
/**
* An {@link Ellipse2D} that stores its coordinates using
* @param x - x coordinate of the upper-left of the bounding rectangle
* @param y - y coordinate of the upper-left of the bounding rectangle
* @param w - width of the ellipse
* @param h - height of the ellipse
*/
public Double(double x, double y, double w, double h)
{
this.x = x;
this.y = y;
height = h;
width = w;
}
/**
* Returns the bounding-box of the ellipse.
* @return The bounding box.
*/
public Rectangle2D getBounds2D()
{
return new Rectangle2D.Double(x, y, width, height);
}
/**
* Returns the height of the ellipse.
* @return The height of the ellipse.
*/
public double getHeight()
{
return height;
}
/**
* Returns the width of the ellipse.
* @return The width of the ellipse.
*/
public double getWidth()
{
return width;
}
/**
* Returns x coordinate of the upper-left corner of
* the ellipse's bounding-box.
* @return The x coordinate.
*/
public double getX()
{
return x;
}
/**
* Returns y coordinate of the upper-left corner of
* the ellipse's bounding-box.
* @return The y coordinate.
*/
public double getY()
{
return y;
}
/**
* Returns
*
* @param x - x coordinate of the upper-left of the bounding rectangle
* @param y - y coordinate of the upper-left of the bounding rectangle
* @param w - width of the ellipse
* @param h - height of the ellipse
*/
public void setFrame(double x, double y, double w, double h)
{
this.x = x;
this.y = y;
height = h;
width = w;
}
} // class Double
/**
* An {@link Ellipse2D} that stores its coordinates using
* @param x - x coordinate of the upper-left of the bounding rectangle
* @param y - y coordinate of the upper-left of the bounding rectangle
* @param w - width of the ellipse
* @param h - height of the ellipse
*
*/
public Float(float x, float y, float w, float h)
{
this.x = x;
this.y = y;
this.height = h;
this.width = w;
}
/**
* Returns the bounding-box of the ellipse.
* @return The bounding box.
*/
public Rectangle2D getBounds2D()
{
return new Rectangle2D.Float(x, y, width, height);
}
/**
* Returns the height of the ellipse.
* @return The height of the ellipse.
*/
public double getHeight()
{
return height;
}
/**
* Returns the width of the ellipse.
* @return The width of the ellipse.
*/
public double getWidth()
{
return width;
}
/**
* Returns x coordinate of the upper-left corner of
* the ellipse's bounding-box.
* @return The x coordinate.
*/
public double getX()
{
return x;
}
/**
* Returns y coordinate of the upper-left corner of
* the ellipse's bounding-box.
* @return The y coordinate.
*/
public double getY()
{
return y;
}
/**
* Returns
*
* @param x - x coordinate of the upper-left of the bounding rectangle
* @param y - y coordinate of the upper-left of the bounding rectangle
* @param w - width of the ellipse
* @param h - height of the ellipse
*/
public void setFrame(float x, float y, float w, float h)
{
this.x = x;
this.y = y;
height = h;
width = w;
}
/**
* Sets the geometry of the ellipse's bounding box.
*
* Note: This leads to a loss of precision.
*
* @param x - x coordinate of the upper-left of the bounding rectangle
* @param y - y coordinate of the upper-left of the bounding rectangle
* @param w - width of the ellipse
* @param h - height of the ellipse
*/
public void setFrame(double x, double y, double w, double h)
{
this.x = (float) x;
this.y = (float) y;
height = (float) h;
width = (float) w;
}
} // class Float
} // class Ellipse2D
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 15610
Content-Disposition: inline; filename="FlatteningPathIterator.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "629936bf76b7d2c6a4d444a794b677eb9851dbe6"
/* FlatteningPathIterator.java -- Approximates curves by straight lines
Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;
/**
* A PathIterator for approximating curved path segments by sequences
* of straight lines. Instances of this class will only return
* segments of type {@link PathIterator#SEG_MOVETO}, {@link
* PathIterator#SEG_LINETO}, and {@link PathIterator#SEG_CLOSE}.
*
* The accuracy of the approximation is determined by two
* parameters:
*
* Memory Efficiency: The memory consumption grows linearly
* with the recursion limit. Neither the flatness parameter nor
* the number of segments in the flattened path will affect the memory
* consumption.
*
* Thread Safety: Multiple threads can safely work on
* separate instances of this class. However, multiple threads should
* not concurrently access the same instance, as no synchronization is
* performed.
*
* @see Implementation Note
*
* @author Sascha Brawer (brawer@dandelis.ch)
*
* @since 1.2
*/
public class FlatteningPathIterator
implements PathIterator
{
/**
* The PathIterator whose curved segments are being approximated.
*/
private final PathIterator srcIter;
/**
* The square of the flatness threshold value, which determines when
* a curve segment is considered flat enough that no further
* subdivision is needed.
*
* Calculating flatness actually produces the squared flatness
* value. To avoid the relatively expensive calculation of a square
* root for each curve segment, we perform all flatness comparisons
* on squared values.
*
* @see QuadCurve2D#getFlatnessSq()
* @see CubicCurve2D#getFlatnessSq()
*/
private final double flatnessSq;
/**
* The maximal number of subdivions that are performed to
* approximate a quadratic or cubic curve segment.
*/
private final int recursionLimit;
/**
* A stack for holding the coordinates of subdivided segments.
*
* @see Implementation Note
*/
private double[] stack;
/**
* The current stack size.
*
* @see Implementation Note
*/
private int stackSize;
/**
* The number of recursions that were performed to arrive at
* a segment on the stack.
*
* @see Implementation Note
*/
private int[] recLevel;
private final double[] scratch = new double[6];
/**
* The segment type of the last segment that was returned by
* the source iterator.
*/
private int srcSegType;
/**
* The current x position of the source iterator.
*/
private double srcPosX;
/**
* The current y position of the source iterator.
*/
private double srcPosY;
/**
* A flag that indicates when this path iterator has finished its
* iteration over path segments.
*/
private boolean done;
/**
* Constructs a new PathIterator for approximating an input
* PathIterator with straight lines. The approximation works by
* recursive subdivisons, until the specified flatness threshold is
* not exceeded.
*
* There will not be more than 10 nested recursion steps, which
* means that a single The inside of the curve is defined for drawing purposes by a winding
* rule. Either the WIND_EVEN_ODD or WIND_NON_ZERO winding rule can be chosen.
*
* The EVEN_ODD winding rule defines a point as inside a path if:
* A ray from the point towards infinity in an arbitrary direction
* intersects the path an odd number of times. Points A and
* C in the image are considered to be outside the path.
* (both intersect twice)
* Point B intersects once, and is inside.
*
* The NON_ZERO winding rule defines a point as inside a path if:
* The path intersects the ray in an equal number of opposite directions.
* Point A in the image is outside (one intersection in the
* ’up’
* direction, one in the ’down’ direction) Point B in
* the image is inside (one intersection ’down’)
* Point C in the image is inside (two intersections in the
* ’down’ direction)
*
* @see Line2D
* @see CubicCurve2D
* @see QuadCurve2D
*
* @author Sascha Brawer (brawer@dandelis.ch)
* @author Sven de Marothy (sven@physto.se)
*
* @since 1.2
*/
public final class GeneralPath implements Shape, Cloneable
{
/** Same constant as {@link PathIterator#WIND_EVEN_ODD}. */
public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
/** Same constant as {@link PathIterator#WIND_NON_ZERO}. */
public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
/** Initial size if not specified. */
private static final int INIT_SIZE = 10;
/** A big number, but not so big it can't survive a few float operations */
private static final double BIG_VALUE = Double.MAX_VALUE / 10.0;
/** The winding rule.
* This is package-private to avoid an accessor method.
*/
int rule;
/**
* The path type in points. Note that xpoints[index] and ypoints[index] maps
* to types[index]; the control points of quad and cubic paths map as
* well but are ignored.
* This is package-private to avoid an accessor method.
*/
byte[] types;
/**
* The list of all points seen. Since you can only append floats, it makes
* sense for these to be float[]. I have no idea why Sun didn't choose to
* allow a general path of double precision points.
* Note: Storing x and y coords seperately makes for a slower transforms,
* But it speeds up and simplifies box-intersection checking a lot.
* These are package-private to avoid accessor methods.
*/
float[] xpoints;
float[] ypoints;
/** The index of the most recent moveto point, or null. */
private int subpath = -1;
/** The next available index into points.
* This is package-private to avoid an accessor method.
*/
int index;
/**
* Constructs a GeneralPath with the default (NON_ZERO)
* winding rule and initial capacity (20).
*/
public GeneralPath()
{
this(WIND_NON_ZERO, INIT_SIZE);
}
/**
* Constructs a GeneralPath with a specific winding rule
* and the default initial capacity (20).
* @param rule the winding rule ({@link #WIND_NON_ZERO} or
* {@link #WIND_EVEN_ODD})
*
* @throws IllegalArgumentException if The curve does not keep any reference to the passed point
* objects. Therefore, a later change to The curve does not keep references to the passed point
* objects. Therefore, a later change to the In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. The result will be the
* the square of the distance between C and the gray line, i.e.
* the squared length of the red line.
*
* @param x1 the x coordinate of the start point P1.
* @param y1 the y coordinate of the start point P1.
* @param cx the x coordinate of the control point C.
* @param cy the y coordinate of the control point C.
* @param x2 the x coordinate of the end point P2.
* @param y2 the y coordinate of the end point P2.
*/
public static double getFlatnessSq(double x1, double y1, double cx,
double cy, double x2, double y2)
{
return Line2D.ptSegDistSq(x1, y1, x2, y2, cx, cy);
}
/**
* Calculates the flatness of a quadratic curve, directly specifying
* each coordinate value. The flatness is the distance of the
* control point to the line between start and end point.
*
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. The result will be the
* the distance between C and the gray line, i.e. the length of
* the red line.
*
* @param x1 the x coordinate of the start point P1.
* @param y1 the y coordinate of the start point P1.
* @param cx the x coordinate of the control point C.
* @param cy the y coordinate of the control point C.
* @param x2 the x coordinate of the end point P2.
* @param y2 the y coordinate of the end point P2.
*/
public static double getFlatness(double x1, double y1, double cx, double cy,
double x2, double y2)
{
return Line2D.ptSegDist(x1, y1, x2, y2, cx, cy);
}
/**
* Calculates the squared flatness of a quadratic curve, specifying
* the coordinate values in an array. The flatness is the distance
* of the control point to the line between start and end point.
*
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. The result will be the
* the square of the distance between C and the gray line, i.e.
* the squared length of the red line.
*
* @param coords an array containing the coordinate values. The
* x coordinate of the start point P1 is located at
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. The result will be the
* the the distance between C and the gray line, i.e. the length of
* the red line.
*
* @param coords an array containing the coordinate values. The
* x coordinate of the start point P1 is located at
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. The result will be the
* the square of the distance between C and the gray line, i.e. the
* squared length of the red line.
*/
public double getFlatnessSq()
{
return Line2D.ptSegDistSq(getX1(), getY1(), getX2(), getY2(), getCtrlX(),
getCtrlY());
}
/**
* Calculates the flatness of this curve. The flatness is the
* distance of the control point to the line between start and end
* point.
*
* In the above drawing, the straight line connecting start point
* P1 and end point P2 is depicted in gray. The result will be the
* the distance between C and the gray line, i.e. the length of the
* red line.
*/
public double getFlatness()
{
return Line2D.ptSegDist(getX1(), getY1(), getX2(), getY2(), getCtrlX(),
getCtrlY());
}
/**
* Subdivides this curve into two halves.
*
* The left end point and the right start point will always be
* identical. Memory-concious programmers thus may want to pass the
* same array for both For some background about solving quadratic equations, see the
* article “Quadratic Formula” in PlanetMath. For an extensive library
* of numerical algorithms written in the C programming language,
* see the GNU Scientific
* Library.
*
* @see #solveQuadratic(double[], double[])
* @see CubicCurve2D#solveCubic(double[], double[])
*
* @param eqn an array with the coefficients of the equation. When
* this procedure has returned, For some background about solving quadratic equations, see the
* article “Quadratic Formula” in PlanetMath. For an extensive library
* of numerical algorithms written in the C programming language,
* see the GNU Scientific
* Library.
*
* @see CubicCurve2D#solveCubic(double[],double[])
*
* @param eqn an array with the coefficients of the equation.
*
* @param res an array into which the non-complex roots will be
* stored. The results may be in an arbitrary order. It is safe to
* pass the same array object reference for both The above drawing illustrates in which area points are
* considered “inside” a QuadCurve2D.
*/
public boolean contains(double x, double y)
{
if (! getBounds2D().contains(x, y))
return false;
return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0);
}
/**
* Determines whether a point is inside the area bounded
* by the curve and the straight line connecting its end points.
*
* The above drawing illustrates in which area points are
* considered “inside” a QuadCurve2D.
*/
public boolean contains(Point2D p)
{
return contains(p.getX(), p.getY());
}
/**
* Determines whether any part of a rectangle is inside the area bounded
* by the curve and the straight line connecting its end points.
*
* The above drawing illustrates in which area points are
* considered “inside” in a CubicCurve2D.
*/
public boolean intersects(double x, double y, double w, double h)
{
if (! getBounds2D().contains(x, y, w, h))
return false;
/* Does any edge intersect? */
if (getAxisIntersections(x, y, true, w) != 0 /* top */
|| getAxisIntersections(x, y + h, true, w) != 0 /* bottom */
|| getAxisIntersections(x + w, y, false, h) != 0 /* right */
|| getAxisIntersections(x, y, false, h) != 0) /* left */
return true;
/* No intersections, is any point inside? */
if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0)
return true;
return false;
}
/**
* Determines whether any part of a Rectangle2D is inside the area bounded
* by the curve and the straight line connecting its end points.
* @see #intersects(double, double, double, double)
*/
public boolean intersects(Rectangle2D r)
{
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Determines whether a rectangle is entirely inside the area bounded
* by the curve and the straight line connecting its end points.
*
* The above drawing illustrates in which area points are
* considered “inside” a QuadCurve2D.
* @see #contains(double, double)
*/
public boolean contains(double x, double y, double w, double h)
{
if (! getBounds2D().intersects(x, y, w, h))
return false;
/* Does any edge intersect? */
if (getAxisIntersections(x, y, true, w) != 0 /* top */
|| getAxisIntersections(x, y + h, true, w) != 0 /* bottom */
|| getAxisIntersections(x + w, y, false, h) != 0 /* right */
|| getAxisIntersections(x, y, false, h) != 0) /* left */
return false;
/* No intersections, is any point inside? */
if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0)
return true;
return false;
}
/**
* Determines whether a Rectangle2D is entirely inside the area that is
* bounded by the curve and the straight line connecting its end points.
* @see #contains(double, double, double, double)
*/
public boolean contains(Rectangle2D r)
{
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Determines the smallest rectangle that encloses the
* curve’s start, end and control point. As the illustration
* below shows, the invisible control point may cause the bounds to
* be much larger than the area that is actually covered by the
* curve.
*
* 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 Classes to represent 2D objects and different path transformations.null
not permitted).
*
* @throws NullPointerException if s
is null
.
*/
public Area(Shape s)
{
this();
Vector
* @param area - the area to be unioned with this one
*/
public void add(Area area)
{
if (equals(area))
return;
if (area.isEmpty())
return;
Area B = (Area) area.clone();
Vector
* @param area the area to be subtracted from this area.
* @throws NullPointerException if area
is null
.
*/
public void subtract(Area area)
{
if (isEmpty() || area.isEmpty())
return;
if (equals(area))
{
reset();
return;
}
Vector
* @param area - the area to be intersected with this area.
* @throws NullPointerException if area
is null
.
*/
public void intersect(Area area)
{
if (isEmpty() || area.isEmpty())
{
reset();
return;
}
if (equals(area))
return;
Vector
* @param area - the area to be XORed with this area.
* @throws NullPointerException if area
is null
.
*/
public void exclusiveOr(Area area)
{
if (area.isEmpty())
return;
if (isEmpty())
{
Area B = (Area) area.clone();
solids = B.solids;
holes = B.holes;
return;
}
if (equals(area))
{
reset();
return;
}
Vector
*
*
* null
* permitted).
* @return true
if the areas are equal, and false
* otherwise.
*/
public boolean equals(Area area)
{
if (area == null)
return false;
if (! getBounds2D().equals(area.getBounds2D()))
return false;
if (solids.size() != area.solids.size()
|| holes.size() != area.holes.size())
return false;
Vectorat
is null
.
*/
public Area createTransformedArea(AffineTransform at)
{
Area a = (Area) clone();
a.transform(at);
return a;
}
/**
* Determines if the point (x,y) is contained within this Area.
*
* @param x the x-coordinate of the point.
* @param y the y-coordinate of the point.
* @return true if the point is contained, false otherwise.
*/
public boolean contains(double x, double y)
{
int n = 0;
for (int i = 0; i < solids.size(); i++)
if (solids.elementAt(i).contains(x, y))
n++;
for (int i = 0; i < holes.size(); i++)
if (holes.elementAt(i).contains(x, y))
n--;
return (n != 0);
}
/**
* Determines if the Point2D p is contained within this Area.
*
* @param p the point.
* @return true
if the point is contained, false
* otherwise.
* @throws NullPointerException if p
is null
.
*/
public boolean contains(Point2D p)
{
return contains(p.getX(), p.getY());
}
/**
* Determines if the rectangle specified by (x,y) as the upper-left
* and with width w and height h is completely contained within this Area,
* returns false otherwise.true
if the rectangle is considered contained
*/
public boolean contains(double x, double y, double w, double h)
{
LineSegment[] l = new LineSegment[4];
l[0] = new LineSegment(x, y, x + w, y);
l[1] = new LineSegment(x, y + h, x + w, y + h);
l[2] = new LineSegment(x, y, x, y + h);
l[3] = new LineSegment(x + w, y, x + w, y + h);
// Since every segment in the area must a contour
// between inside/outside segments, ANY intersection
// will mean the rectangle is not entirely contained.
for (int i = 0; i < 4; i++)
{
for (int path = 0; path < solids.size(); path++)
{
Segment v;
Segment start;
start = v = solids.elementAt(path);
do
{
if (l[i].hasIntersections(v))
return false;
v = v.next;
}
while (v != start);
}
for (int path = 0; path < holes.size(); path++)
{
Segment v;
Segment start;
start = v = holes.elementAt(path);
do
{
if (l[i].hasIntersections(v))
return false;
v = v.next;
}
while (v != start);
}
}
// Is any point inside?
if (! contains(x, y))
return false;
// Final hoop: Is the rectangle non-intersecting and inside,
// but encloses a hole?
Rectangle2D r = new Rectangle2D.Double(x, y, w, h);
for (int path = 0; path < holes.size(); path++)
if (! holes.elementAt(path).isSegmentOutside(r))
return false;
return true;
}
/**
* Determines if the Rectangle2D specified by r is completely contained
* within this Area, returns false otherwise.true
if the rectangle is considered contained
*
* @throws NullPointerException if r
is null
.
*/
public boolean contains(Rectangle2D r)
{
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Determines if the rectangle specified by (x,y) as the upper-left
* and with width w and height h intersects any part of this Area.
*
* @param x the x-coordinate for the rectangle.
* @param y the y-coordinate for the rectangle.
* @param w the width of the rectangle.
* @param h the height of the rectangle.
* @return true
if the rectangle intersects the area,
* false
otherwise.
*/
public boolean intersects(double x, double y, double w, double h)
{
if (solids.size() == 0)
return false;
LineSegment[] l = new LineSegment[4];
l[0] = new LineSegment(x, y, x + w, y);
l[1] = new LineSegment(x, y + h, x + w, y + h);
l[2] = new LineSegment(x, y, x, y + h);
l[3] = new LineSegment(x + w, y, x + w, y + h);
// Return true on any intersection
for (int i = 0; i < 4; i++)
{
for (int path = 0; path < solids.size(); path++)
{
Segment v;
Segment start;
start = v = solids.elementAt(path);
do
{
if (l[i].hasIntersections(v))
return true;
v = v.next;
}
while (v != start);
}
for (int path = 0; path < holes.size(); path++)
{
Segment v;
Segment start;
start = v = holes.elementAt(path);
do
{
if (l[i].hasIntersections(v))
return true;
v = v.next;
}
while (v != start);
}
}
// Non-intersecting, Is any point inside?
if (contains(x + w * 0.5, y + h * 0.5))
return true;
// What if the rectangle encloses the whole shape?
Point2D p = solids.elementAt(0).getMidPoint();
if ((new Rectangle2D.Double(x, y, w, h)).contains(p))
return true;
return false;
}
/**
* Determines if the Rectangle2D specified by r intersects any
* part of this Area.
* @param r the rectangle to test intersection with (null
* not permitted).
* @return true
if the rectangle intersects the area,
* false
otherwise.
* @throws NullPointerException if r
is null
.
*/
public boolean intersects(Rectangle2D r)
{
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Returns a PathIterator object defining the contour of this Area,
* transformed by at.
*
* @param at the transform.
* @return A path iterator.
*/
public PathIterator getPathIterator(AffineTransform at)
{
return (new AreaIterator(at));
}
/**
* Returns a flattened PathIterator object defining the contour of this
* Area, transformed by at and with a defined flatness.
*
* @param at the transform.
* @param flatness the flatness.
* @return A path iterator.
*/
public PathIterator getPathIterator(AffineTransform at, double flatness)
{
return new FlatteningPathIterator(getPathIterator(at), flatness);
}
//---------------------------------------------------------------------
// Non-public methods and classes
/**
* Private pathiterator object.
*/
private class AreaIterator implements PathIterator
{
private Vector
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Graydon Hoare (graydon@redhat.com)
* @author Sascha Brawer (brawer@dandelis.ch)
* @author Sven de Marothy (sven@physto.se)
*
* @since 1.2
*/
public abstract class CubicCurve2D implements Shape, Cloneable
{
private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0;
private static final double EPSILON = 1E-10;
/**
* Constructs a new CubicCurve2D. Typical users will want to
* construct instances of a subclass, such as {@link
* CubicCurve2D.Float} or {@link CubicCurve2D.Double}.
*/
protected CubicCurve2D()
{
}
/**
* Returns the x coordinate of the curve’s start
* point.
*/
public abstract double getX1();
/**
* Returns the y coordinate of the curve’s start
* point.
*/
public abstract double getY1();
/**
* Returns the curve’s start point.
*/
public abstract Point2D getP1();
/**
* Returns the x coordinate of the curve’s first
* control point.
*/
public abstract double getCtrlX1();
/**
* Returns the y coordinate of the curve’s first
* control point.
*/
public abstract double getCtrlY1();
/**
* Returns the curve’s first control point.
*/
public abstract Point2D getCtrlP1();
/**
* Returns the x coordinate of the curve’s second
* control point.
*/
public abstract double getCtrlX2();
/**
* Returns the y coordinate of the curve’s second
* control point.
*/
public abstract double getCtrlY2();
/**
* Returns the curve’s second control point.
*/
public abstract Point2D getCtrlP2();
/**
* Returns the x coordinate of the curve’s end
* point.
*/
public abstract double getX2();
/**
* Returns the y coordinate of the curve’s end
* point.
*/
public abstract double getY2();
/**
* Returns the curve’s end point.
*/
public abstract Point2D getP2();
/**
* Changes the curve geometry, separately specifying each coordinate
* value.
*
*
*
* @param x1 the x coordinate of the curve’s new start
* point.
*
* @param y1 the y coordinate of the curve’s new start
* point.
*
* @param cx1 the x coordinate of the curve’s new
* first control point.
*
* @param cy1 the y coordinate of the curve’s new
* first control point.
*
* @param cx2 the x coordinate of the curve’s new
* second control point.
*
* @param cy2 the y coordinate of the curve’s new
* second control point.
*
* @param x2 the x coordinate of the curve’s new end
* point.
*
* @param y2 the y coordinate of the curve’s new end
* point.
*/
public abstract void setCurve(double x1, double y1, double cx1, double cy1,
double cx2, double cy2, double x2, double y2);
/**
* Changes the curve geometry, specifying coordinate values in an
* array.
*
* @param coords an array containing the new coordinate values. The
* x coordinate of the new start point is located at
*
coords[offset]
, its y coordinate at
* coords[offset + 1]
. The x coordinate of the
* new first control point is located at coords[offset +
* 2]
, its y coordinate at coords[offset +
* 3]
. The x coordinate of the new second control
* point is located at coords[offset + 4]
, its y
* coordinate at coords[offset + 5]
. The x
* coordinate of the new end point is located at coords[offset
* + 6]
, its y coordinate at coords[offset +
* 7]
.
*
* @param offset the offset of the first coordinate value in
* coords
.
*/
public void setCurve(double[] coords, int offset)
{
setCurve(coords[offset++], coords[offset++], coords[offset++],
coords[offset++], coords[offset++], coords[offset++],
coords[offset++], coords[offset++]);
}
/**
* Changes the curve geometry, specifying coordinate values in
* separate Point objects.
*
*
*
*
p1
,
* c1
, c2
or p2
will not
* affect the curve geometry.
*
* @param p1 the new start point.
* @param c1 the new first control point.
* @param c2 the new second control point.
* @param p2 the new end point.
*/
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());
}
/**
* Changes the curve geometry, specifying coordinate values in an
* array of Point objects.
*
*
*
*
pts
array
* or any of its elements will not affect the curve geometry.
*
* @param pts an array containing the points. The new start point
* is located at pts[offset]
, the new first control
* point at pts[offset + 1]
, the new second control
* point at pts[offset + 2]
, and the new end point
* at pts[offset + 3]
.
*
* @param offset the offset of the start point in pts
.
*/
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());
}
/**
* Changes the curve geometry to that of another curve.
*
* @param c the curve whose coordinates will be copied.
*/
public void setCurve(CubicCurve2D c)
{
setCurve(c.getX1(), c.getY1(), c.getCtrlX1(), c.getCtrlY1(),
c.getCtrlX2(), c.getCtrlY2(), c.getX2(), c.getY2());
}
/**
* Calculates the squared flatness of a cubic curve, directly
* specifying each coordinate value. The flatness is the maximal
* distance of a control point to the line between start and end
* point.
*
*
*
*
*
*
*
*
coords[offset]
, its y coordinate at
* coords[offset + 1]
. The x coordinate of the
* first control point C1 is located at coords[offset +
* 2]
, its y coordinate at coords[offset +
* 3]
. The x coordinate of the second control point C2
* is located at coords[offset + 4]
, its y
* coordinate at coords[offset + 5]
. The x
* coordinate of the end point P2 is located at coords[offset
* + 6]
, its y coordinate at coords[offset +
* 7]
.
*
* @param offset the offset of the first coordinate value in
* coords
.
*/
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++]);
}
/**
* Calculates the flatness of a cubic curve, specifying the
* coordinate values in an array. The flatness is the maximal
* distance of a control point to the line between start and end
* point.
*
*
*
*
coords[offset]
, its y coordinate at
* coords[offset + 1]
. The x coordinate of the
* first control point C1 is located at coords[offset +
* 2]
, its y coordinate at coords[offset +
* 3]
. The x coordinate of the second control point C2
* is located at coords[offset + 4]
, its y
* coordinate at coords[offset + 5]
. The x
* coordinate of the end point P2 is located at coords[offset
* + 6]
, its y coordinate at coords[offset +
* 7]
.
*
* @param offset the offset of the first coordinate value in
* coords
.
*/
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++]));
}
/**
* Calculates the squared flatness of this curve. The flatness is
* the maximal distance of a control point to the line between start
* and end point.
*
*
*
*
*
*
*
* @param left a curve whose geometry will be set to the left half
* of this curve, or
null
if the caller is not
* interested in the left half.
*
* @param right a curve whose geometry will be set to the right half
* of this curve, or null
if the caller is not
* interested in the right half.
*/
public void subdivide(CubicCurve2D left, CubicCurve2D right)
{
// 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);
if (left != null)
left.setCurve(d, 0);
if (right != null)
right.setCurve(d, 6);
}
/**
* Subdivides a cubic curve into two halves.
*
*
*
* @param src the curve to be subdivided.
*
* @param left a curve whose geometry will be set to the left half
* of
src
, or null
if the caller is not
* interested in the left half.
*
* @param right a curve whose geometry will be set to the right half
* of src
, or null
if the caller is not
* interested in the right half.
*/
public static void subdivide(CubicCurve2D src, CubicCurve2D left,
CubicCurve2D right)
{
src.subdivide(left, right);
}
/**
* Subdivides a cubic curve into two halves, passing all coordinates
* in an array.
*
*
*
*
left
and right
, and
* set rightOff
to leftOff + 6
.
*
* @param src an array containing the coordinates of the curve to be
* subdivided. The x coordinate of the start point P1 is
* located at src[srcOff]
, its y at
* src[srcOff + 1]
. The x coordinate of the
* first control point C1 is located at src[srcOff +
* 2]
, its y at src[srcOff + 3]
. The
* x coordinate of the second control point C2 is located at
* src[srcOff + 4]
, its y at src[srcOff +
* 5]
. The x coordinate of the end point is located at
* src[srcOff + 6]
, its y at src[srcOff +
* 7]
.
*
* @param srcOff an offset into src
, specifying
* the index of the start point’s x coordinate.
*
* @param left an array that will receive the coordinates of the
* left half of src
. It is acceptable to pass
* src
. A caller who is not interested in the left half
* can pass null
.
*
* @param leftOff an offset into left
, specifying the
* index where the start point’s x coordinate will be
* stored.
*
* @param right an array that will receive the coordinates of the
* right half of src
. It is acceptable to pass
* src
or left
. A caller who is not
* interested in the right half can pass null
.
*
* @param rightOff an offset into right
, specifying the
* index where the start point’s x coordinate will be
* stored.
*/
public static void subdivide(double[] src, int srcOff, double[] left,
int leftOff, double[] right, int rightOff)
{
// To understand this code, please have a look at the image
// "CubicCurve2D-3.png" in the sub-directory "doc-files".
double src_C1_x;
double src_C1_y;
double src_C2_x;
double src_C2_y;
double left_P1_x;
double left_P1_y;
double left_C1_x;
double left_C1_y;
double left_C2_x;
double left_C2_y;
double right_C1_x;
double right_C1_y;
double right_C2_x;
double right_C2_y;
double right_P2_x;
double right_P2_y;
double Mid_x; // Mid = left.P2 = right.P1
double Mid_y; // Mid = left.P2 = right.P1
left_P1_x = src[srcOff];
left_P1_y = src[srcOff + 1];
src_C1_x = src[srcOff + 2];
src_C1_y = src[srcOff + 3];
src_C2_x = src[srcOff + 4];
src_C2_y = src[srcOff + 5];
right_P2_x = src[srcOff + 6];
right_P2_y = src[srcOff + 7];
left_C1_x = (left_P1_x + src_C1_x) / 2;
left_C1_y = (left_P1_y + src_C1_y) / 2;
right_C2_x = (right_P2_x + src_C2_x) / 2;
right_C2_y = (right_P2_y + src_C2_y) / 2;
Mid_x = (src_C1_x + src_C2_x) / 2;
Mid_y = (src_C1_y + src_C2_y) / 2;
left_C2_x = (left_C1_x + Mid_x) / 2;
left_C2_y = (left_C1_y + Mid_y) / 2;
right_C1_x = (Mid_x + right_C2_x) / 2;
right_C1_y = (Mid_y + right_C2_y) / 2;
Mid_x = (left_C2_x + right_C1_x) / 2;
Mid_y = (left_C2_y + right_C1_y) / 2;
if (left != null)
{
left[leftOff] = left_P1_x;
left[leftOff + 1] = left_P1_y;
left[leftOff + 2] = left_C1_x;
left[leftOff + 3] = left_C1_y;
left[leftOff + 4] = left_C2_x;
left[leftOff + 5] = left_C2_y;
left[leftOff + 6] = Mid_x;
left[leftOff + 7] = Mid_y;
}
if (right != null)
{
right[rightOff] = Mid_x;
right[rightOff + 1] = Mid_y;
right[rightOff + 2] = right_C1_x;
right[rightOff + 3] = right_C1_y;
right[rightOff + 4] = right_C2_x;
right[rightOff + 5] = right_C2_y;
right[rightOff + 6] = right_P2_x;
right[rightOff + 7] = right_P2_y;
}
}
/**
* Finds the non-complex roots of a cubic equation, placing the
* results into the same array as the equation coefficients. The
* following equation is being solved:
*
*
*
* eqn[3]
· x3
* + eqn[2]
· x2
* + eqn[1]
· x
* + eqn[0]
* = 0
* eqn
will contain the
* non-complex solutions of the equation, in no particular order.
*
* @return the number of non-complex solutions. A result of 0
* indicates that the equation has no non-complex solutions. A
* result of -1 indicates that the equation is constant (i.e.,
* always or never zero).
*
* @see #solveCubic(double[], double[])
* @see QuadCurve2D#solveQuadratic(double[],double[])
*
* @author Brian Gough (bjg@network-theory.com)
* (original C implementation in the GNU Scientific Library)
*
* @author Sascha Brawer (brawer@dandelis.ch)
* (adaptation to Java)
*/
public static int solveCubic(double[] eqn)
{
return solveCubic(eqn, eqn);
}
/**
* Finds the non-complex roots of a cubic equation. The following
* equation is being solved:
*
*
*
* eqn[3]
· x3
* + eqn[2]
· x2
* + eqn[1]
· x
* + eqn[0]
* = 0
* eqn
* and res
.
*
* @return the number of non-complex solutions. A result of 0
* indicates that the equation has no non-complex solutions. A
* result of -1 indicates that the equation is constant (i.e.,
* always or never zero).
*
* @author Brian Gough (bjg@network-theory.com)
* (original C implementation in the GNU Scientific Library)
*
* @author Sascha Brawer (brawer@dandelis.ch)
* (adaptation to Java)
*/
public static int solveCubic(double[] eqn, double[] res)
{
// Adapted from poly/solve_cubic.c in the GNU Scientific Library
// (GSL), revision 1.7 of 2003-07-26. For the original source, see
// http://www.gnu.org/software/gsl/
//
// Brian Gough, the author of that code, has granted the
// permission to use it in GNU Classpath under the GNU Classpath
// license, and has assigned the copyright to the Free Software
// Foundation.
//
// The Java implementation is very similar to the GSL code, but
// not a strict one-to-one copy. For example, GSL would sort the
// result.
double a;
double b;
double c;
double q;
double r;
double Q;
double R;
double c3;
double Q3;
double R2;
double CR2;
double CQ3;
// If the cubic coefficient is zero, we have a quadratic equation.
c3 = eqn[3];
if (c3 == 0)
return QuadCurve2D.solveQuadratic(eqn, res);
// Divide the equation by the cubic coefficient.
c = eqn[0] / c3;
b = eqn[1] / c3;
a = eqn[2] / c3;
// We now need to solve x^3 + ax^2 + bx + c = 0.
q = a * a - 3 * b;
r = 2 * a * a * a - 9 * a * b + 27 * c;
Q = q / 9;
R = r / 54;
Q3 = Q * Q * Q;
R2 = R * R;
CR2 = 729 * r * r;
CQ3 = 2916 * q * q * q;
if (R == 0 && Q == 0)
{
// The GNU Scientific Library would return three identical
// solutions in this case.
res[0] = -a / 3;
return 1;
}
if (CR2 == CQ3)
{
/* this test is actually R2 == Q3, written in a form suitable
for exact computation with integers */
/* Due to finite precision some double roots may be missed, and
considered to be a pair of complex roots z = x +/- epsilon i
close to the real axis. */
double sqrtQ = Math.sqrt(Q);
if (R > 0)
{
res[0] = -2 * sqrtQ - a / 3;
res[1] = sqrtQ - a / 3;
}
else
{
res[0] = -sqrtQ - a / 3;
res[1] = 2 * sqrtQ - a / 3;
}
return 2;
}
if (CR2 < CQ3) /* equivalent to R2 < Q3 */
{
double sqrtQ = Math.sqrt(Q);
double sqrtQ3 = sqrtQ * sqrtQ * sqrtQ;
double theta = Math.acos(R / sqrtQ3);
double norm = -2 * sqrtQ;
res[0] = norm * Math.cos(theta / 3) - a / 3;
res[1] = norm * Math.cos((theta + 2.0 * Math.PI) / 3) - a / 3;
res[2] = norm * Math.cos((theta - 2.0 * Math.PI) / 3) - a / 3;
// The GNU Scientific Library sorts the results. We don't.
return 3;
}
double sgnR = (R >= 0 ? 1 : -1);
double A = -sgnR * Math.pow(Math.abs(R) + Math.sqrt(R2 - Q3), 1.0 / 3.0);
double B = Q / A;
res[0] = A + B - a / 3;
return 1;
}
/**
* Determines whether a position lies inside the area bounded
* by the curve and the straight line connecting its end points.
*
*
*
*
*
*
*
*
*
*
*
*
*
* @param x1 the x coordinate of the curve’s start
* point.
*
* @param y1 the y coordinate of the curve’s start
* point.
*
* @param cx1 the x coordinate of the curve’s first
* control point.
*
* @param cy1 the y coordinate of the curve’s first
* control point.
*
* @param cx2 the x coordinate of the curve’s second
* control point.
*
* @param cy2 the y coordinate of the curve’s second
* control point.
*
* @param x2 the x coordinate of the curve’s end
* point.
*
* @param y2 the y coordinate of the curve’s end
* point.
*/
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;
}
/**
* Returns the x coordinate of the curve’s start
* point.
*/
public double getX1()
{
return x1;
}
/**
* Returns the y coordinate of the curve’s start
* point.
*/
public double getY1()
{
return y1;
}
/**
* Returns the curve’s start point.
*/
public Point2D getP1()
{
return new Point2D.Double(x1, y1);
}
/**
* Returns the x coordinate of the curve’s first
* control point.
*/
public double getCtrlX1()
{
return ctrlx1;
}
/**
* Returns the y coordinate of the curve’s first
* control point.
*/
public double getCtrlY1()
{
return ctrly1;
}
/**
* Returns the curve’s first control point.
*/
public Point2D getCtrlP1()
{
return new Point2D.Double(ctrlx1, ctrly1);
}
/**
* Returns the x coordinate of the curve’s second
* control point.
*/
public double getCtrlX2()
{
return ctrlx2;
}
/**
* Returns the y coordinate of the curve’s second
* control point.
*/
public double getCtrlY2()
{
return ctrly2;
}
/**
* Returns the curve’s second control point.
*/
public Point2D getCtrlP2()
{
return new Point2D.Double(ctrlx2, ctrly2);
}
/**
* Returns the x coordinate of the curve’s end
* point.
*/
public double getX2()
{
return x2;
}
/**
* Returns the y coordinate of the curve’s end
* point.
*/
public double getY2()
{
return y2;
}
/**
* Returns the curve’s end point.
*/
public Point2D getP2()
{
return new Point2D.Double(x2, y2);
}
/**
* Changes the curve geometry, separately specifying each coordinate
* value.
*
*
*
* @param x1 the x coordinate of the curve’s new start
* point.
*
* @param y1 the y coordinate of the curve’s new start
* point.
*
* @param cx1 the x coordinate of the curve’s new
* first control point.
*
* @param cy1 the y coordinate of the curve’s new
* first control point.
*
* @param cx2 the x coordinate of the curve’s new
* second control point.
*
* @param cy2 the y coordinate of the curve’s new
* second control point.
*
* @param x2 the x coordinate of the curve’s new end
* point.
*
* @param y2 the y coordinate of the curve’s new end
* point.
*/
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;
}
/**
* Determines the smallest rectangle that encloses the
* curve’s start, end and control points. As the
* illustration below shows, the invisible control points may cause
* the bounds to be much larger than the area that is actually
* covered by the curve.
*
*
*/
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);
}
}
/**
* A two-dimensional curve that is parameterized with a cubic
* function and stores coordinate values in single-precision
* floating-point format.
*
* @see CubicCurve2D.Float
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Sascha Brawer (brawer@dandelis.ch)
*/
public static class Float extends CubicCurve2D
{
/**
* The x coordinate of the curve’s start point.
*/
public float x1;
/**
* The y coordinate of the curve’s start point.
*/
public float y1;
/**
* The x coordinate of the curve’s first control point.
*/
public float ctrlx1;
/**
* The y coordinate of the curve’s first control point.
*/
public float ctrly1;
/**
* The x coordinate of the curve’s second control point.
*/
public float ctrlx2;
/**
* The y coordinate of the curve’s second control point.
*/
public float ctrly2;
/**
* The x coordinate of the curve’s end point.
*/
public float x2;
/**
* The y coordinate of the curve’s end point.
*/
public float y2;
/**
* Constructs a new CubicCurve2D that stores its coordinate values
* in single-precision floating-point format. All points are
* initially at position (0, 0).
*/
public Float()
{
}
/**
* Constructs a new CubicCurve2D that stores its coordinate values
* in single-precision floating-point format, specifying the
* initial position of each point.
*
*
*
* @param x1 the x coordinate of the curve’s start
* point.
*
* @param y1 the y coordinate of the curve’s start
* point.
*
* @param cx1 the x coordinate of the curve’s first
* control point.
*
* @param cy1 the y coordinate of the curve’s first
* control point.
*
* @param cx2 the x coordinate of the curve’s second
* control point.
*
* @param cy2 the y coordinate of the curve’s second
* control point.
*
* @param x2 the x coordinate of the curve’s end
* point.
*
* @param y2 the y coordinate of the curve’s end
* point.
*/
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;
}
/**
* Returns the x coordinate of the curve’s start
* point.
*/
public double getX1()
{
return x1;
}
/**
* Returns the y coordinate of the curve’s start
* point.
*/
public double getY1()
{
return y1;
}
/**
* Returns the curve’s start point.
*/
public Point2D getP1()
{
return new Point2D.Float(x1, y1);
}
/**
* Returns the x coordinate of the curve’s first
* control point.
*/
public double getCtrlX1()
{
return ctrlx1;
}
/**
* Returns the y coordinate of the curve’s first
* control point.
*/
public double getCtrlY1()
{
return ctrly1;
}
/**
* Returns the curve’s first control point.
*/
public Point2D getCtrlP1()
{
return new Point2D.Float(ctrlx1, ctrly1);
}
/**
* Returns the s coordinate of the curve’s second
* control point.
*/
public double getCtrlX2()
{
return ctrlx2;
}
/**
* Returns the y coordinate of the curve’s second
* control point.
*/
public double getCtrlY2()
{
return ctrly2;
}
/**
* Returns the curve’s second control point.
*/
public Point2D getCtrlP2()
{
return new Point2D.Float(ctrlx2, ctrly2);
}
/**
* Returns the x coordinate of the curve’s end
* point.
*/
public double getX2()
{
return x2;
}
/**
* Returns the y coordinate of the curve’s end
* point.
*/
public double getY2()
{
return y2;
}
/**
* Returns the curve’s end point.
*/
public Point2D getP2()
{
return new Point2D.Float(x2, y2);
}
/**
* Changes the curve geometry, separately specifying each coordinate
* value as a double-precision floating-point number.
*
*
*
* @param x1 the x coordinate of the curve’s new start
* point.
*
* @param y1 the y coordinate of the curve’s new start
* point.
*
* @param cx1 the x coordinate of the curve’s new
* first control point.
*
* @param cy1 the y coordinate of the curve’s new
* first control point.
*
* @param cx2 the x coordinate of the curve’s new
* second control point.
*
* @param cy2 the y coordinate of the curve’s new
* second control point.
*
* @param x2 the x coordinate of the curve’s new end
* point.
*
* @param y2 the y coordinate of the curve’s new end
* point.
*/
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;
}
/**
* Changes the curve geometry, separately specifying each coordinate
* value as a single-precision floating-point number.
*
*
*
* @param x1 the x coordinate of the curve’s new start
* point.
*
* @param y1 the y coordinate of the curve’s new start
* point.
*
* @param cx1 the x coordinate of the curve’s new
* first control point.
*
* @param cy1 the y coordinate of the curve’s new
* first control point.
*
* @param cx2 the x coordinate of the curve’s new
* second control point.
*
* @param cy2 the y coordinate of the curve’s new
* second control point.
*
* @param x2 the x coordinate of the curve’s new end
* point.
*
* @param y2 the y coordinate of the curve’s new end
* point.
*/
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;
}
/**
* Determines the smallest rectangle that encloses the
* curve’s start, end and control points. As the
* illustration below shows, the invisible control points may cause
* the bounds to be much larger than the area that is actually
* covered by the curve.
*
*
*/
public Rectangle2D getBounds2D()
{
float nx1 = Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2));
float ny1 = Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2));
float nx2 = Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2));
float ny2 = Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2));
return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1);
}
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 3568
Content-Disposition: inline; filename="Dimension2D.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "6b5ce8830a93a2e14ed7b9426361830f24a3dc36"
/* Dimension2D.java -- abstraction of a dimension
Copyright (C) 1999, 2000, 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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 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)
* @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();
/**
* 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);
/**
* 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(d.getWidth(), d.getHeight());
}
/**
* Create a new dimension of the same run-time type with the same contents
* as this one.
*
* @return the clone
*
* @exception OutOfMemoryError If there is not enough memory available.
*
* @since 1.2
*/
public Object clone()
{
try
{
return super.clone();
}
catch (CloneNotSupportedException e)
{
throw (Error) new InternalError().initCause(e); // Impossible
}
}
} // class Dimension2D
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 11311
Content-Disposition: inline; filename="Ellipse2D.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "3bbf2f0104b57c5304dfe681416b00e4ab44fc53"
/* Ellipse2D.java -- represents an ellipse in 2-D space
Copyright (C) 2000, 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;
/**
* Ellipse2D is the shape of an ellipse.
*
*
* The ellipse is defined by it's bounding box (shown in red),
* and is defined by the implicit curve:
* (x/a)2 +
* (y/b)2 = 1
*
* @author Tom Tromey (tromey@cygnus.com)
* @author Eric Blake (ebb9@email.byu.edu)
*
* @since 1.2
*/
public abstract class Ellipse2D extends RectangularShape
{
/**
* Ellipse2D is defined as abstract.
* Implementing classes are Ellipse2D.Float and Ellipse2D.Double.
*/
protected Ellipse2D()
{
}
/**
* Determines if a point is contained within the ellipse. double
* primitives.
*/
public static class Double extends Ellipse2D
{
/**
* The height of the ellipse.
*/
public double height;
/**
* The width of the ellipse.
*/
public double width;
/**
* The upper-left x coordinate of the bounding-box
*/
public double x;
/**
* The upper-left y coordinate of the bounding-box
*/
public double y;
/**
* Creates a new Ellipse2D with an upper-left coordinate of (0,0)
* and a zero size.
*/
public Double()
{
}
/**
* Creates a new Ellipse2D within a given rectangle
* using double-precision coordinates.true
if the ellipse encloses no area, and
* false
otherwise.
*
* @return A boolean.
*/
public boolean isEmpty()
{
return height <= 0 || width <= 0;
}
/**
* Sets the geometry of the ellipse's bounding box.float
* primitives.
*/
public static class Float extends Ellipse2D
{
/**
* The height of the ellipse.
*/
public float height;
/**
* The width of the ellipse.
*/
public float width;
/**
* The upper-left x coordinate of the bounding-box
*/
public float x;
/**
* The upper-left y coordinate of the bounding-box
*/
public float y;
/**
* Creates a new Ellipse2D with an upper-left coordinate of (0,0)
* and a zero size.
*/
public Float()
{
}
/**
* Creates a new Ellipse2D within a given rectangle
* using floating-point precision.true
if the ellipse encloses no area, and
* false
otherwise.
*
* @return A boolean.
*/
public boolean isEmpty()
{
return height <= 0 || width <= 0;
}
/**
* Sets the geometry of the ellipse's bounding box.
*
* SEG_QUADTO
or
* SEG_CUBICTO
segment is approximated by at most
* 210 = 1024 straight lines.
*/
public FlatteningPathIterator(PathIterator src, double flatness)
{
this(src, flatness, 10);
}
/**
* Constructs a new PathIterator for approximating an input
* PathIterator with straight lines. The approximation works by
* recursive subdivisons, until the specified flatness threshold is
* not exceeded. Additionally, the number of recursions is also
* bound by the specified recursion limit.
*/
public FlatteningPathIterator(PathIterator src, double flatness,
int limit)
{
if (flatness < 0 || limit < 0)
throw new IllegalArgumentException();
srcIter = src;
flatnessSq = flatness * flatness;
recursionLimit = limit;
fetchSegment();
}
/**
* Returns the maximally acceptable flatness.
*
* @see QuadCurve2D#getFlatness()
* @see CubicCurve2D#getFlatness()
*/
public double getFlatness()
{
return Math.sqrt(flatnessSq);
}
/**
* Returns the maximum number of recursive curve subdivisions.
*/
public int getRecursionLimit()
{
return recursionLimit;
}
// Documentation will be copied from PathIterator.
public int getWindingRule()
{
return srcIter.getWindingRule();
}
// Documentation will be copied from PathIterator.
public boolean isDone()
{
return done;
}
// Documentation will be copied from PathIterator.
public void next()
{
if (stackSize > 0)
{
--stackSize;
if (stackSize > 0)
{
switch (srcSegType)
{
case PathIterator.SEG_QUADTO:
subdivideQuadratic();
return;
case PathIterator.SEG_CUBICTO:
subdivideCubic();
return;
default:
throw new IllegalStateException();
}
}
}
srcIter.next();
fetchSegment();
}
// Documentation will be copied from PathIterator.
public int currentSegment(double[] coords)
{
if (done)
throw new NoSuchElementException();
switch (srcSegType)
{
case PathIterator.SEG_CLOSE:
return srcSegType;
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
coords[0] = srcPosX;
coords[1] = srcPosY;
return srcSegType;
case PathIterator.SEG_QUADTO:
if (stackSize == 0)
{
coords[0] = srcPosX;
coords[1] = srcPosY;
}
else
{
int sp = stack.length - 4 * stackSize;
coords[0] = stack[sp + 2];
coords[1] = stack[sp + 3];
}
return PathIterator.SEG_LINETO;
case PathIterator.SEG_CUBICTO:
if (stackSize == 0)
{
coords[0] = srcPosX;
coords[1] = srcPosY;
}
else
{
int sp = stack.length - 6 * stackSize;
coords[0] = stack[sp + 4];
coords[1] = stack[sp + 5];
}
return PathIterator.SEG_LINETO;
}
throw new IllegalStateException();
}
// Documentation will be copied from PathIterator.
public int currentSegment(float[] coords)
{
if (done)
throw new NoSuchElementException();
switch (srcSegType)
{
case PathIterator.SEG_CLOSE:
return srcSegType;
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
coords[0] = (float) srcPosX;
coords[1] = (float) srcPosY;
return srcSegType;
case PathIterator.SEG_QUADTO:
if (stackSize == 0)
{
coords[0] = (float) srcPosX;
coords[1] = (float) srcPosY;
}
else
{
int sp = stack.length - 4 * stackSize;
coords[0] = (float) stack[sp + 2];
coords[1] = (float) stack[sp + 3];
}
return PathIterator.SEG_LINETO;
case PathIterator.SEG_CUBICTO:
if (stackSize == 0)
{
coords[0] = (float) srcPosX;
coords[1] = (float) srcPosY;
}
else
{
int sp = stack.length - 6 * stackSize;
coords[0] = (float) stack[sp + 4];
coords[1] = (float) stack[sp + 5];
}
return PathIterator.SEG_LINETO;
}
throw new IllegalStateException();
}
/**
* Fetches the next segment from the source iterator.
*/
private void fetchSegment()
{
int sp;
if (srcIter.isDone())
{
done = true;
return;
}
srcSegType = srcIter.currentSegment(scratch);
switch (srcSegType)
{
case PathIterator.SEG_CLOSE:
return;
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
srcPosX = scratch[0];
srcPosY = scratch[1];
return;
case PathIterator.SEG_QUADTO:
if (recursionLimit == 0)
{
srcPosX = scratch[2];
srcPosY = scratch[3];
stackSize = 0;
return;
}
sp = 4 * recursionLimit;
stackSize = 1;
if (stack == null)
{
stack = new double[sp + /* 4 + 2 */ 6];
recLevel = new int[recursionLimit + 1];
}
recLevel[0] = 0;
stack[sp] = srcPosX; // P1.x
stack[sp + 1] = srcPosY; // P1.y
stack[sp + 2] = scratch[0]; // C.x
stack[sp + 3] = scratch[1]; // C.y
srcPosX = stack[sp + 4] = scratch[2]; // P2.x
srcPosY = stack[sp + 5] = scratch[3]; // P2.y
subdivideQuadratic();
break;
case PathIterator.SEG_CUBICTO:
if (recursionLimit == 0)
{
srcPosX = scratch[4];
srcPosY = scratch[5];
stackSize = 0;
return;
}
sp = 6 * recursionLimit;
stackSize = 1;
if ((stack == null) || (stack.length < sp + 8))
{
stack = new double[sp + /* 6 + 2 */ 8];
recLevel = new int[recursionLimit + 1];
}
recLevel[0] = 0;
stack[sp] = srcPosX; // P1.x
stack[sp + 1] = srcPosY; // P1.y
stack[sp + 2] = scratch[0]; // C1.x
stack[sp + 3] = scratch[1]; // C1.y
stack[sp + 4] = scratch[2]; // C2.x
stack[sp + 5] = scratch[3]; // C2.y
srcPosX = stack[sp + 6] = scratch[4]; // P2.x
srcPosY = stack[sp + 7] = scratch[5]; // P2.y
subdivideCubic();
return;
}
}
/**
* Repeatedly subdivides the quadratic curve segment that is on top
* of the stack. The iteration terminates when the recursion limit
* has been reached, or when the resulting segment is flat enough.
*/
private void subdivideQuadratic()
{
int sp;
int level;
sp = stack.length - 4 * stackSize - 2;
level = recLevel[stackSize - 1];
while ((level < recursionLimit)
&& (QuadCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
{
recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
QuadCurve2D.subdivide(stack, sp, stack, sp - 4, stack, sp);
++stackSize;
sp -= 4;
}
}
/**
* Repeatedly subdivides the cubic curve segment that is on top
* of the stack. The iteration terminates when the recursion limit
* has been reached, or when the resulting segment is flat enough.
*/
private void subdivideCubic()
{
int sp;
int level;
sp = stack.length - 6 * stackSize - 2;
level = recLevel[stackSize - 1];
while ((level < recursionLimit)
&& (CubicCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
{
recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
CubicCurve2D.subdivide(stack, sp, stack, sp - 6, stack, sp);
++stackSize;
sp -= 6;
}
}
/* These routines were useful for debugging. Since they would
* just bloat the implementation, they are commented out.
*
*
private static String segToString(int segType, double[] d, int offset)
{
String s;
switch (segType)
{
case PathIterator.SEG_CLOSE:
return "SEG_CLOSE";
case PathIterator.SEG_MOVETO:
return "SEG_MOVETO (" + d[offset] + ", " + d[offset + 1] + ")";
case PathIterator.SEG_LINETO:
return "SEG_LINETO (" + d[offset] + ", " + d[offset + 1] + ")";
case PathIterator.SEG_QUADTO:
return "SEG_QUADTO (" + d[offset] + ", " + d[offset + 1]
+ ") (" + d[offset + 2] + ", " + d[offset + 3] + ")";
case PathIterator.SEG_CUBICTO:
return "SEG_CUBICTO (" + d[offset] + ", " + d[offset + 1]
+ ") (" + d[offset + 2] + ", " + d[offset + 3]
+ ") (" + d[offset + 4] + ", " + d[offset + 5] + ")";
}
throw new IllegalStateException();
}
private void dumpQuadraticStack(String msg)
{
int sp = stack.length - 4 * stackSize - 2;
int i = 0;
System.err.print(" " + msg + ":");
while (sp < stack.length)
{
System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")");
if (i < recLevel.length)
System.out.print("/" + recLevel[i++]);
if (sp + 3 < stack.length)
System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
sp += 4;
}
System.err.println();
}
private void dumpCubicStack(String msg)
{
int sp = stack.length - 6 * stackSize - 2;
int i = 0;
System.err.print(" " + msg + ":");
while (sp < stack.length)
{
System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")");
if (i < recLevel.length)
System.out.print("/" + recLevel[i++]);
if (sp + 3 < stack.length)
{
System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
System.err.print(" [" + stack[sp+4] + ", " + stack[sp+5] + "]");
}
sp += 6;
}
System.err.println();
}
*
*
*/
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 30342
Content-Disposition: inline; filename="GeneralPath.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "99f1905e24e71d202926c0c746c2dca938e93933"
/* GeneralPath.java -- represents a shape built from subpaths
Copyright (C) 2002, 2003, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;
/**
* A general geometric path, consisting of any number of subpaths
* constructed out of straight lines and cubic or quadratic Bezier
* curves.
*
*
*
rule
is not one of the
* listed values.
*/
public GeneralPath(int rule)
{
this(rule, INIT_SIZE);
}
/**
* Constructs a GeneralPath with a specific winding rule
* and the initial capacity. The initial capacity should be
* the approximate number of path segments to be used.
* @param rule the winding rule ({@link #WIND_NON_ZERO} or
* {@link #WIND_EVEN_ODD})
* @param capacity the inital capacity, in path segments
*
* @throws IllegalArgumentException if rule
is not one of the
* listed values.
*/
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];
xpoints = new float[capacity];
ypoints = new float[capacity];
}
/**
* Constructs a GeneralPath from an arbitrary shape object.
* The Shapes PathIterator path and winding rule will be used.
*
* @param s the shape (null
not permitted).
*
* @throws NullPointerException if shape
is null
.
*/
public GeneralPath(Shape s)
{
types = new byte[INIT_SIZE];
xpoints = new float[INIT_SIZE];
ypoints = new float[INIT_SIZE];
PathIterator pi = s.getPathIterator(null);
setWindingRule(pi.getWindingRule());
append(pi, false);
}
/**
* Adds a new point to a path.
*
* @param x the x-coordinate.
* @param y the y-coordinate.
*/
public void moveTo(float x, float y)
{
subpath = index;
ensureSize(index + 1);
types[index] = PathIterator.SEG_MOVETO;
xpoints[index] = x;
ypoints[index++] = y;
}
/**
* Appends a straight line to the current path.
* @param x x coordinate of the line endpoint.
* @param y y coordinate of the line endpoint.
*/
public void lineTo(float x, float y)
{
ensureSize(index + 1);
types[index] = PathIterator.SEG_LINETO;
xpoints[index] = x;
ypoints[index++] = y;
}
/**
* Appends a quadratic Bezier curve to the current path.
* @param x1 x coordinate of the control point
* @param y1 y coordinate of the control point
* @param x2 x coordinate of the curve endpoint.
* @param y2 y coordinate of the curve endpoint.
*/
public void quadTo(float x1, float y1, float x2, float y2)
{
ensureSize(index + 2);
types[index] = PathIterator.SEG_QUADTO;
xpoints[index] = x1;
ypoints[index++] = y1;
xpoints[index] = x2;
ypoints[index++] = y2;
}
/**
* Appends a cubic Bezier curve to the current path.
* @param x1 x coordinate of the first control point
* @param y1 y coordinate of the first control point
* @param x2 x coordinate of the second control point
* @param y2 y coordinate of the second control point
* @param x3 x coordinate of the curve endpoint.
* @param y3 y coordinate of the curve endpoint.
*/
public void curveTo(float x1, float y1, float x2, float y2, float x3,
float y3)
{
ensureSize(index + 3);
types[index] = PathIterator.SEG_CUBICTO;
xpoints[index] = x1;
ypoints[index++] = y1;
xpoints[index] = x2;
ypoints[index++] = y2;
xpoints[index] = x3;
ypoints[index++] = y3;
}
/**
* Closes the current subpath by drawing a line
* back to the point of the last moveTo, unless the path is already closed.
*/
public void closePath()
{
if (index >= 1 && types[index - 1] == PathIterator.SEG_CLOSE)
return;
ensureSize(index + 1);
types[index] = PathIterator.SEG_CLOSE;
xpoints[index] = xpoints[subpath];
ypoints[index++] = ypoints[subpath];
}
/**
* Appends the segments of a Shape to the path. If connect
is
* true, the new path segments are connected to the existing one with a line.
* The winding rule of the Shape is ignored.
*
* @param s the shape (null
not permitted).
* @param connect whether to connect the new shape to the existing path.
*
* @throws NullPointerException if s
is null
.
*/
public void append(Shape s, boolean connect)
{
append(s.getPathIterator(null), connect);
}
/**
* Appends the segments of a PathIterator to this GeneralPath.
* Optionally, the initial {@link PathIterator#SEG_MOVETO} segment
* of the appended path is changed into a {@link
* PathIterator#SEG_LINETO} segment.
*
* @param iter the PathIterator specifying which segments shall be
* appended (null
not permitted).
*
* @param connect true
for substituting the initial
* {@link PathIterator#SEG_MOVETO} segment by a {@link
* PathIterator#SEG_LINETO}, or false
for not
* performing any substitution. If this GeneralPath is currently
* empty, connect
is assumed to be false
,
* thus leaving the initial {@link PathIterator#SEG_MOVETO}
* unchanged.
*/
public void append(PathIterator iter, boolean connect)
{
// A bad implementation of this method had caused Classpath bug #6076.
float[] f = new float[6];
while (! iter.isDone())
{
switch (iter.currentSegment(f))
{
case PathIterator.SEG_MOVETO:
if (! connect || (index == 0))
{
moveTo(f[0], f[1]);
break;
}
if ((index >= 1) && (types[index - 1] == PathIterator.SEG_CLOSE)
&& (f[0] == xpoints[index - 1])
&& (f[1] == ypoints[index - 1]))
break;
// Fall through.
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;
case PathIterator.SEG_CLOSE:
closePath();
break;
}
connect = false;
iter.next();
}
}
/**
* Returns the path’s current winding rule.
*
* @return {@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO}.
*/
public int getWindingRule()
{
return rule;
}
/**
* Sets the path’s winding rule, which controls which areas are
* considered ’inside’ or ’outside’ the path
* on drawing. Valid rules are WIND_EVEN_ODD for an even-odd winding rule,
* or WIND_NON_ZERO for a non-zero winding rule.
*
* @param rule the rule ({@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO}).
*/
public void setWindingRule(int rule)
{
if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
throw new IllegalArgumentException();
this.rule = rule;
}
/**
* Returns the current appending point of the path.
*
* @return The point.
*/
public Point2D getCurrentPoint()
{
if (subpath < 0)
return null;
return new Point2D.Float(xpoints[index - 1], ypoints[index - 1]);
}
/**
* Resets the path. All points and segments are destroyed.
*/
public void reset()
{
subpath = -1;
index = 0;
}
/**
* Applies a transform to the path.
*
* @param xform the transform (null
not permitted).
*/
public void transform(AffineTransform xform)
{
double nx;
double ny;
double[] m = new double[6];
xform.getMatrix(m);
for (int i = 0; i < index; i++)
{
nx = m[0] * xpoints[i] + m[2] * ypoints[i] + m[4];
ny = m[1] * xpoints[i] + m[3] * ypoints[i] + m[5];
xpoints[i] = (float) nx;
ypoints[i] = (float) ny;
}
}
/**
* Creates a transformed version of the path.
* @param xform the transform to apply
* @return a new transformed GeneralPath
*/
public Shape createTransformedShape(AffineTransform xform)
{
GeneralPath p = new GeneralPath(this);
p.transform(xform);
return p;
}
/**
* Returns the path’s bounding box.
*/
public Rectangle getBounds()
{
return getBounds2D().getBounds();
}
/**
* Returns the path’s bounding box, in float
precision
*/
public Rectangle2D getBounds2D()
{
float x1;
float y1;
float x2;
float y2;
if (index > 0)
{
x1 = x2 = xpoints[0];
y1 = y2 = ypoints[0];
}
else
x1 = x2 = y1 = y2 = 0.0f;
for (int i = 0; i < index; i++)
{
x1 = Math.min(xpoints[i], x1);
y1 = Math.min(ypoints[i], y1);
x2 = Math.max(xpoints[i], x2);
y2 = Math.max(ypoints[i], y2);
}
return (new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1));
}
/**
* Evaluates if a point is within the GeneralPath,
* The NON_ZERO winding rule is used, regardless of the
* set winding rule.
* @param x x coordinate of the point to evaluate
* @param y y coordinate of the point to evaluate
* @return true if the point is within the path, false otherwise
*/
public boolean contains(double x, double y)
{
return (getWindingNumber(x, y) != 0);
}
/**
* Evaluates if a Point2D is within the GeneralPath,
* The NON_ZERO winding rule is used, regardless of the
* set winding rule.
* @param p The Point2D to evaluate
* @return true if the point is within the path, false otherwise
*/
public boolean contains(Point2D p)
{
return contains(p.getX(), p.getY());
}
/**
* Evaluates if a rectangle is completely contained within the path.
* This method will return false in the cases when the box
* intersects an inner segment of the path.
* (i.e.: The method is accurate for the EVEN_ODD winding rule)
*/
public boolean contains(double x, double y, double w, double h)
{
if (! getBounds2D().intersects(x, y, w, h))
return false;
/* Does any edge intersect? */
if (getAxisIntersections(x, y, false, w) != 0 /* top */
|| getAxisIntersections(x, y + h, false, w) != 0 /* bottom */
|| getAxisIntersections(x + w, y, true, h) != 0 /* right */
|| getAxisIntersections(x, y, true, h) != 0) /* left */
return false;
/* No intersections, is any point inside? */
if (getWindingNumber(x, y) != 0)
return true;
return false;
}
/**
* Evaluates if a rectangle is completely contained within the path.
* This method will return false in the cases when the box
* intersects an inner segment of the path.
* (i.e.: The method is accurate for the EVEN_ODD winding rule)
* @param r the rectangle
* @return true
if the rectangle is completely contained
* within the path, false
otherwise
*/
public boolean contains(Rectangle2D r)
{
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Evaluates if a rectangle intersects the path.
* @param x x coordinate of the rectangle
* @param y y coordinate of the rectangle
* @param w width of the rectangle
* @param h height of the rectangle
* @return true
if the rectangle intersects the path,
* false
otherwise
*/
public boolean intersects(double x, double y, double w, double h)
{
/* Does any edge intersect? */
if (getAxisIntersections(x, y, false, w) != 0 /* top */
|| getAxisIntersections(x, y + h, false, w) != 0 /* bottom */
|| getAxisIntersections(x + w, y, true, h) != 0 /* right */
|| getAxisIntersections(x, y, true, h) != 0) /* left */
return true;
/* No intersections, is any point inside? */
if (getWindingNumber(x, y) != 0)
return true;
return false;
}
/**
* Evaluates if a Rectangle2D intersects the path.
* @param r The rectangle
* @return true
if the rectangle intersects the path,
* false
otherwise
*/
public boolean intersects(Rectangle2D r)
{
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* A PathIterator that iterates over the segments of a GeneralPath.
*
* @author Sascha Brawer (brawer@dandelis.ch)
*/
private static class GeneralPathIterator implements PathIterator
{
/**
* The number of coordinate values for each segment type.
*/
private static final int[] NUM_COORDS = {
/* 0: SEG_MOVETO */ 1,
/* 1: SEG_LINETO */ 1,
/* 2: SEG_QUADTO */ 2,
/* 3: SEG_CUBICTO */ 3,
/* 4: SEG_CLOSE */ 0};
/**
* The GeneralPath whose segments are being iterated.
* This is package-private to avoid an accessor method.
*/
final GeneralPath path;
/**
* The affine transformation used to transform coordinates.
*/
private final AffineTransform transform;
/**
* The current position of the iterator.
*/
private int pos;
/**
* Constructs a new iterator for enumerating the segments of a
* GeneralPath.
*
* @param path the path to enumerate
* @param transform an affine transformation for projecting the returned
* points, or null
to return the original points
* without any mapping.
*/
GeneralPathIterator(GeneralPath path, AffineTransform transform)
{
this.path = path;
this.transform = transform;
}
/**
* Returns the current winding rule of the GeneralPath.
*/
public int getWindingRule()
{
return path.rule;
}
/**
* Determines whether the iterator has reached the last segment in
* the path.
*/
public boolean isDone()
{
return pos >= path.index;
}
/**
* Advances the iterator position by one segment.
*/
public void next()
{
int seg;
/*
* Increment pos by the number of coordinate pairs.
*/
seg = path.types[pos];
if (seg == SEG_CLOSE)
pos++;
else
pos += NUM_COORDS[seg];
}
/**
* Returns the current segment in float coordinates.
*/
public int currentSegment(float[] coords)
{
int seg;
int numCoords;
seg = path.types[pos];
numCoords = NUM_COORDS[seg];
if (numCoords > 0)
{
for (int i = 0; i < numCoords; i++)
{
coords[i << 1] = path.xpoints[pos + i];
coords[(i << 1) + 1] = path.ypoints[pos + i];
}
if (transform != null)
transform.transform( /* src */
coords, /* srcOffset */
0, /* dest */ coords, /* destOffset */
0, /* numPoints */ numCoords);
}
return seg;
}
/**
* Returns the current segment in double coordinates.
*/
public int currentSegment(double[] coords)
{
int seg;
int numCoords;
seg = path.types[pos];
numCoords = NUM_COORDS[seg];
if (numCoords > 0)
{
for (int i = 0; i < numCoords; i++)
{
coords[i << 1] = (double) path.xpoints[pos + i];
coords[(i << 1) + 1] = (double) path.ypoints[pos + i];
}
if (transform != null)
transform.transform( /* src */
coords, /* srcOffset */
0, /* dest */ coords, /* destOffset */
0, /* numPoints */ numCoords);
}
return seg;
}
}
/**
* Creates a PathIterator for iterating along the segments of the path.
*
* @param at an affine transformation for projecting the returned
* points, or null
to let the created iterator return
* the original points without any mapping.
*/
public PathIterator getPathIterator(AffineTransform at)
{
return new GeneralPathIterator(this, at);
}
/**
* Creates a new FlatteningPathIterator for the path
*/
public PathIterator getPathIterator(AffineTransform at, double flatness)
{
return new FlatteningPathIterator(getPathIterator(at), flatness);
}
/**
* Creates a new shape of the same run-time type with the same contents
* as this one.
*
* @return the clone
*
* @exception OutOfMemoryError If there is not enough memory available.
*
* @since 1.2
*/
public Object clone()
{
// This class is final; no need to use super.clone().
return new GeneralPath(this);
}
/**
* Helper method - ensure the size of the data arrays,
* otherwise, reallocate new ones twice the size
*
* @param size the minimum array size.
*/
private void ensureSize(int size)
{
if (subpath < 0)
throw new IllegalPathStateException("need initial moveto");
if (size <= xpoints.length)
return;
byte[] b = new byte[types.length << 1];
System.arraycopy(types, 0, b, 0, index);
types = b;
float[] f = new float[xpoints.length << 1];
System.arraycopy(xpoints, 0, f, 0, index);
xpoints = f;
f = new float[ypoints.length << 1];
System.arraycopy(ypoints, 0, f, 0, index);
ypoints = f;
}
/**
* Helper method - Get the total number of intersections from (x,y) along
* a given axis, within a given distance.
*/
private int getAxisIntersections(double x, double y, boolean useYaxis,
double distance)
{
return (evaluateCrossings(x, y, false, useYaxis, distance));
}
/**
* Helper method - returns the winding number of a point.
*/
private int getWindingNumber(double x, double y)
{
/* Evaluate the crossings from x,y to infinity on the y axis (arbitrary
choice). Note that we don't actually use Double.INFINITY, since that's
slower, and may cause problems. */
return (evaluateCrossings(x, y, true, true, BIG_VALUE));
}
/**
* Helper method - evaluates the number of intersections on an axis from
* the point (x,y) to the point (x,y+distance) or (x+distance,y).
* @param x x coordinate.
* @param y y coordinate.
* @param neg True if opposite-directed intersections should cancel,
* false to sum all intersections.
* @param useYaxis Use the Y axis, false uses the X axis.
* @param distance Interval from (x,y) on the selected axis to find
* intersections.
*/
private int evaluateCrossings(double x, double y, boolean neg,
boolean useYaxis, double distance)
{
float cx = 0.0f;
float cy = 0.0f;
float firstx = 0.0f;
float firsty = 0.0f;
int negative = (neg) ? -1 : 1;
double x0;
double x1;
double x2;
double x3;
double y0;
double y1;
double y2;
double y3;
double[] r = new double[4];
int nRoots;
double epsilon = 0.0;
int pos = 0;
int windingNumber = 0;
boolean pathStarted = false;
if (index == 0)
return (0);
if (useYaxis)
{
float[] swap1;
swap1 = ypoints;
ypoints = xpoints;
xpoints = swap1;
double swap2;
swap2 = y;
y = x;
x = swap2;
}
/* Get a value which is hopefully small but not insignificant relative
the path. */
epsilon = ypoints[0] * 1E-7;
if(epsilon == 0)
epsilon = 1E-7;
pos = 0;
while (pos < index)
{
switch (types[pos])
{
case PathIterator.SEG_MOVETO:
if (pathStarted) // close old path
{
x0 = cx;
y0 = cy;
x1 = firstx;
y1 = firsty;
if (y0 == 0.0)
y0 -= epsilon;
if (y1 == 0.0)
y1 -= epsilon;
if (Line2D.linesIntersect(x0, y0, x1, y1,
epsilon, 0.0, distance, 0.0))
windingNumber += (y1 < y0) ? 1 : negative;
cx = firstx;
cy = firsty;
}
cx = firstx = xpoints[pos] - (float) x;
cy = firsty = ypoints[pos++] - (float) y;
pathStarted = true;
break;
case PathIterator.SEG_CLOSE:
x0 = cx;
y0 = cy;
x1 = firstx;
y1 = firsty;
if (y0 == 0.0)
y0 -= epsilon;
if (y1 == 0.0)
y1 -= epsilon;
if (Line2D.linesIntersect(x0, y0, x1, y1,
epsilon, 0.0, distance, 0.0))
windingNumber += (y1 < y0) ? 1 : negative;
cx = firstx;
cy = firsty;
pos++;
pathStarted = false;
break;
case PathIterator.SEG_LINETO:
x0 = cx;
y0 = cy;
x1 = xpoints[pos] - (float) x;
y1 = ypoints[pos++] - (float) y;
if (y0 == 0.0)
y0 -= epsilon;
if (y1 == 0.0)
y1 -= epsilon;
if (Line2D.linesIntersect(x0, y0, x1, y1,
epsilon, 0.0, distance, 0.0))
windingNumber += (y1 < y0) ? 1 : negative;
cx = xpoints[pos - 1] - (float) x;
cy = ypoints[pos - 1] - (float) y;
break;
case PathIterator.SEG_QUADTO:
x0 = cx;
y0 = cy;
x1 = xpoints[pos] - x;
y1 = ypoints[pos++] - y;
x2 = xpoints[pos] - x;
y2 = ypoints[pos++] - y;
/* check if curve may intersect X+ axis. */
if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0)
&& (y0 * y1 <= 0 || y1 * y2 <= 0))
{
if (y0 == 0.0)
y0 -= epsilon;
if (y2 == 0.0)
y2 -= epsilon;
r[0] = y0;
r[1] = 2 * (y1 - y0);
r[2] = (y2 - 2 * y1 + y0);
/* degenerate roots (=tangent points) do not
contribute to the winding number. */
if ((nRoots = QuadCurve2D.solveQuadratic(r)) == 2)
for (int i = 0; i < nRoots; i++)
{
float t = (float) r[i];
if (t > 0.0f && t < 1.0f)
{
double crossing = t * t * (x2 - 2 * x1 + x0)
+ 2 * t * (x1 - x0) + x0;
if (crossing >= 0.0 && crossing <= distance)
windingNumber += (2 * t * (y2 - 2 * y1 + y0)
+ 2 * (y1 - y0) < 0) ? 1 : negative;
}
}
}
cx = xpoints[pos - 1] - (float) x;
cy = ypoints[pos - 1] - (float) y;
break;
case PathIterator.SEG_CUBICTO:
x0 = cx;
y0 = cy;
x1 = xpoints[pos] - x;
y1 = ypoints[pos++] - y;
x2 = xpoints[pos] - x;
y2 = ypoints[pos++] - y;
x3 = xpoints[pos] - x;
y3 = ypoints[pos++] - y;
/* check if curve may intersect X+ axis. */
if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0)
&& (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0))
{
if (y0 == 0.0)
y0 -= epsilon;
if (y3 == 0.0)
y3 -= epsilon;
r[0] = y0;
r[1] = 3 * (y1 - y0);
r[2] = 3 * (y2 + y0 - 2 * y1);
r[3] = y3 - 3 * y2 + 3 * y1 - y0;
if ((nRoots = CubicCurve2D.solveCubic(r)) != 0)
for (int i = 0; i < nRoots; i++)
{
float t = (float) r[i];
if (t > 0.0 && t < 1.0)
{
double crossing = -(t * t * t) * (x0 - 3 * x1
+ 3 * x2 - x3)
+ 3 * t * t * (x0 - 2 * x1 + x2)
+ 3 * t * (x1 - x0) + x0;
if (crossing >= 0 && crossing <= distance)
windingNumber += (3 * t * t * (y3 + 3 * y1
- 3 * y2 - y0)
+ 6 * t * (y0 - 2 * y1 + y2)
+ 3 * (y1 - y0) < 0) ? 1 : negative;
}
}
}
cx = xpoints[pos - 1] - (float) x;
cy = ypoints[pos - 1] - (float) y;
break;
}
}
// swap coordinates back
if (useYaxis)
{
float[] swap;
swap = ypoints;
ypoints = xpoints;
xpoints = swap;
}
return (windingNumber);
}
} // class GeneralPath
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 2502
Content-Disposition: inline; filename="IllegalPathStateException.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "4d190c74814c526b859d7ddc7683eb31afb4edf6"
/* IllegalPathStateException.java -- an operation was in an illegal path state
Copyright (C) 2000, 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;
/**
* Thrown when an operation on a path is in an illegal state, such as appending
* a segment to a GeneralPath
without an initial moveto.
*
* @author Tom Tromey (tromey@cygnus.com)
* @see GeneralPath
* @status updated to 1.4
*/
public class IllegalPathStateException extends RuntimeException
{
/**
* Compatible with JDK 1.2+.
*/
private static final long serialVersionUID = -5158084205220481094L;
/**
* Create an exception with no message.
*/
public IllegalPathStateException()
{
}
/**
* Create an exception with a message.
*
* @param msg the message
*/
public IllegalPathStateException(String msg)
{
super(msg);
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 35566
Content-Disposition: inline; filename="Line2D.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "c92aab004b3a9254b5709e260a70c27acf03f961"
/* 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.
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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;
/**
* 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)
* @author Eric Blake (ebb9@email.byu.edu)
* @author David Gilbert
* @since 1.2
* @status updated to 1.4
*/
public abstract class Line2D implements Shape, Cloneable
{
/**
* The default constructor.
*/
protected Line2D()
{
}
/**
* 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)
{
setLine(p1.getX(), p1.getY(), p2.getX(), p2.getY());
}
/**
* 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)
{
setLine(l.getX1(), l.getY1(), l.getX2(), l.getY2());
}
/**
* 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)
{
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);
}
/**
* 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 relativeCCW(getX1(), getY1(), getX2(), getY2(), px, py);
}
/**
* 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 relativeCCW(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY());
}
/**
* Computes twice the (signed) area of the triangle defined by the three
* points. This method is used for intersection testing.
*
* @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.
* @param x3 the x-coordinate of the third point.
* @param y3 the y-coordinate of the third point.
*
* @return Twice the area.
*/
private static double area2(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
return (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
}
/**
* Returns true
if (x3, y3) lies between (x1, y1) and (x2, y2),
* and false otherwise, This test assumes that the three points are
* collinear, and is used for intersection testing.
*
* @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.
* @param x3 the x-coordinate of the third point.
* @param y3 the y-coordinate of the third point.
*
* @return A boolean.
*/
private static boolean between(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
if (x1 != x2) {
return (x1 <= x3 && x3 <= x2) || (x1 >= x3 && x3 >= x2);
}
else {
return (y1 <= y3 && y3 <= y2) || (y1 >= y3 && y3 >= y2);
}
}
/**
* 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 a1, a2, a3, a4;
// deal with special cases
if ((a1 = area2(x1, y1, x2, y2, x3, y3)) == 0.0)
{
// check if p3 is between p1 and p2 OR
// p4 is collinear also AND either between p1 and p2 OR at opposite ends
if (between(x1, y1, x2, y2, x3, y3))
{
return true;
}
else
{
if (area2(x1, y1, x2, y2, x4, y4) == 0.0)
{
return between(x3, y3, x4, y4, x1, y1)
|| between (x3, y3, x4, y4, x2, y2);
}
else {
return false;
}
}
}
else if ((a2 = area2(x1, y1, x2, y2, x4, y4)) == 0.0)
{
// check if p4 is between p1 and p2 (we already know p3 is not
// collinear)
return between(x1, y1, x2, y2, x4, y4);
}
if ((a3 = area2(x3, y3, x4, y4, x1, y1)) == 0.0) {
// check if p1 is between p3 and p4 OR
// p2 is collinear also AND either between p1 and p2 OR at opposite ends
if (between(x3, y3, x4, y4, x1, y1)) {
return true;
}
else {
if (area2(x3, y3, x4, y4, x2, y2) == 0.0) {
return between(x1, y1, x2, y2, x3, y3)
|| between (x1, y1, x2, y2, x4, y4);
}
else {
return false;
}
}
}
else if ((a4 = area2(x3, y3, x4, y4, x2, y2)) == 0.0) {
// check if p2 is between p3 and p4 (we already know p1 is not
// collinear)
return between(x3, y3, x4, y4, x2, y2);
}
else { // test for regular intersection
return ((a1 > 0.0) ^ (a2 > 0.0)) && ((a3 > 0.0) ^ (a4 > 0.0));
}
}
/**
* 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 linesIntersect(getX1(), getY1(), getX2(), getY2(),
x1, y1, x2, y2);
}
/**
* 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 linesIntersect(getX1(), getY1(), getX2(), getY2(),
l.getX1(), l.getY1(), l.getX2(), l.getY2());
}
/**
* 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 pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - 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);
}
}
return (x - px) * (x - px) + (y - py) * (y - py);
}
/**
* 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)
{
return Math.sqrt(ptSegDistSq(x1, y1, x2, y2, px, 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 ptSegDistSq(getX1(), getY1(), getX2(), getY2(), px, 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 ptSegDistSq(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY());
}
/**
* 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 ptSegDist(getX1(), getY1(), getX2(), getY2(), px, 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 ptSegDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY());
}
/**
* 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;
}
else
{
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);
}
/**
* 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 Math.sqrt(ptLineDistSq(x1, y1, x2, y2, px, 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 ptLineDistSq(getX1(), getY1(), getX2(), getY2(), px, 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 ptLineDistSq(getX1(), getY1(), getX2(), getY2(),
p.getX(), p.getY());
}
/**
* 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 ptLineDist(getX1(), getY1(), getX2(), getY2(), px, 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 ptLineDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY());
}
/**
* 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)
{
return false;
}
/**
* 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;
}
/**
* 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));
}
/**
* 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 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;
}
/**
* Gets a bounding box (not necessarily minimal) for this line.
*
* @return the integer bounding box
* @see #getBounds2D()
*/
public Rectangle getBounds()
{
return getBounds2D().getBounds();
}
/**
* 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)
{
return new PathIterator()
{
/** Current coordinate. */
private int current = 0;
public int getWindingRule()
{
return WIND_NON_ZERO;
}
public boolean isDone()
{
return current >= 2;
}
public void next()
{
current++;
}
public int currentSegment(float[] coords)
{
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;
}
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;
}
};
}
/**
* 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 getPathIterator(at);
}
/**
* Create a new line of the same run-time type with the same contents as
* this one.
*
* @return the clone
*
* @exception OutOfMemoryError If there is not enough memory available.
*
* @since 1.2
*/
public Object clone()
{
try
{
return super.clone();
}
catch (CloneNotSupportedException e)
{
throw (Error) new InternalError().initCause(e); // Impossible
}
}
/**
* This class defines a point in double
precision.
*
* @author Eric Blake (ebb9@email.byu.edu)
* @since 1.2
* @status updated to 1.4
*/
public static class Double extends Line2D
{
/** The x coordinate of the first point. */
public double x1;
/** 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;
/**
* Construct the line segment (0,0)->(0,0).
*/
public Double()
{
}
/**
* 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;
this.x2 = x2;
this.y2 = y2;
}
/**
* 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)
{
x1 = p1.getX();
y1 = p1.getY();
x2 = p2.getX();
y2 = p2.getY();
}
/**
* Return the x coordinate of the first point.
*
* @return the value of x1
*/
public double getX1()
{
return x1;
}
/**
* Return the y coordinate of the first point.
*
* @return the value of y1
*/
public double getY1()
{
return y1;
}
/**
* Return the first point.
*
* @return the point (x1,y1)
*/
public Point2D getP1()
{
return new Point2D.Double(x1, y1);
}
/**
* Return the x coordinate of the second point.
*
* @return the value of x2
*/
public double getX2()
{
return x2;
}
/**
* Return the y coordinate of the second point.
*
* @return the value of y2
*/
public double getY2()
{
return y2;
}
/**
* Return the second point.
*
* @return the point (x2,y2)
*/
public Point2D getP2()
{
return new Point2D.Double(x2, 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;
}
/**
* 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 float
precision.
*
* @author Eric Blake (ebb9@email.byu.edu)
* @since 1.2
* @status updated to 1.4
*/
public static class Float extends Line2D
{
/** 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;
/** The y coordinate of the second point. */
public float y2;
/**
* Construct the line segment (0,0)->(0,0).
*/
public Float()
{
}
/**
* 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;
this.x2 = x2;
this.y2 = y2;
}
/**
* 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)
{
x1 = (float) p1.getX();
y1 = (float) p1.getY();
x2 = (float) p2.getX();
y2 = (float) p2.getY();
}
/**
* Return the x coordinate of the first point.
*
* @return the value of x1
*/
public double getX1()
{
return x1;
}
/**
* Return the y coordinate of the first point.
*
* @return the value of y1
*/
public double getY1()
{
return y1;
}
/**
* Return the first point.
*
* @return the point (x1,y1)
*/
public Point2D getP1()
{
return new Point2D.Float(x1, y1);
}
/**
* Return the x coordinate of the second point.
*
* @return the value of x2
*/
public double getX2()
{
return x2;
}
/**
* Return the y coordinate of the second point.
*
* @return the value of y2
*/
public double getY2()
{
return y2;
}
/**
* Return the second point.
*
* @return the point (x2,y2)
*/
public Point2D getP2()
{
return new Point2D.Float(x2, 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 = (float) x1;
this.y1 = (float) y1;
this.x2 = (float) x2;
this.y2 = (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(float x1, float y1, float x2, float y2)
{
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
/**
* Return the exact bounds of this line segment.
*
* @return the bounding box
*/
public Rectangle2D getBounds2D()
{
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);
}
} // class Float
} // class Line2D
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 2383
Content-Disposition: inline; filename="NoninvertibleTransformException.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "7995a52eb61d5a30eb691988d5aeeedb3e458617"
/* NoninvertibleTransformException.java -- a transform can't be inverted
Copyright (C) 2000, 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;
/**
* Thrown if an operation requires an inverse of an
* AffineTransform
, but the transform is in a non-invertible
* state.
*
* @author Tom Tromey (tromey@cygnus.com)
* @see AffineTransform
* @status updated to 1.4
*/
public class NoninvertibleTransformException extends Exception
{
/**
* 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);
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 7159
Content-Disposition: inline; filename="PathIterator.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "2cd08b9b48bd7dc37694cb96394a8bbadc8bb651"
/* PathIterator.java -- describes a shape by iterating over its vertices
Copyright (C) 2000, 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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 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)
* @author Eric Blake (ebb9@email.byu.edu)
* @see java.awt.Shape
* @see java.awt.Stroke
* @see FlatteningPathIterator
* @since 1.2
* @status updated to 1.4
*/
public interface PathIterator
{
/**
* 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):
*
* 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)!)
*
*/
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):
*
* 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)!)
*
*/
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 true, 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 true.
*
* @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
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 9634
Content-Disposition: inline; filename="Point2D.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "a2689abf80cdba37144ed64e2db7ffda1e15814e"
/* Point2D.java -- generic point in 2-D space
Copyright (C) 1999, 2000, 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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 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)
* @author Eric Blake (ebb9@email.byu.edu)
* @since 1.2
* @status updated to 1.4
*/
public abstract class Point2D implements Cloneable
{
/**
* The default constructor.
*
* @see java.awt.Point
* @see Point2D.Float
* @see Point2D.Double
*/
protected Point2D()
{
}
/**
* Get the X coordinate, in double precision.
*
* @return the x coordinate
*/
public abstract double getX();
/**
* Get the Y coordinate, in double precision.
*
* @return the y coordinate
*/
public abstract double getY();
/**
* 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());
}
/**
* 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;
}
/**
* 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)
*/
public static double distance(double x1, double y1, double x2, double y2)
{
return Math.sqrt(distanceSq(x1, y1, x2, y2));
}
/**
* 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(), getY(), x, y);
}
/**
* 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 distanceSq(getX(), getY(), p.getX(), p.getY());
}
/**
* 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 distance(getX(), getY(), x, y);
}
/**
* 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(), getY(), p.getX(), p.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 e)
{
throw (Error) new InternalError().initCause(e); // Impossible
}
}
/**
* Return the hashcode for this point. The formula is not documented, but
* appears to be the same as:
*
* long l = Double.doubleToLongBits(getY());
* l = l * 31 ^ Double.doubleToLongBits(getX());
* return (int) ((l >> 32) ^ l);
*
*
* @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();
}
/**
* This class defines a point in double
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;
/**
* Create a new point at (0,0).
*/
public Double()
{
}
/**
* 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;
}
/**
* Return the x coordinate.
*
* @return the x coordinate
*/
public double getX()
{
return x;
}
/**
* Return the y coordinate.
*
* @return the y coordinate
*/
public double getY()
{
return 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;
}
/**
* Returns a string representation of this object. The format is:
* "Point2D.Double[" + x + ", " + y + ']'
.
*
* @return a string representation of this object
*/
public String toString()
{
return "Point2D.Double[" + x + ", " + y + ']';
}
} // class Double
/**
* This class defines a point in float
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;
/**
* Create a new point at (0,0).
*/
public Float()
{
}
/**
* 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;
}
/**
* Return the x coordinate.
*
* @return the x coordinate
*/
public double getX()
{
return x;
}
/**
* Return the y coordinate.
*
* @return the y coordinate
*/
public double getY()
{
return 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;
}
/**
* 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;
}
/**
* Returns a string representation of this object. The format is:
* "Point2D.Float[" + x + ", " + y + ']'
.
*
* @return a string representation of this object
*/
public String toString()
{
return "Point2D.Float[" + x + ", " + y + ']';
}
} // class Float
} // class Point2D
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 44738
Content-Disposition: inline; filename="QuadCurve2D.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "62c829d30c5432796808fc8014ce5269e61426a4"
/* QuadCurve2D.java -- represents a parameterized quadratic curve in 2-D space
Copyright (C) 2002, 2003, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;
/**
* A two-dimensional curve that is parameterized with a quadratic
* function.
*
*
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Graydon Hoare (graydon@redhat.com)
* @author Sascha Brawer (brawer@dandelis.ch)
* @author Sven de Marothy (sven@physto.se)
*
* @since 1.2
*/
public abstract class QuadCurve2D implements Shape, Cloneable
{
private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0;
private static final double EPSILON = 1E-10;
/**
* Constructs a new QuadCurve2D. Typical users will want to
* construct instances of a subclass, such as {@link
* QuadCurve2D.Float} or {@link QuadCurve2D.Double}.
*/
protected QuadCurve2D()
{
}
/**
* Returns the x coordinate of the curve’s start
* point.
*/
public abstract double getX1();
/**
* Returns the y coordinate of the curve’s start
* point.
*/
public abstract double getY1();
/**
* Returns the curve’s start point.
*/
public abstract Point2D getP1();
/**
* Returns the x coordinate of the curve’s control
* point.
*/
public abstract double getCtrlX();
/**
* Returns the y coordinate of the curve’s control
* point.
*/
public abstract double getCtrlY();
/**
* Returns the curve’s control point.
*/
public abstract Point2D getCtrlPt();
/**
* Returns the x coordinate of the curve’s end
* point.
*/
public abstract double getX2();
/**
* Returns the y coordinate of the curve’s end
* point.
*/
public abstract double getY2();
/**
* Returns the curve’s end point.
*/
public abstract Point2D getP2();
/**
* Changes the curve geometry, separately specifying each coordinate
* value.
*
* @param x1 the x coordinate of the curve’s new start
* point.
*
* @param y1 the y coordinate of the curve’s new start
* point.
*
* @param cx the x coordinate of the curve’s new
* control point.
*
* @param cy the y coordinate of the curve’s new
* control point.
*
* @param x2 the x coordinate of the curve’s new end
* point.
*
* @param y2 the y coordinate of the curve’s new end
* point.
*/
public abstract void setCurve(double x1, double y1, double cx, double cy,
double x2, double y2);
/**
* Changes the curve geometry, passing coordinate values in an
* array.
*
* @param coords an array containing the new coordinate values. The
* x coordinate of the new start point is located at
*
coords[offset]
, its y coordinate at
* coords[offset + 1]
. The x coordinate of the
* new control point is located at coords[offset + 2]
,
* its y coordinate at coords[offset + 3]
. The
* x coordinate of the new end point is located at
* coords[offset + 4]
, its y coordinate at
* coords[offset + 5]
.
*
* @param offset the offset of the first coordinate value in
* coords
.
*/
public void setCurve(double[] coords, int offset)
{
setCurve(coords[offset++], coords[offset++], coords[offset++],
coords[offset++], coords[offset++], coords[offset++]);
}
/**
* Changes the curve geometry, specifying coordinate values in
* separate Point objects.
*
*
*
*
p1
,
* c
p2
will not affect the curve
* geometry.
*
* @param p1 the new start point.
* @param c the new control point.
* @param p2 the new end point.
*/
public void setCurve(Point2D p1, Point2D c, Point2D p2)
{
setCurve(p1.getX(), p1.getY(), c.getX(), c.getY(), p2.getX(), p2.getY());
}
/**
* Changes the curve geometry, specifying coordinate values in an
* array of Point objects.
*
*
*
*
pts
array
* or any of its elements will not affect the curve geometry.
*
* @param pts an array containing the points. The new start point
* is located at pts[offset]
, the new control
* point at pts[offset + 1]
, and the new end point
* at pts[offset + 2]
.
*
* @param offset the offset of the start point in pts
.
*/
public void setCurve(Point2D[] pts, int offset)
{
setCurve(pts[offset].getX(), pts[offset].getY(), pts[offset + 1].getX(),
pts[offset + 1].getY(), pts[offset + 2].getX(),
pts[offset + 2].getY());
}
/**
* Changes the geometry of the curve to that of another curve.
*
* @param c the curve whose coordinates will be copied.
*/
public void setCurve(QuadCurve2D c)
{
setCurve(c.getX1(), c.getY1(), c.getCtrlX(), c.getCtrlY(), c.getX2(),
c.getY2());
}
/**
* Calculates the squared flatness of a quadratic curve, directly
* specifying each coordinate value. The flatness is the distance of
* the control point to the line between start and end point.
*
*
*
*
*
*
*
*
coords[offset]
, its y coordinate at
* coords[offset + 1]
. The x coordinate of the
* control point C is located at coords[offset + 2]
,
* its y coordinate at coords[offset + 3]
. The
* x coordinate of the end point P2 is located at
* coords[offset + 4]
, its y coordinate at
* coords[offset + 5]
.
*
* @param offset the offset of the first coordinate value in
* coords
.
*/
public static double getFlatnessSq(double[] coords, int offset)
{
return Line2D.ptSegDistSq(coords[offset], coords[offset + 1],
coords[offset + 4], coords[offset + 5],
coords[offset + 2], coords[offset + 3]);
}
/**
* Calculates the flatness of a quadratic curve, specifying the
* coordinate values in an array. The flatness is the distance of
* the control point to the line between start and end point.
*
*
*
*
coords[offset]
, its y coordinate at
* coords[offset + 1]
. The x coordinate of the
* control point C is located at coords[offset + 2]
,
* its y coordinate at coords[offset + 3]
. The
* x coordinate of the end point P2 is located at
* coords[offset + 4]
, its y coordinate at
* coords[offset + 5]
.
*
* @param offset the offset of the first coordinate value in
* coords
.
*/
public static double getFlatness(double[] coords, int offset)
{
return Line2D.ptSegDist(coords[offset], coords[offset + 1],
coords[offset + 4], coords[offset + 5],
coords[offset + 2], coords[offset + 3]);
}
/**
* Calculates the squared flatness of this curve. The flatness is
* the distance of the control point to the line between start and
* end point.
*
*
*
*
*
*
*
* @param left a curve whose geometry will be set to the left half
* of this curve, or
null
if the caller is not
* interested in the left half.
*
* @param right a curve whose geometry will be set to the right half
* of this curve, or null
if the caller is not
* interested in the right half.
*/
public void subdivide(QuadCurve2D left, QuadCurve2D right)
{
// 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);
if (left != null)
left.setCurve(d, 0);
if (right != null)
right.setCurve(d, 4);
}
/**
* Subdivides a quadratic curve into two halves.
*
*
*
* @param src the curve to be subdivided.
*
* @param left a curve whose geometry will be set to the left half
* of
src
, or null
if the caller is not
* interested in the left half.
*
* @param right a curve whose geometry will be set to the right half
* of src
, or null
if the caller is not
* interested in the right half.
*/
public static void subdivide(QuadCurve2D src, QuadCurve2D left,
QuadCurve2D right)
{
src.subdivide(left, right);
}
/**
* Subdivides a quadratic curve into two halves, passing all
* coordinates in an array.
*
*
*
*
left
and right
, and
* set rightOff
to leftOff + 4
.
*
* @param src an array containing the coordinates of the curve to be
* subdivided. The x coordinate of the start point is
* located at src[srcOff]
, its y at
* src[srcOff + 1]
. The x coordinate of the
* control point is located at src[srcOff + 2]
, its
* y at src[srcOff + 3]
. The x
* coordinate of the end point is located at src[srcOff +
* 4]
, its y at src[srcOff + 5]
.
*
* @param srcOff an offset into src
, specifying
* the index of the start point’s x coordinate.
*
* @param left an array that will receive the coordinates of the
* left half of src
. It is acceptable to pass
* src
. A caller who is not interested in the left half
* can pass null
.
*
* @param leftOff an offset into left
, specifying the
* index where the start point’s x coordinate will be
* stored.
*
* @param right an array that will receive the coordinates of the
* right half of src
. It is acceptable to pass
* src
or left
. A caller who is not
* interested in the right half can pass null
.
*
* @param rightOff an offset into right
, specifying the
* index where the start point’s x coordinate will be
* stored.
*/
public static void subdivide(double[] src, int srcOff, double[] left,
int leftOff, double[] right, int rightOff)
{
double x1;
double y1;
double xc;
double yc;
double x2;
double y2;
x1 = src[srcOff];
y1 = src[srcOff + 1];
xc = src[srcOff + 2];
yc = src[srcOff + 3];
x2 = src[srcOff + 4];
y2 = src[srcOff + 5];
if (left != null)
{
left[leftOff] = x1;
left[leftOff + 1] = y1;
}
if (right != null)
{
right[rightOff + 4] = x2;
right[rightOff + 5] = y2;
}
x1 = (x1 + xc) / 2;
x2 = (xc + x2) / 2;
xc = (x1 + x2) / 2;
y1 = (y1 + yc) / 2;
y2 = (y2 + yc) / 2;
yc = (y1 + y2) / 2;
if (left != null)
{
left[leftOff + 2] = x1;
left[leftOff + 3] = y1;
left[leftOff + 4] = xc;
left[leftOff + 5] = yc;
}
if (right != null)
{
right[rightOff] = xc;
right[rightOff + 1] = yc;
right[rightOff + 2] = x2;
right[rightOff + 3] = y2;
}
}
/**
* Finds the non-complex roots of a quadratic equation, placing the
* results into the same array as the equation coefficients. The
* following equation is being solved:
*
*
*
* eqn[2]
· x2
* + eqn[1]
· x
* + eqn[0]
* = 0
* eqn
will contain the
* non-complex solutions of the equation, in no particular order.
*
* @return the number of non-complex solutions. A result of 0
* indicates that the equation has no non-complex solutions. A
* result of -1 indicates that the equation is constant (i.e.,
* always or never zero).
*
* @author Brian Gough (bjg@network-theory.com)
* (original C implementation in the GNU Scientific Library)
*
* @author Sascha Brawer (brawer@dandelis.ch)
* (adaptation to Java)
*/
public static int solveQuadratic(double[] eqn)
{
return solveQuadratic(eqn, eqn);
}
/**
* Finds the non-complex roots of a quadratic equation. The
* following equation is being solved:
*
*
*
* eqn[2]
· x2
* + eqn[1]
· x
* + eqn[0]
* = 0
* eqn
* and res
.
*
* @return the number of non-complex solutions. A result of 0
* indicates that the equation has no non-complex solutions. A
* result of -1 indicates that the equation is constant (i.e.,
* always or never zero).
*
* @author Brian Gough (bjg@network-theory.com)
* (original C implementation in the GNU Scientific Library)
*
* @author Sascha Brawer (brawer@dandelis.ch)
* (adaptation to Java)
*/
public static int solveQuadratic(double[] eqn, double[] res)
{
// Taken from poly/solve_quadratic.c in the GNU Scientific Library
// (GSL), cvs revision 1.7 of 2003-07-26. For the original source,
// see http://www.gnu.org/software/gsl/
//
// Brian Gough, the author of that code, has granted the
// permission to use it in GNU Classpath under the GNU Classpath
// license, and has assigned the copyright to the Free Software
// Foundation.
//
// The Java implementation is very similar to the GSL code, but
// not a strict one-to-one copy. For example, GSL would sort the
// result.
double a;
double b;
double c;
double disc;
c = eqn[0];
b = eqn[1];
a = eqn[2];
// Check for linear or constant functions. This is not done by the
// GNU Scientific Library. Without this special check, we
// wouldn't return -1 for constant functions, and 2 instead of 1
// for linear functions.
if (a == 0)
{
if (b == 0)
return -1;
res[0] = -c / b;
return 1;
}
disc = b * b - 4 * a * c;
if (disc < 0)
return 0;
if (disc == 0)
{
// The GNU Scientific Library returns two identical results here.
// We just return one.
res[0] = -0.5 * b / a;
return 1;
}
// disc > 0
if (b == 0)
{
double r;
r = Math.abs(0.5 * Math.sqrt(disc) / a);
res[0] = -r;
res[1] = r;
}
else
{
double sgnb;
double temp;
sgnb = (b > 0 ? 1 : -1);
temp = -0.5 * (b + sgnb * Math.sqrt(disc));
// The GNU Scientific Library sorts the result here. We don't.
res[0] = temp / a;
res[1] = c / temp;
}
return 2;
}
/**
* Determines whether a point is inside the area bounded
* by the curve and the straight line connecting its end points.
*
*
*
*
*
*
*
*
*
*
*/
public Rectangle getBounds()
{
return getBounds2D().getBounds();
}
public PathIterator getPathIterator(final AffineTransform at)
{
return new PathIterator()
{
/** Current coordinate. */
private int current = 0;
public int getWindingRule()
{
return WIND_NON_ZERO;
}
public boolean isDone()
{
return current >= 2;
}
public void next()
{
current++;
}
public int currentSegment(float[] coords)
{
int result;
switch (current)
{
case 0:
coords[0] = (float) getX1();
coords[1] = (float) getY1();
result = SEG_MOVETO;
break;
case 1:
coords[0] = (float) getCtrlX();
coords[1] = (float) getCtrlY();
coords[2] = (float) getX2();
coords[3] = (float) getY2();
result = SEG_QUADTO;
break;
default:
throw new NoSuchElementException("quad iterator out of bounds");
}
if (at != null)
at.transform(coords, 0, coords, 0, 2);
return result;
}
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] = getCtrlX();
coords[1] = getCtrlY();
coords[2] = getX2();
coords[3] = getY2();
result = SEG_QUADTO;
break;
default:
throw new NoSuchElementException("quad iterator out of bounds");
}
if (at != null)
at.transform(coords, 0, coords, 0, 2);
return result;
}
};
}
public PathIterator getPathIterator(AffineTransform at, double flatness)
{
return new FlatteningPathIterator(getPathIterator(at), flatness);
}
/**
* Creates a new curve 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
}
}
/**
* Helper method used by contains() and intersects() methods
* Return the number of curve/line intersections on a given axis
* extending from a certain point. useYaxis is true for using the Y axis,
* @param x x coordinate of the origin point
* @param y y coordinate of the origin point
* @param useYaxis axis to follow, if true the positive Y axis is used,
* false uses the positive X axis.
*
* This is an implementation of the line-crossings algorithm,
* Detailed in an article on Eric Haines' page:
* http://www.acm.org/tog/editors/erich/ptinpoly/
*/
private int getAxisIntersections(double x, double y, boolean useYaxis,
double distance)
{
int nCrossings = 0;
double a0;
double a1;
double a2;
double b0;
double b1;
double b2;
double[] r = new double[3];
int nRoots;
a0 = a2 = 0.0;
if (useYaxis)
{
a0 = getY1() - y;
a1 = getCtrlY() - y;
a2 = getY2() - y;
b0 = getX1() - x;
b1 = getCtrlX() - x;
b2 = getX2() - x;
}
else
{
a0 = getX1() - x;
a1 = getCtrlX() - x;
a2 = getX2() - x;
b0 = getY1() - y;
b1 = getCtrlY() - y;
b2 = getY2() - y;
}
/* If the axis intersects a start/endpoint, shift it up by some small
amount to guarantee the line is 'inside'
If this is not done,bad behaviour may result for points on that axis. */
if (a0 == 0.0 || a2 == 0.0)
{
double small = getFlatness() * EPSILON;
if (a0 == 0.0)
a0 -= small;
if (a2 == 0.0)
a2 -= small;
}
r[0] = a0;
r[1] = 2 * (a1 - a0);
r[2] = (a2 - 2 * a1 + a0);
nRoots = solveQuadratic(r);
for (int i = 0; i < nRoots; i++)
{
double t = r[i];
if (t >= 0.0 && t <= 1.0)
{
double crossing = t * t * (b2 - 2 * b1 + b0) + 2 * t * (b1 - b0)
+ b0;
/* single root is always doubly degenerate in quads */
if (crossing > 0 && crossing < distance)
nCrossings += (nRoots == 1) ? 2 : 1;
}
}
if (useYaxis)
{
if (Line2D.linesIntersect(b0, a0, b2, a2, EPSILON, 0.0, distance, 0.0))
nCrossings++;
}
else
{
if (Line2D.linesIntersect(a0, b0, a2, b2, 0.0, EPSILON, 0.0, distance))
nCrossings++;
}
return (nCrossings);
}
/**
* A two-dimensional curve that is parameterized with a quadratic
* function and stores coordinate values in double-precision
* floating-point format.
*
* @see QuadCurve2D.Float
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Sascha Brawer (brawer@dandelis.ch)
*/
public static class Double extends QuadCurve2D
{
/**
* The x coordinate of the curve’s start point.
*/
public double x1;
/**
* The y coordinate of the curve’s start point.
*/
public double y1;
/**
* The x coordinate of the curve’s control point.
*/
public double ctrlx;
/**
* The y coordinate of the curve’s control point.
*/
public double ctrly;
/**
* The x coordinate of the curve’s end point.
*/
public double x2;
/**
* The y coordinate of the curve’s end point.
*/
public double y2;
/**
* Constructs a new QuadCurve2D that stores its coordinate values
* in double-precision floating-point format. All points are
* initially at position (0, 0).
*/
public Double()
{
}
/**
* Constructs a new QuadCurve2D that stores its coordinate values
* in double-precision floating-point format, specifying the
* initial position of each point.
*
* @param x1 the x coordinate of the curve’s start
* point.
*
* @param y1 the y coordinate of the curve’s start
* point.
*
* @param cx the x coordinate of the curve’s control
* point.
*
* @param cy the y coordinate of the curve’s control
* point.
*
* @param x2 the x coordinate of the curve’s end
* point.
*
* @param y2 the y coordinate of the curve’s end
* point.
*/
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;
}
/**
* Returns the x coordinate of the curve’s start
* point.
*/
public double getX1()
{
return x1;
}
/**
* Returns the y coordinate of the curve’s start
* point.
*/
public double getY1()
{
return y1;
}
/**
* Returns the curve’s start point.
*/
public Point2D getP1()
{
return new Point2D.Double(x1, y1);
}
/**
* Returns the x coordinate of the curve’s control
* point.
*/
public double getCtrlX()
{
return ctrlx;
}
/**
* Returns the y coordinate of the curve’s control
* point.
*/
public double getCtrlY()
{
return ctrly;
}
/**
* Returns the curve’s control point.
*/
public Point2D getCtrlPt()
{
return new Point2D.Double(ctrlx, ctrly);
}
/**
* Returns the x coordinate of the curve’s end
* point.
*/
public double getX2()
{
return x2;
}
/**
* Returns the y coordinate of the curve’s end
* point.
*/
public double getY2()
{
return y2;
}
/**
* Returns the curve’s end point.
*/
public Point2D getP2()
{
return new Point2D.Double(x2, y2);
}
/**
* Changes the geometry of the curve.
*
* @param x1 the x coordinate of the curve’s new
* start point.
*
* @param y1 the y coordinate of the curve’s new
* start point.
*
* @param cx the x coordinate of the curve’s new
* control point.
*
* @param cy the y coordinate of the curve’s new
* control point.
*
* @param x2 the x coordinate of the curve’s new
* end point.
*
* @param y2 the y coordinate of the curve’s new
* end point.
*/
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;
}
/**
* Determines the smallest rectangle that encloses the
* curve’s start, end and control point. As the
* illustration below shows, the invisible control point may cause
* the bounds to be much larger than the area that is actually
* covered by the curve.
*
*
*/
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);
}
}
/**
* A two-dimensional curve that is parameterized with a quadratic
* function and stores coordinate values in single-precision
* floating-point format.
*
* @see QuadCurve2D.Double
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Sascha Brawer (brawer@dandelis.ch)
*/
public static class Float extends QuadCurve2D
{
/**
* The x coordinate of the curve’s start point.
*/
public float x1;
/**
* The y coordinate of the curve’s start point.
*/
public float y1;
/**
* The x coordinate of the curve’s control point.
*/
public float ctrlx;
/**
* The y coordinate of the curve’s control point.
*/
public float ctrly;
/**
* The x coordinate of the curve’s end point.
*/
public float x2;
/**
* The y coordinate of the curve’s end point.
*/
public float y2;
/**
* Constructs a new QuadCurve2D that stores its coordinate values
* in single-precision floating-point format. All points are
* initially at position (0, 0).
*/
public Float()
{
}
/**
* Constructs a new QuadCurve2D that stores its coordinate values
* in single-precision floating-point format, specifying the
* initial position of each point.
*
* @param x1 the x coordinate of the curve’s start
* point.
*
* @param y1 the y coordinate of the curve’s start
* point.
*
* @param cx the x coordinate of the curve’s control
* point.
*
* @param cy the y coordinate of the curve’s control
* point.
*
* @param x2 the x coordinate of the curve’s end
* point.
*
* @param y2 the y coordinate of the curve’s end
* point.
*/
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;
}
/**
* Returns the x coordinate of the curve’s start
* point.
*/
public double getX1()
{
return x1;
}
/**
* Returns the y coordinate of the curve’s start
* point.
*/
public double getY1()
{
return y1;
}
/**
* Returns the curve’s start point.
*/
public Point2D getP1()
{
return new Point2D.Float(x1, y1);
}
/**
* Returns the x coordinate of the curve’s control
* point.
*/
public double getCtrlX()
{
return ctrlx;
}
/**
* Returns the y coordinate of the curve’s control
* point.
*/
public double getCtrlY()
{
return ctrly;
}
/**
* Returns the curve’s control point.
*/
public Point2D getCtrlPt()
{
return new Point2D.Float(ctrlx, ctrly);
}
/**
* Returns the x coordinate of the curve’s end
* point.
*/
public double getX2()
{
return x2;
}
/**
* Returns the y coordinate of the curve’s end
* point.
*/
public double getY2()
{
return y2;
}
/**
* Returns the curve’s end point.
*/
public Point2D getP2()
{
return new Point2D.Float(x2, y2);
}
/**
* Changes the geometry of the curve, specifying coordinate values
* as double-precision floating-point numbers.
*
* @param x1 the x coordinate of the curve’s new
* start point.
*
* @param y1 the y coordinate of the curve’s new
* start point.
*
* @param cx the x coordinate of the curve’s new
* control point.
*
* @param cy the y coordinate of the curve’s new
* control point.
*
* @param x2 the x coordinate of the curve’s new
* end point.
*
* @param y2 the y coordinate of the curve’s new
* end point.
*/
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;
}
/**
* Changes the geometry of the curve, specifying coordinate values
* as single-precision floating-point numbers.
*
* @param x1 the x coordinate of the curve’s new
* start point.
*
* @param y1 the y coordinate of the curve’s new
* start point.
*
* @param cx the x coordinate of the curve’s new
* control point.
*
* @param cy the y coordinate of the curve’s new
* control point.
*
* @param x2 the x coordinate of the curve’s new
* end point.
*
* @param y2 the y coordinate of the curve’s new
* end point.
*/
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;
}
/**
* Determines the smallest rectangle that encloses the
* curve’s start, end and control point. As the
* illustration below shows, the invisible control point may cause
* the bounds to be much larger than the area that is actually
* covered by the curve.
*
*
*/
public Rectangle2D getBounds2D()
{
float nx1 = Math.min(Math.min(x1, ctrlx), x2);
float ny1 = Math.min(Math.min(y1, ctrly), y2);
float nx2 = Math.max(Math.max(x1, ctrlx), x2);
float ny2 = Math.max(Math.max(y1, ctrly), y2);
return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1);
}
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 27642
Content-Disposition: inline; filename="Rectangle2D.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "6a255f95391aaef07914854440fc241321d9afec"
/* Rectangle2D.java -- generic rectangles in 2-D space
Copyright (C) 2000, 2001, 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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 describes a rectangle by a point (x,y) and dimension (w x h).
* The actual storage is left up to subclasses.
*
*
contains
or intersects
is
* undefined unless the rectangle has positive width and height.
*
* @author Tom Tromey (tromey@cygnus.com)
* @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(double, double)
*/
public static final int OUT_LEFT = 1;
/**
* The point lies above the rectangle (p.y < r.y).
*
* @see #outcode(double, double)
*/
public static final int OUT_TOP = 2;
/**
* The point lies right of the rectangle (p.x > r.maxX).
*
* @see #outcode(double, double)
*/
public static final int OUT_RIGHT = 4;
/**
* The point lies below of the rectangle (p.y > r.maxY).
*
* @see #outcode(double, double)
*/
public static final int OUT_BOTTOM = 8;
/**
* Default constructor.
*/
protected Rectangle2D()
{
}
/**
* 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);
/**
* 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)
{
setRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* 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)
{
double x = getX();
double y = getY();
double w = getWidth();
double h = getHeight();
if (w <= 0 || h <= 0)
return false;
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 (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));
}
/**
* 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)
{
return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2());
}
/**
* 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());
}
/**
* 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)
{
setRect(x, y, w, h);
}
/**
* 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 (Rectangle2D) clone();
}
/**
* 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)
{
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;
}
/**
* 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 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;
}
/**
* 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 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;
}
/**
* 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)
{
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);
}
/**
* 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);
}
/**
* 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 contains
may
* return false.
*
* @param newx the X coordinate of the point to add to this rectangle
* @param newy the Y coordinate of the point to add to this rectangle
*/
public void add(double newx, double newy)
{
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);
}
/**
* 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 contains
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());
}
/**
* 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, Rectangle2D, Rectangle2D)
*/
public void add(Rectangle2D r)
{
union(this, r, this);
}
/**
* 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 at an optional transform to apply to the iterator
* @return a new iterator over the boundary
* @since 1.2
*/
public PathIterator getPathIterator(final AffineTransform at)
{
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()
{
// A test program showed that Sun J2SE 1.3.1 and 1.4.1_01
// return WIND_NON_ZERO paths. While this does not really
// make any difference for rectangles (because they are not
// self-intersecting), it seems appropriate to behave
// identically.
return WIND_NON_ZERO;
}
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;
}
};
}
/**
* 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 at an optional transform to apply to the iterator
* @param flatness 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)
{
return getPathIterator(at);
}
/**
* Return the hashcode for this rectangle. The formula is not documented, but
* appears to be the same as:
*
* long l = Double.doubleToLongBits(getX())
* + 37 * Double.doubleToLongBits(getY())
* + 43 * Double.doubleToLongBits(getWidth())
* + 47 * Double.doubleToLongBits(getHeight());
* return (int) ((l >> 32) ^ l);
*
*
* @return the hashcode
*/
public int hashCode()
{
// 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);
}
/**
* 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)
{
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 double
precision.
*
* @author Eric Blake (ebb9@email.byu.edu)
* @since 1.2
* @status updated to 1.4
*/
public static class Double extends Rectangle2D
{
/** The x coordinate of the lower left corner. */
public double x;
/** The y coordinate of the lower left corner. */
public double y;
/** 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()
{
}
/**
* 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;
width = w;
height = h;
}
/**
* Return the X coordinate.
*
* @return the value of x
*/
public double getX()
{
return x;
}
/**
* Return the Y coordinate.
*
* @return the value of y
*/
public double getY()
{
return y;
}
/**
* Return the width.
*
* @return the value of width
*/
public double getWidth()
{
return width;
}
/**
* Return the height.
*
* @return the value of height
*/
public double getHeight()
{
return height;
}
/**
* Test if the rectangle is empty.
*
* @return true if width or height is not positive
*/
public boolean isEmpty()
{
return width <= 0 || height <= 0;
}
/**
* 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;
width = w;
height = h;
}
/**
* 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)
{
x = r.getX();
y = r.getY();
width = r.getWidth();
height = r.getHeight();
}
/**
* 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 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;
}
/**
* 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 Double(x, y, width, height);
}
/**
* 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);
return res;
}
/**
* 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);
return res;
}
/**
* Returns a string representation of this rectangle. This is in the form
* getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
* + ",h=" + height + ']'
.
*
* @return a string representation of this rectangle
*/
public String toString()
{
return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
+ ",h=" + height + ']';
}
}
/**
* This class defines a rectangle in float
precision.
*
* @author Eric Blake (ebb9@email.byu.edu)
* @since 1.2
* @status updated to 1.4
*/
public static class Float extends Rectangle2D
{
/** The x coordinate of the lower left corner. */
public float x;
/** The y coordinate of the lower left corner. */
public float y;
/** 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()
{
}
/**
* 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;
width = w;
height = 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
*/
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;
}
/**
* Return the Y coordinate.
*
* @return the value of y
*/
public double getY()
{
return y;
}
/**
* Return the width.
*
* @return the value of width
*/
public double getWidth()
{
return width;
}
/**
* Return the height.
*
* @return the value of height
*/
public double getHeight()
{
return height;
}
/**
* Test if the rectangle is empty.
*
* @return true if width or height is not positive
*/
public boolean isEmpty()
{
return width <= 0 || height <= 0;
}
/**
* 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 = x;
this.y = y;
width = w;
height = 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 = (float) x;
this.y = (float) y;
width = (float) w;
height = (float) h;
}
/**
* 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)
{
x = (float) r.getX();
y = (float) r.getY();
width = (float) r.getWidth();
height = (float) r.getHeight();
}
/**
* 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 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;
}
/**
* 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 Float(x, y, width, height);
}
/**
* 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);
return res;
}
/**
* 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);
return res;
}
/**
* Returns a string representation of this rectangle. This is in the form
* getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
* + ",h=" + height + ']'
.
*
* @return a string representation of this rectangle
*/
public String toString()
{
return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
+ ",h=" + height + ']';
}
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 11111
Content-Disposition: inline; filename="RectangularShape.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "68bc451ccc4defd448bb803927cc642d3d6aa647"
/* RectangularShape.java -- a rectangular frame for several generic shapes
Copyright (C) 2000, 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;
/**
* 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)
* @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
{
/**
* Default constructor.
*/
protected RectangularShape()
{
}
/**
* 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();
}
/**
* 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 getY();
}
/**
* 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();
}
/**
* 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 getY() + getHeight();
}
/**
* 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 the y coordinate of the center point of the framing rectangle.
*
* @return the central y coordinate
*/
public double getCenterY()
{
return getY() + getHeight() / 2;
}
/**
* 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());
}
/**
* 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(p.getX(), p.getY(), d.getWidth(), d.getHeight());
}
/**
* 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());
}
/**
* 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)
{
double t = x2;
x2 = x1;
x1 = t;
}
if (y1 > y2)
{
double t = y2;
y2 = y1;
y1 = t;
}
setFrame(x1, y1, x2 - x1, y2 - y1);
}
/**
* 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());
}
/**
* 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, halfw + halfw, halfh + halfh);
}
/**
* 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());
}
/**
* 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)
{
return contains(p.getX(), p.getY());
}
/**
* 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)
{
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* 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)
{
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Returns a bounding box for this shape, in integer format. Notice that you
* may get a tighter bound with getBounds2D.
*
* @return a bounding box
*/
public Rectangle getBounds()
{
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));
}
/**
* 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 at 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 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()
{
try
{
return super.clone();
}
catch (CloneNotSupportedException e)
{
throw (Error) new InternalError().initCause(e); // Impossible
}
}
} // class RectangularShape
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 15797
Content-Disposition: inline; filename="RoundRectangle2D.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "19a7b423700937e95b2c697e4173b318f692c4d9"
/* RoundRectangle2D.java -- represents a rectangle with rounded corners
Copyright (C) 2000, 2002, 2003, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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 implements a rectangle with rounded corners.
* @author Tom Tromey (tromey@cygnus.com)
* @date December 3, 2000
*/
public abstract class RoundRectangle2D extends RectangularShape
{
/**
* Return the arc height of this round rectangle. The arc height and width
* control the roundness of the corners of the rectangle.
*
* @return The arc height.
*
* @see #getArcWidth()
*/
public abstract double getArcHeight();
/**
* Return the arc width of this round rectangle. The arc width and height
* control the roundness of the corners of the rectangle.
*
* @return The arc width.
*
* @see #getArcHeight()
*/
public abstract double getArcWidth();
/**
* Set the values of this round rectangle.
*
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
* @param h The height
* @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);
/**
* Create a RoundRectangle2D. This is protected because this class
* is abstract and cannot be instantiated.
*/
protected RoundRectangle2D()
{
}
/**
* Return true if this object contains the specified point.
* @param x The x coordinate
* @param y The y coordinate
*/
public boolean contains(double x, double y)
{
double mx = getX();
double mw = getWidth();
if (x < mx || x >= mx + mw)
return false;
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));
// The arc dimensions are that of the corresponding ellipse
// thus a 90 degree segment is half of that.
double aw = getArcWidth() / 2.0;
double ah = getArcHeight() / 2.0;
if (dx > aw || dy > ah)
return true;
// At this point DX represents the distance from the nearest edge
// of the rectangle. But we want to transform it to represent the
// scaled distance from the center of the ellipse that forms the
// arc. Hence this code:
dy = (ah - dy) / ah;
dx = (aw - dx) / aw;
return dx * dx + dy * dy <= 1.0;
}
/**
* Return true if this object contains the specified rectangle
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
* @param h The height
*/
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, y + h) && contains(x + w, y + h)
&& contains(x + w, y));
}
/**
* Return a new path iterator which iterates over this rectangle.
*
* @param at An affine transform to apply to the object
*/
public PathIterator getPathIterator(final AffineTransform at)
{
double arcW = Math.min(getArcWidth(), getWidth());
double arcH = Math.min(getArcHeight(), getHeight());
// check for special cases...
if (arcW <= 0 || arcH <= 0)
{
Rectangle2D r = new Rectangle2D.Double(getX(), getY(), getWidth(),
getHeight());
return r.getPathIterator(at);
}
else if (arcW >= getWidth() && arcH >= getHeight())
{
Ellipse2D e = new Ellipse2D.Double(getX(), getY(), getWidth(),
getHeight());
return e.getPathIterator(at);
}
// otherwise return the standard case...
return new PathIterator()
{
double x = getX();
double y = getY();
double w = getWidth();
double h = getHeight();
double arcW = Math.min(getArcWidth(), w);
double arcH = Math.min(getArcHeight(), h);
Arc2D.Double arc = new Arc2D.Double();
PathIterator corner;
int step = -1;
public int currentSegment(double[] coords)
{
if (corner != null) // steps 1, 3, 5 and 7
{
int r = corner.currentSegment(coords);
if (r == SEG_MOVETO)
r = SEG_LINETO;
return r;
}
if (step == -1)
{
// move to the start position
coords[0] = x + w - arcW / 2;
coords[1] = y;
}
else if (step == 0)
{
// top line
coords[0] = x + arcW / 2;
coords[1] = y;
}
else if (step == 2)
{
// left line
coords[0] = x;
coords[1] = y + h - arcH / 2;
}
else if (step == 4)
{
// bottom line
coords[0] = x + w - arcW / 2;
coords[1] = y + h;
}
else if (step == 6)
{
// right line
coords[0] = x + w;
coords[1] = y + arcH / 2;
}
if (at != null)
at.transform(coords, 0, coords, 0, 1);
return step == -1 ? SEG_MOVETO : SEG_LINETO;
}
public int currentSegment(float[] coords) {
if (corner != null) // steps 1, 3, 5 and 7
{
int r = corner.currentSegment(coords);
if (r == SEG_MOVETO)
r = SEG_LINETO;
return r;
}
if (step == -1)
{
// move to the start position
coords[0] = (float) (x + w - arcW / 2);
coords[1] = (float) y;
}
else if (step == 0)
{
// top line
coords[0] = (float) (x + arcW / 2);
coords[1] = (float) y;
}
else if (step == 2)
{
// left line
coords[0] = (float) x;
coords[1] = (float) (y + h - arcH / 2);
}
else if (step == 4)
{
// bottom line
coords[0] = (float) (x + w - arcW / 2);
coords[1] = (float) (y + h);
}
else if (step == 6)
{
// right line
coords[0] = (float) (x + w);
coords[1] = (float) (y + arcH / 2);
}
if (at != null)
at.transform(coords, 0, coords, 0, 1);
return step == -1 ? SEG_MOVETO : SEG_LINETO;
}
public int getWindingRule() {
return WIND_NON_ZERO;
}
public boolean isDone() {
return step >= 8;
}
public void next()
{
if (corner != null)
{
corner.next();
if (corner.isDone())
{
corner = null;
step++;
}
}
else
{
step++;
if (step == 1)
{
// create top left corner
arc.setArc(x, y, arcW, arcH, 90, 90, Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
else if (step == 3)
{
// create bottom left corner
arc.setArc(x, y + h - arcH, arcW, arcH, 180, 90,
Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
else if (step == 5)
{
// create bottom right corner
arc.setArc(x + w - arcW, y + h - arcH, arcW, arcH, 270, 90,
Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
else if (step == 7)
{
// create top right corner
arc.setArc(x + w - arcW, y, arcW, arcH, 0, 90, Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
}
}
};
}
/**
* Return true if the given rectangle intersects this shape.
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
* @param h The height
*/
public boolean intersects(double x, double y, double w, double h)
{
// Check if any corner is within the rectangle
return (contains(x, y) || contains(x, y + h) || contains(x + w, y + h)
|| contains(x + w, y));
}
/**
* Set the boundary of this round rectangle.
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
* @param h The height
*/
public void setFrame(double x, double y, double w, double h)
{
// This is a bit lame.
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)
{
setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(),
rr.getArcWidth(), rr.getArcHeight());
}
/**
* A subclass of RoundRectangle which keeps its parameters as
* doubles.
*/
public static class Double extends RoundRectangle2D
{
/** The height of the corner arc. */
public double archeight;
/** The width of the corner arc. */
public double arcwidth;
/** The x coordinate of this object. */
public double x;
/** The y coordinate of this object. */
public double y;
/** The width of this object. */
public double width;
/** The height of this object. */
public double height;
/**
* Construct a new instance, with all parameters set to 0.
*/
public Double()
{
}
/**
* Construct a new instance with the given arguments.
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
* @param h The height
* @param arcWidth The arc width
* @param arcHeight The arc height
*/
public Double(double x, double y, double w, double h, double arcWidth,
double arcHeight)
{
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.arcwidth = arcWidth;
this.archeight = arcHeight;
}
public double getArcHeight()
{
return archeight;
}
public double getArcWidth()
{
return arcwidth;
}
public Rectangle2D getBounds2D()
{
return new Rectangle2D.Double(x, y, width, height);
}
public double getX()
{
return x;
}
public double getY()
{
return y;
}
public double getWidth()
{
return width;
}
public double getHeight()
{
return height;
}
public boolean isEmpty()
{
return width <= 0 || height <= 0;
}
public void setRoundRect(double x, double y, double w, double h,
double arcWidth, double arcHeight)
{
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.arcwidth = arcWidth;
this.archeight = arcHeight;
}
} // class Double
/**
* A subclass of RoundRectangle which keeps its parameters as
* floats.
*/
public static class Float extends RoundRectangle2D
{
/** The height of the corner arc. */
public float archeight;
/** The width of the corner arc. */
public float arcwidth;
/** The x coordinate of this object. */
public float x;
/** The y coordinate of this object. */
public float y;
/** The width of this object. */
public float width;
/** The height of this object. */
public float height;
/**
* Construct a new instance, with all parameters set to 0.
*/
public Float()
{
}
/**
* Construct a new instance with the given arguments.
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
* @param h The height
* @param arcWidth The arc width
* @param arcHeight The arc height
*/
public Float(float x, float y, float w, float h, float arcWidth,
float arcHeight)
{
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.arcwidth = arcWidth;
this.archeight = arcHeight;
}
public double getArcHeight()
{
return archeight;
}
public double getArcWidth()
{
return arcwidth;
}
public Rectangle2D getBounds2D()
{
return new Rectangle2D.Float(x, y, width, height);
}
public double getX()
{
return x;
}
public double getY()
{
return y;
}
public double getWidth()
{
return width;
}
public double getHeight()
{
return height;
}
public boolean isEmpty()
{
return width <= 0 || height <= 0;
}
/**
* Sets the dimensions for this rounded rectangle.
*
* @param x the x-coordinate of the top left corner.
* @param y the y-coordinate of the top left corner.
* @param w the width of the rectangle.
* @param h the height of the rectangle.
* @param arcWidth the arc width.
* @param arcHeight the arc height.
*
* @see #setRoundRect(double, double, double, double, double, double)
*/
public void setRoundRect(float x, float y, float w, float h,
float arcWidth, float arcHeight)
{
this.x = x;
this.y = y;
this.width = w;
this.height = h;
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
Content-Type: text/plain; charset=UTF-8
Content-Length: 15797
Content-Disposition: inline; filename="RoundRectangle2D.java"
Last-Modified: Thu, 01 May 2025 00:06:13 GMT
Expires: Thu, 01 May 2025 00:11:13 GMT
ETag: "f35c670cec8dd6385dfd9eeb76e2453e8845513a"
/java/awt/geom/doc-files/