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.
*/
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);
}
}
+55
View File
@@ -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 <code>+x+y</code>,
* <code>+x-y</code>, <code>-x-y</code>, etc.
+1 -1
View File
@@ -20,7 +20,7 @@ public interface IDimension extends Cloneable
float getHeight ();
/**
* Clones this dimension.
* Returns a mutable copy of this dimension.
*/
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);
/**
* Clones this point.
* Returns a mutable copy of this point.
*/
Point clone ();
}
+26 -31
View File
@@ -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 ();
}
@@ -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.
*/
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.
+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.
*/
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);
}
}
+35 -173
View File
@@ -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);
}
}
@@ -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);
// }
}