diff options
Diffstat (limited to 'libjava/java/awt/geom')
-rw-r--r-- | libjava/java/awt/geom/CubicCurve2D.java | 667 | ||||
-rw-r--r-- | libjava/java/awt/geom/GeneralPath.java | 839 | ||||
-rw-r--r-- | libjava/java/awt/geom/QuadCurve2D.java | 578 | ||||
-rw-r--r-- | libjava/java/awt/geom/RoundRectangle2D.java | 346 |
4 files changed, 1534 insertions, 896 deletions
diff --git a/libjava/java/awt/geom/CubicCurve2D.java b/libjava/java/awt/geom/CubicCurve2D.java index 096e7ad..56b90e9 100644 --- a/libjava/java/awt/geom/CubicCurve2D.java +++ b/libjava/java/awt/geom/CubicCurve2D.java @@ -1,5 +1,5 @@ /* CubicCurve2D.java -- represents a parameterized cubic curve in 2-D space - Copyright (C) 2002, 2003 Free Software Foundation + Copyright (C) 2002, 2003, 2004 Free Software Foundation This file is part of GNU Classpath. @@ -35,7 +35,6 @@ 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; @@ -53,12 +52,14 @@ import java.util.NoSuchElementException; * @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 +public abstract class CubicCurve2D implements Shape, Cloneable { + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + /** * Constructs a new CubicCurve2D. Typical users will want to * construct instances of a subclass, such as {@link @@ -68,87 +69,74 @@ public abstract class CubicCurve2D { } - /** * Returns the <i>x</i> coordinate of the curve’s start * point. */ public abstract double getX1(); - /** * Returns the <i>y</i> coordinate of the curve’s start * point. */ public abstract double getY1(); - /** * Returns the curve’s start point. */ public abstract Point2D getP1(); - /** * Returns the <i>x</i> coordinate of the curve’s first * control point. */ public abstract double getCtrlX1(); - /** * Returns the <i>y</i> 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 <i>x</i> coordinate of the curve’s second * control point. */ public abstract double getCtrlX2(); - /** * Returns the <i>y</i> 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 <i>x</i> coordinate of the curve’s end * point. */ public abstract double getX2(); - /** * Returns the <i>y</i> 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. @@ -183,7 +171,6 @@ public abstract class CubicCurve2D 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. @@ -206,13 +193,11 @@ public abstract class CubicCurve2D */ public void setCurve(double[] coords, int offset) { - setCurve(coords[offset++], coords[offset++], - coords[offset++], coords[offset++], - coords[offset++], coords[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. @@ -232,11 +217,10 @@ public abstract class CubicCurve2D */ 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()); + 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. @@ -258,12 +242,10 @@ public abstract class CubicCurve2D */ 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(), + 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. @@ -276,7 +258,6 @@ public abstract class CubicCurve2D 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 @@ -309,7 +290,6 @@ public abstract class CubicCurve2D 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 @@ -340,7 +320,6 @@ public abstract class CubicCurve2D 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 @@ -374,13 +353,11 @@ public abstract class CubicCurve2D */ public static double getFlatnessSq(double[] coords, int offset) { - return getFlatnessSq(coords[offset++], coords[offset++], - coords[offset++], coords[offset++], - coords[offset++], coords[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 @@ -420,7 +397,6 @@ public abstract class CubicCurve2D 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 @@ -441,7 +417,6 @@ public abstract class CubicCurve2D 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 @@ -458,12 +433,10 @@ public abstract class CubicCurve2D */ public double getFlatness() { - return Math.sqrt(getFlatnessSq(getX1(), getY1(), getCtrlX1(), - getCtrlY1(), getCtrlX2(), getCtrlY2(), - getX2(), getY2())); + return Math.sqrt(getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), + getCtrlX2(), getCtrlY2(), getX2(), getY2())); } - /** * Subdivides this curve into two halves. * @@ -482,9 +455,11 @@ public abstract class CubicCurve2D 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 }; + 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); @@ -492,7 +467,6 @@ public abstract class CubicCurve2D right.setCurve(d, 6); } - /** * Subdivides a cubic curve into two halves. * @@ -510,13 +484,12 @@ public abstract class CubicCurve2D * of <code>src</code>, or <code>null</code> if the caller is not * interested in the right half. */ - public static void subdivide(CubicCurve2D src, - CubicCurve2D left, CubicCurve2D right) + 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. @@ -563,18 +536,29 @@ public abstract class CubicCurve2D * index where the start point’s <i>x</i> coordinate will be * stored. */ - public static void subdivide(double[] src, int srcOff, - double[] left, int leftOff, - double[] right, int rightOff) + 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, src_C1_y, src_C2_x, src_C2_y; - double left_P1_x, left_P1_y; - double left_C1_x, left_C1_y, left_C2_x, left_C2_y; - double right_C1_x, right_C1_y, right_C2_x, right_C2_y; - double right_P2_x, right_P2_y; - double Mid_x, Mid_y; // Mid = left.P2 = right.P1 + 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]; @@ -599,31 +583,30 @@ public abstract class CubicCurve2D 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; - } + { + 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; - } + { + 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 @@ -670,7 +653,6 @@ public abstract class CubicCurve2D return solveCubic(eqn, eqn); } - /** * Finds the non-complex roots of a cubic equation. The following * equation is being solved: @@ -727,9 +709,19 @@ public abstract class CubicCurve2D // 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, b, c, q, r, Q, R; - double c3, Q3, R2, CR2, CQ3; + + 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]; @@ -755,219 +747,267 @@ public abstract class CubicCurve2D 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; + // The GNU Scientific Library would return three identical + // solutions in this case. + res[0] = -a / 3; + return 1; } - else + + if (CR2 == CQ3) { - res[0] = -sqrtQ - a/3; - res[1] = 2 * sqrtQ - a/3; + /* 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; } - 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 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; + 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 that is bounded + * Determines whether a position lies inside the area bounded * by the curve and the straight line connecting its end points. * * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" * alt="A drawing of the area spanned by the curve" /> * * <p>The above drawing illustrates in which area points are - * considered “contained” in a CubicCurve2D. + * considered “inside” a CubicCurve2D. */ public boolean contains(double x, double y) { - // XXX Implement. - throw new Error("not implemented"); - } + if (! getBounds2D().contains(x, y)) + return false; + return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0); + } /** - * Determines whether a point lies inside the area that is bounded + * Determines whether a point lies inside the area bounded * by the curve and the straight line connecting its end points. * * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" * alt="A drawing of the area spanned by the curve" /> * * <p>The above drawing illustrates in which area points are - * considered “contained” in a CubicCurve2D. + * 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. + * + * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>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) { - // XXX Implement. - throw new Error("not implemented"); - } + 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. + * + * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>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) { - // XXX Implement. - throw new Error("not implemented"); - } + 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. + * + * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>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. 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. - * - * <p><img src="doc-files/CubicCurve2D-2.png" width="350" height="180" - * alt="An illustration of the bounds of a CubicCurve2D" /> + * 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; - } - }; + /** 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. * @@ -976,15 +1016,118 @@ public abstract class CubicCurve2D public Object clone() { try - { - return super.clone(); - } + { + return super.clone(); + } catch (CloneNotSupportedException e) - { - throw (Error) new InternalError().initCause(e); // Impossible - } + { + 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() * (1E-10); + if (a0 == 0.0) + a0 += small; + if (a3 == 0.0) + a3 += small; + } + + if (useYaxis) + { + if (Line2D.linesIntersect(b0, a0, b3, a3, 0.0, 0.0, distance, 0.0)) + nCrossings++; + } + else + { + if (Line2D.linesIntersect(a0, b0, a3, b3, 0.0, 0.0, 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 @@ -996,57 +1139,48 @@ public abstract class CubicCurve2D * @author Eric Blake (ebb9@email.byu.edu) * @author Sascha Brawer (brawer@dandelis.ch) */ - public static class Double - extends CubicCurve2D + public static class Double extends CubicCurve2D { /** * The <i>x</i> coordinate of the curve’s start point. */ public double x1; - /** * The <i>y</i> coordinate of the curve’s start point. */ public double y1; - /** * The <i>x</i> coordinate of the curve’s first control point. */ public double ctrlx1; - /** * The <i>y</i> coordinate of the curve’s first control point. */ public double ctrly1; - /** * The <i>x</i> coordinate of the curve’s second control point. */ public double ctrlx2; - /** * The <i>y</i> coordinate of the curve’s second control point. */ public double ctrly2; - /** * The <i>x</i> coordinate of the curve’s end point. */ public double x2; - /** * The <i>y</i> 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 @@ -1056,7 +1190,6 @@ public abstract class CubicCurve2D { } - /** * Constructs a new CubicCurve2D that stores its coordinate values * in double-precision floating-point format, specifying the @@ -1089,8 +1222,8 @@ public abstract class CubicCurve2D * @param y2 the <i>y</i> 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) + public Double(double x1, double y1, double cx1, double cy1, double cx2, + double cy2, double x2, double y2) { this.x1 = x1; this.y1 = y1; @@ -1102,7 +1235,6 @@ public abstract class CubicCurve2D this.y2 = y2; } - /** * Returns the <i>x</i> coordinate of the curve’s start * point. @@ -1112,7 +1244,6 @@ public abstract class CubicCurve2D return x1; } - /** * Returns the <i>y</i> coordinate of the curve’s start * point. @@ -1122,7 +1253,6 @@ public abstract class CubicCurve2D return y1; } - /** * Returns the curve’s start point. */ @@ -1131,7 +1261,6 @@ public abstract class CubicCurve2D return new Point2D.Double(x1, y1); } - /** * Returns the <i>x</i> coordinate of the curve’s first * control point. @@ -1141,7 +1270,6 @@ public abstract class CubicCurve2D return ctrlx1; } - /** * Returns the <i>y</i> coordinate of the curve’s first * control point. @@ -1151,7 +1279,6 @@ public abstract class CubicCurve2D return ctrly1; } - /** * Returns the curve’s first control point. */ @@ -1160,7 +1287,6 @@ public abstract class CubicCurve2D return new Point2D.Double(ctrlx1, ctrly1); } - /** * Returns the <i>x</i> coordinate of the curve’s second * control point. @@ -1170,7 +1296,6 @@ public abstract class CubicCurve2D return ctrlx2; } - /** * Returns the <i>y</i> coordinate of the curve’s second * control point. @@ -1180,7 +1305,6 @@ public abstract class CubicCurve2D return ctrly2; } - /** * Returns the curve’s second control point. */ @@ -1189,7 +1313,6 @@ public abstract class CubicCurve2D return new Point2D.Double(ctrlx2, ctrly2); } - /** * Returns the <i>x</i> coordinate of the curve’s end * point. @@ -1199,7 +1322,6 @@ public abstract class CubicCurve2D return x2; } - /** * Returns the <i>y</i> coordinate of the curve’s end * point. @@ -1209,7 +1331,6 @@ public abstract class CubicCurve2D return y2; } - /** * Returns the curve’s end point. */ @@ -1218,7 +1339,6 @@ public abstract class CubicCurve2D return new Point2D.Double(x2, y2); } - /** * Changes the curve geometry, separately specifying each coordinate * value. @@ -1263,7 +1383,6 @@ public abstract class CubicCurve2D this.y2 = y2; } - /** * Determines the smallest rectangle that encloses the * curve’s start, end and control points. As the @@ -1284,7 +1403,6 @@ public abstract class CubicCurve2D } } - /** * A two-dimensional curve that is parameterized with a cubic * function and stores coordinate values in single-precision @@ -1295,57 +1413,48 @@ public abstract class CubicCurve2D * @author Eric Blake (ebb9@email.byu.edu) * @author Sascha Brawer (brawer@dandelis.ch) */ - public static class Float - extends CubicCurve2D + public static class Float extends CubicCurve2D { /** * The <i>x</i> coordinate of the curve’s start point. */ public float x1; - /** * The <i>y</i> coordinate of the curve’s start point. */ public float y1; - /** * The <i>x</i> coordinate of the curve’s first control point. */ public float ctrlx1; - /** * The <i>y</i> coordinate of the curve’s first control point. */ public float ctrly1; - /** * The <i>x</i> coordinate of the curve’s second control point. */ public float ctrlx2; - /** * The <i>y</i> coordinate of the curve’s second control point. */ public float ctrly2; - /** * The <i>x</i> coordinate of the curve’s end point. */ public float x2; - /** * The <i>y</i> 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 @@ -1355,7 +1464,6 @@ public abstract class CubicCurve2D { } - /** * Constructs a new CubicCurve2D that stores its coordinate values * in single-precision floating-point format, specifying the @@ -1388,8 +1496,8 @@ public abstract class CubicCurve2D * @param y2 the <i>y</i> 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) + public Float(float x1, float y1, float cx1, float cy1, float cx2, + float cy2, float x2, float y2) { this.x1 = x1; this.y1 = y1; @@ -1401,7 +1509,6 @@ public abstract class CubicCurve2D this.y2 = y2; } - /** * Returns the <i>x</i> coordinate of the curve’s start * point. @@ -1411,7 +1518,6 @@ public abstract class CubicCurve2D return x1; } - /** * Returns the <i>y</i> coordinate of the curve’s start * point. @@ -1421,7 +1527,6 @@ public abstract class CubicCurve2D return y1; } - /** * Returns the curve’s start point. */ @@ -1430,7 +1535,6 @@ public abstract class CubicCurve2D return new Point2D.Float(x1, y1); } - /** * Returns the <i>x</i> coordinate of the curve’s first * control point. @@ -1440,7 +1544,6 @@ public abstract class CubicCurve2D return ctrlx1; } - /** * Returns the <i>y</i> coordinate of the curve’s first * control point. @@ -1450,7 +1553,6 @@ public abstract class CubicCurve2D return ctrly1; } - /** * Returns the curve’s first control point. */ @@ -1459,7 +1561,6 @@ public abstract class CubicCurve2D return new Point2D.Float(ctrlx1, ctrly1); } - /** * Returns the <i>s</i> coordinate of the curve’s second * control point. @@ -1469,7 +1570,6 @@ public abstract class CubicCurve2D return ctrlx2; } - /** * Returns the <i>y</i> coordinate of the curve’s second * control point. @@ -1479,7 +1579,6 @@ public abstract class CubicCurve2D return ctrly2; } - /** * Returns the curve’s second control point. */ @@ -1488,7 +1587,6 @@ public abstract class CubicCurve2D return new Point2D.Float(ctrlx2, ctrly2); } - /** * Returns the <i>x</i> coordinate of the curve’s end * point. @@ -1498,7 +1596,6 @@ public abstract class CubicCurve2D return x2; } - /** * Returns the <i>y</i> coordinate of the curve’s end * point. @@ -1508,7 +1605,6 @@ public abstract class CubicCurve2D return y2; } - /** * Returns the curve’s end point. */ @@ -1517,7 +1613,6 @@ public abstract class CubicCurve2D return new Point2D.Float(x2, y2); } - /** * Changes the curve geometry, separately specifying each coordinate * value as a double-precision floating-point number. @@ -1562,7 +1657,6 @@ public abstract class CubicCurve2D this.y2 = (float) y2; } - /** * Changes the curve geometry, separately specifying each coordinate * value as a single-precision floating-point number. @@ -1594,8 +1688,8 @@ public abstract class CubicCurve2D * @param y2 the <i>y</i> 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) + 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; @@ -1607,7 +1701,6 @@ public abstract class CubicCurve2D this.y2 = y2; } - /** * Determines the smallest rectangle that encloses the * curve’s start, end and control points. As the diff --git a/libjava/java/awt/geom/GeneralPath.java b/libjava/java/awt/geom/GeneralPath.java index 05d98c7..40182ea 100644 --- a/libjava/java/awt/geom/GeneralPath.java +++ b/libjava/java/awt/geom/GeneralPath.java @@ -1,50 +1,80 @@ /* GeneralPath.java -- represents a shape built from subpaths - Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA -02111-1307 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ - + 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., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ package java.awt.geom; import java.awt.Rectangle; import java.awt.Shape; + /** - * STUBS ONLY - * XXX Implement and document. Note that Sun's implementation only expects - * float precision, not double. + * A general geometric path, consisting of any number of subpaths + * constructed out of straight lines and cubic or quadratic Bezier + * curves. + * + * <p>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. + * + * <p><img src="doc-files/GeneralPath-1.png" width="300" height="210" + * alt="A drawing of a GeneralPath" /> + * <p>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 <b>A</b> and + * <b>C</b> in the image are considered to be outside the path. + * (both intersect twice) + * Point <b>B</b> intersects once, and is inside. + * + * <p>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 <b>A</b> in the image is outside (one intersection in the + * ’up’ + * direction, one in the ’down’ direction) Point <b>B</b> in + * the image is inside (one intersection ’down’) + * Point <b>C</b> in the image is outside (two intersections + * ’down’) + * + * @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 { @@ -52,35 +82,63 @@ public final class GeneralPath implements Shape, Cloneable public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; /** Initial size if not specified. */ - private static final int INIT_SIZE = 20; + 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 = java.lang.Double.MAX_VALUE / 10.0; /** The winding rule. */ private int rule; + /** - * The path type in points. Note that points[index] maps to - * types[index >> 1]; the control points of quad and cubic paths map as + * 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. */ private byte[] types; + /** * The list of all points seen. Since you can only append floats, it makes - * sense for this to be a float[]. I have no idea why Sun didn't choose to + * 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. */ - private float[] points; + private float[] xpoints; + private float[] ypoints; + /** The index of the most recent moveto point, or null. */ private int subpath = -1; + /** The next available index into points. */ private 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 (WIND_NON_ZERO or WIND_EVEN_ODD) + */ 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 (WIND_NON_ZERO or WIND_EVEN_ODD) + * @param capacity the inital capacity, in path segments + */ public GeneralPath(int rule, int capacity) { if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) @@ -88,68 +146,112 @@ public final class GeneralPath implements Shape, Cloneable this.rule = rule; if (capacity < INIT_SIZE) capacity = INIT_SIZE; - types = new byte[capacity >> 1]; - points = new float[capacity]; + 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 + */ public GeneralPath(Shape s) { - types = new byte[INIT_SIZE >> 1]; - points = new float[INIT_SIZE]; + 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. + */ public void moveTo(float x, float y) { subpath = index; - ensureSize(index + 2); - types[index >> 1] = PathIterator.SEG_MOVETO; - points[index++] = x; - points[index++] = y; + 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 + 2); - types[index >> 1] = PathIterator.SEG_LINETO; - points[index++] = x; - points[index++] = 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 + 4); - types[index >> 1] = PathIterator.SEG_QUADTO; - points[index++] = x1; - points[index++] = y1; - points[index++] = x2; - points[index++] = y2; - } - public void curveTo(float x1, float y1, float x2, float y2, - float x3, float y3) - { - ensureSize(index + 6); - types[index >> 1] = PathIterator.SEG_CUBICTO; - points[index++] = x1; - points[index++] = y1; - points[index++] = x2; - points[index++] = y2; - points[index++] = x3; - points[index++] = y3; + 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. + */ public void closePath() { - ensureSize(index + 2); - types[index >> 1] = PathIterator.SEG_CLOSE; - points[index++] = points[subpath]; - points[index++] = points[subpath + 1]; + 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 <code>connect</code> is + * true, the new path segments are connected to the existing one with a line. + * The winding rule of the Shape is ignored. + */ 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 @@ -158,7 +260,7 @@ public final class GeneralPath implements Shape, Cloneable * * @param iter the PathIterator specifying which segments shall be * appended. - * + * * @param connect <code>true</code> for substituting the initial * {@link PathIterator#SEG_MOVETO} segment by a {@link * PathIterator#SEG_LINETO}, or <code>false</code> for not @@ -171,50 +273,55 @@ public final class GeneralPath implements Shape, Cloneable { // A bad implementation of this method had caused Classpath bug #6076. float[] f = new float[6]; - while (!iter.isDone()) - { - switch (iter.currentSegment(f)) + while (! iter.isDone()) { - case PathIterator.SEG_MOVETO: - if (!connect || (index == 0)) - { - moveTo(f[0], f[1]); - break; - } - - if ((index >= 2) && (types[(index - 2) >> 2] == PathIterator.SEG_CLOSE) - && (f[0] == points[index - 2]) && (f[1] == points[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; + 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(); } - - connect = false; - iter.next(); - } } - + /** + * Returns the path’s current winding rule. + */ 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. + */ public void setWindingRule(int rule) { if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) @@ -222,22 +329,48 @@ public final class GeneralPath implements Shape, Cloneable this.rule = rule; } + /** + * Returns the current appending point of the path. + */ public Point2D getCurrentPoint() { if (subpath < 0) return null; - return new Point2D.Float(points[index - 2], points[index - 1]); + 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. + */ public void transform(AffineTransform xform) { - xform.transform(points, 0, points, 0, index >> 1); + 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); @@ -245,85 +378,174 @@ public final class GeneralPath implements Shape, Cloneable return p; } + /** + * Returns the path’s bounding box. + */ public Rectangle getBounds() { return getBounds2D().getBounds(); } + + /** + * Returns the path’s bounding box, in <code>float</code> precision + */ public Rectangle2D getBounds2D() { - // XXX Implement. - throw new Error("not implemented"); + 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) { - // XXX Implement. - throw new Error("not implemented"); + 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) { - // XXX Implement. - throw new Error("not implemented"); + 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 <code>true</code> if the rectangle is completely contained + * within the path, <code>false</code> 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 <code>true</code> if the rectangle intersects the path, + * <code>false</code> otherwise + */ public boolean intersects(double x, double y, double w, double h) { - // XXX Implement. - throw new Error("not implemented"); + /* 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 <code>true</code> if the rectangle intersects the path, + * <code>false</code> 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 + private static class GeneralPathIterator implements PathIterator { /** * The number of coordinate values for each segment type. */ - private static final int[] NUM_COORDS = - { - /* 0: SEG_MOVETO */ 2, - /* 1: SEG_LINETO */ 2, - /* 2: SEG_QUADTO */ 4, - /* 3: SEG_CUBICTO */ 6, - /* 4: SEG_CLOSE */ 0 - }; - + 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. */ private 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. @@ -338,7 +560,6 @@ public final class GeneralPath implements Shape, Cloneable this.transform = transform; } - /** * Returns the current winding rule of the GeneralPath. */ @@ -347,7 +568,6 @@ public final class GeneralPath implements Shape, Cloneable return path.rule; } - /** * Determines whether the iterator has reached the last segment in * the path. @@ -357,7 +577,6 @@ public final class GeneralPath implements Shape, Cloneable return pos >= path.index; } - /** * Advances the iterator position by one segment. */ @@ -365,70 +584,72 @@ public final class GeneralPath implements Shape, Cloneable { int seg; - /* Increment pos by the number of coordinate values. Note that - * we store two values even for a SEG_CLOSE segment, which is - * why we increment pos at least by 2. + /* + * Increment pos by the number of coordinate pairs. */ - seg = path.types[pos >> 1]; + seg = path.types[pos]; if (seg == SEG_CLOSE) - pos += 2; + pos++; else - pos += NUM_COORDS[seg]; + pos += NUM_COORDS[seg]; } - /** * Returns the current segment in float coordinates. */ public int currentSegment(float[] coords) { - int seg, numCoords; + int seg; + int numCoords; - seg = path.types[pos >> 1]; + seg = path.types[pos]; numCoords = NUM_COORDS[seg]; if (numCoords > 0) - { - if (transform == null) - System.arraycopy(path.points, pos, coords, 0, numCoords); - else - transform.transform(/* src */ path.points, /* srcOffset */ pos, - /* dest */ coords, /* destOffset */ 0, - /* numPoints */ numCoords >> 1); - } + { + 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, numCoords; + int seg; + int numCoords; - seg = path.types[pos >> 1]; + seg = path.types[pos]; numCoords = NUM_COORDS[seg]; if (numCoords > 0) - { - if (transform == null) { - // System.arraycopy throws an exception if the source and destination - // array are not of the same primitive type. - for (int i = 0; i < numCoords; i++) - coords[i] = (double) path.points[pos + i]; + 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 */ + pos, /* dest */ coords, /* destOffset */ + 0, /* numPoints */ numCoords); } - else - transform.transform(/* src */ path.points, /* srcOffset */ pos, - /* dest */ coords, /* destOffset */ 0, - /* numPoints */ numCoords >> 1); - } return seg; } } - /** - * Creates a PathIterator for iterating along the segments of this path. + * Creates a PathIterator for iterating along the segments of the path. * * @param at an affine transformation for projecting the returned * points, or <code>null</code> to let the created iterator return @@ -439,15 +660,17 @@ public final class GeneralPath implements Shape, Cloneable 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); } /** - * Create a new shape of the same run-time type with the same contents as - * this one. + * Creates a new shape of the same run-time type with the same contents + * as this one. * * @return the clone * @@ -461,17 +684,261 @@ public final class GeneralPath implements Shape, Cloneable return new GeneralPath(this); } + /** + * Helper method - ensure the size of the data arrays, + * otherwise, reallocate new ones twice the size + */ private void ensureSize(int size) { if (subpath < 0) throw new IllegalPathStateException("need initial moveto"); - if (size <= points.length) + if (size <= xpoints.length) return; - byte[] b = new byte[points.length]; - System.arraycopy(types, 0, b, 0, index >> 1); + byte[] b = new byte[types.length << 1]; + System.arraycopy(types, 0, b, 0, index); types = b; - float[] f = new float[points.length << 1]; - System.arraycopy(points, 0, f, 0, index); - points = f; + 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-9; + + 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, 0.0, 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, 0.0, 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, 0.0, 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 diff --git a/libjava/java/awt/geom/QuadCurve2D.java b/libjava/java/awt/geom/QuadCurve2D.java index 409c484..0cc9eb4 100644 --- a/libjava/java/awt/geom/QuadCurve2D.java +++ b/libjava/java/awt/geom/QuadCurve2D.java @@ -1,5 +1,5 @@ /* QuadCurve2D.java -- represents a parameterized quadratic curve in 2-D space - Copyright (C) 2002, 2003 Free Software Foundation + Copyright (C) 2002, 2003, 2004 Free Software Foundation This file is part of GNU Classpath. @@ -35,7 +35,6 @@ 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; @@ -53,12 +52,14 @@ import java.util.NoSuchElementException; * @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 +public abstract class QuadCurve2D implements Shape, Cloneable { + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + /** * Constructs a new QuadCurve2D. Typical users will want to * construct instances of a subclass, such as {@link @@ -68,67 +69,57 @@ public abstract class QuadCurve2D { } - /** * Returns the <i>x</i> coordinate of the curve’s start * point. */ public abstract double getX1(); - /** * Returns the <i>y</i> coordinate of the curve’s start * point. */ public abstract double getY1(); - /** * Returns the curve’s start point. */ public abstract Point2D getP1(); - /** * Returns the <i>x</i> coordinate of the curve’s control * point. */ public abstract double getCtrlX(); - /** * Returns the <i>y</i> coordinate of the curve’s control * point. */ public abstract double getCtrlY(); - /** * Returns the curve’s control point. */ public abstract Point2D getCtrlPt(); - /** * Returns the <i>x</i> coordinate of the curve’s end * point. */ public abstract double getX2(); - /** * Returns the <i>y</i> 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. @@ -154,7 +145,6 @@ public abstract class QuadCurve2D 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. @@ -174,12 +164,10 @@ public abstract class QuadCurve2D */ public void setCurve(double[] coords, int offset) { - setCurve(coords[offset++], coords[offset++], - coords[offset++], coords[offset++], - coords[offset++], coords[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. @@ -198,11 +186,9 @@ public abstract class QuadCurve2D */ public void setCurve(Point2D p1, Point2D c, Point2D p2) { - setCurve(p1.getX(), p1.getY(), c.getX(), c.getY(), - p2.getX(), p2.getY()); + 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. @@ -223,12 +209,11 @@ public abstract class QuadCurve2D */ 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()); + 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. * @@ -236,11 +221,10 @@ public abstract class QuadCurve2D */ public void setCurve(QuadCurve2D c) { - setCurve(c.getX1(), c.getY1(), c.getCtrlX(), c.getCtrlY(), - c.getX2(), c.getY2()); + 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 @@ -267,7 +251,6 @@ public abstract class QuadCurve2D 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 @@ -294,7 +277,6 @@ public abstract class QuadCurve2D 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 @@ -328,7 +310,6 @@ public abstract class QuadCurve2D 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 @@ -362,7 +343,6 @@ public abstract class QuadCurve2D 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 @@ -378,12 +358,10 @@ public abstract class QuadCurve2D */ public double getFlatnessSq() { - return Line2D.ptSegDistSq(getX1(), getY1(), - getX2(), getY2(), - getCtrlX(), getCtrlY()); + 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 @@ -399,12 +377,10 @@ public abstract class QuadCurve2D */ public double getFlatness() { - return Line2D.ptSegDist(getX1(), getY1(), - getX2(), getY2(), - getCtrlX(), getCtrlY()); + return Line2D.ptSegDist(getX1(), getY1(), getX2(), getY2(), getCtrlX(), + getCtrlY()); } - /** * Subdivides this curve into two halves. * @@ -423,8 +399,11 @@ public abstract class QuadCurve2D 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 }; + 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); @@ -432,7 +411,6 @@ public abstract class QuadCurve2D right.setCurve(d, 4); } - /** * Subdivides a quadratic curve into two halves. * @@ -456,7 +434,6 @@ public abstract class QuadCurve2D src.subdivide(left, right); } - /** * Subdivides a quadratic curve into two halves, passing all * coordinates in an array. @@ -500,11 +477,15 @@ public abstract class QuadCurve2D * index where the start point’s <i>x</i> coordinate will be * stored. */ - public static void subdivide(double[] src, int srcOff, - double[] left, int leftOff, - double[] right, int rightOff) + public static void subdivide(double[] src, int srcOff, double[] left, + int leftOff, double[] right, int rightOff) { - double x1, y1, xc, yc, x2, y2; + double x1; + double y1; + double xc; + double yc; + double x2; + double y2; x1 = src[srcOff]; y1 = src[srcOff + 1]; @@ -514,16 +495,16 @@ public abstract class QuadCurve2D y2 = src[srcOff + 5]; if (left != null) - { - left[leftOff] = x1; - left[leftOff + 1] = y1; - } + { + left[leftOff] = x1; + left[leftOff + 1] = y1; + } if (right != null) - { - right[rightOff + 4] = x2; - right[rightOff + 5] = y2; - } + { + right[rightOff + 4] = x2; + right[rightOff + 5] = y2; + } x1 = (x1 + xc) / 2; x2 = (xc + x2) / 2; @@ -533,23 +514,22 @@ public abstract class QuadCurve2D yc = (y1 + y2) / 2; if (left != null) - { - left[leftOff + 2] = x1; - left[leftOff + 3] = y1; - left[leftOff + 4] = xc; - left[leftOff + 5] = yc; - } + { + 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; - } + { + 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 @@ -594,7 +574,6 @@ public abstract class QuadCurve2D return solveQuadratic(eqn, eqn); } - /** * Finds the non-complex roots of a quadratic equation. The * following equation is being solved: @@ -649,8 +628,10 @@ public abstract class QuadCurve2D // 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, b, c, disc; + double a; + double b; + double c; + double disc; c = eqn[0]; b = eqn[1]; @@ -661,13 +642,13 @@ public abstract class QuadCurve2D // 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; - } + { + if (b == 0) + return -1; + + res[0] = -c / b; + return 1; + } disc = b * b - 4 * a * c; @@ -675,96 +656,149 @@ public abstract class QuadCurve2D 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; - } + { + // 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; + { + double r; - r = Math.abs(0.5 * Math.sqrt(disc) / a); - res[0] = -r; - res[1] = r; - } + r = Math.abs(0.5 * Math.sqrt(disc) / a); + res[0] = -r; + res[1] = r; + } else - { - double sgnb, 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; - } + { + 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 lies inside the area that is bounded + * Determines whether a point is inside the area bounded * by the curve and the straight line connecting its end points. * * <p><img src="doc-files/QuadCurve2D-5.png" width="350" height="180" * alt="A drawing of the area spanned by the curve" /> * * <p>The above drawing illustrates in which area points are - * considered “contained” in a QuadCurve2D. + * considered “inside” a QuadCurve2D. */ public boolean contains(double x, double y) { - // XXX Implement. - throw new Error("not implemented"); - } + if (! getBounds2D().contains(x, y)) + return false; + return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0); + } /** - * Determines whether a point lies inside the area that is bounded + * Determines whether a point is inside the area bounded * by the curve and the straight line connecting its end points. * * <p><img src="doc-files/QuadCurve2D-5.png" width="350" height="180" * alt="A drawing of the area spanned by the curve" /> * * <p>The above drawing illustrates in which area points are - * considered “contained” in a QuadCurve2D. + * 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. + * + * <p><img src="doc-files/QuadCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>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) { - // XXX Implement. - throw new Error("not implemented"); - } + 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. + * + * <p><img src="doc-files/QuadCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>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) { - // XXX Implement. - throw new Error("not implemented"); - } + 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 @@ -780,97 +814,85 @@ public abstract class QuadCurve2D 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; - } - }; + /** 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. * @@ -879,15 +901,106 @@ public abstract class QuadCurve2D public Object clone() { try - { - return super.clone(); - } + { + return super.clone(); + } catch (CloneNotSupportedException e) - { - throw (Error) new InternalError().initCause(e); // Impossible - } + { + 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() * (1E-10); + 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, 0.0, 0.0, distance, 0.0)) + nCrossings++; + } + else + { + if (Line2D.linesIntersect(a0, b0, a2, b2, 0.0, 0.0, 0.0, distance)) + nCrossings++; + } + + return (nCrossings); + } /** * A two-dimensional curve that is parameterized with a quadratic @@ -899,45 +1012,38 @@ public abstract class QuadCurve2D * @author Eric Blake (ebb9@email.byu.edu) * @author Sascha Brawer (brawer@dandelis.ch) */ - public static class Double - extends QuadCurve2D + public static class Double extends QuadCurve2D { /** * The <i>x</i> coordinate of the curve’s start point. */ public double x1; - /** * The <i>y</i> coordinate of the curve’s start point. */ public double y1; - /** * The <i>x</i> coordinate of the curve’s control point. */ public double ctrlx; - /** * The <i>y</i> coordinate of the curve’s control point. */ public double ctrly; - /** * The <i>x</i> coordinate of the curve’s end point. */ public double x2; - /** * The <i>y</i> 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 @@ -947,7 +1053,6 @@ public abstract class QuadCurve2D { } - /** * Constructs a new QuadCurve2D that stores its coordinate values * in double-precision floating-point format, specifying the @@ -971,8 +1076,8 @@ public abstract class QuadCurve2D * @param y2 the <i>y</i> coordinate of the curve’s end * point. */ - public Double(double x1, double y1, double cx, double cy, - double x2, double y2) + public Double(double x1, double y1, double cx, double cy, double x2, + double y2) { this.x1 = x1; this.y1 = y1; @@ -982,7 +1087,6 @@ public abstract class QuadCurve2D this.y2 = y2; } - /** * Returns the <i>x</i> coordinate of the curve’s start * point. @@ -992,7 +1096,6 @@ public abstract class QuadCurve2D return x1; } - /** * Returns the <i>y</i> coordinate of the curve’s start * point. @@ -1002,7 +1105,6 @@ public abstract class QuadCurve2D return y1; } - /** * Returns the curve’s start point. */ @@ -1011,7 +1113,6 @@ public abstract class QuadCurve2D return new Point2D.Double(x1, y1); } - /** * Returns the <i>x</i> coordinate of the curve’s control * point. @@ -1021,7 +1122,6 @@ public abstract class QuadCurve2D return ctrlx; } - /** * Returns the <i>y</i> coordinate of the curve’s control * point. @@ -1031,7 +1131,6 @@ public abstract class QuadCurve2D return ctrly; } - /** * Returns the curve’s control point. */ @@ -1040,7 +1139,6 @@ public abstract class QuadCurve2D return new Point2D.Double(ctrlx, ctrly); } - /** * Returns the <i>x</i> coordinate of the curve’s end * point. @@ -1050,7 +1148,6 @@ public abstract class QuadCurve2D return x2; } - /** * Returns the <i>y</i> coordinate of the curve’s end * point. @@ -1060,7 +1157,6 @@ public abstract class QuadCurve2D return y2; } - /** * Returns the curve’s end point. */ @@ -1069,7 +1165,6 @@ public abstract class QuadCurve2D return new Point2D.Double(x2, y2); } - /** * Changes the geometry of the curve. * @@ -1102,7 +1197,6 @@ public abstract class QuadCurve2D this.y2 = y2; } - /** * Determines the smallest rectangle that encloses the * curve’s start, end and control point. As the @@ -1123,7 +1217,6 @@ public abstract class QuadCurve2D } } - /** * A two-dimensional curve that is parameterized with a quadratic * function and stores coordinate values in single-precision @@ -1134,45 +1227,38 @@ public abstract class QuadCurve2D * @author Eric Blake (ebb9@email.byu.edu) * @author Sascha Brawer (brawer@dandelis.ch) */ - public static class Float - extends QuadCurve2D + public static class Float extends QuadCurve2D { /** * The <i>x</i> coordinate of the curve’s start point. */ public float x1; - /** * The <i>y</i> coordinate of the curve’s start point. */ public float y1; - /** * The <i>x</i> coordinate of the curve’s control point. */ public float ctrlx; - /** * The <i>y</i> coordinate of the curve’s control point. */ public float ctrly; - /** * The <i>x</i> coordinate of the curve’s end point. */ public float x2; - /** * The <i>y</i> 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 @@ -1182,7 +1268,6 @@ public abstract class QuadCurve2D { } - /** * Constructs a new QuadCurve2D that stores its coordinate values * in single-precision floating-point format, specifying the @@ -1206,8 +1291,7 @@ public abstract class QuadCurve2D * @param y2 the <i>y</i> coordinate of the curve’s end * point. */ - public Float(float x1, float y1, float cx, float cy, - float x2, float y2) + public Float(float x1, float y1, float cx, float cy, float x2, float y2) { this.x1 = x1; this.y1 = y1; @@ -1217,7 +1301,6 @@ public abstract class QuadCurve2D this.y2 = y2; } - /** * Returns the <i>x</i> coordinate of the curve’s start * point. @@ -1227,7 +1310,6 @@ public abstract class QuadCurve2D return x1; } - /** * Returns the <i>y</i> coordinate of the curve’s start * point. @@ -1237,7 +1319,6 @@ public abstract class QuadCurve2D return y1; } - /** * Returns the curve’s start point. */ @@ -1246,7 +1327,6 @@ public abstract class QuadCurve2D return new Point2D.Float(x1, y1); } - /** * Returns the <i>x</i> coordinate of the curve’s control * point. @@ -1256,7 +1336,6 @@ public abstract class QuadCurve2D return ctrlx; } - /** * Returns the <i>y</i> coordinate of the curve’s control * point. @@ -1266,7 +1345,6 @@ public abstract class QuadCurve2D return ctrly; } - /** * Returns the curve’s control point. */ @@ -1275,7 +1353,6 @@ public abstract class QuadCurve2D return new Point2D.Float(ctrlx, ctrly); } - /** * Returns the <i>x</i> coordinate of the curve’s end * point. @@ -1285,7 +1362,6 @@ public abstract class QuadCurve2D return x2; } - /** * Returns the <i>y</i> coordinate of the curve’s end * point. @@ -1295,7 +1371,6 @@ public abstract class QuadCurve2D return y2; } - /** * Returns the curve’s end point. */ @@ -1304,7 +1379,6 @@ public abstract class QuadCurve2D return new Point2D.Float(x2, y2); } - /** * Changes the geometry of the curve, specifying coordinate values * as double-precision floating-point numbers. @@ -1338,7 +1412,6 @@ public abstract class QuadCurve2D this.y2 = (float) y2; } - /** * Changes the geometry of the curve, specifying coordinate values * as single-precision floating-point numbers. @@ -1361,8 +1434,8 @@ public abstract class QuadCurve2D * @param y2 the <i>y</i> coordinate of the curve’s new * end point. */ - public void setCurve(float x1, float y1, float cx, float cy, - float x2, float y2) + public void setCurve(float x1, float y1, float cx, float cy, float x2, + float y2) { this.x1 = x1; this.y1 = y1; @@ -1372,7 +1445,6 @@ public abstract class QuadCurve2D this.y2 = y2; } - /** * Determines the smallest rectangle that encloses the * curve’s start, end and control point. As the diff --git a/libjava/java/awt/geom/RoundRectangle2D.java b/libjava/java/awt/geom/RoundRectangle2D.java index 3a7899d..3f004ed 100644 --- a/libjava/java/awt/geom/RoundRectangle2D.java +++ b/libjava/java/awt/geom/RoundRectangle2D.java @@ -1,5 +1,5 @@ /* RoundRectangle2D.java -- represents a rectangle with rounded corners - Copyright (C) 2000, 2002, 2003 Free Software Foundation + Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation This file is part of GNU Classpath. @@ -39,6 +39,7 @@ package java.awt.geom; import java.util.NoSuchElementException; + /** This class implements a rectangle with rounded corners. * @author Tom Tromey <tromey@cygnus.com> * @date December 3, 2000 @@ -60,12 +61,12 @@ public abstract class RoundRectangle2D extends RectangularShape * @param arcHeight The arc height */ public abstract void setRoundRect(double x, double y, double w, double h, - double arcWidth, double arcHeight); + double arcWidth, double arcHeight); /** Create a RoundRectangle2D. This is protected because this class * is abstract and cannot be instantiated. */ - protected RoundRectangle2D() + protected RoundRectangle2D() { } @@ -87,8 +88,11 @@ public abstract class RoundRectangle2D extends RectangularShape // Now check to see if the point is in range of an arc. double dy = Math.min(Math.abs(my - y), Math.abs(my + mh - y)); double dx = Math.min(Math.abs(mx - x), Math.abs(mx + mw - x)); - double aw = getArcWidth(); - double ah = getArcHeight(); + + // 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; @@ -112,8 +116,8 @@ public abstract class RoundRectangle2D extends RectangularShape { // We have to check all four points here (for ordinary rectangles // we can just check opposing corners). - return (contains(x, y) && contains(x + w, h) - && contains(x, y + h) && contains(x + w, y + h)); + return (contains(x, y) && contains(x, y + h) && contains(x + w, y + h) + && contains(x + w, y)); } /** Return a new path iterator which iterates over this rectangle. @@ -128,154 +132,161 @@ public abstract class RoundRectangle2D extends RectangularShape final double arcwidth = getArcWidth(); final double archeight = getArcHeight(); return new PathIterator() - { - /** We iterate clockwise around the rectangle, starting in the - * upper left. This variable tracks our current point, which - * can be on either side of a given corner. */ - private int current = 0; - - /** Child path iterator, used for corners. */ - private PathIterator corner; - - /** This is used when rendering the corners. We re-use the arc - * for each corner. */ - private Arc2D arc = new Arc2D.Double(); - - /** Temporary array used by getPoint. */ - private double[] temp = new double[2]; - - public int getWindingRule() { - return WIND_NON_ZERO; - } - - public boolean isDone() - { - return current > 9; - } - - private void getPoint(int val) - { - switch (val) - { - case 0: - case 8: - temp[0] = minx; - temp[1] = miny + archeight; - break; - case 1: - temp[0] = minx + arcwidth; - temp[1] = miny; - break; - case 2: - temp[0] = maxx - arcwidth; - temp[1] = maxy; - break; - case 3: - temp[0] = maxx; - temp[1] = miny + archeight; - break; - case 4: - temp[0] = maxx; - temp[1] = maxy - archeight; - break; - case 5: - temp[0] = maxx - arcwidth; - temp[1] = maxy; - break; - case 6: - temp[0] = minx + arcwidth; - temp[1] = maxy; - break; - case 7: - temp[0] = minx; - temp[1] = maxy - archeight; - break; - } - } - - public void next() - { - if (current >= 8) - ++current; - else if (corner != null) - { - // We're iterating through the corner. Work on the child - // iterator; if it finishes, reset and move to the next - // point along the rectangle. - corner.next(); - if (corner.isDone()) - { - corner = null; - ++current; - } - } - else - { - // Make an arc between this point on the rectangle and - // the next one, and then iterate over this arc. - getPoint(current); - double x1 = temp[0]; - double y1 = temp[1]; - getPoint(current + 1); - arc.setFrameFromDiagonal(x1, y1, temp[0], temp[1]); - arc.setAngles(x1, y1, temp[0], temp[1]); - corner = arc.getPathIterator(at); - } - } - - public int currentSegment(float[] coords) - { - if (corner != null) - { - int r = corner.currentSegment(coords); - if (r == SEG_MOVETO) - r = SEG_LINETO; - return r; - } - - if (current < 9) - { - getPoint(current); - coords[0] = (float) temp[0]; - coords[1] = (float) temp[1]; - } - else if (current == 9) - return SEG_CLOSE; - else - 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) - { - if (corner != null) - { - int r = corner.currentSegment(coords); - if (r == SEG_MOVETO) - r = SEG_LINETO; - return r; - } - - if (current < 9) - { - getPoint(current); - coords[0] = temp[0]; - coords[1] = temp[1]; - } - else if (current == 9) - return SEG_CLOSE; - else - 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; - } - }; + /** We iterate counterclockwise around the rectangle, starting in the + * upper right. This variable tracks our current point, which + * can be on either side of a given corner. */ + private int current = 0; + + /** Child path iterator, used for corners. */ + private PathIterator corner; + + /** This is used when rendering the corners. We re-use the arc + * for each corner. */ + private Arc2D arc = new Arc2D.Double(); + + /** Temporary array used by getPoint. */ + private double[] temp = new double[2]; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current > 9; + } + + private void getPoint(int val) + { + switch (val) + { + case 0: + case 8: + temp[0] = maxx; + temp[1] = miny + archeight; + break; + case 7: + temp[0] = maxx; + temp[1] = maxy - archeight; + break; + case 6: + temp[0] = maxx - arcwidth; + temp[1] = maxy; + break; + case 5: + temp[0] = minx + arcwidth; + temp[1] = maxy; + break; + case 4: + temp[0] = minx; + temp[1] = maxy - archeight; + break; + case 3: + temp[0] = minx; + temp[1] = miny + archeight; + break; + case 2: + temp[0] = minx + arcwidth; + temp[1] = miny; + break; + case 1: + temp[0] = maxx - arcwidth; + temp[1] = miny; + break; + } + } + + public void next() + { + if (current >= 8) + ++current; + else if (corner != null) + { + // We're iterating through the corner. Work on the child + // iterator; if it finishes, reset and move to the next + // point along the rectangle. + corner.next(); + if (corner.isDone()) + { + corner = null; + ++current; + } + } + else + { + // Make an arc between this point on the rectangle and + // the next one, and then iterate over this arc. + getPoint(current); + double x1 = temp[0]; + double y1 = temp[1]; + getPoint(current + 1); + Rectangle2D.Double r = new Rectangle2D.Double(Math.min(x1, + temp[0]), + Math.min(y1, + temp[1]), + Math.abs(x1 + - temp[0]), + Math.abs(y1 + - temp[1])); + arc.setArc(r, (current >> 1) * 90.0, 90.0, Arc2D.OPEN); + corner = arc.getPathIterator(at); + } + } + + public int currentSegment(float[] coords) + { + if (corner != null) + { + int r = corner.currentSegment(coords); + if (r == SEG_MOVETO) + r = SEG_LINETO; + return r; + } + + if (current < 9) + { + getPoint(current); + coords[0] = (float) temp[0]; + coords[1] = (float) temp[1]; + } + else if (current == 9) + return SEG_CLOSE; + else + 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) + { + if (corner != null) + { + int r = corner.currentSegment(coords); + if (r == SEG_MOVETO) + r = SEG_LINETO; + return r; + } + + if (current < 9) + { + getPoint(current); + coords[0] = temp[0]; + coords[1] = temp[1]; + } + else if (current == 9) + return SEG_CLOSE; + else + 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 true if the given rectangle intersects this shape. @@ -286,14 +297,9 @@ public abstract class RoundRectangle2D extends RectangularShape */ public boolean intersects(double x, double y, double w, double h) { - // Here we can use the same code we use for an ordinary rectangle. - double mx = getX(); - double mw = getWidth(); - if (x < mx || x >= mx + mw || x + w < mx || x + w >= mx + mw) - return false; - double my = getY(); - double mh = getHeight(); - return y >= my && y < my + mh && y + h >= my && y + h < my + mh; + // 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. @@ -315,7 +321,7 @@ public abstract class RoundRectangle2D extends RectangularShape public void setRoundRect(RoundRectangle2D rr) { setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(), - rr.getArcWidth(), rr.getArcHeight()); + rr.getArcWidth(), rr.getArcHeight()); } /** A subclass of RoundRectangle which keeps its parameters as @@ -353,8 +359,8 @@ public abstract class RoundRectangle2D extends RectangularShape * @param arcWidth The arc width * @param arcHeight The arc height */ - public Double(double x, double y, double w, double h, - double arcWidth, double arcHeight) + public Double(double x, double y, double w, double h, double arcWidth, + double arcHeight) { this.x = x; this.y = y; @@ -405,7 +411,7 @@ public abstract class RoundRectangle2D extends RectangularShape } public void setRoundRect(double x, double y, double w, double h, - double arcWidth, double arcHeight) + double arcWidth, double arcHeight) { this.x = x; this.y = y; @@ -451,8 +457,8 @@ public abstract class RoundRectangle2D extends RectangularShape * @param arcWidth The arc width * @param arcHeight The arc height */ - public Float(float x, float y, float w, float h, - float arcWidth, float arcHeight) + public Float(float x, float y, float w, float h, float arcWidth, + float arcHeight) { this.x = x; this.y = y; @@ -503,7 +509,7 @@ public abstract class RoundRectangle2D extends RectangularShape } public void setRoundRect(float x, float y, float w, float h, - float arcWidth, float arcHeight) + float arcWidth, float arcHeight) { this.x = x; this.y = y; @@ -514,7 +520,7 @@ public abstract class RoundRectangle2D extends RectangularShape } public void setRoundRect(double x, double y, double w, double h, - double arcWidth, double arcHeight) + double arcWidth, double arcHeight) { this.x = (float) x; this.y = (float) y; |