Index: java/awt/geom/Arc2D.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/awt/geom/Arc2D.java,v retrieving revision 1.5 diff -u -r1.5 Arc2D.java --- java/awt/geom/Arc2D.java 7 May 2004 15:33:19 -0000 1.5 +++ java/awt/geom/Arc2D.java 23 Aug 2004 04:25:08 -0000 @@ -1,5 +1,5 @@ /* Arc2D.java -- represents an arc 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,11 +35,11 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ - package java.awt.geom; import java.util.NoSuchElementException; + /** * This class represents all arcs (segments of an ellipse in 2-D space). The * arcs are defined by starting angle and extent (arc length) in degrees, as @@ -51,8 +51,8 @@ * first 360 degrees. Storage is up to the subclasses. * * @author Eric Blake (address@hidden) + * @author Sven de Marothy (address@hidden) * @since 1.2 - * @status updated to 1.4, but still missing functionality */ public abstract class Arc2D extends RectangularShape { @@ -154,8 +154,8 @@ * extent sweeps counterclockwise (from the positive x-axis to the negative * y-axis). * - * @param x the new x coordinate of the lower left of the bounding box - * @param y the new y coordinate of the lower left of the bounding box + * @param x the new x coordinate of the upper left of the bounding box + * @param y the new y coordinate of the upper left of the bounding box * @param w the new width of the bounding box * @param h the new height of the bounding box * @param start the start angle, in degrees @@ -171,7 +171,7 @@ * extent sweeps counterclockwise (from the positive x-axis to the negative * y-axis). * - * @param p the lower left point of the bounding box + * @param p the upper left point of the bounding box * @param d the dimensions of the bounding box * @param start the start angle, in degrees * @param extent the arc extent, in degrees @@ -179,11 +179,10 @@ * @throws IllegalArgumentException if type is invalid * @throws NullPointerException if p or d is null */ - public void setArc(Point2D p, Dimension2D d, - double start, double extent, int type) + public void setArc(Point2D p, Dimension2D d, double start, double extent, + int type) { - setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(), - start, extent, type); + setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(), start, extent, type); } /** @@ -200,8 +199,7 @@ */ public void setArc(Rectangle2D r, double start, double extent, int type) { - setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(), - start, extent, type); + setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(), start, extent, type); } /** @@ -212,8 +210,8 @@ */ public void setArc(Arc2D a) { - setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), - a.getAngleStart(), a.getAngleExtent(), a.getArcType()); + setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), a.getAngleStart(), + a.getAngleExtent(), a.getArcType()); } /** @@ -230,8 +228,8 @@ * @param type one of address@hidden #OPEN}, address@hidden #CHORD}, or address@hidden #PIE} * @throws IllegalArgumentException if type is invalid */ - public void setArcByCenter(double x, double y, double r, - double start, double extent, int type) + public void setArcByCenter(double x, double y, double r, double start, + double extent, int type) { setArc(x - r, y - r, r + r, r + r, start, extent, type); } @@ -252,8 +250,50 @@ */ public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double r) { - // XXX Implement. - throw new Error("not implemented"); + if ((p2.getX() - p1.getX()) * (p3.getY() - p1.getY()) + - (p3.getX() - p1.getX()) * (p2.getY() - p1.getY()) > 0) + { + Point2D p = p3; + p3 = p1; + p1 = p; + } + + // normalized tangent vectors + double dx1 = (p1.getX() - p2.getX()) / p1.distance(p2); + double dy1 = (p1.getY() - p2.getY()) / p1.distance(p2); + double dx2 = (p2.getX() - p3.getX()) / p3.distance(p2); + double dy2 = (p2.getY() - p3.getY()) / p3.distance(p2); + double theta1 = Math.atan2(dx1, dy1); + double theta2 = Math.atan2(dx2, dy2); + + double dx = r * Math.cos(theta2) - r * Math.cos(theta1); + double dy = -r * Math.sin(theta2) + r * Math.sin(theta1); + + if (theta1 < 0) + theta1 += 2 * Math.PI; + if (theta2 < 0) + theta2 += 2 * Math.PI; + if (theta2 < theta1) + theta2 += 2 * Math.PI; + + // Vectors of the lines, not normalized, note we change + // the direction of line 2. + dx1 = p1.getX() - p2.getX(); + dy1 = p1.getY() - p2.getY(); + dx2 = p3.getX() - p2.getX(); + dy2 = p3.getY() - p2.getY(); + + // Calculate the tangent point to the second line + double t2 = -(dx1 * dy - dy1 * dx) / (dx2 * dy1 - dx1 * dy2); + double x2 = t2 * (p3.getX() - p2.getX()) + p2.getX(); + double y2 = t2 * (p3.getY() - p2.getY()) + p2.getY(); + + // calculate the center point + double x = x2 - r * Math.cos(theta2); + double y = y2 + r * Math.sin(theta2); + + setArc(x - r, y - r, 2 * r, 2 * r, Math.toDegrees(theta1), + Math.toDegrees(theta2 - theta1), getArcType()); } /** @@ -413,8 +453,8 @@ * @param h the height * @return the rectangle for use in getBounds2D */ - protected abstract Rectangle2D makeBounds(double x, double y, - double w, double h); + protected abstract Rectangle2D makeBounds(double x, double y, double w, + double h); /** * Tests if the given angle, in degrees, is included in the arc. @@ -426,18 +466,28 @@ public boolean containsAngle(double a) { double start = getAngleStart(); - double end = start + getAngleExtent(); + double extent = getAngleExtent(); + double end = start + extent; + + if (extent >= 360 || extent <= -360) + return true; + + if (extent < 0) + { + end = start; + start += extent; + } start %= 360; - if (start < 0) + while (start < 0) start += 360; end %= 360; - if (end < 0) + while (end < start) end += 360; a %= 360; - if (a < 0) + while (a < start) a += 360; return a >= start && a <= end; @@ -447,6 +497,10 @@ * Determines if the arc contains the given point. If the bounding box * is empty, then this will return false. * + * The area considered 'inside' an arc of type OPEN is the same as the + * area inside an equivalent filled PIE-type arc. The area considered + * 'inside' a CHORD-type arc is the same as the filled area. + * * @param x the x coordinate to test * @param y the y coordinate to test * @return true if the point is inside the arc @@ -455,15 +509,50 @@ { double w = getWidth(); double h = getHeight(); - if (w <= 0 || h <= 0) + double extent = getAngleExtent(); + if (w <= 0 || h <= 0 || extent == 0) + return false; + + double mx = getX() + w / 2; + double my = getY() + h / 2; + double dx = (x - mx) * 2 / w; + double dy = (y - my) * 2 / h; + if ((dx * dx + dy * dy) >= 1.0) return false; - // XXX Finish implementing. - throw new Error("not implemented"); + + double angle = Math.toDegrees(Math.atan2(-dy, dx)); + if (getArcType() != CHORD) + return containsAngle(angle); + + double a1 = Math.toRadians(getAngleStart()); + double a2 = Math.toRadians(getAngleStart() + extent); + double x1 = mx + getWidth() * Math.cos(a1) / 2; + double y1 = my - getHeight() * Math.sin(a1) / 2; + double x2 = mx + getWidth() * Math.cos(a2) / 2; + double y2 = my - getHeight() * Math.sin(a2) / 2; + double sgn = ((x2 - x1) * (my - y1) - (mx - x1) * (y2 - y1)) * ((x2 - x1) * (y + - y1) - (x - x1) * (y2 - y1)); + + if (Math.abs(extent) > 180) + { + if (containsAngle(angle)) + return true; + return sgn > 0; + } + else + { + if (! containsAngle(angle)) + return false; + return sgn < 0; + } } /** * Tests if a given rectangle intersects the area of the arc. * + * For a definition of the 'inside' area, see the contains() method. + * @see #contains(double, double) + * * @param x the x coordinate of the rectangle * @param y the y coordinate of the rectangle * @param w the width of the rectangle @@ -472,12 +561,51 @@ */ public boolean intersects(double x, double y, double w, double h) { - double mw = getWidth(); - double mh = getHeight(); - if (mw <= 0 || mh <= 0 || w <= 0 || h <= 0) + double extent = getAngleExtent(); + if (extent == 0) return false; - // XXX Finish implementing. - throw new Error("not implemented"); + + if (contains(x, y) || contains(x, y + h) || contains(x + w, y) + || contains(x + w, y + h)) + return true; + + double mx = getX() + getWidth() / 2; + double my = getY() + getHeight() / 2; + double x1 = mx + + getWidth() * Math.cos(Math.toRadians(getAngleStart())) / 2; + double y1 = my + - getHeight() * Math.sin(Math.toRadians(getAngleStart())) / 2; + double x2 = mx + + getWidth() * Math.cos(Math.toRadians(getAngleStart() + + extent)) / 2; + double y2 = my + - getHeight() * Math.sin(Math.toRadians(getAngleStart() + + extent)) / 2; + if (getArcType() != CHORD) + { + // check intersections against the pie radii + if (Line2D.linesIntersect(mx, my, x1, y1, x, y, x + w, y) + || Line2D.linesIntersect(mx, my, x1, y1, x + w, y, x + w, y + h) + || Line2D.linesIntersect(mx, my, x1, y1, x, y, x, y + h) + || Line2D.linesIntersect(mx, my, x1, y1, x, y + h, x + w, y + h)) + return true; + + if (Line2D.linesIntersect(mx, my, x2, y2, x, y, x + w, y) + || Line2D.linesIntersect(mx, my, x2, y2, x + w, y, x + w, y + h) + || Line2D.linesIntersect(mx, my, x2, y2, x, y, x, y + h) + || Line2D.linesIntersect(mx, my, x2, y2, x, y + h, x + w, y + h)) + return true; + } + else if (Line2D.linesIntersect(x1, y1, x2, y2, x, y, x + w, y) + || Line2D.linesIntersect(x1, y1, x2, y2, x + w, y, x + w, y + h) + || Line2D.linesIntersect(x1, y1, x2, y2, x, y, x, y + h) + || Line2D.linesIntersect(x1, y1, x2, y2, x, y + h, x + w, y + h)) + return true; + + if ((new Rectangle2D.Double(x, y, w, h)).contains(x1, y1)) + return true; + + return false; } /** @@ -491,12 +619,47 @@ */ public boolean contains(double x, double y, double w, double h) { - double mw = getWidth(); - double mh = getHeight(); - if (mw <= 0 || mh <= 0 || w <= 0 || h <= 0) + double extent = getAngleExtent(); + if (extent == 0) + return false; + + if (! (contains(x, y) && contains(x, y + h) && contains(x + w, y) + && contains(x + w, y + h))) + return false; + + double mx = getX() + getWidth() / 2; + double my = getY() + getHeight() / 2; + double x1 = mx + + getWidth() * Math.cos(Math.toRadians(getAngleStart())) / 2; + double y1 = my + - getHeight() * Math.sin(Math.toRadians(getAngleStart())) / 2; + double x2 = mx + + getWidth() * Math.cos(Math.toRadians(getAngleStart() + + extent)) / 2; + double y2 = my + - getHeight() * Math.sin(Math.toRadians(getAngleStart() + + extent)) / 2; + if (getArcType() != CHORD) + { + // check intersections against the pie radii + if (Line2D.linesIntersect(mx, my, x1, y1, x, y, x + w, y) + || Line2D.linesIntersect(mx, my, x1, y1, x + w, y, x + w, y + h) + || Line2D.linesIntersect(mx, my, x1, y1, x, y, x, y + h) + || Line2D.linesIntersect(mx, my, x1, y1, x, y + h, x + w, y + h)) + return false; + + if (Line2D.linesIntersect(mx, my, x2, y2, x, y, x + w, y) + || Line2D.linesIntersect(mx, my, x2, y2, x + w, y, x + w, y + h) + || Line2D.linesIntersect(mx, my, x2, y2, x, y, x, y + h) + || Line2D.linesIntersect(mx, my, x2, y2, x, y + h, x + w, y + h)) + return false; + } + else if (Line2D.linesIntersect(x1, y1, x2, y2, x, y, x + w, y) + || Line2D.linesIntersect(x1, y1, x2, y2, x + w, y, x + w, y + h) + || Line2D.linesIntersect(x1, y1, x2, y2, x, y, x, y + h) + || Line2D.linesIntersect(x1, y1, x2, y2, x, y + h, x + w, y + h)) return false; - // XXX Finish implementing. - throw new Error("not implemented"); + return true; } /** @@ -529,7 +692,7 @@ * * @author Eric Blake (address@hidden) */ - static final class ArcIterator implements PathIterator + private static final class ArcIterator implements PathIterator { /** The current iteration. */ private int current; @@ -567,29 +730,37 @@ * @param a the arc * @param xform the transform */ - ArcIterator(Arc2D a, AffineTransform xform) + public ArcIterator(Arc2D a, AffineTransform xform) { this.xform = xform; x = a.getX(); y = a.getY(); w = a.getWidth(); h = a.getHeight(); - start = a.getAngleStart() * (Math.PI / 180); - extent = a.getAngleExtent() * (Math.PI / 180); + double start = a.getAngleStart() * (Math.PI / 180); + double extent = a.getAngleExtent() * (Math.PI / 180); + + if (extent < 0) + { + extent = -extent; + start = 2 * Math.PI - extent + start; + } + this.start = start; + this.extent = extent; + type = a.type; - double e = extent < 0 ? -extent : extent; if (w < 0 || h < 0) - limit = -1; - else if (e == 0) - limit = type; - else if (e <= Math.PI / 2.0) - limit = type + 1; - else if (e <= Math.PI) - limit = type + 2; - else if (e <= 3.0 * (Math.PI / 2.0)) - limit = type + 3; + limit = -1; + else if (extent == 0) + limit = type; + else if (extent <= Math.PI / 2.0) + limit = type + 1; + else if (extent <= Math.PI) + limit = type + 2; + else if (extent <= 3.0 * (Math.PI / 2.0)) + limit = type + 3; else - limit = type + 4; + limit = type + 4; } /** @@ -598,7 +769,7 @@ * @param e the ellipse * @param xform the transform */ - ArcIterator(Ellipse2D e, AffineTransform xform) + public ArcIterator(Ellipse2D e, AffineTransform xform) { this.xform = xform; x = e.getX(); @@ -606,7 +777,7 @@ w = e.getWidth(); h = e.getHeight(); start = 0; - extent = -2 * Math.PI; + extent = 2 * Math.PI; type = CHORD; limit = (w < 0 || h < 0) ? -1 : 5; } @@ -650,9 +821,9 @@ public int currentSegment(float[] coords) { double[] double_coords = new double[6]; - int code = currentSegment (double_coords); + int code = currentSegment(double_coords); for (int i = 0; i < 6; ++i) - coords[i] = (float) double_coords[i]; + coords[i] = (float) double_coords[i]; return code; } @@ -666,48 +837,38 @@ */ public int currentSegment(double[] coords) { - double rx = w/2; - double ry = h/2; + double rx = w / 2; + double ry = h / 2; double xmid = x + rx; double ymid = y + ry; - + if (current > limit) - throw new NoSuchElementException("arc iterator out of bounds"); + throw new NoSuchElementException("arc iterator out of bounds"); if (current == 0) { - coords[0] = xmid + rx * Math.cos(start); - coords[1] = ymid - ry * Math.sin(start); - if (xform != null) - xform.transform(coords, 0, coords, 0, 1); - return SEG_MOVETO; + coords[0] = xmid + rx * Math.cos(start); + coords[1] = ymid - ry * Math.sin(start); + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; } if (type != OPEN && current == limit) - return SEG_CLOSE; + return SEG_CLOSE; - if ((current == limit - 1) && - (type == PIE) || (type == CHORD)) + if ((current == limit - 1) && (type == PIE)) { - if (type == PIE) - { - coords[0] = xmid; - coords[1] = ymid; - } - else if (type == CHORD) - { - coords[0] = xmid + rx * Math.cos(start); - coords[1] = ymid - ry * Math.sin(start); - } - if (xform != null) - xform.transform(coords, 0, coords, 0, 1); - return SEG_LINETO; + coords[0] = xmid; + coords[1] = ymid; + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_LINETO; } // note that this produces a cubic approximation of the arc segment, // not a true ellipsoid. there's no ellipsoid path segment code, // unfortunately. the cubic approximation looks about right, though. - double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0); double quad = (Math.PI / 2.0); @@ -717,14 +878,14 @@ double x0 = xmid + rx * Math.cos(curr_begin); double y0 = ymid - ry * Math.sin(curr_begin); - + double x1 = xmid + rx * Math.cos(curr_begin + curr_extent); double y1 = ymid - ry * Math.sin(curr_begin + curr_extent); - AffineTransform trans = new AffineTransform (); - double [] cvec = new double[2]; - double len = kappa * portion_of_a_quadrant; - double angle = curr_begin; + AffineTransform trans = new AffineTransform(); + double[] cvec = new double[2]; + double len = kappa * portion_of_a_quadrant; + double angle = curr_begin; // in a hypothetical "first quadrant" setting, our first control // vector would be sticking up, from [1,0] to [1,kappa]. @@ -733,31 +894,29 @@ // from what one would consider "normal" first quadrant rules, so we // will *subtract* the y value of this control vector from our first // point. - cvec[0] = 0; cvec[1] = len; - trans.scale (rx, ry); - trans.rotate (angle); + trans.scale(rx, ry); + trans.rotate(angle); trans.transform(cvec, 0, cvec, 0, 1); coords[0] = x0 + cvec[0]; coords[1] = y0 - cvec[1]; // control vector #2 would, ideally, be sticking out and to the // right, in a first quadrant arc segment. again, subtraction of y. - cvec[0] = 0; cvec[1] = -len; - trans.rotate (curr_extent); + trans.rotate(curr_extent); trans.transform(cvec, 0, cvec, 0, 1); coords[2] = x1 + cvec[0]; coords[3] = y1 - cvec[1]; - + // end point coords[4] = x1; coords[5] = y1; if (xform != null) - xform.transform(coords, 0, coords, 0, 3); + xform.transform(coords, 0, coords, 0, 3); return SEG_CUBICTO; } @@ -820,8 +979,8 @@ * @param type the arc type: address@hidden #OPEN}, address@hidden #CHORD}, or address@hidden #PIE} * @throws IllegalArgumentException if type is invalid */ - public Double(double x, double y, double w, double h, - double start, double extent, int type) + public Double(double x, double y, double w, double h, double start, + double extent, int type) { super(type); this.x = x; @@ -831,7 +990,7 @@ this.start = start; this.extent = extent; } - + /** * Create a new arc with the given dimensions. * @@ -935,8 +1094,8 @@ * @param type the arc type: address@hidden #OPEN}, address@hidden #CHORD}, or address@hidden #PIE} * @throws IllegalArgumentException if type is invalid */ - public void setArc(double x, double y, double w, double h, - double start, double extent, int type) + public void setArc(double x, double y, double w, double h, double start, + double extent, int type) { this.x = x; this.y = y; @@ -1039,8 +1198,8 @@ * @param type the arc type: address@hidden #OPEN}, address@hidden #CHORD}, or address@hidden #PIE} * @throws IllegalArgumentException if type is invalid */ - public Float(float x, float y, float w, float h, - float start, float extent, int type) + public Float(float x, float y, float w, float h, float start, + float extent, int type) { super(type); this.x = x; @@ -1050,7 +1209,7 @@ this.start = start; this.extent = extent; } - + /** * Create a new arc with the given dimensions. * @@ -1069,7 +1228,7 @@ width = (float) r.getWidth(); height = (float) r.getHeight(); this.start = start; - this.extent = extent; + this.extent = (float) extent; } /** @@ -1154,8 +1313,8 @@ * @param type the arc type: address@hidden #OPEN}, address@hidden #CHORD}, or address@hidden #PIE} * @throws IllegalArgumentException if type is invalid */ - public void setArc(double x, double y, double w, double h, - double start, double extent, int type) + public void setArc(double x, double y, double w, double h, double start, + double extent, int type) { this.x = (float) x; this.y = (float) y; Index: java/awt/geom/Ellipse2D.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/awt/geom/Ellipse2D.java,v retrieving revision 1.3 diff -u -r1.3 Ellipse2D.java --- java/awt/geom/Ellipse2D.java 22 Mar 2002 16:54:31 -0000 1.3 +++ java/awt/geom/Ellipse2D.java 23 Aug 2004 04:25:08 -0000 @@ -1,5 +1,5 @@ /* Ellipse2D.java -- represents an ellipse in 2-D space - Copyright (C) 2000, 2002 Free Software Foundation + Copyright (C) 2000, 2002, 2004 Free Software Foundation This file is part of GNU Classpath. @@ -37,58 +37,147 @@ package java.awt.geom; + /** + * Ellipse2D is the shape of an ellipse. + *
+ * A drawing of an ellipse
+ * The ellipse is defined by it's bounding box (shown in red), + * and is defined by the implicit curve:
+ *
(x/a)2 + + * (y/b)2 = 1

+ * * @author Tom Tromey * @author Eric Blake + * * @since 1.2 - * @status still needs documentation */ public abstract class Ellipse2D extends RectangularShape { + /** + * Ellipse2D is defined as abstract. + * Implementing classes are Ellipse2D.Float and Ellipse2D.Double. + */ protected Ellipse2D() { } + /** + * Determines if a point is contained within the ellipse.

+ * @param x - x coordinate of the point. + * @param y - y coordinate of the point. + * @return true if the point is within the ellipse, false otherwise. + */ public boolean contains(double x, double y) { double rx = getWidth() / 2; double ry = getHeight() / 2; - double tx = (x - getCenterX()) / rx; - double ty = (y - getCenterY()) / ry; - return tx * tx + ty * ty <= 1.0; + double tx = (x - (getX() + rx)) / rx; + double ty = (y - (getY() + ry)) / ry; + return tx * tx + ty * ty < 1.0; } + /** + * Determines if a rectangle is completely contained within the + * ellipse.

+ * @param x - x coordinate of the upper-left corner of the rectangle + * @param y - y coordinate of the upper-left corner of the rectangle + * @param w - width of the rectangle + * @param h - height of the rectangle + * @return true if the rectangle is completely contained, false otherwise. + */ public boolean contains(double x, double y, double w, double h) { double x2 = x + w; double y2 = y + h; - return (contains(x, y) && contains(x, y2) - && contains(x2, y) && contains(x2, y2)); + return (contains(x, y) && contains(x, y2) && contains(x2, y) + && contains(x2, y2)); } + /** + * Returns a PathIterator object corresponding to the ellipse.

+ * + * Note: An ellipse cannot be represented exactly in PathIterator + * segments, the outline is thefore approximated with cubic + * Bezier segments. + */ public PathIterator getPathIterator(AffineTransform at) { // An ellipse is just a complete arc. return new Arc2D.ArcIterator(this, at); } + /** + * Determines if a rectangle intersects any part of the ellipse.

+ * @param x - x coordinate of the upper-left corner of the rectangle + * @param y - y coordinate of the upper-left corner of the rectangle + * @param w - width of the rectangle + * @param h - height of the rectangle + * @return true if the rectangle intersects the ellipse, false otherwise. + */ public boolean intersects(double x, double y, double w, double h) { - // fixme + Rectangle2D r = new Rectangle2D.Double(x, y, w, h); + if (! r.intersects(getX(), getY(), getWidth(), getHeight())) + return false; + + if (contains(x, y) || contains(x, y + h) || contains(x + w, y) + || contains(x + w, y + h)) + return true; + + Line2D l1 = new Line2D.Double(getX(), getY() + (getHeight() / 2), + getX() + getWidth(), + getY() + (getHeight() / 2)); + Line2D l2 = new Line2D.Double(getX() + (getWidth() / 2), getY(), + getX() + (getWidth() / 2), + getY() + getHeight()); + + if (l1.intersects(r) || l2.intersects(r)) + return true; + return false; } public static class Double extends Ellipse2D { + /** + * The height of the ellipse. + */ public double height; + + /** + * The width of the ellipse. + */ public double width; + + /** + * The upper-left x coordinate of the bounding-box + */ public double x; + + /** + * The upper-left y coordinate of the bounding-box + */ public double y; + /** + * Creates a new Ellipse2D with an upper-right coordinate of (0,0) + * and a zero size. + */ public Double() { } + /** + * Creates a new Ellipse2D within a given rectangle + * using double-precision coordinates.

+ * @param x - x coordinate of the upper-right of the bounding rectangle + * @param y - y coordinate of the upper-right of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + * + */ public Double(double x, double y, double w, double h) { this.x = x; @@ -97,36 +186,64 @@ width = w; } + /** + * Returns the bounding-box of the ellipse. + */ public Rectangle2D getBounds2D() { return new Rectangle2D.Double(x, y, width, height); } + /** + * Returns the height of the ellipse. + */ public double getHeight() { return height; } + /** + * Returns the width of the ellipse. + */ public double getWidth() { return width; } + /** + * Returns x coordinate of the upper-left corner of + * the ellipse's bounding-box. + */ public double getX() { return x; } + /** + * Returns y coordinate of the upper-left corner of + * the ellipse's bounding-box. + */ public double getY() { return y; } + /** + * Returns true if the ellipse encloses any area. + */ public boolean isEmpty() { return height <= 0 || width <= 0; } + /** + * Sets the geometry of the ellipse's bounding box.

+ * + * @param x - x coordinate of the upper-right of the bounding rectangle + * @param y - y coordinate of the upper-right of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ public void setFrame(double x, double y, double w, double h) { this.x = x; @@ -138,15 +255,43 @@ public static class Float extends Ellipse2D { + /** + * The height of the ellipse. + */ public float height; + + /** + * The width of the ellipse. + */ public float width; + + /** + * The upper-left x coordinate of the bounding-box + */ public float x; + + /** + * The upper-left y coordinate of the bounding-box + */ public float y; + /** + * Creates a new Ellipse2D with an upper-right coordinate of (0,0) + * and a zero size. + */ public Float() { } + /** + * Creates a new Ellipse2D within a given rectangle + * using floating-point precision.

+ * @param x - x coordinate of the upper-right of the bounding rectangle + * @param y - y coordinate of the upper-right of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + * + */ public Float(float x, float y, float w, float h) { this.x = x; @@ -155,36 +300,64 @@ this.width = w; } + /** + * Returns the bounding-box of the ellipse. + */ public Rectangle2D getBounds2D() { return new Rectangle2D.Float(x, y, width, height); } + /** + * Returns the height of the ellipse. + */ public double getHeight() { return height; } + /** + * Returns the width of the ellipse. + */ public double getWidth() { return width; } + /** + * Returns x coordinate of the upper-left corner of + * the ellipse's bounding-box. + */ public double getX() { return x; } + /** + * Returns y coordinate of the upper-left corner of + * the ellipse's bounding-box. + */ public double getY() { return y; } + /** + * Returns true if the ellipse encloses any area. + */ public boolean isEmpty() { return height <= 0 || width <= 0; } + /** + * Sets the geometry of the ellipse's bounding box.

+ * + * @param x - x coordinate of the upper-right of the bounding rectangle + * @param y - y coordinate of the upper-right of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ public void setFrame(float x, float y, float w, float h) { this.x = x; @@ -193,6 +366,16 @@ width = w; } + /** + * Sets the geometry of the ellipse's bounding box. + * + * Note: This leads to a loss of precision.

+ * + * @param x - x coordinate of the upper-right of the bounding rectangle + * @param y - y coordinate of the upper-right of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ public void setFrame(double x, double y, double w, double h) { this.x = (float) x; Index: java/awt/geom/Line2D.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/awt/geom/Line2D.java,v retrieving revision 1.6 diff -u -r1.6 Line2D.java --- java/awt/geom/Line2D.java 18 Jul 2003 19:35:02 -0000 1.6 +++ java/awt/geom/Line2D.java 23 Aug 2004 04:25:08 -0000 @@ -48,6 +48,7 @@ * * @author Tom Tromey * @author Eric Blake + * @author David Gilbert * @since 1.2 * @status updated to 1.4 */ @@ -235,11 +236,57 @@ } /** - * Test if the line segment (x1,y1)->(x2,y2) intersects the line segment + * Computes twice the (signed) area of the triangle defined by the three + * points. This method is used for intersection testing. + * + * @param x1 the x-coordinate of the first point. + * @param y1 the y-coordinate of the first point. + * @param x2 the x-coordinate of the second point. + * @param y2 the y-coordinate of the second point. + * @param x3 the x-coordinate of the third point. + * @param y3 the y-coordinate of the third point. + * + * @return Twice the area. + */ + private static double area2(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + return (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1); + } + + /** + * Returns true if (x3, y3) lies between (x1, y1) and (x2, y2), + * and false otherwise, This test assumes that the three points are + * collinear, and is used for intersection testing. + * + * @param x1 the x-coordinate of the first point. + * @param y1 the y-coordinate of the first point. + * @param x2 the x-coordinate of the second point. + * @param y2 the y-coordinate of the second point. + * @param x3 the x-coordinate of the third point. + * @param y3 the y-coordinate of the third point. + * + * @return A boolean. + */ + private static boolean between(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + if (x1 != x2) { + return (x1 <= x3 && x3 <= x2) || (x1 >= x3 && x3 >= x2); + } + else { + return (y1 <= y3 && y3 <= y2) || (y1 >= y3 && y3 >= y2); + } + } + + /** + * Test if the line segment (x1,y1)->(x2,y2) intersects the line segment * (x3,y3)->(x4,y4). * * @param x1 the first x coordinate of the first segment - * @param y1 the first y coordinate of the first segment + * @param y1 the first y coordinate of the first segment * @param x2 the second x coordinate of the first segment * @param y2 the second y coordinate of the first segment * @param x3 the first x coordinate of the second segment @@ -249,16 +296,64 @@ * @return true if the segments intersect */ public static boolean linesIntersect(double x1, double y1, - double x2, double y2, - double x3, double y3, - double x4, double y4) - { - double beta = (((y1 - y3) * (x4 - x3) + (x1 - x3) * (y4 - y3)) - / ((y2 - y1) * (x4 - x3) + (x2 - x1) * (y4 - y3))); - if (beta < 0.0 || beta > 1.0) - return false; - double alpha = (x1 + beta * (x2 - x1) - x3) / (x4 - x3); - return alpha >= 0.0 && alpha <= 1.0; + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + double a1, a2, a3, a4; + + // deal with special cases + if ((a1 = area2(x1, y1, x2, y2, x3, y3)) == 0.0) + { + // check if p3 is between p1 and p2 OR + // p4 is collinear also AND either between p1 and p2 OR at opposite ends + if (between(x1, y1, x2, y2, x3, y3)) + { + return true; + } + else + { + if (area2(x1, y1, x2, y2, x4, y4) == 0.0) + { + return between(x3, y3, x4, y4, x1, y1) + || between (x3, y3, x4, y4, x2, y2); + } + else { + return false; + } + } + } + else if ((a2 = area2(x1, y1, x2, y2, x4, y4)) == 0.0) + { + // check if p4 is between p1 and p2 (we already know p3 is not + // collinear) + return between(x1, y1, x2, y2, x4, y4); + } + + if ((a3 = area2(x3, y3, x4, y4, x1, y1)) == 0.0) { + // check if p1 is between p3 and p4 OR + // p2 is collinear also AND either between p1 and p2 OR at opposite ends + if (between(x3, y3, x4, y4, x1, y1)) { + return true; + } + else { + if (area2(x3, y3, x4, y4, x2, y2) == 0.0) { + return between(x1, y1, x2, y2, x3, y3) + || between (x1, y1, x2, y2, x4, y4); + } + else { + return false; + } + } + } + else if ((a4 = area2(x3, y3, x4, y4, x2, y2)) == 0.0) { + // check if p2 is between p3 and p4 (we already know p1 is not + // collinear) + return between(x3, y3, x4, y4, x2, y2); + } + else { // test for regular intersection + return ((a1 > 0.0) ^ (a2 > 0.0)) && ((a3 > 0.0) ^ (a4 > 0.0)); + } } /**