Added the 800lb gorilla: Area, and its myriad helper classes.

This commit is contained in:
Michael Bayne
2011-06-10 15:22:14 -07:00
parent 8a3b1a04aa
commit f782ea69d2
5 changed files with 2396 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,291 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.f;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* An internal class used to compute crossings.
*/
class CrossingHelper
{
private float[][] coords;
private int[] sizes;
private List<IntersectPoint> isectPoints = new ArrayList<IntersectPoint>();
public CrossingHelper (float[][] 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, float[] coords1, int length1,
float[] coords2, int length2, Edge initEdge) {
int areaOfEdge1, areaOfEdge2;
int initBegin, initEnd;
int addBegin, addEnd;
float x1, y1, x2, y2, x3, y3, x4, y4;
float[] point = new float[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 (float[] coords1, int length1,
float[] coords2, int length2, int[] array) {
int temp;
int length = length1 + length2;
float 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 (float[] 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 (float x1, float y1, float x2, float 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,273 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.f;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
/**
* An internal class used to compute crossings.
*/
class CurveCrossingHelper
{
private float[][] coords;
private int[][] rules;
private int[] sizes;
private int[] rulesSizes;
private int[][] offsets;
private List<IntersectPoint> isectPoints = new ArrayList<IntersectPoint>();
public CurveCrossingHelper (float[][] 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 () {
float[] edge1 = new float[8];
float[] edge2 = new float[8];
float[] points = new float[6];
float[] params = new float[6];
float[] mp1 = new float[2];
float[] cp1 = new float[2];
float[] mp2 = new float[2];
float[] cp2 = new float[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, float[] c, float[] mp, float[] 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 (float x, float 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,481 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.f;
/**
* Various geometry utility methods.
*/
public class GeometryUtil
{
public static final float EPSILON = (float)Math.pow(10, -14);
public static int intersectLinesWithParams (float x1, float y1, float x2, float y2,
float x3, float y3, float x4, float y4,
float[] params) {
float dx = x4 - x3;
float dy = y4 - y3;
float d = dx * (y2 - y1) - dy * (x2 - x1);
// float 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 (float x1, float y1, float x2, float y2, float x3, float y3,
float x4, float y4, float[] point) {
float A1 = -(y2 - y1);
float B1 = (x2 - x1);
float C1 = x1 * y2 - x2 * y1;
float A2 = -(y4 - y3);
float B2 = (x4 - x3);
float C2 = x3 * y4 - x4 * y3;
float coefParallel = A1 * B2 - A2 * B1;
// float 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 (float x1, float y1, float x2, float y2,
float qx1, float qy1, float qx2, float qy2,
float qx3, float qy3, float[] params) {
float[] eqn = new float[3];
float[] t = new float[2];
float[] s = new float[2];
float dy = y2 - y1;
float 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 (float x1, float y1, float x2, float y2,
float cx1, float cy1, float cx2, float cy2,
float cx3, float cy3, float cx4, float cy4,
float[] params) {
float[] eqn = new float[4];
float[] t = new float[3];
float[] s = new float[3];
float dy = y2 - y1;
float 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 (float x1, float y1, float x2, float y2, float x3, float y3,
float qx1, float qy1, float qx2, float qy2, float qx3,
float qy3, float[] params) {
float[] initParams = new float[2];
float[] xCoefs1 = new float[3];
float[] yCoefs1 = new float[3];
float[] xCoefs2 = new float[3];
float[] yCoefs2 = new float[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 (float qx1, float qy1, float qx2, float qy2,
float qx3, float qy3, float cx1, float cy1,
float cx2, float cy2, float cx3, float cy3,
float cx4, float cy4, float[] params) {
int quantity = 0;
float[] initParams = new float[3];
float[] xCoefs1 = new float[3];
float[] yCoefs1 = new float[3];
float[] xCoefs2 = new float[4];
float[] yCoefs2 = new float[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 (float x1, float y1, float x2, float y2, float x3, float y3,
float x4, float y4, float cx1, float cy1,
float cx2, float cy2, float cx3, float cy3,
float cx4, float cy4, float[] params) {
int quantity = 0;
float[] initParams = new float[3];
float[] xCoefs1 = new float[4];
float[] yCoefs1 = new float[4];
float[] xCoefs2 = new float[4];
float[] yCoefs2 = new float[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 float line (float t, float x1, float x2) {
return x1 * (1f - t) + x2 * t;
}
public static float quad (float t, float x1, float x2, float x3) {
return x1 * (1f - t) * (1f - t) + 2f * x2 * t * (1f - t) + x3 * t * t;
}
public static float cubic (float t, float x1, float x2, float x3, float 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 (float[] coef, float 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 (float[] coef, float 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 (float[] xCoefs1, float[] yCoefs1,
float[] xCoefs2, float[] yCoefs2, float[] params) {
float t = 0f, s = 0f;
float t1 = params[0];
float s1 = params[1];
float 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 (float xCoefs1[], float yCoefs1[],
float xCoefs2[], float yCoefs2[], float[] params) {
float t = 0f, s = 0f;
float t1 = params[0];
float s1 = params[1];
float 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 (float xCoefs1[], float yCoefs1[],
float xCoefs2[], float yCoefs2[], float params[]) {
float t = 0f, s = 0f;
float t1 = params[0];
float s1 = params[1];
float 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,107 @@
//
// Pythagoras - a collection of geometry classes
// http://github.com/samskivert/pythagoras
package pythagoras.f;
/**
* An internal helper class that represents the intersection point of two edges.
*/
class IntersectPoint
{
public IntersectPoint (int begIndex1, int endIndex1, int begIndex2, int endIndex2,
float x, float 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,
float x, float y, float param1, float 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 float getParam (boolean isCurrentArea) {
return isCurrentArea ? param1 : param2;
}
public int getRule (boolean isCurrentArea) {
return isCurrentArea ? rule1 : rule2;
}
public float getX () {
return x;
}
public float 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 float 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 float x;
// the ordinate coordinate of the point
private final float y;
// the parameter value of edge2
private float param2;
}