package net.sourceforge.jmicropolygon;
import java.util.Stack;
import javax.microedition.lcdui.Graphics;
import com.nutiteq.components.MapPos;
import com.nutiteq.components.Rectangle;
/**
* Polygon rendering for J2ME MIDP 2.0.
*
* <p>
* This needs MIDP 2.0 for the fillTriangle() method.
* </p>
*
* @author <a href="mailto:simonturner@users.sourceforge.net">Simon Turner</a>
* @version $Id: PolygonGraphics.java,v 1.4 2007/02/27 21:40:13 simonturner Exp
* $
*/
public class PolygonGraphics {
/**
* Draw a polygon
*
* @param g
* The Graphics object to draw the polygon onto
* @param xPoints
* The x-points of the polygon
* @param yPoints
* The y-points of the polygon
*/
public static void drawPolygon(final Graphics g, final int[] xPoints, final int[] yPoints) {
final int max = xPoints.length - 1;
for (int i = 0; i < max; i++) {
g.drawLine(xPoints[i], yPoints[i], xPoints[i + 1], yPoints[i + 1]);
}
g.drawLine(xPoints[max], yPoints[max], xPoints[0], yPoints[0]);
}
/**
* Hash a polygon
*
* @param g
* The Graphics object to draw the polygon onto
* @param xPoints
* The x-points of the polygon
* @param yPoints
* The y-points of the polygon
* @param screenArea
* @return
*/
public static void hashPolygon(final Graphics g, final int[] xPoints, final int[] yPoints,
final int displayWidth, final int displayHeight, final int minScreenY,
final Rectangle screenArea) {
final Stack stack = new Stack();
hashPolygon(g, xPoints, yPoints, stack, displayWidth, displayHeight, minScreenY, screenArea);
while (!stack.isEmpty()) {
hashPolygon(g, (int[]) stack.pop(), (int[]) stack.pop(), stack, displayWidth, displayHeight,
minScreenY, screenArea);
}
}
public static boolean cursorOnPolygon(final MapPos[] points, final int cursorX, final int cursorY) {
final Stack stack = new Stack();
if (cursorOnPolygon(points, stack, cursorX, cursorY)) {
return true;
}
while (!stack.isEmpty()) {
if (cursorOnPolygon((MapPos[]) stack.pop(), stack, cursorX, cursorY)) {
return true;
}
}
return false;
}
private static boolean cursorOnPolygon(MapPos[] points, final Stack stack, final int cursorX,
final int cursorY) {
boolean withinBounds = false;
while (points.length > 2) {
// a, b & c represents a candidate triangle to draw.
// a is the left-most point of the polygon
final int a = indexOfLeastX(points);
// b is the point after a
final int b = (a + 1) % points.length;
// c is the point before a
final int c = (a > 0) ? a - 1 : points.length - 1;
// The value leastInternalIndex holds the index of the left-most
// polygon point found within the candidate triangle, if any.
int leastInternalIndex = -1;
boolean leastInternalSet = false;
// If only 3 points in polygon, skip the tests
if (points.length > 3) {
// Check if any of the other points are within the candidate triangle
for (int i = 0; i < points.length; i++) {
if (i != a && i != b && i != c) {
if (GeomUtils.withinBounds(points[i].getX(), points[i].getY(), points[a].getX(),
points[a].getY(), points[b].getX(), points[b].getY(), points[c].getX(), points[c]
.getY())) {
// Is this point the left-most point within the candidate triangle?
if (!leastInternalSet || points[i].getX() < points[leastInternalIndex].getX()) {
leastInternalIndex = i;
leastInternalSet = true;
}
}
}
}
}
// No internal points found, fill the triangle, and reservoir-dog the polygon
if (!leastInternalSet) {
withinBounds = pointInTriangle(points, a, b, c, cursorX, cursorY) | withinBounds;
//g.fillTriangle(xPoints[a], yPoints[a], xPoints[b], yPoints[b], xPoints[c], yPoints[c]);
points = trimEar(points, a);
// Internal points found, split the polygon into two, using the line between
// "a" (left-most point of the polygon) and leastInternalIndex (left-most
// polygon-point within the candidate triangle) and recurse with each new polygon
} else {
final MapPos[][] split = split(points, a, leastInternalIndex);
stack.push(split[1]);
stack.push(split[0]);
break;
}
}
return withinBounds;
}
static MapPos[][] split(final MapPos[] points, final int aIndex, final int bIndex) {
int firstLen, secondLen;
if (bIndex < aIndex) {
firstLen = (points.length - aIndex) + bIndex + 1;
} else {
firstLen = (bIndex - aIndex) + 1;
}
secondLen = (points.length - firstLen) + 2;
final MapPos[] first = new MapPos[firstLen];
final MapPos[] second = new MapPos[secondLen];
for (int i = 0; i < firstLen; i++) {
final int index = (aIndex + i) % points.length;
first[i] = points[index];
}
for (int i = 0; i < secondLen; i++) {
final int index = (bIndex + i) % points.length;
second[i] = points[index];
}
final MapPos[][] result = new MapPos[][] { first, second };
return result;
}
static MapPos[] trimEar(final MapPos[] points, final int earIndex) {
final MapPos[] newPoints = new MapPos[points.length - 1];
int p = 0;
for (int i = 0; i < points.length; i++) {
if (i != earIndex) {
newPoints[p] = points[i];
p++;
}
}
return newPoints;
}
private final static boolean pointInTriangle(final MapPos[] points, final int a, final int b,
final int c, final int cursorX, final int cursorY) {
final int min_x = GeomUtils.min(points[a].getX(), points[b].getX(), points[c].getX());
final int min_y = GeomUtils.min(points[a].getY(), points[b].getY(), points[c].getY());
final int max_x = GeomUtils.max(points[a].getX(), points[b].getX(), points[c].getX());
final int max_y = GeomUtils.max(points[a].getY(), points[b].getY(), points[c].getY());
int h = max_y - min_y;
int w = max_x - min_x;
//TODO jaanus : what is this?
h += min_y;
w += min_x;
//Check if the screen center point is within the triangle
return GeomUtils.withinBounds(cursorX, cursorY, points[a].getX(), points[a].getY(), points[b]
.getX(), points[b].getY(), points[c].getX(), points[c].getY());
}
static int indexOfLeastX(final MapPos[] elements) {
int index = 0;
int least = elements[0].getX();
for (int i = 1; i < elements.length; i++) {
if (elements[i].getX() < least) {
index = i;
least = elements[i].getX();
}
}
return index;
}
/**
* Hash a polygon
*
* @param g
* The Graphics object to draw the polygon onto
* @param xPoints
* The x-points of the polygon
* @param yPoints
* The y-points of the polygon
* @param stack
* The Stack
* @return
*/
private static void hashPolygon(final Graphics g, int[] xPoints, int[] yPoints,
final Stack stack, final int displayWidth, final int displayHeight, final int minScreenY,
final Rectangle screenArea) {
while (xPoints.length > 2) {
// a, b & c represents a candidate triangle to draw.
// a is the left-most point of the polygon
final int a = GeomUtils.indexOfLeast(xPoints);
// b is the point after a
final int b = (a + 1) % xPoints.length;
// c is the point before a
final int c = (a > 0) ? a - 1 : xPoints.length - 1;
// The value leastInternalIndex holds the index of the left-most
// polygon point found within the candidate triangle, if any.
int leastInternalIndex = -1;
boolean leastInternalSet = false;
// If only 3 points in polygon, skip the tests
if (xPoints.length > 3) {
// Check if any of the other points are within the candidate triangle
for (int i = 0; i < xPoints.length; i++) {
if (i != a && i != b && i != c) {
if (GeomUtils.withinBounds(xPoints[i], yPoints[i], xPoints[a], yPoints[a], xPoints[b],
yPoints[b], xPoints[c], yPoints[c])) {
// Is this point the left-most point within the candidate triangle?
if (!leastInternalSet || xPoints[i] < xPoints[leastInternalIndex]) {
leastInternalIndex = i;
leastInternalSet = true;
}
}
}
}
}
// No internal points found, fill the triangle, and reservoir-dog the polygon
if (!leastInternalSet) {
hashTriangle(g, xPoints, yPoints, a, b, c, displayWidth, displayHeight, minScreenY,
screenArea);
//g.fillTriangle(xPoints[a], yPoints[a], xPoints[b], yPoints[b], xPoints[c], yPoints[c]);
final int[][] trimmed = GeomUtils.trimEar(xPoints, yPoints, a);
xPoints = trimmed[0];
yPoints = trimmed[1];
// Internal points found, split the polygon into two, using the line between
// "a" (left-most point of the polygon) and leastInternalIndex (left-most
// polygon-point within the candidate triangle) and recurse with each new polygon
} else {
final int[][][] split = GeomUtils.split(xPoints, yPoints, a, leastInternalIndex);
final int[][] poly1 = split[0];
final int[][] poly2 = split[1];
// fillPolygon(g, poly1[0], poly1[1]);
// fillPolygon(g, poly2[0], poly2[1]);
stack.push(poly2[1]);
stack.push(poly2[0]);
stack.push(poly1[1]);
stack.push(poly1[0]);
break;
}
}
g.setClip(0, 0, displayWidth, displayHeight);
}
/**
* Hashes the triangle and returns the boolean value indicating if the center
* of the screen is in that triangle.
*
* @param g
* The graphics context
* @param xPoints
* The x-coordinates of polygon
* @param yPoints
* The y-coordinates of polygon
* @param a
* The index of first corner of triangle in the given polygon
* coordinates
* @param b
* The index of second corner of triangle in the given polygon
* coordinates
* @param c
* The index of third corner of triangle in the given polygon
* coordinates
* @param screenArea
* @return True if screen center point is in the triangle, false otherwise.
*/
private final static void hashTriangle(final Graphics g, final int[] xPoints,
final int[] yPoints, final int a, final int b, final int c, final int displayWidth,
final int displayHeight, final int minScreenY, final Rectangle screenArea) {
final int min_x = GeomUtils.min(xPoints[a], xPoints[b], xPoints[c]);
final int min_y = GeomUtils.min(yPoints[a], yPoints[b], yPoints[c]);
final int max_x = GeomUtils.max(xPoints[a], xPoints[b], xPoints[c]);
final int max_y = GeomUtils.max(yPoints[a], yPoints[b], yPoints[c]);
int h = max_y - min_y;
int w = max_x - min_x;
//TODO jaanus : what is this?
h += min_y;
w += min_x;
//TODO jaanus : remove + 3
if ((w < min_x || w > 0) && (h < min_y || h > 0) && (displayWidth < 0 || displayWidth > min_x)
&& (displayHeight < 0 || displayHeight > min_y)) {
for (int i = minScreenY; i < max_y; i += 5) {
g.setClip(min_x, i, max_x - min_x, 1);
g.fillTriangle(xPoints[a], yPoints[a], xPoints[b], yPoints[b], xPoints[c], yPoints[c]);
}
}
}
}