Further refinement of read-only interfaces; added RectangularShape-ness; added

Line and friends; added AbstractX classes which implement all non-mutating
methods and can be used to create read-only views.
This commit is contained in:
Michael Bayne
2011-06-09 16:21:20 -07:00
parent e3c4be2602
commit 61dbc0e38d
16 changed files with 914 additions and 293 deletions
@@ -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());
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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());
}
}
+1 -32
View File
@@ -9,7 +9,7 @@ import java.io.Serializable;
/** /**
* Represents a magnitude in two dimensions. * 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. */ /** The magnitude in the x-dimension. */
public float width; public float width;
@@ -62,35 +62,4 @@ public class Dimension implements IDimension, Serializable
public float getHeight () { public float getHeight () {
return height; 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);
}
} }
+55
View File
@@ -25,6 +25,61 @@ public class Geometry
return (float)Math.sqrt(distanceSq(x1, y1, x2, y2)); 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 <code>+x+y</code>, * Returns a string describing the supplied point, of the form <code>+x+y</code>,
* <code>+x-y</code>, <code>-x-y</code>, etc. * <code>+x-y</code>, <code>-x-y</code>, etc.
+1 -1
View File
@@ -20,7 +20,7 @@ public interface IDimension extends Cloneable
float getHeight (); float getHeight ();
/** /**
* Clones this dimension. * Returns a mutable copy of this dimension.
*/ */
Dimension clone (); Dimension clone ();
} }
+59
View File
@@ -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 ();
}
+1 -1
View File
@@ -40,7 +40,7 @@ public interface IPoint extends Cloneable
float distance (IPoint p); float distance (IPoint p);
/** /**
* Clones this point. * Returns a mutable copy of this point.
*/ */
Point clone (); Point clone ();
} }
+26 -31
View File
@@ -7,7 +7,7 @@ package pythagoras.f;
/** /**
* Provides read-only access to a {@link Rectangle}. * 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. /** The bitmask that indicates that a point lies to the left of this rectangle.
* See {@link #outcode}. */ * See {@link #outcode}. */
@@ -24,41 +24,26 @@ public interface IRectangle extends Shape, Cloneable
int OUT_BOTTOM = 8; 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. * 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.
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.
*/ */
Point getLocation (); Point getLocation ();
/** /**
* Returns the size of this rectangle as a newly constructed dimension. Note that this * Returns a view of this rectangle's size. Subsequent changes to this rectangle's size will be
* constructs a new instance, because a {@link Rectangle} is not an {@link IDimension} and * reflected in the returned dimension.
* cannot efficiently masquerade as one. */
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 (); Dimension getSize ();
@@ -80,11 +65,21 @@ public interface IRectangle extends Shape, Cloneable
*/ */
Rectangle union (IRectangle r); 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 * Returns a set of flags indicating where the specified point lies in relation to the bounds
* of this rectangle. See {@link #OUT_LEFT}, etc. * 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 * 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); int outcode (IPoint point);
/** /**
* Clones this rectangle. * Returns a mutable copy of this rectangle.
*/ */
Rectangle clone (); Rectangle clone ();
} }
@@ -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 <em>defines</em> the geometry, but may in some cases differ from
* the <em>bounding</em> 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 ();
}
@@ -7,8 +7,13 @@ package pythagoras.f;
/** /**
* An interface provided by all shapes. * 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. * Returns true if this shape contains the specified point.
*/ */
@@ -40,9 +45,16 @@ public interface Shape
boolean intersects (IRectangle r); 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. // * Returns an iterator over the path described by this shape.
+74
View File
@@ -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;
}
}
+1 -52
View File
@@ -9,7 +9,7 @@ import java.io.Serializable;
/** /**
* Represents a point on a plane. * 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. */ /** The x-coordinate of the point. */
public float x; public float x;
@@ -77,55 +77,4 @@ public class Point implements IPoint, Serializable
public float getY () { public float getY () {
return y; 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);
}
} }
+35 -173
View File
@@ -9,7 +9,7 @@ import java.io.Serializable;
/** /**
* Represents an area in two dimensions. * 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. */ /** The x-coordinate of the rectangle's upper left corner. */
public float x; 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). * Constructs a rectangle with the supplied upper-left corner and dimensions (0,0).
*/ */
public Rectangle (Point p) { public Rectangle (IPoint p) {
setBounds(p.x, p.y, 0, 0); setBounds(p.getX(), p.getY(), 0, 0);
} }
/** /**
* Constructs a rectangle with upper-left corner at (0,) and the supplied dimensions. * Constructs a rectangle with upper-left corner at (0,) and the supplied dimensions.
*/ */
public Rectangle (Dimension d) { public Rectangle (IDimension d) {
setBounds(0, 0, d.width, d.height); setBounds(0, 0, d.getWidth(), d.getHeight());
} }
/** /**
* Constructs a rectangle with upper-left corner at the supplied point and with the supplied * Constructs a rectangle with upper-left corner at the supplied point and with the supplied
* dimensions. * dimensions.
*/ */
public Rectangle (Point p, Dimension d) { public Rectangle (IPoint p, IDimension d) {
setBounds(p.x, p.y, d.width, d.height); 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. * Constructs a rectangle with bounds equal to the supplied rectangle.
*/ */
public Rectangle (Rectangle r) { public Rectangle (IRectangle r) {
setBounds(r.x, r.y, r.width, r.height); setBounds(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* 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);
} }
/** /**
@@ -96,6 +81,21 @@ public class Rectangle implements IRectangle, Serializable
setLocation(p.getX(), p.getY()); 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. * 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); setBounds(x1, y1, x2 - x1, y2 - y1);
} }
@Override // from interface IRectangle @Override // from interface IRectangularShape
public float getX () public float getX () {
{
return x; return x;
} }
@Override // from interface IRectangle @Override // from interface IRectangularShape
public float getY () public float getY () {
{
return y; return y;
} }
@Override // from interface IRectangle @Override // from interface IRectangularShape
public float getWidth () public float getWidth () {
{
return width; return width;
} }
@Override // from interface IRectangle @Override // from interface IRectangularShape
public float getHeight () public float getHeight () {
{
return height; return height;
} }
@Override // from interface IRectangle @Override // from RectangularShape
public boolean isEmpty () { public void setFrame (float x, float y, float w, float h) {
return width <= 0 || height <= 0; setBounds(x, y, w, h);
}
@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);
} }
} }
@@ -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);
// }
}