From cf7ea58945191dc9b9bcd97e22e2b8f912ffaa4f Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Fri, 10 Jun 2011 14:24:25 -0700 Subject: [PATCH] Added RoundRectangle and friends. --- .../pythagoras/f/AbstractRoundRectangle.java | 158 ++++++++++++++++++ .../java/pythagoras/f/IRoundRectangle.java | 20 +++ .../java/pythagoras/f/RoundRectangle.java | 78 +++++++++ 3 files changed, 256 insertions(+) create mode 100644 src/main/java/pythagoras/f/AbstractRoundRectangle.java create mode 100644 src/main/java/pythagoras/f/IRoundRectangle.java create mode 100644 src/main/java/pythagoras/f/RoundRectangle.java diff --git a/src/main/java/pythagoras/f/AbstractRoundRectangle.java b/src/main/java/pythagoras/f/AbstractRoundRectangle.java new file mode 100644 index 0000000..4a49ff0 --- /dev/null +++ b/src/main/java/pythagoras/f/AbstractRoundRectangle.java @@ -0,0 +1,158 @@ +// +// Pythagoras - a collection of geometry classes +// http://github.com/samskivert/pythagoras + +package pythagoras.f; + +import java.util.NoSuchElementException; + +/** + * Provides most of the implementation of {@link IRoundRectangle}, obtaining the framing rectangle + * from the derived class. + */ +public abstract class AbstractRoundRectangle extends RectangularShape implements IRoundRectangle +{ + @Override // from interface IRoundRectangle + public RoundRectangle clone () { + return new RoundRectangle(getX(), getY(), getWidth(), getHeight(), + getArcWidth(), getArcHeight()); + } + + @Override // from interface IShape + public boolean contains (float px, float py) { + if (isEmpty()) return false; + + float rx1 = getX(), ry1 = getY(); + float rx2 = rx1 + getWidth(), ry2 = ry1 + getHeight(); + if (px < rx1 || px >= rx2 || py < ry1 || py >= ry2) { + return false; + } + + float aw = getArcWidth() / 2f, ah = getArcHeight() / 2f; + float cx, cy; + if (px < rx1 + aw) { + cx = rx1 + aw; + } else if (px > rx2 - aw) { + cx = rx2 - aw; + } else { + return true; + } + + if (py < ry1 + ah) { + cy = ry1 + ah; + } else if (py > ry2 - ah) { + cy = ry2 - ah; + } else { + return true; + } + + px = (px - cx) / aw; + py = (py - cy) / ah; + return px * px + py * py <= 1f; + } + + @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 x1 = getX(), y1 = getY(), x2 = x1 + getWidth(), y2 = y1 + getHeight(); + float rx1 = rx, ry1 = ry, rx2 = rx + rw, ry2 = ry + rh; + if (rx2 < x1 || x2 < rx1 || ry2 < y1 || y2 < ry1) { + return false; + } + + float cx = (x1 + x2) / 2f, cy = (y1 + y2) / 2f; + 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); + } + + /** Provides an iterator over an {@link IRoundRectangle}. */ + protected static class Iterator implements PathIterator + { + private final float x, y, width, height, aw, ah; + private final AffineTransform t; + private int index; + + Iterator (IRoundRectangle rr, AffineTransform at) { + this.x = rr.getX(); + this.y = rr.getY(); + this.width = rr.getWidth(); + this.height = rr.getHeight(); + this.aw = Math.min(width, rr.getArcWidth()); + this.ah = Math.min(height, rr.getArcHeight()); + this.t = at; + if (width < 0f || height < 0f || aw < 0f || ah < 0f) { + index = POINTS.length; + } + } + + @Override public int getWindingRule () { + return WIND_NON_ZERO; + } + + @Override public boolean isDone () { + return index > POINTS.length; + } + + @Override public void next () { + index++; + } + + @Override public int currentSegment (float[] coords) { + if (isDone()) { + throw new NoSuchElementException("Iterator out of bounds"); + } + if (index == POINTS.length) { + return SEG_CLOSE; + } + int j = 0; + float p[] = POINTS[index]; + for (int i = 0; i < p.length; i += 4) { + coords[j++] = x + p[i + 0] * width + p[i + 1] * aw; + coords[j++] = y + p[i + 2] * height + p[i + 3] * ah; + } + if (t != null) { + t.transform(coords, 0, coords, 0, j / 2); + } + return TYPES[index]; + } + } + + // the path for round corners is generated the same way as for Ellipse + + /** The segment types correspond to points array. */ + protected static final int[] TYPES = { + PathIterator.SEG_MOVETO, PathIterator.SEG_LINETO, PathIterator.SEG_CUBICTO, + PathIterator.SEG_LINETO, PathIterator.SEG_CUBICTO, PathIterator.SEG_LINETO, + PathIterator.SEG_CUBICTO, PathIterator.SEG_LINETO, PathIterator.SEG_CUBICTO + }; + + /** The coefficient to calculate control points of Bezier curves. */ + protected static final float U = 0.5f - 2f / 3f * ((float)Math.sqrt(2f) - 1f); + + /** The points coordinates calculation table. */ + protected static final float[][] POINTS = { + { 0f, 0.5f, 0f, 0f }, // MOVETO + { 1f, -0.5f, 0f, 0f }, // LINETO + { 1f, -U, 0f, 0f, 1f, 0f, 0f, U, 1f, 0f, 0f, 0.5f }, // CUBICTO + { 1f, 0f, 1f, -0.5f }, // LINETO + { 1f, 0f, 1f, -U, 1f, -U, 1f, 0f, 1f, -0.5f, 1f, 0f }, // CUBICTO + { 0f, 0.5f, 1f, 0f }, // LINETO + { 0f, U, 1f, 0f, 0f, 0f, 1f, -U, 0f, 0f, 1f, -0.5f }, // CUBICTO + { 0f, 0f, 0f, 0.5f }, // LINETO + { 0f, 0f, 0f, U, 0f, U, 0f, 0f, 0f, 0.5f, 0f, 0f }, // CUBICTO + }; +} diff --git a/src/main/java/pythagoras/f/IRoundRectangle.java b/src/main/java/pythagoras/f/IRoundRectangle.java new file mode 100644 index 0000000..6d2d81d --- /dev/null +++ b/src/main/java/pythagoras/f/IRoundRectangle.java @@ -0,0 +1,20 @@ +// +// Pythagoras - a collection of geometry classes +// http://github.com/samskivert/pythagoras + +package pythagoras.f; + +/** + * Provides read-only access to a {@link RoundRectangle}. + */ +public interface IRoundRectangle extends IRectangularShape, Cloneable +{ + /** Returns the width of the corner arc. */ + float getArcWidth (); + + /** Returns the height of the corner arc. */ + float getArcHeight (); + + /** Returns a mutable copy of this round rectangle. */ + RoundRectangle clone (); +} diff --git a/src/main/java/pythagoras/f/RoundRectangle.java b/src/main/java/pythagoras/f/RoundRectangle.java new file mode 100644 index 0000000..0ff5fad --- /dev/null +++ b/src/main/java/pythagoras/f/RoundRectangle.java @@ -0,0 +1,78 @@ +// +// Pythagoras - a collection of geometry classes +// http://github.com/samskivert/pythagoras + +package pythagoras.f; + +import java.io.Serializable; + +/** + * Represents a rectangle with rounded corners, defined by an arc width and height. + */ +public class RoundRectangle extends AbstractRoundRectangle implements Serializable +{ + public float x; + public float y; + public float width; + public float height; + public float arcwidth; + public float archeight; + + public RoundRectangle () { + } + + public RoundRectangle (float x, float y, float width, float height, + float arcwidth, float archeight) { + setRoundRect(x, y, width, height, arcwidth, archeight); + } + + public void setRoundRect (float x, float y, float width, float height, + float arcwidth, float archeight) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.arcwidth = arcwidth; + this.archeight = archeight; + } + + public void setRoundRect (IRoundRectangle rr) { + setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(), + rr.getArcWidth(), rr.getArcHeight()); + } + + @Override // from interface IRoundRectangle + public float getArcWidth () { + return arcwidth; + } + + @Override // from interface IRoundRectangle + public float getArcHeight () { + return archeight; + } + + @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 RoundRectangle + public void setFrame (float x, float y, float width, float height) { + setRoundRect(x, y, width, height, arcwidth, archeight); + } +}