Another copy of the classes, specialized to work on double. Double the

maintenance! (But not worth the PITA of programmatically generating the code,
IMO.)
This commit is contained in:
Michael Bayne
2011-06-10 15:56:13 -07:00
parent 33766947bf
commit 520167978e
48 changed files with 8175 additions and 0 deletions
+357
View File
@@ -0,0 +1,357 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.NoSuchElementException;
/**
* Provides most of the implementation of {@link IArc}, obtaining only the frame and other metrics
* from the derived class.
*/
public abstract class AbstractArc extends RectangularShape implements IArc
{
@Override // from interface IArc
public Point getStartPoint () {
return getStartPoint(new Point());
}
@Override // from interface IArc
public Point getStartPoint (Point target) {
double a = Math.toRadians(getAngleStart());
target.setLocation(getX() + (1f + Math.cos(a)) * getWidth() / 2f,
getY() + (1f - Math.sin(a)) * getHeight() / 2f);
return target;
}
@Override // from interface IArc
public Point getEndPoint () {
return getEndPoint(new Point());
}
@Override // from interface IArc
public Point getEndPoint (Point target) {
double a = Math.toRadians(getAngleStart() + getAngleExtent());
target.setLocation(getX() + (1f + Math.cos(a)) * getWidth() / 2f,
getY() + (1f - Math.sin(a)) * getHeight() / 2f);
return target;
}
@Override // from interface IArc
public boolean containsAngle (double angle) {
double extent = getAngleExtent();
if (extent >= 360f) {
return true;
}
angle = getNormAngle(angle);
double a1 = getNormAngle(getAngleStart());
double a2 = a1 + extent;
if (a2 > 360f) {
return angle >= a1 || angle <= a2 - 360f;
}
if (a2 < 0f) {
return angle >= a2 + 360f || angle <= a1;
}
return (extent > 0f) ? a1 <= angle && angle <= a2 : a2 <= angle && angle <= a1;
}
@Override // from interface IArc
public Arc clone () {
return new Arc(getX(), getY(), getWidth(), getHeight(), getAngleStart(), getAngleExtent(),
getArcType());
}
@Override // from RectangularShape
public boolean isEmpty () {
return getArcType() == OPEN || super.isEmpty();
}
@Override // from RectangularShape
public boolean contains (double px, double py) {
// normalize point
double nx = (px - getX()) / getWidth() - 0.5f;
double ny = (py - getY()) / getHeight() - 0.5f;
if ((nx * nx + ny * ny) > 0.25) {
return false;
}
double extent = getAngleExtent();
double absExtent = Math.abs(extent);
if (absExtent >= 360f) {
return true;
}
boolean containsAngle = containsAngle(Math.toDegrees(-Math.atan2(ny, nx)));
if (getArcType() == PIE) {
return containsAngle;
}
if (absExtent <= 180f && !containsAngle) {
return false;
}
Line l = new Line(getStartPoint(), getEndPoint());
int ccw1 = l.relativeCCW(px, py);
int ccw2 = l.relativeCCW(getCenterX(), getCenterY());
return ccw1 == 0 || ccw2 == 0 || ((ccw1 + ccw2) == 0 ^ absExtent > 180f);
}
@Override // from RectangularShape
public boolean contains (double rx, double ry, double rw, double rh) {
if (!(contains(rx, ry) && contains(rx + rw, ry) &&
contains(rx + rw, ry + rh) && contains(rx, ry + rh))) {
return false;
}
double absExtent = Math.abs(getAngleExtent());
if (getArcType() != PIE || absExtent <= 180f || absExtent >= 360f) {
return true;
}
Rectangle r = new Rectangle(rx, ry, rw, rh);
double cx = getCenterX(), cy = getCenterY();
if (r.contains(cx, cy)) {
return false;
}
Point p1 = getStartPoint(), p2 = getEndPoint();
return !r.intersectsLine(cx, cy, p1.getX(), p1.getY()) &&
!r.intersectsLine(cx, cy, p2.getX(), p2.getY());
}
@Override // from RectangularShape
public boolean intersects (double rx, double ry, double rw, double rh) {
if (isEmpty() || rw <= 0f || rh <= 0f) {
return false;
}
// check: does arc contain rectangle's points
if (contains(rx, ry) || contains(rx + rw, ry) ||
contains(rx, ry + rh) || contains(rx + rw, ry + rh)) {
return true;
}
double cx = getCenterX(), cy = getCenterY();
Point p1 = getStartPoint(), p2 = getEndPoint();
// check: does rectangle contain arc's points
Rectangle r = new Rectangle(rx, ry, rw, rh);
if (r.contains(p1) || r.contains(p2) || (getArcType() == PIE && r.contains(cx, cy))) {
return true;
}
if (getArcType() == PIE) {
if (r.intersectsLine(p1.getX(), p1.getY(), cx, cy) ||
r.intersectsLine(p2.getX(), p2.getY(), cx, cy)) {
return true;
}
} else {
if (r.intersectsLine(p1.getX(), p1.getY(), p2.getX(), p2.getY())) {
return true;
}
}
// nearest rectangle point
double nx = cx < rx ? rx : (cx > rx + rw ? rx + rw : cx);
double ny = cy < ry ? ry : (cy > ry + rh ? ry + rh : cy);
return contains(nx, ny);
}
@Override // from RectangularShape
public Rectangle getBounds (Rectangle target) {
if (isEmpty()) {
target.setBounds(getX(), getY(), getWidth(), getHeight());
return target;
}
double rx1 = getX();
double ry1 = getY();
double rx2 = rx1 + getWidth();
double ry2 = ry1 + getHeight();
Point p1 = getStartPoint(), p2 = getEndPoint();
double bx1 = containsAngle(180f) ? rx1 : Math.min(p1.getX(), p2.getX());
double by1 = containsAngle(90f) ? ry1 : Math.min(p1.getY(), p2.getY());
double bx2 = containsAngle(0f) ? rx2 : Math.max(p1.getX(), p2.getX());
double by2 = containsAngle(270f) ? ry2 : Math.max(p1.getY(), p2.getY());
if (getArcType() == PIE) {
double cx = getCenterX();
double cy = getCenterY();
bx1 = Math.min(bx1, cx);
by1 = Math.min(by1, cy);
bx2 = Math.max(bx2, cx);
by2 = Math.max(by2, cy);
}
target.setBounds(bx1, by1, bx2 - bx1, by2 - by1);
return target;
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform at) {
return new Iterator(this, at);
}
/** Returns a normalized angle (bound between 0 and 360 degrees). */
protected double getNormAngle (double angle) {
return angle - Math.floor(angle / 360f) * 360f;
}
/** An iterator over an {@link IArc}. */
protected static class Iterator implements PathIterator
{
/** The x coordinate of left-upper corner of the arc rectangle bounds */
private double x;
/** The y coordinate of left-upper corner of the arc rectangle bounds */
private double y;
/** The width of the arc rectangle bounds */
private double width;
/** The height of the arc rectangle bounds */
private double height;
/** The start angle of the arc in degrees */
private double angle;
/** The angle extent in degrees */
private double extent;
/** The closure type of the arc */
private int type;
/** The path iterator transformation */
private AffineTransform t;
/** The current segmenet index */
private int index;
/** The number of arc segments the source arc subdivided to be approximated by Bezier
* curves. Depends on extent value. */
private int arcCount;
/** The number of line segments. Depends on closure type. */
private int lineCount;
/** The step to calculate next arc subdivision point */
private double step;
/** The tempopary value of cosinus of the current angle */
private double cos;
/** The tempopary value of sinus of the current angle */
private double sin;
/** The coefficient to calculate control points of Bezier curves */
private double k;
/** The tempopary value of x coordinate of the Bezier curve control vector */
private double kx;
/** The tempopary value of y coordinate of the Bezier curve control vector */
private double ky;
/** The x coordinate of the first path point (MOVE_TO) */
private double mx;
/** The y coordinate of the first path point (MOVE_TO) */
private double my;
Iterator (IArc a, AffineTransform t) {
this.width = a.getWidth() / 2f;
this.height = a.getHeight() / 2f;
this.x = a.getX() + width;
this.y = a.getY() + height;
this.angle = -Math.toRadians(a.getAngleStart());
this.extent = -a.getAngleExtent();
this.type = a.getArcType();
this.t = t;
if (width < 0 || height < 0) {
arcCount = 0;
lineCount = 0;
index = 1;
return;
}
if (Math.abs(extent) >= 360f) {
arcCount = 4;
k = 4f / 3f * (Math.sqrt(2f) - 1f);
step = Math.PI / 2f;
if (extent < 0f) {
step = -step;
k = -k;
}
} else {
arcCount = (int)Math.rint(Math.abs(extent) / 90f);
step = Math.toRadians(extent / arcCount);
k = 4f / 3f * (1f - Math.cos(step / 2f)) / Math.sin(step / 2f);
}
lineCount = 0;
if (type == CHORD) {
lineCount++;
} else if (type == PIE) {
lineCount += 2;
}
}
@Override public int getWindingRule () {
return WIND_NON_ZERO;
}
@Override public boolean isDone () {
return index > arcCount + lineCount;
}
@Override public void next () {
index++;
}
@Override public int currentSegment (double[] coords) {
if (isDone()) {
throw new NoSuchElementException("Iterator out of bounds");
}
int type;
int count;
if (index == 0) {
type = SEG_MOVETO;
count = 1;
cos = Math.cos(angle);
sin = Math.sin(angle);
kx = k * width * sin;
ky = k * height * cos;
coords[0] = mx = x + cos * width;
coords[1] = my = y + sin * height;
} else if (index <= arcCount) {
type = SEG_CUBICTO;
count = 3;
coords[0] = mx - kx;
coords[1] = my + ky;
angle += step;
cos = Math.cos(angle);
sin = Math.sin(angle);
kx = k * width * sin;
ky = k * height * cos;
coords[4] = mx = x + cos * width;
coords[5] = my = y + sin * height;
coords[2] = mx + kx;
coords[3] = my - ky;
} else if (index == arcCount + lineCount) {
type = SEG_CLOSE;
count = 0;
} else {
type = SEG_LINETO;
count = 1;
coords[0] = x;
coords[1] = y;
}
if (t != null) {
t.transform(coords, 0, coords, 0, count);
}
return type;
}
}
}
@@ -0,0 +1,174 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
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 (double px, double py) {
return Crossing.isInsideEvenOdd(Crossing.crossShape(this, px, py));
}
@Override // from interface IShape
public boolean contains (double rx, double ry, double rw, double 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 (double rx, double ry, double rw, double 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) {
double x1 = getX1(), y1 = getY1(), x2 = getX2(), y2 = getY2();
double ctrlx1 = getCtrlX1(), ctrly1 = getCtrlY1();
double ctrlx2 = getCtrlX2(), ctrly2 = getCtrlY2();
double rx1 = Math.min(Math.min(x1, x2), Math.min(ctrlx1, ctrlx2));
double ry1 = Math.min(Math.min(y1, y2), Math.min(ctrly1, ctrly2));
double rx2 = Math.max(Math.max(x1, x2), Math.max(ctrlx1, ctrlx2));
double 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, double 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 (double[] 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;
}
}
}
@@ -0,0 +1,41 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides most of the implementation of {@link IDimension}, obtaining only width and height from
* the derived class.
*/
public abstract class AbstractDimension implements IDimension
{
@Override // from interface IDimension
public Dimension clone () {
return new Dimension(this);
}
@Override
public int hashCode () {
long bits = Double.doubleToLongBits(getWidth());
bits += Double.doubleToLongBits(getHeight()) * 37;
return (((int) bits) ^ ((int) (bits >> 32)));
}
@Override
public boolean equals (Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof AbstractDimension) {
AbstractDimension d = (AbstractDimension)obj;
return (d.getWidth() == getWidth() && d.getHeight() == getHeight());
}
return false;
}
@Override
public String toString () {
return Dimensions.dimenToString(getWidth(), getHeight());
}
}
@@ -0,0 +1,128 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.NoSuchElementException;
/**
* Provides most of the implementation of {@link IEllipse}, obtaining the framing rectangle from
* the derived class.
*/
public abstract class AbstractEllipse extends RectangularShape implements IEllipse
{
@Override // from IEllipse
public Ellipse clone () {
return new Ellipse(getX(), getY(), getWidth(), getHeight());
}
@Override // from interface IShape
public boolean contains (double px, double py) {
if (isEmpty()) return false;
double a = (px - getX()) / getWidth() - 0.5f;
double b = (py - getY()) / getHeight() - 0.5f;
return a * a + b * b < 0.25f;
}
@Override // from interface IShape
public boolean contains (double rx, double ry, double rw, double rh) {
if (isEmpty() || rw <= 0f || rh <= 0f) return false;
double rx1 = rx, ry1 = ry, rx2 = rx + rw, ry2 = ry + rh;
return contains(rx1, ry1) && contains(rx2, ry1) && contains(rx2, ry2) && contains(rx1, ry2);
}
@Override // from interface IShape
public boolean intersects (double rx, double ry, double rw, double rh) {
if (isEmpty() || rw <= 0f || rh <= 0f) return false;
double cx = getX() + getWidth() / 2f;
double cy = getY() + getHeight() / 2f;
double rx1 = rx, ry1 = ry, rx2 = rx + rw, ry2 = ry + rh;
double nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx);
double ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy);
return contains(nx, ny);
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform at) {
return new Iterator(this, at);
}
/** An iterator over an {@link IEllipse}. */
protected static class Iterator implements PathIterator
{
private final double x, y, width, height;
private final AffineTransform t;
private int index;
Iterator (IEllipse e, AffineTransform t) {
this.x = e.getX();
this.y = e.getY();
this.width = e.getWidth();
this.height = e.getHeight();
this.t = t;
if (width < 0f || height < 0f) {
index = 6;
}
}
@Override public int getWindingRule () {
return WIND_NON_ZERO;
}
@Override public boolean isDone () {
return index > 5;
}
@Override public void next () {
index++;
}
@Override public int currentSegment (double[] coords) {
if (isDone()) {
throw new NoSuchElementException("Iterator out of bounds");
}
if (index == 5) {
return SEG_CLOSE;
}
int type;
int count;
if (index == 0) {
type = SEG_MOVETO;
count = 1;
double[] p = POINTS[3];
coords[0] = x + p[4] * width;
coords[1] = y + p[5] * height;
} else {
type = SEG_CUBICTO;
count = 3;
double[] p = POINTS[index - 1];
int j = 0;
for (int i = 0; i < 3; i++) {
coords[j] = x + p[j++] * width;
coords[j] = y + p[j++] * height;
}
}
if (t != null) {
t.transform(coords, 0, coords, 0, count);
}
return type;
}
}
// An ellipse is subdivided into four quarters by x and y axis. Each part is approximated by a
// cubic Bezier curve. The arc in the first quarter starts in (a, 0) and finishes in (0, b)
// points. Control points for the cubic curve are (a, 0), (a, m), (n, b) and (0, b) where n and
// m are calculated based on the requirement that the Bezier curve in point 0.5 should lay on
// the arc.
/** The coefficient to calculate control points of Bezier curves. */
private static final double U = 2f / 3f * (Math.sqrt(2) - 1f);
/** The points coordinates calculation table. */
private static final double[][] POINTS = {
{ 1f, 0.5f + U, 0.5f + U, 1f, 0.5f, 1f },
{ 0.5f - U, 1f, 0f, 0.5f + U, 0f, 0.5f },
{ 0f, 0.5f - U, 0.5f - U, 0f, 0.5f, 0f },
{ 0.5f + U, 0f, 1f, 0.5f - U, 1f, 0.5f } };
}
@@ -0,0 +1,211 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.NoSuchElementException;
/**
* Provides most of the implementation of {@link ILine}, obtaining only the start and end points
* from the derived class.
*/
public abstract class AbstractLine implements ILine
{
@Override // from interface ILine
public Point getP1 () {
return getP1(new Point());
}
@Override // from interface ILine
public Point getP1 (Point target) {
target.setLocation(getX1(), getY1());
return target;
}
@Override // from interface ILine
public Point getP2 () {
return getP2(new Point());
}
@Override // from interface ILine
public Point getP2 (Point target) {
target.setLocation(getX2(), getY2());
return target;
}
@Override // from interface ILine
public double pointLineDistSq (double px, double py) {
return Lines.pointLineDistSq(px, py, getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public double pointLineDistSq (IPoint p) {
return Lines.pointLineDistSq(p.getX(), p.getY(), getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public double pointLineDist (double px, double py) {
return Lines.pointLineDist(px, py, getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public double pointLineDist (IPoint p) {
return Lines.pointLineDist(p.getX(), p.getY(), getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public double pointSegDistSq (double px, double py) {
return Lines.pointSegDistSq(px, py, getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public double pointSegDistSq (IPoint p) {
return Lines.pointSegDistSq(p.getX(), p.getY(), getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public double pointSegDist (double px, double py) {
return Lines.pointSegDist(px, py, getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public double pointSegDist (IPoint p) {
return Lines.pointSegDist(p.getX(), p.getY(), getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public int relativeCCW (double px, double py) {
return Lines.relativeCCW(px, py, getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public int relativeCCW (IPoint p) {
return Lines.relativeCCW(p.getX(), p.getY(), getX1(), getY1(), getX2(), getY2());
}
@Override // from interface ILine
public Line clone () {
return new Line(getX1(), getY1(), getX2(), getY2());
}
@Override // from interface IShape
public boolean isEmpty () {
return false;
}
@Override // from interface IShape
public boolean contains (double x, double y) {
return false;
}
@Override // from interface IShape
public boolean contains (IPoint point) {
return false;
}
@Override // from interface IShape
public boolean contains (double x, double y, double w, double h) {
return false;
}
@Override // from interface IShape
public boolean contains (IRectangle r) {
return false;
}
@Override // from interface IShape
public boolean intersects (double rx, double ry, double rw, double rh) {
return Lines.lineIntersectsRect(getX1(), getY1(), getX2(), getY2(), rx, ry, rw, rh);
}
@Override // from interface IShape
public boolean intersects (IRectangle r) {
return r.intersectsLine(this);
}
@Override // from interface IShape
public Rectangle getBounds () {
return getBounds(new Rectangle());
}
@Override // from interface IShape
public Rectangle getBounds (Rectangle target) {
double x1 = getX1(), x2 = getX2(), y1 = getY1(), y2 = getY2();
double rx, ry, rw, rh;
if (x1 < x2) {
rx = x1;
rw = x2 - x1;
} else {
rx = x2;
rw = x1 - x2;
}
if (y1 < y2) {
ry = y1;
rh = y2 - y1;
} else {
ry = y2;
rh = y1 - y2;
}
target.setBounds(rx, ry, rw, rh);
return target;
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform at) {
return new Iterator(this, at);
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform at, double flatness) {
return new Iterator(this, at);
}
/** An iterator over an {@link ILine}. */
protected static class Iterator implements PathIterator
{
private double x1, y1, x2, y2;
private AffineTransform t;
private int index;
Iterator (ILine l, AffineTransform at) {
this.x1 = l.getX1();
this.y1 = l.getY1();
this.x2 = l.getX2();
this.y2 = l.getY2();
this.t = at;
}
@Override public int getWindingRule () {
return WIND_NON_ZERO;
}
@Override public boolean isDone () {
return index > 1;
}
@Override public void next () {
index++;
}
@Override public int currentSegment (double[] coords) {
if (isDone()) {
throw new NoSuchElementException("Iterator out of bounds");
}
int type;
if (index == 0) {
type = SEG_MOVETO;
coords[0] = x1;
coords[1] = y1;
} else {
type = SEG_LINETO;
coords[0] = x2;
coords[1] = y2;
}
if (t != null) {
t.transform(coords, 0, coords, 0, 1);
}
return type;
}
}
}
@@ -0,0 +1,61 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides most of the implementation of {@link IPoint}, obtaining only the location from the
* derived class.
*/
public abstract class AbstractPoint implements IPoint
{
@Override // from interface IPoint
public double distanceSq (double px, double py) {
return Points.distanceSq(getX(), getY(), px, py);
}
@Override // from interface IPoint
public double distanceSq (IPoint p) {
return Points.distanceSq(getX(), getY(), p.getX(), p.getY());
}
@Override // from interface IPoint
public double distance (double px, double py) {
return Points.distance(getX(), getY(), px, py);
}
@Override // from interface IPoint
public double distance (IPoint p) {
return Points.distance(getX(), getY(), p.getX(), p.getY());
}
@Override // from interface IPoint
public Point clone () {
return new Point(this);
}
@Override
public boolean equals (Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof AbstractPoint) {
AbstractPoint p = (AbstractPoint)obj;
return getX() == p.getX() && getY() == p.getY();
}
return false;
}
@Override
public int hashCode () {
long bits = Double.doubleToLongBits(getX());
bits += Double.doubleToLongBits(getY()) * 37;
return (((int) bits) ^ ((int) (bits >> 32)));
}
@Override
public String toString () {
return Points.pointToString(getX(), getY());
}
}
@@ -0,0 +1,163 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
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 double getFlatnessSq () {
return Lines.pointSegDistSq(getCtrlX(), getCtrlY(), getX1(), getY1(), getX2(), getY2());
}
@Override // from interface IQuadCurve
public double 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 (double px, double py) {
return Crossing.isInsideEvenOdd(Crossing.crossShape(this, px, py));
}
@Override // from interface IShape
public boolean contains (double rx, double ry, double rw, double 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 (double rx, double ry, double rw, double 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) {
double x1 = getX1(), y1 = getY1(), x2 = getX2(), y2 = getY2();
double ctrlx = getCtrlX(), ctrly = getCtrlY();
double rx0 = Math.min(Math.min(x1, x2), ctrlx);
double ry0 = Math.min(Math.min(y1, y2), ctrly);
double rx1 = Math.max(Math.max(x1, x2), ctrlx);
double 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, double 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 (double[] 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;
}
}
}
@@ -0,0 +1,238 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.NoSuchElementException;
/**
* Provides most of the implementation of {@link IRectangle}, obtaining only the location and
* dimensions from the derived class.
*/
public abstract class AbstractRectangle extends RectangularShape implements IRectangle
{
@Override // from interface IRectangle
public Point getLocation () {
return getLocation(new Point());
}
@Override // from interface IRectangle
public Point getLocation (Point target) {
target.setLocation(getX(), getY());
return target;
}
@Override // from interface IRectangle
public Dimension getSize () {
return getSize(new Dimension());
}
@Override // from interface IRectangle
public Dimension getSize (Dimension target) {
target.setSize(getWidth(), getHeight());
return target;
}
@Override // from interface IRectangle
public Rectangle intersection (double rx, double ry, double rw, double rh) {
double x1 = Math.max(getX(), rx);
double y1 = Math.max(getY(), ry);
double x2 = Math.min(getMaxX(), rx + rw);
double y2 = Math.min(getMaxY(), ry + rh);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
@Override // from interface IRectangle
public Rectangle intersection (IRectangle r) {
return intersection(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
@Override // from interface IRectangle
public Rectangle union (IRectangle r) {
Rectangle rect = new Rectangle(this);
rect.add(r);
return rect;
}
@Override // from interface IRectangle
public boolean intersectsLine (double x1, double y1, double x2, double y2) {
return Lines.lineIntersectsRect(x1, y1, x2, y2, getX(), getY(), getWidth(), getHeight());
}
@Override // from interface IRectangle
public boolean intersectsLine (ILine l) {
return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2());
}
@Override // from interface IRectangle
public int outcode (double px, double py) {
int code = 0;
if (getWidth() <= 0) {
code |= OUT_LEFT | OUT_RIGHT;
} else if (px < getX()) {
code |= OUT_LEFT;
} else if (px > getMaxX()) {
code |= OUT_RIGHT;
}
if (getHeight() <= 0) {
code |= OUT_TOP | OUT_BOTTOM;
} else if (py < getY()) {
code |= OUT_TOP;
} else if (py > getMaxY()) {
code |= OUT_BOTTOM;
}
return code;
}
@Override // from interface IRectangle
public int outcode (IPoint p) {
return outcode(p.getX(), p.getY());
}
@Override // from interface IRectangle
public Rectangle clone () {
return new Rectangle(this);
}
@Override // from interface IShape
public boolean contains (double px, double py) {
if (isEmpty()) return false;
double x = getX(), y = getY();
if (px < x || py < y) return false;
px -= x;
py -= y;
return px < getWidth() && py < getHeight();
}
@Override // from interface IShape
public boolean contains (double rx, double ry, double rw, double rh) {
if (isEmpty()) return false;
double x1 = getX(), y1 = getY(), x2 = x1 + getWidth(), y2 = y1 + getHeight();
return (x1 <= rx) && (rx + rw <= x2) && (y1 <= ry) && (ry + rh <= y2);
}
@Override // from interface IShape
public boolean intersects (double rx, double ry, double rw, double rh) {
if (isEmpty()) return false;
double x1 = getX(), y1 = getY(), x2 = x1 + getWidth(), y2 = y1 + getHeight();
return (rx + rw > x1) && (rx < x2) && (ry + rh > y1) && (ry < y2);
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform t) {
return new Iterator(this, t);
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform t, double flatness) {
return new Iterator(this, t);
}
@Override // from Object
public boolean equals (Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof AbstractRectangle) {
AbstractRectangle r = (AbstractRectangle)obj;
return r.getX() == getX() && r.getY() == getY() &&
r.getWidth() == getWidth() && r.getHeight() == getHeight();
}
return false;
}
@Override // from Object
public int hashCode () {
long bits = Double.doubleToLongBits(getX());
bits += Double.doubleToLongBits(getY()) * 37;
bits += Double.doubleToLongBits(getWidth()) * 43;
bits += Double.doubleToLongBits(getHeight()) * 47;
return (((int) bits) ^ ((int) (bits >> 32)));
}
@Override // from Object
public String toString () {
return Dimensions.dimenToString(getWidth(), getHeight()) +
Points.pointToString(getX(), getY());
}
/** An iterator over an {@link IRectangle}. */
protected static class Iterator implements PathIterator
{
private double x, y, width, height;
private AffineTransform t;
/** The current segmenet index. */
private int index;
Iterator (IRectangle r, AffineTransform at) {
this.x = r.getX();
this.y = r.getY();
this.width = r.getWidth();
this.height = r.getHeight();
this.t = at;
if (width < 0f || height < 0f) {
index = 6;
}
}
@Override public int getWindingRule () {
return WIND_NON_ZERO;
}
@Override public boolean isDone () {
return index > 5;
}
@Override public void next () {
index++;
}
@Override public int currentSegment (double[] coords) {
if (isDone()) {
throw new NoSuchElementException("Iterator out of bounds");
}
if (index == 5) {
return SEG_CLOSE;
}
int type;
if (index == 0) {
type = SEG_MOVETO;
coords[0] = x;
coords[1] = y;
} else {
type = SEG_LINETO;
switch (index) {
case 1:
coords[0] = x + width;
coords[1] = y;
break;
case 2:
coords[0] = x + width;
coords[1] = y + height;
break;
case 3:
coords[0] = x;
coords[1] = y + height;
break;
case 4:
coords[0] = x;
coords[1] = y;
break;
}
}
if (t != null) {
t.transform(coords, 0, coords, 0, 1);
}
return type;
}
}
}
@@ -0,0 +1,158 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.NoSuchElementException;
/**
* Provides most of the implementation of {@link IRoundRectangle}, obtaining the framing rectangle
* from the derived class.
*/
public abstract class AbstractRoundRectangle extends RectangularShape implements IRoundRectangle
{
@Override // from interface IRoundRectangle
public RoundRectangle clone () {
return new RoundRectangle(getX(), getY(), getWidth(), getHeight(),
getArcWidth(), getArcHeight());
}
@Override // from interface IShape
public boolean contains (double px, double py) {
if (isEmpty()) return false;
double rx1 = getX(), ry1 = getY();
double rx2 = rx1 + getWidth(), ry2 = ry1 + getHeight();
if (px < rx1 || px >= rx2 || py < ry1 || py >= ry2) {
return false;
}
double aw = getArcWidth() / 2f, ah = getArcHeight() / 2f;
double cx, cy;
if (px < rx1 + aw) {
cx = rx1 + aw;
} else if (px > rx2 - aw) {
cx = rx2 - aw;
} else {
return true;
}
if (py < ry1 + ah) {
cy = ry1 + ah;
} else if (py > ry2 - ah) {
cy = ry2 - ah;
} else {
return true;
}
px = (px - cx) / aw;
py = (py - cy) / ah;
return px * px + py * py <= 1f;
}
@Override // from interface IShape
public boolean contains (double rx, double ry, double rw, double rh) {
if (isEmpty() || rw <= 0f || rh <= 0f) return false;
double rx1 = rx, ry1 = ry, rx2 = rx + rw, ry2 = ry + rh;
return contains(rx1, ry1) && contains(rx2, ry1) && contains(rx2, ry2) && contains(rx1, ry2);
}
@Override // from interface IShape
public boolean intersects (double rx, double ry, double rw, double rh) {
if (isEmpty() || rw <= 0f || rh <= 0f) return false;
double x1 = getX(), y1 = getY(), x2 = x1 + getWidth(), y2 = y1 + getHeight();
double rx1 = rx, ry1 = ry, rx2 = rx + rw, ry2 = ry + rh;
if (rx2 < x1 || x2 < rx1 || ry2 < y1 || y2 < ry1) {
return false;
}
double cx = (x1 + x2) / 2f, cy = (y1 + y2) / 2f;
double nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx);
double ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy);
return contains(nx, ny);
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform at) {
return new Iterator(this, at);
}
/** Provides an iterator over an {@link IRoundRectangle}. */
protected static class Iterator implements PathIterator
{
private final double x, y, width, height, aw, ah;
private final AffineTransform t;
private int index;
Iterator (IRoundRectangle rr, AffineTransform at) {
this.x = rr.getX();
this.y = rr.getY();
this.width = rr.getWidth();
this.height = rr.getHeight();
this.aw = Math.min(width, rr.getArcWidth());
this.ah = Math.min(height, rr.getArcHeight());
this.t = at;
if (width < 0f || height < 0f || aw < 0f || ah < 0f) {
index = POINTS.length;
}
}
@Override public int getWindingRule () {
return WIND_NON_ZERO;
}
@Override public boolean isDone () {
return index > POINTS.length;
}
@Override public void next () {
index++;
}
@Override public int currentSegment (double[] coords) {
if (isDone()) {
throw new NoSuchElementException("Iterator out of bounds");
}
if (index == POINTS.length) {
return SEG_CLOSE;
}
int j = 0;
double[] p = POINTS[index];
for (int i = 0; i < p.length; i += 4) {
coords[j++] = x + p[i + 0] * width + p[i + 1] * aw;
coords[j++] = y + p[i + 2] * height + p[i + 3] * ah;
}
if (t != null) {
t.transform(coords, 0, coords, 0, j / 2);
}
return TYPES[index];
}
}
// the path for round corners is generated the same way as for Ellipse
/** The segment types correspond to points array. */
protected static final int[] TYPES = {
PathIterator.SEG_MOVETO, PathIterator.SEG_LINETO, PathIterator.SEG_CUBICTO,
PathIterator.SEG_LINETO, PathIterator.SEG_CUBICTO, PathIterator.SEG_LINETO,
PathIterator.SEG_CUBICTO, PathIterator.SEG_LINETO, PathIterator.SEG_CUBICTO
};
/** The coefficient to calculate control points of Bezier curves. */
protected static final double U = 0.5f - 2f / 3f * (Math.sqrt(2f) - 1f);
/** The points coordinates calculation table. */
protected static final double[][] POINTS = {
{ 0f, 0.5f, 0f, 0f }, // MOVETO
{ 1f, -0.5f, 0f, 0f }, // LINETO
{ 1f, -U, 0f, 0f, 1f, 0f, 0f, U, 1f, 0f, 0f, 0.5f }, // CUBICTO
{ 1f, 0f, 1f, -0.5f }, // LINETO
{ 1f, 0f, 1f, -U, 1f, -U, 1f, 0f, 1f, -0.5f, 1f, 0f }, // CUBICTO
{ 0f, 0.5f, 1f, 0f }, // LINETO
{ 0f, U, 1f, 0f, 0f, 0f, 1f, -U, 0f, 0f, 1f, -0.5f }, // CUBICTO
{ 0f, 0f, 0f, 0.5f }, // LINETO
{ 0f, 0f, 0f, U, 0f, U, 0f, 0f, 0f, 0.5f, 0f, 0f }, // CUBICTO
};
}
@@ -0,0 +1,489 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.io.Serializable;
/**
* Represents a 2D affine transform, which performs a linear mapping that preserves the
* straightness and parallelness of lines.
*
* @see http://download.oracle.com/javase/6/docs/api/java/awt/geom/AffineTransform.html
*/
public class AffineTransform implements Cloneable, Serializable
{
public static final int TYPE_IDENTITY = 0;
public static final int TYPE_TRANSLATION = 1;
public static final int TYPE_UNIFORM_SCALE = 2;
public static final int TYPE_GENERAL_SCALE = 4;
public static final int TYPE_QUADRANT_ROTATION = 8;
public static final int TYPE_GENERAL_ROTATION = 16;
public static final int TYPE_GENERAL_TRANSFORM = 32;
public static final int TYPE_FLIP = 64;
public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE;
public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION;
public AffineTransform () {
this.type = TYPE_IDENTITY;
this.m00 = this.m11 = 1f;
this.m10 = this.m01 = this.m02 = this.m12 = 0;
}
public AffineTransform (AffineTransform t) {
this.type = t.type;
this.m00 = t.m00;
this.m10 = t.m10;
this.m01 = t.m01;
this.m11 = t.m11;
this.m02 = t.m02;
this.m12 = t.m12;
}
public AffineTransform (double m00, double m10, double m01,
double m11, double m02, double m12) {
this.type = TYPE_UNKNOWN;
this.m00 = m00;
this.m10 = m10;
this.m01 = m01;
this.m11 = m11;
this.m02 = m02;
this.m12 = m12;
}
public AffineTransform (double[] matrix) {
this.type = TYPE_UNKNOWN;
m00 = matrix[0];
m10 = matrix[1];
m01 = matrix[2];
m11 = matrix[3];
if (matrix.length > 4) {
m02 = matrix[4];
m12 = matrix[5];
}
}
/*
* Method returns type of affine transformation.
*
* Transform matrix is m00 m01 m02 m10 m11 m12
*
* According analytic geometry new basis vectors are (m00, m01) and (m10,
* m11), translation vector is (m02, m12). Original basis vectors are (1, 0)
* and (0, 1). Type transformations classification: TYPE_IDENTITY - new
* basis equals original one and zero translation TYPE_TRANSLATION -
* translation vector isn't zero TYPE_UNIFORM_SCALE - vectors length of new
* basis equals TYPE_GENERAL_SCALE - vectors length of new basis doesn't
* equal TYPE_FLIP - new basis vector orientation differ from original one
* TYPE_QUADRANT_ROTATION - new basis is rotated by 90, 180, 270, or 360
* degrees TYPE_GENERAL_ROTATION - new basis is rotated by arbitrary angle
* TYPE_GENERAL_TRANSFORM - transformation can't be inversed
*/
public int getType () {
if (type != TYPE_UNKNOWN) {
return type;
}
int type = 0;
if (m00 * m01 + m10 * m11 != 0) {
type |= TYPE_GENERAL_TRANSFORM;
return type;
}
if (m02 != 0 || m12 != 0) {
type |= TYPE_TRANSLATION;
} else if (m00 == 1f && m11 == 1f && m01 == 0 && m10 == 0) {
type = TYPE_IDENTITY;
return type;
}
if (m00 * m11 - m01 * m10 < 0) {
type |= TYPE_FLIP;
}
double dx = m00 * m00 + m10 * m10;
double dy = m01 * m01 + m11 * m11;
if (dx != dy) {
type |= TYPE_GENERAL_SCALE;
} else if (dx != 1f) {
type |= TYPE_UNIFORM_SCALE;
}
if ((m00 == 0 && m11 == 0) || (m10 == 0 && m01 == 0 && (m00 < 0 || m11 < 0))) {
type |= TYPE_QUADRANT_ROTATION;
} else if (m01 != 0 || m10 != 0) {
type |= TYPE_GENERAL_ROTATION;
}
return type;
}
public double getScaleX () {
return m00;
}
public double getScaleY () {
return m11;
}
public double getShearX () {
return m01;
}
public double getShearY () {
return m10;
}
public double getTranslateX () {
return m02;
}
public double getTranslateY () {
return m12;
}
public boolean isIdentity () {
return getType() == TYPE_IDENTITY;
}
public void getMatrix (double[] matrix) {
matrix[0] = m00;
matrix[1] = m10;
matrix[2] = m01;
matrix[3] = m11;
if (matrix.length > 4) {
matrix[4] = m02;
matrix[5] = m12;
}
}
public double getDeterminant () {
return m00 * m11 - m01 * m10;
}
public void setTransform (double m00, double m10, double m01, double m11, double m02, double m12) {
this.type = TYPE_UNKNOWN;
this.m00 = m00;
this.m10 = m10;
this.m01 = m01;
this.m11 = m11;
this.m02 = m02;
this.m12 = m12;
}
public void setTransform (AffineTransform t) {
type = t.type;
setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12);
}
public void setToIdentity () {
type = TYPE_IDENTITY;
m00 = m11 = 1f;
m10 = m01 = m02 = m12 = 0;
}
public void setToTranslation (double mx, double my) {
m00 = m11 = 1f;
m01 = m10 = 0;
m02 = mx;
m12 = my;
if (mx == 0 && my == 0) {
type = TYPE_IDENTITY;
} else {
type = TYPE_TRANSLATION;
}
}
public void setToScale (double scx, double scy) {
m00 = scx;
m11 = scy;
m10 = m01 = m02 = m12 = 0;
if (scx != 1f || scy != 1f) {
type = TYPE_UNKNOWN;
} else {
type = TYPE_IDENTITY;
}
}
public void setToShear (double shx, double shy) {
m00 = m11 = 1f;
m02 = m12 = 0;
m01 = shx;
m10 = shy;
if (shx != 0 || shy != 0) {
type = TYPE_UNKNOWN;
} else {
type = TYPE_IDENTITY;
}
}
public void setToRotation (double angle) {
double sin = Math.sin(angle);
double cos = Math.cos(angle);
if (Math.abs(cos) < ZERO) {
cos = 0;
sin = sin > 0 ? 1f : -1f;
} else if (Math.abs(sin) < ZERO) {
sin = 0;
cos = cos > 0 ? 1f : -1f;
}
m00 = m11 = cos;
m01 = -sin;
m10 = sin;
m02 = m12 = 0;
type = TYPE_UNKNOWN;
}
public void setToRotation (double angle, double px, double py) {
setToRotation(angle);
m02 = px * (1f - m00) + py * m10;
m12 = py * (1f - m00) - px * m10;
type = TYPE_UNKNOWN;
}
public static AffineTransform getTranslateInstance (double mx, double my) {
AffineTransform t = new AffineTransform();
t.setToTranslation(mx, my);
return t;
}
public static AffineTransform getScaleInstance (double scx, double scY) {
AffineTransform t = new AffineTransform();
t.setToScale(scx, scY);
return t;
}
public static AffineTransform getShearInstance (double shx, double shy) {
AffineTransform m = new AffineTransform();
m.setToShear(shx, shy);
return m;
}
public static AffineTransform getRotateInstance (double angle) {
AffineTransform t = new AffineTransform();
t.setToRotation(angle);
return t;
}
public static AffineTransform getRotateInstance (double angle, double x, double y) {
AffineTransform t = new AffineTransform();
t.setToRotation(angle, x, y);
return t;
}
public void translate (double mx, double my) {
concatenate(AffineTransform.getTranslateInstance(mx, my));
}
public void scale (double scx, double scy) {
concatenate(AffineTransform.getScaleInstance(scx, scy));
}
public void shear (double shx, double shy) {
concatenate(AffineTransform.getShearInstance(shx, shy));
}
public void rotate (double angle) {
concatenate(AffineTransform.getRotateInstance(angle));
}
public void rotate (double angle, double px, double py) {
concatenate(AffineTransform.getRotateInstance(angle, px, py));
}
/**
* Multiply matrix of two AffineTransform objects
*
* @param t1
* - the AffineTransform object is a multiplicand
* @param t2
* - the AffineTransform object is a multiplier
* @return an AffineTransform object that is a result of t1 multiplied by
* matrix t2.
*/
AffineTransform multiply (AffineTransform t1, AffineTransform t2) {
return new AffineTransform(t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00
t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01
t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10
t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11
t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02
t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12);// m12
}
public void concatenate (AffineTransform t) {
setTransform(multiply(t, this));
}
public void preConcatenate (AffineTransform t) {
setTransform(multiply(this, t));
}
public AffineTransform createInverse () throws NoninvertibleTransformException {
double det = getDeterminant();
if (Math.abs(det) < ZERO) {
throw new NoninvertibleTransformException("Determinant is zero");
}
return new AffineTransform(m11 / det, // m00
-m10 / det, // m10
-m01 / det, // m01
m00 / det, // m11
(m01 * m12 - m11 * m02) / det, // m02
(m10 * m02 - m00 * m12) / det // m12
);
}
public Point transform (IPoint src, Point dst) {
if (dst == null) {
dst = new Point();
}
double x = src.getX(), y = src.getY();
dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
return dst;
}
public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int length) {
while (--length >= 0) {
IPoint srcPoint = src[srcOff++];
double x = srcPoint.getX();
double y = srcPoint.getY();
Point dstPoint = dst[dstOff];
if (dstPoint == null) {
dstPoint = new Point();
}
dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
dst[dstOff++] = dstPoint;
}
}
public void transform (double[] src, int srcOff, double[] dst, int dstOff, int length) {
int step = 2;
if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
srcOff = srcOff + length * 2 - 2;
dstOff = dstOff + length * 2 - 2;
step = -2;
}
while (--length >= 0) {
double x = src[srcOff + 0];
double y = src[srcOff + 1];
dst[dstOff + 0] = (x * m00 + y * m01 + m02);
dst[dstOff + 1] = (x * m10 + y * m11 + m12);
srcOff += step;
dstOff += step;
}
}
public Point deltaTransform (IPoint src, Point dst) {
if (dst == null) {
dst = new Point();
}
double x = src.getX(), y = src.getY();
dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
return dst;
}
public void deltaTransform (double[] src, int srcOff, double[] dst, int dstOff, int length) {
while (--length >= 0) {
double x = src[srcOff++], y = src[srcOff++];
dst[dstOff++] = x * m00 + y * m01;
dst[dstOff++] = x * m10 + y * m11;
}
}
public Point inverseTransform (IPoint src, Point dst) throws NoninvertibleTransformException {
double det = getDeterminant();
if (Math.abs(det) < ZERO) {
throw new NoninvertibleTransformException("Determinant is zero");
}
if (dst == null) {
dst = new Point();
}
double x = src.getX() - m02, y = src.getY() - m12;
dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10) / det);
return dst;
}
public void inverseTransform (double[] src, int srcOff, double[] dst, int dstOff, int length)
throws NoninvertibleTransformException {
double det = getDeterminant();
if (Math.abs(det) < ZERO) {
throw new NoninvertibleTransformException("Determinant is zero");
}
while (--length >= 0) {
double x = src[srcOff++] - m02, y = src[srcOff++] - m12;
dst[dstOff++] = (x * m11 - y * m01) / det;
dst[dstOff++] = (y * m00 - x * m10) / det;
}
}
public IShape createTransformedShape (IShape src) {
if (src == null) {
return null;
}
if (src instanceof Path) {
return ((Path)src).createTransformedShape(this);
}
PathIterator path = src.getPathIterator(this);
Path dst = new Path(path.getWindingRule());
dst.append(path, false);
return dst;
}
@Override
public String toString () {
return getClass().getName() +
"[[" + m00 + ", " + m01 + ", " + m02 + "], [" + m10 + ", " + m11 + ", " + m12 + "]]";
}
@Override
public AffineTransform clone () {
try {
return (AffineTransform)super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
@Override
public int hashCode () {
long bits = Double.doubleToLongBits(m00);
bits += Double.doubleToLongBits(m01) * 37;
bits += Double.doubleToLongBits(m02) * 43;
bits += Double.doubleToLongBits(m10) * 47;
bits += Double.doubleToLongBits(m11) * 53;
bits += Double.doubleToLongBits(m12) * 59;
return (((int) bits) ^ ((int) (bits >> 32)));
}
@Override
public boolean equals (Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof AffineTransform) {
AffineTransform t = (AffineTransform)obj;
return m00 == t.m00 && m01 == t.m01 && m02 == t.m02 &&
m10 == t.m10 && m11 == t.m11 && m12 == t.m12;
}
return false;
}
// the values of transformation matrix
private double m00;
private double m10;
private double m01;
private double m11;
private double m02;
private double m12;
/** The transformation {@code type}. */
private transient int type;
/** An initial type value. */
private static final int TYPE_UNKNOWN = -1;
/** The min value equivalent to zero. An absolute value < ZERO is considered to be zero. */
private static final double ZERO = 1E-10f;
}
+241
View File
@@ -0,0 +1,241 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.io.Serializable;
/**
* Represents an arc defined by a framing rectangle, start angle, angular extend, and closure type.
*/
public class Arc extends AbstractArc implements Serializable
{
/** The x-coordinate of this arc's framing rectangle. */
public double x;
/** The y-coordinate of this arc's framing rectangle. */
public double y;
/** The width of this arc's framing rectangle. */
public double width;
/** The height of this arc's framing rectangle. */
public double height;
/** The starting angle of this arc. */
public double start;
/** The angular extent of this arc. */
public double extent;
/**
* Creates an open arc with frame (0x0+0+0) and zero angles.
*/
public Arc () {
this(OPEN);
}
/**
* Creates an arc of the specified type with frame (0x0+0+0) and zero angles.
*/
public Arc (int type) {
setArcType(type);
}
/**
* Creates an arc of the specified type with the specified framing rectangle, starting angle
* and angular extent.
*/
public Arc (double x, double y, double width, double height,
double start, double extent, int type) {
setArc(x, y, width, height, start, extent, type);
}
/**
* Creates an arc of the specified type with the supplied framing rectangle, starting angle and
* angular extent.
*/
public Arc (IRectangle bounds, double start, double extent, int type) {
setArc(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(),
start, extent, type);
}
@Override // from interface IArc
public int getArcType () {
return type;
}
@Override // from interface IArc
public double getX () {
return x;
}
@Override // from interface IArc
public double getY () {
return y;
}
@Override // from interface IArc
public double getWidth () {
return width;
}
@Override // from interface IArc
public double getHeight () {
return height;
}
@Override // from interface IArc
public double getAngleStart () {
return start;
}
@Override // from interface IArc
public double getAngleExtent () {
return extent;
}
/**
* Sets the type of this arc to the specified value.
*/
public void setArcType (int type) {
if (type != OPEN && type != CHORD && type != PIE) {
throw new IllegalArgumentException("Invalid Arc type: " + type);
}
this.type = type;
}
/**
* Sets the starting angle of this arc to the specified value.
*/
public void setAngleStart (double start) {
this.start = start;
}
/**
* Sets the angular extent of this arc to the specified value.
*/
public void setAngleExtent (double extent) {
this.extent = extent;
}
/**
* Sets the location, size, angular extents, and closure type of this arc to the specified
* values.
*/
public void setArc (double x, double y, double width, double height,
double start, double extent, int type) {
setArcType(type);
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.start = start;
this.extent = extent;
}
/**
* Sets the location, size, angular extents, and closure type of this arc to the specified
* values.
*/
public void setArc (IPoint point, IDimension size, double start, double extent, int type) {
setArc(point.getX(), point.getY(), size.getWidth(), size.getHeight(), start, extent, type);
}
/**
* Sets the location, size, angular extents, and closure type of this arc to the specified
* values.
*/
public void setArc (IRectangle rect, double start, double extent, int type) {
setArc(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(), start, extent, type);
}
/**
* Sets the location, size, angular extents, and closure type of this arc to the same values as
* the supplied arc.
*/
public void setArc (IArc arc) {
setArc(arc.getX(), arc.getY(), arc.getWidth(), arc.getHeight(), arc.getAngleStart(),
arc.getAngleExtent(), arc.getArcType());
}
/**
* Sets the location, size, angular extents, and closure type of this arc based on the
* specified values.
*/
public void setArcByCenter (double x, double y, double radius,
double start, double extent, int type) {
setArc(x - radius, y - radius, radius * 2f, radius * 2f, start, extent, type);
}
/**
* Sets the location, size, angular extents, and closure type of this arc based on the
* specified values.
*/
public void setArcByTangent (IPoint p1, IPoint p2, IPoint p3, double radius) {
// use simple geometric calculations of arc center, radius and angles by tangents
double a1 = -Math.atan2(p1.getY() - p2.getY(), p1.getX() - p2.getX());
double a2 = -Math.atan2(p3.getY() - p2.getY(), p3.getX() - p2.getX());
double am = (a1 + a2) / 2f;
double ah = a1 - am;
double d = radius / Math.abs(Math.sin(ah));
double x = p2.getX() + d * Math.cos(am);
double y = p2.getY() - d * Math.sin(am);
ah = ah >= 0f ? Math.PI * 1.5f - ah : Math.PI * 0.5f - ah;
a1 = getNormAngle(Math.toDegrees(am - ah));
a2 = getNormAngle(Math.toDegrees(am + ah));
double delta = a2 - a1;
if (delta <= 0f) {
delta += 360f;
}
setArcByCenter(x, y, radius, a1, delta, type);
}
/**
* Sets the starting angle of this arc to the angle defined by the supplied point relative to
* the center of this arc.
*/
public void setAngleStart (IPoint point) {
double angle = Math.atan2(point.getY() - getCenterY(), point.getX() - getCenterX());
setAngleStart(getNormAngle(-Math.toDegrees(angle)));
}
/**
* Sets the starting angle and angular extent of this arc using two sets of coordinates. The
* first set of coordinates is used to determine the angle of the starting point relative to
* the arc's center. The second set of coordinates is used to determine the angle of the end
* point relative to the arc's center. The arc will always be non-empty and extend
* counterclockwise from the first point around to the second point.
*/
public void setAngles (double x1, double y1, double x2, double y2) {
double cx = getCenterX();
double cy = getCenterY();
double a1 = getNormAngle(-Math.toDegrees(Math.atan2(y1 - cy, x1 - cx)));
double a2 = getNormAngle(-Math.toDegrees(Math.atan2(y2 - cy, x2 - cx)));
a2 -= a1;
if (a2 <= 0f) {
a2 += 360f;
}
setAngleStart(a1);
setAngleExtent(a2);
}
/**
* Sets the starting angle and angular extent of this arc using two sets of coordinates. The
* first set of coordinates is used to determine the angle of the starting point relative to
* the arc's center. The second set of coordinates is used to determine the angle of the end
* point relative to the arc's center. The arc will always be non-empty and extend
* counterclockwise from the first point around to the second point.
*/
public void setAngles (IPoint p1, IPoint p2) {
setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY());
}
@Override // from RectangularShape
public void setFrame (double x, double y, double width, double height) {
setArc(x, y, width, height, getAngleStart(), getAngleExtent(), type);
}
private int type;
}
File diff suppressed because it is too large Load Diff
+861
View File
@@ -0,0 +1,861 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Internal utility methods for computing intersections and containment.
*/
class Crossing
{
/** Return value indicating that a crossing was found. */
public static final int CROSSING = 255;
/** Return value indicating the crossing result is unknown. */
public static final int UNKNOWN = 254;
/**
* Solves quadratic equation
*
* @param eqn the coefficients of the equation
* @param res the roots of the equation
* @return a number of roots
*/
public static int solveQuad (double[] eqn, double[] res) {
double a = eqn[2];
double b = eqn[1];
double c = eqn[0];
int rc = 0;
if (a == 0f) {
if (b == 0f) {
return -1;
}
res[rc++] = -c / b;
} else {
double d = b * b - 4f * a * c;
// d < 0f
if (d < 0f) {
return 0;
}
d = Math.sqrt(d);
res[rc++] = (-b + d) / (a * 2f);
// d != 0f
if (d != 0f) {
res[rc++] = (-b - d) / (a * 2f);
}
}
return fixRoots(res, rc);
}
/**
* Solves cubic equation
*
* @param eqn the coefficients of the equation
* @param res the roots of the equation
* @return a number of roots
*/
public static int solveCubic (double[] eqn, double[] res) {
double d = eqn[3];
if (d == 0) {
return solveQuad(eqn, res);
}
double a = eqn[2] / d;
double b = eqn[1] / d;
double c = eqn[0] / d;
int rc = 0;
double Q = (a * a - 3f * b) / 9f;
double R = (2f * a * a * a - 9f * a * b + 27f * c) / 54f;
double Q3 = Q * Q * Q;
double R2 = R * R;
double n = -a / 3f;
if (R2 < Q3) {
double t = Math.acos(R / Math.sqrt(Q3)) / 3f;
double p = 2f * Math.PI / 3f;
double m = -2f * Math.sqrt(Q);
res[rc++] = m * Math.cos(t) + n;
res[rc++] = m * Math.cos(t + p) + n;
res[rc++] = m * Math.cos(t - p) + n;
} else {
// Debug.println("R2 >= Q3 (" + R2 + "/" + Q3 + ")");
double A = Math.pow(Math.abs(R) + Math.sqrt(R2 - Q3), 1f / 3f);
if (R > 0f) {
A = -A;
}
// if (A == 0f) {
if (-ROOT_DELTA < A && A < ROOT_DELTA) {
res[rc++] = n;
} else {
double B = Q / A;
res[rc++] = A + B + n;
// if (R2 == Q3) {
double delta = R2 - Q3;
if (-ROOT_DELTA < delta && delta < ROOT_DELTA) {
res[rc++] = -(A + B) / 2f + n;
}
}
}
return fixRoots(res, rc);
}
/**
* Excludes double roots. Roots are double if they lies enough close with each other.
*
* @param res the roots
* @param rc the roots count
* @return new roots count
*/
protected static int fixRoots (double[] res, int rc) {
int tc = 0;
for (int i = 0; i < rc; i++) {
out: {
for (int j = i + 1; j < rc; j++) {
if (isZero(res[i] - res[j])) {
break out;
}
}
res[tc++] = res[i];
}
}
return tc;
}
/**
* QuadCurve class provides basic functionality to find curve crossing and calculating bounds
*/
public static class QuadCurve
{
double ax, ay, bx, by;
double Ax, Ay, Bx, By;
public QuadCurve (double x1, double y1, double cx, double cy, double x2, double y2) {
ax = x2 - x1;
ay = y2 - y1;
bx = cx - x1;
by = cy - y1;
Bx = bx + bx; // Bx = 2f * bx
Ax = ax - Bx; // Ax = ax - 2f * bx
By = by + by; // By = 2f * by
Ay = ay - By; // Ay = ay - 2f * by
}
public int cross (double[] res, int rc, double py1, double py2) {
int cross = 0;
for (int i = 0; i < rc; i++) {
double t = res[i];
// CURVE-OUTSIDE
if (t < -DELTA || t > 1 + DELTA) {
continue;
}
// CURVE-START
if (t < DELTA) {
if (py1 < 0f && (bx != 0f ? bx : ax - bx) < 0f) {
cross--;
}
continue;
}
// CURVE-END
if (t > 1 - DELTA) {
if (py1 < ay && (ax != bx ? ax - bx : bx) > 0f) {
cross++;
}
continue;
}
// CURVE-INSIDE
double ry = t * (t * Ay + By);
// ry = t * t * Ay + t * By
if (ry > py2) {
double rxt = t * Ax + bx;
// rxt = 2f * t * Ax + Bx = 2f * t * Ax + 2f * bx
if (rxt > -DELTA && rxt < DELTA) {
continue;
}
cross += rxt > 0f ? 1 : -1;
}
} // for
return cross;
}
public int solvePoint (double[] res, double px) {
double[] eqn = { -px, Bx, Ax };
return solveQuad(eqn, res);
}
public int solveExtreme (double[] res) {
int rc = 0;
if (Ax != 0f) {
res[rc++] = -Bx / (Ax + Ax);
}
if (Ay != 0f) {
res[rc++] = -By / (Ay + Ay);
}
return rc;
}
public int addBound (double[] bound, int bc, double[] res, int rc, double minX, double maxX,
boolean changeId, int id) {
for (int i = 0; i < rc; i++) {
double t = res[i];
if (t > -DELTA && t < 1 + DELTA) {
double rx = t * (t * Ax + Bx);
if (minX <= rx && rx <= maxX) {
bound[bc++] = t;
bound[bc++] = rx;
bound[bc++] = t * (t * Ay + By);
bound[bc++] = id;
if (changeId) {
id++;
}
}
}
}
return bc;
}
}
/** CubicCurve helper for finding curve crossing and calculating bounds. */
public static class CubicCurveH
{
double ax, ay, bx, by, cx, cy;
double Ax, Ay, Bx, By, Cx, Cy;
double Ax3, Bx2;
public CubicCurveH (double x1, double y1, double cx1, double cy1, double cx2, double cy2,
double x2, double y2) {
ax = x2 - x1;
ay = y2 - y1;
bx = cx1 - x1;
by = cy1 - y1;
cx = cx2 - x1;
cy = cy2 - y1;
Cx = bx + bx + bx; // Cx = 3f * bx
Bx = cx + cx + cx - Cx - Cx; // Bx = 3f * cx - 6f * bx
Ax = ax - Bx - Cx; // Ax = ax - 3f * cx + 3f * bx
Cy = by + by + by; // Cy = 3f * by
By = cy + cy + cy - Cy - Cy; // By = 3f * cy - 6f * by
Ay = ay - By - Cy; // Ay = ay - 3f * cy + 3f * by
Ax3 = Ax + Ax + Ax;
Bx2 = Bx + Bx;
}
public int cross (double[] res, int rc, double py1, double py2) {
int cross = 0;
for (int i = 0; i < rc; i++) {
double t = res[i];
// CURVE-OUTSIDE
if (t < -DELTA || t > 1 + DELTA) {
continue;
}
// CURVE-START
if (t < DELTA) {
if (py1 < 0f && (bx != 0f ? bx : (cx != bx ? cx - bx : ax - cx)) < 0f) {
cross--;
}
continue;
}
// CURVE-END
if (t > 1 - DELTA) {
if (py1 < ay && (ax != cx ? ax - cx : (cx != bx ? cx - bx : bx)) > 0f) {
cross++;
}
continue;
}
// CURVE-INSIDE
double ry = t * (t * (t * Ay + By) + Cy);
// ry = t * t * t * Ay + t * t * By + t * Cy
if (ry > py2) {
double rxt = t * (t * Ax3 + Bx2) + Cx;
// rxt = 3f * t * t * Ax + 2f * t * Bx + Cx
if (rxt > -DELTA && rxt < DELTA) {
rxt = t * (Ax3 + Ax3) + Bx2;
// rxt = 6f * t * Ax + 2f * Bx
if (rxt < -DELTA || rxt > DELTA) {
// Inflection point
continue;
}
rxt = ax;
}
cross += rxt > 0f ? 1 : -1;
}
} // for
return cross;
}
public int solvePoint (double[] res, double px) {
double[] eqn = { -px, Cx, Bx, Ax };
return solveCubic(eqn, res);
}
public int solveExtremeX (double[] res) {
double[] eqn = { Cx, Bx2, Ax3 };
return solveQuad(eqn, res);
}
public int solveExtremeY (double[] res) {
double[] eqn = { Cy, By + By, Ay + Ay + Ay };
return solveQuad(eqn, res);
}
public int addBound (double[] bound, int bc, double[] res, int rc, double minX, double maxX,
boolean changeId, int id) {
for (int i = 0; i < rc; i++) {
double t = res[i];
if (t > -DELTA && t < 1 + DELTA) {
double rx = t * (t * (t * Ax + Bx) + Cx);
if (minX <= rx && rx <= maxX) {
bound[bc++] = t;
bound[bc++] = rx;
bound[bc++] = t * (t * (t * Ay + By) + Cy);
bound[bc++] = id;
if (changeId) {
id++;
}
}
}
}
return bc;
}
}
/**
* Returns how many times ray from point (x,y) cross line.
*/
public static int crossLine (double x1, double y1, double x2, double y2, double x, double y) {
// LEFT/RIGHT/UP/EMPTY
if ((x < x1 && x < x2) || (x > x1 && x > x2) || (y > y1 && y > y2) || (x1 == x2)) {
return 0;
}
// DOWN
if (y < y1 && y < y2) {
} else {
// INSIDE
if ((y2 - y1) * (x - x1) / (x2 - x1) <= y - y1) {
// INSIDE-UP
return 0;
}
}
// START
if (x == x1) {
return x1 < x2 ? 0 : -1;
}
// END
if (x == x2) {
return x1 < x2 ? 1 : 0;
}
// INSIDE-DOWN
return x1 < x2 ? 1 : -1;
}
/**
* Returns how many times ray from point (x,y) cross quard curve
*/
public static int crossQuad (double x1, double y1, double cx, double cy, double x2, double y2,
double x, double y) {
// LEFT/RIGHT/UP/EMPTY
if ((x < x1 && x < cx && x < x2) || (x > x1 && x > cx && x > x2)
|| (y > y1 && y > cy && y > y2) || (x1 == cx && cx == x2)) {
return 0;
}
// DOWN
if (y < y1 && y < cy && y < y2 && x != x1 && x != x2) {
if (x1 < x2) {
return x1 < x && x < x2 ? 1 : 0;
}
return x2 < x && x < x1 ? -1 : 0;
}
// INSIDE
QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2);
double px = x - x1, py = y - y1;
double[] res = new double[3];
int rc = c.solvePoint(res, px);
return c.cross(res, rc, py, py);
}
/**
* Returns how many times ray from point (x,y) cross cubic curve
*/
public static int crossCubic (double x1, double y1, double cx1, double cy1, double cx2,
double cy2, double x2, double y2, double x, double y) {
// LEFT/RIGHT/UP/EMPTY
if ((x < x1 && x < cx1 && x < cx2 && x < x2) || (x > x1 && x > cx1 && x > cx2 && x > x2)
|| (y > y1 && y > cy1 && y > cy2 && y > y2)
|| (x1 == cx1 && cx1 == cx2 && cx2 == x2)) {
return 0;
}
// DOWN
if (y < y1 && y < cy1 && y < cy2 && y < y2 && x != x1 && x != x2) {
if (x1 < x2) {
return x1 < x && x < x2 ? 1 : 0;
}
return x2 < x && x < x1 ? -1 : 0;
}
// INSIDE
CubicCurveH c = new CubicCurveH(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
double px = x - x1, py = y - y1;
double[] res = new double[3];
int rc = c.solvePoint(res, px);
return c.cross(res, rc, py, py);
}
/**
* Returns how many times ray from point (x,y) cross path
*/
public static int crossPath (PathIterator p, double x, double y) {
int cross = 0;
double mx, my, cx, cy;
mx = my = cx = cy = 0f;
double[] coords = new double[6];
while (!p.isDone()) {
switch (p.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
if (cx != mx || cy != my) {
cross += crossLine(cx, cy, mx, my, x, y);
}
mx = cx = coords[0];
my = cy = coords[1];
break;
case PathIterator.SEG_LINETO:
cross += crossLine(cx, cy, cx = coords[0], cy = coords[1], x, y);
break;
case PathIterator.SEG_QUADTO:
cross += crossQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], x,
y);
break;
case PathIterator.SEG_CUBICTO:
cross += crossCubic(cx, cy, coords[0], coords[1], coords[2], coords[3],
cx = coords[4], cy = coords[5], x, y);
break;
case PathIterator.SEG_CLOSE:
if (cy != my || cx != mx) {
cross += crossLine(cx, cy, cx = mx, cy = my, x, y);
}
break;
}
// checks if the point (x,y) is the vertex of shape with PathIterator p
if (x == cx && y == cy) {
cross = 0;
cy = my;
break;
}
p.next();
}
if (cy != my) {
cross += crossLine(cx, cy, mx, my, x, y);
}
return cross;
}
/**
* Returns how many times a ray from point (x,y) crosses a shape.
*/
public static int crossShape (IShape s, double x, double y) {
if (!s.getBounds().contains(x, y)) {
return 0;
}
return crossPath(s.getPathIterator(null), x, y);
}
/**
* Returns true if value is close enough to zero.
*/
public static boolean isZero (double val) {
return -DELTA < val && val < DELTA;
}
/**
* Returns how many times rectangle stripe cross line or the are intersect
*/
public static int intersectLine (double x1, double y1, double x2, double y2, double rx1,
double ry1, double rx2, double ry2) {
// LEFT/RIGHT/UP
if ((rx2 < x1 && rx2 < x2) || (rx1 > x1 && rx1 > x2) || (ry1 > y1 && ry1 > y2)) {
return 0;
}
// DOWN
if (ry2 < y1 && ry2 < y2) {
} else {
// INSIDE
if (x1 == x2) {
return CROSSING;
}
// Build bound
double bx1, bx2;
if (x1 < x2) {
bx1 = x1 < rx1 ? rx1 : x1;
bx2 = x2 < rx2 ? x2 : rx2;
} else {
bx1 = x2 < rx1 ? rx1 : x2;
bx2 = x1 < rx2 ? x1 : rx2;
}
double k = (y2 - y1) / (x2 - x1);
double by1 = k * (bx1 - x1) + y1;
double by2 = k * (bx2 - x1) + y1;
// BOUND-UP
if (by1 < ry1 && by2 < ry1) {
return 0;
}
// BOUND-DOWN
if (by1 > ry2 && by2 > ry2) {
} else {
return CROSSING;
}
}
// EMPTY
if (x1 == x2) {
return 0;
}
// CURVE-START
if (rx1 == x1) {
return x1 < x2 ? 0 : -1;
}
// CURVE-END
if (rx1 == x2) {
return x1 < x2 ? 1 : 0;
}
if (x1 < x2) {
return x1 < rx1 && rx1 < x2 ? 1 : 0;
}
return x2 < rx1 && rx1 < x1 ? -1 : 0;
}
/**
* Returns how many times rectangle stripe cross quad curve or the are
* intersect
*/
public static int intersectQuad (double x1, double y1, double cx, double cy, double x2,
double y2, double rx1, double ry1, double rx2, double ry2) {
// LEFT/RIGHT/UP ------------------------------------------------------
if ((rx2 < x1 && rx2 < cx && rx2 < x2) || (rx1 > x1 && rx1 > cx && rx1 > x2) ||
(ry1 > y1 && ry1 > cy && ry1 > y2)) {
return 0;
}
// DOWN ---------------------------------------------------------------
if (ry2 < y1 && ry2 < cy && ry2 < y2 && rx1 != x1 && rx1 != x2) {
if (x1 < x2) {
return x1 < rx1 && rx1 < x2 ? 1 : 0;
}
return x2 < rx1 && rx1 < x1 ? -1 : 0;
}
// INSIDE -------------------------------------------------------------
QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2);
double px1 = rx1 - x1;
double py1 = ry1 - y1;
double px2 = rx2 - x1;
double py2 = ry2 - y1;
double[] res1 = new double[3];
double[] res2 = new double[3];
int rc1 = c.solvePoint(res1, px1);
int rc2 = c.solvePoint(res2, px2);
// INSIDE-LEFT/RIGHT
if (rc1 == 0 && rc2 == 0) {
return 0;
}
// Build bound --------------------------------------------------------
double minX = px1 - DELTA;
double maxX = px2 + DELTA;
double[] bound = new double[28];
int bc = 0;
// Add roots
bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0);
bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1);
// Add extremal points
rc2 = c.solveExtreme(res2);
bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2);
// Add start and end
if (rx1 < x1 && x1 < rx2) {
bound[bc++] = 0f;
bound[bc++] = 0f;
bound[bc++] = 0f;
bound[bc++] = 4;
}
if (rx1 < x2 && x2 < rx2) {
bound[bc++] = 1f;
bound[bc++] = c.ax;
bound[bc++] = c.ay;
bound[bc++] = 5;
}
// End build bound ----------------------------------------------------
int cross = crossBound(bound, bc, py1, py2);
if (cross != UNKNOWN) {
return cross;
}
return c.cross(res1, rc1, py1, py2);
}
/**
* Returns how many times rectangle stripe cross cubic curve or the are
* intersect
*/
public static int intersectCubic (double x1, double y1, double cx1, double cy1,
double cx2, double cy2, double x2, double y2,
double rx1, double ry1, double rx2, double ry2) {
// LEFT/RIGHT/UP
if ((rx2 < x1 && rx2 < cx1 && rx2 < cx2 && rx2 < x2)
|| (rx1 > x1 && rx1 > cx1 && rx1 > cx2 && rx1 > x2)
|| (ry1 > y1 && ry1 > cy1 && ry1 > cy2 && ry1 > y2)) {
return 0;
}
// DOWN
if (ry2 < y1 && ry2 < cy1 && ry2 < cy2 && ry2 < y2 && rx1 != x1 && rx1 != x2) {
if (x1 < x2) {
return x1 < rx1 && rx1 < x2 ? 1 : 0;
}
return x2 < rx1 && rx1 < x1 ? -1 : 0;
}
// INSIDE
CubicCurveH c = new CubicCurveH(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
double px1 = rx1 - x1;
double py1 = ry1 - y1;
double px2 = rx2 - x1;
double py2 = ry2 - y1;
double[] res1 = new double[3];
double[] res2 = new double[3];
int rc1 = c.solvePoint(res1, px1);
int rc2 = c.solvePoint(res2, px2);
// LEFT/RIGHT
if (rc1 == 0 && rc2 == 0) {
return 0;
}
double minX = px1 - DELTA;
double maxX = px2 + DELTA;
// Build bound --------------------------------------------------------
double[] bound = new double[40];
int bc = 0;
// Add roots
bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0);
bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1);
// Add extremal points
rc2 = c.solveExtremeX(res2);
bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2);
rc2 = c.solveExtremeY(res2);
bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 4);
// Add start and end
if (rx1 < x1 && x1 < rx2) {
bound[bc++] = 0f;
bound[bc++] = 0f;
bound[bc++] = 0f;
bound[bc++] = 6;
}
if (rx1 < x2 && x2 < rx2) {
bound[bc++] = 1f;
bound[bc++] = c.ax;
bound[bc++] = c.ay;
bound[bc++] = 7;
}
// End build bound ----------------------------------------------------
int cross = crossBound(bound, bc, py1, py2);
if (cross != UNKNOWN) {
return cross;
}
return c.cross(res1, rc1, py1, py2);
}
/**
* Returns how many times rectangle stripe cross path or the are intersect
*/
public static int intersectPath (PathIterator p, double x, double y, double w, double h) {
int cross = 0;
int count;
double mx, my, cx, cy;
mx = my = cx = cy = 0f;
double[] coords = new double[6];
double rx1 = x;
double ry1 = y;
double rx2 = x + w;
double ry2 = y + h;
while (!p.isDone()) {
count = 0;
switch (p.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
if (cx != mx || cy != my) {
count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
}
mx = cx = coords[0];
my = cy = coords[1];
break;
case PathIterator.SEG_LINETO:
count = intersectLine(cx, cy, cx = coords[0], cy = coords[1], rx1, ry1, rx2, ry2);
break;
case PathIterator.SEG_QUADTO:
count = intersectQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3],
rx1, ry1, rx2, ry2);
break;
case PathIterator.SEG_CUBICTO:
count = intersectCubic(cx, cy, coords[0], coords[1], coords[2], coords[3],
cx = coords[4], cy = coords[5], rx1, ry1, rx2, ry2);
break;
case PathIterator.SEG_CLOSE:
if (cy != my || cx != mx) {
count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
}
cx = mx;
cy = my;
break;
}
if (count == CROSSING) {
return CROSSING;
}
cross += count;
p.next();
}
if (cy != my) {
count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
if (count == CROSSING) {
return CROSSING;
}
cross += count;
}
return cross;
}
/**
* Returns how many times rectangle stripe cross shape or the are intersect
*/
public static int intersectShape (IShape s, double x, double y, double w, double h) {
if (!s.getBounds().intersects(x, y, w, h)) {
return 0;
}
return intersectPath(s.getPathIterator(null), x, y, w, h);
}
/**
* Returns true if cross count correspond inside location for non zero path
* rule
*/
public static boolean isInsideNonZero (int cross) {
return cross != 0;
}
/**
* Returns true if cross count correspond inside location for even-odd path
* rule
*/
public static boolean isInsideEvenOdd (int cross) {
return (cross & 1) != 0;
}
/**
* Sorts a bound array.
*/
protected static void sortBound (double[] bound, int bc) {
for (int i = 0; i < bc - 4; i += 4) {
int k = i;
for (int j = i + 4; j < bc; j += 4) {
if (bound[k] > bound[j]) {
k = j;
}
}
if (k != i) {
double tmp = bound[i];
bound[i] = bound[k];
bound[k] = tmp;
tmp = bound[i + 1];
bound[i + 1] = bound[k + 1];
bound[k + 1] = tmp;
tmp = bound[i + 2];
bound[i + 2] = bound[k + 2];
bound[k + 2] = tmp;
tmp = bound[i + 3];
bound[i + 3] = bound[k + 3];
bound[k + 3] = tmp;
}
}
}
/**
* Returns whether bounds intersect a rectangle or not.
*/
protected static int crossBound (double[] bound, int bc, double py1, double py2) {
// LEFT/RIGHT
if (bc == 0) {
return 0;
}
// Check Y coordinate
int up = 0;
int down = 0;
for (int i = 2; i < bc; i += 4) {
if (bound[i] < py1) {
up++;
continue;
}
if (bound[i] > py2) {
down++;
continue;
}
return CROSSING;
}
// UP
if (down == 0) {
return 0;
}
if (up != 0) {
// bc >= 2
sortBound(bound, bc);
boolean sign = bound[2] > py2;
for (int i = 6; i < bc; i += 4) {
boolean sign2 = bound[i] > py2;
if (sign != sign2 && bound[i + 1] != bound[i - 3]) {
return CROSSING;
}
sign = sign2;
}
}
return UNKNOWN;
}
/** Allowable tolerance for bounds comparison */
protected static final double DELTA = 1E-5f;
/** If roots have distance less then <code>ROOT_DELTA</code> they are double */
protected static final double ROOT_DELTA = 1E-10f;
}
@@ -0,0 +1,291 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* An internal class used to compute crossings.
*/
class CrossingHelper
{
private double[][] coords;
private int[] sizes;
private List<IntersectPoint> isectPoints = new ArrayList<IntersectPoint>();
public CrossingHelper (double[][] coords, int[] sizes) {
this.coords = coords;
this.sizes = sizes;
}
public IntersectPoint[] findCrossing () {
int pointCount1 = sizes[0] / 2;
int pointCount2 = sizes[1] / 2;
int[] indices = new int[pointCount1 + pointCount2];
for (int i = 0; i < pointCount1 + pointCount2; i++) {
indices[i] = i;
}
sort(coords[0], pointCount1, coords[1], pointCount2, indices);
// the set for the shapes edges storing
List<Edge> edges = new ArrayList<Edge>();
Edge edge;
int begIndex, endIndex;
int areaNumber;
for (int i = 0; i < indices.length; i++) {
if (indices[i] < pointCount1) {
begIndex = indices[i];
endIndex = indices[i] - 1;
if (endIndex < 0) {
endIndex = pointCount1 - 1;
}
areaNumber = 0;
} else if (indices[i] < pointCount1 + pointCount2) {
begIndex = indices[i] - pointCount1;
endIndex = indices[i] - 1 - pointCount1;
if (endIndex < 0) {
endIndex = pointCount2 - 1;
}
areaNumber = 1;
} else {
throw new IndexOutOfBoundsException();
}
if (!removeEdge(edges, begIndex, endIndex)) {
edge = new Edge(begIndex, endIndex, areaNumber);
intersectShape(edges, coords[0], pointCount1, coords[1], pointCount2, edge);
edges.add(edge);
}
begIndex = indices[i];
endIndex = indices[i] + 1;
if ((begIndex < pointCount1) && (endIndex == pointCount1)) {
endIndex = 0;
} else if ((begIndex >= pointCount1) && (endIndex == (pointCount2 + pointCount1))) {
endIndex = pointCount1;
}
if (endIndex < pointCount1) {
areaNumber = 0;
} else {
areaNumber = 1;
endIndex -= pointCount1;
begIndex -= pointCount1;
}
if (!removeEdge(edges, begIndex, endIndex)) {
edge = new Edge(begIndex, endIndex, areaNumber);
intersectShape(edges, coords[0], pointCount1, coords[1], pointCount2, edge);
edges.add(edge);
}
}
return isectPoints.toArray(new IntersectPoint[isectPoints.size()]);
}
private boolean removeEdge (List<Edge> edges, int begIndex, int endIndex) {
for (Edge edge : edges) {
if (edge.reverseCompare(begIndex, endIndex)) {
edges.remove(edge);
return true;
}
}
return false;
}
// return the quantity of intersect points
private void intersectShape (List<Edge> edges, double[] coords1, int length1,
double[] coords2, int length2, Edge initEdge) {
int areaOfEdge1, areaOfEdge2;
int initBegin, initEnd;
int addBegin, addEnd;
double x1, y1, x2, y2, x3, y3, x4, y4;
double[] point = new double[2];
Edge edge;
if (initEdge.areaNumber == 0) {
x1 = coords1[2 * initEdge.begIndex];
y1 = coords1[2 * initEdge.begIndex + 1];
x2 = coords1[2 * initEdge.endIndex];
y2 = coords1[2 * initEdge.endIndex + 1];
areaOfEdge1 = 0;
} else {
x1 = coords2[2 * initEdge.begIndex];
y1 = coords2[2 * initEdge.begIndex + 1];
x2 = coords2[2 * initEdge.endIndex];
y2 = coords2[2 * initEdge.endIndex + 1];
areaOfEdge1 = 1;
}
for (Iterator<Edge> iter = edges.iterator(); iter.hasNext();) {
edge = iter.next();
if (edge.areaNumber == 0) {
x3 = coords1[2 * edge.begIndex];
y3 = coords1[2 * edge.begIndex + 1];
x4 = coords1[2 * edge.endIndex];
y4 = coords1[2 * edge.endIndex + 1];
areaOfEdge2 = 0;
} else {
x3 = coords2[2 * edge.begIndex];
y3 = coords2[2 * edge.begIndex + 1];
x4 = coords2[2 * edge.endIndex];
y4 = coords2[2 * edge.endIndex + 1];
areaOfEdge2 = 1;
}
if ((areaOfEdge1 != areaOfEdge2) &&
(GeometryUtil.intersectLines(x1, y1, x2, y2, x3, y3, x4, y4, point) == 1) &&
(!containsPoint(point))) {
if (initEdge.areaNumber == 0) {
initBegin = initEdge.begIndex;
initEnd = initEdge.endIndex;
addBegin = edge.begIndex;
addEnd = edge.endIndex;
} else {
initBegin = edge.begIndex;
initEnd = edge.endIndex;
addBegin = initEdge.begIndex;
addEnd = initEdge.endIndex;
}
if (((initEnd == length1 - 1) && (initBegin == 0 && initEnd > initBegin)) ||
(((initEnd != length1 - 1) || (initBegin != 0)) &&
((initBegin != length1 - 1) || (initEnd != 0)) && (initBegin > initEnd))) {
int temp = initBegin;
initBegin = initEnd;
initEnd = temp;
}
if (((addEnd == length2 - 1) && (addBegin == 0) && (addEnd > addBegin)) ||
(((addEnd != length2 - 1) || (addBegin != 0)) &&
((addBegin != length2 - 1) || (addEnd != 0)) && (addBegin > addEnd))) {
int temp = addBegin;
addBegin = addEnd;
addEnd = temp;
}
IntersectPoint ip;
for (Iterator<IntersectPoint> i = isectPoints.iterator(); i.hasNext();) {
ip = i.next();
if ((initBegin == ip.getBegIndex(true)) && (initEnd == ip.getEndIndex(true))) {
if (compare(ip.getX(), ip.getY(), point[0], point[1]) > 0) {
initEnd = -(isectPoints.indexOf(ip) + 1);
ip.setBegIndex1(-(isectPoints.size() + 1));
} else {
initBegin = -(isectPoints.indexOf(ip) + 1);
ip.setEndIndex1(-(isectPoints.size() + 1));
}
}
if ((addBegin == ip.getBegIndex(false)) && (addEnd == ip.getEndIndex(false))) {
if (compare(ip.getX(), ip.getY(), point[0], point[1]) > 0) {
addEnd = -(isectPoints.indexOf(ip) + 1);
ip.setBegIndex2(-(isectPoints.size() + 1));
} else {
addBegin = -(isectPoints.indexOf(ip) + 1);
ip.setEndIndex2(-(isectPoints.size() + 1));
}
}
}
isectPoints.add(new IntersectPoint(initBegin, initEnd, addBegin, addEnd,
point[0], point[1]));
}
}
}
// the array sorting
private static void sort (double[] coords1, int length1,
double[] coords2, int length2, int[] array) {
int temp;
int length = length1 + length2;
double x1, y1, x2, y2;
for (int i = 1; i < length; i++) {
if (array[i - 1] < length1) {
x1 = coords1[2 * array[i - 1]];
y1 = coords1[2 * array[i - 1] + 1];
} else {
x1 = coords2[2 * (array[i - 1] - length1)];
y1 = coords2[2 * (array[i - 1] - length1) + 1];
}
if (array[i] < length1) {
x2 = coords1[2 * array[i]];
y2 = coords1[2 * array[i] + 1];
} else {
x2 = coords2[2 * (array[i] - length1)];
y2 = coords2[2 * (array[i] - length1) + 1];
}
int j = i;
while (j > 0 && compare(x1, y1, x2, y2) <= 0) {
temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
j--;
if (j > 0) {
if (array[j - 1] < length1) {
x1 = coords1[2 * array[j - 1]];
y1 = coords1[2 * array[j - 1] + 1];
} else {
x1 = coords2[2 * (array[j - 1] - length1)];
y1 = coords2[2 * (array[j - 1] - length1) + 1];
}
if (array[j] < length1) {
x2 = coords1[2 * array[j]];
y2 = coords1[2 * array[j] + 1];
} else {
x2 = coords2[2 * (array[j] - length1)];
y2 = coords2[2 * (array[j] - length1) + 1];
}
}
}
}
}
public boolean containsPoint (double[] point) {
IntersectPoint ipoint;
for (Iterator<IntersectPoint> i = isectPoints.iterator(); i.hasNext();) {
ipoint = i.next();
if (ipoint.getX() == point[0] && ipoint.getY() == point[1]) {
return true;
}
}
return false;
}
public static int compare (double x1, double y1, double x2, double y2) {
if ((x1 < x2) || (x1 == x2 && y1 < y2)) {
return 1;
} else if (x1 == x2 && y1 == y2) {
return 0;
}
return -1;
}
private static final class Edge
{
final int begIndex;
final int endIndex;
final int areaNumber;
Edge (int begIndex, int endIndex, int areaNumber) {
this.begIndex = begIndex;
this.endIndex = endIndex;
this.areaNumber = areaNumber;
}
boolean reverseCompare (int begIndex, int endIndex) {
return this.begIndex == endIndex && this.endIndex == begIndex;
}
}
}
+143
View File
@@ -0,0 +1,143 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
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 double x1;
/** The y-coordinate of the start of this curve. */
public double y1;
/** The x-coordinate of the first control point. */
public double ctrlx1;
/** The y-coordinate of the first control point. */
public double ctrly1;
/** The x-coordinate of the second control point. */
public double ctrlx2;
/** The x-coordinate of the second control point. */
public double ctrly2;
/** The x-coordinate of the end of this curve. */
public double x2;
/** The y-coordinate of the end of this curve. */
public double 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 (double x1, double y1, double ctrlx1, double ctrly1,
double ctrlx2, double ctrly2, double x2, double y2) {
setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
}
/**
* Configures the start, control and end points for this curve.
*/
public void setCurve (double x1, double y1, double ctrlx1, double ctrly1, double ctrlx2,
double ctrly2, double x2, double 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 (double[] 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 double getX1 () {
return x1;
}
@Override // from interface ICubicCurve
public double getY1 () {
return y1;
}
@Override // from interface ICubicCurve
public double getCtrlX1 () {
return ctrlx1;
}
@Override // from interface ICubicCurve
public double getCtrlY1 () {
return ctrly1;
}
@Override // from interface ICubicCurve
public double getCtrlX2 () {
return ctrlx2;
}
@Override // from interface ICubicCurve
public double getCtrlY2 () {
return ctrly2;
}
@Override // from interface ICubicCurve
public double getX2 () {
return x2;
}
@Override // from interface ICubicCurve
public double getY2 () {
return y2;
}
}
+101
View File
@@ -0,0 +1,101 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Cubic curve-related utility methods.
*/
public class CubicCurves
{
public static double getFlatnessSq (double x1, double y1, double ctrlx1, double ctrly1,
double ctrlx2, double ctrly2, double x2, double y2) {
return Math.max(Lines.pointSegDistSq(ctrlx1, ctrly1, x1, y1, x2, y2),
Lines.pointSegDistSq(ctrlx2, ctrly2, x1, y1, x2, y2));
}
public static double getFlatnessSq (double[] 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 double getFlatness (double x1, double y1, double ctrlx1, double ctrly1,
double ctrlx2, double ctrly2, double x2, double y2) {
return Math.sqrt(getFlatnessSq(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2));
}
public static double getFlatness (double[] 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) {
double x1 = src.getX1(), y1 = src.getY1();
double cx1 = src.getCtrlX1(), cy1 = src.getCtrlY1();
double cx2 = src.getCtrlX2(), cy2 = src.getCtrlY2();
double x2 = src.getX2(), y2 = src.getY2();
double cx = (cx1 + cx2) / 2f, cy = (cy1 + cy2) / 2f;
cx1 = (x1 + cx1) / 2f;
cy1 = (y1 + cy1) / 2f;
cx2 = (x2 + cx2) / 2f;
cy2 = (y2 + cy2) / 2f;
double ax = (cx1 + cx) / 2f, ay = (cy1 + cy) / 2f;
double 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 (double[] src, int srcOff, double left[], int leftOff,
double[] right, int rightOff) {
double x1 = src[srcOff + 0], y1 = src[srcOff + 1];
double cx1 = src[srcOff + 2], cy1 = src[srcOff + 3];
double cx2 = src[srcOff + 4], cy2 = src[srcOff + 5];
double x2 = src[srcOff + 6], y2 = src[srcOff + 7];
double cx = (cx1 + cx2) / 2f, cy = (cy1 + cy2) / 2f;
cx1 = (x1 + cx1) / 2f;
cy1 = (y1 + cy1) / 2f;
cx2 = (x2 + cx2) / 2f;
cy2 = (y2 + cy2) / 2f;
double ax = (cx1 + cx) / 2f, ay = (cy1 + cy) / 2f;
double 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 (double[] eqn) {
return solveCubic(eqn, eqn);
}
public static int solveCubic (double[] eqn, double[] res) {
return Crossing.solveCubic(eqn, res);
}
}
@@ -0,0 +1,273 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
/**
* An internal class used to compute crossings.
*/
class CurveCrossingHelper
{
private double[][] coords;
private int[][] rules;
private int[] sizes;
private int[] rulesSizes;
private int[][] offsets;
private List<IntersectPoint> isectPoints = new ArrayList<IntersectPoint>();
public CurveCrossingHelper (double[][] coords, int[] sizes,
int[][] rules, int[] rulesSizes, int[][] offsets) {
this.coords = coords;
this.rules = rules;
this.sizes = sizes;
this.rulesSizes = rulesSizes;
this.offsets = offsets;
}
public IntersectPoint[] findCrossing () {
double[] edge1 = new double[8];
double[] edge2 = new double[8];
double[] points = new double[6];
double[] params = new double[6];
double[] mp1 = new double[2];
double[] cp1 = new double[2];
double[] mp2 = new double[2];
double[] cp2 = new double[2];
int rule1, rule2, endIndex1, endIndex2;
int ipCount = 0;
for (int i = 0; i < rulesSizes[0]; i++) {
rule1 = rules[0][i];
endIndex1 = getCurrentEdge(0, i, edge1, mp1, cp1);
for (int j = 0; j < rulesSizes[1]; j++) {
ipCount = 0;
rule2 = rules[1][j];
endIndex2 = getCurrentEdge(1, j, edge2, mp2, cp2);
if (((rule1 == PathIterator.SEG_LINETO) || (rule1 == PathIterator.SEG_CLOSE)) &&
((rule2 == PathIterator.SEG_LINETO) || (rule2 == PathIterator.SEG_CLOSE))) {
ipCount = GeometryUtil.intersectLinesWithParams(
edge1[0], edge1[1], edge1[2], edge1[3],
edge2[0], edge2[1], edge2[2], edge2[3], params);
if (ipCount != 0) {
points[0] = GeometryUtil.line(params[0], edge1[0], edge1[2]);
points[1] = GeometryUtil.line(params[0], edge1[1], edge1[3]);
}
} else if (((rule1 == PathIterator.SEG_LINETO) ||
(rule1 == PathIterator.SEG_CLOSE)) &&
(rule2 == PathIterator.SEG_QUADTO)) {
ipCount = GeometryUtil.intersectLineAndQuad(
edge1[0], edge1[1], edge1[2], edge1[3],
edge2[0], edge2[1], edge2[2], edge2[3], edge2[4], edge2[5], params);
for (int k = 0; k < ipCount; k++) {
points[2 * k] = GeometryUtil.line(params[2 * k], edge1[0], edge1[2]);
points[2 * k + 1] = GeometryUtil.line(params[2 * k], edge1[1], edge1[3]);
}
} else if (rule1 == PathIterator.SEG_QUADTO &&
(rule2 == PathIterator.SEG_LINETO || rule2 == PathIterator.SEG_CLOSE)) {
ipCount = GeometryUtil.intersectLineAndQuad(
edge2[0], edge2[1], edge2[2], edge2[3],
edge1[0], edge1[1], edge1[2], edge1[3], edge1[4], edge1[5], params);
for (int k = 0; k < ipCount; k++) {
points[2 * k] = GeometryUtil.line(params[2 * k + 1], edge2[0], edge2[2]);
points[2 * k + 1] = GeometryUtil.line(
params[2 * k + 1], edge2[1], edge2[3]);
}
} else if ((rule1 == PathIterator.SEG_CUBICTO) &&
((rule2 == PathIterator.SEG_LINETO) ||
(rule2 == PathIterator.SEG_CLOSE))) {
ipCount = GeometryUtil.intersectLineAndCubic(
edge1[0], edge1[1], edge1[2], edge1[3], edge1[4], edge1[5], edge1[6],
edge1[7], edge2[0], edge2[1], edge2[2], edge2[3], params);
for (int k = 0; k < ipCount; k++) {
points[2 * k] = GeometryUtil.line(params[2 * k + 1], edge2[0], edge2[2]);
points[2 * k + 1] = GeometryUtil.line(
params[2 * k + 1], edge2[1], edge2[3]);
}
} else if (((rule1 == PathIterator.SEG_LINETO) ||
(rule1 == PathIterator.SEG_CLOSE)) &&
(rule2 == PathIterator.SEG_CUBICTO)) {
ipCount = GeometryUtil.intersectLineAndCubic(
edge1[0], edge1[1], edge1[2], edge1[3], edge2[0], edge2[1],
edge2[2], edge2[3], edge2[4], edge2[5], edge2[6], edge2[7], params);
for (int k = 0; k < ipCount; k++) {
points[2 * k] = GeometryUtil.line(params[2 * k], edge1[0], edge1[2]);
points[2 * k + 1] = GeometryUtil.line(params[2 * k], edge1[1], edge1[3]);
}
} else if ((rule1 == PathIterator.SEG_QUADTO) &&
(rule2 == PathIterator.SEG_QUADTO)) {
ipCount = GeometryUtil.intersectQuads(
edge1[0], edge1[1], edge1[2], edge1[3], edge1[4], edge1[5],
edge2[0], edge2[1], edge2[2], edge2[3], edge2[4], edge2[5], params);
for (int k = 0; k < ipCount; k++) {
points[2 * k] = GeometryUtil.quad(
params[2 * k], edge1[0], edge1[2], edge1[4]);
points[2 * k + 1] = GeometryUtil.quad(
params[2 * k], edge1[1], edge1[3], edge1[5]);
}
} else if ((rule1 == PathIterator.SEG_QUADTO) &&
(rule2 == PathIterator.SEG_CUBICTO)) {
ipCount = GeometryUtil.intersectQuadAndCubic(
edge1[0], edge1[1], edge1[2], edge1[3], edge1[4], edge1[5],
edge2[0], edge2[1], edge2[2], edge2[3], edge2[4], edge2[5],
edge2[6], edge2[7], params);
for (int k = 0; k < ipCount; k++) {
points[2 * k] = GeometryUtil.quad(
params[2 * k], edge1[0], edge1[2], edge1[4]);
points[2 * k + 1] = GeometryUtil.quad(
params[2 * k], edge1[1], edge1[3], edge1[5]);
}
} else if ((rule1 == PathIterator.SEG_CUBICTO) &&
(rule2 == PathIterator.SEG_QUADTO)) {
ipCount = GeometryUtil.intersectQuadAndCubic(
edge2[0], edge2[1], edge2[2], edge2[3], edge2[4], edge2[5],
edge1[0], edge1[1], edge1[2], edge1[3], edge1[4], edge1[5],
edge2[6], edge2[7], params);
for (int k = 0; k < ipCount; k++) {
points[2 * k] = GeometryUtil.quad(
params[2 * k + 1], edge2[0], edge2[2], edge2[4]);
points[2 * k + 1] = GeometryUtil.quad(
params[2 * k + 1], edge2[1], edge2[3], edge2[5]);
}
} else if ((rule1 == PathIterator.SEG_CUBICTO) &&
(rule2 == PathIterator.SEG_CUBICTO)) {
ipCount = GeometryUtil.intersectCubics(
edge1[0], edge1[1], edge1[2], edge1[3], edge1[4], edge1[5], edge1[6],
edge1[7], edge2[0], edge2[1], edge2[2], edge2[3], edge2[4], edge2[5],
edge2[6], edge2[7], params);
for (int k = 0; k < ipCount; k++) {
points[2 * k] = GeometryUtil.cubic(
params[2 * k], edge1[0], edge1[2], edge1[4], edge1[6]);
points[2 * k + 1] = GeometryUtil.cubic(
params[2 * k], edge1[1], edge1[3], edge1[5], edge1[7]);
}
}
endIndex1 = i;
endIndex2 = j;
int begIndex1 = i - 1;
int begIndex2 = j - 1;
for (int k = 0; k < ipCount; k++) {
IntersectPoint ip = null;
if (!containsPoint(points[2 * k], points[2 * k + 1])) {
for (Iterator<IntersectPoint> iter = isectPoints.iterator();
iter.hasNext();) {
ip = iter.next();
if ((begIndex1 == ip.getBegIndex(true)) &&
(endIndex1 == ip.getEndIndex(true))) {
if (ip.getParam(true) > params[2 * k]) {
endIndex1 = -(isectPoints.indexOf(ip) + 1);
ip.setBegIndex1(-(isectPoints.size() + 1));
} else {
begIndex1 = -(isectPoints.indexOf(ip) + 1);
ip.setEndIndex1(-(isectPoints.size() + 1));
}
}
if ((begIndex2 == ip.getBegIndex(false)) &&
(endIndex2 == ip.getEndIndex(false))) {
if (ip.getParam(false) > params[2 * k + 1]) {
endIndex2 = -(isectPoints.indexOf(ip) + 1);
ip.setBegIndex2(-(isectPoints.size() + 1));
} else {
begIndex2 = -(isectPoints.indexOf(ip) + 1);
ip.setEndIndex2(-(isectPoints.size() + 1));
}
}
}
if (rule1 == PathIterator.SEG_CLOSE) {
rule1 = PathIterator.SEG_LINETO;
}
if (rule2 == PathIterator.SEG_CLOSE) {
rule2 = PathIterator.SEG_LINETO;
}
isectPoints.add(new IntersectPoint(
begIndex1, endIndex1, rule1, i, begIndex2, endIndex2,
rule2, j, points[2 * k], points[2 * k + 1],
params[2 * k], params[2 * k + 1]));
}
}
}
}
return isectPoints.toArray(new IntersectPoint[isectPoints.size()]);
}
private int getCurrentEdge (int areaIndex, int index, double[] c, double[] mp, double[] cp) {
int endIndex = 0;
switch (rules[areaIndex][index]) {
case PathIterator.SEG_MOVETO:
cp[0] = mp[0] = coords[areaIndex][offsets[areaIndex][index]];
cp[1] = mp[1] = coords[areaIndex][offsets[areaIndex][index] + 1];
break;
case PathIterator.SEG_LINETO:
c[0] = cp[0];
c[1] = cp[1];
cp[0] = c[2] = coords[areaIndex][offsets[areaIndex][index]];
cp[1] = c[3] = coords[areaIndex][offsets[areaIndex][index] + 1];
endIndex = 0;
break;
case PathIterator.SEG_QUADTO:
c[0] = cp[0];
c[1] = cp[1];
c[2] = coords[areaIndex][offsets[areaIndex][index]];
c[3] = coords[areaIndex][offsets[areaIndex][index] + 1];
cp[0] = c[4] = coords[areaIndex][offsets[areaIndex][index] + 2];
cp[1] = c[5] = coords[areaIndex][offsets[areaIndex][index] + 3];
endIndex = 2;
break;
case PathIterator.SEG_CUBICTO:
c[0] = cp[0];
c[1] = cp[1];
c[2] = coords[areaIndex][offsets[areaIndex][index]];
c[3] = coords[areaIndex][offsets[areaIndex][index] + 1];
c[4] = coords[areaIndex][offsets[areaIndex][index] + 2];
c[5] = coords[areaIndex][offsets[areaIndex][index] + 3];
cp[0] = c[6] = coords[areaIndex][offsets[areaIndex][index] + 4];
cp[1] = c[7] = coords[areaIndex][offsets[areaIndex][index] + 5];
endIndex = 4;
break;
case PathIterator.SEG_CLOSE:
c[0] = cp[0];
c[1] = cp[1];
cp[0] = c[2] = mp[0];
cp[1] = c[3] = mp[1];
if (offsets[areaIndex][index] >= sizes[areaIndex]) {
endIndex = -sizes[areaIndex];
} else {
endIndex = 0;
}
break;
}
return offsets[areaIndex][index] + endIndex;
}
private boolean containsPoint (double x, double y) {
IntersectPoint ipoint;
for (Iterator<IntersectPoint> i = isectPoints.iterator(); i.hasNext();) {
ipoint = i.next();
if ((Math.abs(ipoint.getX() - x) < Math.pow(10, -6)) &&
(Math.abs(ipoint.getY() - y) < Math.pow(10, -6))) {
return true;
}
}
return false;
}
}
+65
View File
@@ -0,0 +1,65 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.io.Serializable;
/**
* Represents a magnitude in two dimensions.
*/
public class Dimension extends AbstractDimension implements Serializable
{
/** The magnitude in the x-dimension. */
public double width;
/** The magnitude in the y-dimension. */
public double height;
/**
* Creates a dimension with magnitude (0, 0).
*/
public Dimension () {
this(0, 0);
}
/**
* Creates a dimension with the specified width and height.
*/
public Dimension (double width, double height) {
setSize(width, height);
}
/**
* Creates a dimension with width and height equal to the supplied dimension.
*/
public Dimension (IDimension d) {
this(d.getWidth(), d.getHeight());
}
/**
* Sets the magnitudes of this dimension to the specified width and height.
*/
public void setSize (double width, double height) {
this.width = width;
this.height = height;
}
/**
* Sets the magnitudes of this dimension to be equal to the supplied dimension.
*/
public void setSize (IDimension d) {
setSize(d.getWidth(), d.getHeight());
}
@Override // from interface IDimension
public double getWidth () {
return width;
}
@Override // from interface IDimension
public double getHeight () {
return height;
}
}
@@ -0,0 +1,18 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Dimension-related utility methods.
*/
public class Dimensions
{
/**
* Returns a string describing the supplied dimension, of the form <code>widthxheight</code>.
*/
public static String dimenToString (double width, double height) {
return width + "x" + height;
}
}
+66
View File
@@ -0,0 +1,66 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.io.Serializable;
/**
* Represents an ellipse that is described by a framing rectangle.
*/
public class Ellipse extends AbstractEllipse implements Serializable
{
/** The x-coordinate of the framing rectangle. */
public double x;
/** The y-coordinate of the framing rectangle. */
public double y;
/** The width of the framing rectangle. */
public double width;
/** The height of the framing rectangle. */
public double height;
/**
* Creates an ellipse with framing rectangle (0x0+0+0).
*/
public Ellipse () {
}
/**
* Creates an ellipse with the specified framing rectangle.
*/
public Ellipse (double x, double y, double width, double height) {
setFrame(x, y, width, height);
}
@Override // from interface IRectangularShape
public double getX () {
return x;
}
@Override // from interface IRectangularShape
public double getY () {
return y;
}
@Override // from interface IRectangularShape
public double getWidth () {
return width;
}
@Override // from interface IRectangularShape
public double getHeight () {
return height;
}
@Override // from RectangularShape
public void setFrame (double x, double y, double width, double height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
@@ -0,0 +1,233 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.NoSuchElementException;
/**
* A path iterator that flattens curves.
*/
class FlatteningPathIterator implements PathIterator
{
public FlatteningPathIterator (PathIterator path, double flatness) {
this(path, flatness, BUFFER_LIMIT);
}
public FlatteningPathIterator (PathIterator path, double flatness, int limit) {
if (flatness < 0) {
throw new IllegalArgumentException("Flatness is less then zero");
}
if (limit < 0) {
throw new IllegalArgumentException("Limit is less then zero");
}
if (path == null) {
throw new NullPointerException("Path is null");
}
this.p = path;
this.flatness = flatness;
this.flatness2 = flatness * flatness;
this.bufLimit = limit;
this.bufSize = Math.min(bufLimit, BUFFER_SIZE);
this.buf = new double[bufSize];
this.bufIndex = bufSize;
}
public double getFlatness () {
return flatness;
}
public int getRecursionLimit () {
return bufLimit;
}
@Override
// from interface PathIterator
public int getWindingRule () {
return p.getWindingRule();
}
@Override
// from interface PathIterator
public boolean isDone () {
return bufEmpty && p.isDone();
}
@Override
// from interface PathIterator
public void next () {
if (bufEmpty) {
p.next();
}
}
@Override
// from interface PathIterator
public int currentSegment (double[] coords) {
if (isDone()) {
throw new NoSuchElementException("Iterator out of bounds");
}
evaluate();
int type = bufType;
if (type != SEG_CLOSE) {
coords[0] = px;
coords[1] = py;
if (type != SEG_MOVETO) {
type = SEG_LINETO;
}
}
return type;
}
/** 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. */
protected void evaluate () {
if (bufEmpty) {
bufType = p.currentSegment(coords);
}
switch (bufType) {
case SEG_MOVETO:
case SEG_LINETO:
px = coords[0];
py = coords[1];
break;
case SEG_QUADTO:
if (bufEmpty) {
bufIndex -= 6;
buf[bufIndex + 0] = px;
buf[bufIndex + 1] = py;
System.arraycopy(coords, 0, buf, bufIndex + 2, 4);
bufSubdiv = 0;
}
while (bufSubdiv < bufLimit) {
if (QuadCurves.getFlatnessSq(buf, bufIndex) < flatness2) {
break;
}
// Realloc buffer
if (bufIndex <= 4) {
double[] tmp = new double[bufSize + BUFFER_CAPACITY];
System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize
- bufIndex);
buf = tmp;
bufSize += BUFFER_CAPACITY;
bufIndex += BUFFER_CAPACITY;
}
QuadCurves.subdivide(buf, bufIndex, buf, bufIndex - 4, buf, bufIndex);
bufIndex -= 4;
bufSubdiv++;
}
bufIndex += 4;
px = buf[bufIndex];
py = buf[bufIndex + 1];
bufEmpty = (bufIndex == bufSize - 2);
if (bufEmpty) {
bufIndex = bufSize;
bufType = SEG_LINETO;
}
break;
case SEG_CUBICTO:
if (bufEmpty) {
bufIndex -= 8;
buf[bufIndex + 0] = px;
buf[bufIndex + 1] = py;
System.arraycopy(coords, 0, buf, bufIndex + 2, 6);
bufSubdiv = 0;
}
while (bufSubdiv < bufLimit) {
if (CubicCurves.getFlatnessSq(buf, bufIndex) < flatness2) {
break;
}
// Realloc buffer
if (bufIndex <= 6) {
double[] tmp = new double[bufSize + BUFFER_CAPACITY];
System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize
- bufIndex);
buf = tmp;
bufSize += BUFFER_CAPACITY;
bufIndex += BUFFER_CAPACITY;
}
CubicCurves.subdivide(buf, bufIndex, buf, bufIndex - 6, buf, bufIndex);
bufIndex -= 6;
bufSubdiv++;
}
bufIndex += 6;
px = buf[bufIndex];
py = buf[bufIndex + 1];
bufEmpty = (bufIndex == bufSize - 2);
if (bufEmpty) {
bufIndex = bufSize;
bufType = SEG_LINETO;
}
break;
}
}
/** The type of current segment to be flat */
private int bufType;
/** The curve subdivision limit */
private int bufLimit;
/** The current points buffer size */
private int bufSize;
/** The inner cursor position in points buffer */
private int bufIndex;
/** The current subdivision count */
private int bufSubdiv;
/** The points buffer */
private double[] buf;
/** The indicator of empty points buffer */
private boolean bufEmpty = true;
/** The source PathIterator */
private PathIterator p;
/** The flatness of new path */
private double flatness;
/** The square of flatness */
private double flatness2;
/** The x coordinate of previous path segment */
private double px;
/** The y coordinate of previous path segment */
private double py;
/** The tamporary buffer for getting points from PathIterator */
private double[] coords = new double[6];
/** The default points buffer size */
private static final int BUFFER_SIZE = 16;
/** The default curve subdivision limit */
private static final int BUFFER_LIMIT = 16;
/** The points buffer capacity */
private static final int BUFFER_CAPACITY = 16;
}
@@ -0,0 +1,483 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Various geometry utility methods.
*/
public class GeometryUtil
{
public static final double EPSILON = Math.pow(10, -14);
public static int intersectLinesWithParams (double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4,
double[] params) {
double dx = x4 - x3;
double dy = y4 - y3;
double d = dx * (y2 - y1) - dy * (x2 - x1);
// double comparison
if (Math.abs(d) < EPSILON) {
return 0;
}
params[0] = (-dx * (y1 - y3) + dy * (x1 - x3)) / d;
if (dx != 0) {
params[1] = (line(params[0], x1, x2) - x3) / dx;
} else if (dy != 0) {
params[1] = (line(params[0], y1, y2) - y3) / dy;
} else {
params[1] = 0f;
}
if (params[0] >= 0 && params[0] <= 1 && params[1] >= 0 && params[1] <= 1) {
return 1;
}
return 0;
}
/**
* Checks whether line (x1, y1) - (x2, y2) and line (x3, y3) - (x4, y4) intersect. If lines
* intersect then the result parameters are saved to point array. The size of {@code point}
* must be at least 2.
*
* @return 1 if two lines intersect in the defined interval, otherwise 0.
*/
public static int intersectLines (double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4, double[] point) {
double A1 = -(y2 - y1);
double B1 = (x2 - x1);
double C1 = x1 * y2 - x2 * y1;
double A2 = -(y4 - y3);
double B2 = (x4 - x3);
double C2 = x3 * y4 - x4 * y3;
double coefParallel = A1 * B2 - A2 * B1;
// double comparison
if (x3 == x4 && y3 == y4 && (A1 * x3 + B1 * y3 + C1 == 0) && (x3 >= Math.min(x1, x2)) &&
(x3 <= Math.max(x1, x2)) && (y3 >= Math.min(y1, y2)) && (y3 <= Math.max(y1, y2))) {
return 1;
}
if (Math.abs(coefParallel) < EPSILON) {
return 0;
}
point[0] = (B1 * C2 - B2 * C1) / coefParallel;
point[1] = (A2 * C1 - A1 * C2) / coefParallel;
if (point[0] >= Math.min(x1, x2) && point[0] >= Math.min(x3, x4) &&
point[0] <= Math.max(x1, x2) && point[0] <= Math.max(x3, x4) &&
point[1] >= Math.min(y1, y2) && point[1] >= Math.min(y3, y4) &&
point[1] <= Math.max(y1, y2) && point[1] <= Math.max(y3, y4)) {
return 1;
}
return 0;
}
/**
* Checks whether there is intersection of the line (x1, y1) - (x2, y2) and the quad curve
* (qx1, qy1) - (qx2, qy2) - (qx3, qy3). The parameters of the intersection area saved to
* {@code params}. Therefore {@code params} must be of length at least 4.
*
* @return the number of roots that lie in the defined interval.
*/
public static int intersectLineAndQuad (double x1, double y1, double x2, double y2,
double qx1, double qy1, double qx2, double qy2,
double qx3, double qy3, double[] params) {
double[] eqn = new double[3];
double[] t = new double[2];
double[] s = new double[2];
double dy = y2 - y1;
double dx = x2 - x1;
int quantity = 0;
int count = 0;
eqn[0] = dy * (qx1 - x1) - dx * (qy1 - y1);
eqn[1] = 2 * dy * (qx2 - qx1) - 2 * dx * (qy2 - qy1);
eqn[2] = dy * (qx1 - 2 * qx2 + qx3) - dx * (qy1 - 2 * qy2 + qy3);
if ((count = Crossing.solveQuad(eqn, t)) == 0) {
return 0;
}
for (int i = 0; i < count; i++) {
if (dx != 0) {
s[i] = (quad(t[i], qx1, qx2, qx3) - x1) / dx;
} else if (dy != 0) {
s[i] = (quad(t[i], qy1, qy2, qy3) - y1) / dy;
} else {
s[i] = 0f;
}
if (t[i] >= 0 && t[i] <= 1 && s[i] >= 0 && s[i] <= 1) {
params[2 * quantity] = t[i];
params[2 * quantity + 1] = s[i];
++quantity;
}
}
return quantity;
}
/**
* Checks whether the line (x1, y1) - (x2, y2) and the cubic curve (cx1, cy1) - (cx2, cy2) -
* (cx3, cy3) - (cx4, cy4) intersect. The points of intersection are saved to {@code points}.
* Therefore {@code points} must be of length at least 6.
*
* @return the numbers of roots that lie in the defined interval.
*/
public static int intersectLineAndCubic (double x1, double y1, double x2, double y2,
double cx1, double cy1, double cx2, double cy2,
double cx3, double cy3, double cx4, double cy4,
double[] params) {
double[] eqn = new double[4];
double[] t = new double[3];
double[] s = new double[3];
double dy = y2 - y1;
double dx = x2 - x1;
int quantity = 0;
int count = 0;
eqn[0] = (cy1 - y1) * dx + (x1 - cx1) * dy;
eqn[1] = -3 * (cy1 - cy2) * dx + 3 * (cx1 - cx2) * dy;
eqn[2] = (3 * cy1 - 6 * cy2 + 3 * cy3) * dx - (3 * cx1 - 6 * cx2 + 3 * cx3) * dy;
eqn[3] = (-3 * cy1 + 3 * cy2 - 3 * cy3 + cy4) * dx +
(3 * cx1 - 3 * cx2 + 3 * cx3 - cx4) * dy;
if ((count = Crossing.solveCubic(eqn, t)) == 0) {
return 0;
}
for (int i = 0; i < count; i++) {
if (dx != 0) {
s[i] = (cubic(t[i], cx1, cx2, cx3, cx4) - x1) / dx;
} else if (dy != 0) {
s[i] = (cubic(t[i], cy1, cy2, cy3, cy4) - y1) / dy;
} else {
s[i] = 0f;
}
if (t[i] >= 0 && t[i] <= 1 && s[i] >= 0 && s[i] <= 1) {
params[2 * quantity] = t[i];
params[2 * quantity + 1] = s[i];
++quantity;
}
}
return quantity;
}
/**
* Checks whether two quads (x1, y1) - (x2, y2) - (x3, y3) and (qx1, qy1) - (qx2, qy2) - (qx3,
* qy3) intersect. The result is saved to {@code params}. Thus {@code params} must be of length
* at least 4.
*
* @return the number of roots that lie in the interval.
*/
public static int intersectQuads (double x1, double y1, double x2, double y2,
double x3, double y3,
double qx1, double qy1, double qx2, double qy2,
double qx3, double qy3, double[] params) {
double[] initParams = new double[2];
double[] xCoefs1 = new double[3];
double[] yCoefs1 = new double[3];
double[] xCoefs2 = new double[3];
double[] yCoefs2 = new double[3];
int quantity = 0;
xCoefs1[0] = x1 - 2 * x2 + x3;
xCoefs1[1] = -2 * x1 + 2 * x2;
xCoefs1[2] = x1;
yCoefs1[0] = y1 - 2 * y2 + y3;
yCoefs1[1] = -2 * y1 + 2 * y2;
yCoefs1[2] = y1;
xCoefs2[0] = qx1 - 2 * qx2 + qx3;
xCoefs2[1] = -2 * qx1 + 2 * qx2;
xCoefs2[2] = qx1;
yCoefs2[0] = qy1 - 2 * qy2 + qy3;
yCoefs2[1] = -2 * qy1 + 2 * qy2;
yCoefs2[2] = qy1;
// initialize params[0] and params[1]
params[0] = params[1] = 0.25f;
quadNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, initParams);
if (initParams[0] <= 1 && initParams[0] >= 0 && initParams[1] >= 0 && initParams[1] <= 1) {
params[2 * quantity] = initParams[0];
params[2 * quantity + 1] = initParams[1];
++quantity;
}
// initialize params
params[0] = params[1] = 0.75f;
quadNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params);
if (initParams[0] <= 1 && initParams[0] >= 0 && initParams[1] >= 0 && initParams[1] <= 1) {
params[2 * quantity] = initParams[0];
params[2 * quantity + 1] = initParams[1];
++quantity;
}
return quantity;
}
/**
* Checks whether the quad (x1, y1) - (x2, y2) - (x3, y3) and the cubic (cx1, cy1) - (cx2, cy2)
* - (cx3, cy3) - (cx4, cy4) curves intersect. The points of the intersection are saved to
* {@code params}. Thus {@code params} must be of length at least 6.
*
* @return the number of intersection points that lie in the interval.
*/
public static int intersectQuadAndCubic (double qx1, double qy1, double qx2, double qy2,
double qx3, double qy3, double cx1, double cy1,
double cx2, double cy2, double cx3, double cy3,
double cx4, double cy4, double[] params) {
int quantity = 0;
double[] initParams = new double[3];
double[] xCoefs1 = new double[3];
double[] yCoefs1 = new double[3];
double[] xCoefs2 = new double[4];
double[] yCoefs2 = new double[4];
xCoefs1[0] = qx1 - 2 * qx2 + qx3;
xCoefs1[1] = 2 * qx2 - 2 * qx1;
xCoefs1[2] = qx1;
yCoefs1[0] = qy1 - 2 * qy2 + qy3;
yCoefs1[1] = 2 * qy2 - 2 * qy1;
yCoefs1[2] = qy1;
xCoefs2[0] = -cx1 + 3 * cx2 - 3 * cx3 + cx4;
xCoefs2[1] = 3 * cx1 - 6 * cx2 + 3 * cx3;
xCoefs2[2] = -3 * cx1 + 3 * cx2;
xCoefs2[3] = cx1;
yCoefs2[0] = -cy1 + 3 * cy2 - 3 * cy3 + cy4;
yCoefs2[1] = 3 * cy1 - 6 * cy2 + 3 * cy3;
yCoefs2[2] = -3 * cy1 + 3 * cy2;
yCoefs2[3] = cy1;
// initialize params[0] and params[1]
params[0] = params[1] = 0.25f;
quadAndCubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, initParams);
if (initParams[0] <= 1 && initParams[0] >= 0 && initParams[1] >= 0 && initParams[1] <= 1) {
params[2 * quantity] = initParams[0];
params[2 * quantity + 1] = initParams[1];
++quantity;
}
// initialize params
params[0] = params[1] = 0.5f;
quadAndCubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params);
if (initParams[0] <= 1 && initParams[0] >= 0 && initParams[1] >= 0 && initParams[1] <= 1) {
params[2 * quantity] = initParams[0];
params[2 * quantity + 1] = initParams[1];
++quantity;
}
params[0] = params[1] = 0.75f;
quadAndCubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params);
if (initParams[0] <= 1 && initParams[0] >= 0 && initParams[1] >= 0 && initParams[1] <= 1) {
params[2 * quantity] = initParams[0];
params[2 * quantity + 1] = initParams[1];
++quantity;
}
return quantity;
}
/**
* Checks whether two cubic curves (x1, y1) - (x2, y2) - (x3, y3) - (x4, y4) and (cx1, cy1) -
* (cx2, cy2) - (cx3, cy3) - (cx4, cy4) intersect. The result is saved to {@code params}. Thus
* {@code params} must be of length at least 6.
*
* @return the number of intersection points that lie in the interval.
*/
public static int intersectCubics (double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4,
double cx1, double cy1, double cx2, double cy2,
double cx3, double cy3, double cx4, double cy4,
double[] params) {
int quantity = 0;
double[] initParams = new double[3];
double[] xCoefs1 = new double[4];
double[] yCoefs1 = new double[4];
double[] xCoefs2 = new double[4];
double[] yCoefs2 = new double[4];
xCoefs1[0] = -x1 + 3 * x2 - 3 * x3 + x4;
xCoefs1[1] = 3 * x1 - 6 * x2 + 3 * x3;
xCoefs1[2] = -3 * x1 + 3 * x2;
xCoefs1[3] = x1;
yCoefs1[0] = -y1 + 3 * y2 - 3 * y3 + y4;
yCoefs1[1] = 3 * y1 - 6 * y2 + 3 * y3;
yCoefs1[2] = -3 * y1 + 3 * y2;
yCoefs1[3] = y1;
xCoefs2[0] = -cx1 + 3 * cx2 - 3 * cx3 + cx4;
xCoefs2[1] = 3 * cx1 - 6 * cx2 + 3 * cx3;
xCoefs2[2] = -3 * cx1 + 3 * cx2;
xCoefs2[3] = cx1;
yCoefs2[0] = -cy1 + 3 * cy2 - 3 * cy3 + cy4;
yCoefs2[1] = 3 * cy1 - 6 * cy2 + 3 * cy3;
yCoefs2[2] = -3 * cy1 + 3 * cy2;
yCoefs2[3] = cy1;
// TODO
params[0] = params[1] = 0.25f;
cubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, initParams);
if (initParams[0] <= 1 && initParams[0] >= 0 && initParams[1] >= 0 && initParams[1] <= 1) {
params[2 * quantity] = initParams[0];
params[2 * quantity + 1] = initParams[1];
++quantity;
}
params[0] = params[1] = 0.5f;
cubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params);
if (initParams[0] <= 1 && initParams[0] >= 0 && initParams[1] >= 0 && initParams[1] <= 1) {
params[2 * quantity] = initParams[0];
params[2 * quantity + 1] = initParams[1];
++quantity;
}
params[0] = params[1] = 0.75f;
cubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params);
if (initParams[0] <= 1 && initParams[0] >= 0 && initParams[1] >= 0 && initParams[1] <= 1) {
params[2 * quantity] = initParams[0];
params[2 * quantity + 1] = initParams[1];
++quantity;
}
return quantity;
}
public static double line (double t, double x1, double x2) {
return x1 * (1f - t) + x2 * t;
}
public static double quad (double t, double x1, double x2, double x3) {
return x1 * (1f - t) * (1f - t) + 2f * x2 * t * (1f - t) + x3 * t * t;
}
public static double cubic (double t, double x1, double x2, double x3, double x4) {
return x1 * (1f - t) * (1f - t) * (1f - t) + 3f * x2 * (1f - t) * (1f - t) * t + 3f * x3 *
(1f - t) * t * t + x4 * t * t * t;
}
// x, y - the coordinates of new vertex
// t0 - ?
public static void subQuad (double[] coef, double t0, boolean left) {
if (left) {
coef[2] = (1 - t0) * coef[0] + t0 * coef[2];
coef[3] = (1 - t0) * coef[1] + t0 * coef[3];
} else {
coef[2] = (1 - t0) * coef[2] + t0 * coef[4];
coef[3] = (1 - t0) * coef[3] + t0 * coef[5];
}
}
public static void subCubic (double[] coef, double t0, boolean left) {
if (left) {
coef[2] = (1 - t0) * coef[0] + t0 * coef[2];
coef[3] = (1 - t0) * coef[1] + t0 * coef[3];
} else {
coef[4] = (1 - t0) * coef[4] + t0 * coef[6];
coef[5] = (1 - t0) * coef[5] + t0 * coef[7];
}
}
private static void cubicNewton (double[] xCoefs1, double[] yCoefs1,
double[] xCoefs2, double[] yCoefs2, double[] params) {
double t = 0f, s = 0f;
double t1 = params[0];
double s1 = params[1];
double d, dt, ds;
while (Math.sqrt((t - t1) * (t - t1) + (s - s1) * (s - s1)) > EPSILON) {
d = -(3 * t * t * xCoefs1[0] + 2 * t * xCoefs1[1] + xCoefs1[2]) *
(3 * s * s * yCoefs2[0] + 2 * s * yCoefs2[1] + yCoefs2[2]) +
(3 * t * t * yCoefs1[0] + 2 * t * yCoefs1[1] + yCoefs1[2]) *
(3 * s * s * xCoefs2[0] + 2 * s * xCoefs2[1] + xCoefs2[2]);
dt = (t * t * t * xCoefs1[0] + t * t * xCoefs1[1] + t * xCoefs1[2] + xCoefs1[3] -
s * s * s * xCoefs2[0] - s * s * xCoefs2[1] - s * xCoefs2[2] - xCoefs2[3]) *
(-3 * s * s * yCoefs2[0] - 2 * s * yCoefs2[1] - yCoefs2[2]) +
(t * t * t * yCoefs1[0] + t * t * yCoefs1[1] + t * yCoefs1[2] + yCoefs1[3] -
s * s * s * yCoefs2[0] - s * s * yCoefs2[1] - s * yCoefs2[2] - yCoefs2[3]) *
(3 * s * s * xCoefs2[0] + 2 * s * xCoefs2[1] + xCoefs2[2]);
ds = (3 * t * t * xCoefs1[0] + 2 * t * xCoefs1[1] + xCoefs1[2]) *
(t * t * t * yCoefs1[0] + t * t * yCoefs1[1] + t * yCoefs1[2] + yCoefs1[3] -
s * s * s * yCoefs2[0] - s * s * yCoefs2[1] - s * yCoefs2[2] - yCoefs2[3]) -
(3 * t * t * yCoefs1[0] + 2 * t * yCoefs1[1] + yCoefs1[2]) *
(t * t * t * xCoefs1[0] + t * t * xCoefs1[1] + t * xCoefs1[2] + xCoefs1[3] -
s * s * s * xCoefs2[0] - s * s * xCoefs2[1] - s * xCoefs2[2] - xCoefs2[3]);
t1 = t - dt / d;
s1 = s - ds / d;
}
params[0] = t1;
params[1] = s1;
}
private static void quadAndCubicNewton (double xCoefs1[], double yCoefs1[],
double xCoefs2[], double yCoefs2[], double[] params) {
double t = 0f, s = 0f;
double t1 = params[0];
double s1 = params[1];
double d, dt, ds;
while (Math.sqrt((t - t1) * (t - t1) + (s - s1) * (s - s1)) > EPSILON) {
d = -(2 * t * xCoefs1[0] + xCoefs1[1]) *
(3 * s * s * yCoefs2[0] + 2 * s * yCoefs2[1] + yCoefs2[2]) +
(2 * t * yCoefs1[0] + yCoefs1[1]) *
(3 * s * s * xCoefs2[0] + 2 * s * xCoefs2[1] + xCoefs2[2]);
dt = (t * t * xCoefs1[0] + t * xCoefs1[1] + xCoefs1[2] + -s * s * s * xCoefs2[0] -
s * s * xCoefs2[1] - s * xCoefs2[2] - xCoefs2[3]) *
(-3 * s * s * yCoefs2[0] - 2 * s * yCoefs2[1] - yCoefs2[2]) +
(t * t * yCoefs1[0] + t * yCoefs1[1] + yCoefs1[2] - s * s * s * yCoefs2[0] -
s * s * yCoefs2[1] - s * yCoefs2[2] - yCoefs2[3]) *
(3 * s * s * xCoefs2[0] + 2 * s * xCoefs2[1] + xCoefs2[2]);
ds = (2 * t * xCoefs1[0] + xCoefs1[1]) *
(t * t * yCoefs1[0] + t * yCoefs1[1] + yCoefs1[2] - s * s * s * yCoefs2[0] -
s * s * yCoefs2[1] - s * yCoefs2[2] - yCoefs2[3]) -
(2 * t * yCoefs1[0] + yCoefs1[1]) *
(t * t * xCoefs1[0] + t * xCoefs1[1] + xCoefs1[2] - s * s * s * xCoefs2[0] -
s * s * xCoefs2[1] - s * xCoefs2[2] - xCoefs2[3]);
t1 = t - dt / d;
s1 = s - ds / d;
}
params[0] = t1;
params[1] = s1;
}
private static void quadNewton (double xCoefs1[], double yCoefs1[],
double xCoefs2[], double yCoefs2[], double params[]) {
double t = 0f, s = 0f;
double t1 = params[0];
double s1 = params[1];
double d, dt, ds;
while (Math.sqrt((t - t1) * (t - t1) + (s - s1) * (s - s1)) > EPSILON) {
t = t1;
s = s1;
d = -(2 * t * xCoefs1[0] + xCoefs1[1]) * (2 * s * yCoefs2[0] + yCoefs2[1]) +
(2 * s * xCoefs2[0] + xCoefs2[1]) * (2 * t * yCoefs1[0] + yCoefs1[1]);
dt = -(t * t * xCoefs1[0] + t * xCoefs1[1] + xCoefs1[1] - s * s * xCoefs2[0] -
s * xCoefs2[1] - xCoefs2[2]) * (2 * s * yCoefs2[0] + yCoefs2[1]) +
(2 * s * xCoefs2[0] + xCoefs2[1]) *
(t * t * yCoefs1[0] + t * yCoefs1[1] + yCoefs1[2] - s * s * yCoefs2[0] -
s * yCoefs2[1] - yCoefs2[2]);
ds = (2 * t * xCoefs1[0] + xCoefs1[1]) *
(t * t * yCoefs1[0] + t * yCoefs1[1] + yCoefs1[2] - s * s * yCoefs2[0] -
s * yCoefs2[1] - yCoefs2[2]) - (2 * t * yCoefs1[0] + yCoefs1[1]) *
(t * t * xCoefs1[0] + t * xCoefs1[1] + xCoefs1[2] - s * s * xCoefs2[0] -
s * xCoefs2[1] - xCoefs2[2]);
t1 = t - dt / d;
s1 = s - ds / d;
}
params[0] = t1;
params[1] = s1;
}
}
+56
View File
@@ -0,0 +1,56 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to an {@link Arc}.
*/
public interface IArc extends IRectangularShape, Cloneable
{
/** An arc type indicating a simple, unconnected curve. */
int OPEN = 0;
/** An arc type indicating a closed curve, connected by a straight line from the starting to
* the ending point of the arc. */
int CHORD = 1;
/** An arc type indicating a closed curve, connected by a line from the starting point of the
* arc to the center of the circle defining the arc, and another straight line from that center
* to the ending point of the arc. */
int PIE = 2;
/** Returns the type of this arc: {@link #OPEN}, etc. */
int getArcType ();
/** Returns the starting angle of this arc. */
double getAngleStart ();
/** Returns the angular extent of this arc. */
double getAngleExtent ();
/** Returns the intersection of the ray from the center (defined by the starting angle) and the
* elliptical boundary of the arc. */
Point getStartPoint ();
/** Writes the intersection of the ray from the center (defined by the starting angle) and the
* elliptical boundary of the arc into {@code target}.
* @return the supplied point. */
Point getStartPoint (Point target);
/** Returns the intersection of the ray from the center (defined by the starting angle plus the
* angular extent of the arc) and the elliptical boundary of the arc. */
Point getEndPoint ();
/** Writes the intersection of the ray from the center (defined by the starting angle plus the
* angular extent of the arc) and the elliptical boundary of the arc into {@code target}.
* @return the supplied point. */
Point getEndPoint (Point target);
/** Returns whether the specified angle is within the angular extents of this arc. */
boolean containsAngle (double angle);
/** Returns a mutable copy of this arc. */
Arc clone ();
}
@@ -0,0 +1,61 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to a {@link CubicCurve}.
*/
public interface ICubicCurve extends IShape, Cloneable
{
/** Returns the x-coordinate of the start of this curve. */
double getX1 ();
/** Returns the y-coordinate of the start of this curve. */
double getY1 ();
/** Returns the x-coordinate of the first control point. */
double getCtrlX1 ();
/** Returns the y-coordinate of the first control point. */
double getCtrlY1 ();
/** Returns the x-coordinate of the second control point. */
double getCtrlX2 ();
/** Returns the y-coordinate of the second control point. */
double getCtrlY2 ();
/** Returns the x-coordinate of the end of this curve. */
double getX2 ();
/** Returns the y-coordinate of the end of this curve. */
double 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 ();
}
@@ -0,0 +1,26 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to a {@link Dimension}.
*/
public interface IDimension extends Cloneable
{
/**
* Returns the magnitude in the x-dimension.
*/
double getWidth ();
/**
* Returns the magnitude in the y-dimension.
*/
double getHeight ();
/**
* Returns a mutable copy of this dimension.
*/
Dimension clone ();
}
+14
View File
@@ -0,0 +1,14 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to an {@link Ellipse}.
*/
public interface IEllipse extends IRectangularShape, Cloneable
{
/** Returns a mutable copy of this ellipse. */
Ellipse clone ();
}
+70
View File
@@ -0,0 +1,70 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to a {@link Line}.
*/
public interface ILine extends IShape, Cloneable
{
/** Returns the x-coordinate of the start of this line. */
double getX1 ();
/** Returns the y-coordinate of the start of this line. */
double getY1 ();
/** Returns the x-coordinate of the end of this line. */
double getX2 ();
/** Returns the y-coordinate of the end of this line. */
double getY2 ();
/** Returns a copy of the starting point of this line. */
Point getP1 ();
/** 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. */
Point getP2 ();
/** 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. */
double pointLineDistSq (double px, double py);
/** Returns the square of the distance from the supplied point to the line defined by this line
* segment. */
double pointLineDistSq (IPoint p);
/** Returns the distance from the specified point to the line defined by this line segment. */
double pointLineDist (double px, double py);
/** Returns the distance from the supplied point to the line defined by this line segment. */
double pointLineDist (IPoint p);
/** Returns the square of the distance from the specified point this line segment. */
double pointSegDistSq (double px, double py);
/** Returns the square of the distance from the supplied point this line segment. */
double pointSegDistSq (IPoint p);
/** Returns the distance from the specified point this line segment. */
double pointSegDist (double px, double py);
/** Returns the distance from the supplied point this line segment. */
double pointSegDist (IPoint p);
int relativeCCW (double px, double py);
int relativeCCW (IPoint p);
/** Returns a mutable copy of this line. */
Line clone ();
}
+32
View File
@@ -0,0 +1,32 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to a {@link Point}.
*/
public interface IPoint extends Cloneable
{
/** Returns this point's x-coordinate. */
double getX ();
/** Returns this point's y-coordinate. */
double getY ();
/** Returns the squared Euclidian distance between this point and the specified point. */
double distanceSq (double px, double py);
/** Returns the squared Euclidian distance between this point and the supplied point. */
double distanceSq (IPoint p);
/** Returns the Euclidian distance between this point and the specified point. */
double distance (double px, double py);
/** Returns the Euclidian distance between this point and the supplied point. */
double distance (IPoint p);
/** Returns a mutable copy of this point. */
Point clone ();
}
@@ -0,0 +1,52 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to a {@link QuadCurve}.
*/
public interface IQuadCurve extends IShape, Cloneable
{
/** Returns the x-coordinate of the start of this curve. */
double getX1 ();
/** Returns the y-coordinate of the start of this curve. */
double getY1 ();
/** Returns the x-coordinate of the control point. */
double getCtrlX ();
/** Returns the y-coordinate of the control point. */
double getCtrlY ();
/** Returns the x-coordinate of the end of this curve. */
double getX2 ();
/** Returns the y-coordinate of the end of this curve. */
double 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. */
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 (QuadCurve left, QuadCurve right);
/** Returns a mutable copy of this curve. */
QuadCurve clone ();
}
@@ -0,0 +1,68 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to a {@link Rectangle}.
*/
public interface IRectangle extends IRectangularShape, Cloneable
{
/** 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}. */
int OUT_RIGHT = 4;
/** The bitmask that indicates that a point lies below this rectangle. See {@link #outcode}. */
int OUT_BOTTOM = 8;
/** Returns a copy of this rectangle's upper-left corner. */
Point getLocation ();
/** 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. */
Dimension getSize ();
/** 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 (double rx, double ry, double rw, double rh);
/** 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). */
Rectangle union (IRectangle r);
/** Returns true if the specified line segment intersects this rectangle. */
boolean intersectsLine (double x1, double y1, double x2, double y2);
/** Returns true if the supplied line segment intersects this rectangle. */
boolean intersectsLine (ILine l);
/** Returns a set of flags indicating where the specified point lies in relation to the bounds
* of this rectangle. See {@link #OUT_LEFT}, etc. */
int outcode (double px, double py);
/** 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. */
Rectangle clone ();
}
@@ -0,0 +1,50 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* An interface implemented by {@link Shape} classes whose geometry is defined by a rectangular
* frame. The framing rectangle <em>defines</em> the geometry, but may in some cases differ from
* the <em>bounding</em> rectangle of the shape.
*/
public interface IRectangularShape extends IShape
{
/** Returns the x-coordinate of the upper-left corner of the framing rectangle. */
double getX ();
/** Returns the y-coordinate of the upper-left corner of the framing rectangle. */
double getY ();
/** Returns the width of the framing rectangle. */
double getWidth ();
/** Returns the height of the framing rectangle. */
double getHeight ();
/** Returns the minimum x-coordinate of the framing rectangle. */
double getMinX ();
/** Returns the minimum y-coordinate of the framing rectangle. */
double getMinY ();
/** Returns the maximum x-coordinate of the framing rectangle. */
double getMaxX ();
/** Returns the maximum y-coordinate of the framing rectangle. */
double getMaxY ();
/** Returns the x-coordinate of the center of the framing rectangle. */
double getCenterX ();
/** Returns the y-coordinate of the center of the framing rectangle. */
double getCenterY ();
/** 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);
}
@@ -0,0 +1,20 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Provides read-only access to a {@link RoundRectangle}.
*/
public interface IRoundRectangle extends IRectangularShape, Cloneable
{
/** Returns the width of the corner arc. */
double getArcWidth ();
/** Returns the height of the corner arc. */
double getArcHeight ();
/** Returns a mutable copy of this round rectangle. */
RoundRectangle clone ();
}
+56
View File
@@ -0,0 +1,56 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* An interface provided by all shapes.
*/
public interface IShape
{
/** Returns true if this shape encloses no area. */
boolean isEmpty ();
/** Returns true if this shape contains the specified point. */
boolean contains (double x, double y);
/** Returns true if this shape contains the supplied point. */
boolean contains (IPoint point);
/** Returns true if this shape completely contains the specified rectangle. */
boolean contains (double x, double y, double width, double height);
/** Returns true if this shape completely contains the supplied rectangle. */
boolean contains (IRectangle r);
/** Returns true if this shape intersects the specified rectangle. */
boolean intersects (double x, double y, double width, double height);
/** Returns true if this shape intersects the supplied rectangle. */
boolean intersects (IRectangle r);
/** 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.
*/
PathIterator getPathIterator (AffineTransform at, double flatness);
}
@@ -0,0 +1,20 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* An exception thrown if an operation is performed on a {@link Path} that is in an illegal state
* with respect to the particular operation being performed. For example, appending a segment to a
* path without an initial moveto.
*/
public class IllegalPathStateException extends RuntimeException
{
public IllegalPathStateException () {
}
public IllegalPathStateException (String s) {
super(s);
}
}
@@ -0,0 +1,107 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* An internal helper class that represents the intersection point of two edges.
*/
class IntersectPoint
{
public IntersectPoint (int begIndex1, int endIndex1, int begIndex2, int endIndex2,
double x, double y) {
this.begIndex1 = begIndex1;
this.endIndex1 = endIndex1;
this.begIndex2 = begIndex2;
this.endIndex2 = endIndex2;
this.x = x;
this.y = y;
}
public IntersectPoint (int begIndex1, int endIndex1, int rule1, int ruleIndex1,
int begIndex2, int endIndex2, int rule2, int ruleIndex2,
double x, double y, double param1, double param2) {
this.begIndex1 = begIndex1;
this.endIndex1 = endIndex1;
this.rule1 = rule1;
this.ruleIndex1 = ruleIndex1;
this.param1 = param1;
this.begIndex2 = begIndex2;
this.endIndex2 = endIndex2;
this.rule2 = rule2;
this.ruleIndex2 = ruleIndex2;
this.param2 = param2;
this.x = x;
this.y = y;
}
public int getBegIndex (boolean isCurrentArea) {
return isCurrentArea ? begIndex1 : begIndex2;
}
public int getEndIndex (boolean isCurrentArea) {
return isCurrentArea ? endIndex1 : endIndex2;
}
public int getRuleIndex (boolean isCurrentArea) {
return isCurrentArea ? ruleIndex1 : ruleIndex2;
}
public double getParam (boolean isCurrentArea) {
return isCurrentArea ? param1 : param2;
}
public int getRule (boolean isCurrentArea) {
return isCurrentArea ? rule1 : rule2;
}
public double getX () {
return x;
}
public double getY () {
return y;
}
public void setBegIndex1 (int begIndex) {
this.begIndex1 = begIndex;
}
public void setEndIndex1 (int endIndex) {
this.endIndex1 = endIndex;
}
public void setBegIndex2 (int begIndex) {
this.begIndex2 = begIndex;
}
public void setEndIndex2 (int endIndex) {
this.endIndex2 = endIndex;
}
// the edge begin number of first line
private int begIndex1;
// the edge end number of first line
private int endIndex1;
// the edge rule of first figure
private int rule1;
// the index of the first figure rules array
private int ruleIndex1;
// the parameter value of edge1
private double param1;
// the edge begin number of second line
private int begIndex2;
// the edge end number of second line
private int endIndex2;
// the edge rule of second figure
private int rule2;
// the index of the second figure rules array
private int ruleIndex2;
// the absciss coordinate of the point
private final double x;
// the ordinate coordinate of the point
private final double y;
// the parameter value of edge2
private double param2;
}
+82
View File
@@ -0,0 +1,82 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.io.Serializable;
/**
* Represents a line segment.
*/
public class Line extends AbstractLine implements Serializable
{
/** The x-coordinate of the start of this line segment. */
public double x1;
/** The y-coordinate of the start of this line segment. */
public double y1;
/** The x-coordinate of the end of this line segment. */
public double x2;
/** The y-coordinate of the end of this line segment. */
public double y2;
/**
* Creates a line from (0,0) to (0,0).
*/
public Line () {
}
/**
* Creates a line from (x1,y1), to (x2,y2).
*/
public Line (double x1, double y1, double x2, double y2) {
setLine(x1, y1, x2, y2);
}
/**
* Creates a line from p1 to p2.
*/
public Line (IPoint p1, IPoint p2) {
setLine(p1, p2);
}
/**
* Sets the start and end point of this line to the specified values.
*/
public void setLine (double x1, double y1, double x2, double y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
/**
* Sets the start and end of this line to the specified points.
*/
public void setLine (IPoint p1, IPoint p2) {
setLine(p1.getX(), p1.getY(), p2.getY(), p2.getY());
}
@Override // from interface ILine
public double getX1 () {
return x1;
}
@Override // from interface ILine
public double getY1 () {
return y1;
}
@Override // from interface ILine
public double getX2 () {
return x2;
}
@Override // from interface ILine
public double getY2 () {
return y2;
}
}
+155
View File
@@ -0,0 +1,155 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Line-related utility methods.
*/
public class Lines
{
/**
* Returns true if the specified two line segments intersect.
*/
public static boolean linesIntersect (double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
// A = (x2-x1, y2-y1)
// B = (x3-x1, y3-y1)
// C = (x4-x1, y4-y1)
// D = (x4-x3, y4-y3) = C-B
// E = (x1-x3, y1-y3) = -B
// F = (x2-x3, y2-y3) = A-B
//
// Result is ((AxB) * (AxC) <= 0) and ((DxE) * (DxF) <= 0)
//
// DxE = (C-B)x(-B) = BxB-CxB = BxC
// DxF = (C-B)x(A-B) = CxA-CxB-BxA+BxB = AxB+BxC-AxC
x2 -= x1; // A
y2 -= y1;
x3 -= x1; // B
y3 -= y1;
x4 -= x1; // C
y4 -= y1;
double AvB = x2 * y3 - x3 * y2;
double AvC = x2 * y4 - x4 * y2;
// online
if (AvB == 0 && AvC == 0) {
if (x2 != 0) {
return (x4 * x3 <= 0) ||
((x3 * x2 >= 0) && (x2 > 0 ? x3 <= x2 || x4 <= x2 : x3 >= x2 || x4 >= x2));
}
if (y2 != 0) {
return (y4 * y3 <= 0) ||
((y3 * y2 >= 0) && (y2 > 0 ? y3 <= y2 || y4 <= y2 : y3 >= y2 || y4 >= y2));
}
return false;
}
double BvC = x3 * y4 - x4 * y3;
return (AvB * AvC <= 0) && (BvC * (AvB + BvC - AvC) <= 0);
}
/**
* Returns true if the specified line segment intersects the specified rectangle.
*/
public static boolean lineIntersectsRect (double x1, double y1, double x2, double y2,
double rx, double ry, double rw, double rh) {
double rr = rx + rw, rb = ry + rh;
return (rx <= x1 && x1 <= rr && ry <= y1 && y1 <= rb)
|| (rx <= x2 && x2 <= rr && ry <= y2 && y2 <= rb)
|| linesIntersect(rx, ry, rr, rb, x1, y1, x2, y2)
|| linesIntersect(rr, ry, rx, rb, x1, y1, x2, y2);
}
/**
* Returns the square of the distance from the specified point to the specified line.
*/
public static double pointLineDistSq (double px, double py,
double x1, double y1, double x2, double y2) {
x2 -= x1;
y2 -= y1;
px -= x1;
py -= y1;
double s = px * y2 - py * x2;
return (s * s) / (x2 * x2 + y2 * y2);
}
/**
* Returns the distance from the specified point to the specified line.
*/
public static double pointLineDist (double px, double py,
double x1, double y1, double x2, double y2) {
return Math.sqrt(pointLineDistSq(px, py, x1, y1, x2, y2));
}
/**
* Returns the square of the distance between the specified point and the specified line
* segment.
*/
public static double pointSegDistSq (double px, double py,
double x1, double y1, double x2, double y2) {
// A = (x2 - x1, y2 - y1)
// P = (px - x1, py - y1)
x2 -= x1; // A = (x2, y2)
y2 -= y1;
px -= x1; // P = (px, py)
py -= y1;
double dist;
if (px * x2 + py * y2 <= 0.0) { // P*A
dist = px * px + py * py;
} else {
px = x2 - px; // P = A - P = (x2 - px, y2 - py)
py = y2 - py;
if (px * x2 + py * y2 <= 0.0) { // P*A
dist = px * px + py * py;
} else {
dist = px * y2 - py * x2;
dist = dist * dist / (x2 * x2 + y2 * y2); // pxA/|A|
}
}
if (dist < 0) {
dist = 0;
}
return dist;
}
/**
* Returns the distance between the specified point and the specified line segment.
*/
public static double pointSegDist (double px, double py,
double x1, double y1, double x2, double y2) {
return Math.sqrt(pointSegDistSq(px, py, x1, y1, x2, y2));
}
/**
* Returns an indicator of where the specified point (px,py) lies with respect to the line
* segment from (x1,y1) to (x2,y2).
*
* @see http://download.oracle.com/javase/6/docs/api/java/awt/geom/Line2D.html
*/
public static int relativeCCW (double px, double py,
double x1, double y1, double x2, double y2) {
// A = (x2-x1, y2-y1)
// P = (px-x1, py-y1)
x2 -= x1;
y2 -= y1;
px -= x1;
py -= y1;
double t = px * y2 - py * x2; // PxA
if (t == 0f) {
t = px * x2 + py * y2; // P*A
if (t > 0f) {
px -= x2; // B-A
py -= y2;
t = px * x2 + py * y2; // (P-A)*A
if (t < 0f) {
t = 0f;
}
}
}
return (t < 0f) ? -1 : (t > 0f ? 1 : 0);
}
}
@@ -0,0 +1,16 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* An exception thrown by {@link AffineTransform} when a request for an inverse transform cannot be
* satisfied.
*/
public class NoninvertibleTransformException extends java.lang.Exception
{
public NoninvertibleTransformException (String s) {
super(s);
}
}
+374
View File
@@ -0,0 +1,374 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.util.NoSuchElementException;
/**
* Represents a path constructed from lines and curves and which can contain subpaths.
*/
public final class Path implements IShape, Cloneable
{
/** Specifies the even/odd rule for determining the interior of a path. */
public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
/** Specifies the non-zero rule for determining the interior of a path. */
public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
public Path () {
this(WIND_NON_ZERO, BUFFER_SIZE);
}
public Path (int rule) {
this(rule, BUFFER_SIZE);
}
public Path (int rule, int initialCapacity) {
setWindingRule(rule);
types = new byte[initialCapacity];
points = new double[initialCapacity * 2];
}
public Path (IShape shape) {
this(WIND_NON_ZERO, BUFFER_SIZE);
PathIterator p = shape.getPathIterator(null);
setWindingRule(p.getWindingRule());
append(p, false);
}
public void setWindingRule (int rule) {
if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
throw new IllegalArgumentException("Invalid winding rule value");
}
this.rule = rule;
}
public int getWindingRule () {
return rule;
}
public void moveTo (double x, double y) {
if (typeSize > 0 && types[typeSize - 1] == PathIterator.SEG_MOVETO) {
points[pointSize - 2] = x;
points[pointSize - 1] = y;
} else {
checkBuf(2, false);
types[typeSize++] = PathIterator.SEG_MOVETO;
points[pointSize++] = x;
points[pointSize++] = y;
}
}
public void lineTo (double x, double y) {
checkBuf(2, true);
types[typeSize++] = PathIterator.SEG_LINETO;
points[pointSize++] = x;
points[pointSize++] = y;
}
public void quadTo (double x1, double y1, double x2, double y2) {
checkBuf(4, true);
types[typeSize++] = PathIterator.SEG_QUADTO;
points[pointSize++] = x1;
points[pointSize++] = y1;
points[pointSize++] = x2;
points[pointSize++] = y2;
}
public void curveTo (double x1, double y1, double x2, double y2, double x3, double y3) {
checkBuf(6, true);
types[typeSize++] = PathIterator.SEG_CUBICTO;
points[pointSize++] = x1;
points[pointSize++] = y1;
points[pointSize++] = x2;
points[pointSize++] = y2;
points[pointSize++] = x3;
points[pointSize++] = y3;
}
public void closePath () {
if (typeSize == 0 || types[typeSize - 1] != PathIterator.SEG_CLOSE) {
checkBuf(0, true);
types[typeSize++] = PathIterator.SEG_CLOSE;
}
}
public void append (IShape shape, boolean connect) {
PathIterator p = shape.getPathIterator(null);
append(p, connect);
}
public void append (PathIterator path, boolean connect) {
while (!path.isDone()) {
double[] coords = new double[6];
switch (path.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
if (!connect || typeSize == 0) {
moveTo(coords[0], coords[1]);
} else if (types[typeSize - 1] != PathIterator.SEG_CLOSE &&
points[pointSize - 2] == coords[0] &&
points[pointSize - 1] == coords[1]) {
// we're already here
} else {
lineTo(coords[0], coords[1]);
}
break;
case PathIterator.SEG_LINETO:
lineTo(coords[0], coords[1]);
break;
case PathIterator.SEG_QUADTO:
quadTo(coords[0], coords[1], coords[2], coords[3]);
break;
case PathIterator.SEG_CUBICTO:
curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
break;
case PathIterator.SEG_CLOSE:
closePath();
break;
}
path.next();
connect = false;
}
}
public Point getCurrentPoint () {
if (typeSize == 0) {
return null;
}
int j = pointSize - 2;
if (types[typeSize - 1] == PathIterator.SEG_CLOSE) {
for (int i = typeSize - 2; i > 0; i--) {
int type = types[i];
if (type == PathIterator.SEG_MOVETO) {
break;
}
j -= pointShift[type];
}
}
return new Point(points[j], points[j + 1]);
}
public void reset () {
typeSize = 0;
pointSize = 0;
}
public void transform (AffineTransform t) {
t.transform(points, 0, points, 0, pointSize / 2);
}
public IShape createTransformedShape (AffineTransform t) {
Path p = clone();
if (t != null) {
p.transform(t);
}
return p;
}
@Override // from interface IShape
public Rectangle getBounds () {
return getBounds(new Rectangle());
}
@Override // from interface IShape
public Rectangle getBounds (Rectangle target) {
double rx1, ry1, rx2, ry2;
if (pointSize == 0) {
rx1 = ry1 = rx2 = ry2 = 0f;
} else {
int i = pointSize - 1;
ry1 = ry2 = points[i--];
rx1 = rx2 = points[i--];
while (i > 0) {
double y = points[i--];
double x = points[i--];
if (x < rx1) {
rx1 = x;
} else if (x > rx2) {
rx2 = x;
}
if (y < ry1) {
ry1 = y;
} else if (y > ry2) {
ry2 = y;
}
}
}
target.setBounds(rx1, ry1, rx2 - rx1, ry2 - ry1);
return target;
}
@Override // from interface IShape
public boolean isEmpty () {
// TODO: will this be insanely difficult to do correctly?
return getBounds().isEmpty();
}
@Override // from interface IShape
public boolean contains (double px, double py) {
return isInside(Crossing.crossShape(this, px, py));
}
@Override // from interface IShape
public boolean contains (double rx, double ry, double rw, double rh) {
int cross = Crossing.intersectShape(this, rx, ry, rw, rh);
return cross != Crossing.CROSSING && isInside(cross);
}
@Override // from interface IShape
public boolean intersects (double rx, double ry, double rw, double rh) {
int cross = Crossing.intersectShape(this, rx, ry, rw, rh);
return cross == Crossing.CROSSING || isInside(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 (IRectangle r) {
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform t) {
return new Iterator(this, t);
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform t, double flatness) {
return new FlatteningPathIterator(getPathIterator(t), flatness);
}
@Override
public Path clone () {
try {
Path p = (Path)super.clone();
p.types = types.clone();
p.points = points.clone();
return p;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
/**
* Checks points and types buffer size to add pointCount points. If necessary realloc buffers
* to enlarge size.
*
* @param pointCount the point count to be added in buffer
*/
protected void checkBuf (int pointCount, boolean checkMove) {
if (checkMove && typeSize == 0) {
throw new IllegalPathStateException("First segment must be a SEG_MOVETO");
}
if (typeSize == types.length) {
byte[] tmp = new byte[typeSize + BUFFER_CAPACITY];
System.arraycopy(types, 0, tmp, 0, typeSize);
types = tmp;
}
if (pointSize + pointCount > points.length) {
double[] tmp = new double[pointSize + Math.max(BUFFER_CAPACITY * 2, pointCount)];
System.arraycopy(points, 0, tmp, 0, pointSize);
points = tmp;
}
}
/**
* Checks cross count according to path rule to define is it point inside shape or not.
*
* @param cross the point cross count.
* @return true if point is inside path, or false otherwise.
*/
protected boolean isInside (int cross) {
return (rule == WIND_NON_ZERO) ? Crossing.isInsideNonZero(cross) :
Crossing.isInsideEvenOdd(cross);
}
/** An iterator over a {@link Path}. */
protected static class Iterator implements PathIterator
{
/** The current cursor position in types buffer. */
private int typeIndex;
/** The current cursor position in points buffer. */
private int pointIndex;
/** The source Path object. */
private Path p;
/** The path iterator transformation. */
private AffineTransform t;
Iterator (Path path) {
this(path, null);
}
Iterator (Path path, AffineTransform at) {
this.p = path;
this.t = at;
}
@Override public int getWindingRule () {
return p.getWindingRule();
}
@Override public boolean isDone () {
return typeIndex >= p.typeSize;
}
@Override public void next () {
typeIndex++;
}
@Override public int currentSegment (double[] coords) {
if (isDone()) {
throw new NoSuchElementException("Iterator out of bounds");
}
int type = p.types[typeIndex];
int count = Path.pointShift[type];
System.arraycopy(p.points, pointIndex, coords, 0, count);
if (t != null) {
t.transform(coords, 0, coords, 0, count / 2);
}
pointIndex += count;
return type;
}
}
/** The point's types buffer. */
protected byte[] types;
/** The points buffer. */
protected double[] points;
/** The point's type buffer size. */
protected int typeSize;
/** The points buffer size. */
protected int pointSize;
/* The path rule. */
protected int rule;
/** The space required in points buffer for different segmenet types. */
protected static int[] pointShift = { 2, // MOVETO
2, // LINETO
4, // QUADTO
6, // CUBICTO
0 }; // CLOSE
/** The default initial buffer size. */
protected static final int BUFFER_SIZE = 10;
/** The amount by which to expand the buffer capacity. */
protected static final int BUFFER_CAPACITY = 10;
}
@@ -0,0 +1,61 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Used to return the boundary of a {@link IShape}, one segment at a time.
*/
public interface PathIterator
{
/** Specifies the even/odd rule for determining the interior of a path. */
int WIND_EVEN_ODD = 0;
/** Specifies the non-zero rule for determining the interior of a path. */
int WIND_NON_ZERO = 1;
/** Indicates the starting location for a new subpath. */
int SEG_MOVETO = 0;
/** Indicates the end point of a line to be drawn from the most recently specified point. */
int SEG_LINETO = 1;
/** Indicates a pair of points that specify a quadratic parametric curve to be drawn from the
* most recently specified point. */
int SEG_QUADTO = 2;
/** Indicates a pair of points that specify a cubic parametric curve to be drawn from the most
* recently specified point. */
int SEG_CUBICTO = 3;
/** Indicates that the preceding subpath should be closed by appending a line segment back to
* the point corresponding to the most recent {@link #SEG_MOVETO}. */
int SEG_CLOSE = 4;
/**
* Returns the winding rule used to determine the interior of this path.
*/
int getWindingRule ();
/**
* Returns true if this path has no additional segments.
*/
boolean isDone ();
/**
* Advances this path to the next segment.
*/
void next ();
/**
* Returns the coordinates and type of the current path segment. The number of points stored in
* {@code coords} differs by path segment type: 0 - {@link #SEG_CLOSE}, 1 - {@link
* #SEG_MOVETO}, {@link #SEG_LINETO}, 2 - {@link #SEG_QUADTO}, 3 - {@link #SEG_CUBICTO}.
*
* @param coords a buffer into which the current coordinates will be copied. It must be of
* length 6. Each point is stored as a pair of x,y coordinates.
* @return the path segment type, e.g. {@link #SEG_MOVETO}.
*/
int currentSegment (double[] coords);
}
+79
View File
@@ -0,0 +1,79 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.io.Serializable;
/**
* Represents a point on a plane.
*/
public class Point extends AbstractPoint implements Serializable
{
/** The x-coordinate of the point. */
public double x;
/** The y-coordinate of the point. */
public double y;
/**
* Constructs a point at (0, 0).
*/
public Point () {
}
/**
* Constructs a point at the specified coordinates.
*/
public Point (double x, double y) {
setLocation(x, y);
}
/**
* Constructs a point with coordinates equal to the supplied point.
*/
public Point (IPoint p) {
setLocation(p.getX(), p.getY());
}
/**
* Sets the coordinates of this point to be equal to those of the supplied point.
*/
public void setLocation (IPoint p) {
setLocation(p.getX(), p.getY());
}
/**
* Sets the coordinates of this point to the supplied values.
*/
public void setLocation (double x, double y) {
this.x = x;
this.y = y;
}
/**
* A synonym for {@link #setLocation}.
*/
public void move (double x, double y) {
setLocation(x, y);
}
/**
* Translates this point by the specified offset.
*/
public void translate (double dx, double dy) {
x += dx;
y += dy;
}
@Override // from interface IPoint
public double getX () {
return x;
}
@Override // from interface IPoint
public double getY () {
return y;
}
}
+40
View File
@@ -0,0 +1,40 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Point-related utility methods.
*/
public class Points
{
/**
* Returns the squared Euclidian distance between the specified two points.
*/
public static double distanceSq (double x1, double y1, double x2, double y2) {
x2 -= x1;
y2 -= y1;
return x2 * x2 + y2 * y2;
}
/**
* Returns the Euclidian distance between the specified two points.
*/
public static double distance (double x1, double y1, double x2, double y2) {
return Math.sqrt(distanceSq(x1, y1, x2, y2));
}
/**
* Returns a string describing the supplied point, of the form <code>+x+y</code>,
* <code>+x-y</code>, <code>-x-y</code>, etc.
*/
public static String pointToString (double x, double y) {
StringBuilder buf = new StringBuilder();
if (x >= 0) buf.append("+");
buf.append(x);
if (y >= 0) buf.append("+");
buf.append(y);
return buf.toString();
}
}
+122
View File
@@ -0,0 +1,122 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
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 double x1;
/** The y-coordinate of the start of this curve. */
public double y1;
/** The x-coordinate of the control point. */
public double ctrlx;
/** The y-coordinate of the control point. */
public double ctrly;
/** The x-coordinate of the end of this curve. */
public double x2;
/** The y-coordinate of the end of this curve. */
public double 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 (double x1, double y1, double ctrlx, double ctrly, double x2, double y2) {
setCurve(x1, y1, ctrlx, ctrly, x2, y2);
}
/**
* Configures the start, control, and end points for this curve.
*/
public void setCurve (double x1, double y1, double ctrlx, double ctrly, double x2, double 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 (double[] 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 double getX1 () {
return x1;
}
@Override // from interface IQuadCurve
public double getY1 () {
return y1;
}
@Override // from interface IQuadCurve
public double getCtrlX () {
return ctrlx;
}
@Override // from interface IQuadCurve
public double getCtrlY () {
return ctrly;
}
@Override // from interface IQuadCurve
public double getX2 () {
return x2;
}
@Override // from interface IQuadCurve
public double getY2 () {
return y2;
}
}
@@ -0,0 +1,94 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Quad curve-related utility methods.
*/
public class QuadCurves
{
public static double getFlatnessSq (double x1, double y1, double ctrlx, double ctrly,
double x2, double y2) {
return Lines.pointSegDistSq(ctrlx, ctrly, x1, y1, x2, y2);
}
public static double getFlatnessSq (double[] 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 double getFlatness (double x1, double y1, double ctrlx, double ctrly,
double x2, double y2) {
return Lines.pointSegDist(ctrlx, ctrly, x1, y1, x2, y2);
}
public static double getFlatness (double[] 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) {
double x1 = src.getX1();
double y1 = src.getY1();
double cx = src.getCtrlX();
double cy = src.getCtrlY();
double x2 = src.getX2();
double y2 = src.getY2();
double cx1 = (x1 + cx) / 2f;
double cy1 = (y1 + cy) / 2f;
double cx2 = (x2 + cx) / 2f;
double 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 (double[] src, int srcoff,
double[] left, int leftOff, double[] right, int rightOff) {
double x1 = src[srcoff + 0];
double y1 = src[srcoff + 1];
double cx = src[srcoff + 2];
double cy = src[srcoff + 3];
double x2 = src[srcoff + 4];
double y2 = src[srcoff + 5];
double cx1 = (x1 + cx) / 2f;
double cy1 = (y1 + cy) / 2f;
double cx2 = (x2 + cx) / 2f;
double 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 (double[] eqn) {
return solveQuadratic(eqn, eqn);
}
public static int solveQuadratic (double[] eqn, double[] res) {
return Crossing.solveQuad(eqn, res);
}
}
+188
View File
@@ -0,0 +1,188 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.io.Serializable;
/**
* Represents an area in two dimensions.
*/
public class Rectangle extends AbstractRectangle implements Serializable
{
/** The x-coordinate of the rectangle's upper left corner. */
public double x;
/** The y-coordinate of the rectangle's upper left corner. */
public double y;
/** The width of the rectangle. */
public double width;
/** The height of the rectangle. */
public double height;
/**
* Constructs a rectangle at (0,0) and with dimensions (0,0).
*/
public Rectangle () {
}
/**
* Constructs a rectangle with the supplied upper-left corner and dimensions (0,0).
*/
public Rectangle (IPoint p) {
setBounds(p.getX(), p.getY(), 0, 0);
}
/**
* Constructs a rectangle with upper-left corner at (0,) and the supplied dimensions.
*/
public Rectangle (IDimension d) {
setBounds(0, 0, d.getWidth(), d.getHeight());
}
/**
* Constructs a rectangle with upper-left corner at the supplied point and with the supplied
* dimensions.
*/
public Rectangle (IPoint p, IDimension d) {
setBounds(p.getX(), p.getY(), d.getWidth(), d.getHeight());
}
/**
* Constructs a rectangle with the specified upper-left corner and dimensions.
*/
public Rectangle (double x, double y, double width, double height) {
setBounds(x, y, width, height);
}
/**
* Constructs a rectangle with bounds equal to the supplied rectangle.
*/
public Rectangle (IRectangle r) {
setBounds(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Sets the upper-left corner of this rectangle to the specified point.
*/
public void setLocation (double x, double y) {
this.x = x;
this.y = y;
}
/**
* Sets the upper-left corner of this rectangle to the supplied point.
*/
public void setLocation (IPoint p) {
setLocation(p.getX(), p.getY());
}
/**
* Sets the size of this rectangle to the specified dimensions.
*/
public void setSize (double width, double height) {
this.width = width;
this.height = height;
}
/**
* Sets the size of this rectangle to the supplied dimensions.
*/
public void setSize (Dimension d) {
setSize(d.width, d.height);
}
/**
* Sets the bounds of this rectangle to the specified bounds.
*/
public void setBounds (double x, double y, double width, double height) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
}
/**
* Sets the bounds of this rectangle to those of the supplied rectangle.
*/
public void setBounds (IRectangle r) {
setBounds(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Grows the bounds of this rectangle by the specified amount (i.e. the upper-left corner moves
* by the specified amount in the negative x and y direction and the width and height grow by
* twice the specified amount).
*/
public void grow (double dx, double dy) {
x -= dx;
y -= dy;
width += dx + dx;
height += dy + dy;
}
/**
* Translates the upper-left corner of this rectangle by the specified amount.
*/
public void translate (double mx, double my) {
x += mx;
y += my;
}
/**
* Expands the bounds of this rectangle to contain the specified point.
*/
public void add (double px, double py) {
double x1 = Math.min(x, px);
double x2 = Math.max(x + width, px);
double y1 = Math.min(y, py);
double y2 = Math.max(y + height, py);
setBounds(x1, y1, x2 - x1, y2 - y1);
}
/**
* Expands the bounds of this rectangle to contain the supplied point.
*/
public void add (IPoint p) {
add(p.getX(), p.getY());
}
/**
* Expands the bounds of this rectangle to contain the supplied rectangle.
*/
public void add (IRectangle r) {
double x1 = Math.min(x, r.getX());
double x2 = Math.max(x + width, r.getX() + r.getWidth());
double y1 = Math.min(y, r.getY());
double y2 = Math.max(y + height, r.getY() + r.getHeight());
setBounds(x1, y1, x2 - x1, y2 - y1);
}
@Override // from interface IRectangularShape
public double getX () {
return x;
}
@Override // from interface IRectangularShape
public double getY () {
return y;
}
@Override // from interface IRectangularShape
public double getWidth () {
return width;
}
@Override // from interface IRectangularShape
public double getHeight () {
return height;
}
@Override // from RectangularShape
public void setFrame (double x, double y, double width, double height) {
setBounds(x, y, width, height);
}
}
@@ -0,0 +1,33 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* Rectangle-related utility methods.
*/
public class Rectangles
{
/**
* Intersects the supplied two rectangles, writing the result into {@link dst}.
*/
public static void intersect (IRectangle src1, IRectangle src2, Rectangle dst) {
double x1 = Math.max(src1.getMinX(), src2.getMinX());
double y1 = Math.max(src1.getMinY(), src2.getMinY());
double x2 = Math.min(src1.getMaxX(), src2.getMaxX());
double y2 = Math.min(src1.getMaxY(), src2.getMaxY());
dst.setBounds(x1, y1, x2 - x1, y2 - y1);
}
/**
* Unions the supplied two rectangles, writing the result into {@link dst}.
*/
public static void union (IRectangle src1, IRectangle src2, Rectangle dst) {
double x1 = Math.min(src1.getMinX(), src2.getMinX());
double y1 = Math.min(src1.getMinY(), src2.getMinY());
double x2 = Math.max(src1.getMaxX(), src2.getMaxX());
double y2 = Math.max(src1.getMaxY(), src2.getMaxY());
dst.setBounds(x1, y1, x2 - x1, y2 - y1);
}
}
@@ -0,0 +1,158 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
/**
* The base class for various {@link Shape} objects whose geometry is defined by a rectangular
* frame.
*/
public abstract class RectangularShape implements IRectangularShape
{
/**
* Sets the location and size of the framing rectangle of this shape to the specified values.
*/
public abstract void setFrame (double x, double y, double width, double height);
/**
* Sets the location and size of the framing rectangle of this shape to the supplied values.
*/
public void setFrame (IPoint loc, IDimension size) {
setFrame(loc.getX(), loc.getY(), size.getWidth(), size.getHeight());
}
/**
* Sets the location and size of the framing rectangle of this shape to be equal to the
* supplied rectangle.
*/
public void setFrame (IRectangle r) {
setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
/**
* Sets the location and size of the framing rectangle of this shape based on the specified
* diagonal line.
*/
public void setFrameFromDiagonal (double x1, double y1, double x2, double y2) {
double rx, ry, rw, rh;
if (x1 < x2) {
rx = x1;
rw = x2 - x1;
} else {
rx = x2;
rw = x1 - x2;
}
if (y1 < y2) {
ry = y1;
rh = y2 - y1;
} else {
ry = y2;
rh = y1 - y2;
}
setFrame(rx, ry, rw, rh);
}
/**
* Sets the location and size of the framing rectangle of this shape based on the supplied
* diagonal line.
*/
public void setFrameFromDiagonal (IPoint p1, IPoint p2) {
setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY());
}
/**
* Sets the location and size of the framing rectangle of this shape based on the specified
* center and corner points.
*/
public void setFrameFromCenter (double centerX, double centerY,
double cornerX, double cornerY) {
double width = Math.abs(cornerX - centerX);
double height = Math.abs(cornerY - centerY);
setFrame(centerX - width, centerY - height, width * 2, height * 2);
}
/**
* Sets the location and size of the framing rectangle of this shape based on the supplied
* center and corner points.
*/
public void setFrameFromCenter (IPoint center, IPoint corner) {
setFrameFromCenter(center.getX(), center.getY(), corner.getX(), corner.getY());
}
@Override // from IRectangularShape
public double getMinX () {
return getX();
}
@Override // from IRectangularShape
public double getMinY () {
return getY();
}
@Override // from IRectangularShape
public double getMaxX () {
return getX() + getWidth();
}
@Override // from IRectangularShape
public double getMaxY () {
return getY() + getHeight();
}
@Override // from IRectangularShape
public double getCenterX () {
return getX() + getWidth() / 2;
}
@Override // from IRectangularShape
public double getCenterY () {
return getY() + getHeight() / 2;
}
@Override // from IRectangularShape
public Rectangle getFrame () {
return getBounds();
}
@Override // from IRectangularShape
public Rectangle getFrame (Rectangle target) {
return getBounds(target);
}
@Override // from interface IShape
public boolean isEmpty () {
return getWidth() <= 0 || getHeight() <= 0;
}
@Override // from interface IShape
public boolean contains (IPoint point) {
return contains(point.getX(), point.getY());
}
@Override // from interface IShape
public boolean contains (IRectangle rect) {
return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
}
@Override // from interface IShape
public boolean intersects (IRectangle rect) {
return intersects(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
}
@Override // from interface IShape
public Rectangle getBounds () {
return getBounds(new Rectangle());
}
@Override // from interface IShape
public Rectangle getBounds (Rectangle target) {
target.setBounds(getX(), getY(), getWidth(), getHeight());
return target;
}
@Override // from interface IShape
public PathIterator getPathIterator (AffineTransform t, double flatness) {
return new FlatteningPathIterator(getPathIterator(t), flatness);
}
}
@@ -0,0 +1,102 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.d;
import java.io.Serializable;
/**
* Represents a rectangle with rounded corners, defined by an arc width and height.
*/
public class RoundRectangle extends AbstractRoundRectangle implements Serializable
{
/** The x-coordinate of the framing rectangle. */
public double x;
/** The y-coordinate of the framing rectangle. */
public double y;
/** The width of the framing rectangle. */
public double width;
/** The height of the framing rectangle. */
public double height;
/** The width of the arc that defines the rounded corners. */
public double arcwidth;
/** The height of the arc that defines the rounded corners. */
public double archeight;
/**
* Creates a rounded rectangle with frame (0x0+0+0) and corners of size (0x0).
*/
public RoundRectangle () {
}
/**
* Creates a rounded rectangle with the specified frame and corner dimensions.
*/
public RoundRectangle (double x, double y, double width, double height,
double arcwidth, double archeight) {
setRoundRect(x, y, width, height, arcwidth, archeight);
}
/**
* Sets the frame and corner dimensions of this rectangle to the specified values.
*/
public void setRoundRect (double x, double y, double width, double height,
double arcwidth, double archeight) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.arcwidth = arcwidth;
this.archeight = archeight;
}
/**
* Sets the frame and corner dimensions of this rectangle to be equal to those of the supplied
* rectangle.
*/
public void setRoundRect (IRoundRectangle rr) {
setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(),
rr.getArcWidth(), rr.getArcHeight());
}
@Override // from interface IRoundRectangle
public double getArcWidth () {
return arcwidth;
}
@Override // from interface IRoundRectangle
public double getArcHeight () {
return archeight;
}
@Override // from interface IRectangularShape
public double getX () {
return x;
}
@Override // from interface IRectangularShape
public double getY () {
return y;
}
@Override // from interface IRectangularShape
public double getWidth () {
return width;
}
@Override // from interface IRectangularShape
public double getHeight () {
return height;
}
@Override // from RoundRectangle
public void setFrame (double x, double y, double width, double height) {
setRoundRect(x, y, width, height, arcwidth, archeight);
}
}