From e61f929c761d6a1568f5c67a79941f4731bfc7ec Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Fri, 10 Jun 2011 14:03:21 -0700 Subject: [PATCH] Ellipses (by which I don't mean multiple ...s). --- .../java/pythagoras/f/AbstractEllipse.java | 127 ++++++++++++++++++ src/main/java/pythagoras/f/Ellipse.java | 65 +++++++++ src/main/java/pythagoras/f/IEllipse.java | 13 ++ 3 files changed, 205 insertions(+) create mode 100644 src/main/java/pythagoras/f/AbstractEllipse.java create mode 100644 src/main/java/pythagoras/f/Ellipse.java create mode 100644 src/main/java/pythagoras/f/IEllipse.java diff --git a/src/main/java/pythagoras/f/AbstractEllipse.java b/src/main/java/pythagoras/f/AbstractEllipse.java new file mode 100644 index 0000000..6e71aed --- /dev/null +++ b/src/main/java/pythagoras/f/AbstractEllipse.java @@ -0,0 +1,127 @@ +// +// $Id$ + +package pythagoras.f; + +import java.util.NoSuchElementException; + +/** + * Provides most of the implementation of {@link IEllipse}, obtaining the framing rectangle from + * the derived class. + */ +public abstract class AbstractEllipse extends RectangularShape implements IEllipse +{ + @Override // from IEllipse + public Ellipse clone () { + return new Ellipse(getX(), getY(), getWidth(), getHeight()); + } + + @Override // from interface IShape + public boolean contains (float px, float py) { + if (isEmpty()) return false; + float a = (px - getX()) / getWidth() - 0.5f; + float b = (py - getY()) / getHeight() - 0.5f; + return a * a + b * b < 0.25f; + } + + @Override // from interface IShape + public boolean contains (float rx, float ry, float rw, float rh) { + if (isEmpty() || rw <= 0f || rh <= 0f) return false; + float rx1 = rx, ry1 = ry, rx2 = rx + rw, ry2 = ry + rh; + return contains(rx1, ry1) && contains(rx2, ry1) && contains(rx2, ry2) && contains(rx1, ry2); + } + + @Override // from interface IShape + public boolean intersects (float rx, float ry, float rw, float rh) { + if (isEmpty() || rw <= 0f || rh <= 0f) return false; + float cx = getX() + getWidth() / 2f; + float cy = getY() + getHeight() / 2f; + float rx1 = rx, ry1 = ry, rx2 = rx + rw, ry2 = ry + rh; + float nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx); + float ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy); + return contains(nx, ny); + } + + @Override // from interface IShape + public PathIterator getPathIterator (AffineTransform at) { + return new Iterator(this, at); + } + + /** An iterator over an {@link IEllipse}. */ + protected static class Iterator implements PathIterator + { + private final float x, y, width, height; + private final AffineTransform t; + private int index; + + Iterator (IEllipse e, AffineTransform t) { + this.x = e.getX(); + this.y = e.getY(); + this.width = e.getWidth(); + this.height = e.getHeight(); + this.t = t; + if (width < 0f || height < 0f) { + index = 6; + } + } + + @Override public int getWindingRule () { + return WIND_NON_ZERO; + } + + @Override public boolean isDone () { + return index > 5; + } + + @Override public void next () { + index++; + } + + @Override public int currentSegment (float[] coords) { + if (isDone()) { + throw new NoSuchElementException("Iterator out of bounds"); + } + if (index == 5) { + return SEG_CLOSE; + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + count = 1; + float p[] = POINTS[3]; + coords[0] = x + p[4] * width; + coords[1] = y + p[5] * height; + } else { + type = SEG_CUBICTO; + count = 3; + float p[] = POINTS[index - 1]; + int j = 0; + for (int i = 0; i < 3; i++) { + coords[j] = x + p[j++] * width; + coords[j] = y + p[j++] * height; + } + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + } + + // An ellipse is subdivided into four quarters by x and y axis. Each part is approximated by a + // cubic Bezier curve. The arc in the first quarter starts in (a, 0) and finishes in (0, b) + // points. Control points for the cubic curve are (a, 0), (a, m), (n, b) and (0, b) where n and + // m are calculated based on the requirement that the Bezier curve in point 0.5 should lay on + // the arc. + + /** The coefficient to calculate control points of Bezier curves. */ + private static final float U = 2f / 3f * ((float)Math.sqrt(2) - 1f); + + /** The points coordinates calculation table. */ + private static final float[][] POINTS = { + { 1f, 0.5f + U, 0.5f + U, 1f, 0.5f, 1f }, + { 0.5f - U, 1f, 0f, 0.5f + U, 0f, 0.5f }, + { 0f, 0.5f - U, 0.5f - U, 0f, 0.5f, 0f }, + { 0.5f + U, 0f, 1f, 0.5f - U, 1f, 0.5f } }; +} diff --git a/src/main/java/pythagoras/f/Ellipse.java b/src/main/java/pythagoras/f/Ellipse.java new file mode 100644 index 0000000..19fff97 --- /dev/null +++ b/src/main/java/pythagoras/f/Ellipse.java @@ -0,0 +1,65 @@ +// +// $Id$ + +package pythagoras.f; + +import java.io.Serializable; + +/** + * Represents an ellipse that is described by a framing rectangle. + */ +public class Ellipse extends AbstractEllipse implements Serializable +{ + /** The x-coordinate of the framing rectangle. */ + public float x; + + /** The y-coordinate of the framing rectangle. */ + public float y; + + /** The width of the framing rectangle. */ + public float width; + + /** The height of the framing rectangle. */ + public float height; + + /** + * Creates an ellipse with framing rectangle (0x0+0+0). + */ + public Ellipse () { + } + + /** + * Creates an ellipse with the specified framing rectangle. + */ + public Ellipse (float x, float y, float width, float height) { + setFrame(x, y, width, height); + } + + @Override // from interface IRectangularShape + public float getX () { + return x; + } + + @Override // from interface IRectangularShape + public float getY () { + return y; + } + + @Override // from interface IRectangularShape + public float getWidth () { + return width; + } + + @Override // from interface IRectangularShape + public float getHeight () { + return height; + } + + @Override // from RectangularShape + public void setFrame (float x, float y, float w, float h) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} diff --git a/src/main/java/pythagoras/f/IEllipse.java b/src/main/java/pythagoras/f/IEllipse.java new file mode 100644 index 0000000..3f4368f --- /dev/null +++ b/src/main/java/pythagoras/f/IEllipse.java @@ -0,0 +1,13 @@ +// +// $Id$ + +package pythagoras.f; + +/** + * Provides read-only access to an {@link Ellipse}. + */ +public interface IEllipse extends IRectangularShape, Cloneable +{ + /** Returns a mutable copy of this ellipse. */ + Ellipse clone (); +}