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.
+ *
+ *
+ * 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); + } + + /** + * Returnstrue
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)); + } } /**