diff --git a/src/main/java/pythagoras/f/AbstractCubicCurve.java b/src/main/java/pythagoras/f/AbstractCubicCurve.java
new file mode 100644
index 0000000..1a49e96
--- /dev/null
+++ b/src/main/java/pythagoras/f/AbstractCubicCurve.java
@@ -0,0 +1,173 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Provides most of the implementation of {@link ICubicCurve}, obtaining only the start, end and
+ * control points from the derived class.
+ */
+public abstract class AbstractCubicCurve implements ICubicCurve
+{
+ @Override // from interface ICubicCurve
+ public Point getP1 () {
+ return new Point(getX1(), getY1());
+ }
+
+ @Override // from interface ICubicCurve
+ public Point getCtrlP1 () {
+ return new Point(getCtrlX1(), getCtrlY1());
+ }
+
+ @Override // from interface ICubicCurve
+ public Point getCtrlP2 () {
+ return new Point(getCtrlX2(), getCtrlY2());
+ }
+
+ @Override // from interface ICubicCurve
+ public Point getP2 () {
+ return new Point(getX2(), getY2());
+ }
+
+ @Override // from interface ICubicCurve
+ public double getFlatnessSq () {
+ return CubicCurves.getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(),
+ getCtrlX2(), getCtrlY2(), getX2(), getY2());
+ }
+
+ @Override // from interface ICubicCurve
+ public double getFlatness () {
+ return CubicCurves.getFlatness(getX1(), getY1(), getCtrlX1(), getCtrlY1(),
+ getCtrlX2(), getCtrlY2(), getX2(), getY2());
+ }
+
+ @Override // from interface ICubicCurve
+ public void subdivide (CubicCurve left, CubicCurve right) {
+ CubicCurves.subdivide(this, left, right);
+ }
+
+ @Override // from interface ICubicCurve
+ public CubicCurve clone () {
+ return new CubicCurve(getX1(), getY1(), getCtrlX1(), getCtrlY1(),
+ getCtrlX2(), getCtrlY2(), getX2(), getY2());
+ }
+
+ @Override // from interface IShape
+ public boolean isEmpty () {
+ return true; // curves contain no space
+ }
+
+ @Override // from interface IShape
+ public boolean contains (float px, float py) {
+ return Crossing.isInsideEvenOdd(Crossing.crossShape(this, px, py));
+ }
+
+ @Override // from interface IShape
+ public boolean contains (float rx, float ry, float rw, float rh) {
+ int cross = Crossing.intersectShape(this, rx, ry, rw, rh);
+ return (cross != Crossing.CROSSING) && Crossing.isInsideEvenOdd(cross);
+ }
+
+ @Override // from interface IShape
+ public boolean contains (IPoint p) {
+ return contains(p.getX(), p.getY());
+ }
+
+ @Override // from interface IShape
+ public boolean contains (IRectangle r) {
+ return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
+ }
+
+ @Override // from interface IShape
+ public boolean intersects (float rx, float ry, float rw, float rh) {
+ int cross = Crossing.intersectShape(this, rx, ry, rw, rh);
+ return (cross == Crossing.CROSSING) || Crossing.isInsideEvenOdd(cross);
+ }
+
+ @Override // from interface IShape
+ public boolean intersects (IRectangle r) {
+ return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
+ }
+
+ @Override // from interface IShape
+ public Rectangle getBounds () {
+ return getBounds(new Rectangle());
+ }
+
+ @Override // from interface IShape
+ public Rectangle getBounds (Rectangle target) {
+ float x1 = getX1(), y1 = getY1(), x2 = getX2(), y2 = getY2();
+ float ctrlx1 = getCtrlX1(), ctrly1 = getCtrlY1();
+ float ctrlx2 = getCtrlX2(), ctrly2 = getCtrlY2();
+ float rx1 = Math.min(Math.min(x1, x2), Math.min(ctrlx1, ctrlx2));
+ float ry1 = Math.min(Math.min(y1, y2), Math.min(ctrly1, ctrly2));
+ float rx2 = Math.max(Math.max(x1, x2), Math.max(ctrlx1, ctrlx2));
+ float ry2 = Math.max(Math.max(y1, y2), Math.max(ctrly1, ctrly2));
+ target.setBounds(rx1, ry1, rx2 - rx1, ry2 - ry1);
+ return target;
+ }
+
+ @Override // from interface IShape
+ public PathIterator getPathIterator (AffineTransform t) {
+ return new Iterator(this, t);
+ }
+
+ @Override // from interface IShape
+ public PathIterator getPathIterator (AffineTransform at, float flatness) {
+ return new FlatteningPathIterator(getPathIterator(at), flatness);
+ }
+
+ /** An iterator over an {@link ICubicCurve}. */
+ protected static class Iterator implements PathIterator
+ {
+ private ICubicCurve c;
+ private AffineTransform t;
+ private int index;
+
+ Iterator (ICubicCurve c, AffineTransform t) {
+ this.c = c;
+ this.t = t;
+ }
+
+ @Override public int getWindingRule () {
+ return WIND_NON_ZERO;
+ }
+
+ @Override public boolean isDone () {
+ return index > 1;
+ }
+
+ @Override public void next () {
+ index++;
+ }
+
+ @Override public int currentSegment (float[] coords) {
+ if (isDone()) {
+ throw new NoSuchElementException("Iterator out of bounds");
+ }
+ int type;
+ int count;
+ if (index == 0) {
+ type = SEG_MOVETO;
+ coords[0] = c.getX1();
+ coords[1] = c.getY1();
+ count = 1;
+ } else {
+ type = SEG_CUBICTO;
+ coords[0] = c.getCtrlX1();
+ coords[1] = c.getCtrlY1();
+ coords[2] = c.getCtrlX2();
+ coords[3] = c.getCtrlY2();
+ coords[4] = c.getX2();
+ coords[5] = c.getY2();
+ count = 3;
+ }
+ if (t != null) {
+ t.transform(coords, 0, coords, 0, count);
+ }
+ return type;
+ }
+ }
+}
diff --git a/src/main/java/pythagoras/f/AbstractLine.java b/src/main/java/pythagoras/f/AbstractLine.java
index 37a0693..1878ff2 100644
--- a/src/main/java/pythagoras/f/AbstractLine.java
+++ b/src/main/java/pythagoras/f/AbstractLine.java
@@ -11,38 +11,26 @@ import java.util.NoSuchElementException;
*/
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());
+ return getP1(new Point());
}
@Override // from interface ILine
- public IPoint p2 () {
- return new AbstractPoint() {
- @Override public float getX () {
- return getX2();
- }
- @Override public float getY () {
- return getY2();
- }
- };
+ public Point getP1 (Point target) {
+ target.setLocation(getX1(), getY1());
+ return target;
}
@Override // from interface ILine
public Point getP2 () {
- return new Point(getX2(), getY2());
+ return getP2(new Point());
+ }
+
+ @Override // from interface ILine
+ public Point getP2 (Point target) {
+ target.setLocation(getX2(), getY2());
+ return target;
}
@Override // from interface ILine
@@ -126,31 +114,12 @@ public abstract class AbstractLine implements ILine
}
@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();
- }
- };
+ public Rectangle getBounds () {
+ return getBounds(new Rectangle());
}
@Override // from interface IShape
- public Rectangle getBounds () {
+ public Rectangle getBounds (Rectangle target) {
float x1 = getX1(), x2 = getX2(), y1 = getY1(), y2 = getY2();
float rx, ry, rw, rh;
if (x1 < x2) {
@@ -167,7 +136,8 @@ public abstract class AbstractLine implements ILine
ry = y2;
rh = y1 - y2;
}
- return new Rectangle(rx, ry, rw, rh);
+ target.setBounds(rx, ry, rw, rh);
+ return target;
}
@Override // from interface IShape
diff --git a/src/main/java/pythagoras/f/AbstractQuadCurve.java b/src/main/java/pythagoras/f/AbstractQuadCurve.java
new file mode 100644
index 0000000..a8426fa
--- /dev/null
+++ b/src/main/java/pythagoras/f/AbstractQuadCurve.java
@@ -0,0 +1,162 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Provides most of the implementation of {@link IQuadCurve}, obtaining only the start, end and
+ * control point from the derived class.
+ */
+public abstract class AbstractQuadCurve implements IQuadCurve
+{
+ @Override // from interface IQuadCurve
+ public Point getP1 () {
+ return new Point(getX1(), getY1());
+ }
+
+ @Override // from interface IQuadCurve
+ public Point getCtrlP () {
+ return new Point(getCtrlX(), getCtrlY());
+ }
+
+ @Override // from interface IQuadCurve
+ public Point getP2 () {
+ return new Point(getX2(), getY2());
+ }
+
+ @Override // from interface IQuadCurve
+ public float getFlatnessSq () {
+ return Lines.pointSegDistSq(getCtrlX(), getCtrlY(), getX1(), getY1(), getX2(), getY2());
+ }
+
+ @Override // from interface IQuadCurve
+ public float getFlatness () {
+ return Lines.pointSegDist(getCtrlX(), getCtrlY(), getX1(), getY1(), getX2(), getY2());
+ }
+
+ @Override // from interface IQuadCurve
+ public void subdivide (QuadCurve left, QuadCurve right) {
+ QuadCurves.subdivide(this, left, right);
+ }
+
+ @Override // from interface IQuadCurve
+ public QuadCurve clone () {
+ return new QuadCurve(getX1(), getY1(), getCtrlX(), getCtrlY(), getX2(), getY2());
+ }
+
+ @Override // from interface IShape
+ public boolean isEmpty () {
+ return true; // curves contain no space
+ }
+
+ @Override // from interface IShape
+ public boolean contains (float px, float py) {
+ return Crossing.isInsideEvenOdd(Crossing.crossShape(this, px, py));
+ }
+
+ @Override // from interface IShape
+ public boolean contains (float rx, float ry, float rw, float rh) {
+ int cross = Crossing.intersectShape(this, rx, ry, rw, rh);
+ return cross != Crossing.CROSSING && Crossing.isInsideEvenOdd(cross);
+ }
+
+ @Override // from interface IShape
+ public boolean contains (IPoint p) {
+ return contains(p.getX(), p.getY());
+ }
+
+ @Override // from interface IShape
+ public boolean contains (IRectangle r) {
+ return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
+ }
+
+ @Override // from interface IShape
+ public boolean intersects (float rx, float ry, float rw, float rh) {
+ int cross = Crossing.intersectShape(this, rx, ry, rw, rh);
+ return cross == Crossing.CROSSING || Crossing.isInsideEvenOdd(cross);
+ }
+
+ @Override // from interface IShape
+ public boolean intersects (IRectangle r) {
+ return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
+ }
+
+ @Override // from interface IShape
+ public Rectangle getBounds () {
+ return getBounds(new Rectangle());
+ }
+
+ @Override // from interface IShape
+ public Rectangle getBounds (Rectangle target) {
+ float x1 = getX1(), y1 = getY1(), x2 = getX2(), y2 = getY2();
+ float ctrlx = getCtrlX(), ctrly = getCtrlY();
+ float rx0 = Math.min(Math.min(x1, x2), ctrlx);
+ float ry0 = Math.min(Math.min(y1, y2), ctrly);
+ float rx1 = Math.max(Math.max(x1, x2), ctrlx);
+ float ry1 = Math.max(Math.max(y1, y2), ctrly);
+ target.setBounds(rx0, ry0, rx1 - rx0, ry1 - ry0);
+ return target;
+ }
+
+ @Override // from interface IShape
+ public PathIterator getPathIterator (AffineTransform t) {
+ return new Iterator(this, t);
+ }
+
+ @Override // from interface IShape
+ public PathIterator getPathIterator (AffineTransform t, float flatness) {
+ return new FlatteningPathIterator(getPathIterator(t), flatness);
+ }
+
+ /** An iterator over an {@link IQuadCurve}. */
+ protected static class Iterator implements PathIterator
+ {
+ private IQuadCurve c;
+ private AffineTransform t;
+ private int index;
+
+ Iterator (IQuadCurve q, AffineTransform t) {
+ this.c = q;
+ this.t = t;
+ }
+
+ @Override public int getWindingRule () {
+ return WIND_NON_ZERO;
+ }
+
+ @Override public boolean isDone () {
+ return (index > 1);
+ }
+
+ @Override public void next () {
+ index++;
+ }
+
+ @Override public int currentSegment (float[] coords) {
+ if (isDone()) {
+ throw new NoSuchElementException("Iterator out of bounds");
+ }
+ int type;
+ int count;
+ if (index == 0) {
+ type = SEG_MOVETO;
+ coords[0] = c.getX1();
+ coords[1] = c.getY1();
+ count = 1;
+ } else {
+ type = SEG_QUADTO;
+ coords[0] = c.getCtrlX();
+ coords[1] = c.getCtrlY();
+ coords[2] = c.getX2();
+ coords[3] = c.getY2();
+ count = 2;
+ }
+ if (t != null) {
+ t.transform(coords, 0, coords, 0, count);
+ }
+ return type;
+ }
+ }
+}
diff --git a/src/main/java/pythagoras/f/AbstractRectangle.java b/src/main/java/pythagoras/f/AbstractRectangle.java
index 813c111..d9c96ab 100644
--- a/src/main/java/pythagoras/f/AbstractRectangle.java
+++ b/src/main/java/pythagoras/f/AbstractRectangle.java
@@ -11,38 +11,26 @@ import java.util.NoSuchElementException;
*/
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());
+ return getLocation(new Point());
}
@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();
- }
- };
+ public Point getLocation (Point target) {
+ target.setLocation(getX(), getY());
+ return target;
}
@Override // from interface IRectangle
public Dimension getSize () {
- return new Dimension(getWidth(), getHeight());
+ return getSize(new Dimension());
+ }
+
+ @Override // from interface IRectangle
+ public Dimension getSize (Dimension target) {
+ target.setSize(getWidth(), getHeight());
+ return target;
}
@Override // from interface IRectangle
@@ -147,16 +135,6 @@ public abstract class AbstractRectangle extends RectangularShape implements IRec
return new Iterator(this, t);
}
- @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) {
diff --git a/src/main/java/pythagoras/f/Crossing.java b/src/main/java/pythagoras/f/Crossing.java
index 3d117d0..eeb089d 100644
--- a/src/main/java/pythagoras/f/Crossing.java
+++ b/src/main/java/pythagoras/f/Crossing.java
@@ -218,17 +218,15 @@ class Crossing
}
}
- /**
- * CubicCurve class provides basic functionality to find curve crossing and calculating bounds
- */
- public static class CubicCurve
+ /** CubicCurve helper for finding curve crossing and calculating bounds. */
+ public static class CubicCurveH
{
float ax, ay, bx, by, cx, cy;
float Ax, Ay, Bx, By, Cx, Cy;
float Ax3, Bx2;
- public CubicCurve (float x1, float y1, float cx1, float cy1, float cx2, float cy2,
- float x2, float y2) {
+ public CubicCurveH (float x1, float y1, float cx1, float cy1, float cx2, float cy2,
+ float x2, float y2) {
ax = x2 - x1;
ay = y2 - y1;
bx = cx1 - x1;
@@ -410,7 +408,7 @@ class Crossing
}
// INSIDE
- CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
+ CubicCurveH c = new CubicCurveH(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
float px = x - x1, py = y - y1;
float[] res = new float[3];
int rc = c.solvePoint(res, px);
@@ -471,7 +469,7 @@ class Crossing
* Returns how many times a ray from point (x,y) crosses a shape.
*/
public static int crossShape (IShape s, float x, float y) {
- if (!s.bounds().contains(x, y)) {
+ if (!s.getBounds().contains(x, y)) {
return 0;
}
return crossPath(s.getPathIterator(null), x, y);
@@ -642,7 +640,7 @@ class Crossing
}
// INSIDE
- CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
+ CubicCurveH c = new CubicCurveH(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
float px1 = rx1 - x1;
float py1 = ry1 - y1;
float px2 = rx2 - x1;
@@ -758,7 +756,7 @@ class Crossing
* Returns how many times rectangle stripe cross shape or the are intersect
*/
public static int intersectShape (IShape s, float x, float y, float w, float h) {
- if (!s.bounds().intersects(x, y, w, h)) {
+ if (!s.getBounds().intersects(x, y, w, h)) {
return 0;
}
return intersectPath(s.getPathIterator(null), x, y, w, h);
diff --git a/src/main/java/pythagoras/f/CubicCurve.java b/src/main/java/pythagoras/f/CubicCurve.java
new file mode 100644
index 0000000..f63d8d4
--- /dev/null
+++ b/src/main/java/pythagoras/f/CubicCurve.java
@@ -0,0 +1,142 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+import java.io.Serializable;
+
+/**
+ * Represents a cubic curve.
+ */
+public class CubicCurve extends AbstractCubicCurve implements Serializable
+{
+ /** The x-coordinate of the start of this curve. */
+ public float x1;
+
+ /** The y-coordinate of the start of this curve. */
+ public float y1;
+
+ /** The x-coordinate of the first control point. */
+ public float ctrlx1;
+
+ /** The y-coordinate of the first control point. */
+ public float ctrly1;
+
+ /** The x-coordinate of the second control point. */
+ public float ctrlx2;
+
+ /** The x-coordinate of the second control point. */
+ public float ctrly2;
+
+ /** The x-coordinate of the end of this curve. */
+ public float x2;
+
+ /** The y-coordinate of the end of this curve. */
+ public float y2;
+
+ /**
+ * Creates a cubic curve with all points at (0,0).
+ */
+ public CubicCurve () {
+ }
+
+ /**
+ * Creates a cubic curve with the specified start, control, and end points.
+ */
+ public CubicCurve (float x1, float y1, float ctrlx1, float ctrly1,
+ float ctrlx2, float ctrly2, float x2, float y2) {
+ setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
+ }
+
+ /**
+ * Configures the start, control and end points for this curve.
+ */
+ public void setCurve (float x1, float y1, float ctrlx1, float ctrly1, float ctrlx2,
+ float ctrly2, float x2, float y2) {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.ctrlx1 = ctrlx1;
+ this.ctrly1 = ctrly1;
+ this.ctrlx2 = ctrlx2;
+ this.ctrly2 = ctrly2;
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+ /**
+ * Configures the start, control and end points for this curve.
+ */
+ public void setCurve (IPoint p1, IPoint cp1, IPoint cp2, IPoint p2) {
+ setCurve(p1.getX(), p1.getY(), cp1.getX(), cp1.getY(),
+ cp2.getX(), cp2.getY(), p2.getX(), p2.getY());
+ }
+
+ /**
+ * Configures the start, control and end points for this curve, using the values at the
+ * specified offset in the {@link coords} array.
+ */
+ public void setCurve (float[] coords, int offset) {
+ setCurve(coords[offset + 0], coords[offset + 1], coords[offset + 2], coords[offset + 3],
+ coords[offset + 4], coords[offset + 5], coords[offset + 6], coords[offset + 7]);
+ }
+
+ /**
+ * Configures the start, control and end points for this curve, using the values at the
+ * specified offset in the {@link points} array.
+ */
+ public void setCurve (IPoint[] points, int offset) {
+ setCurve(points[offset + 0].getX(), points[offset + 0].getY(),
+ points[offset + 1].getX(), points[offset + 1].getY(),
+ points[offset + 2].getX(), points[offset + 2].getY(),
+ points[offset + 3].getX(), points[offset + 3].getY());
+ }
+
+ /**
+ * Configures the start, control and end points for this curve to be the same as the supplied
+ * curve.
+ */
+ public void setCurve (ICubicCurve curve) {
+ setCurve(curve.getX1(), curve.getY1(), curve.getCtrlX1(), curve.getCtrlY1(),
+ curve.getCtrlX2(), curve.getCtrlY2(), curve.getX2(), curve.getY2());
+ }
+
+ @Override // from interface ICubicCurve
+ public float getX1 () {
+ return x1;
+ }
+
+ @Override // from interface ICubicCurve
+ public float getY1 () {
+ return y1;
+ }
+
+ @Override // from interface ICubicCurve
+ public float getCtrlX1 () {
+ return ctrlx1;
+ }
+
+ @Override // from interface ICubicCurve
+ public float getCtrlY1 () {
+ return ctrly1;
+ }
+
+ @Override // from interface ICubicCurve
+ public float getCtrlX2 () {
+ return ctrlx2;
+ }
+
+ @Override // from interface ICubicCurve
+ public float getCtrlY2 () {
+ return ctrly2;
+ }
+
+ @Override // from interface ICubicCurve
+ public float getX2 () {
+ return x2;
+ }
+
+ @Override // from interface ICubicCurve
+ public float getY2 () {
+ return y2;
+ }
+}
diff --git a/src/main/java/pythagoras/f/CubicCurves.java b/src/main/java/pythagoras/f/CubicCurves.java
new file mode 100644
index 0000000..b073209
--- /dev/null
+++ b/src/main/java/pythagoras/f/CubicCurves.java
@@ -0,0 +1,100 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Cubic curve-related utility methods.
+ */
+public class CubicCurves
+{
+ public static float getFlatnessSq (float x1, float y1, float ctrlx1, float ctrly1,
+ float ctrlx2, float ctrly2, float x2, float y2) {
+ return Math.max(Lines.pointSegDistSq(ctrlx1, ctrly1, x1, y1, x2, y2),
+ Lines.pointSegDistSq(ctrlx2, ctrly2, x1, y1, x2, y2));
+ }
+
+ public static float getFlatnessSq (float coords[], int offset) {
+ return getFlatnessSq(coords[offset + 0], coords[offset + 1], coords[offset + 2],
+ coords[offset + 3], coords[offset + 4], coords[offset + 5],
+ coords[offset + 6], coords[offset + 7]);
+ }
+
+ public static float getFlatness (float x1, float y1, float ctrlx1, float ctrly1,
+ float ctrlx2, float ctrly2, float x2, float y2) {
+ return (float)Math.sqrt(getFlatnessSq(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2));
+ }
+
+ public static float getFlatness (float coords[], int offset) {
+ return getFlatness(coords[offset + 0], coords[offset + 1], coords[offset + 2],
+ coords[offset + 3], coords[offset + 4], coords[offset + 5],
+ coords[offset + 6], coords[offset + 7]);
+ }
+
+ public static void subdivide (ICubicCurve src, CubicCurve left, CubicCurve right) {
+ float x1 = src.getX1(), y1 = src.getY1();
+ float cx1 = src.getCtrlX1(), cy1 = src.getCtrlY1();
+ float cx2 = src.getCtrlX2(), cy2 = src.getCtrlY2();
+ float x2 = src.getX2(), y2 = src.getY2();
+ float cx = (cx1 + cx2) / 2f, cy = (cy1 + cy2) / 2f;
+ cx1 = (x1 + cx1) / 2f;
+ cy1 = (y1 + cy1) / 2f;
+ cx2 = (x2 + cx2) / 2f;
+ cy2 = (y2 + cy2) / 2f;
+ float ax = (cx1 + cx) / 2f, ay = (cy1 + cy) / 2f;
+ float bx = (cx2 + cx) / 2f, by = (cy2 + cy) / 2f;
+ cx = (ax + bx) / 2f;
+ cy = (ay + by) / 2f;
+ if (left != null) {
+ left.setCurve(x1, y1, cx1, cy1, ax, ay, cx, cy);
+ }
+ if (right != null) {
+ right.setCurve(cx, cy, bx, by, cx2, cy2, x2, y2);
+ }
+ }
+
+ public static void subdivide (float src[], int srcOff, float left[], int leftOff,
+ float right[], int rightOff) {
+ float x1 = src[srcOff + 0], y1 = src[srcOff + 1];
+ float cx1 = src[srcOff + 2], cy1 = src[srcOff + 3];
+ float cx2 = src[srcOff + 4], cy2 = src[srcOff + 5];
+ float x2 = src[srcOff + 6], y2 = src[srcOff + 7];
+ float cx = (cx1 + cx2) / 2f, cy = (cy1 + cy2) / 2f;
+ cx1 = (x1 + cx1) / 2f;
+ cy1 = (y1 + cy1) / 2f;
+ cx2 = (x2 + cx2) / 2f;
+ cy2 = (y2 + cy2) / 2f;
+ float ax = (cx1 + cx) / 2f, ay = (cy1 + cy) / 2f;
+ float bx = (cx2 + cx) / 2f, by = (cy2 + cy) / 2f;
+ cx = (ax + bx) / 2f;
+ cy = (ay + by) / 2f;
+ if (left != null) {
+ left[leftOff + 0] = x1;
+ left[leftOff + 1] = y1;
+ left[leftOff + 2] = cx1;
+ left[leftOff + 3] = cy1;
+ left[leftOff + 4] = ax;
+ left[leftOff + 5] = ay;
+ left[leftOff + 6] = cx;
+ left[leftOff + 7] = cy;
+ }
+ if (right != null) {
+ right[rightOff + 0] = cx;
+ right[rightOff + 1] = cy;
+ right[rightOff + 2] = bx;
+ right[rightOff + 3] = by;
+ right[rightOff + 4] = cx2;
+ right[rightOff + 5] = cy2;
+ right[rightOff + 6] = x2;
+ right[rightOff + 7] = y2;
+ }
+ }
+
+ public static int solveCubic (float[] eqn) {
+ return solveCubic(eqn, eqn);
+ }
+
+ public static int solveCubic (float[] eqn, float[] res) {
+ return Crossing.solveCubic(eqn, res);
+ }
+}
diff --git a/src/main/java/pythagoras/f/FlatteningPathIterator.java b/src/main/java/pythagoras/f/FlatteningPathIterator.java
index 5950c37..162d38a 100644
--- a/src/main/java/pythagoras/f/FlatteningPathIterator.java
+++ b/src/main/java/pythagoras/f/FlatteningPathIterator.java
@@ -128,17 +128,15 @@ public class FlatteningPathIterator implements PathIterator
}
/**
- * Calculates flat path points for current segment of the source shape.
- *
- * Line segment is flat by itself. Flatness of quad and cubic curves
- * evaluated by getFlatnessSq() method. Curves subdivided until current
- * flatness is bigger than user defined and subdivision limit isn't
- * exhausted. Single source segment translated to series of buffer points.
- * The less flatness the bigger serries. Every currentSegment() call extract
- * one point from the buffer. When series completed evaluate() takes next
+ * Calculates flat path points for the current segment of the source shape. Line segment is
+ * flat by itself. Flatness of quad and cubic curves are evaluated by the getFlatnessSq()
+ * method. Curves are subdivided until current flatness is bigger than user defined value and
+ * subdivision limit isn't exhausted. Single source segments are translated to a series of
+ * buffer points. The smaller the flatness the bigger the series. Every currentSegment() call
+ * extracts one point from the buffer. When a series is completed, evaluate() takes the next
* source shape segment.
*/
- void evaluate () {
+ protected void evaluate () {
if (bufEmpty) {
bufType = p.currentSegment(coords);
}
@@ -149,6 +147,7 @@ public class FlatteningPathIterator implements PathIterator
px = coords[0];
py = coords[1];
break;
+
case SEG_QUADTO:
if (bufEmpty) {
bufIndex -= 6;
@@ -159,7 +158,7 @@ public class FlatteningPathIterator implements PathIterator
}
while (bufSubdiv < bufLimit) {
- if (QuadCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) {
+ if (QuadCurves.getFlatnessSq(buf, bufIndex) < flatness2) {
break;
}
@@ -173,7 +172,7 @@ public class FlatteningPathIterator implements PathIterator
bufIndex += BUFFER_CAPACITY;
}
- QuadCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 4, buf, bufIndex);
+ QuadCurves.subdivide(buf, bufIndex, buf, bufIndex - 4, buf, bufIndex);
bufIndex -= 4;
bufSubdiv++;
@@ -189,6 +188,7 @@ public class FlatteningPathIterator implements PathIterator
bufType = SEG_LINETO;
}
break;
+
case SEG_CUBICTO:
if (bufEmpty) {
bufIndex -= 8;
@@ -199,7 +199,7 @@ public class FlatteningPathIterator implements PathIterator
}
while (bufSubdiv < bufLimit) {
- if (CubicCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) {
+ if (CubicCurves.getFlatnessSq(buf, bufIndex) < flatness2) {
break;
}
@@ -213,7 +213,7 @@ public class FlatteningPathIterator implements PathIterator
bufIndex += BUFFER_CAPACITY;
}
- CubicCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 6, buf, bufIndex);
+ CubicCurves.subdivide(buf, bufIndex, buf, bufIndex - 6, buf, bufIndex);
bufIndex -= 6;
bufSubdiv++;
@@ -230,7 +230,6 @@ public class FlatteningPathIterator implements PathIterator
}
break;
}
-
}
public void next () {
diff --git a/src/main/java/pythagoras/f/ICubicCurve.java b/src/main/java/pythagoras/f/ICubicCurve.java
new file mode 100644
index 0000000..cd2052a
--- /dev/null
+++ b/src/main/java/pythagoras/f/ICubicCurve.java
@@ -0,0 +1,60 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Provides read-only access to a {@link CubicCurve}.
+ */
+public interface ICubicCurve extends IShape, Cloneable
+{
+ /** Returns the x-coordinate of the start of this curve. */
+ float getX1 ();
+
+ /** Returns the y-coordinate of the start of this curve. */
+ float getY1 ();
+
+ /** Returns the x-coordinate of the first control point. */
+ float getCtrlX1 ();
+
+ /** Returns the y-coordinate of the first control point. */
+ float getCtrlY1 ();
+
+ /** Returns the x-coordinate of the second control point. */
+ float getCtrlX2 ();
+
+ /** Returns the y-coordinate of the second control point. */
+ float getCtrlY2 ();
+
+ /** Returns the x-coordinate of the end of this curve. */
+ float getX2 ();
+
+ /** Returns the y-coordinate of the end of this curve. */
+ float getY2 ();
+
+ /** Returns a copy of the starting point of this curve. */
+ Point getP1 ();
+
+ /** Returns a copy of the first control point of this curve. */
+ Point getCtrlP1 ();
+
+ /** Returns a copy of the second control point of this curve. */
+ Point getCtrlP2 ();
+
+ /** Returns a copy of the ending point of this curve. */
+ Point getP2 ();
+
+ /** Returns the square of the flatness (maximum distance of a control point from the line
+ * connecting the end points) of this curve. */
+ double getFlatnessSq ();
+
+ /** Returns the flatness (maximum distance of a control point from the line connecting the end
+ * points) of this curve. */
+ double getFlatness ();
+
+ /** Subdivides this curve and stores the results into {@code left} and {@code right}. */
+ void subdivide (CubicCurve left, CubicCurve right);
+
+ /** Returns a mutable copy of this curve. */
+ CubicCurve clone ();
+}
diff --git a/src/main/java/pythagoras/f/ILine.java b/src/main/java/pythagoras/f/ILine.java
index edd09ad..354e803 100644
--- a/src/main/java/pythagoras/f/ILine.java
+++ b/src/main/java/pythagoras/f/ILine.java
@@ -8,94 +8,58 @@ package pythagoras.f;
*/
public interface ILine extends IShape, Cloneable
{
- /**
- * Returns the x-coordinate of the start of this line.
- */
+ /** Returns the x-coordinate of the start of this line. */
float getX1 ();
- /**
- * Returns the y-coordinate of the start of this line.
- */
+ /** Returns the y-coordinate of the start of this line. */
float getY1 ();
- /**
- * Returns the x-coordinate of the end of this line.
- */
+ /** Returns the x-coordinate of the end of this line. */
float getX2 ();
- /**
- * Returns the y-coordinate of the end of this line.
- */
+ /** 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.
- */
+ /** Returns a copy of the starting point of this line. */
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 ();
+ /** Initializes the supplied point with this line's starting point.
+ * @return the supplied point. */
+ Point getP1 (Point target);
- /**
- * Returns a copy of the ending point of this line. Subsequent changes to the ending point will
- * not be visible in the returned point.
- */
+ /** Returns a copy of the ending point of this line. */
Point getP2 ();
- /**
- * Returns the square of the distance from the specified point to the line defined by this line
- * segment.
- */
+ /** Initializes the supplied point with this line's ending point.
+ * @return the supplied point. */
+ Point getP2 (Point target);
+
+ /** Returns the square of the distance from the specified point to the line defined by this
+ * line segment. */
float pointLineDistSq (float px, float py);
- /**
- * Returns the square of the distance from the supplied point to the line defined by this line
- * segment.
- */
+ /** Returns the square of the distance from the supplied point to the line defined by this line
+ * segment. */
float pointLineDistSq (IPoint p);
- /**
- * Returns the distance from the specified point to the line defined by this line segment.
- */
+ /** Returns the distance from the specified point to the line defined by this line segment. */
float pointLineDist (float px, float py);
- /**
- * Returns the distance from the supplied point to the line defined by this line segment.
- */
+ /** Returns the distance from the supplied point to the line defined by this line segment. */
float pointLineDist (IPoint p);
- /**
- * Returns the square of the distance from the specified point this line segment.
- */
+ /** Returns the square of the distance from the specified point this line segment. */
float pointSegDistSq (float px, float py);
- /**
- * Returns the square of the distance from the supplied point this line segment.
- */
+ /** Returns the square of the distance from the supplied point this line segment. */
float pointSegDistSq (IPoint p);
- /**
- * Returns the distance from the specified point this line segment.
- */
+ /** Returns the distance from the specified point this line segment. */
float pointSegDist (float px, float py);
- /**
- * Returns the distance from the supplied point this line segment.
- */
+ /** Returns the distance from the supplied point this line segment. */
float pointSegDist (IPoint p);
- /**
- * Returns a mutable copy of this line.
- */
+ /** 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 365b12a..c34bd86 100644
--- a/src/main/java/pythagoras/f/IPoint.java
+++ b/src/main/java/pythagoras/f/IPoint.java
@@ -9,38 +9,24 @@ package pythagoras.f;
*/
public interface IPoint extends Cloneable
{
- /**
- * Returns this point's x-coordinate.
- */
+ /** Returns this point's x-coordinate. */
float getX ();
- /**
- * Returns this point's y-coordinate.
- */
+ /** Returns this point's y-coordinate. */
float getY ();
- /**
- * Returns the squared Euclidian distance between this point and the specified point.
- */
+ /** Returns the squared Euclidian distance between this point and the specified point. */
float distanceSq (float px, float py);
- /**
- * Returns the squared Euclidian distance between this point and the supplied point.
- */
+ /** Returns the squared Euclidian distance between this point and the supplied point. */
float distanceSq (IPoint p);
- /**
- * Returns the Euclidian distance between this point and the specified point.
- */
+ /** Returns the Euclidian distance between this point and the specified point. */
float distance (float px, float py);
- /**
- * Returns the Euclidian distance between this point and the supplied point.
- */
+ /** Returns the Euclidian distance between this point and the supplied point. */
float distance (IPoint p);
- /**
- * Returns a mutable copy of this point.
- */
+ /** Returns a mutable copy of this point. */
Point clone ();
}
diff --git a/src/main/java/pythagoras/f/IQuadCurve.java b/src/main/java/pythagoras/f/IQuadCurve.java
new file mode 100644
index 0000000..fc5b8db
--- /dev/null
+++ b/src/main/java/pythagoras/f/IQuadCurve.java
@@ -0,0 +1,51 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Provides read-only access to a {@link QuadCurve}.
+ */
+public interface IQuadCurve extends IShape, Cloneable
+{
+ /** Returns the x-coordinate of the start of this curve. */
+ float getX1 ();
+
+ /** Returns the y-coordinate of the start of this curve. */
+ float getY1 ();
+
+ /** Returns the x-coordinate of the control point. */
+ float getCtrlX ();
+
+ /** Returns the y-coordinate of the control point. */
+ float getCtrlY ();
+
+ /** Returns the x-coordinate of the end of this curve. */
+ float getX2 ();
+
+ /** Returns the y-coordinate of the end of this curve. */
+ float getY2 ();
+
+ /** Returns a copy of the starting point of this curve. */
+ Point getP1 ();
+
+ /** Returns a copy of the control point of this curve. */
+ Point getCtrlP ();
+
+ /** Returns a copy of the ending point of this curve. */
+ Point getP2 ();
+
+ /** Returns the square of the flatness (maximum distance of a control point from the line
+ * connecting the end points) of this curve. */
+ float getFlatnessSq ();
+
+ /** Returns the flatness (maximum distance of a control point from the line connecting the end
+ * points) of this curve. */
+ float getFlatness ();
+
+ /** Subdivides this curve and stores the results into {@code left} and {@code right}. */
+ void subdivide (QuadCurve left, QuadCurve right);
+
+ /** Returns a mutable copy of this curve. */
+ QuadCurve clone ();
+}
diff --git a/src/main/java/pythagoras/f/IRectangle.java b/src/main/java/pythagoras/f/IRectangle.java
index 990a4ac..10a9234 100644
--- a/src/main/java/pythagoras/f/IRectangle.java
+++ b/src/main/java/pythagoras/f/IRectangle.java
@@ -9,86 +9,60 @@ package pythagoras.f;
*/
public interface IRectangle extends IRectangularShape, Cloneable
{
- /** The bitmask that indicates that a point lies to the left of this rectangle.
- * See {@link #outcode}. */
+ /** The bitmask that indicates that a point lies to the left of this rectangle. See
+ * {@link #outcode}. */
int OUT_LEFT = 1;
/** The bitmask that indicates that a point lies above this rectangle. See {@link #outcode}. */
int OUT_TOP = 2;
- /** The bitmask that indicates that a point lies to the right of this rectangle.
- * See {@link #outcode}. */
+ /** The bitmask that indicates that a point lies to the right of this rectangle. See
+ * {@link #outcode}. */
int OUT_RIGHT = 4;
/** The bitmask that indicates that a point lies below this rectangle. See {@link #outcode}. */
int OUT_BOTTOM = 8;
- /**
- * Returns a view of this rectangle's upper-left corner. Subsequent changes to this rectangle's
- * location will be reflected in the returned point.
- */
- IPoint location ();
-
- /**
- * 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.
- */
+ /** Returns a copy of this rectangle's upper-left corner. */
Point getLocation ();
- /**
- * Returns a view of this rectangle's size. Subsequent changes to this rectangle's size will be
- * reflected in the returned dimension.
- */
- IDimension size ();
+ /** Initializes the supplied point with this rectangle's upper-left corner.
+ * @return the supplied point. */
+ Point getLocation (Point target);
- /**
- * Returns a copy of this rectangle's size. Subsequent changes to this rectangle's size will
- * not be reflected in the returned dimension.
- */
+ /** Returns a copy of this rectangle's size. */
Dimension getSize ();
- /**
- * Returns the intersection of the specified rectangle and this rectangle (i.e. the largest
- * rectangle contained in both this and the specified rectangle).
- */
+ /** Initializes the supplied dimension with this rectangle's size.
+ * @return the supplied dimension. */
+ Dimension getSize (Dimension target);
+
+ /** Returns the intersection of the specified rectangle and this rectangle (i.e. the largest
+ * rectangle contained in both this and the specified rectangle). */
Rectangle intersection (float rx, float ry, float rw, float rh);
- /**
- * Returns the intersection of the supplied rectangle and this rectangle (i.e. the largest
- * rectangle contained in both this and the supplied rectangle).
- */
+ /** Returns the intersection of the supplied rectangle and this rectangle (i.e. the largest
+ * rectangle contained in both this and the supplied rectangle). */
Rectangle intersection (IRectangle r);
- /**
- * Returns the union of the supplied rectangle and this rectangle (i.e. the smallest rectangle
- * that contains both this and the supplied rectangle).
- */
+ /** Returns the union of the supplied rectangle and this rectangle (i.e. the smallest rectangle
+ * that contains both this and the supplied rectangle). */
Rectangle union (IRectangle r);
- /**
- * Returns true if the specified line segment intersects this rectangle.
- */
+ /** 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.
- */
+ /** 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.
- */
+ /** 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 (float px, float py);
- /**
- * Returns a set of flags indicating where the supplied point lies in relation to the bounds of
- * this rectangle. See {@link #OUT_LEFT}, etc.
- */
+ /** Returns a set of flags indicating where the supplied point lies in relation to the bounds of
+ * this rectangle. See {@link #OUT_LEFT}, etc. */
int outcode (IPoint point);
- /**
- * Returns a mutable copy of 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
index 7844878..7969295 100644
--- a/src/main/java/pythagoras/f/IRectangularShape.java
+++ b/src/main/java/pythagoras/f/IRectangularShape.java
@@ -11,65 +11,40 @@ package pythagoras.f;
*/
public interface IRectangularShape extends IShape
{
- /**
- * Returns the x-coordinate of the upper-left corner of the framing rectangle.
- */
+ /** 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.
- */
+ /** Returns the y-coordinate of the upper-left corner of the framing rectangle. */
float getY ();
- /**
- * Returns the width of the framing rectangle.
- */
+ /** Returns the width of the framing rectangle. */
float getWidth ();
- /**
- * Returns the height of the framing rectangle.
- */
+ /** Returns the height of the framing rectangle. */
float getHeight ();
- /**
- * Returns the minimum x-coordinate of the framing rectangle.
- */
+ /** Returns the minimum x-coordinate of the framing rectangle. */
float getMinX ();
- /**
- * Returns the minimum y-coordinate of the framing rectangle.
- */
+ /** Returns the minimum y-coordinate of the framing rectangle. */
float getMinY ();
- /**
- * Returns the maximum x-coordinate of the framing rectangle.
- */
+ /** Returns the maximum x-coordinate of the framing rectangle. */
float getMaxX ();
- /**
- * Returns the maximum y-coordinate of the framing rectangle.
- */
+ /** Returns the maximum y-coordinate of the framing rectangle. */
float getMaxY ();
- /**
- * Returns the x-coordinate of the center of the framing rectangle.
- */
+ /** Returns the x-coordinate of the center of the framing rectangle. */
float getCenterX ();
- /**
- * Returns the y-coordinate of the center of the framing rectangle.
- */
+ /** 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.
- */
+ /** Returns a copy of this shape's framing rectangle. */
Rectangle getFrame ();
+
+ /** Initializes the supplied rectangle with this shape's framing rectangle.
+ * @return the supplied rectangle. */
+ Rectangle getFrame (Rectangle target);
}
diff --git a/src/main/java/pythagoras/f/IShape.java b/src/main/java/pythagoras/f/IShape.java
index 7511606..639881b 100644
--- a/src/main/java/pythagoras/f/IShape.java
+++ b/src/main/java/pythagoras/f/IShape.java
@@ -9,67 +9,48 @@ package pythagoras.f;
*/
public interface IShape
{
- /**
- * Returns true if this shape encloses no area.
- */
+ /** 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. */
boolean contains (float x, float y);
- /**
- * Returns true if this shape contains the supplied point.
- */
+ /** Returns true if this shape contains the supplied point. */
boolean contains (IPoint point);
- /**
- * Returns true if this shape completely contains the specified rectangle.
- */
+ /** Returns true if this shape completely contains the specified rectangle. */
boolean contains (float x, float y, float w, float h);
- /**
- * Returns true if this shape completely contains the supplied rectangle.
- */
+ /** Returns true if this shape completely contains the supplied rectangle. */
boolean contains (IRectangle r);
- /**
- * Returns true if this shape intersects the specified rectangle.
- */
+ /** Returns true if this shape intersects the specified rectangle. */
boolean intersects (float x, float y, float w, float h);
- /**
- * Returns true if this shape intersects the supplied rectangle.
- */
+ /** Returns true if this shape intersects the supplied rectangle. */
boolean intersects (IRectangle r);
- /**
- * Returns a view of the bounding rectangle for this shape. Subsequent changes to the bounding
- * rectangle will be reflected in the returned rectangle.
- */
- 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.
- */
+ /** Returns a copy of the bounding rectangle for this shape. */
Rectangle getBounds ();
+ /** Initializes the supplied rectangle with this shape's bounding rectangle.
+ * @return the supplied rectangle. */
+ Rectangle getBounds (Rectangle target);
+
/**
* Returns an iterator over the path described by this shape.
- *
+ *
* @param at if supplied, the points in the path are transformed using this.
*/
PathIterator getPathIterator (AffineTransform at);
/**
* Returns an iterator over the path described by this shape.
- *
+ *
* @param at if supplied, the points in the path are transformed using this.
* @param flatness when approximating curved segments with lines, this controls the maximum
- * distance the lines are allowed to deviate from the approximated curve, thus a higher
- * flatness value generally allows for a path with fewer segments.
+ * distance the lines are allowed to deviate from the approximated curve, thus a higher
+ * flatness value generally allows for a path with fewer segments.
*/
PathIterator getPathIterator (AffineTransform at, float flatness);
}
diff --git a/src/main/java/pythagoras/f/Path.java b/src/main/java/pythagoras/f/Path.java
index 1751fcd..3d3c7bf 100644
--- a/src/main/java/pythagoras/f/Path.java
+++ b/src/main/java/pythagoras/f/Path.java
@@ -167,18 +167,13 @@ public final class Path implements IShape, Cloneable
return p;
}
- /**
- * {@inheritDoc}
- *
- * Note: this method violates the contract in that the bounds returned will
- * not reflect subsequent changes to this path. Doing so is prohibitively expensive.
- */
- @Override public IRectangle bounds () {
- return getBounds();
+ @Override // from interface IShape
+ public Rectangle getBounds () {
+ return getBounds(new Rectangle());
}
@Override // from interface IShape
- public Rectangle getBounds () {
+ public Rectangle getBounds (Rectangle target) {
float rx1, ry1, rx2, ry2;
if (pointSize == 0) {
rx1 = ry1 = rx2 = ry2 = 0f;
@@ -201,7 +196,8 @@ public final class Path implements IShape, Cloneable
}
}
}
- return new Rectangle(rx1, ry1, rx2 - rx1, ry2 - ry1);
+ target.setBounds(rx1, ry1, rx2 - rx1, ry2 - ry1);
+ return target;
}
@Override // from interface IShape
diff --git a/src/main/java/pythagoras/f/Point.java b/src/main/java/pythagoras/f/Point.java
index bfe0ddd..25a9092 100644
--- a/src/main/java/pythagoras/f/Point.java
+++ b/src/main/java/pythagoras/f/Point.java
@@ -21,7 +21,6 @@ public class Point extends AbstractPoint implements Serializable
* Constructs a point at (0, 0).
*/
public Point () {
- setLocation(0, 0);
}
/**
diff --git a/src/main/java/pythagoras/f/QuadCurve.java b/src/main/java/pythagoras/f/QuadCurve.java
new file mode 100644
index 0000000..f13f990
--- /dev/null
+++ b/src/main/java/pythagoras/f/QuadCurve.java
@@ -0,0 +1,121 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+import java.io.Serializable;
+
+/**
+ * Represents a quadratic curve.
+ */
+public class QuadCurve extends AbstractQuadCurve implements Serializable
+{
+ /** The x-coordinate of the start of this curve. */
+ public float x1;
+
+ /** The y-coordinate of the start of this curve. */
+ public float y1;
+
+ /** The x-coordinate of the control point. */
+ public float ctrlx;
+
+ /** The y-coordinate of the control point. */
+ public float ctrly;
+
+ /** The x-coordinate of the end of this curve. */
+ public float x2;
+
+ /** The y-coordinate of the end of this curve. */
+ public float y2;
+
+ /**
+ * Creates a quad curve with all points at (0,0).
+ */
+ public QuadCurve () {
+ }
+
+ /**
+ * Creates a quad curve with the specified start, control, and end points.
+ */
+ public QuadCurve (float x1, float y1, float ctrlx, float ctrly, float x2, float y2) {
+ setCurve(x1, y1, ctrlx, ctrly, x2, y2);
+ }
+
+ /**
+ * Configures the start, control, and end points for this curve.
+ */
+ public void setCurve (float x1, float y1, float ctrlx, float ctrly, float x2, float y2) {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.ctrlx = ctrlx;
+ this.ctrly = ctrly;
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+ /**
+ * Configures the start, control, and end points for this curve.
+ */
+ public void setCurve (IPoint p1, IPoint cp, IPoint p2) {
+ setCurve(p1.getX(), p1.getY(), cp.getX(), cp.getY(), p2.getX(), p2.getY());
+ }
+
+ /**
+ * Configures the start, control, and end points for this curve, using the values at the
+ * specified offset in the {@link coords} array.
+ */
+ public void setCurve (float[] coords, int offset) {
+ setCurve(coords[offset + 0], coords[offset + 1],
+ coords[offset + 2], coords[offset + 3],
+ coords[offset + 4], coords[offset + 5]);
+ }
+
+ /**
+ * Configures the start, control, and end points for this curve, using the values at the
+ * specified offset in the {@link points} array.
+ */
+ public void setCurve (IPoint[] points, int offset) {
+ setCurve(points[offset + 0].getX(), points[offset + 0].getY(),
+ points[offset + 1].getX(), points[offset + 1].getY(),
+ points[offset + 2].getX(), points[offset + 2].getY());
+ }
+
+ /**
+ * Configures the start, control, and end points for this curve to be the same as the supplied
+ * curve.
+ */
+ public void setCurve (IQuadCurve curve) {
+ setCurve(curve.getX1(), curve.getY1(), curve.getCtrlX(), curve.getCtrlY(),
+ curve.getX2(), curve.getY2());
+ }
+
+ @Override // from interface IQuadCurve
+ public float getX1 () {
+ return x1;
+ }
+
+ @Override // from interface IQuadCurve
+ public float getY1 () {
+ return y1;
+ }
+
+ @Override // from interface IQuadCurve
+ public float getCtrlX () {
+ return ctrlx;
+ }
+
+ @Override // from interface IQuadCurve
+ public float getCtrlY () {
+ return ctrly;
+ }
+
+ @Override // from interface IQuadCurve
+ public float getX2 () {
+ return x2;
+ }
+
+ @Override // from interface IQuadCurve
+ public float getY2 () {
+ return y2;
+ }
+}
diff --git a/src/main/java/pythagoras/f/QuadCurves.java b/src/main/java/pythagoras/f/QuadCurves.java
new file mode 100644
index 0000000..4e7cdef
--- /dev/null
+++ b/src/main/java/pythagoras/f/QuadCurves.java
@@ -0,0 +1,93 @@
+//
+// $Id$
+
+package pythagoras.f;
+
+/**
+ * Quad curve-related utility methods.
+ */
+public class QuadCurves
+{
+ public static float getFlatnessSq (float x1, float y1, float ctrlx, float ctrly,
+ float x2, float y2) {
+ return Lines.pointSegDistSq(ctrlx, ctrly, x1, y1, x2, y2);
+ }
+
+ public static float getFlatnessSq (float coords[], int offset) {
+ return Lines.pointSegDistSq(coords[offset + 2], coords[offset + 3],
+ coords[offset + 0], coords[offset + 1],
+ coords[offset + 4], coords[offset + 5]);
+ }
+
+ public static float getFlatness (float x1, float y1, float ctrlx, float ctrly,
+ float x2, float y2) {
+ return Lines.pointSegDist(ctrlx, ctrly, x1, y1, x2, y2);
+ }
+
+ public static float getFlatness (float[] coords, int offset) {
+ return Lines.pointSegDist(coords[offset + 2], coords[offset + 3],
+ coords[offset + 0], coords[offset + 1],
+ coords[offset + 4], coords[offset + 5]);
+ }
+
+ public static void subdivide (IQuadCurve src, QuadCurve left, QuadCurve right) {
+ float x1 = src.getX1();
+ float y1 = src.getY1();
+ float cx = src.getCtrlX();
+ float cy = src.getCtrlY();
+ float x2 = src.getX2();
+ float y2 = src.getY2();
+ float cx1 = (x1 + cx) / 2f;
+ float cy1 = (y1 + cy) / 2f;
+ float cx2 = (x2 + cx) / 2f;
+ float cy2 = (y2 + cy) / 2f;
+ cx = (cx1 + cx2) / 2f;
+ cy = (cy1 + cy2) / 2f;
+ if (left != null) {
+ left.setCurve(x1, y1, cx1, cy1, cx, cy);
+ }
+ if (right != null) {
+ right.setCurve(cx, cy, cx2, cy2, x2, y2);
+ }
+ }
+
+ public static void subdivide (float[] src, int srcoff,
+ float left[], int leftOff, float[] right, int rightOff) {
+ float x1 = src[srcoff + 0];
+ float y1 = src[srcoff + 1];
+ float cx = src[srcoff + 2];
+ float cy = src[srcoff + 3];
+ float x2 = src[srcoff + 4];
+ float y2 = src[srcoff + 5];
+ float cx1 = (x1 + cx) / 2f;
+ float cy1 = (y1 + cy) / 2f;
+ float cx2 = (x2 + cx) / 2f;
+ float cy2 = (y2 + cy) / 2f;
+ cx = (cx1 + cx2) / 2f;
+ cy = (cy1 + cy2) / 2f;
+ if (left != null) {
+ left[leftOff + 0] = x1;
+ left[leftOff + 1] = y1;
+ left[leftOff + 2] = cx1;
+ left[leftOff + 3] = cy1;
+ left[leftOff + 4] = cx;
+ left[leftOff + 5] = cy;
+ }
+ if (right != null) {
+ right[rightOff + 0] = cx;
+ right[rightOff + 1] = cy;
+ right[rightOff + 2] = cx2;
+ right[rightOff + 3] = cy2;
+ right[rightOff + 4] = x2;
+ right[rightOff + 5] = y2;
+ }
+ }
+
+ public static int solveQuadratic (float[] eqn) {
+ return solveQuadratic(eqn, eqn);
+ }
+
+ public static int solveQuadratic (float[] eqn, float[] res) {
+ return Crossing.solveQuad(eqn, res);
+ }
+}
diff --git a/src/main/java/pythagoras/f/Rectangle.java b/src/main/java/pythagoras/f/Rectangle.java
index a55c15f..79bc69a 100644
--- a/src/main/java/pythagoras/f/Rectangle.java
+++ b/src/main/java/pythagoras/f/Rectangle.java
@@ -27,7 +27,6 @@ public class Rectangle extends AbstractRectangle implements Serializable
* Constructs a rectangle at (0,0) and with dimensions (0,0).
*/
public Rectangle () {
- setBounds(0, 0, 0, 0);
}
/**
diff --git a/src/main/java/pythagoras/f/RectangularShape.java b/src/main/java/pythagoras/f/RectangularShape.java
index 0083829..c6fc67a 100644
--- a/src/main/java/pythagoras/f/RectangularShape.java
+++ b/src/main/java/pythagoras/f/RectangularShape.java
@@ -111,13 +111,13 @@ public abstract class RectangularShape implements IRectangularShape
}
@Override // from IRectangularShape
- public IRectangle frame () {
- return bounds();
+ public Rectangle getFrame () {
+ return getBounds();
}
@Override // from IRectangularShape
- public Rectangle getFrame () {
- return getBounds();
+ public Rectangle getFrame (Rectangle target) {
+ return getBounds(target);
}
@Override // from interface IShape
@@ -141,32 +141,17 @@ public abstract class RectangularShape implements IRectangularShape
}
@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();
- }
- };
+ public Rectangle getBounds () {
+ return getBounds(new Rectangle());
}
@Override // from interface IShape
- public Rectangle getBounds () {
- return new Rectangle(getX(), getY(), getWidth(), getHeight());
+ public Rectangle getBounds (Rectangle target) {
+ target.setBounds(getX(), getY(), getWidth(), getHeight());
+ return target;
}
+ // @Override // from interface IShape
// public PathIterator getPathIterator (AffineTransform t, float flatness) {
// return new FlatteningPathIterator(getPathIterator(t), flatness);
// }