/**
*
*/
package maps.util;
/**
* from http://www.ewjordan.com/
*
*/
public class Triangulation {
/*
* Triangulates a polygon using simple O(N^2) ear-clipping algorithm Returns
* a Triangle array unless the polygon can't be triangulated, in which case
* null is returned. This should only happen if the polygon self-intersects,
* though it will not _always_ return null for a bad polygon - it is the
* caller's responsibility to check for self-intersection, and if it
* doesn't, it should at least check that the return value is non-null
* before using. You're warned!
*/
public static Triangle[] triangulatePolygon(double[] xv, double[] yv,
int vNum) {
if (vNum < 3)
return null;
Triangle[] buffer = new Triangle[vNum];
int bufferSize = 0;
double[] xrem = new double[vNum];
double[] yrem = new double[vNum];
for (int i = 0; i < vNum; ++i) {
xrem[i] = xv[i];
yrem[i] = yv[i];
}
while (vNum > 3) {
// Find an ear
int earIndex = -1;
for (int i = 0; i < vNum; ++i) {
if (isEar(i, xrem, yrem)) {
earIndex = i;
break;
}
}
// If we still haven't found an ear, we're screwed.
// The user did Something Bad, so return null.
// This will probably crash their program, since
// they won't bother to check the return value.
// At this we shall laugh, heartily and with great gusto.
if (earIndex == -1)
return null;
// Clip off the ear:
// - remove the ear tip from the list
// Opt note: actually creates a new list, maybe
// this should be done in-place instead. A linked
// list would be even better to avoid array-fu.
--vNum;
double[] newx = new double[vNum];
double[] newy = new double[vNum];
int currDest = 0;
for (int i = 0; i < vNum; ++i) {
if (currDest == earIndex)
++currDest;
newx[i] = xrem[currDest];
newy[i] = yrem[currDest];
++currDest;
}
// - add the clipped triangle to the triangle list
int under = (earIndex == 0) ? (xrem.length - 1) : (earIndex - 1);
int over = (earIndex == xrem.length - 1) ? 0 : (earIndex + 1);
Triangle toAdd = new Triangle(xrem[earIndex], yrem[earIndex],
xrem[over], yrem[over], xrem[under], yrem[under]);
buffer[bufferSize] = toAdd;
++bufferSize;
// - replace the old list with the new one
xrem = newx;
yrem = newy;
}
Triangle toAdd = new Triangle(xrem[1], yrem[1], xrem[2], yrem[2],
xrem[0], yrem[0]);
buffer[bufferSize] = toAdd;
++bufferSize;
Triangle[] res = new Triangle[bufferSize];
for (int i = 0; i < bufferSize; i++) {
res[i] = buffer[i];
}
return res;
}
// public static Polygon[] repeatedpolygonizeTriangles(Triangle[]
// triangulated) {
// }
public static Polygon[] polygonizeTriangles(Triangle[] triangulated,
double degreeTolerance) {
Polygon[] polys;
int polyIndex = 0;
if (triangulated == null) {
return null;
} else {
polys = new Polygon[triangulated.length];
boolean[] covered = new boolean[triangulated.length];
for (int i = 0; i < triangulated.length; i++) {
covered[i] = false;
}
boolean notDone = true;
while (notDone) {
int currTri = -1;
for (int i = 0; i < triangulated.length; i++) {
if (covered[i])
continue;
currTri = i;
break;
}
if (currTri == -1) {
notDone = false;
} else {
Polygon poly = new Polygon(triangulated[currTri]);
covered[currTri] = true;
for (int i = 0; i < triangulated.length; i++) {
if (covered[i])
continue;
Polygon newP = poly.add(triangulated[i]);
if (newP == null)
continue;
if (newP.isRoughlyConvex(degreeTolerance)) {// ||
// newP.isRoughlyConvex())
// {
poly = newP;
covered[i] = true;
}
}
polys[polyIndex] = poly;
polyIndex++;
}
}
}
Polygon[] ret = new Polygon[polyIndex];
for (int i = 0; i < polyIndex; i++) {
ret[i] = polys[i];
}
return ret;
}
public static Polygon[] polygonizeTriangles(Triangle[] triangulated) {
return polygonizeTriangles(triangulated, 0);
}
// Checks if vertex i is the tip of an ear
public static boolean isEar(int i, double[] xv, double[] yv) {
double dx0, dy0, dx1, dy1;
dx0 = dy0 = dx1 = dy1 = 0;
if (i >= xv.length || i < 0 || xv.length < 3) {
return false;
}
int upper = i + 1;
int lower = i - 1;
if (i == 0) {
dx0 = xv[0] - xv[xv.length - 1];
dy0 = yv[0] - yv[yv.length - 1];
dx1 = xv[1] - xv[0];
dy1 = yv[1] - yv[0];
lower = xv.length - 1;
} else if (i == xv.length - 1) {
dx0 = xv[i] - xv[i - 1];
dy0 = yv[i] - yv[i - 1];
dx1 = xv[0] - xv[i];
dy1 = yv[0] - yv[i];
upper = 0;
} else {
dx0 = xv[i] - xv[i - 1];
dy0 = yv[i] - yv[i - 1];
dx1 = xv[i + 1] - xv[i];
dy1 = yv[i + 1] - yv[i];
}
double cross = dx0 * dy1 - dx1 * dy0;
if (cross > 0)
return false;
Triangle myTri = new Triangle(xv[i], yv[i], xv[upper], yv[upper],
xv[lower], yv[lower]);
for (int j = 0; j < xv.length; ++j) {
if (j == i || j == lower || j == upper)
continue;
if (myTri.isInside(xv[j], yv[j]))
return false;
}
return true;
}
}