package org.ripple.power.ui.graphics.geom;
import java.util.Iterator;
import java.util.LinkedList;
public class Polygon2D {
public static class Point2D {
public float x;
public float y;
public int type;
public static final int POINT_CONVEX = 1;
public static final int POINT_CONCAVE = 2;
public Point2D(float x, float y) {
this.x = x;
this.y = y;
}
public boolean equals(Object obj) {
Point2D p = (Point2D) obj;
if (p.x == this.x && p.y == this.y && p.type == this.type) {
return true;
} else {
return false;
}
}
public String toString() {
return "(" + this.x + ", " + this.y + ")";
}
}
private LinkedList<Point2D> points = new LinkedList<Point2D>();
public Polygon2D(Point2D[] pts) {
if (polygonClock(pts)) {
for (int i = 0; i < pts.length; i++) {
this.points.add(pts[i]);
}
} else {
for (int i = pts.length - 1; i >= 0; i--) {
this.points.add(pts[i]);
}
}
}
public Triangle2D[] getTriangles() {
float x1;
float y1;
float x2;
float y2;
float x3;
float y3;
LinkedList<Triangle2D> triangles = new LinkedList<Triangle2D>();
boolean finish = false;
if (this.points.size() == 3) {
x1 = points.get(0).x;
y1 = points.get(0).y;
x2 = points.get(1).x;
y2 = points.get(1).y;
x3 = points.get(2).x;
y3 = points.get(2).y;
triangles.add(new Triangle2D(x1, y1, x2, y2, x3, y3));
finish = true;
}
for (; !finish;) {
int pointIndex = 0;
boolean earFound = false;
this.matchPoints();
while (pointIndex < this.points.size()) {
if (isEar(this.points.get(pointIndex), pointIndex)) {
earFound = true;
break;
}
pointIndex++;
}
if (!earFound) {
throw new RuntimeException("Cannot triangle polygon !");
}
Point2D p0 = this.getPreviousPoint(pointIndex);
Point2D p1 = this.points.get(pointIndex);
Point2D p2 = this.getNextPoint(pointIndex);
x1 = p0.x;
y1 = p0.y;
x2 = p1.x;
y2 = p1.y;
x3 = p2.x;
y3 = p2.y;
triangles.add(new Triangle2D(x1, y1, x2, y2, x3, y3));
this.points.remove(pointIndex);
if (this.points.size() == 3) {
x1 = points.get(0).x;
y1 = points.get(0).y;
x2 = points.get(1).x;
y2 = points.get(1).y;
x3 = points.get(2).x;
y3 = points.get(2).y;
triangles.add(new Triangle2D(x1, y1, x2, y2, x3, y3));
finish = true;
}
}
return triangles.toArray(new Triangle2D[0]);
}
private boolean isEar(Point2D point, int index) {
if (point.type == Point2D.POINT_CONCAVE) {
return false;
}
Point2D p0 = this.getPreviousPoint(index);
Point2D p1 = this.points.get(index);
Point2D p2 = this.getNextPoint(index);
int numPoints = this.points.size();
for (int i = 0; i < numPoints; i++) {
Point2D currPoint = this.points.get(i);
if (currPoint.equals(p0) || currPoint.equals(p1)
|| currPoint.equals(p2)) {
continue;
}
if (currPoint.type == Point2D.POINT_CONCAVE) {
if (this.isSide(currPoint, p0, p1, p2)
&& this.isSide(currPoint, p1, p0, p2)
&& this.isSide(currPoint, p2, p0, p1)) {
return false;
}
}
}
return true;
}
public boolean isSide(Point2D p1, Point2D p2, Point2D a, Point2D b) {
double dotProduct = 0.0;
double crossProduct1 = 0.0;
double crossProduct2 = 0.0;
double a1, a2, b1, b2;
a1 = (b.x - a.x);
a2 = (b.y - a.y);
b1 = (p1.x - a.x);
b2 = (p1.y - a.y);
crossProduct1 = a1 * b2 - a2 * b1;
a1 = (b.x - a.x);
a2 = (b.y - a.y);
b1 = (p2.x - a.x);
b2 = (p2.y - a.y);
crossProduct2 = a1 * b2 - a2 * b1;
dotProduct = crossProduct1 * crossProduct2;
if (dotProduct >= 0) {
return true;
} else {
return false;
}
}
public void matchPoints() {
int numPoints = this.points.size();
for (int i = 0; i < numPoints; i++) {
Point2D p1 = this.getPreviousPoint(i);
Point2D p2 = this.points.get(i);
Point2D p3 = this.getNextPoint(i);
if (!convex(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)) {
this.points.get(i).type = Point2D.POINT_CONVEX;
} else {
this.points.get(i).type = Point2D.POINT_CONCAVE;
}
}
}
private boolean convex(double x1, double y1, double x2, double y2,
double x3, double y3) {
if (triangleArea(x1, y1, x2, y2, x3, y3) < 0) {
return true;
} else {
return false;
}
}
private double triangleArea(double x1, double y1, double x2, double y2,
double x3, double y3) {
double areaSum = 0;
areaSum += x1 * (y3 - y2);
areaSum += x2 * (y1 - y3);
areaSum += x3 * (y2 - y1);
return areaSum / 2;
}
private boolean polygonClock(Point2D[] pts) {
int numPoints = pts.length;
if (numPoints < 3) {
throw new RuntimeException("Less than three points !");
}
Point2D minPoint = pts[0];
int minIndex = 0;
for (int i = 1; i < numPoints; i++) {
Point2D curr = pts[i];
if (curr.x > minPoint.x) {
minPoint = curr;
minIndex = i;
} else if (curr.x == minPoint.x && curr.y < minPoint.y) {
minPoint = curr;
minIndex = i;
}
}
Point2D p1 = null, p2 = null, p3 = null;
if (minIndex == 0) {
p1 = pts[numPoints - 1];
p2 = pts[0];
p3 = pts[1];
} else if (minIndex == numPoints - 1) {
p1 = pts[numPoints - 2];
p2 = pts[numPoints - 1];
p3 = pts[0];
} else if (minIndex > 0 && minIndex < numPoints - 1) {
p1 = pts[minIndex - 1];
p2 = pts[minIndex];
p3 = pts[minIndex + 1];
}
double crossProduct = (p2.x - p1.x) * (p3.y - p2.y);
crossProduct = crossProduct - ((p2.y - p1.y) * (p3.x - p2.x));
if (crossProduct < 0) {
return true;
} else {
return false;
}
}
private Point2D getNextPoint(int index) {
if (index != this.points.size() - 1) {
return this.points.get(index + 1);
} else {
return this.points.get(0);
}
}
private Point2D getPreviousPoint(int index) {
if (index != 0) {
return this.points.get(index - 1);
} else {
return this.points.get(this.points.size() - 1);
}
}
public Point2D findCentroid() {
float cx = 0.0f, cy = 0.0f, f = 0;
Iterator<Point2D> it = this.points.iterator();
while (it.hasNext()) {
Point2D p1 = it.next();
if (!it.hasNext()) {
break;
}
Point2D p2 = it.next();
f = p1.x * p2.y - p2.x * p1.y;
cx += (p1.x + p2.y) * f;
cy += (p1.y + p2.y) * f;
}
cx = 1 / (6 * Math.abs(signedArea())) * cx;
cy = 1 / (6 * Math.abs(signedArea())) * cy;
return new Point2D(cx, cy);
}
private float signedArea() {
float sum = 0.0f;
Iterator<Point2D> it = this.points.iterator();
while (it.hasNext()) {
Point2D p1 = it.next();
if (!it.hasNext()) {
break;
}
Point2D p2 = it.next();
sum += (p1.x * p2.y - p2.x * p1.y);
}
return 0.5f * sum;
}
}