diff --git a/src/main/java/pythagoras/f/AbstractDimension.java b/src/main/java/pythagoras/f/AbstractDimension.java
new file mode 100644
index 0000000..06e81cd
--- /dev/null
+++ b/src/main/java/pythagoras/f/AbstractDimension.java
@@ -0,0 +1,38 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Provides most of the implementation of {@link IDimension}, obtaining only width and height from
+ * the derived class.
+ */
+public abstract class AbstractDimension implements IDimension
+{
+ @Override // from interface IDimension
+ public Dimension clone () {
+ return new Dimension(this);
+ }
+
+ @Override
+ public int hashCode () {
+ return Float.floatToIntBits(getWidth()) ^ Float.floatToIntBits(getHeight());
+ }
+
+ @Override
+ public boolean equals (Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof AbstractDimension) {
+ AbstractDimension d = (AbstractDimension)obj;
+ return (d.getWidth() == getWidth() && d.getHeight() == getHeight());
+ }
+ return false;
+ }
+
+ @Override
+ public String toString () {
+ return Geometry.dimenToString(getWidth(), getHeight());
+ }
+}
diff --git a/src/main/java/pythagoras/f/AbstractLine.java b/src/main/java/pythagoras/f/AbstractLine.java
new file mode 100644
index 0000000..89128e5
--- /dev/null
+++ b/src/main/java/pythagoras/f/AbstractLine.java
@@ -0,0 +1,130 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Provides most of the implementation of {@link ILine}, obtaining only the start and end points
+ * from the derived class.
+ */
+public abstract class AbstractLine implements ILine
+{
+ @Override // from interface ILine
+ public IPoint p1 () {
+ return new AbstractPoint() {
+ @Override public float getX () {
+ return getX1();
+ }
+ @Override public float getY () {
+ return getY1();
+ }
+ };
+ }
+
+ @Override // from interface ILine
+ public Point getP1 () {
+ return new Point(getX1(), getY1());
+ }
+
+ @Override // from interface ILine
+ public IPoint p2 () {
+ return new AbstractPoint() {
+ @Override public float getX () {
+ return getX2();
+ }
+ @Override public float getY () {
+ return getY2();
+ }
+ };
+ }
+
+ @Override // from interface ILine
+ public Point getP2 () {
+ return new Point(getX2(), getY2());
+ }
+
+ @Override // from interface ILine
+ public Line clone () {
+ return new Line(getX1(), getY1(), getX2(), getY2());
+ }
+
+ @Override // from interface IShape
+ public boolean isEmpty () {
+ return false;
+ }
+
+ @Override // from interface IShape
+ public boolean contains (float x, float y) {
+ return false;
+ }
+
+ @Override // from interface IShape
+ public boolean contains (IPoint point) {
+ return false;
+ }
+
+ @Override // from interface IShape
+ public boolean contains (float x, float y, float w, float h) {
+ return false;
+ }
+
+ @Override // from interface IShape
+ public boolean contains (IRectangle r) {
+ return false;
+ }
+
+ @Override // from interface IShape
+ public boolean intersects (float rx, float ry, float rw, float rh) {
+ return Geometry.lineIntersectsRect(getX1(), getY1(), getX2(), getY2(), rx, ry, rw, rh);
+ }
+
+ @Override // from interface IShape
+ public boolean intersects (IRectangle r) {
+ return r.intersectsLine(this);
+ }
+
+ @Override // from interface IShape
+ public IRectangle bounds () {
+ return new AbstractRectangle() {
+ @Override public float getX () {
+ return Math.min(getX1(), getX2());
+ }
+ @Override public float getY () {
+ return Math.min(getY1(), getY2());
+ }
+ @Override public float getWidth () {
+ float x1 = getX1(), x2 = getX2();
+ return (x1 < x2) ? (x2 - x1) : (x1 - x2);
+ }
+ @Override public float getHeight () {
+ float y1 = getY1(), y2 = getY2();
+ return (y1 < y2) ? (y2 - y1) : (y1 - y2);
+ }
+ // this isn't visible in the type, so won't be called by non-combatants
+ @Override public void setFrame (float x, float y, float w, float h) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override // from interface IShape
+ public Rectangle getBounds () {
+ float x1 = getX1(), x2 = getX2(), y1 = getY1(), y2 = getY2();
+ float rx, ry, rw, rh;
+ if (x1 < x2) {
+ rx = x1;
+ rw = x2 - x1;
+ } else {
+ rx = x2;
+ rw = x1 - x2;
+ }
+ if (y1 < y2) {
+ ry = y1;
+ rh = y2 - y1;
+ } else {
+ ry = y2;
+ rh = y1 - y2;
+ }
+ return new Rectangle(rx, ry, rw, rh);
+ }
+}
diff --git a/src/main/java/pythagoras/f/AbstractPoint.java b/src/main/java/pythagoras/f/AbstractPoint.java
new file mode 100644
index 0000000..347ff58
--- /dev/null
+++ b/src/main/java/pythagoras/f/AbstractPoint.java
@@ -0,0 +1,58 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Provides most of the implementation of {@link IPoint}, obtaining only the location from the
+ * derived class.
+ */
+public abstract class AbstractPoint implements IPoint
+{
+ @Override // from interface IPoint
+ public float distanceSq (float px, float py) {
+ return Geometry.distanceSq(getX(), getY(), px, py);
+ }
+
+ @Override // from interface IPoint
+ public float distanceSq (IPoint p) {
+ return Geometry.distanceSq(getX(), getY(), p.getX(), p.getY());
+ }
+
+ @Override // from interface IPoint
+ public float distance (float px, float py) {
+ return Geometry.distance(getX(), getY(), px, py);
+ }
+
+ @Override // from interface IPoint
+ public float distance (IPoint p) {
+ return Geometry.distance(getX(), getY(), p.getX(), p.getY());
+ }
+
+ @Override // from interface IPoint
+ public Point clone () {
+ return new Point(this);
+ }
+
+ @Override
+ public boolean equals (Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof AbstractPoint) {
+ AbstractPoint p = (AbstractPoint)obj;
+ return getX() == p.getX() && getY() == p.getY();
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode () {
+ return Float.floatToIntBits(getX()) ^ Float.floatToIntBits(getY());
+ }
+
+ @Override
+ public String toString () {
+ return Geometry.pointToString(getX(), getY());
+ }
+}
diff --git a/src/main/java/pythagoras/f/AbstractRectangle.java b/src/main/java/pythagoras/f/AbstractRectangle.java
new file mode 100644
index 0000000..0cc5e29
--- /dev/null
+++ b/src/main/java/pythagoras/f/AbstractRectangle.java
@@ -0,0 +1,172 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Provides most of the implementation of {@link IRectangle}, obtaining only the location and
+ * dimensions from the derived class.
+ */
+public abstract class AbstractRectangle extends RectangularShape implements IRectangle
+{
+ @Override // from interface IRectangle
+ public IPoint location () {
+ return new AbstractPoint() {
+ @Override public float getX () {
+ return AbstractRectangle.this.getX();
+ }
+ @Override public float getY () {
+ return AbstractRectangle.this.getY();
+ }
+ };
+ }
+
+ @Override // from interface IRectangle
+ public Point getLocation () {
+ return new Point(getX(), getY());
+ }
+
+ @Override // from interface IRectangle
+ public IDimension size () {
+ return new AbstractDimension() {
+ @Override public float getWidth () {
+ return AbstractRectangle.this.getWidth();
+ }
+ @Override public float getHeight () {
+ return AbstractRectangle.this.getHeight();
+ }
+ };
+ }
+
+ @Override // from interface IRectangle
+ public Dimension getSize () {
+ return new Dimension(getWidth(), getHeight());
+ }
+
+ @Override // from interface IRectangle
+ public Rectangle intersection (float rx, float ry, float rw, float rh) {
+ float x1 = Math.max(getX(), rx);
+ float y1 = Math.max(getY(), ry);
+ float x2 = Math.min(getMaxX(), rx + rw);
+ float y2 = Math.min(getMaxY(), ry + rh);
+ return new Rectangle(x1, y1, x2 - x1, y2 - y1);
+ }
+
+ @Override // from interface IRectangle
+ public Rectangle intersection (IRectangle r) {
+ return intersection(r.getX(), r.getY(), r.getWidth(), r.getHeight());
+ }
+
+ @Override // from interface IRectangle
+ public Rectangle union (IRectangle r) {
+ Rectangle rect = new Rectangle(this);
+ rect.add(r);
+ return rect;
+ }
+
+ @Override // from interface IRectangle
+ public boolean intersectsLine (float x1, float y1, float x2, float y2) {
+ return Geometry.lineIntersectsRect(x1, y1, x2, y2, getX(), getY(), getWidth(), getHeight());
+ }
+
+ @Override // from interface IRectangle
+ public boolean intersectsLine (ILine l) {
+ return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2());
+ }
+
+ @Override // from interface IRectangle
+ public int outcode (float px, float py) {
+ int code = 0;
+
+ if (getWidth() <= 0) {
+ code |= OUT_LEFT | OUT_RIGHT;
+ } else if (px < getX()) {
+ code |= OUT_LEFT;
+ } else if (px > getMaxX()) {
+ code |= OUT_RIGHT;
+ }
+
+ if (getHeight() <= 0) {
+ code |= OUT_TOP | OUT_BOTTOM;
+ } else if (py < getY()) {
+ code |= OUT_TOP;
+ } else if (py > getMaxY()) {
+ code |= OUT_BOTTOM;
+ }
+
+ return code;
+ }
+
+ @Override // from interface IRectangle
+ public int outcode (IPoint p) {
+ return outcode(p.getX(), p.getY());
+ }
+
+ @Override // from interface IRectangle
+ public Rectangle clone () {
+ return new Rectangle(this);
+ }
+
+ @Override // from interface IShape
+ public boolean contains (float px, float py) {
+ if (isEmpty()) return false;
+
+ float x = getX(), y = getY();
+ if (px < x || py < y) return false;
+
+ px -= x;
+ py -= y;
+ return px < getWidth() && py < getHeight();
+ }
+
+ @Override // from interface IShape
+ public boolean contains (float rx, float ry, float rw, float rh) {
+ if (isEmpty()) return false;
+
+ double x1 = getX(), y1 = getY(), x2 = x1 + getWidth(), y2 = y1 + getHeight();
+ return (x1 <= rx) && (rx + rw <= x2) && (y1 <= ry) && (ry + rh <= y2);
+ }
+
+ @Override // from interface IShape
+ public boolean intersects (float rx, float ry, float rw, float rh) {
+ if (isEmpty()) return false;
+
+ double x1 = getX(), y1 = getY(), x2 = x1 + getWidth(), y2 = y1 + getHeight();
+ return (rx + rw > x1) && (rx < x2) && (ry + rh > y1) && (ry < y2);
+ }
+
+ @Override // from RectangularShape
+ public IRectangle frame () {
+ return this;
+ }
+
+ @Override // from RectangularShape
+ public IRectangle bounds () {
+ return this;
+ }
+
+ @Override // from Object
+ public boolean equals (Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof AbstractRectangle) {
+ AbstractRectangle r = (AbstractRectangle)obj;
+ return r.getX() == getX() && r.getY() == getY() &&
+ r.getWidth() == getWidth() && r.getHeight() == getHeight();
+ }
+ return false;
+ }
+
+ @Override // from Object
+ public int hashCode () {
+ return Float.floatToIntBits(getX()) ^ Float.floatToIntBits(getY()) ^
+ Float.floatToIntBits(getWidth()) ^ Float.floatToIntBits(getHeight());
+ }
+
+ @Override // from Object
+ public String toString () {
+ return Geometry.dimenToString(getWidth(), getHeight()) +
+ Geometry.pointToString(getX(), getY());
+ }
+}
diff --git a/src/main/java/pythagoras/f/Dimension.java b/src/main/java/pythagoras/f/Dimension.java
index fd8c618..8df3803 100644
--- a/src/main/java/pythagoras/f/Dimension.java
+++ b/src/main/java/pythagoras/f/Dimension.java
@@ -9,7 +9,7 @@ import java.io.Serializable;
/**
* Represents a magnitude in two dimensions.
*/
-public class Dimension implements IDimension, Serializable
+public class Dimension extends AbstractDimension implements Serializable
{
/** The magnitude in the x-dimension. */
public float width;
@@ -62,35 +62,4 @@ public class Dimension implements IDimension, Serializable
public float getHeight () {
return height;
}
-
- @Override // from interface IDimension
- public Dimension clone () {
- try {
- return (Dimension)super.clone();
- } catch (CloneNotSupportedException cnse) {
- throw new AssertionError(cnse);
- }
- }
-
- @Override
- public int hashCode () {
- return Float.floatToIntBits(width) ^ Float.floatToIntBits(height);
- }
-
- @Override
- public boolean equals (Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj instanceof Dimension) {
- Dimension d = (Dimension)obj;
- return (d.width == width && d.height == height);
- }
- return false;
- }
-
- @Override
- public String toString () {
- return Geometry.dimenToString(width, height);
- }
}
diff --git a/src/main/java/pythagoras/f/Geometry.java b/src/main/java/pythagoras/f/Geometry.java
index d4fc77c..8e054cf 100644
--- a/src/main/java/pythagoras/f/Geometry.java
+++ b/src/main/java/pythagoras/f/Geometry.java
@@ -25,6 +25,61 @@ public class Geometry
return (float)Math.sqrt(distanceSq(x1, y1, x2, y2));
}
+ /**
+ * Returns true if the specified two line segments intersect.
+ */
+ public static boolean linesIntersect (float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4) {
+ // A = (x2-x1, y2-y1)
+ // B = (x3-x1, y3-y1)
+ // C = (x4-x1, y4-y1)
+ // D = (x4-x3, y4-y3) = C-B
+ // E = (x1-x3, y1-y3) = -B
+ // F = (x2-x3, y2-y3) = A-B
+ //
+ // Result is ((AxB) * (AxC) <= 0) and ((DxE) * (DxF) <= 0)
+ //
+ // DxE = (C-B)x(-B) = BxB-CxB = BxC
+ // DxF = (C-B)x(A-B) = CxA-CxB-BxA+BxB = AxB+BxC-AxC
+ x2 -= x1; // A
+ y2 -= y1;
+ x3 -= x1; // B
+ y3 -= y1;
+ x4 -= x1; // C
+ y4 -= y1;
+
+ float AvB = x2 * y3 - x3 * y2;
+ float AvC = x2 * y4 - x4 * y2;
+
+ // online
+ if (AvB == 0 && AvC == 0) {
+ if (x2 != 0) {
+ return (x4 * x3 <= 0) ||
+ ((x3 * x2 >= 0) && (x2 > 0 ? x3 <= x2 || x4 <= x2 : x3 >= x2 || x4 >= x2));
+ }
+ if (y2 != 0) {
+ return (y4 * y3 <= 0) ||
+ ((y3 * y2 >= 0) && (y2 > 0 ? y3 <= y2 || y4 <= y2 : y3 >= y2 || y4 >= y2));
+ }
+ return false;
+ }
+
+ float BvC = x3 * y4 - x4 * y3;
+ return (AvB * AvC <= 0) && (BvC * (AvB + BvC - AvC) <= 0);
+ }
+
+ /**
+ * Returns true if the specified line segment intersects the specified rectangle.
+ */
+ public static boolean lineIntersectsRect (float x1, float y1, float x2, float y2,
+ float rx, float ry, float rw, float rh) {
+ float rr = rx + rw, rb = ry + rh;
+ return (rx <= x1 && x1 <= rr && ry <= y1 && y1 <= rb)
+ || (rx <= x2 && x2 <= rr && ry <= y2 && y2 <= rb)
+ || linesIntersect(rx, ry, rr, rb, x1, y1, x2, y2)
+ || linesIntersect(rr, ry, rx, rb, x1, y1, x2, y2);
+ }
+
/**
* Returns a string describing the supplied point, of the form +x+y,
* +x-y, -x-y, etc.
diff --git a/src/main/java/pythagoras/f/IDimension.java b/src/main/java/pythagoras/f/IDimension.java
index d546383..40b7411 100644
--- a/src/main/java/pythagoras/f/IDimension.java
+++ b/src/main/java/pythagoras/f/IDimension.java
@@ -20,7 +20,7 @@ public interface IDimension extends Cloneable
float getHeight ();
/**
- * Clones this dimension.
+ * Returns a mutable copy of this dimension.
*/
Dimension clone ();
}
diff --git a/src/main/java/pythagoras/f/ILine.java b/src/main/java/pythagoras/f/ILine.java
new file mode 100644
index 0000000..f5a772c
--- /dev/null
+++ b/src/main/java/pythagoras/f/ILine.java
@@ -0,0 +1,59 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Provides read-only access to a {@link Line}.
+ */
+public interface ILine extends IShape, Cloneable
+{
+ /**
+ * Returns the x-coordinate of the start of this line.
+ */
+ float getX1 ();
+
+ /**
+ * Returns the y-coordinate of the start of this line.
+ */
+ float getY1 ();
+
+ /**
+ * Returns the x-coordinate of the end of this line.
+ */
+ float getX2 ();
+
+ /**
+ * Returns the y-coordinate of the end of this line.
+ */
+ float getY2 ();
+
+ /**
+ * Returns a view of the starting point of this line. Subsequent changes to the starting point
+ * will be visible in the returned point.
+ */
+ IPoint p1 ();
+
+ /**
+ * Returns a copy of the starting point of this line. Subsequent changes to the starting point
+ * will not be visible in the returned point.
+ */
+ Point getP1 ();
+
+ /**
+ * Returns a view of the ending point of this line. Subsequent changes to the ending point will
+ * be visible in the returned point.
+ */
+ IPoint p2 ();
+
+ /**
+ * Returns a copy of the ending point of this line. Subsequent changes to the ending point will
+ * not be visible in the returned point.
+ */
+ Point getP2 ();
+
+ /**
+ * Returns a mutable copy of this line.
+ */
+ Line clone ();
+}
diff --git a/src/main/java/pythagoras/f/IPoint.java b/src/main/java/pythagoras/f/IPoint.java
index 7a4359f..365b12a 100644
--- a/src/main/java/pythagoras/f/IPoint.java
+++ b/src/main/java/pythagoras/f/IPoint.java
@@ -40,7 +40,7 @@ public interface IPoint extends Cloneable
float distance (IPoint p);
/**
- * Clones this point.
+ * Returns a mutable copy of this point.
*/
Point clone ();
}
diff --git a/src/main/java/pythagoras/f/IRectangle.java b/src/main/java/pythagoras/f/IRectangle.java
index 5e8eaf2..990a4ac 100644
--- a/src/main/java/pythagoras/f/IRectangle.java
+++ b/src/main/java/pythagoras/f/IRectangle.java
@@ -7,7 +7,7 @@ package pythagoras.f;
/**
* Provides read-only access to a {@link Rectangle}.
*/
-public interface IRectangle extends Shape, Cloneable
+public interface IRectangle extends IRectangularShape, Cloneable
{
/** The bitmask that indicates that a point lies to the left of this rectangle.
* See {@link #outcode}. */
@@ -24,41 +24,26 @@ public interface IRectangle extends Shape, Cloneable
int OUT_BOTTOM = 8;
/**
- * Returns the x-coordinate of the rectangle's upper left corner.
+ * Returns a view of this rectangle's upper-left corner. Subsequent changes to this rectangle's
+ * location will be reflected in the returned point.
*/
- float getX ();
+ IPoint location ();
/**
- * Returns the y-coordinate of the rectangle's upper left corner.
- */
- float getY ();
-
- /**
- * Returns the width of the rectangle.
- */
- float getWidth ();
-
- /**
- * Returns the height of the rectangle.
- */
- float getHeight ();
-
- /**
- * Returns true if this rectangle does not have > 0 width and height.
- */
- boolean isEmpty ();
-
- /**
- * Returns this rectangle's upper-left corner. Note that this constructs a new instance,
- * because a {@link Rectangle} is not an {@link IPoint} and cannot efficiently masquerade as
- * one.
+ * Returns a copy of this rectangle's upper-left corner. Subsequent changes to this rectangle's
+ * location will not be reflected in the returned point.
*/
Point getLocation ();
/**
- * Returns the size of this rectangle as a newly constructed dimension. Note that this
- * constructs a new instance, because a {@link Rectangle} is not an {@link IDimension} and
- * cannot efficiently masquerade as one.
+ * Returns a view of this rectangle's size. Subsequent changes to this rectangle's size will be
+ * reflected in the returned dimension.
+ */
+ IDimension size ();
+
+ /**
+ * Returns a copy of this rectangle's size. Subsequent changes to this rectangle's size will
+ * not be reflected in the returned dimension.
*/
Dimension getSize ();
@@ -80,11 +65,21 @@ public interface IRectangle extends Shape, Cloneable
*/
Rectangle union (IRectangle r);
+ /**
+ * Returns true if the specified line segment intersects this rectangle.
+ */
+ boolean intersectsLine (float x1, float y1, float x2, float y2);
+
+ /**
+ * Returns true if the supplied line segment intersects this rectangle.
+ */
+ boolean intersectsLine (ILine l);
+
/**
* Returns a set of flags indicating where the specified point lies in relation to the bounds
* of this rectangle. See {@link #OUT_LEFT}, etc.
*/
- int outcode (double px, double py);
+ int outcode (float px, float py);
/**
* Returns a set of flags indicating where the supplied point lies in relation to the bounds of
@@ -93,7 +88,7 @@ public interface IRectangle extends Shape, Cloneable
int outcode (IPoint point);
/**
- * Clones this rectangle.
+ * Returns a mutable copy of this rectangle.
*/
Rectangle clone ();
}
diff --git a/src/main/java/pythagoras/f/IRectangularShape.java b/src/main/java/pythagoras/f/IRectangularShape.java
new file mode 100644
index 0000000..7844878
--- /dev/null
+++ b/src/main/java/pythagoras/f/IRectangularShape.java
@@ -0,0 +1,75 @@
+//
+// Pythagoras - a collection of geometry classes
+// http://github.com/samskivert/pythagoras
+
+package pythagoras.f;
+
+/**
+ * An interface implemented by {@link Shape} classes whose geometry is defined by a rectangular
+ * frame. The framing rectangle defines the geometry, but may in some cases differ from
+ * the bounding rectangle of the shape.
+ */
+public interface IRectangularShape extends IShape
+{
+ /**
+ * Returns the x-coordinate of the upper-left corner of the framing rectangle.
+ */
+ float getX ();
+
+ /**
+ * Returns the y-coordinate of the upper-left corner of the framing rectangle.
+ */
+ float getY ();
+
+ /**
+ * Returns the width of the framing rectangle.
+ */
+ float getWidth ();
+
+ /**
+ * Returns the height of the framing rectangle.
+ */
+ float getHeight ();
+
+ /**
+ * Returns the minimum x-coordinate of the framing rectangle.
+ */
+ float getMinX ();
+
+ /**
+ * Returns the minimum y-coordinate of the framing rectangle.
+ */
+ float getMinY ();
+
+ /**
+ * Returns the maximum x-coordinate of the framing rectangle.
+ */
+ float getMaxX ();
+
+ /**
+ * Returns the maximum y-coordinate of the framing rectangle.
+ */
+ float getMaxY ();
+
+ /**
+ * Returns the x-coordinate of the center of the framing rectangle.
+ */
+ float getCenterX ();
+
+ /**
+ * Returns the y-coordinate of the center of the framing rectangle.
+ */
+ float getCenterY ();
+
+ /**
+ * Returns a view of this shape's framing rectangle. Subsequent changes to the shape will be
+ * reflected in the returned rectangle.
+ */
+ IRectangle frame ();
+
+ /**
+ * Returns a copy of this shape's framing rectangle. Subsequent changes to the shape will be
+ * reflected in the returned rectangle.
+ */
+ Rectangle getFrame ();
+}
diff --git a/src/main/java/pythagoras/f/Shape.java b/src/main/java/pythagoras/f/IShape.java
similarity index 77%
rename from src/main/java/pythagoras/f/Shape.java
rename to src/main/java/pythagoras/f/IShape.java
index 426d5dc..0f9dd9f 100644
--- a/src/main/java/pythagoras/f/Shape.java
+++ b/src/main/java/pythagoras/f/IShape.java
@@ -7,8 +7,13 @@ package pythagoras.f;
/**
* An interface provided by all shapes.
*/
-public interface Shape
+public interface IShape
{
+ /**
+ * Returns true if this shape encloses no area.
+ */
+ boolean isEmpty ();
+
/**
* Returns true if this shape contains the specified point.
*/
@@ -40,9 +45,16 @@ public interface Shape
boolean intersects (IRectangle r);
/**
- * Returns the bounding rectangle for this shape.
+ * Returns a view of the bounding rectangle for this shape. Subsequent changes to the bounding
+ * rectangle will be reflected in the returned rectangle.
*/
- IRectangle getBounds ();
+ IRectangle bounds();
+
+ /**
+ * Returns a copy of the bounding rectangle for this shape. Subsequent changes to the bounding
+ * rectangle will not be reflected in the returned rectangle.
+ */
+ Rectangle getBounds ();
// /**
// * Returns an iterator over the path described by this shape.
diff --git a/src/main/java/pythagoras/f/Line.java b/src/main/java/pythagoras/f/Line.java
new file mode 100644
index 0000000..fd6e553
--- /dev/null
+++ b/src/main/java/pythagoras/f/Line.java
@@ -0,0 +1,74 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+import java.io.Serializable;
+
+/**
+ * Represents a line segment.
+ */
+public class Line extends AbstractLine implements Serializable
+{
+ public float x1;
+ public float y1;
+ public float x2;
+ public float y2;
+
+ /**
+ * Creates a line from (0,0) to (0,0).
+ */
+ public Line () {
+ }
+
+ /**
+ * Creates a line from (x1,y1), to (x2,y2).
+ */
+ public Line (float x1, float y1, float x2, float y2) {
+ setLine(x1, y1, x2, y2);
+ }
+
+ /**
+ * Creates a line from p1 to p2.
+ */
+ public Line (IPoint p1, IPoint p2) {
+ setLine(p1, p2);
+ }
+
+ /**
+ * Sets the start and end point of this line to the specified values.
+ */
+ public void setLine (float x1, float y1, float x2, float y2) {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+ /**
+ * Sets the start and end of this line to the specified points.
+ */
+ public void setLine (IPoint p1, IPoint p2) {
+ setLine(p1.getX(), p1.getY(), p2.getY(), p2.getY());
+ }
+
+ @Override // from interface ILine
+ public float getX1 () {
+ return x1;
+ }
+
+ @Override // from interface ILine
+ public float getY1 () {
+ return y1;
+ }
+
+ @Override // from interface ILine
+ public float getX2 () {
+ return x2;
+ }
+
+ @Override // from interface ILine
+ public float getY2 () {
+ return y2;
+ }
+}
diff --git a/src/main/java/pythagoras/f/Point.java b/src/main/java/pythagoras/f/Point.java
index 59e9c65..bfe0ddd 100644
--- a/src/main/java/pythagoras/f/Point.java
+++ b/src/main/java/pythagoras/f/Point.java
@@ -9,7 +9,7 @@ import java.io.Serializable;
/**
* Represents a point on a plane.
*/
-public class Point implements IPoint, Serializable
+public class Point extends AbstractPoint implements Serializable
{
/** The x-coordinate of the point. */
public float x;
@@ -77,55 +77,4 @@ public class Point implements IPoint, Serializable
public float getY () {
return y;
}
-
- @Override // from interface IPoint
- public float distanceSq (float px, float py) {
- return Geometry.distanceSq(x, y, px, py);
- }
-
- @Override // from interface IPoint
- public float distanceSq (IPoint p) {
- return Geometry.distanceSq(x, y, p.getX(), p.getY());
- }
-
- @Override // from interface IPoint
- public float distance (float px, float py) {
- return Geometry.distance(x, y, px, py);
- }
-
- @Override // from interface IPoint
- public float distance (IPoint p) {
- return Geometry.distance(x, y, p.getX(), p.getY());
- }
-
- @Override // from interface IPoint
- public Point clone () {
- try {
- return (Point)super.clone();
- } catch (CloneNotSupportedException cnse) {
- throw new AssertionError(cnse);
- }
- }
-
- @Override
- public boolean equals (Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj instanceof Point) {
- Point p = (Point)obj;
- return x == p.x && y == p.y;
- }
- return false;
- }
-
- @Override
- public int hashCode () {
- return Float.floatToIntBits(x) ^ Float.floatToIntBits(y);
- }
-
- @Override
- public String toString () {
- return Geometry.pointToString(x, y);
- }
}
diff --git a/src/main/java/pythagoras/f/Rectangle.java b/src/main/java/pythagoras/f/Rectangle.java
index ef4d965..a55c15f 100644
--- a/src/main/java/pythagoras/f/Rectangle.java
+++ b/src/main/java/pythagoras/f/Rectangle.java
@@ -9,7 +9,7 @@ import java.io.Serializable;
/**
* Represents an area in two dimensions.
*/
-public class Rectangle implements IRectangle, Serializable
+public class Rectangle extends AbstractRectangle implements Serializable
{
/** The x-coordinate of the rectangle's upper left corner. */
public float x;
@@ -33,23 +33,23 @@ public class Rectangle implements IRectangle, Serializable
/**
* Constructs a rectangle with the supplied upper-left corner and dimensions (0,0).
*/
- public Rectangle (Point p) {
- setBounds(p.x, p.y, 0, 0);
+ public Rectangle (IPoint p) {
+ setBounds(p.getX(), p.getY(), 0, 0);
}
/**
* Constructs a rectangle with upper-left corner at (0,) and the supplied dimensions.
*/
- public Rectangle (Dimension d) {
- setBounds(0, 0, d.width, d.height);
+ public Rectangle (IDimension d) {
+ setBounds(0, 0, d.getWidth(), d.getHeight());
}
/**
* Constructs a rectangle with upper-left corner at the supplied point and with the supplied
* dimensions.
*/
- public Rectangle (Point p, Dimension d) {
- setBounds(p.x, p.y, d.width, d.height);
+ public Rectangle (IPoint p, IDimension d) {
+ setBounds(p.getX(), p.getY(), d.getWidth(), d.getHeight());
}
/**
@@ -62,23 +62,8 @@ public class Rectangle implements IRectangle, Serializable
/**
* Constructs a rectangle with bounds equal to the supplied rectangle.
*/
- public Rectangle (Rectangle r) {
- setBounds(r.x, r.y, r.width, r.height);
- }
-
- /**
- * Sets the size of this rectangle to the specified dimensions.
- */
- public void setSize (float width, float height) {
- this.width = width;
- this.height = height;
- }
-
- /**
- * Sets the size of this rectangle to the supplied dimensions.
- */
- public void setSize (Dimension d) {
- setSize(d.width, d.height);
+ public Rectangle (IRectangle r) {
+ setBounds(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
@@ -96,6 +81,21 @@ public class Rectangle implements IRectangle, Serializable
setLocation(p.getX(), p.getY());
}
+ /**
+ * Sets the size of this rectangle to the specified dimensions.
+ */
+ public void setSize (float width, float height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Sets the size of this rectangle to the supplied dimensions.
+ */
+ public void setSize (Dimension d) {
+ setSize(d.width, d.height);
+ }
+
/**
* Sets the bounds of this rectangle to the specified bounds.
*/
@@ -162,166 +162,28 @@ public class Rectangle implements IRectangle, Serializable
setBounds(x1, y1, x2 - x1, y2 - y1);
}
- @Override // from interface IRectangle
- public float getX ()
- {
+ @Override // from interface IRectangularShape
+ public float getX () {
return x;
}
- @Override // from interface IRectangle
- public float getY ()
- {
+ @Override // from interface IRectangularShape
+ public float getY () {
return y;
}
- @Override // from interface IRectangle
- public float getWidth ()
- {
+ @Override // from interface IRectangularShape
+ public float getWidth () {
return width;
}
- @Override // from interface IRectangle
- public float getHeight ()
- {
+ @Override // from interface IRectangularShape
+ public float getHeight () {
return height;
}
- @Override // from interface IRectangle
- public boolean isEmpty () {
- return width <= 0 || height <= 0;
- }
-
- @Override // from interface IRectangle
- public Point getLocation () {
- return new Point(x, y);
- }
-
- @Override // from interface IRectangle
- public Dimension getSize () {
- return new Dimension(width, height);
- }
-
- @Override // from interface IRectangle
- public Rectangle intersection (float rx, float ry, float rw, float rh) {
- float x1 = Math.max(x, rx);
- float y1 = Math.max(y, ry);
- float x2 = Math.min(x + width, rx + rw);
- float y2 = Math.min(y + height, ry + rh);
- return new Rectangle(x1, y1, x2 - x1, y2 - y1);
- }
-
- @Override // from interface IRectangle
- public Rectangle intersection (IRectangle r) {
- return intersection(r.getX(), r.getY(), r.getWidth(), r.getHeight());
- }
-
- @Override // from interface IRectangle
- public Rectangle union (IRectangle r) {
- Rectangle rect = new Rectangle(this);
- rect.add(r);
- return rect;
- }
-
- @Override // from interface IRectangle
- public int outcode (double px, double py) {
- int code = 0;
-
- if (width <= 0) {
- code |= OUT_LEFT | OUT_RIGHT;
- } else if (px < x) {
- code |= OUT_LEFT;
- } else if (px > x + width) {
- code |= OUT_RIGHT;
- }
-
- if (height <= 0) {
- code |= OUT_TOP | OUT_BOTTOM;
- } else if (py < y) {
- code |= OUT_TOP;
- } else if (py > y + height) {
- code |= OUT_BOTTOM;
- }
-
- return code;
- }
-
- @Override // from interface IRectangle
- public int outcode (IPoint p) {
- return outcode(p.getX(), p.getY());
- }
-
- @Override // from interface IRectangle
- public Rectangle clone () {
- try {
- return (Rectangle)super.clone();
- } catch (CloneNotSupportedException cnse) {
- throw new AssertionError(cnse);
- }
- }
-
- @Override // from interface Shape
- public boolean contains (float px, float py) {
- if (isEmpty()) {
- return false;
- }
- if (px < x || py < y) {
- return false;
- }
- px -= x;
- py -= y;
- return px < width && py < height;
- }
-
- @Override // from interface Shape
- public boolean contains (IPoint p) {
- return contains(p.getX(), p.getY());
- }
-
- @Override // from interface Shape
- public boolean contains (float rx, float ry, float rw, float rh) {
- return contains(rx, ry) && contains(rx + rw - 1, ry + rh - 1);
- }
-
- @Override // from interface Shape
- public boolean contains (IRectangle r) {
- return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
- }
-
- @Override // from interface Shape
- public boolean intersects (float rx, float ry, float rw, float rh) {
- return !intersection(rx, ry, rw, rh).isEmpty(); // TODO: don't create garbage
- }
-
- @Override // from interface Shape
- public boolean intersects (IRectangle r) {
- return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
- }
-
- @Override // from interface Shape
- public IRectangle getBounds () {
- return this;
- }
-
- @Override
- public boolean equals (Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj instanceof Rectangle) {
- Rectangle r = (Rectangle)obj;
- return r.x == x && r.y == y && r.width == width && r.height == height;
- }
- return false;
- }
-
- @Override
- public int hashCode () {
- return Float.floatToIntBits(x) ^ Float.floatToIntBits(y) ^
- Float.floatToIntBits(width) ^ Float.floatToIntBits(height);
- }
-
- @Override
- public String toString () {
- return Geometry.dimenToString(width, height) + Geometry.pointToString(x, y);
+ @Override // from RectangularShape
+ public void setFrame (float x, float y, float w, float h) {
+ setBounds(x, y, w, h);
}
}
diff --git a/src/main/java/pythagoras/f/RectangularShape.java b/src/main/java/pythagoras/f/RectangularShape.java
new file mode 100644
index 0000000..0083829
--- /dev/null
+++ b/src/main/java/pythagoras/f/RectangularShape.java
@@ -0,0 +1,173 @@
+//
+// Pythagoras - a collection of geometry classes
+// http://github.com/samskivert/pythagoras
+
+package pythagoras.f;
+
+/**
+ * The base class for various {@link Shape} objects whose geometry is defined by a rectangular
+ * frame.
+ */
+public abstract class RectangularShape implements IRectangularShape
+{
+ /**
+ * Sets the location and size of the framing rectangle of this shape to the specified values.
+ */
+ public abstract void setFrame (float x, float y, float w, float h);
+
+ /**
+ * Sets the location and size of the framing rectangle of this shape to the supplied values.
+ */
+ public void setFrame (IPoint loc, IDimension size) {
+ setFrame(loc.getX(), loc.getY(), size.getWidth(), size.getHeight());
+ }
+
+ /**
+ * Sets the location and size of the framing rectangle of this shape to be equal to the
+ * supplied rectangle.
+ */
+ public void setFrame (IRectangle r) {
+ setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight());
+ }
+
+ /**
+ * Sets the location and size of the framing rectangle of this shape based on the specified
+ * diagonal line.
+ */
+ public void setFrameFromDiagonal (float x1, float y1, float x2, float y2) {
+ float rx, ry, rw, rh;
+ if (x1 < x2) {
+ rx = x1;
+ rw = x2 - x1;
+ } else {
+ rx = x2;
+ rw = x1 - x2;
+ }
+ if (y1 < y2) {
+ ry = y1;
+ rh = y2 - y1;
+ } else {
+ ry = y2;
+ rh = y1 - y2;
+ }
+ setFrame(rx, ry, rw, rh);
+ }
+
+ /**
+ * Sets the location and size of the framing rectangle of this shape based on the supplied
+ * diagonal line.
+ */
+ public void setFrameFromDiagonal (IPoint p1, IPoint p2) {
+ setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY());
+ }
+
+ /**
+ * Sets the location and size of the framing rectangle of this shape based on the specified
+ * center and corner points.
+ */
+ public void setFrameFromCenter (float centerX, float centerY,
+ float cornerX, float cornerY) {
+ float width = Math.abs(cornerX - centerX);
+ float height = Math.abs(cornerY - centerY);
+ setFrame(centerX - width, centerY - height, width * 2, height * 2);
+ }
+
+ /**
+ * Sets the location and size of the framing rectangle of this shape based on the supplied
+ * center and corner points.
+ */
+ public void setFrameFromCenter (IPoint center, IPoint corner) {
+ setFrameFromCenter(center.getX(), center.getY(), corner.getX(), corner.getY());
+ }
+
+ @Override // from IRectangularShape
+ public float getMinX () {
+ return getX();
+ }
+
+ @Override // from IRectangularShape
+ public float getMinY () {
+ return getY();
+ }
+
+ @Override // from IRectangularShape
+ public float getMaxX () {
+ return getX() + getWidth();
+ }
+
+ @Override // from IRectangularShape
+ public float getMaxY () {
+ return getY() + getHeight();
+ }
+
+ @Override // from IRectangularShape
+ public float getCenterX () {
+ return getX() + getWidth() / 2;
+ }
+
+ @Override // from IRectangularShape
+ public float getCenterY () {
+ return getY() + getHeight() / 2;
+ }
+
+ @Override // from IRectangularShape
+ public IRectangle frame () {
+ return bounds();
+ }
+
+ @Override // from IRectangularShape
+ public Rectangle getFrame () {
+ return getBounds();
+ }
+
+ @Override // from interface IShape
+ public boolean isEmpty () {
+ return getWidth() <= 0 || getHeight() <= 0;
+ }
+
+ @Override // from interface IShape
+ public boolean contains (IPoint point) {
+ return contains(point.getX(), point.getY());
+ }
+
+ @Override // from interface IShape
+ public boolean contains (IRectangle rect) {
+ return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
+ }
+
+ @Override // from interface IShape
+ public boolean intersects (IRectangle rect) {
+ return intersects(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
+ }
+
+ @Override // from interface IShape
+ public IRectangle bounds () {
+ return new AbstractRectangle() {
+ @Override public float getX () {
+ return RectangularShape.this.getX();
+ }
+ @Override public float getY () {
+ return RectangularShape.this.getY();
+ }
+ @Override public float getWidth () {
+ return RectangularShape.this.getWidth();
+ }
+ @Override public float getHeight () {
+ return RectangularShape.this.getHeight();
+ }
+ // this isn't visible in the type, so won't be called by non-combatants
+ @Override public void setFrame (float x, float y, float w, float h) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override // from interface IShape
+ public Rectangle getBounds () {
+ return new Rectangle(getX(), getY(), getWidth(), getHeight());
+ }
+
+ // public PathIterator getPathIterator (AffineTransform t, float flatness) {
+ // return new FlatteningPathIterator(getPathIterator(t), flatness);
+ // }
+}