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); // }