add ear-clip triangulator, might be more efficient for simple polys
This commit is contained in:
parent
344f4d7030
commit
c64e0fe5b0
365
src/org/oscim/utils/Triangulator.java
Normal file
365
src/org/oscim/utils/Triangulator.java
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
package org.oscim.utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triangulates a polygon into triangles - duh. Doesn't handle
|
||||||
|
* holes in polys
|
||||||
|
*
|
||||||
|
* @author Public Source from FlipCode
|
||||||
|
*/
|
||||||
|
public class Triangulator {
|
||||||
|
/** The accepted error value */
|
||||||
|
private static final float EPSILON = 0.0000000001f;
|
||||||
|
/** The list of points to be triangulated */
|
||||||
|
private PointList poly = new PointList();
|
||||||
|
/** The list of points describing the triangles */
|
||||||
|
private PointList tris = new PointList();
|
||||||
|
/** True if we've tried to triangulate */
|
||||||
|
private boolean tried;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new triangulator
|
||||||
|
*/
|
||||||
|
public Triangulator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a point describing the polygon to be triangulated
|
||||||
|
*
|
||||||
|
* @param x The x coordinate of the point
|
||||||
|
* @param y the y coordinate of the point
|
||||||
|
*/
|
||||||
|
public void addPolyPoint(float x, float y) {
|
||||||
|
poly.add(new Point(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cause the triangulator to split the polygon
|
||||||
|
*
|
||||||
|
* @return True if we managed the task
|
||||||
|
*/
|
||||||
|
public boolean triangulate() {
|
||||||
|
tried = true;
|
||||||
|
|
||||||
|
boolean worked = process(poly, tris);
|
||||||
|
return worked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a count of the number of triangles produced
|
||||||
|
*
|
||||||
|
* @return The number of triangles produced
|
||||||
|
*/
|
||||||
|
public int getTriangleCount() {
|
||||||
|
if (!tried) {
|
||||||
|
throw new RuntimeException("Call triangulate() before accessing triangles");
|
||||||
|
}
|
||||||
|
return tris.size() / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a point on a specified generated triangle
|
||||||
|
*
|
||||||
|
* @param tri The index of the triangle to interegate
|
||||||
|
* @param i The index of the point within the triangle to retrieve
|
||||||
|
* (0 - 2)
|
||||||
|
* @return The x,y coordinate pair for the point
|
||||||
|
*/
|
||||||
|
public float[] getTrianglePoint(int tri, int i) {
|
||||||
|
if (!tried) {
|
||||||
|
throw new RuntimeException("Call triangulate() before accessing triangles");
|
||||||
|
}
|
||||||
|
return tris.get((tri * 3) + i).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the area of a polygon defined by the series of points
|
||||||
|
* in the list
|
||||||
|
*
|
||||||
|
* @param contour The list of points defined the contour of the polygon
|
||||||
|
* (Vector2f)
|
||||||
|
* @return The area of the polygon defined
|
||||||
|
*/
|
||||||
|
private static float area(PointList contour) {
|
||||||
|
int n = contour.size();
|
||||||
|
|
||||||
|
float A = 0.0f;
|
||||||
|
|
||||||
|
for (int p = n - 1, q = 0; q < n; p = q++) {
|
||||||
|
Point contourP = contour.get(p);
|
||||||
|
Point contourQ = contour.get(q);
|
||||||
|
|
||||||
|
A += contourP.getX() * contourQ.getY() - contourQ.getX()
|
||||||
|
* contourP.getY();
|
||||||
|
}
|
||||||
|
return A * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the point P is inside the triangle defined by
|
||||||
|
* the points A,B,C
|
||||||
|
*
|
||||||
|
* @param Ax Point A x-coordinate
|
||||||
|
* @param Ay Point A y-coordinate
|
||||||
|
* @param Bx Point B x-coordinate
|
||||||
|
* @param By Point B y-coordinate
|
||||||
|
* @param Cx Point C x-coordinate
|
||||||
|
* @param Cy Point C y-coordinate
|
||||||
|
* @param Px Point P x-coordinate
|
||||||
|
* @param Py Point P y-coordinate
|
||||||
|
* @return True if the point specified is within the triangle
|
||||||
|
*/
|
||||||
|
private static boolean insideTriangle(float Ax, float Ay, float Bx,
|
||||||
|
float By, float Cx, float Cy, float Px, float Py) {
|
||||||
|
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
|
||||||
|
float cCROSSap, bCROSScp, aCROSSbp;
|
||||||
|
|
||||||
|
ax = Cx - Bx;
|
||||||
|
ay = Cy - By;
|
||||||
|
bx = Ax - Cx;
|
||||||
|
by = Ay - Cy;
|
||||||
|
cx = Bx - Ax;
|
||||||
|
cy = By - Ay;
|
||||||
|
apx = Px - Ax;
|
||||||
|
apy = Py - Ay;
|
||||||
|
bpx = Px - Bx;
|
||||||
|
bpy = Py - By;
|
||||||
|
cpx = Px - Cx;
|
||||||
|
cpy = Py - Cy;
|
||||||
|
|
||||||
|
aCROSSbp = ax * bpy - ay * bpx;
|
||||||
|
cCROSSap = cx * apy - cy * apx;
|
||||||
|
bCROSScp = bx * cpy - by * cpx;
|
||||||
|
|
||||||
|
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut a the contour and add a triangle into V to describe the
|
||||||
|
* location of the cut
|
||||||
|
*
|
||||||
|
* @param contour The list of points defining the polygon
|
||||||
|
* @param u The index of the first point
|
||||||
|
* @param v The index of the second point
|
||||||
|
* @param w The index of the third point
|
||||||
|
* @param n ?
|
||||||
|
* @param V The array to populate with indicies of triangles
|
||||||
|
* @return True if a triangle was found
|
||||||
|
*/
|
||||||
|
private static boolean snip(PointList contour, int u, int v, int w, int n,
|
||||||
|
int[] V) {
|
||||||
|
int p;
|
||||||
|
float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
|
||||||
|
|
||||||
|
Ax = contour.get(V[u]).getX();
|
||||||
|
Ay = contour.get(V[u]).getY();
|
||||||
|
|
||||||
|
Bx = contour.get(V[v]).getX();
|
||||||
|
By = contour.get(V[v]).getY();
|
||||||
|
|
||||||
|
Cx = contour.get(V[w]).getX();
|
||||||
|
Cy = contour.get(V[w]).getY();
|
||||||
|
|
||||||
|
if (EPSILON > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (p = 0; p < n; p++) {
|
||||||
|
if ((p == u) || (p == v) || (p == w)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Px = contour.get(V[p]).getX();
|
||||||
|
Py = contour.get(V[p]).getY();
|
||||||
|
|
||||||
|
if (insideTriangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a list of points defining a polygon
|
||||||
|
*
|
||||||
|
* @param contour The list of points describing the polygon
|
||||||
|
* @param result The list of points describing the triangles. Groups
|
||||||
|
* of 3 describe each triangle
|
||||||
|
* @return True if we succeeded in completing triangulation
|
||||||
|
*/
|
||||||
|
private static boolean process(PointList contour, PointList result) {
|
||||||
|
/* allocate and initialize list of Vertices in polygon */
|
||||||
|
|
||||||
|
int n = contour.size();
|
||||||
|
if (n < 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int[] V = new int[n];
|
||||||
|
|
||||||
|
/* we want a counter-clockwise polygon in V */
|
||||||
|
|
||||||
|
if (0.0f < area(contour)) {
|
||||||
|
for (int v = 0; v < n; v++)
|
||||||
|
V[v] = v;
|
||||||
|
} else {
|
||||||
|
for (int v = 0; v < n; v++)
|
||||||
|
V[v] = (n - 1) - v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nv = n;
|
||||||
|
|
||||||
|
/* remove nv-2 Vertices, creating 1 triangle every time */
|
||||||
|
int count = 2 * nv; /* error detection */
|
||||||
|
|
||||||
|
for (int m = 0, v = nv - 1; nv > 2;) {
|
||||||
|
/* if we loop, it is probably a non-simple polygon */
|
||||||
|
if (0 >= (count--)) {
|
||||||
|
//** Triangulate: ERROR - probable bad polygon!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* three consecutive vertices in current polygon, <u,v,w> */
|
||||||
|
int u = v;
|
||||||
|
if (nv <= u)
|
||||||
|
u = 0; /* previous */
|
||||||
|
v = u + 1;
|
||||||
|
if (nv <= v)
|
||||||
|
v = 0; /* new v */
|
||||||
|
int w = v + 1;
|
||||||
|
if (nv <= w)
|
||||||
|
w = 0; /* next */
|
||||||
|
|
||||||
|
if (snip(contour, u, v, w, nv, V)) {
|
||||||
|
int a, b, c, s, t;
|
||||||
|
|
||||||
|
/* true names of the vertices */
|
||||||
|
a = V[u];
|
||||||
|
b = V[v];
|
||||||
|
c = V[w];
|
||||||
|
|
||||||
|
/* output Triangle */
|
||||||
|
result.add(contour.get(a));
|
||||||
|
result.add(contour.get(b));
|
||||||
|
result.add(contour.get(c));
|
||||||
|
|
||||||
|
m++;
|
||||||
|
|
||||||
|
/* remove v from remaining polygon */
|
||||||
|
for (s = v, t = v + 1; t < nv; s++, t++) {
|
||||||
|
V[s] = V[t];
|
||||||
|
}
|
||||||
|
nv--;
|
||||||
|
|
||||||
|
/* resest error detection counter */
|
||||||
|
count = 2 * nv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single point handled by the triangulator
|
||||||
|
*
|
||||||
|
* @author Kevin Glass
|
||||||
|
*/
|
||||||
|
private class Point {
|
||||||
|
/** The x coorindate of this point */
|
||||||
|
private float x;
|
||||||
|
/** The y coorindate of this point */
|
||||||
|
private float y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new point
|
||||||
|
*
|
||||||
|
* @param x The x coordindate of the point
|
||||||
|
* @param y The y coordindate of the point
|
||||||
|
*/
|
||||||
|
public Point(float x, float y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the x coordinate of the point
|
||||||
|
*
|
||||||
|
* @return The x coordinate of the point
|
||||||
|
*/
|
||||||
|
public float getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the y coordinate of the point
|
||||||
|
*
|
||||||
|
* @return The y coordinate of the point
|
||||||
|
*/
|
||||||
|
public float getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this point into a float array
|
||||||
|
*
|
||||||
|
* @return The contents of this point as a float array
|
||||||
|
*/
|
||||||
|
public float[] toArray() {
|
||||||
|
return new float[] { x, y };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of type <code>Point</code>
|
||||||
|
*
|
||||||
|
* @author Kevin Glass
|
||||||
|
*/
|
||||||
|
private class PointList {
|
||||||
|
/** The list of points */
|
||||||
|
private ArrayList<Point> points = new ArrayList<Point>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new empty list
|
||||||
|
*/
|
||||||
|
public PointList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a point to the list
|
||||||
|
*
|
||||||
|
* @param point The point to add
|
||||||
|
*/
|
||||||
|
public void add(Point point) {
|
||||||
|
points.add(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a point from the list
|
||||||
|
*
|
||||||
|
* @param point The point to remove
|
||||||
|
*/
|
||||||
|
public void remove(Point point) {
|
||||||
|
points.remove(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the list
|
||||||
|
*
|
||||||
|
* @return The size of the list
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return points.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a point a specific index in the list
|
||||||
|
*
|
||||||
|
* @param i The index of the point to retrieve
|
||||||
|
* @return The point
|
||||||
|
*/
|
||||||
|
public Point get(int i) {
|
||||||
|
return points.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user