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:
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 ();
|
||||||
|
}
|
||||||
@@ -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 ();
|
||||||
|
}
|
||||||
@@ -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 ();
|
||||||
|
}
|
||||||
@@ -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 ();
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user